Skip to content

Conversation

cpsievert
Copy link
Collaborator

No description provided.

gadenbuie added 30 commits June 4, 2025 16:38
Custom Hook (useChatState)
* Pure state management: All chat state logic centralized
* Typed methods: appendMessage, appendMessageChunk, clearMessages, etc.
* No DOM dependencies: Can be tested independently

Clean Component Architecture
* ChatInput: Uses callback pattern to expose methods (onMethodsReady)
* ChatContainer: Uses custom hook + listens for CustomEvents
* No imperative refs: All communication through props and events

Shiny Integration Ready
* Event-driven: Components listen for shiny-chat-* CustomEvents
* Type-safe: All event details properly typed
* Simple wrapper: Future Shiny component just dispatches events
Comment on lines +10 to +15
// Message operations (for Shiny integration)
appendMessage: (message: Message) => void
appendMessageChunk: (message: Message) => void
clearMessages: () => void
removeLoadingMessage: () => void
updateUserInput: (update: UpdateUserInput) => void
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like we could take some inspiration from #105 and re-work/simplify the event system?

Comment on lines +132 to +151
handleWillContentChange = () => {
ShinyMarkdownStreamOutput.#doUnBind(this)
}

// Public callback methods for React component and testing
handleContentChange = () => {
// Render Shiny HTML dependencies and bind inputs/outputs
if (this.streaming) {
ShinyMarkdownStreamOutput._throttledBind(this)
} else {
ShinyMarkdownStreamOutput.#doBind(this)
}

// Callback for when content changes - can be used for custom logic
this.dispatchEvent(
new CustomEvent("contentchange", {
detail: { content: this.content },
}),
)
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feels like the binding logic could be moved into the MarkdownStream component implementation?

if (contentType === "text") {
return <div dangerouslySetInnerHTML={{ __html: processedContent }} />
} else if (contentType === "html") {
return <div dangerouslySetInnerHTML={{ __html: processedContent }} />
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could do something closer to what 'react-markdown' does (parses string into hast tree, then creates a JSX runtime from that) https://github.com/remarkjs/react-markdown/blob/fda7fa560bec901a6103e195f9b1979dab543b17/lib/index.js#L348

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants