cherry-studio icon indicating copy to clipboard operation
cherry-studio copied to clipboard

feat: support dialog map mode

Open CreatorZZY opened this issue 8 months ago • 8 comments

同 #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.

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

CreatorZZY avatar May 04 '25 11:05 CreatorZZY

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.
  • Added a new icon button to the navbar.
  • Defined and emitted a SHOW_DIALOG_MAP event on button click.
src/renderer/src/pages/home/Navbar.tsx
src/renderer/src/services/EventService.ts
Introduced a modal component to host the dialog map.
  • Created DialogMap component using Ant Design Modal.
  • Listens for SHOW_DIALOG_MAP event to control modal visibility.
  • Integrated DialogMap into the main App layout.
src/renderer/src/components/DialogMap.tsx
src/renderer/src/App.tsx
Implemented the core conversation flow visualization logic.
  • Added ChatFlowMap component utilizing @xyflow/react.
  • Created custom node (CustomNode) to represent user messages and associated assistant replies.
  • Implemented buildConversationFlowData to convert message list into React Flow nodes and edges.
  • Calculated node positions considering sequence and branching.
  • Handled rendering of nodes, edges, minimap, and controls.
  • Added loading and empty states.
src/renderer/src/pages/home/Messages/ChatFlowMap.tsx
Added support for message branching.
  • Added optional branchId and parentMessageId fields to the Message type.
  • Included logic in buildConversationFlowData to position branched nodes and draw connecting edges.
  • Added a '+' button on nodes to trigger branch creation.
  • Implemented event handler (flow-add-branch) to dispatch action adding a new branched message.
src/renderer/src/types/newMessage.ts
src/renderer/src/pages/home/Messages/ChatFlowMap.tsx
Enabled interactivity between the map and the chat view.
  • Implemented node click handler.
  • Dispatched custom event (flow-navigate-to-message) and existing event (LOCATE_MESSAGE) to scroll to the corresponding message in the chat list.
src/renderer/src/pages/home/Messages/ChatFlowMap.tsx

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on 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 issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull request title to generate a title at any time. You can also comment @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in the pull request body to generate a PR summary at any time exactly where you want it. You can also comment @sourcery-ai summary on the pull request to (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on 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 dismiss on 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 review to 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.

sourcery-ai[bot] avatar May 04 '25 11:05 sourcery-ai[bot]

image

CreatorZZY avatar May 04 '25 12:05 CreatorZZY

这个好有用啊!

lioensky avatar May 04 '25 15:05 lioensky

思路简单来说是这样:

每次添加分支时把分支赋予branch-id为branch-{timestamp},默认无分支的情况下branch-id为undefined。 绘图的时候对同一个topic下的branch-{timestamp}进行排序,按顺序设置横坐标。遇到undefined的时候默认不调整横坐标。 新建或选择branch后,每一个新的Message都会带有对应branch-id、branch父节点的node-id信息,以方便回溯整个path。 新建或选择branch后,原有的ScrollContainer中的非branch-id被隐藏不显示、调用大模型api时不被包括作为背景。

如果有兴趣,欢迎大佬接着当前工作进行。

CreatorZZY avatar May 04 '25 16:05 CreatorZZY

目前的状态是branchid不知如何传入src/renderer/src/pages/home/Inputbar/Inputbar.tsx。目前的做法是通过ChatFlowMap 组件会广播 flow-add-branch 事件,包含 branchId 和 messageId。但是貌似不太行。 @DeJeune @0xfullex @shiquda

CreatorZZY avatar May 04 '25 16:05 CreatorZZY

提个建议 不知道这个界面能不能和“聊天历史”合并...? 每次新建都得点开流程图,是不是可以考虑把新建节点放在每条用户消息的menubar里(个人看法

Pleasurecruise avatar May 04 '25 17:05 Pleasurecruise

提个建议 不知道这个界面能不能和“聊天历史”合并...? 每次新建都得点开流程图,是不是可以考虑把新建节点放在每条用户消息的menubar里(个人看法

那都是后话了,现在先解决branchid如何添加到message中并持久化的问题

CreatorZZY avatar May 04 '25 17:05 CreatorZZY

持久化需要新增字段了 https://github.com/CherryHQ/cherry-studio/blob/9acd2121d436204fb75a11ad883a47e2ea9c96c9/src/renderer/src/databases/index.ts#L63-L75 重构的时候似乎没有考虑到会有这个需求

Pleasurecruise avatar May 04 '25 17:05 Pleasurecruise