feat: support dialog map mode
同 #5378
What this PR does
Before this PR:
After this PR:
Fixes #
Why we need it and why it was done in this way
The following tradeoffs were made:
The following alternatives were considered:
Links to places where the discussion took place:
Breaking changes
If this PR introduces breaking changes, please describe the changes and the impact on users.
Special notes for your reviewer
Checklist
This checklist is not enforcing, but it's a reminder of items that could be relevant to every PR. Approvers are expected to review this list.
- [ ] PR: The PR description is expressive enough and will help future contributors
- [ ] Code: Write code that humans can understand and Keep it simple
- [ ] Refactor: You have left the code cleaner than you found it (Boy Scout Rule)
- [ ] Upgrade: Impact of this change on upgrade flows was considered and addressed if required
- [ ] Documentation: A user-guide update was considered and is present (link) or not required. You want a user-guide update if it's a user facing feature.
Release note
Summary by Sourcery
Add support for dialog map mode, allowing users to visualize conversation flow and create conversation branches
New Features:
- Implement a dialog flow map visualization for conversations
- Add ability to create conversation branches from the flow map
Enhancements:
- Create a new component to render conversation flow using React Flow
- Add event handling for navigating and interacting with conversation nodes
Reviewer's Guide
This PR introduces a dialog map feature, visualizing conversation history as a flow chart using the @xyflow/react library. It adds a navbar button to open a modal displaying the flow, calculates node positions based on message order and branching, supports creating new branches from the map, and allows navigating from a map node back to the corresponding message in the chat.
Sequence Diagram: Displaying the Dialog Map
sequenceDiagram
actor User
participant Navbar
participant EventEmitter
participant DialogMap
participant ChatFlowMap
participant Store
User->>Navbar: Clicks 'Expand Dialog Map' icon
Navbar->>EventEmitter: emit(EVENT_NAMES.SHOW_DIALOG_MAP)
EventEmitter->>DialogMap: Receives SHOW_DIALOG_MAP event
DialogMap->>DialogMap: setIsOpen(true)
DialogMap->>ChatFlowMap: Renders ChatFlowMap component
ChatFlowMap->>Store: selectMessagesForTopic(currentTopicId)
Store-->>ChatFlowMap: Returns messages
ChatFlowMap->>ChatFlowMap: buildConversationFlowData()
ChatFlowMap->>ChatFlowMap: setNodes([...]), setEdges([...])
ChatFlowMap-->>DialogMap: Renders flow chart in Modal
Sequence Diagram: Adding a New Branch
sequenceDiagram
actor User
participant CustomNode
participant Document
participant ChatFlowMap
participant Store
User->>CustomNode: Clicks '+' button on a node
CustomNode->>Document: dispatchEvent(new CustomEvent('flow-add-branch', { detail: { messageId } }))
Document->>ChatFlowMap: Listens for 'flow-add-branch' event
ChatFlowMap->>ChatFlowMap: handleAddBranch(event)
ChatFlowMap->>ChatFlowMap: Generates branchId, newMessageId
ChatFlowMap->>ChatFlowMap: Creates new Message object with parentMessageId
ChatFlowMap->>Store: dispatch(newMessagesActions.addMessage({ topicId, message }))
Store-->>ChatFlowMap: State updates with new message
ChatFlowMap->>ChatFlowMap: buildConversationFlowData()
ChatFlowMap->>ChatFlowMap: setNodes([...]), setEdges([...])
ChatFlowMap-->>User: Displays updated flow chart with new branch node
Sequence Diagram: Navigating to Message from Map
sequenceDiagram
actor User
participant CustomNode
participant Document
participant MessageListHandler as "Message List Handler (Implied)"
participant EventEmitter
User->>CustomNode: Clicks on a node
CustomNode->>Document: dispatchEvent(new CustomEvent('flow-navigate-to-message', { detail: { messageId, ... } }))
Document->>MessageListHandler: Listens for 'flow-navigate-to-message'
MessageListHandler->>MessageListHandler: Switches tab if needed (based on event detail)
Note over CustomNode, EventEmitter: Delay introduced to allow tab switching
CustomNode->>EventEmitter: emit(EVENT_NAMES.LOCATE_MESSAGE + ':' + messageId)
EventEmitter->>MessageListHandler: Listens for LOCATE_MESSAGE event
MessageListHandler->>MessageListHandler: Scrolls chat view to the specific message
Class Diagram: Dialog Map Feature Components and Types
classDiagram
direction LR
class Message {
+String id
+String role
+String assistantId
+String topicId
+String createdAt
+UserMessageStatus status
+Object[] blocks
+String askId
+Model[] mentions
+MCPServer[] enabledMCPs
+String branchId
+String parentMessageId
+Usage usage
+Metrics metrics
}
note for Message "Added branchId and parentMessageId\nfor conversation branching."
class EventService {
+Object EVENT_NAMES
}
note for EventService "Added EVENT_NAMES.SHOW_DIALOG_MAP"
class Navbar {
+Function onClick()
+EventEmitter eventEmitter
}
note for Navbar "Added button to trigger SHOW_DIALOG_MAP event"
class DialogMap {
+Boolean isOpen
+String currentTopicId
+EventEmitter eventEmitter
+ChatFlowMap chatFlowMap
+handleShowDialogMap()
+handleClose()
}
note for DialogMap "New component: Modal wrapper for ChatFlowMap."
class ChatFlowMap {
+Node[] nodes
+Edge[] edges
+Boolean loading
+Message[] messages
+String conversationId
+Function setNodes
+Function setEdges
+Store store
+EventEmitter eventEmitter
+Document document
+CustomNode customNode
+buildConversationFlowData()
+handleAddBranch()
}
note for ChatFlowMap "New component: Renders the conversation flow using @xyflow/react."
class CustomNode {
+Object data
+Document document
+handleNodeClick()
+handleAddBranch()
}
note for CustomNode "New component: Represents a node in the flow map."
Navbar ..> EventEmitter : Emits event
DialogMap ..> EventEmitter : Listens for event
DialogMap ..> ChatFlowMap : Renders
ChatFlowMap ..> Store : Selects messages, Dispatches actions
ChatFlowMap ..> Document : Listens for events
ChatFlowMap ..> CustomNode : Uses as node type
CustomNode ..> Document : Dispatches events
ChatFlowMap ..> EventEmitter : Emits event
ChatFlowMap ..> Message : Uses
File-Level Changes
| Change | Details | Files |
|---|---|---|
| Added entry point and event for triggering the dialog map view. |
|
src/renderer/src/pages/home/Navbar.tsxsrc/renderer/src/services/EventService.ts |
| Introduced a modal component to host the dialog map. |
|
src/renderer/src/components/DialogMap.tsxsrc/renderer/src/App.tsx |
| Implemented the core conversation flow visualization logic. |
|
src/renderer/src/pages/home/Messages/ChatFlowMap.tsx |
| Added support for message branching. |
|
src/renderer/src/types/newMessage.tssrc/renderer/src/pages/home/Messages/ChatFlowMap.tsx |
| Enabled interactivity between the map and the chat view. |
|
src/renderer/src/pages/home/Messages/ChatFlowMap.tsx |
Tips and commands
Interacting with Sourcery
- Trigger a new review: Comment
@sourcery-ai reviewon the pull request. - Continue discussions: Reply directly to Sourcery's review comments.
- Generate a GitHub issue from a review comment: Ask Sourcery to create an
issue from a review comment by replying to it. You can also reply to a
review comment with
@sourcery-ai issueto create an issue from it. - Generate a pull request title: Write
@sourcery-aianywhere in the pull request title to generate a title at any time. You can also comment@sourcery-ai titleon the pull request to (re-)generate the title at any time. - Generate a pull request summary: Write
@sourcery-ai summaryanywhere in the pull request body to generate a PR summary at any time exactly where you want it. You can also comment@sourcery-ai summaryon the pull request to (re-)generate the summary at any time. - Generate reviewer's guide: Comment
@sourcery-ai guideon the pull request to (re-)generate the reviewer's guide at any time. - Resolve all Sourcery comments: Comment
@sourcery-ai resolveon the pull request to resolve all Sourcery comments. Useful if you've already addressed all the comments and don't want to see them anymore. - Dismiss all Sourcery reviews: Comment
@sourcery-ai dismisson the pull request to dismiss all existing Sourcery reviews. Especially useful if you want to start fresh with a new review - don't forget to comment@sourcery-ai reviewto trigger a new review!
Customizing Your Experience
Access your dashboard to:
- Enable or disable review features such as the Sourcery-generated pull request summary, the reviewer's guide, and others.
- Change the review language.
- Add, remove or edit custom review instructions.
- Adjust other review settings.
Getting Help
- Contact our support team for questions or feedback.
- Visit our documentation for detailed guides and information.
- Keep in touch with the Sourcery team by following us on X/Twitter, LinkedIn or GitHub.
这个好有用啊!
思路简单来说是这样:
每次添加分支时把分支赋予branch-id为branch-{timestamp},默认无分支的情况下branch-id为undefined。 绘图的时候对同一个topic下的branch-{timestamp}进行排序,按顺序设置横坐标。遇到undefined的时候默认不调整横坐标。 新建或选择branch后,每一个新的Message都会带有对应branch-id、branch父节点的node-id信息,以方便回溯整个path。 新建或选择branch后,原有的ScrollContainer中的非branch-id被隐藏不显示、调用大模型api时不被包括作为背景。
如果有兴趣,欢迎大佬接着当前工作进行。
目前的状态是branchid不知如何传入src/renderer/src/pages/home/Inputbar/Inputbar.tsx。目前的做法是通过ChatFlowMap 组件会广播 flow-add-branch 事件,包含 branchId 和 messageId。但是貌似不太行。 @DeJeune @0xfullex @shiquda
提个建议 不知道这个界面能不能和“聊天历史”合并...? 每次新建都得点开流程图,是不是可以考虑把新建节点放在每条用户消息的menubar里(个人看法
提个建议 不知道这个界面能不能和“聊天历史”合并...? 每次新建都得点开流程图,是不是可以考虑把新建节点放在每条用户消息的menubar里(个人看法
那都是后话了,现在先解决branchid如何添加到message中并持久化的问题
持久化需要新增字段了 https://github.com/CherryHQ/cherry-studio/blob/9acd2121d436204fb75a11ad883a47e2ea9c96c9/src/renderer/src/databases/index.ts#L63-L75 重构的时候似乎没有考虑到会有这个需求