@neuronection/assistant-ui 0.24.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -0
- package/README.md +1 -1
- package/dist/chat-drawer.js +1 -1
- package/dist/chat-message.js +1 -1
- package/dist/chat-panel.d.ts +1 -1
- package/dist/chat-panel.js +1 -1
- package/dist/chat-tool-card.d.ts +6 -0
- package/dist/chat-tool-card.js +1 -1
- package/dist/chat-transcript.js +1 -1
- package/dist/chat-turn-status.d.ts +26 -0
- package/dist/chat-turn-status.js +3 -0
- package/dist/chat-turn-status.js.map +1 -0
- package/dist/{chunk-BSIWLVDX.js → chunk-FGYGWZ6S.js} +3 -3
- package/dist/{chunk-BSIWLVDX.js.map → chunk-FGYGWZ6S.js.map} +1 -1
- package/dist/{chunk-BVJT67B5.js → chunk-FS7M7PNS.js} +3 -3
- package/dist/{chunk-BVJT67B5.js.map → chunk-FS7M7PNS.js.map} +1 -1
- package/dist/{chunk-S62VJVWB.js → chunk-LFSICACE.js} +13 -5
- package/dist/chunk-LFSICACE.js.map +1 -0
- package/dist/{chunk-JPDD5AFN.js → chunk-NBT7NLNU.js} +6 -5
- package/dist/chunk-NBT7NLNU.js.map +1 -0
- package/dist/chunk-P75QKU5J.js +57 -0
- package/dist/chunk-P75QKU5J.js.map +1 -0
- package/dist/{chunk-X3C2FBMR.js → chunk-WBSGFOSD.js} +4 -3
- package/dist/chunk-WBSGFOSD.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +12 -11
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +5 -1
- package/dist/chunk-JPDD5AFN.js.map +0 -1
- package/dist/chunk-S62VJVWB.js.map +0 -1
- package/dist/chunk-X3C2FBMR.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,47 @@
|
|
|
1
1
|
# @neuronection/assistant-ui
|
|
2
2
|
|
|
3
|
+
## 0.26.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`e2279ae`](https://github.com/neuronection/assistant-ui/commit/e2279ae29c73565770cc09decf830d6b725b9101) Thanks [@constLiakos](https://github.com/constLiakos)! - ChatPanel `page` variant is now a full-bleed shell: the host no longer centers
|
|
8
|
+
itself with `mx-auto max-w-3xl` — header, banner and composer bar span the
|
|
9
|
+
whole page and only the transcript region and the composer body sit in a
|
|
10
|
+
centered `max-w-3xl` column. App pages hosting the page variant integrate with
|
|
11
|
+
the surrounding route chrome (full-width header/dividers) instead of floating
|
|
12
|
+
as a narrow centered card. Sidebar and bubble variants are unchanged.
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- [`a1b3277`](https://github.com/neuronection/assistant-ui/commit/a1b3277c1902a76909fa3c9235de9fc20a57dfa6) Thanks [@constLiakos](https://github.com/constLiakos)! - `ChatDrawer` now forwards its `ref` to the drawer content element
|
|
17
|
+
(`[data-as="chat-drawer"]`). The render function previously accepted no
|
|
18
|
+
`ref` parameter, which triggered React's "forwardRef render functions
|
|
19
|
+
accept exactly two parameters" development warning in every app using
|
|
20
|
+
the drawer.
|
|
21
|
+
|
|
22
|
+
- [`f761728`](https://github.com/neuronection/assistant-ui/commit/f7617289a65ab829b7e7bed01411663bf82f0593) Thanks [@constLiakos](https://github.com/constLiakos)! - ChatMessage assistant bubbles use the `--as-muted` fill and drop the border
|
|
23
|
+
(the user bubble keeps the `--as-primary` fill). The white bordered
|
|
24
|
+
`--as-surface-raised` bubble read as a detached card against the family's
|
|
25
|
+
soft-surface look — study's original style-source design was a borderless
|
|
26
|
+
muted bubble, and that is now again what the family renders.
|
|
27
|
+
|
|
28
|
+
## 0.25.0
|
|
29
|
+
|
|
30
|
+
### Minor Changes
|
|
31
|
+
|
|
32
|
+
- [`102ef81`](https://github.com/neuronection/assistant-ui/commit/102ef81e08eb2f85bffcfed78195dd3143eeff8e) Thanks [@constLiakos](https://github.com/constLiakos)! - `ChatToolCard` gains a `renderResult?: (result: string) => ReactNode`
|
|
33
|
+
slot (family plan 12 L2): apps can render per-tool result views (quiz
|
|
34
|
+
previews, state confirmations, domain links) in place of the default
|
|
35
|
+
serialized `<pre>` pane. The card is expandable when only the slot is
|
|
36
|
+
provided (tools with no serialized output); default behavior is
|
|
37
|
+
unchanged when the slot is absent.
|
|
38
|
+
|
|
39
|
+
- [`4952849`](https://github.com/neuronection/assistant-ui/commit/4952849151e8e0d922fc95204da0042229fb9abe) Thanks [@constLiakos](https://github.com/constLiakos)! - New `chat-turn-status` module: live pre-text turn status generalizing
|
|
40
|
+
study's `TurnTraceStatus` + `ThinkingDots` (family plan 12 L1). Animated
|
|
41
|
+
dots + phase label in a labelled `role="status"` region, optional elapsed
|
|
42
|
+
timer (`startedAt`, 100 ms tick, tabular-nums), `row`/`card` variants,
|
|
43
|
+
dots disabled under `prefers-reduced-motion`.
|
|
44
|
+
|
|
3
45
|
## 0.24.0
|
|
4
46
|
|
|
5
47
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -90,7 +90,7 @@ Visual reference: [gallery](https://neuronection.github.io/assistant-ui/).
|
|
|
90
90
|
| [`ai-button`](./docs/components/ai-button.md) / [`ai-actions-dropdown`](./docs/components/ai-actions-dropdown.md) / [`ai-magic-fill`](./docs/components/ai-magic-fill.md) | AI affordances: ask-with-suggestions (controlled open, icon-only mode) · action menu + custom prompt · describe-in-words fill modal — API calls stay app-side | [stories](https://neuronection.github.io/assistant-ui/?story=ai-patterns--ai-buttons) |
|
|
91
91
|
| [`chat-core`](./docs/components/chat-core.md) | `useChatStream` (transport-injected live-turn state machine), `liveTurnReducer`, `ChatMessageView`, family `ChatStreamEvent` vocabulary, branch-tree utils (`buildBranchTree`, `walkActivePath`, `variantInfo`) | [stories](https://neuronection.github.io/assistant-ui/?story=chat-core--chat-core-story) |
|
|
92
92
|
| [`chat-markdown`](./docs/components/chat-markdown.md) | `MarkdownSurface` (read-only streaming markdown: GFM + KaTeX + lazy mermaid + copyable code blocks; no raw HTML) · `MarkdownCodeBlock` · `MermaidDiagram` | [stories](https://neuronection.github.io/assistant-ui/?story=chat-markdown--default) |
|
|
93
|
-
| [`chat-message`](./docs/components/chat-message.md) / [`chat-reasoning`](./docs/components/chat-reasoning.md) / [`chat-tool-card`](./docs/components/chat-tool-card.md) | `ChatMessage` (bubble: actions row, variant switcher, edit-and-resend, error/interrupted states) · `ChatReasoning` (collapsible thinking block) · `ChatToolCard` (tool observation: status, duration, args/result) | [stories](https://neuronection.github.io/assistant-ui/?story=chat-message--assistant) |
|
|
93
|
+
| [`chat-message`](./docs/components/chat-message.md) / [`chat-reasoning`](./docs/components/chat-reasoning.md) / [`chat-tool-card`](./docs/components/chat-tool-card.md) / [`chat-turn-status`](./docs/components/chat-turn-status.md) | `ChatMessage` (bubble: actions row, variant switcher, edit-and-resend, error/interrupted states) · `ChatReasoning` (collapsible thinking block) · `ChatToolCard` (tool observation: status, duration, args/result, app-provided result views via `renderResult`) · `ChatTurnStatus` (live pre-text turn status: dots, phase label, elapsed timer) | [stories](https://neuronection.github.io/assistant-ui/?story=chat-message--assistant) |
|
|
94
94
|
| [`chat-composer`](./docs/components/chat-composer.md) | `ChatComposer` — auto-growing IME-safe input with send/stop states, toolbar/attachment/suggestion slots, drag-drop + paste wiring | [stories](https://neuronection.github.io/assistant-ui/?story=chat-composer--default) |
|
|
95
95
|
| [`chat-transcript`](./docs/components/chat-transcript.md) / [`chat-branch-tree`](./docs/components/chat-branch-tree.md) / [`chat-session-list`](./docs/components/chat-session-list.md) | `ChatTranscript` (log semantics, polite announcements, stick-to-bottom + jump pill, optional virtualization) · `ChatBranchTree` (OpenWebUI-style branch rail, keyboard tree nav) · `ChatSessionList` (fuzzy search, date groups, row actions) | [stories](https://neuronection.github.io/assistant-ui/?story=chat-surfaces--transcript) |
|
|
96
96
|
| [`chat-panel`](./docs/components/chat-panel.md) / [`chat-drawer`](./docs/components/chat-drawer.md) / [`chat-launcher`](./docs/components/chat-launcher.md) | the three chat surfaces from one assembly — `ChatPanel` host (`page` \| `sidebar` \| `bubble`) · `ChatDrawer` (resizable sidepanel on Radix Dialog, keyboard resize) · `ChatLauncher` (floating bubble → anchored panel, badge) | [stories](https://neuronection.github.io/assistant-ui/?story=chat-surfaces--transcript) |
|
package/dist/chat-drawer.js
CHANGED
package/dist/chat-message.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { ChatMessage, ChatMessageEditor, MessageVariantSwitcher } from './chunk-
|
|
1
|
+
export { ChatMessage, ChatMessageEditor, MessageVariantSwitcher } from './chunk-FGYGWZ6S.js';
|
|
2
2
|
//# sourceMappingURL=chat-message.js.map
|
|
3
3
|
//# sourceMappingURL=chat-message.js.map
|
package/dist/chat-panel.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import * as React from 'react';
|
|
|
2
2
|
|
|
3
3
|
type ChatPanelVariant = 'page' | 'sidebar' | 'bubble';
|
|
4
4
|
interface ChatPanelProps {
|
|
5
|
-
/** Layout tier: full page (
|
|
5
|
+
/** Layout tier: full page (full-bleed shell, centered conversation column), sidebar (dense column), bubble (compact widget body). */
|
|
6
6
|
variant: ChatPanelVariant;
|
|
7
7
|
/** Header row: title node + action nodes (session list, branch tree, expand, close). */
|
|
8
8
|
title?: React.ReactNode;
|
package/dist/chat-panel.js
CHANGED
package/dist/chat-tool-card.d.ts
CHANGED
|
@@ -18,6 +18,12 @@ interface ChatToolCardProps {
|
|
|
18
18
|
args?: string;
|
|
19
19
|
/** Serialized result. */
|
|
20
20
|
result?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Custom result view (study's per-tool views, family plan 12 L2).
|
|
23
|
+
* Called with the result string (possibly empty when the tool has no
|
|
24
|
+
* serialized output); when absent the default `<pre>` pane renders.
|
|
25
|
+
*/
|
|
26
|
+
renderResult?: (result: string) => React.ReactNode;
|
|
21
27
|
durationMs?: number;
|
|
22
28
|
open?: boolean;
|
|
23
29
|
defaultOpen?: boolean;
|
package/dist/chat-tool-card.js
CHANGED
package/dist/chat-transcript.js
CHANGED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
interface ChatTurnStatusLabels {
|
|
4
|
+
timer: string;
|
|
5
|
+
}
|
|
6
|
+
interface ChatTurnStatusProps {
|
|
7
|
+
/** Current turn phase, e.g. "searching the catalog". */
|
|
8
|
+
label: string;
|
|
9
|
+
/** Turn start timestamp (ms). Presence enables the elapsed timer. */
|
|
10
|
+
startedAt?: number;
|
|
11
|
+
/** `row` = inline pre-text line; `card` = soft surface block. */
|
|
12
|
+
variant?: 'row' | 'card';
|
|
13
|
+
/** Elapsed-timer refresh interval. Default 100 ms (study-proven). */
|
|
14
|
+
tickMs?: number;
|
|
15
|
+
labels?: Partial<ChatTurnStatusLabels>;
|
|
16
|
+
className?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Live pre-text turn status (study's `TurnTraceStatus` + `ThinkingDots`,
|
|
20
|
+
* generalized for family plan 12 L1): animated dots + phase label, with
|
|
21
|
+
* an optional elapsed timer. Presentational and controlled — the turn
|
|
22
|
+
* state itself lives in `useChatStream`; this only narrates it.
|
|
23
|
+
*/
|
|
24
|
+
declare const ChatTurnStatus: React.ForwardRefExoticComponent<ChatTurnStatusProps & React.RefAttributes<HTMLDivElement>>;
|
|
25
|
+
|
|
26
|
+
export { ChatTurnStatus, type ChatTurnStatusLabels, type ChatTurnStatusProps };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"chat-turn-status.js"}
|
|
@@ -145,7 +145,7 @@ var ChatMessage = React.forwardRef(
|
|
|
145
145
|
className: cn(
|
|
146
146
|
"relative flex max-w-[92%] flex-col gap-1.5 rounded-[var(--as-radius-lg)] px-3 py-2 text-sm",
|
|
147
147
|
compact && "px-2.5 py-1.5",
|
|
148
|
-
isUser ? "rounded-br-[var(--as-radius-sm)] bg-[var(--as-primary)] text-[var(--as-primary-fg)]" : "rounded-bl-[var(--as-radius-sm)]
|
|
148
|
+
isUser ? "rounded-br-[var(--as-radius-sm)] bg-[var(--as-primary)] text-[var(--as-primary-fg)]" : "rounded-bl-[var(--as-radius-sm)] bg-[var(--as-muted)] text-[var(--as-fg)]"
|
|
149
149
|
),
|
|
150
150
|
children: [
|
|
151
151
|
reasoning ? /* @__PURE__ */ jsx("div", { className: "not-prose", children: reasoning }) : null,
|
|
@@ -213,5 +213,5 @@ var ChatMessage = React.forwardRef(
|
|
|
213
213
|
);
|
|
214
214
|
|
|
215
215
|
export { ChatMessage, ChatMessageEditor, MessageVariantSwitcher };
|
|
216
|
-
//# sourceMappingURL=chunk-
|
|
217
|
-
//# sourceMappingURL=chunk-
|
|
216
|
+
//# sourceMappingURL=chunk-FGYGWZ6S.js.map
|
|
217
|
+
//# sourceMappingURL=chunk-FGYGWZ6S.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/chat-message/ChatMessageEditor.tsx","../src/components/chat-message/MessageVariantSwitcher.tsx","../src/components/chat-message/ChatMessage.tsx"],"names":["ChatMessageEditor","React2","MessageVariantSwitcher","jsxs","jsx","React3","ChatMessage"],"mappings":";;;;;;AA2BO,IAAM,iBAAA,GAA0B,KAAA,CAAA,UAAA;AAAA,EACrC,SAASA,kBAAAA,CACP,EAAE,KAAA,EAAO,eAAe,QAAA,EAAU,QAAA,EAAU,cAAA,GAAiB,KAAA,EAAO,IAAA,GAAO,CAAA,EAAG,MAAA,EAAQ,SAAA,IACtF,GAAA,EACA;AACA,IAAA,MAAM,SAAA,GAA6D,CAAC,KAAA,KAAU;AAC5E,MAAA,IAAI,KAAA,CAAM,QAAQ,QAAA,EAAU;AAC1B,QAAA,KAAA,CAAM,cAAA,EAAe;AACrB,QAAA,QAAA,EAAS;AACT,QAAA;AAAA,MACF;AACA,MAAA,IAAI,MAAM,GAAA,KAAQ,OAAA,KAAY,KAAA,CAAM,OAAA,IAAW,MAAM,OAAA,CAAA,EAAU;AAC7D,QAAA,KAAA,CAAM,cAAA,EAAe;AACrB,QAAA,IAAI,CAAC,cAAA,EAAgB;AACnB,UAAA,QAAA,EAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF,CAAA;AAEA,IAAA,uBACE,IAAA,CAAC,SAAI,GAAA,EAAU,SAAA,EAAQ,uBAAsB,SAAA,EAAW,EAAA,CAAG,4BAAA,EAA8B,SAAS,CAAA,EAChG,QAAA,EAAA;AAAA,sBAAA,GAAA;AAAA,QAAC,UAAA;AAAA,QAAA;AAAA,UACC,KAAA;AAAA,UACA,IAAA;AAAA,UACA,YAAA,EAAY,QAAQ,SAAA,IAAa,cAAA;AAAA,UACjC,UAAU,CAAC,KAAA,KAAU,aAAA,CAAc,KAAA,CAAM,OAAO,KAAK,CAAA;AAAA,UACrD,SAAA;AAAA,UACA,SAAA,EAAU;AAAA;AAAA,OACZ;AAAA,sBACA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,qCAAA,EACb,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,MAAA,EAAA,EAAO,SAAQ,OAAA,EAAQ,IAAA,EAAK,MAAK,OAAA,EAAS,QAAA,EACxC,QAAA,EAAA,MAAA,EAAQ,MAAA,IAAU,QAAA,EACrB,CAAA;AAAA,wBACA,GAAA,CAAC,MAAA,EAAA,EAAO,IAAA,EAAK,IAAA,EAAK,QAAA,EAAU,gBAAgB,OAAA,EAAS,QAAA,EAClD,QAAA,EAAA,MAAA,EAAQ,IAAA,IAAQ,eAAA,EACnB;AAAA,OAAA,EACF;AAAA,KAAA,EACF,CAAA;AAAA,EAEJ;AACF;AC1CO,IAAM,sBAAA,GAA+BC,KAAA,CAAA,UAAA;AAAA,EAC1C,SAASC,wBAAuB,EAAE,QAAA,EAAU,UAAU,MAAA,EAAQ,SAAA,IAAa,GAAA,EAAK;AAC9E,IAAA,MAAM,EAAE,KAAA,EAAO,KAAA,EAAO,UAAA,EAAW,GAAI,QAAA;AACrC,IAAA,MAAM,aAAa,KAAA,GAAQ,CAAA,GAAI,UAAA,CAAW,KAAA,GAAQ,CAAC,CAAA,GAAI,MAAA;AACvD,IAAA,MAAM,MAAA,GAAS,KAAA,GAAQ,KAAA,GAAQ,UAAA,CAAW,KAAK,CAAA,GAAI,MAAA;AAEnD,IAAA,MAAM,MAAA,GAAS,CAAC,KAAA,EAA4C,SAAA,KAAkC;AAC5F,MAAA,KAAA,CAAM,eAAA,EAAgB;AACtB,MAAA,IAAI,cAAc,MAAA,EAAW;AAC3B,QAAA,QAAA,CAAS,SAAS,CAAA;AAAA,MACpB;AAAA,IACF,CAAA;AAEA,IAAA,uBACEC,IAAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACA,IAAA,EAAK,OAAA;AAAA,QACL,YAAA,EAAY,QAAQ,KAAA,IAAS,kBAAA;AAAA,QAC7B,SAAA,EAAQ,0BAAA;AAAA,QACR,SAAA,EAAW,EAAA,CAAG,6DAAA,EAA+D,SAAS,CAAA;AAAA,QAEtF,QAAA,EAAA;AAAA,0BAAAC,GAAAA;AAAA,YAAC,QAAA;AAAA,YAAA;AAAA,cACC,IAAA,EAAK,QAAA;AAAA,cACL,UAAU,UAAA,KAAe,MAAA;AAAA,cACzB,YAAA,EAAY,QAAQ,QAAA,IAAY,kBAAA;AAAA,cAChC,KAAA,EAAO,QAAQ,QAAA,IAAY,kBAAA;AAAA,cAC3B,OAAA,EAAS,CAAC,KAAA,KAAU,MAAA,CAAO,OAAO,UAAU,CAAA;AAAA,cAC5C,SAAA,EAAU,0QAAA;AAAA,cAEV,0BAAAA,GAAAA,CAAC,WAAA,EAAA,EAAY,SAAA,EAAU,UAAA,EAAW,eAAW,IAAA,EAAC;AAAA;AAAA,WAChD;AAAA,0BACAD,IAAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,gBAAe,KAAA,EAAO,MAAA,EAAQ,SAAA,GAAY,KAAA,EAAO,KAAK,CAAA,IAAK,CAAA,QAAA,EAAW,KAAK,CAAA,IAAA,EAAO,KAAK,CAAA,CAAA,EACpG,QAAA,EAAA;AAAA,YAAA,KAAA;AAAA,YAAM,GAAA;AAAA,YAAE;AAAA,WAAA,EACX,CAAA;AAAA,0BACAC,GAAAA;AAAA,YAAC,QAAA;AAAA,YAAA;AAAA,cACC,IAAA,EAAK,QAAA;AAAA,cACL,UAAU,MAAA,KAAW,MAAA;AAAA,cACrB,YAAA,EAAY,QAAQ,IAAA,IAAQ,cAAA;AAAA,cAC5B,KAAA,EAAO,QAAQ,IAAA,IAAQ,cAAA;AAAA,cACvB,OAAA,EAAS,CAAC,KAAA,KAAU,MAAA,CAAO,OAAO,MAAM,CAAA;AAAA,cACxC,SAAA,EAAU,0QAAA;AAAA,cAEV,0BAAAA,GAAAA,CAAC,YAAA,EAAA,EAAa,SAAA,EAAU,UAAA,EAAW,eAAW,IAAA,EAAC;AAAA;AAAA;AACjD;AAAA;AAAA,KACF;AAAA,EAEJ;AACF;ACVA,IAAM,iBAAA,GACJ,2QAAA;AAQK,IAAM,WAAA,GAAoBC,KAAA,CAAA,UAAA;AAAA,EAC/B,SAASC,YAAAA,CACP;AAAA,IACE,IAAA;AAAA,IACA,OAAA;AAAA,IACA,SAAA;AAAA,IACA,QAAA;AAAA,IACA,MAAA,GAAS,MAAA;AAAA,IACT,KAAA;AAAA,IACA,OAAA;AAAA,IACA,OAAA,GAAU,KAAA;AAAA,IACV,QAAA;AAAA,IACA,eAAA;AAAA,IACA,IAAA;AAAA,IACA,KAAA;AAAA,IACA,WAAA;AAAA,IACA,MAAA;AAAA,IACA,OAAA,GAAU,KAAA;AAAA,IACV;AAAA,KAEF,GAAA,EACA;AACA,IAAA,MAAM,SAAS,IAAA,KAAS,MAAA;AACxB,IAAA,MAAM,WAAW,IAAA,KAAS,QAAA;AAC1B,IAAA,MAAM,WAAA,GACJ,OAAA,KAAY,MAAA,KAAc,OAAA,CAAQ,WAAW,MAAA,IAAa,OAAA,CAAQ,MAAA,KAAW,MAAA,IAAa,QAAQ,YAAA,KAAiB,MAAA,IAAA,CAAc,OAAA,CAAQ,MAAA,EAAQ,UAAU,CAAA,IAAK,CAAA,CAAA;AAClK,IAAA,MAAM,YAAA,GAAe,EAAE,IAAA,EAAM,MAAA,EAAQ,IAAA,EAAM,QAAQ,MAAA,EAAQ,MAAA,EAAQ,SAAA,EAAW,MAAA,EAAQ,SAAA,EAAU;AAChG,IAAA,MAAM,cAAA,GAAiB,EAAE,KAAA,EAAO,MAAA,EAAQ,KAAA,EAAO,SAAA,EAAW,MAAA,EAAQ,SAAA,EAAW,QAAA,EAAU,MAAA,EAAQ,QAAA,EAAU,IAAA,EAAM,QAAQ,IAAA,EAAK;AAE5H,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,uBACEF,GAAAA;AAAA,QAAC,KAAA;AAAA,QAAA;AAAA,UACC,GAAA;AAAA,UACA,SAAA,EAAQ,cAAA;AAAA,UACR,WAAA,EAAW,IAAA;AAAA,UACX,SAAA,EAAW,EAAA,CAAG,qBAAA,EAAuB,SAAS,CAAA;AAAA,UAE9C,QAAA,kBAAAA,GAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,oHACZ,QAAA,EAAA,OAAA,EACH;AAAA;AAAA,OACF;AAAA,IAEJ;AAEA,IAAA,uBACED,IAAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACA,SAAA,EAAQ,cAAA;AAAA,QACR,WAAA,EAAW,IAAA;AAAA,QACX,aAAA,EAAa,MAAA;AAAA,QACb,WAAW,EAAA,CAAG,oCAAA,EAAsC,MAAA,GAAS,WAAA,GAAc,eAAe,SAAS,CAAA;AAAA,QAElG,QAAA,EAAA;AAAA,UAAA,WAAA,mBAAcC,GAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,kDAAA,EAAoD,uBAAY,CAAA,GAAS,IAAA;AAAA,0BACvGD,IAAAA;AAAA,YAAC,KAAA;AAAA,YAAA;AAAA,cACC,SAAA,EAAW,EAAA;AAAA,gBACT,4FAAA;AAAA,gBACA,OAAA,IAAW,eAAA;AAAA,gBACX,SACI,qFAAA,GACA;AAAA,eACN;AAAA,cAEC,QAAA,EAAA;AAAA,gBAAA,SAAA,mBAAYC,GAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,WAAA,EAAa,qBAAU,CAAA,GAAS,IAAA;AAAA,gBAC3D,0BACCA,GAAAA;AAAA,kBAAC,iBAAA;AAAA,kBAAA;AAAA,oBACC,OAAO,OAAA,CAAQ,KAAA;AAAA,oBACf,eAAe,OAAA,CAAQ,aAAA;AAAA,oBACvB,UAAU,OAAA,CAAQ,QAAA;AAAA,oBAClB,UAAU,OAAA,CAAQ,QAAA;AAAA,oBAClB,gBAAgB,OAAA,CAAQ,cAAA;AAAA,oBACxB,MAAA,EAAQ;AAAA;AAAA,oCAGVA,GAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,WAAW,QAAA,EAAA,OAAA,EAAQ,CAAA;AAAA,gBAEnC,MAAA,KAAW,aAAA,mBACVA,GAAAA,CAAC,MAAA,EAAA,EAAK,WAAU,wIAAA,EACb,QAAA,EAAA,MAAA,EAAQ,WAAA,IAAe,SAAA,EAC1B,CAAA,GACE,IAAA;AAAA,gBACH,MAAA,KAAW,OAAA,IAAW,KAAA,mBACrBD,IAAAA;AAAA,kBAAC,KAAA;AAAA,kBAAA;AAAA,oBACC,IAAA,EAAK,OAAA;AAAA,oBACL,SAAA,EAAU,2JAAA;AAAA,oBAEV,QAAA,EAAA;AAAA,sCAAAC,GAAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,iDAAA,EAAmD,gBAAM,IAAA,EAAK,CAAA;AAAA,sCAC9EA,GAAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,iCAAA,EAAmC,gBAAM,OAAA,EAAQ,CAAA;AAAA,sBAChE,MAAM,SAAA,IAAa,OAAA,EAAS,0BAC3BA,GAAAA,CAAC,UAAO,OAAA,EAAQ,SAAA,EAAU,MAAK,IAAA,EAAK,SAAA,EAAU,cAAa,OAAA,EAAS,OAAA,CAAQ,SACzE,QAAA,EAAA,MAAA,EAAQ,KAAA,IAAS,SACpB,CAAA,GACE;AAAA;AAAA;AAAA,iBACN,GACE,IAAA;AAAA,gBACH,wBAAQA,GAAAA,CAAC,SAAI,SAAA,EAAU,wBAAA,EAA0B,iBAAM,CAAA,GAAS,IAAA;AAAA,gBAChE,2BAAWA,GAAAA,CAAC,SAAI,SAAA,EAAU,uBAAA,EAAyB,UAAS,CAAA,GAAS,IAAA;AAAA,gBACrE,WAAA,IAAe,CAAC,OAAA,mBACfD,IAAAA;AAAA,kBAAC,KAAA;AAAA,kBAAA;AAAA,oBACC,SAAA,EAAW,EAAA;AAAA,sBACT,qPAAA;AAAA,sBACA,SAAS,SAAA,GAAY;AAAA,qBACvB;AAAA,oBAEC,QAAA,EAAA;AAAA,sBAAA,OAAA,EAAS,MAAA,mBACRC,GAAAA,CAAC,QAAA,EAAA,EAAO,IAAA,EAAK,QAAA,EAAS,SAAA,EAAW,iBAAA,EAAmB,YAAA,EAAY,MAAA,EAAQ,IAAA,IAAQ,MAAA,EAAQ,KAAA,EAAO,MAAA,EAAQ,IAAA,IAAQ,MAAA,EAAQ,OAAA,EAAS,OAAA,CAAQ,MAAA,EACtI,QAAA,kBAAAA,GAAAA,CAAC,IAAA,EAAA,EAAK,SAAA,EAAU,UAAA,EAAW,aAAA,EAAW,IAAA,EAAC,CAAA,EACzC,CAAA,GACE,IAAA;AAAA,sBACH,OAAA,EAAS,MAAA,mBACRA,GAAAA,CAAC,QAAA,EAAA,EAAO,IAAA,EAAK,QAAA,EAAS,SAAA,EAAW,iBAAA,EAAmB,YAAA,EAAY,MAAA,EAAQ,IAAA,IAAQ,MAAA,EAAQ,KAAA,EAAO,MAAA,EAAQ,IAAA,IAAQ,MAAA,EAAQ,OAAA,EAAS,OAAA,CAAQ,MAAA,EACtI,QAAA,kBAAAA,GAAAA,CAAC,MAAA,EAAA,EAAO,SAAA,EAAU,UAAA,EAAW,aAAA,EAAW,IAAA,EAAC,CAAA,EAC3C,CAAA,GACE,IAAA;AAAA,sBACH,OAAA,EAAS,YAAA,mBACRA,GAAAA,CAAC,QAAA,EAAA,EAAO,IAAA,EAAK,QAAA,EAAS,SAAA,EAAW,iBAAA,EAAmB,YAAA,EAAY,MAAA,EAAQ,UAAA,IAAc,YAAA,EAAc,KAAA,EAAO,MAAA,EAAQ,UAAA,IAAc,YAAA,EAAc,OAAA,EAAS,OAAA,CAAQ,YAAA,EAC9J,QAAA,kBAAAA,GAAAA,CAAC,SAAA,EAAA,EAAU,SAAA,EAAU,UAAA,EAAW,aAAA,EAAW,IAAA,EAAC,CAAA,EAC9C,CAAA,GACE,IAAA;AAAA,sBACH,OAAA,EAAS,MAAA,EAAQ,GAAA,CAAI,CAAC,0BACrBA,GAAAA;AAAA,wBAAC,QAAA;AAAA,wBAAA;AAAA,0BAEC,IAAA,EAAK,QAAA;AAAA,0BACL,SAAA,EAAW,EAAA,CAAG,iBAAA,EAAmB,KAAA,CAAM,UAAU,+BAA+B,CAAA;AAAA,0BAChF,cAAY,KAAA,CAAM,KAAA;AAAA,0BAClB,OAAO,KAAA,CAAM,KAAA;AAAA,0BACb,SAAS,KAAA,CAAM,OAAA;AAAA,0BAEd,gBAAM,IAAA,mBAAOA,IAAC,KAAA,CAAM,IAAA,EAAN,EAAW,SAAA,EAAU,UAAA,EAAW,aAAA,EAAW,IAAA,EAAC,oBAAKA,GAAAA,CAAC,iBAAc,SAAA,EAAU,UAAA,EAAW,eAAW,IAAA,EAAC;AAAA,yBAAA;AAAA,wBAP3G,KAAA,CAAM;AAAA,uBASd;AAAA;AAAA;AAAA,iBACH,GACE;AAAA;AAAA;AAAA,WACN;AAAA,UACC,uBAAOA,GAAAA,CAAC,SAAI,SAAA,EAAU,uFAAA,EAAyF,gBAAK,CAAA,GAAS,IAAA;AAAA,UAC7H,QAAA,IAAY,QAAA,CAAS,KAAA,GAAQ,CAAA,IAAK,kCACjCA,GAAAA,CAAC,sBAAA,EAAA,EAAuB,QAAA,EAAoB,UAAU,eAAA,EAAiB,MAAA,EAAQ,cAAA,EAAgB,SAAA,EAAU,eAAc,CAAA,GACrH;AAAA;AAAA;AAAA,KACN;AAAA,EAEJ;AACF","file":"chunk-BSIWLVDX.js","sourcesContent":["import * as React from 'react'\n\nimport { cn } from '../../lib/utils'\nimport { Button } from '../button/Button'\n\nexport interface ChatMessageEditorLabels {\n save: string\n cancel: string\n ariaLabel: string\n}\n\nexport interface ChatMessageEditorProps {\n value: string\n onValueChange: (value: string) => void\n /** Save & resend — branches the tree (family `edit` endpoint) and reruns the turn. */\n onSubmit: () => void\n onCancel: () => void\n submitDisabled?: boolean\n rows?: number\n labels?: Partial<ChatMessageEditorLabels>\n className?: string\n}\n\n/**\n * Inline user-prompt editor for the edit-and-resend flow (study pattern):\n * controlled textarea, Cmd/Ctrl+Enter saves, Escape cancels.\n */\nexport const ChatMessageEditor = React.forwardRef<HTMLDivElement, ChatMessageEditorProps>(\n function ChatMessageEditor(\n { value, onValueChange, onSubmit, onCancel, submitDisabled = false, rows = 3, labels, className },\n ref,\n ) {\n const onKeyDown: React.KeyboardEventHandler<HTMLTextAreaElement> = (event) => {\n if (event.key === 'Escape') {\n event.preventDefault()\n onCancel()\n return\n }\n if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) {\n event.preventDefault()\n if (!submitDisabled) {\n onSubmit()\n }\n }\n }\n\n return (\n <div ref={ref} data-as=\"chat-message-editor\" className={cn('flex w-full flex-col gap-2', className)}>\n <textarea\n value={value}\n rows={rows}\n aria-label={labels?.ariaLabel ?? 'Edit message'}\n onChange={(event) => onValueChange(event.target.value)}\n onKeyDown={onKeyDown}\n className=\"w-full resize-y rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] px-3 py-2 text-sm text-[var(--as-fg)] placeholder:text-[var(--as-muted-fg)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]\"\n />\n <div className=\"flex items-center justify-end gap-2\">\n <Button variant=\"ghost\" size=\"sm\" onClick={onCancel}>\n {labels?.cancel ?? 'Cancel'}\n </Button>\n <Button size=\"sm\" disabled={submitDisabled} onClick={onSubmit}>\n {labels?.save ?? 'Save & resend'}\n </Button>\n </div>\n </div>\n )\n },\n)\n","import * as React from 'react'\nimport { ChevronLeft, ChevronRight } from 'lucide-react'\n\nimport type { ChatMessageVariants } from '../chat-core/types'\nimport { cn } from '../../lib/utils'\n\nexport interface MessageVariantSwitcherLabels {\n group: string\n variantOf: (index: number, count: number) => string\n previous: string\n next: string\n}\n\nexport interface MessageVariantSwitcherProps {\n variants: ChatMessageVariants\n /** Flip the parent's active-child pointer to the sibling id (family `select` endpoint). */\n onSelect: (messageId: string) => void\n labels?: Partial<MessageVariantSwitcherLabels>\n className?: string\n}\n\n/**\n * The `‹ n/N ›` sibling-variant switcher under a branched message\n * (study's inline switcher, family branching contract).\n */\nexport const MessageVariantSwitcher = React.forwardRef<HTMLDivElement, MessageVariantSwitcherProps>(\n function MessageVariantSwitcher({ variants, onSelect, labels, className }, ref) {\n const { index, count, siblingIds } = variants\n const previousId = index > 1 ? siblingIds[index - 2] : undefined\n const nextId = index < count ? siblingIds[index] : undefined\n\n const select = (event: React.MouseEvent<HTMLButtonElement>, messageId: string | undefined) => {\n event.stopPropagation()\n if (messageId !== undefined) {\n onSelect(messageId)\n }\n }\n\n return (\n <div\n ref={ref}\n role=\"group\"\n aria-label={labels?.group ?? 'Message variants'}\n data-as=\"message-variant-switcher\"\n className={cn('flex items-center gap-0.5 text-xs text-[var(--as-muted-fg)]', className)}\n >\n <button\n type=\"button\"\n disabled={previousId === undefined}\n aria-label={labels?.previous ?? 'Previous variant'}\n title={labels?.previous ?? 'Previous variant'}\n onClick={(event) => select(event, previousId)}\n className=\"rounded-[var(--as-radius-sm)] p-0.5 transition-colors hover:bg-[var(--as-surface-raised)] hover:text-[var(--as-fg)] disabled:pointer-events-none disabled:opacity-30 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <ChevronLeft className=\"size-3.5\" aria-hidden />\n </button>\n <span className=\"tabular-nums\" title={labels?.variantOf?.(index, count) ?? `Variant ${index} of ${count}`}>\n {index}/{count}\n </span>\n <button\n type=\"button\"\n disabled={nextId === undefined}\n aria-label={labels?.next ?? 'Next variant'}\n title={labels?.next ?? 'Next variant'}\n onClick={(event) => select(event, nextId)}\n className=\"rounded-[var(--as-radius-sm)] p-0.5 transition-colors hover:bg-[var(--as-surface-raised)] hover:text-[var(--as-fg)] disabled:pointer-events-none disabled:opacity-30 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <ChevronRight className=\"size-3.5\" aria-hidden />\n </button>\n </div>\n )\n },\n)\n","import * as React from 'react'\nimport { Copy, Pencil, RotateCcw, TriangleAlert } from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\n\nimport { cn } from '../../lib/utils'\nimport { Button } from '../button/Button'\nimport type { ChatError, ChatMessageVariants, ChatRole } from '../chat-core/types'\nimport { ChatMessageEditor, type ChatMessageEditorLabels } from './ChatMessageEditor'\nimport { MessageVariantSwitcher, type MessageVariantSwitcherLabels } from './MessageVariantSwitcher'\n\nexport interface ChatMessageAction {\n key: string\n label: string\n icon?: LucideIcon\n onClick: () => void\n danger?: boolean\n}\n\nexport interface ChatMessageActions {\n onCopy?: () => void\n onEdit?: () => void\n onRegenerate?: () => void\n onRetry?: () => void\n /** Extra icon actions (app-specific). */\n extras?: ChatMessageAction[]\n}\n\nexport interface ChatMessageLabels {\n copy: string\n edit: string\n regenerate: string\n retry: string\n interrupted: string\n error: string\n}\n\nexport interface ChatMessageProps {\n role: ChatRole\n /** Usually a `MarkdownSurface`; any node graph renders here. */\n content: React.ReactNode\n /** Thinking block slot (usually `ChatReasoning`). */\n reasoning?: React.ReactNode\n /** Tool cards / HITL cards below the content. */\n children?: React.ReactNode\n status?: 'streaming' | 'done' | 'error' | 'interrupted'\n error?: ChatError\n actions?: ChatMessageActions\n /** Edit-and-resend mode for user messages (renders `ChatMessageEditor`). */\n editing?: false | { value: string; onValueChange: (value: string) => void; onSubmit: () => void; onCancel: () => void; submitDisabled?: boolean }\n variants?: ChatMessageVariants\n onSelectVariant?: (messageId: string) => void\n /** Badge row (model, latency, tokens — app-defined). */\n meta?: React.ReactNode\n /** Deep-link chip row (career references, health citations). */\n chips?: React.ReactNode\n /** Attachment rail above the content. */\n attachments?: React.ReactNode\n labels?: Partial<ChatMessageLabels & ChatMessageEditorLabels & MessageVariantSwitcherLabels>\n compact?: boolean\n className?: string\n}\n\nconst actionButtonClass =\n 'rounded-[var(--as-radius-sm)] p-1 text-[var(--as-muted-fg)] transition-colors hover:bg-[var(--as-surface-raised)] hover:text-[var(--as-fg)] focus-visible:opacity-100 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]'\n\n/**\n * The family chat bubble: role-aligned, hover action row (copy / edit /\n * regenerate + app extras), variant switcher under branched messages,\n * reasoning and below-content slots, inline edit-and-resend, error +\n * interrupted states. Presentational — state lives app-side.\n */\nexport const ChatMessage = React.forwardRef<HTMLDivElement, ChatMessageProps>(\n function ChatMessage(\n {\n role,\n content,\n reasoning,\n children,\n status = 'done',\n error,\n actions,\n editing = false,\n variants,\n onSelectVariant,\n meta,\n chips,\n attachments,\n labels,\n compact = false,\n className,\n },\n ref,\n ) {\n const isUser = role === 'user'\n const isSystem = role === 'system'\n const showActions =\n actions !== undefined && (actions.onCopy !== undefined || actions.onEdit !== undefined || actions.onRegenerate !== undefined || (actions.extras?.length ?? 0) > 0)\n const editorLabels = { save: labels?.save, cancel: labels?.cancel, ariaLabel: labels?.ariaLabel }\n const switcherLabels = { group: labels?.group, variantOf: labels?.variantOf, previous: labels?.previous, next: labels?.next }\n\n if (isSystem) {\n return (\n <div\n ref={ref}\n data-as=\"chat-message\"\n data-role={role}\n className={cn('flex justify-center', className)}\n >\n <div className=\"rounded-full border border-[var(--as-border)] bg-[var(--as-surface)] px-3 py-1 text-xs text-[var(--as-muted-fg)]\">\n {content}\n </div>\n </div>\n )\n }\n\n return (\n <div\n ref={ref}\n data-as=\"chat-message\"\n data-role={role}\n data-status={status}\n className={cn('group/message flex w-full flex-col', isUser ? 'items-end' : 'items-start', className)}\n >\n {attachments ? <div className=\"mb-1.5 flex w-full flex-wrap justify-end gap-1.5\">{attachments}</div> : null}\n <div\n className={cn(\n 'relative flex max-w-[92%] flex-col gap-1.5 rounded-[var(--as-radius-lg)] px-3 py-2 text-sm',\n compact && 'px-2.5 py-1.5',\n isUser\n ? 'rounded-br-[var(--as-radius-sm)] bg-[var(--as-primary)] text-[var(--as-primary-fg)]'\n : 'rounded-bl-[var(--as-radius-sm)] border border-[var(--as-border)] bg-[var(--as-surface-raised)] text-[var(--as-fg)]',\n )}\n >\n {reasoning ? <div className=\"not-prose\">{reasoning}</div> : null}\n {editing ? (\n <ChatMessageEditor\n value={editing.value}\n onValueChange={editing.onValueChange}\n onSubmit={editing.onSubmit}\n onCancel={editing.onCancel}\n submitDisabled={editing.submitDisabled}\n labels={editorLabels}\n />\n ) : (\n <div className=\"min-w-0\">{content}</div>\n )}\n {status === 'interrupted' ? (\n <span className=\"self-start rounded-full bg-[var(--as-warning)]/15 px-2 py-0.5 text-[10px] font-medium uppercase tracking-wide text-[var(--as-warning)]\">\n {labels?.interrupted ?? 'Stopped'}\n </span>\n ) : null}\n {status === 'error' && error ? (\n <div\n role=\"alert\"\n className=\"flex flex-col gap-1 rounded-[var(--as-radius)] border border-[var(--as-danger)]/40 bg-[var(--as-danger)]/10 px-2.5 py-1.5 text-xs text-[var(--as-danger)]\"\n >\n <span className=\"font-mono font-semibold uppercase tracking-wide\">{error.code}</span>\n <span className=\"whitespace-pre-wrap break-words\">{error.message}</span>\n {error.retryable && actions?.onRetry ? (\n <Button variant=\"outline\" size=\"sm\" className=\"self-start\" onClick={actions.onRetry}>\n {labels?.retry ?? 'Retry'}\n </Button>\n ) : null}\n </div>\n ) : null}\n {chips ? <div className=\"flex flex-wrap gap-1.5\">{chips}</div> : null}\n {children ? <div className=\"flex flex-col gap-1.5\">{children}</div> : null}\n {showActions && !editing ? (\n <div\n className={cn(\n 'absolute top-1 flex items-center gap-0.5 rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] p-0.5 opacity-0 shadow-[var(--as-shadow-1)] transition-opacity focus-within:opacity-100 group-hover/message:opacity-100',\n isUser ? '-left-9' : '-right-9',\n )}\n >\n {actions?.onCopy ? (\n <button type=\"button\" className={actionButtonClass} aria-label={labels?.copy ?? 'Copy'} title={labels?.copy ?? 'Copy'} onClick={actions.onCopy}>\n <Copy className=\"size-3.5\" aria-hidden />\n </button>\n ) : null}\n {actions?.onEdit ? (\n <button type=\"button\" className={actionButtonClass} aria-label={labels?.edit ?? 'Edit'} title={labels?.edit ?? 'Edit'} onClick={actions.onEdit}>\n <Pencil className=\"size-3.5\" aria-hidden />\n </button>\n ) : null}\n {actions?.onRegenerate ? (\n <button type=\"button\" className={actionButtonClass} aria-label={labels?.regenerate ?? 'Regenerate'} title={labels?.regenerate ?? 'Regenerate'} onClick={actions.onRegenerate}>\n <RotateCcw className=\"size-3.5\" aria-hidden />\n </button>\n ) : null}\n {actions?.extras?.map((extra) => (\n <button\n key={extra.key}\n type=\"button\"\n className={cn(actionButtonClass, extra.danger && 'hover:text-[var(--as-danger)]')}\n aria-label={extra.label}\n title={extra.label}\n onClick={extra.onClick}\n >\n {extra.icon ? <extra.icon className=\"size-3.5\" aria-hidden /> : <TriangleAlert className=\"size-3.5\" aria-hidden />}\n </button>\n ))}\n </div>\n ) : null}\n </div>\n {meta ? <div className=\"mt-0.5 flex flex-wrap items-center gap-1.5 px-1 text-[10px] text-[var(--as-muted-fg)]\">{meta}</div> : null}\n {variants && variants.count > 1 && onSelectVariant ? (\n <MessageVariantSwitcher variants={variants} onSelect={onSelectVariant} labels={switcherLabels} className=\"mt-0.5 px-1\" />\n ) : null}\n </div>\n )\n },\n)\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/components/chat-message/ChatMessageEditor.tsx","../src/components/chat-message/MessageVariantSwitcher.tsx","../src/components/chat-message/ChatMessage.tsx"],"names":["ChatMessageEditor","React2","MessageVariantSwitcher","jsxs","jsx","React3","ChatMessage"],"mappings":"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","file":"chunk-FGYGWZ6S.js","sourcesContent":["import * as React from 'react'\n\nimport { cn } from '../../lib/utils'\nimport { Button } from '../button/Button'\n\nexport interface ChatMessageEditorLabels {\n save: string\n cancel: string\n ariaLabel: string\n}\n\nexport interface ChatMessageEditorProps {\n value: string\n onValueChange: (value: string) => void\n /** Save & resend — branches the tree (family `edit` endpoint) and reruns the turn. */\n onSubmit: () => void\n onCancel: () => void\n submitDisabled?: boolean\n rows?: number\n labels?: Partial<ChatMessageEditorLabels>\n className?: string\n}\n\n/**\n * Inline user-prompt editor for the edit-and-resend flow (study pattern):\n * controlled textarea, Cmd/Ctrl+Enter saves, Escape cancels.\n */\nexport const ChatMessageEditor = React.forwardRef<HTMLDivElement, ChatMessageEditorProps>(\n function ChatMessageEditor(\n { value, onValueChange, onSubmit, onCancel, submitDisabled = false, rows = 3, labels, className },\n ref,\n ) {\n const onKeyDown: React.KeyboardEventHandler<HTMLTextAreaElement> = (event) => {\n if (event.key === 'Escape') {\n event.preventDefault()\n onCancel()\n return\n }\n if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) {\n event.preventDefault()\n if (!submitDisabled) {\n onSubmit()\n }\n }\n }\n\n return (\n <div ref={ref} data-as=\"chat-message-editor\" className={cn('flex w-full flex-col gap-2', className)}>\n <textarea\n value={value}\n rows={rows}\n aria-label={labels?.ariaLabel ?? 'Edit message'}\n onChange={(event) => onValueChange(event.target.value)}\n onKeyDown={onKeyDown}\n className=\"w-full resize-y rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] px-3 py-2 text-sm text-[var(--as-fg)] placeholder:text-[var(--as-muted-fg)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]\"\n />\n <div className=\"flex items-center justify-end gap-2\">\n <Button variant=\"ghost\" size=\"sm\" onClick={onCancel}>\n {labels?.cancel ?? 'Cancel'}\n </Button>\n <Button size=\"sm\" disabled={submitDisabled} onClick={onSubmit}>\n {labels?.save ?? 'Save & resend'}\n </Button>\n </div>\n </div>\n )\n },\n)\n","import * as React from 'react'\nimport { ChevronLeft, ChevronRight } from 'lucide-react'\n\nimport type { ChatMessageVariants } from '../chat-core/types'\nimport { cn } from '../../lib/utils'\n\nexport interface MessageVariantSwitcherLabels {\n group: string\n variantOf: (index: number, count: number) => string\n previous: string\n next: string\n}\n\nexport interface MessageVariantSwitcherProps {\n variants: ChatMessageVariants\n /** Flip the parent's active-child pointer to the sibling id (family `select` endpoint). */\n onSelect: (messageId: string) => void\n labels?: Partial<MessageVariantSwitcherLabels>\n className?: string\n}\n\n/**\n * The `‹ n/N ›` sibling-variant switcher under a branched message\n * (study's inline switcher, family branching contract).\n */\nexport const MessageVariantSwitcher = React.forwardRef<HTMLDivElement, MessageVariantSwitcherProps>(\n function MessageVariantSwitcher({ variants, onSelect, labels, className }, ref) {\n const { index, count, siblingIds } = variants\n const previousId = index > 1 ? siblingIds[index - 2] : undefined\n const nextId = index < count ? siblingIds[index] : undefined\n\n const select = (event: React.MouseEvent<HTMLButtonElement>, messageId: string | undefined) => {\n event.stopPropagation()\n if (messageId !== undefined) {\n onSelect(messageId)\n }\n }\n\n return (\n <div\n ref={ref}\n role=\"group\"\n aria-label={labels?.group ?? 'Message variants'}\n data-as=\"message-variant-switcher\"\n className={cn('flex items-center gap-0.5 text-xs text-[var(--as-muted-fg)]', className)}\n >\n <button\n type=\"button\"\n disabled={previousId === undefined}\n aria-label={labels?.previous ?? 'Previous variant'}\n title={labels?.previous ?? 'Previous variant'}\n onClick={(event) => select(event, previousId)}\n className=\"rounded-[var(--as-radius-sm)] p-0.5 transition-colors hover:bg-[var(--as-surface-raised)] hover:text-[var(--as-fg)] disabled:pointer-events-none disabled:opacity-30 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <ChevronLeft className=\"size-3.5\" aria-hidden />\n </button>\n <span className=\"tabular-nums\" title={labels?.variantOf?.(index, count) ?? `Variant ${index} of ${count}`}>\n {index}/{count}\n </span>\n <button\n type=\"button\"\n disabled={nextId === undefined}\n aria-label={labels?.next ?? 'Next variant'}\n title={labels?.next ?? 'Next variant'}\n onClick={(event) => select(event, nextId)}\n className=\"rounded-[var(--as-radius-sm)] p-0.5 transition-colors hover:bg-[var(--as-surface-raised)] hover:text-[var(--as-fg)] disabled:pointer-events-none disabled:opacity-30 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <ChevronRight className=\"size-3.5\" aria-hidden />\n </button>\n </div>\n )\n },\n)\n","import * as React from 'react'\nimport { Copy, Pencil, RotateCcw, TriangleAlert } from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\n\nimport { cn } from '../../lib/utils'\nimport { Button } from '../button/Button'\nimport type { ChatError, ChatMessageVariants, ChatRole } from '../chat-core/types'\nimport { ChatMessageEditor, type ChatMessageEditorLabels } from './ChatMessageEditor'\nimport { MessageVariantSwitcher, type MessageVariantSwitcherLabels } from './MessageVariantSwitcher'\n\nexport interface ChatMessageAction {\n key: string\n label: string\n icon?: LucideIcon\n onClick: () => void\n danger?: boolean\n}\n\nexport interface ChatMessageActions {\n onCopy?: () => void\n onEdit?: () => void\n onRegenerate?: () => void\n onRetry?: () => void\n /** Extra icon actions (app-specific). */\n extras?: ChatMessageAction[]\n}\n\nexport interface ChatMessageLabels {\n copy: string\n edit: string\n regenerate: string\n retry: string\n interrupted: string\n error: string\n}\n\nexport interface ChatMessageProps {\n role: ChatRole\n /** Usually a `MarkdownSurface`; any node graph renders here. */\n content: React.ReactNode\n /** Thinking block slot (usually `ChatReasoning`). */\n reasoning?: React.ReactNode\n /** Tool cards / HITL cards below the content. */\n children?: React.ReactNode\n status?: 'streaming' | 'done' | 'error' | 'interrupted'\n error?: ChatError\n actions?: ChatMessageActions\n /** Edit-and-resend mode for user messages (renders `ChatMessageEditor`). */\n editing?: false | { value: string; onValueChange: (value: string) => void; onSubmit: () => void; onCancel: () => void; submitDisabled?: boolean }\n variants?: ChatMessageVariants\n onSelectVariant?: (messageId: string) => void\n /** Badge row (model, latency, tokens — app-defined). */\n meta?: React.ReactNode\n /** Deep-link chip row (career references, health citations). */\n chips?: React.ReactNode\n /** Attachment rail above the content. */\n attachments?: React.ReactNode\n labels?: Partial<ChatMessageLabels & ChatMessageEditorLabels & MessageVariantSwitcherLabels>\n compact?: boolean\n className?: string\n}\n\nconst actionButtonClass =\n 'rounded-[var(--as-radius-sm)] p-1 text-[var(--as-muted-fg)] transition-colors hover:bg-[var(--as-surface-raised)] hover:text-[var(--as-fg)] focus-visible:opacity-100 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]'\n\n/**\n * The family chat bubble: role-aligned, hover action row (copy / edit /\n * regenerate + app extras), variant switcher under branched messages,\n * reasoning and below-content slots, inline edit-and-resend, error +\n * interrupted states. Presentational — state lives app-side.\n */\nexport const ChatMessage = React.forwardRef<HTMLDivElement, ChatMessageProps>(\n function ChatMessage(\n {\n role,\n content,\n reasoning,\n children,\n status = 'done',\n error,\n actions,\n editing = false,\n variants,\n onSelectVariant,\n meta,\n chips,\n attachments,\n labels,\n compact = false,\n className,\n },\n ref,\n ) {\n const isUser = role === 'user'\n const isSystem = role === 'system'\n const showActions =\n actions !== undefined && (actions.onCopy !== undefined || actions.onEdit !== undefined || actions.onRegenerate !== undefined || (actions.extras?.length ?? 0) > 0)\n const editorLabels = { save: labels?.save, cancel: labels?.cancel, ariaLabel: labels?.ariaLabel }\n const switcherLabels = { group: labels?.group, variantOf: labels?.variantOf, previous: labels?.previous, next: labels?.next }\n\n if (isSystem) {\n return (\n <div\n ref={ref}\n data-as=\"chat-message\"\n data-role={role}\n className={cn('flex justify-center', className)}\n >\n <div className=\"rounded-full border border-[var(--as-border)] bg-[var(--as-surface)] px-3 py-1 text-xs text-[var(--as-muted-fg)]\">\n {content}\n </div>\n </div>\n )\n }\n\n return (\n <div\n ref={ref}\n data-as=\"chat-message\"\n data-role={role}\n data-status={status}\n className={cn('group/message flex w-full flex-col', isUser ? 'items-end' : 'items-start', className)}\n >\n {attachments ? <div className=\"mb-1.5 flex w-full flex-wrap justify-end gap-1.5\">{attachments}</div> : null}\n <div\n className={cn(\n 'relative flex max-w-[92%] flex-col gap-1.5 rounded-[var(--as-radius-lg)] px-3 py-2 text-sm',\n compact && 'px-2.5 py-1.5',\n isUser\n ? 'rounded-br-[var(--as-radius-sm)] bg-[var(--as-primary)] text-[var(--as-primary-fg)]'\n : 'rounded-bl-[var(--as-radius-sm)] bg-[var(--as-muted)] text-[var(--as-fg)]',\n )}\n >\n {reasoning ? <div className=\"not-prose\">{reasoning}</div> : null}\n {editing ? (\n <ChatMessageEditor\n value={editing.value}\n onValueChange={editing.onValueChange}\n onSubmit={editing.onSubmit}\n onCancel={editing.onCancel}\n submitDisabled={editing.submitDisabled}\n labels={editorLabels}\n />\n ) : (\n <div className=\"min-w-0\">{content}</div>\n )}\n {status === 'interrupted' ? (\n <span className=\"self-start rounded-full bg-[var(--as-warning)]/15 px-2 py-0.5 text-[10px] font-medium uppercase tracking-wide text-[var(--as-warning)]\">\n {labels?.interrupted ?? 'Stopped'}\n </span>\n ) : null}\n {status === 'error' && error ? (\n <div\n role=\"alert\"\n className=\"flex flex-col gap-1 rounded-[var(--as-radius)] border border-[var(--as-danger)]/40 bg-[var(--as-danger)]/10 px-2.5 py-1.5 text-xs text-[var(--as-danger)]\"\n >\n <span className=\"font-mono font-semibold uppercase tracking-wide\">{error.code}</span>\n <span className=\"whitespace-pre-wrap break-words\">{error.message}</span>\n {error.retryable && actions?.onRetry ? (\n <Button variant=\"outline\" size=\"sm\" className=\"self-start\" onClick={actions.onRetry}>\n {labels?.retry ?? 'Retry'}\n </Button>\n ) : null}\n </div>\n ) : null}\n {chips ? <div className=\"flex flex-wrap gap-1.5\">{chips}</div> : null}\n {children ? <div className=\"flex flex-col gap-1.5\">{children}</div> : null}\n {showActions && !editing ? (\n <div\n className={cn(\n 'absolute top-1 flex items-center gap-0.5 rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] p-0.5 opacity-0 shadow-[var(--as-shadow-1)] transition-opacity focus-within:opacity-100 group-hover/message:opacity-100',\n isUser ? '-left-9' : '-right-9',\n )}\n >\n {actions?.onCopy ? (\n <button type=\"button\" className={actionButtonClass} aria-label={labels?.copy ?? 'Copy'} title={labels?.copy ?? 'Copy'} onClick={actions.onCopy}>\n <Copy className=\"size-3.5\" aria-hidden />\n </button>\n ) : null}\n {actions?.onEdit ? (\n <button type=\"button\" className={actionButtonClass} aria-label={labels?.edit ?? 'Edit'} title={labels?.edit ?? 'Edit'} onClick={actions.onEdit}>\n <Pencil className=\"size-3.5\" aria-hidden />\n </button>\n ) : null}\n {actions?.onRegenerate ? (\n <button type=\"button\" className={actionButtonClass} aria-label={labels?.regenerate ?? 'Regenerate'} title={labels?.regenerate ?? 'Regenerate'} onClick={actions.onRegenerate}>\n <RotateCcw className=\"size-3.5\" aria-hidden />\n </button>\n ) : null}\n {actions?.extras?.map((extra) => (\n <button\n key={extra.key}\n type=\"button\"\n className={cn(actionButtonClass, extra.danger && 'hover:text-[var(--as-danger)]')}\n aria-label={extra.label}\n title={extra.label}\n onClick={extra.onClick}\n >\n {extra.icon ? <extra.icon className=\"size-3.5\" aria-hidden /> : <TriangleAlert className=\"size-3.5\" aria-hidden />}\n </button>\n ))}\n </div>\n ) : null}\n </div>\n {meta ? <div className=\"mt-0.5 flex flex-wrap items-center gap-1.5 px-1 text-[10px] text-[var(--as-muted-fg)]\">{meta}</div> : null}\n {variants && variants.count > 1 && onSelectVariant ? (\n <MessageVariantSwitcher variants={variants} onSelect={onSelectVariant} labels={switcherLabels} className=\"mt-0.5 px-1\" />\n ) : null}\n </div>\n )\n },\n)\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ChatMessage } from './chunk-
|
|
1
|
+
import { ChatMessage } from './chunk-FGYGWZ6S.js';
|
|
2
2
|
import { MarkdownSurface } from './chunk-DZBTJZZV.js';
|
|
3
3
|
import { cn } from './chunk-BLJWR4ZV.js';
|
|
4
4
|
import * as React from 'react';
|
|
@@ -130,5 +130,5 @@ var ChatTranscript = React.forwardRef(
|
|
|
130
130
|
);
|
|
131
131
|
|
|
132
132
|
export { ChatTranscript };
|
|
133
|
-
//# sourceMappingURL=chunk-
|
|
134
|
-
//# sourceMappingURL=chunk-
|
|
133
|
+
//# sourceMappingURL=chunk-FS7M7PNS.js.map
|
|
134
|
+
//# sourceMappingURL=chunk-FS7M7PNS.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/chat-transcript/ChatTranscript.tsx"],"names":["ChatTranscript"],"mappings":"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","file":"chunk-BVJT67B5.js","sourcesContent":["import * as React from 'react'\nimport { ArrowDown } from 'lucide-react'\nimport { useVirtualizer } from '@tanstack/react-virtual'\n\nimport { cn } from '../../lib/utils'\nimport { ChatMessage } from '../chat-message/ChatMessage'\nimport { MarkdownSurface } from '../chat-markdown/MarkdownSurface'\nimport type { ChatMessageView } from '../chat-core/types'\n\nexport interface ChatTranscriptLabels {\n log: string\n scrollToBottom: string\n empty: string\n replied: string\n}\n\nexport interface ChatTranscriptProps {\n items: ChatMessageView[]\n /** Override the default `ChatMessage` + `MarkdownSurface` rendering. */\n renderItem?: (message: ChatMessageView, index: number) => React.ReactNode\n /** Streaming tail — the live turn bubble (or any node). */\n live?: React.ReactNode\n emptyState?: React.ReactNode\n /** Stick to bottom while the user hasn't scrolled away. Default true. */\n autoScroll?: boolean\n /** Distance from the bottom that still counts as \"at the bottom\". Default 120 px. */\n scrollThresholdPx?: number\n /** Virtualize long transcripts (dynamic row measurement). Default false. */\n virtualized?: boolean\n labels?: Partial<ChatTranscriptLabels>\n className?: string\n}\n\nfunction defaultRenderItem(message: ChatMessageView) {\n return (\n <ChatMessage\n role={message.role}\n status={message.status}\n content={<MarkdownSurface value={message.content} streaming={message.status === 'streaming'} />}\n variants={message.variants}\n />\n )\n}\n\n/**\n * The conversation list: `role=\"log\"` with polite turn-completion\n * announcements, stick-to-bottom auto-scroll with a jump-to-latest pill,\n * optional virtualization for thousand-message threads.\n */\nexport const ChatTranscript = React.forwardRef<HTMLDivElement, ChatTranscriptProps>(\n function ChatTranscript(\n {\n items,\n renderItem = defaultRenderItem,\n live,\n emptyState,\n autoScroll = true,\n scrollThresholdPx = 120,\n virtualized = false,\n labels,\n className,\n },\n ref,\n ) {\n const scrollRef = React.useRef<HTMLDivElement | null>(null)\n const atBottomRef = React.useRef(true)\n const [showJump, setShowJump] = React.useState(false)\n const [announcement, setAnnouncement] = React.useState('')\n const previousCountRef = React.useRef(-1)\n\n const onScroll = () => {\n const element = scrollRef.current\n if (element === null) {\n return\n }\n const distance = element.scrollHeight - element.scrollTop - element.clientHeight\n const atBottom = distance <= scrollThresholdPx\n atBottomRef.current = atBottom\n setShowJump(element.scrollHeight > element.clientHeight && !atBottom)\n }\n\n React.useEffect(() => {\n const element = scrollRef.current\n if (element === null || !autoScroll || !atBottomRef.current) {\n return\n }\n const frame = requestAnimationFrame(() => {\n element.scrollTop = element.scrollHeight\n })\n return () => cancelAnimationFrame(frame)\n })\n\n React.useEffect(() => {\n if (previousCountRef.current === -1) {\n previousCountRef.current = items.length\n return\n }\n if (items.length > previousCountRef.current) {\n const added = items[items.length - 1]\n previousCountRef.current = items.length\n if (added && added.role !== 'user') {\n setAnnouncement(labels?.replied ?? 'Assistant replied')\n }\n return\n }\n previousCountRef.current = items.length\n }, [items, labels])\n\n const scrollToBottom = () => {\n const element = scrollRef.current\n if (element !== null) {\n element.scrollTo({ top: element.scrollHeight, behavior: 'smooth' })\n }\n }\n\n const rowCount = items.length + (live !== undefined ? 1 : 0)\n const virtualizer = useVirtualizer({\n count: virtualized ? rowCount : 0,\n getScrollElement: () => scrollRef.current,\n estimateSize: () => 120,\n overscan: 8,\n initialRect: { width: 480, height: 600 },\n })\n\n return (\n <div ref={ref} data-as=\"chat-transcript\" className={cn('relative min-h-0 flex-1', className)}>\n <div\n ref={scrollRef}\n onScroll={onScroll}\n role=\"log\"\n aria-label={labels?.log ?? 'Conversation'}\n aria-busy={live !== undefined}\n className=\"h-full overflow-y-auto overscroll-contain px-1 py-2\"\n >\n {items.length === 0 && live === undefined ? (\n <div className=\"flex h-full min-h-40 items-center justify-center p-4 text-sm text-[var(--as-muted-fg)]\">\n {emptyState ?? (labels?.empty ?? 'Start the conversation')}\n </div>\n ) : virtualized ? (\n <div style={{ height: virtualizer.getTotalSize(), position: 'relative' }}>\n {virtualizer.getVirtualItems().map((row) => {\n const isLive = row.index >= items.length\n const node = isLive ? live : renderItem(items[row.index]!, row.index)\n return (\n <div\n key={row.key}\n data-index={row.index}\n ref={virtualizer.measureElement}\n style={{ position: 'absolute', top: 0, left: 0, width: '100%', transform: `translateY(${row.start}px)` }}\n className=\"px-2 py-1.5\"\n >\n {node}\n </div>\n )\n })}\n </div>\n ) : (\n <div className=\"flex flex-col gap-3 px-2\">\n {items.map((message, index) => (\n <div key={message.id}>{renderItem(message, index)}</div>\n ))}\n {live !== undefined ? <div data-as=\"chat-transcript-live\">{live}</div> : null}\n </div>\n )}\n </div>\n <div aria-live=\"polite\" className=\"sr-only\">\n {announcement}\n </div>\n {showJump ? (\n <button\n type=\"button\"\n onClick={scrollToBottom}\n aria-label={labels?.scrollToBottom ?? 'Scroll to latest'}\n title={labels?.scrollToBottom ?? 'Scroll to latest'}\n className=\"absolute bottom-3 left-1/2 flex size-8 -translate-x-1/2 items-center justify-center rounded-full border border-[var(--as-border)] bg-[var(--as-surface-raised)] text-[var(--as-fg)] shadow-[var(--as-shadow-2)] transition-colors hover:bg-[var(--as-secondary)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <ArrowDown className=\"size-4\" aria-hidden />\n </button>\n ) : null}\n </div>\n )\n },\n)\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/components/chat-transcript/ChatTranscript.tsx"],"names":["ChatTranscript"],"mappings":"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","file":"chunk-FS7M7PNS.js","sourcesContent":["import * as React from 'react'\nimport { ArrowDown } from 'lucide-react'\nimport { useVirtualizer } from '@tanstack/react-virtual'\n\nimport { cn } from '../../lib/utils'\nimport { ChatMessage } from '../chat-message/ChatMessage'\nimport { MarkdownSurface } from '../chat-markdown/MarkdownSurface'\nimport type { ChatMessageView } from '../chat-core/types'\n\nexport interface ChatTranscriptLabels {\n log: string\n scrollToBottom: string\n empty: string\n replied: string\n}\n\nexport interface ChatTranscriptProps {\n items: ChatMessageView[]\n /** Override the default `ChatMessage` + `MarkdownSurface` rendering. */\n renderItem?: (message: ChatMessageView, index: number) => React.ReactNode\n /** Streaming tail — the live turn bubble (or any node). */\n live?: React.ReactNode\n emptyState?: React.ReactNode\n /** Stick to bottom while the user hasn't scrolled away. Default true. */\n autoScroll?: boolean\n /** Distance from the bottom that still counts as \"at the bottom\". Default 120 px. */\n scrollThresholdPx?: number\n /** Virtualize long transcripts (dynamic row measurement). Default false. */\n virtualized?: boolean\n labels?: Partial<ChatTranscriptLabels>\n className?: string\n}\n\nfunction defaultRenderItem(message: ChatMessageView) {\n return (\n <ChatMessage\n role={message.role}\n status={message.status}\n content={<MarkdownSurface value={message.content} streaming={message.status === 'streaming'} />}\n variants={message.variants}\n />\n )\n}\n\n/**\n * The conversation list: `role=\"log\"` with polite turn-completion\n * announcements, stick-to-bottom auto-scroll with a jump-to-latest pill,\n * optional virtualization for thousand-message threads.\n */\nexport const ChatTranscript = React.forwardRef<HTMLDivElement, ChatTranscriptProps>(\n function ChatTranscript(\n {\n items,\n renderItem = defaultRenderItem,\n live,\n emptyState,\n autoScroll = true,\n scrollThresholdPx = 120,\n virtualized = false,\n labels,\n className,\n },\n ref,\n ) {\n const scrollRef = React.useRef<HTMLDivElement | null>(null)\n const atBottomRef = React.useRef(true)\n const [showJump, setShowJump] = React.useState(false)\n const [announcement, setAnnouncement] = React.useState('')\n const previousCountRef = React.useRef(-1)\n\n const onScroll = () => {\n const element = scrollRef.current\n if (element === null) {\n return\n }\n const distance = element.scrollHeight - element.scrollTop - element.clientHeight\n const atBottom = distance <= scrollThresholdPx\n atBottomRef.current = atBottom\n setShowJump(element.scrollHeight > element.clientHeight && !atBottom)\n }\n\n React.useEffect(() => {\n const element = scrollRef.current\n if (element === null || !autoScroll || !atBottomRef.current) {\n return\n }\n const frame = requestAnimationFrame(() => {\n element.scrollTop = element.scrollHeight\n })\n return () => cancelAnimationFrame(frame)\n })\n\n React.useEffect(() => {\n if (previousCountRef.current === -1) {\n previousCountRef.current = items.length\n return\n }\n if (items.length > previousCountRef.current) {\n const added = items[items.length - 1]\n previousCountRef.current = items.length\n if (added && added.role !== 'user') {\n setAnnouncement(labels?.replied ?? 'Assistant replied')\n }\n return\n }\n previousCountRef.current = items.length\n }, [items, labels])\n\n const scrollToBottom = () => {\n const element = scrollRef.current\n if (element !== null) {\n element.scrollTo({ top: element.scrollHeight, behavior: 'smooth' })\n }\n }\n\n const rowCount = items.length + (live !== undefined ? 1 : 0)\n const virtualizer = useVirtualizer({\n count: virtualized ? rowCount : 0,\n getScrollElement: () => scrollRef.current,\n estimateSize: () => 120,\n overscan: 8,\n initialRect: { width: 480, height: 600 },\n })\n\n return (\n <div ref={ref} data-as=\"chat-transcript\" className={cn('relative min-h-0 flex-1', className)}>\n <div\n ref={scrollRef}\n onScroll={onScroll}\n role=\"log\"\n aria-label={labels?.log ?? 'Conversation'}\n aria-busy={live !== undefined}\n className=\"h-full overflow-y-auto overscroll-contain px-1 py-2\"\n >\n {items.length === 0 && live === undefined ? (\n <div className=\"flex h-full min-h-40 items-center justify-center p-4 text-sm text-[var(--as-muted-fg)]\">\n {emptyState ?? (labels?.empty ?? 'Start the conversation')}\n </div>\n ) : virtualized ? (\n <div style={{ height: virtualizer.getTotalSize(), position: 'relative' }}>\n {virtualizer.getVirtualItems().map((row) => {\n const isLive = row.index >= items.length\n const node = isLive ? live : renderItem(items[row.index]!, row.index)\n return (\n <div\n key={row.key}\n data-index={row.index}\n ref={virtualizer.measureElement}\n style={{ position: 'absolute', top: 0, left: 0, width: '100%', transform: `translateY(${row.start}px)` }}\n className=\"px-2 py-1.5\"\n >\n {node}\n </div>\n )\n })}\n </div>\n ) : (\n <div className=\"flex flex-col gap-3 px-2\">\n {items.map((message, index) => (\n <div key={message.id}>{renderItem(message, index)}</div>\n ))}\n {live !== undefined ? <div data-as=\"chat-transcript-live\">{live}</div> : null}\n </div>\n )}\n </div>\n <div aria-live=\"polite\" className=\"sr-only\">\n {announcement}\n </div>\n {showJump ? (\n <button\n type=\"button\"\n onClick={scrollToBottom}\n aria-label={labels?.scrollToBottom ?? 'Scroll to latest'}\n title={labels?.scrollToBottom ?? 'Scroll to latest'}\n className=\"absolute bottom-3 left-1/2 flex size-8 -translate-x-1/2 items-center justify-center rounded-full border border-[var(--as-border)] bg-[var(--as-surface-raised)] text-[var(--as-fg)] shadow-[var(--as-shadow-2)] transition-colors hover:bg-[var(--as-secondary)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <ArrowDown className=\"size-4\" aria-hidden />\n </button>\n ) : null}\n </div>\n )\n },\n)\n"]}
|
|
@@ -12,7 +12,6 @@ var ChatPanel = React.forwardRef(
|
|
|
12
12
|
"data-variant": variant,
|
|
13
13
|
className: cn(
|
|
14
14
|
"flex h-full min-h-0 w-full flex-col bg-[var(--as-surface)] text-[var(--as-fg)]",
|
|
15
|
-
variant === "page" && "mx-auto w-full max-w-3xl",
|
|
16
15
|
variant === "bubble" && "overflow-hidden",
|
|
17
16
|
className
|
|
18
17
|
),
|
|
@@ -22,8 +21,17 @@ var ChatPanel = React.forwardRef(
|
|
|
22
21
|
actions ? /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-0.5", children: actions }) : null
|
|
23
22
|
] }) : null,
|
|
24
23
|
banner ? /* @__PURE__ */ jsx("div", { "data-as": "chat-panel-banner", className: "shrink-0 px-3 py-1.5 text-xs text-[var(--as-muted-fg)]", children: banner }) : null,
|
|
25
|
-
/* @__PURE__ */ jsx("div", { className: "flex min-h-0 flex-1 flex-col", children:
|
|
26
|
-
|
|
24
|
+
/* @__PURE__ */ jsx("div", { className: "flex min-h-0 flex-1 flex-col", children: /* @__PURE__ */ jsx(
|
|
25
|
+
"div",
|
|
26
|
+
{
|
|
27
|
+
className: cn(
|
|
28
|
+
"flex min-h-0 w-full flex-1 flex-col",
|
|
29
|
+
variant === "page" && "mx-auto max-w-3xl"
|
|
30
|
+
),
|
|
31
|
+
children: transcript
|
|
32
|
+
}
|
|
33
|
+
) }),
|
|
34
|
+
composer ? /* @__PURE__ */ jsx("div", { className: "shrink-0 border-t border-[var(--as-border)] p-2", children: /* @__PURE__ */ jsx("div", { className: cn(variant === "page" && "mx-auto w-full max-w-3xl"), children: composer }) }) : null,
|
|
27
35
|
footer ? /* @__PURE__ */ jsx("div", { className: "shrink-0 px-3 pb-2 pt-1 text-center text-[10px] text-[var(--as-muted-fg)]", children: footer }) : null
|
|
28
36
|
]
|
|
29
37
|
}
|
|
@@ -32,5 +40,5 @@ var ChatPanel = React.forwardRef(
|
|
|
32
40
|
);
|
|
33
41
|
|
|
34
42
|
export { ChatPanel };
|
|
35
|
-
//# sourceMappingURL=chunk-
|
|
36
|
-
//# sourceMappingURL=chunk-
|
|
43
|
+
//# sourceMappingURL=chunk-LFSICACE.js.map
|
|
44
|
+
//# sourceMappingURL=chunk-LFSICACE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/chat-panel/ChatPanel.tsx"],"names":["ChatPanel"],"mappings":";;;;AA6BO,IAAM,SAAA,GAAkB,KAAA,CAAA,UAAA;AAAA,EAC7B,SAASA,UAAAA,CAAU,EAAE,OAAA,EAAS,KAAA,EAAO,OAAA,EAAS,MAAA,EAAQ,UAAA,EAAY,QAAA,EAAU,MAAA,EAAQ,SAAA,EAAU,EAAG,GAAA,EAAK;AACpG,IAAA,uBACE,IAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACA,SAAA,EAAQ,YAAA;AAAA,QACR,cAAA,EAAc,OAAA;AAAA,QACd,SAAA,EAAW,EAAA;AAAA,UACT,gFAAA;AAAA,UACA,YAAY,QAAA,IAAY,iBAAA;AAAA,UACxB;AAAA,SACF;AAAA,QAEC,QAAA,EAAA;AAAA,UAAA,KAAA,KAAU,UAAa,OAAA,KAAY,MAAA,mBAClC,IAAA,CAAC,QAAA,EAAA,EAAO,WAAU,+EAAA,EAChB,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,+CAAA,EAAiD,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,YACrE,0BAAU,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,oCAAA,EAAsC,mBAAQ,CAAA,GAAS;AAAA,WAAA,EACnF,CAAA,GACE,IAAA;AAAA,UACH,MAAA,uBACE,KAAA,EAAA,EAAI,SAAA,EAAQ,qBAAoB,SAAA,EAAU,wDAAA,EACxC,kBACH,CAAA,GACE,IAAA;AAAA,0BACJ,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,8BAAA,EACb,QAAA,kBAAA,GAAA;AAAA,YAAC,KAAA;AAAA,YAAA;AAAA,cACC,SAAA,EAAW,EAAA;AAAA,gBACT,qCAAA;AAAA,gBACA,YAAY,MAAA,IAAU;AAAA,eACxB;AAAA,cAEC,QAAA,EAAA;AAAA;AAAA,WACH,EACF,CAAA;AAAA,UACC,QAAA,mBACC,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,mDACb,QAAA,kBAAA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,EAAA,CAAG,YAAY,MAAA,IAAU,0BAA0B,CAAA,EAAI,QAAA,EAAA,QAAA,EAAS,GAClF,CAAA,GACE,IAAA;AAAA,UACH,yBACC,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,2EAAA,EAA6E,kBAAO,CAAA,GACjG;AAAA;AAAA;AAAA,KACN;AAAA,EAEJ;AACF","file":"chunk-LFSICACE.js","sourcesContent":["import * as React from 'react'\n\nimport { cn } from '../../lib/utils'\n\nexport type ChatPanelVariant = 'page' | 'sidebar' | 'bubble'\n\nexport interface ChatPanelProps {\n /** Layout tier: full page (full-bleed shell, centered conversation column), sidebar (dense column), bubble (compact widget body). */\n variant: ChatPanelVariant\n /** Header row: title node + action nodes (session list, branch tree, expand, close). */\n title?: React.ReactNode\n actions?: React.ReactNode\n /** Status strip under the header (connection warnings, context summary). */\n banner?: React.ReactNode\n /** Usually `<ChatTranscript …/>`. */\n transcript: React.ReactNode\n /** Usually `<ChatComposer …/>`. */\n composer?: React.ReactNode\n /** Disclaimer row under the composer (health guidance, etc.). */\n footer?: React.ReactNode\n className?: string\n}\n\n/**\n * The composed chat host — the one uniform surface assembly behind the\n * family's three shapes (full page / sidepanel / bubble). Owns layout\n * only; transcript, composer and every feature slot stay app-composed\n * (ADR-006 tier 3, `RichTextEditor` precedent).\n */\nexport const ChatPanel = React.forwardRef<HTMLDivElement, ChatPanelProps>(\n function ChatPanel({ variant, title, actions, banner, transcript, composer, footer, className }, ref) {\n return (\n <div\n ref={ref}\n data-as=\"chat-panel\"\n data-variant={variant}\n className={cn(\n 'flex h-full min-h-0 w-full flex-col bg-[var(--as-surface)] text-[var(--as-fg)]',\n variant === 'bubble' && 'overflow-hidden',\n className,\n )}\n >\n {title !== undefined || actions !== undefined ? (\n <header className=\"flex shrink-0 items-center gap-2 border-b border-[var(--as-border)] px-3 py-2\">\n <div className=\"min-w-0 flex-1 truncate text-sm font-semibold\">{title}</div>\n {actions ? <div className=\"flex shrink-0 items-center gap-0.5\">{actions}</div> : null}\n </header>\n ) : null}\n {banner ? (\n <div data-as=\"chat-panel-banner\" className=\"shrink-0 px-3 py-1.5 text-xs text-[var(--as-muted-fg)]\">\n {banner}\n </div>\n ) : null}\n <div className=\"flex min-h-0 flex-1 flex-col\">\n <div\n className={cn(\n 'flex min-h-0 w-full flex-1 flex-col',\n variant === 'page' && 'mx-auto max-w-3xl',\n )}\n >\n {transcript}\n </div>\n </div>\n {composer ? (\n <div className=\"shrink-0 border-t border-[var(--as-border)] p-2\">\n <div className={cn(variant === 'page' && 'mx-auto w-full max-w-3xl')}>{composer}</div>\n </div>\n ) : null}\n {footer ? (\n <div className=\"shrink-0 px-3 pb-2 pt-1 text-center text-[10px] text-[var(--as-muted-fg)]\">{footer}</div>\n ) : null}\n </div>\n )\n },\n)\n"]}
|
|
@@ -17,6 +17,7 @@ var ChatToolCard = React.forwardRef(
|
|
|
17
17
|
status,
|
|
18
18
|
args,
|
|
19
19
|
result,
|
|
20
|
+
renderResult,
|
|
20
21
|
durationMs,
|
|
21
22
|
open: openProp,
|
|
22
23
|
defaultOpen = false,
|
|
@@ -29,7 +30,7 @@ var ChatToolCard = React.forwardRef(
|
|
|
29
30
|
const isControlled = openProp !== void 0;
|
|
30
31
|
const open = isControlled ? openProp : uncontrolledOpen;
|
|
31
32
|
const regionId = React.useId();
|
|
32
|
-
const expandable = Boolean(args || result);
|
|
33
|
+
const expandable = Boolean(args || result || renderResult);
|
|
33
34
|
const toggle = () => {
|
|
34
35
|
if (!expandable) {
|
|
35
36
|
return;
|
|
@@ -103,9 +104,9 @@ var ChatToolCard = React.forwardRef(
|
|
|
103
104
|
/* @__PURE__ */ jsx("p", { className: "mb-1 font-medium uppercase tracking-wide text-[var(--as-muted-fg)]", children: labels?.args ?? "Arguments" }),
|
|
104
105
|
/* @__PURE__ */ jsx("pre", { className: "max-h-40 overflow-auto whitespace-pre-wrap break-words rounded-[var(--as-radius-sm)] bg-[var(--as-surface-raised)] p-2 font-mono text-[11px] leading-relaxed", children: args })
|
|
105
106
|
] }) : null,
|
|
106
|
-
result ? /* @__PURE__ */ jsxs("div", { children: [
|
|
107
|
+
result || renderResult ? /* @__PURE__ */ jsxs("div", { children: [
|
|
107
108
|
/* @__PURE__ */ jsx("p", { className: "mb-1 font-medium uppercase tracking-wide text-[var(--as-muted-fg)]", children: labels?.result ?? "Result" }),
|
|
108
|
-
/* @__PURE__ */ jsx("pre", { className: "max-h-40 overflow-auto whitespace-pre-wrap break-words rounded-[var(--as-radius-sm)] bg-[var(--as-surface-raised)] p-2 font-mono text-[11px] leading-relaxed", children: result })
|
|
109
|
+
renderResult ? renderResult(result ?? "") : /* @__PURE__ */ jsx("pre", { className: "max-h-40 overflow-auto whitespace-pre-wrap break-words rounded-[var(--as-radius-sm)] bg-[var(--as-surface-raised)] p-2 font-mono text-[11px] leading-relaxed", children: result })
|
|
109
110
|
] }) : null
|
|
110
111
|
] }) : null
|
|
111
112
|
]
|
|
@@ -115,5 +116,5 @@ var ChatToolCard = React.forwardRef(
|
|
|
115
116
|
);
|
|
116
117
|
|
|
117
118
|
export { ChatToolCard };
|
|
118
|
-
//# sourceMappingURL=chunk-
|
|
119
|
-
//# sourceMappingURL=chunk-
|
|
119
|
+
//# sourceMappingURL=chunk-NBT7NLNU.js.map
|
|
120
|
+
//# sourceMappingURL=chunk-NBT7NLNU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/chat-tool-card/ChatToolCard.tsx"],"names":["ChatToolCard"],"mappings":"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","file":"chunk-NBT7NLNU.js","sourcesContent":["import * as React from 'react'\nimport { Check, ChevronDown, TriangleAlert, Wrench } from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\n\nimport { cn } from '../../lib/utils'\nimport { Spinner } from '../spinner/Spinner'\n\nexport interface ChatToolCardLabels {\n running: string\n done: string\n failed: string\n args: string\n result: string\n details: string\n}\n\nexport interface ChatToolCardProps {\n name: string\n /** Human title, e.g. \"Searching the job catalog\". */\n title?: string\n status: 'running' | 'done' | 'failed'\n /** Serialized arguments (JSON string or formatted text). */\n args?: string\n /** Serialized result. */\n result?: string\n /**\n * Custom result view (study's per-tool views, family plan 12 L2).\n * Called with the result string (possibly empty when the tool has no\n * serialized output); when absent the default `<pre>` pane renders.\n */\n renderResult?: (result: string) => React.ReactNode\n durationMs?: number\n open?: boolean\n defaultOpen?: boolean\n onOpenChange?: (open: boolean) => void\n labels?: Partial<ChatToolCardLabels>\n icon?: LucideIcon\n className?: string\n}\n\nfunction formatDuration(ms: number): string {\n if (ms < 1000) {\n return `${Math.round(ms)} ms`\n }\n return `${(ms / 1000).toFixed(1)} s`\n}\n\n/**\n * Inline tool-call observation card (study's `ToolCallCard` + health's tool\n * chips, generalized): status, duration, expandable args/result. The\n * inspector stays app-side (health opens its own modal from `onOpenChange`\n * or wrapping markup).\n */\nexport const ChatToolCard = React.forwardRef<HTMLDivElement, ChatToolCardProps>(\n function ChatToolCard(\n {\n name,\n title,\n status,\n args,\n result,\n renderResult,\n durationMs,\n open: openProp,\n defaultOpen = false,\n onOpenChange,\n labels,\n icon: Icon = Wrench,\n className,\n },\n ref,\n ) {\n const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen)\n const isControlled = openProp !== undefined\n const open = isControlled ? openProp : uncontrolledOpen\n const regionId = React.useId()\n const expandable = Boolean(args || result || renderResult)\n\n const toggle = () => {\n if (!expandable) {\n return\n }\n const next = !open\n if (!isControlled) {\n setUncontrolledOpen(next)\n }\n onOpenChange?.(next)\n }\n\n const onKeyDown: React.KeyboardEventHandler<HTMLButtonElement> = (event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n toggle()\n }\n }\n\n const statusLabel =\n status === 'running' ? (labels?.running ?? 'Running') : status === 'done' ? (labels?.done ?? 'Done') : (labels?.failed ?? 'Failed')\n\n const Header = (expandable ? 'button' : 'div') as 'button'\n const headerProps: React.ComponentPropsWithoutRef<'button'> = expandable\n ? {\n type: 'button',\n 'aria-expanded': open,\n 'aria-controls': regionId,\n onClick: toggle,\n onKeyDown,\n }\n : {}\n\n return (\n <div\n ref={ref}\n data-as=\"chat-tool-card\"\n data-status={status}\n className={cn(\n 'rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] text-xs',\n className,\n )}\n >\n <Header\n {...headerProps}\n className=\"flex w-full items-center gap-2 px-2.5 py-1.5 text-left focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <Icon className=\"size-3.5 shrink-0 text-[var(--as-muted-fg)]\" aria-hidden />\n <span className=\"min-w-0 flex-1\">\n <span className=\"font-medium text-[var(--as-fg)]\">{title ?? name}</span>\n {title ? <span className=\"ml-1.5 font-mono text-[var(--as-muted-fg)]\">{name}</span> : null}\n </span>\n {durationMs !== undefined ? (\n <span className=\"shrink-0 tabular-nums text-[var(--as-muted-fg)]\">{formatDuration(durationMs)}</span>\n ) : null}\n <span\n role={status === 'running' ? 'status' : undefined}\n className={cn(\n 'flex shrink-0 items-center gap-1',\n status === 'done' && 'text-[var(--as-success)]',\n status === 'failed' && 'text-[var(--as-danger)]',\n status === 'running' && 'text-[var(--as-muted-fg)]',\n )}\n >\n {status === 'running' ? (\n <Spinner size=\"sm\" />\n ) : status === 'done' ? (\n <Check className=\"size-3.5\" aria-hidden />\n ) : (\n <TriangleAlert className=\"size-3.5\" aria-hidden />\n )}\n <span className=\"sr-only\">{statusLabel}</span>\n </span>\n {expandable ? (\n <ChevronDown className={cn('size-3.5 shrink-0 text-[var(--as-muted-fg)] transition-transform', open && 'rotate-180')} aria-hidden />\n ) : null}\n </Header>\n {expandable && open ? (\n <div id={regionId} className=\"flex flex-col gap-2 border-t border-[var(--as-border)] px-2.5 py-2\">\n {args ? (\n <div>\n <p className=\"mb-1 font-medium uppercase tracking-wide text-[var(--as-muted-fg)]\">{labels?.args ?? 'Arguments'}</p>\n <pre className=\"max-h-40 overflow-auto whitespace-pre-wrap break-words rounded-[var(--as-radius-sm)] bg-[var(--as-surface-raised)] p-2 font-mono text-[11px] leading-relaxed\">\n {args}\n </pre>\n </div>\n ) : null}\n {result || renderResult ? (\n <div>\n <p className=\"mb-1 font-medium uppercase tracking-wide text-[var(--as-muted-fg)]\">{labels?.result ?? 'Result'}</p>\n {renderResult ? (\n renderResult(result ?? '')\n ) : (\n <pre className=\"max-h-40 overflow-auto whitespace-pre-wrap break-words rounded-[var(--as-radius-sm)] bg-[var(--as-surface-raised)] p-2 font-mono text-[11px] leading-relaxed\">\n {result}\n </pre>\n )}\n </div>\n ) : null}\n </div>\n ) : null}\n </div>\n )\n },\n)\n"]}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { cn } from './chunk-BLJWR4ZV.js';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
function formatElapsed(ms) {
|
|
6
|
+
if (ms < 1e3) {
|
|
7
|
+
return `${Math.max(0, Math.round(ms))} ms`;
|
|
8
|
+
}
|
|
9
|
+
return `${(ms / 1e3).toFixed(ms < 1e4 ? 1 : 0)} s`;
|
|
10
|
+
}
|
|
11
|
+
var ChatTurnStatus = React.forwardRef(
|
|
12
|
+
function ChatTurnStatus2({ label, startedAt, variant = "row", tickMs = 100, labels, className }, ref) {
|
|
13
|
+
const [now, setNow] = React.useState(() => Date.now());
|
|
14
|
+
React.useEffect(() => {
|
|
15
|
+
if (startedAt === void 0) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
setNow(Date.now());
|
|
19
|
+
const id = window.setInterval(() => setNow(Date.now()), tickMs);
|
|
20
|
+
return () => window.clearInterval(id);
|
|
21
|
+
}, [startedAt, tickMs]);
|
|
22
|
+
const elapsed = startedAt === void 0 ? null : Math.max(0, now - startedAt);
|
|
23
|
+
return /* @__PURE__ */ jsxs(
|
|
24
|
+
"div",
|
|
25
|
+
{
|
|
26
|
+
ref,
|
|
27
|
+
role: "status",
|
|
28
|
+
"aria-label": label,
|
|
29
|
+
"data-as": "chat-turn-status",
|
|
30
|
+
"data-variant": variant,
|
|
31
|
+
className: cn(
|
|
32
|
+
"flex items-center gap-2 text-[var(--as-muted-fg)]",
|
|
33
|
+
variant === "card" && "rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface-raised)] px-3 py-2.5",
|
|
34
|
+
variant === "row" && "px-1 py-0.5",
|
|
35
|
+
className
|
|
36
|
+
),
|
|
37
|
+
children: [
|
|
38
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "flex items-center gap-1", children: [0, 1, 2].map((index) => /* @__PURE__ */ jsx(
|
|
39
|
+
"span",
|
|
40
|
+
{
|
|
41
|
+
className: "as-dot size-1.5 rounded-full bg-[var(--as-muted-fg)]",
|
|
42
|
+
style: { animationDelay: `${index * 0.16}s` }
|
|
43
|
+
},
|
|
44
|
+
index
|
|
45
|
+
)) }),
|
|
46
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs", children: label }),
|
|
47
|
+
elapsed !== null ? /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "font-mono text-[10px] tabular-nums", children: formatElapsed(elapsed) }) : null,
|
|
48
|
+
labels?.timer ? /* @__PURE__ */ jsx("span", { className: "sr-only", children: labels.timer }) : null
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
export { ChatTurnStatus };
|
|
56
|
+
//# sourceMappingURL=chunk-P75QKU5J.js.map
|
|
57
|
+
//# sourceMappingURL=chunk-P75QKU5J.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/chat-turn-status/ChatTurnStatus.tsx"],"names":["ChatTurnStatus"],"mappings":";;;;AAqBA,SAAS,cAAc,EAAA,EAAoB;AACzC,EAAA,IAAI,KAAK,GAAA,EAAM;AACb,IAAA,OAAO,CAAA,EAAG,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,KAAA,CAAM,EAAE,CAAC,CAAC,CAAA,GAAA,CAAA;AAAA,EACvC;AACA,EAAA,OAAO,CAAA,EAAA,CAAI,KAAK,GAAA,EAAM,OAAA,CAAQ,KAAK,GAAA,GAAQ,CAAA,GAAI,CAAC,CAAC,CAAA,EAAA,CAAA;AACnD;AAQO,IAAM,cAAA,GAAuB,KAAA,CAAA,UAAA;AAAA,EAClC,SAASA,eAAAA,CACP,EAAE,KAAA,EAAO,SAAA,EAAW,OAAA,GAAU,KAAA,EAAO,MAAA,GAAS,GAAA,EAAK,MAAA,EAAQ,SAAA,EAAU,EACrE,GAAA,EACA;AACA,IAAA,MAAM,CAAC,KAAK,MAAM,CAAA,GAAU,eAAS,MAAM,IAAA,CAAK,KAAK,CAAA;AAErD,IAAM,gBAAU,MAAM;AACpB,MAAA,IAAI,cAAc,MAAA,EAAW;AAC3B,QAAA;AAAA,MACF;AACA,MAAA,MAAA,CAAO,IAAA,CAAK,KAAK,CAAA;AACjB,MAAA,MAAM,EAAA,GAAK,OAAO,WAAA,CAAY,MAAM,OAAO,IAAA,CAAK,GAAA,EAAK,CAAA,EAAG,MAAM,CAAA;AAC9D,MAAA,OAAO,MAAM,MAAA,CAAO,aAAA,CAAc,EAAE,CAAA;AAAA,IACtC,CAAA,EAAG,CAAC,SAAA,EAAW,MAAM,CAAC,CAAA;AAEtB,IAAA,MAAM,OAAA,GAAU,cAAc,MAAA,GAAY,IAAA,GAAO,KAAK,GAAA,CAAI,CAAA,EAAG,MAAM,SAAS,CAAA;AAE5E,IAAA,uBACE,IAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACA,IAAA,EAAK,QAAA;AAAA,QACL,YAAA,EAAY,KAAA;AAAA,QACZ,SAAA,EAAQ,kBAAA;AAAA,QACR,cAAA,EAAc,OAAA;AAAA,QACd,SAAA,EAAW,EAAA;AAAA,UACT,mDAAA;AAAA,UACA,YAAY,MAAA,IACV,uGAAA;AAAA,UACF,YAAY,KAAA,IAAS,aAAA;AAAA,UACrB;AAAA,SACF;AAAA,QAEA,QAAA,EAAA;AAAA,0BAAA,GAAA,CAAC,MAAA,EAAA,EAAK,aAAA,EAAW,IAAA,EAAC,SAAA,EAAU,yBAAA,EACzB,QAAA,EAAA,CAAC,CAAA,EAAG,CAAA,EAAG,CAAC,CAAA,CAAE,GAAA,CAAI,CAAC,KAAA,qBACd,GAAA;AAAA,YAAC,MAAA;AAAA,YAAA;AAAA,cAEC,SAAA,EAAU,sDAAA;AAAA,cACV,OAAO,EAAE,cAAA,EAAgB,CAAA,EAAG,KAAA,GAAQ,IAAI,CAAA,CAAA,CAAA;AAAI,aAAA;AAAA,YAFvC;AAAA,WAIR,CAAA,EACH,CAAA;AAAA,0BACA,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,SAAA,EAAW,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,UAChC,OAAA,KAAY,IAAA,mBACX,GAAA,CAAC,MAAA,EAAA,EAAK,aAAA,EAAW,IAAA,EAAC,SAAA,EAAU,oCAAA,EACzB,QAAA,EAAA,aAAA,CAAc,OAAO,CAAA,EACxB,CAAA,GACE,IAAA;AAAA,UACH,MAAA,EAAQ,wBAAQ,GAAA,CAAC,MAAA,EAAA,EAAK,WAAU,SAAA,EAAW,QAAA,EAAA,MAAA,CAAO,OAAM,CAAA,GAAU;AAAA;AAAA;AAAA,KACrE;AAAA,EAEJ;AACF","file":"chunk-P75QKU5J.js","sourcesContent":["import * as React from 'react'\n\nimport { cn } from '../../lib/utils'\n\nexport interface ChatTurnStatusLabels {\n timer: string\n}\n\nexport interface ChatTurnStatusProps {\n /** Current turn phase, e.g. \"searching the catalog\". */\n label: string\n /** Turn start timestamp (ms). Presence enables the elapsed timer. */\n startedAt?: number\n /** `row` = inline pre-text line; `card` = soft surface block. */\n variant?: 'row' | 'card'\n /** Elapsed-timer refresh interval. Default 100 ms (study-proven). */\n tickMs?: number\n labels?: Partial<ChatTurnStatusLabels>\n className?: string\n}\n\nfunction formatElapsed(ms: number): string {\n if (ms < 1000) {\n return `${Math.max(0, Math.round(ms))} ms`\n }\n return `${(ms / 1000).toFixed(ms < 10000 ? 1 : 0)} s`\n}\n\n/**\n * Live pre-text turn status (study's `TurnTraceStatus` + `ThinkingDots`,\n * generalized for family plan 12 L1): animated dots + phase label, with\n * an optional elapsed timer. Presentational and controlled — the turn\n * state itself lives in `useChatStream`; this only narrates it.\n */\nexport const ChatTurnStatus = React.forwardRef<HTMLDivElement, ChatTurnStatusProps>(\n function ChatTurnStatus(\n { label, startedAt, variant = 'row', tickMs = 100, labels, className },\n ref,\n ) {\n const [now, setNow] = React.useState(() => Date.now())\n\n React.useEffect(() => {\n if (startedAt === undefined) {\n return\n }\n setNow(Date.now())\n const id = window.setInterval(() => setNow(Date.now()), tickMs)\n return () => window.clearInterval(id)\n }, [startedAt, tickMs])\n\n const elapsed = startedAt === undefined ? null : Math.max(0, now - startedAt)\n\n return (\n <div\n ref={ref}\n role=\"status\"\n aria-label={label}\n data-as=\"chat-turn-status\"\n data-variant={variant}\n className={cn(\n 'flex items-center gap-2 text-[var(--as-muted-fg)]',\n variant === 'card' &&\n 'rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface-raised)] px-3 py-2.5',\n variant === 'row' && 'px-1 py-0.5',\n className,\n )}\n >\n <span aria-hidden className=\"flex items-center gap-1\">\n {[0, 1, 2].map((index) => (\n <span\n key={index}\n className=\"as-dot size-1.5 rounded-full bg-[var(--as-muted-fg)]\"\n style={{ animationDelay: `${index * 0.16}s` }}\n />\n ))}\n </span>\n <span className=\"text-xs\">{label}</span>\n {elapsed !== null ? (\n <span aria-hidden className=\"font-mono text-[10px] tabular-nums\">\n {formatElapsed(elapsed)}\n </span>\n ) : null}\n {labels?.timer ? <span className=\"sr-only\">{labels.timer}</span> : null}\n </div>\n )\n },\n)\n"]}
|
|
@@ -38,7 +38,7 @@ var ChatDrawer = React.forwardRef(
|
|
|
38
38
|
container,
|
|
39
39
|
overlayClassName,
|
|
40
40
|
className
|
|
41
|
-
}) {
|
|
41
|
+
}, ref) {
|
|
42
42
|
const narrow = useIsNarrow(fullScreenBreakpointPx);
|
|
43
43
|
const draggingRef = React.useRef(false);
|
|
44
44
|
const clamp = (value) => Math.min(maxWidth, Math.max(minWidth, value));
|
|
@@ -90,6 +90,7 @@ var ChatDrawer = React.forwardRef(
|
|
|
90
90
|
/* @__PURE__ */ jsxs(
|
|
91
91
|
DialogPrimitive.Content,
|
|
92
92
|
{
|
|
93
|
+
ref,
|
|
93
94
|
"data-as": "chat-drawer",
|
|
94
95
|
"data-side": side,
|
|
95
96
|
"data-full-screen": narrow || void 0,
|
|
@@ -137,5 +138,5 @@ var ChatDrawer = React.forwardRef(
|
|
|
137
138
|
);
|
|
138
139
|
|
|
139
140
|
export { ChatDrawer };
|
|
140
|
-
//# sourceMappingURL=chunk-
|
|
141
|
-
//# sourceMappingURL=chunk-
|
|
141
|
+
//# sourceMappingURL=chunk-WBSGFOSD.js.map
|
|
142
|
+
//# sourceMappingURL=chunk-WBSGFOSD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/chat-drawer/ChatDrawer.tsx"],"names":["ChatDrawer"],"mappings":"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","file":"chunk-WBSGFOSD.js","sourcesContent":["import * as React from 'react'\nimport * as DialogPrimitive from '@radix-ui/react-dialog'\nimport { X } from 'lucide-react'\n\nimport { cn } from '../../lib/utils'\n\nexport interface ChatDrawerProps {\n open: boolean\n onOpenChange: (open: boolean) => void\n /** The sidepanel content — usually `ChatPanel variant=\"sidebar\"`. */\n panel: React.ReactNode\n side?: 'right' | 'left'\n /** Controlled width in px (persist app-side). Default 480. */\n width?: number\n onWidthChange?: (width: number) => void\n minWidth?: number\n maxWidth?: number\n resizable?: boolean\n closeLabel?: string\n resizeLabel?: string\n /** Accessible dialog name (visually hidden). Default \"Chat\". */\n title?: string\n /** Below this viewport width the drawer goes full-screen. Default 768. */\n fullScreenBreakpointPx?: number\n container?: HTMLElement | null\n overlayClassName?: string\n className?: string\n}\n\nfunction useIsNarrow(breakpointPx: number): boolean {\n const [narrow, setNarrow] = React.useState(() =>\n typeof window !== 'undefined' && window.matchMedia !== undefined\n ? window.matchMedia(`(max-width: ${breakpointPx - 1}px)`).matches\n : false,\n )\n React.useEffect(() => {\n if (typeof window === 'undefined' || window.matchMedia === undefined) {\n return\n }\n const query = window.matchMedia(`(max-width: ${breakpointPx - 1}px)`)\n const listener = (event: MediaQueryListEvent) => setNarrow(event.matches)\n query.addEventListener('change', listener)\n setNarrow(query.matches)\n return () => query.removeEventListener('change', listener)\n }, [breakpointPx])\n return narrow\n}\n\n/**\n * The resizable chat sidepanel (health's `AIDrawer` + study's resizable\n * sidebar, family-standard): portal overlay on Radix Dialog (focus trap,\n * Escape), drag + keyboard resize, full-screen below the breakpoint.\n */\nexport const ChatDrawer = React.forwardRef<HTMLDivElement, ChatDrawerProps>(\n function ChatDrawer(\n {\n open,\n onOpenChange,\n panel,\n side = 'right',\n width = 480,\n onWidthChange,\n minWidth = 320,\n maxWidth = 860,\n resizable = true,\n closeLabel = 'Close chat',\n resizeLabel = 'Resize chat panel',\n title = 'Chat',\n fullScreenBreakpointPx = 768,\n container,\n overlayClassName,\n className,\n },\n ref,\n ) {\n const narrow = useIsNarrow(fullScreenBreakpointPx)\n const draggingRef = React.useRef(false)\n\n const clamp = (value: number) => Math.min(maxWidth, Math.max(minWidth, value))\n\n const onHandleKeyDown = (event: React.KeyboardEvent) => {\n if (!resizable || narrow) {\n return\n }\n const step = event.shiftKey ? 48 : 16\n if (event.key === (side === 'right' ? 'ArrowLeft' : 'ArrowRight')) {\n event.preventDefault()\n onWidthChange?.(clamp(width + step))\n } else if (event.key === (side === 'right' ? 'ArrowRight' : 'ArrowLeft')) {\n event.preventDefault()\n onWidthChange?.(clamp(width - step))\n }\n }\n\n const onHandlePointerDown = (event: React.PointerEvent<HTMLDivElement>) => {\n if (!resizable || narrow || event.button !== 0) {\n return\n }\n event.preventDefault()\n draggingRef.current = true\n event.currentTarget.setPointerCapture(event.pointerId)\n }\n\n const onHandlePointerMove = (event: React.PointerEvent<HTMLDivElement>) => {\n if (!draggingRef.current) {\n return\n }\n const delta = side === 'right' ? window.innerWidth - event.clientX : event.clientX\n onWidthChange?.(clamp(delta))\n }\n\n const onHandlePointerUp = (event: React.PointerEvent<HTMLDivElement>) => {\n if (!draggingRef.current) {\n return\n }\n draggingRef.current = false\n if (event.currentTarget.hasPointerCapture(event.pointerId)) {\n event.currentTarget.releasePointerCapture(event.pointerId)\n }\n }\n\n return (\n <DialogPrimitive.Root open={open} onOpenChange={onOpenChange}>\n <DialogPrimitive.Portal container={container}>\n <DialogPrimitive.Overlay\n data-as=\"chat-drawer-overlay\"\n className={cn('as-anim-fade fixed inset-0 z-[var(--as-z-modal)] bg-[var(--as-overlay)]', overlayClassName)}\n />\n <DialogPrimitive.Content\n ref={ref}\n data-as=\"chat-drawer\"\n data-side={side}\n data-full-screen={narrow || undefined}\n style={narrow ? undefined : { width: `${width}px`, maxWidth: '100vw' }}\n className={cn(\n 'as-anim-drawer fixed top-0 z-[var(--as-z-modal)] flex h-dvh flex-col border-[var(--as-border)] bg-[var(--as-surface)] text-[var(--as-fg)] shadow-[var(--as-shadow-3)] focus:outline-none',\n narrow\n ? 'inset-x-0 bottom-0 border-t'\n : side === 'right'\n ? 'bottom-0 right-0 border-l'\n : 'bottom-0 left-0 border-r',\n className,\n )}\n >\n {resizable && !narrow ? (\n <div\n role=\"separator\"\n aria-orientation=\"vertical\"\n aria-label={resizeLabel}\n tabIndex={0}\n onKeyDown={onHandleKeyDown}\n onPointerDown={onHandlePointerDown}\n onPointerMove={onHandlePointerMove}\n onPointerUp={onHandlePointerUp}\n onPointerCancel={onHandlePointerUp}\n className={cn(\n 'group/handle absolute top-0 z-10 h-dvh w-1.5 cursor-col-resize focus-visible:outline-2 focus-visible:outline-[var(--as-focus-ring)]',\n side === 'right' ? '-left-1' : '-right-1',\n )}\n >\n <span className=\"mx-auto block h-full w-px bg-transparent transition-colors group-hover/handle:bg-[var(--as-border)]\" />\n </div>\n ) : null}\n <DialogPrimitive.Title className=\"sr-only\">{title}</DialogPrimitive.Title>\n <div className=\"flex min-h-0 flex-1 flex-col\">{panel}</div>\n <DialogPrimitive.Close\n aria-label={closeLabel}\n className=\"absolute right-3 top-2.5 rounded-[var(--as-radius-sm)] p-1 text-[var(--as-muted-fg)] transition-colors hover:bg-[var(--as-secondary)] hover:text-[var(--as-fg)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <X aria-hidden className=\"size-4\" />\n </DialogPrimitive.Close>\n </DialogPrimitive.Content>\n </DialogPrimitive.Portal>\n </DialogPrimitive.Root>\n )\n },\n)\n"]}
|