Skip to content

feat(flow-chat): add quote-select — copy or quote selected message text into composer - #2321

Open
buddha68 wants to merge 1 commit into
GCWing:mainfrom
buddha68:feature/quote-select
Open

feat(flow-chat): add quote-select — copy or quote selected message text into composer#2321
buddha68 wants to merge 1 commit into
GCWing:mainfrom
buddha68:feature/quote-select

Conversation

@buddha68

Copy link
Copy Markdown

Summary

Adds a "select-to-quote" workflow to the chat composer, mirroring the interaction
popularized by qoderwork and dsh's @deepseek-ai/dsh-quote-select:

  1. Select text inside a user or assistant message → a floating menu appears with
    Copy text and Add to conversation.
  2. Add to conversation writes a provenance marker
    【引用自对话第 N 条消息(你的原话/AI 的回答)】「原文」 into the composer
    (quote first, existing draft below) and renders a removable quote chip above
    the input.
  3. Chips stay in sync with the draft: removing the marker text removes the chip;
    sending clears both; clicking ✕ removes the marker from the draft.

Design

  • New component src/web-ui/src/flow_chat/components/QuoteSelect/:
    • QuoteSelect.tsx — selection detection + floating menu (portal to
      getAppearanceOverlayHost()), copy & add handlers.
    • quoteSelectPick.ts — pure selection extraction (separate file to satisfy
      react-refresh/only-export-components).
    • quoteSelectStore.ts — zustand store; composer read/write via the existing
      public event-bus contracts.
    • QuoteDock.tsx — removable quote chips rendered through the new
      composerDock extension point.
    • QuoteSelect.scss, QuoteSelect.test.tsx (12 tests).
  • Minimal host changes (~15 lines total):
    • ChatInput.tsx: new optional render-prop prop composerDock?: (draft: string) => React.ReactNode,
      rendered above the composer inside bitfun-chat-input__container.
    • ChatPane.tsx: mounts <QuoteSelect /> and passes <QuoteDock draft={...} />
      to ChatInput.
  • Reuses existing contracts (no DOM hacks):
    • Message anchors: data-bf-component="user-message-item" (+ data-turn-id) and
      data-bf-component="model-round-item".
    • Composer write: globalEventBus.emit('fill-chat-input', { content, mode }).
    • Composer read: globalEventBus.emit('chat-input:get-state', { getValue }).
  • New dev dependencies: @testing-library/react, @testing-library/dom (test-only).

Verification

  • vitest run QuoteSelect → 12/12 passed.
  • eslint on changed files → 0 errors, 0 warnings.
  • Existing ChatInput* test suites → 83/83 passed (no regressions).
  • vite dev-server compile check on all touched modules → 200 OK.

Notes / follow-ups

  • v1 message ordinal is a DOM-based approximation (visible rows). A follow-up
    could compute global ordinals via absoluteSessionTurnIndexForId and add
    data-turn-id to ModelRoundItem for consistency.
  • Tool-output-only quotes and multi-message quotes are out of scope for v1.

@GCWing
GCWing requested a review from kev1n77 August 16, 2026 00:54
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.

1 participant