@qredence/fleet 0.5.0 → 0.5.1
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 +14 -0
- package/README.md +8 -1
- package/dist/web/client/assets/artifacts-panel-CX62LQ13.js +1 -0
- package/dist/web/client/assets/artifacts-utils-N_kUFbz2.js +1 -0
- package/dist/web/client/assets/bot-Bs7QWJ2q.js +1 -0
- package/dist/web/client/assets/{chat-C64Sv6V-.js → chat-CsepBQwy.js} +5 -5
- package/dist/web/client/assets/{chat-command-palette-HpsmKfLO.js → chat-command-palette-C4sSf8Kg.js} +1 -1
- package/dist/web/client/assets/collapsible-DmyYcezk.js +1 -0
- package/dist/web/client/assets/command-CB6emP8m.js +2 -0
- package/dist/web/client/assets/conversation-turns-ByFoWPXe.js +6 -0
- package/dist/web/client/assets/ease-vFBiNFNK.js +9 -0
- package/dist/web/client/assets/{fleet-pi-tool-renderer-ZKiX6Ob_.js → fleet-pi-tool-renderer-BD_ZAjlG.js} +28 -30
- package/dist/web/client/assets/globals-CqqQdk2S.css +2 -0
- package/dist/web/client/assets/horizontal-resize-C1TpRLNC.js +1 -0
- package/dist/web/client/assets/index-DweoFRMs.js +12 -0
- package/dist/web/client/assets/{input-Clyn18XG.js → input-CqC82Agz.js} +1 -1
- package/dist/web/client/assets/{input-group-MxDAWX3j.js → input-group-H0TG-mkU.js} +1 -1
- package/dist/web/client/assets/ipython-tool-BfeIa0nK.js +3 -0
- package/dist/web/client/assets/markdown-DG7pUdsX.js +2 -0
- package/dist/web/client/assets/{markdown-code-pIaEVDUU.js → markdown-code-BgMzpy7O.js} +1 -1
- package/dist/web/client/assets/notify-Duj26eeZ.js +1 -0
- package/dist/web/client/assets/{openui-renderer-NLUpAS3h.js → openui-renderer-DlUo6ehT.js} +5 -5
- package/dist/web/client/assets/openui-utils-DawGoQNp.js +1 -0
- package/dist/web/client/assets/prime-agent-artifacts-CHGb5V_j.js +5 -0
- package/dist/web/client/assets/{provider-catalog-Gx9qaHBM.js → provider-catalog-BZhZuKSz.js} +1 -1
- package/dist/web/client/assets/provider-label-jKm8v18u.js +1 -0
- package/dist/web/client/assets/range-vSJ7Ux_5.js +2 -0
- package/dist/web/client/assets/repl-panel-DiGS6uCw.js +1 -0
- package/dist/web/client/assets/resource-helpers-ULlyU8qK.js +2 -0
- package/dist/web/client/assets/resources-panel-DSU7VGX2.js +1 -0
- package/dist/web/client/assets/routes-CxlNCi_v.js +16 -0
- package/dist/web/client/assets/session-insights-panel-DwmwiikS.js +1 -0
- package/dist/web/client/assets/settings-dialog-CkfpTKfM.js +1 -0
- package/dist/web/client/assets/skeleton-loaders-fStjEeRP.js +1 -0
- package/dist/web/client/assets/square-terminal-BYTN_I86.js +1 -0
- package/dist/web/client/assets/subagents-panel-DGnsK6Nx.js +1 -0
- package/dist/web/client/assets/{switch-ojzdMno9.js → switch-BFEBpUEU.js} +1 -1
- package/dist/web/client/assets/{tool-result-QvhjpNGX.js → tool-result-CBzwwUE9.js} +3 -3
- package/dist/web/client/assets/ui-preferences-DUEFuJFj.js +1 -0
- package/dist/web/client/assets/workspace-panel-CjdXlEvd.js +1 -0
- package/dist/web/client/assets/workspace-path-nav-C0AQNZyy.js +1 -0
- package/dist/web/server/{_tanstack-start-manifest_v-uZddQetG-MNZGQOSA.js → _tanstack-start-manifest_v-bDUlyKY0-KFW35JRO.js} +23 -16
- package/dist/web/server/{artifacts-panel-knpQvJ-q-WJLZ76QB.js → artifacts-panel-D9w1DHAN-FGRDCYU2.js} +37 -70
- package/dist/web/server/{chat-command-palette-CPv5GmGJ-22J32S6P.js → chat-command-palette-BaenPkCN-BHSOPGU4.js} +16 -14
- package/dist/web/server/{chunk-OADQILSQ.js → chunk-2U7PWSJX.js} +49 -47
- package/dist/web/server/{chunk-ZX3XOSYO.js → chunk-3GSFXWL5.js} +2 -2
- package/dist/web/server/{chunk-IFV5M33X.js → chunk-3IKSPTYI.js} +12 -26
- package/dist/web/server/{chunk-W7LNJCRJ.js → chunk-44IVTHDV.js} +18 -10
- package/dist/web/server/{chunk-I3FDTJFO.js → chunk-46VQY7SG.js} +6 -87
- package/dist/web/server/{chunk-WTYNN6EV.js → chunk-54II5QXQ.js} +23 -1
- package/dist/web/server/{chunk-PRT4QYOB.js → chunk-5AVQZ4AZ.js} +1 -35
- package/dist/web/server/chunk-6GMKRR3C.js +161 -0
- package/dist/web/server/chunk-74QOI457.js +45 -0
- package/dist/web/server/chunk-AQ66E5OW.js +33 -0
- package/dist/web/server/{chunk-TTQ75CXC.js → chunk-AZZHUVJR.js} +88 -23
- package/dist/web/server/{chunk-KAWYGHDN.js → chunk-B2F6MLVZ.js} +23 -1
- package/dist/web/server/{chunk-C7EKOPZU.js → chunk-DO3TVNGA.js} +87 -87
- package/dist/web/server/chunk-EIVNRYP3.js +48 -0
- package/dist/web/server/{chunk-UBUXV2TW.js → chunk-FBOO3G67.js} +6 -6
- package/dist/web/server/{chunk-MK3P3OBZ.js → chunk-FVWDJNXR.js} +5 -5
- package/dist/web/server/chunk-G5BCQQ5N.js +34 -0
- package/dist/web/server/{chunk-4KFMONRT.js → chunk-H64JLONZ.js} +9 -9
- package/dist/web/server/{chunk-KDSISV4P.js → chunk-HBZ6YKRB.js} +2 -50
- package/dist/web/server/{chunk-5277VI3D.js → chunk-KPNGZR2R.js} +12 -74
- package/dist/web/server/chunk-M2FGGPNA.js +376 -0
- package/dist/web/server/{chunk-KVHPE5XH.js → chunk-MDYA4NXK.js} +22 -8
- package/dist/web/server/chunk-NFRJDPB3.js +52 -0
- package/dist/web/server/chunk-P66QQWUM.js +67 -0
- package/dist/web/server/{fleet-pi-tool-renderer-5vYFNeZS-IHKMLT7W.js → chunk-PCJ5EKFE.js} +40 -392
- package/dist/web/server/{chunk-APN3LVRT.js → chunk-PWK2HJIV.js} +27 -30
- package/dist/web/server/{chunk-XXNFEXDU.js → chunk-QJBEWT4E.js} +2 -70
- package/dist/web/server/{chunk-JD6EWZCU.js → chunk-VHCAD46L.js} +48 -48
- package/dist/web/server/chunk-Y6LJ4WBI.js +1797 -0
- package/dist/web/server/{chunk-NGI44XRR.js → chunk-ZEVZFLIY.js} +1 -1
- package/dist/web/server/fleet-pi-tool-renderer-lJvZWmBJ-4M5YI6VS.js +35 -0
- package/dist/web/server/{fork-picker-dialog-BZzfxdQd-C6SPHFAW.js → fork-picker-dialog-BZzfxdQd-Y725VJ7G.js} +2 -2
- package/dist/web/server/{markdown-Ct_q4D-z-VFWZRBTY.js → markdown-Boni3lHk-AZGKDNAR.js} +1 -1
- package/dist/web/server/{markdown-code-C_ih1bX1-5CIWT3CH.js → markdown-code-BrWukbg3-D2GN4Y77.js} +2 -2
- package/dist/web/server/{openui-renderer-BT5jL0Yf-XYLLN5U6.js → openui-renderer-Cg4RSE9k-7BTJIYZJ.js} +12 -11
- package/dist/web/server/repl-panel-BEQrC2ef-V3YEZ7EO.js +117 -0
- package/dist/web/server/{resources-panel-DNCVHTIg-PJYMXVGE.js → resources-panel-CwKh4U9f-D3VDEMDW.js} +6 -6
- package/dist/web/server/{router-DDxOMz3K-HRBXILHM.js → router-CEahIUpn-SRG7XKIH.js} +131 -21
- package/dist/web/server/{routes-BX4Tv-6R-FHIZYUWW.js → routes-BznemByz-F4DJAYIM.js} +1371 -2499
- package/dist/web/server/server.js +2 -2
- package/dist/web/server/{session-insights-panel-gYkkAE8H-W4O74Z7H.js → session-insights-panel-JfeTHE0d-3HZB3ZWI.js} +2 -2
- package/dist/web/server/{settings-dialog-BTBFLQ1P-ZENQ4KWK.js → settings-dialog-DgeBMORv-7SRSENPM.js} +47 -61
- package/dist/web/server/subagents-panel-CzZe3co7-2VZ5JTWF.js +332 -0
- package/dist/web/server/{chunk-5SZXUN3Y.js → workspace-panel-y54P79_B-VPIYUXF6.js} +29 -16
- package/package.json +11 -3
- package/dist/web/client/assets/artifacts-panel-8TyrNBM_.js +0 -1
- package/dist/web/client/assets/collapsible-WCSnxhr2.js +0 -1
- package/dist/web/client/assets/command-DgfiCKcY.js +0 -2
- package/dist/web/client/assets/globals-BmxtUHFx.css +0 -2
- package/dist/web/client/assets/horizontal-resize-B76jjn5v.js +0 -1
- package/dist/web/client/assets/index-0pBr_bKy.js +0 -12
- package/dist/web/client/assets/markdown-BXWmNJo7.js +0 -2
- package/dist/web/client/assets/prime-agent-artifacts-k9JiTDZM.js +0 -5
- package/dist/web/client/assets/provider-brand-icon-VCebsuTD.js +0 -1
- package/dist/web/client/assets/range-KAaLzImH.js +0 -2
- package/dist/web/client/assets/resources-panel-BWuKeyrb.js +0 -1
- package/dist/web/client/assets/routes-BPRw0UzV.js +0 -20
- package/dist/web/client/assets/session-insights-panel-CPymkzZT.js +0 -1
- package/dist/web/client/assets/settings-dialog-6p8mjg8t.js +0 -1
- package/dist/web/client/assets/shared-DAI9OAw0.js +0 -2
- package/dist/web/client/assets/skeleton-loaders-qxIkJKCA.js +0 -1
- package/dist/web/client/assets/workspace-panel-C67-fqw4.js +0 -1
- package/dist/web/client/assets/workspace-path-nav-BrQHo6es.js +0 -9
- package/dist/web/server/chunk-KJ4UUAEU.js +0 -83
- package/dist/web/server/workspace-panel-BNAXF57q-EQCBPEG4.js +0 -28
- package/dist/web/server/{chunk-AQWTGP35.js → chunk-FE4KN6T2.js} +5 -5
|
@@ -0,0 +1,1797 @@
|
|
|
1
|
+
import {
|
|
2
|
+
LazyMarkdown,
|
|
3
|
+
Terminal,
|
|
4
|
+
useHoverCapable
|
|
5
|
+
} from "./chunk-44IVTHDV.js";
|
|
6
|
+
import {
|
|
7
|
+
notify
|
|
8
|
+
} from "./chunk-EIVNRYP3.js";
|
|
9
|
+
import {
|
|
10
|
+
CircleX,
|
|
11
|
+
Copy,
|
|
12
|
+
Wrench
|
|
13
|
+
} from "./chunk-PWK2HJIV.js";
|
|
14
|
+
import {
|
|
15
|
+
AnimatePresence,
|
|
16
|
+
EASE_OUT,
|
|
17
|
+
SPRING_LAYOUT,
|
|
18
|
+
m,
|
|
19
|
+
useReducedMotion
|
|
20
|
+
} from "./chunk-QJBEWT4E.js";
|
|
21
|
+
import {
|
|
22
|
+
CircleCheck,
|
|
23
|
+
LoaderCircle
|
|
24
|
+
} from "./chunk-PKCTG7EW.js";
|
|
25
|
+
import {
|
|
26
|
+
ChevronRight
|
|
27
|
+
} from "./chunk-7QCDOBZ3.js";
|
|
28
|
+
import {
|
|
29
|
+
segmentOpenUIContent
|
|
30
|
+
} from "./chunk-NFRJDPB3.js";
|
|
31
|
+
import {
|
|
32
|
+
ChevronDown
|
|
33
|
+
} from "./chunk-QLY3L4A2.js";
|
|
34
|
+
import {
|
|
35
|
+
Check
|
|
36
|
+
} from "./chunk-NLIUTTKL.js";
|
|
37
|
+
import {
|
|
38
|
+
X
|
|
39
|
+
} from "./chunk-X2TUFLTP.js";
|
|
40
|
+
import {
|
|
41
|
+
CircleAlert,
|
|
42
|
+
FileText
|
|
43
|
+
} from "./chunk-7APYEQIO.js";
|
|
44
|
+
import {
|
|
45
|
+
createLucideIcon
|
|
46
|
+
} from "./chunk-E26EAQIM.js";
|
|
47
|
+
import {
|
|
48
|
+
cn
|
|
49
|
+
} from "./chunk-DJMHPJBP.js";
|
|
50
|
+
|
|
51
|
+
// web/app/dist/server/assets/conversation-turns-wRm6Ard7.js
|
|
52
|
+
import { Suspense, cloneElement, createContext, lazy, memo, useCallback, useContext, useEffect, useEffectEvent, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
53
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
54
|
+
import { createPortal } from "react-dom";
|
|
55
|
+
var ChevronLeft = createLucideIcon("chevron-left", [["path", {
|
|
56
|
+
d: "m15 18-6-6 6-6",
|
|
57
|
+
key: "1wnfg3"
|
|
58
|
+
}]]);
|
|
59
|
+
var FileBraces = createLucideIcon("file-braces", [
|
|
60
|
+
["path", {
|
|
61
|
+
d: "M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z",
|
|
62
|
+
key: "1oefj6"
|
|
63
|
+
}],
|
|
64
|
+
["path", {
|
|
65
|
+
d: "M14 2v5a1 1 0 0 0 1 1h5",
|
|
66
|
+
key: "wfsgrz"
|
|
67
|
+
}],
|
|
68
|
+
["path", {
|
|
69
|
+
d: "M10 12a1 1 0 0 0-1 1v1a1 1 0 0 1-1 1 1 1 0 0 1 1 1v1a1 1 0 0 0 1 1",
|
|
70
|
+
key: "1oajmo"
|
|
71
|
+
}],
|
|
72
|
+
["path", {
|
|
73
|
+
d: "M14 18a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1 1 1 0 0 1-1-1v-1a1 1 0 0 0-1-1",
|
|
74
|
+
key: "mpwhp6"
|
|
75
|
+
}]
|
|
76
|
+
]);
|
|
77
|
+
var FileCode = createLucideIcon("file-code", [
|
|
78
|
+
["path", {
|
|
79
|
+
d: "M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z",
|
|
80
|
+
key: "1oefj6"
|
|
81
|
+
}],
|
|
82
|
+
["path", {
|
|
83
|
+
d: "M14 2v5a1 1 0 0 0 1 1h5",
|
|
84
|
+
key: "wfsgrz"
|
|
85
|
+
}],
|
|
86
|
+
["path", {
|
|
87
|
+
d: "M10 12.5 8 15l2 2.5",
|
|
88
|
+
key: "1tg20x"
|
|
89
|
+
}],
|
|
90
|
+
["path", {
|
|
91
|
+
d: "m14 12.5 2 2.5-2 2.5",
|
|
92
|
+
key: "yinavb"
|
|
93
|
+
}]
|
|
94
|
+
]);
|
|
95
|
+
var FilePenLine = createLucideIcon("file-pen-line", [
|
|
96
|
+
["path", {
|
|
97
|
+
d: "M14.364 13.634a2 2 0 0 0-.506.854l-.837 2.87a.5.5 0 0 0 .62.62l2.87-.837a2 2 0 0 0 .854-.506l4.013-4.009a1 1 0 0 0-3.004-3.004z",
|
|
98
|
+
key: "ukzhwg"
|
|
99
|
+
}],
|
|
100
|
+
["path", {
|
|
101
|
+
d: "M14.487 7.858A1 1 0 0 1 14 7V2",
|
|
102
|
+
key: "1klhew"
|
|
103
|
+
}],
|
|
104
|
+
["path", {
|
|
105
|
+
d: "M20 19.645V20a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l2.516 2.516",
|
|
106
|
+
key: "rxaxab"
|
|
107
|
+
}],
|
|
108
|
+
["path", {
|
|
109
|
+
d: "M8 18h1",
|
|
110
|
+
key: "13wk12"
|
|
111
|
+
}]
|
|
112
|
+
]);
|
|
113
|
+
var FileSearch = createLucideIcon("file-search", [
|
|
114
|
+
["path", {
|
|
115
|
+
d: "M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z",
|
|
116
|
+
key: "1oefj6"
|
|
117
|
+
}],
|
|
118
|
+
["path", {
|
|
119
|
+
d: "M14 2v5a1 1 0 0 0 1 1h5",
|
|
120
|
+
key: "wfsgrz"
|
|
121
|
+
}],
|
|
122
|
+
["circle", {
|
|
123
|
+
cx: "11.5",
|
|
124
|
+
cy: "14.5",
|
|
125
|
+
r: "2.5",
|
|
126
|
+
key: "1bq0ko"
|
|
127
|
+
}],
|
|
128
|
+
["path", {
|
|
129
|
+
d: "M13.3 16.3 15 18",
|
|
130
|
+
key: "2quom7"
|
|
131
|
+
}]
|
|
132
|
+
]);
|
|
133
|
+
var Image = createLucideIcon("image", [
|
|
134
|
+
["rect", {
|
|
135
|
+
width: "18",
|
|
136
|
+
height: "18",
|
|
137
|
+
x: "3",
|
|
138
|
+
y: "3",
|
|
139
|
+
rx: "2",
|
|
140
|
+
ry: "2",
|
|
141
|
+
key: "1m3agn"
|
|
142
|
+
}],
|
|
143
|
+
["circle", {
|
|
144
|
+
cx: "9",
|
|
145
|
+
cy: "9",
|
|
146
|
+
r: "2",
|
|
147
|
+
key: "af1f0g"
|
|
148
|
+
}],
|
|
149
|
+
["path", {
|
|
150
|
+
d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21",
|
|
151
|
+
key: "1xmnt7"
|
|
152
|
+
}]
|
|
153
|
+
]);
|
|
154
|
+
var MessageSideContext = createContext(void 0);
|
|
155
|
+
var MessageBubbleContext = createContext({
|
|
156
|
+
animateIn: true,
|
|
157
|
+
variant: "soft"
|
|
158
|
+
});
|
|
159
|
+
var MessageBubbleLayoutContext = createContext(() => {
|
|
160
|
+
});
|
|
161
|
+
function mergeRefs(...refs) {
|
|
162
|
+
return (node) => {
|
|
163
|
+
for (const ref of refs) if (typeof ref === "function") ref(node);
|
|
164
|
+
else if (ref) ref.current = node;
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
var BUBBLE_CONTENT_REVEAL = {
|
|
168
|
+
duration: 0.12,
|
|
169
|
+
ease: EASE_OUT,
|
|
170
|
+
delay: 0.04
|
|
171
|
+
};
|
|
172
|
+
var BUBBLE_POP = {
|
|
173
|
+
type: "spring",
|
|
174
|
+
stiffness: 520,
|
|
175
|
+
damping: 27,
|
|
176
|
+
mass: 0.52
|
|
177
|
+
};
|
|
178
|
+
function MessageBubble({ variant = "soft", align, animateIn = false, className, children, initial, animate, exit, transition, layout, ...props }) {
|
|
179
|
+
const reduce = useReducedMotion() ?? false;
|
|
180
|
+
const messageSide = useContext(MessageSideContext);
|
|
181
|
+
const resolvedAlign = align ?? messageSide ?? "start";
|
|
182
|
+
const messageBubbleContextValue = useMemo(() => ({
|
|
183
|
+
align: resolvedAlign,
|
|
184
|
+
animateIn,
|
|
185
|
+
variant
|
|
186
|
+
}), [
|
|
187
|
+
animateIn,
|
|
188
|
+
resolvedAlign,
|
|
189
|
+
variant
|
|
190
|
+
]);
|
|
191
|
+
return /* @__PURE__ */ jsx(MessageBubbleContext.Provider, {
|
|
192
|
+
value: messageBubbleContextValue,
|
|
193
|
+
children: /* @__PURE__ */ jsx(m.div, {
|
|
194
|
+
"data-slot": "message-bubble",
|
|
195
|
+
"data-align": resolvedAlign,
|
|
196
|
+
"data-variant": variant,
|
|
197
|
+
layout,
|
|
198
|
+
initial: initial ?? false,
|
|
199
|
+
animate,
|
|
200
|
+
exit: exit ?? (reduce ? { opacity: 0 } : {
|
|
201
|
+
opacity: 0,
|
|
202
|
+
y: -3,
|
|
203
|
+
scale: 0.99
|
|
204
|
+
}),
|
|
205
|
+
transition: transition ?? (reduce ? { duration: 0.12 } : SPRING_LAYOUT),
|
|
206
|
+
className: cn("group/bubble flex w-full flex-col", resolvedAlign === "end" ? "items-end" : "items-start", className),
|
|
207
|
+
...props,
|
|
208
|
+
children
|
|
209
|
+
})
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
function bubbleContentClass(variant, interactive) {
|
|
213
|
+
return cn("relative z-0 min-w-9 max-w-[82%] rounded-2xl px-3.5 py-2.5 text-sm leading-6 text-foreground", "[&_a]:font-medium [&_a]:underline [&_a]:underline-offset-4 [&_code]:rounded [&_code]:bg-background/60 [&_code]:px-1 [&_code]:py-0.5 [&_code]:font-mono [&_code]:text-[0.9em] [&_ol]:my-2 [&_ol]:list-decimal [&_ol]:pl-5 [&_p+p]:mt-2 [&_pre]:my-2 [&_pre]:overflow-x-auto [&_pre]:rounded-xl [&_pre]:bg-background/60 [&_pre]:p-3 [&_ul]:my-2 [&_ul]:list-disc [&_ul]:pl-5", variant === "solid" && "text-background", variant === "ghost" && "w-full max-w-none rounded-none px-0 py-0", variant === "danger" && "text-destructive", interactive && "cursor-pointer text-left outline-none transition-[background-color,color,transform] duration-150 hover:brightness-[0.98] focus-visible:ring-2 focus-visible:ring-ring active:scale-[0.99]");
|
|
214
|
+
}
|
|
215
|
+
function bubbleSurfaceClass(variant, align) {
|
|
216
|
+
return cn("pointer-events-none absolute inset-0 -z-10 rounded-[inherit]", align === "end" ? "origin-bottom-right" : "origin-bottom-left", variant === "solid" && "bg-foreground", variant === "soft" && "bg-muted", variant === "tint" && "bg-primary/10", variant === "outline" && "border border-border/70 bg-background", variant === "danger" && "bg-destructive/10");
|
|
217
|
+
}
|
|
218
|
+
function MessageBubbleContent({ render, className, children, ref, ...props }) {
|
|
219
|
+
const reduce = useReducedMotion() ?? false;
|
|
220
|
+
const { align = "start", animateIn, variant } = useContext(MessageBubbleContext);
|
|
221
|
+
const [layoutVersion, setLayoutVersion] = useState(0);
|
|
222
|
+
const notifyLayout = useCallback(() => setLayoutVersion((version) => version + 1), []);
|
|
223
|
+
const interactive = render?.type === "button" || render?.type === "a";
|
|
224
|
+
const classes = cn(bubbleContentClass(variant, interactive), className);
|
|
225
|
+
const composedChildren = /* @__PURE__ */ jsxs(Fragment$1, { children: [variant !== "ghost" ? /* @__PURE__ */ jsx(m.span, {
|
|
226
|
+
"aria-hidden": "true",
|
|
227
|
+
layout: reduce ? false : "size",
|
|
228
|
+
layoutDependency: layoutVersion,
|
|
229
|
+
initial: animateIn && !reduce ? {
|
|
230
|
+
opacity: 0,
|
|
231
|
+
scale: 0.92
|
|
232
|
+
} : false,
|
|
233
|
+
animate: {
|
|
234
|
+
opacity: 1,
|
|
235
|
+
scale: 1
|
|
236
|
+
},
|
|
237
|
+
transition: reduce ? { duration: 0 } : {
|
|
238
|
+
opacity: {
|
|
239
|
+
duration: 0.12,
|
|
240
|
+
ease: EASE_OUT
|
|
241
|
+
},
|
|
242
|
+
scale: BUBBLE_POP,
|
|
243
|
+
layout: SPRING_LAYOUT
|
|
244
|
+
},
|
|
245
|
+
className: bubbleSurfaceClass(variant, align)
|
|
246
|
+
}) : null, /* @__PURE__ */ jsx(MessageBubbleLayoutContext.Provider, {
|
|
247
|
+
value: notifyLayout,
|
|
248
|
+
children: /* @__PURE__ */ jsx(m.div, {
|
|
249
|
+
initial: animateIn ? reduce ? { opacity: 0 } : { opacity: 0 } : false,
|
|
250
|
+
animate: { opacity: 1 },
|
|
251
|
+
transition: reduce ? {
|
|
252
|
+
duration: 0.12,
|
|
253
|
+
ease: EASE_OUT
|
|
254
|
+
} : BUBBLE_CONTENT_REVEAL,
|
|
255
|
+
className: "relative",
|
|
256
|
+
children
|
|
257
|
+
})
|
|
258
|
+
})] });
|
|
259
|
+
if (render) {
|
|
260
|
+
const child = render;
|
|
261
|
+
return cloneElement(child, {
|
|
262
|
+
...props,
|
|
263
|
+
ref: mergeRefs(child.props.ref, ref),
|
|
264
|
+
className: cn(classes, child.props.className),
|
|
265
|
+
children: composedChildren,
|
|
266
|
+
"data-slot": "message-bubble-content"
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
return /* @__PURE__ */ jsx("div", {
|
|
270
|
+
ref,
|
|
271
|
+
"data-slot": "message-bubble-content",
|
|
272
|
+
className: classes,
|
|
273
|
+
...props,
|
|
274
|
+
children: composedChildren
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
function DefaultPreview({ item }) {
|
|
278
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
279
|
+
"data-slot": "preview-rail-card",
|
|
280
|
+
className: "rounded-2xl border border-border bg-card p-4 shadow-sm",
|
|
281
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
282
|
+
"data-slot": "preview-rail-title",
|
|
283
|
+
className: "font-medium text-card-foreground",
|
|
284
|
+
children: item.label
|
|
285
|
+
}), item.description ? /* @__PURE__ */ jsx("div", {
|
|
286
|
+
"data-slot": "preview-rail-description",
|
|
287
|
+
className: "mt-1 text-sm leading-6 text-muted-foreground",
|
|
288
|
+
children: item.description
|
|
289
|
+
}) : null]
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
function PreviewRail({ items, label = "Section navigation", orientation = "vertical", activeId, defaultActiveId, onActiveChange, onItemSelect, renderPreview, showPreview = true, previewSide = "after", highlightActive = false, itemSize = 24, children, className, railClassName, previewContainerClassName, previewClassName }) {
|
|
293
|
+
const uid = useId();
|
|
294
|
+
const reduce = useReducedMotion();
|
|
295
|
+
const canHover = useHoverCapable();
|
|
296
|
+
const [internalActiveId, setInternalActiveId] = useState(defaultActiveId ?? items[0]?.id ?? "");
|
|
297
|
+
const [hoveredId, setHoveredId] = useState(null);
|
|
298
|
+
const [focusedId, setFocusedId] = useState(null);
|
|
299
|
+
const requestedActiveId = activeId ?? internalActiveId;
|
|
300
|
+
const selectedId = items.some((item) => item.id === requestedActiveId) ? requestedActiveId : items[0]?.id ?? "";
|
|
301
|
+
const displayedId = hoveredId ?? focusedId ?? "";
|
|
302
|
+
const highlightedId = displayedId || (highlightActive ? selectedId : "");
|
|
303
|
+
const displayedIndex = items.findIndex((item) => item.id === highlightedId);
|
|
304
|
+
const rowTemplate = items.length ? `repeat(${items.length}, ${itemSize}px)` : void 0;
|
|
305
|
+
const isHorizontal = orientation === "horizontal";
|
|
306
|
+
const selectItem = (id) => {
|
|
307
|
+
if (activeId === void 0) setInternalActiveId(id);
|
|
308
|
+
onActiveChange?.(id);
|
|
309
|
+
};
|
|
310
|
+
return /* @__PURE__ */ jsxs(m.div, {
|
|
311
|
+
layoutRoot: true,
|
|
312
|
+
onBlur: (event) => {
|
|
313
|
+
if (!event.currentTarget.contains(event.relatedTarget)) setFocusedId(null);
|
|
314
|
+
},
|
|
315
|
+
className: cn("isolate relative flex w-full overflow-visible", isHorizontal ? "min-h-64 flex-col items-center justify-center" : "min-h-80", className),
|
|
316
|
+
children: [
|
|
317
|
+
/* @__PURE__ */ jsx("nav", {
|
|
318
|
+
"aria-label": label,
|
|
319
|
+
onPointerLeave: () => setHoveredId(null),
|
|
320
|
+
style: isHorizontal ? { gridTemplateColumns: rowTemplate } : { gridTemplateRows: rowTemplate },
|
|
321
|
+
className: cn("relative z-10 grid shrink-0", isHorizontal ? "h-12 w-fit max-w-full self-center justify-center" : "w-12 content-center", railClassName),
|
|
322
|
+
children: items.map((item, index) => {
|
|
323
|
+
const selected = item.id === selectedId;
|
|
324
|
+
const highlighted = item.id === highlightedId;
|
|
325
|
+
const distance = displayedIndex < 0 ? Number.POSITIVE_INFINITY : Math.abs(index - displayedIndex);
|
|
326
|
+
const scale = highlighted ? 1 : distance === 1 ? 0.68 : distance === 2 ? 0.44 : 0.25;
|
|
327
|
+
const itemContent = /* @__PURE__ */ jsx(Fragment$1, { children: /* @__PURE__ */ jsx(m.span, {
|
|
328
|
+
"data-slot": "preview-rail-tick",
|
|
329
|
+
"aria-hidden": "true",
|
|
330
|
+
animate: isHorizontal ? { scaleY: scale } : { scaleX: scale },
|
|
331
|
+
transition: reduce ? { duration: 0 } : SPRING_LAYOUT,
|
|
332
|
+
className: cn("block bg-current", isHorizontal ? "h-12 w-0.5 origin-bottom" : "h-0.5 w-12 origin-left", highlighted ? "text-foreground" : void 0)
|
|
333
|
+
}) });
|
|
334
|
+
const sharedClassName = cn("relative flex text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background", isHorizontal ? "h-12 w-6 items-end justify-center" : "h-6 w-12 items-center");
|
|
335
|
+
const sharedStyle = isHorizontal ? { width: itemSize } : { height: itemSize };
|
|
336
|
+
const handlePointerEnter = () => {
|
|
337
|
+
if (canHover) setHoveredId(item.id);
|
|
338
|
+
};
|
|
339
|
+
const handleFocus = (currentTarget) => {
|
|
340
|
+
if (currentTarget.matches(":focus-visible")) setFocusedId(item.id);
|
|
341
|
+
};
|
|
342
|
+
const handleSelect = () => {
|
|
343
|
+
selectItem(item.id);
|
|
344
|
+
onItemSelect?.(item);
|
|
345
|
+
};
|
|
346
|
+
return item.href ? /* @__PURE__ */ jsx("a", {
|
|
347
|
+
"data-slot": "preview-rail-item",
|
|
348
|
+
href: item.href,
|
|
349
|
+
target: item.target,
|
|
350
|
+
rel: item.rel ?? (item.target === "_blank" ? "noreferrer noopener" : void 0),
|
|
351
|
+
"aria-label": item.ariaLabel ?? item.label,
|
|
352
|
+
"aria-current": selected ? "page" : void 0,
|
|
353
|
+
onPointerEnter: handlePointerEnter,
|
|
354
|
+
onMouseEnter: handlePointerEnter,
|
|
355
|
+
onPointerDown: () => setFocusedId(null),
|
|
356
|
+
onFocus: (event) => handleFocus(event.currentTarget),
|
|
357
|
+
onClick: handleSelect,
|
|
358
|
+
style: sharedStyle,
|
|
359
|
+
className: sharedClassName,
|
|
360
|
+
children: itemContent
|
|
361
|
+
}, item.id) : /* @__PURE__ */ jsx("button", {
|
|
362
|
+
"data-slot": "preview-rail-item",
|
|
363
|
+
type: "button",
|
|
364
|
+
"aria-label": item.ariaLabel ?? item.label,
|
|
365
|
+
"aria-current": selected ? "location" : void 0,
|
|
366
|
+
onPointerEnter: handlePointerEnter,
|
|
367
|
+
onMouseEnter: handlePointerEnter,
|
|
368
|
+
onPointerDown: () => setFocusedId(null),
|
|
369
|
+
onFocus: (event) => handleFocus(event.currentTarget),
|
|
370
|
+
onClick: handleSelect,
|
|
371
|
+
style: sharedStyle,
|
|
372
|
+
className: sharedClassName,
|
|
373
|
+
children: itemContent
|
|
374
|
+
}, item.id);
|
|
375
|
+
})
|
|
376
|
+
}),
|
|
377
|
+
showPreview ? /* @__PURE__ */ jsx("div", {
|
|
378
|
+
"aria-hidden": "true",
|
|
379
|
+
style: isHorizontal ? { gridTemplateColumns: rowTemplate } : { gridTemplateRows: rowTemplate },
|
|
380
|
+
className: cn("pointer-events-none absolute z-50 grid", isHorizontal ? "top-1/2 left-1/2 h-5 w-fit max-w-full -translate-x-1/2 -translate-y-1/2 justify-center" : previewSide === "before" ? "inset-y-0 right-16 left-4 content-center" : "inset-y-0 right-4 left-16 content-center", previewContainerClassName),
|
|
381
|
+
children: items.map((item) => /* @__PURE__ */ jsx("div", {
|
|
382
|
+
style: isHorizontal ? { width: itemSize } : { height: itemSize },
|
|
383
|
+
className: cn("relative flex items-center", isHorizontal ? "justify-center" : void 0),
|
|
384
|
+
children: /* @__PURE__ */ jsx(AnimatePresence, {
|
|
385
|
+
mode: "wait",
|
|
386
|
+
initial: false,
|
|
387
|
+
children: item.id === displayedId ? /* @__PURE__ */ jsx(m.div, {
|
|
388
|
+
initial: reduce ? { opacity: 0 } : {
|
|
389
|
+
opacity: 0,
|
|
390
|
+
y: 4,
|
|
391
|
+
filter: "blur(6px)"
|
|
392
|
+
},
|
|
393
|
+
animate: reduce ? { opacity: 1 } : {
|
|
394
|
+
opacity: 1,
|
|
395
|
+
y: 0,
|
|
396
|
+
filter: "blur(0px)"
|
|
397
|
+
},
|
|
398
|
+
exit: reduce ? { opacity: 0 } : {
|
|
399
|
+
opacity: 0,
|
|
400
|
+
y: -2,
|
|
401
|
+
filter: "blur(4px)",
|
|
402
|
+
transition: {
|
|
403
|
+
duration: 0.12,
|
|
404
|
+
ease: EASE_OUT
|
|
405
|
+
}
|
|
406
|
+
},
|
|
407
|
+
transition: {
|
|
408
|
+
duration: reduce ? 0 : 0.18,
|
|
409
|
+
ease: EASE_OUT
|
|
410
|
+
},
|
|
411
|
+
className: cn(isHorizontal ? "absolute bottom-12 left-1/2 w-72 -translate-x-1/2" : cn("w-full max-w-sm", previewSide === "before" && "ml-auto"), previewClassName),
|
|
412
|
+
children: /* @__PURE__ */ jsx(m.div, {
|
|
413
|
+
layoutId: `preview-rail-card-${uid}`,
|
|
414
|
+
transition: reduce ? { duration: 0 } : SPRING_LAYOUT,
|
|
415
|
+
children: renderPreview ? renderPreview(item) : /* @__PURE__ */ jsx(DefaultPreview, { item })
|
|
416
|
+
})
|
|
417
|
+
}, item.id) : null
|
|
418
|
+
})
|
|
419
|
+
}, item.id))
|
|
420
|
+
}) : null,
|
|
421
|
+
children ? /* @__PURE__ */ jsx("div", {
|
|
422
|
+
className: "min-h-0 min-w-0 flex-1",
|
|
423
|
+
children
|
|
424
|
+
}) : null
|
|
425
|
+
]
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
var PREVIEW_TITLE_LENGTH = 56;
|
|
429
|
+
var PREVIEW_DESCRIPTION_LENGTH = 88;
|
|
430
|
+
function truncateMessageText(text, limit) {
|
|
431
|
+
if (text.length <= limit) return text;
|
|
432
|
+
const excerpt = text.slice(0, limit);
|
|
433
|
+
const boundary = excerpt.lastIndexOf(" ");
|
|
434
|
+
return `${excerpt.slice(0, boundary > limit * 0.65 ? boundary : limit).trim()}\u2026`;
|
|
435
|
+
}
|
|
436
|
+
function getMessageText(message) {
|
|
437
|
+
return ((message.querySelector('[data-slot="message-bubble-content"]') ?? message.querySelector('[data-slot="message-content"]') ?? message).textContent ?? "").replace(/\s+/g, " ").trim();
|
|
438
|
+
}
|
|
439
|
+
function getMessagePreview(message, assistantResponse) {
|
|
440
|
+
const text = getMessageText(message);
|
|
441
|
+
if (!text) return {
|
|
442
|
+
label: "Message",
|
|
443
|
+
description: void 0
|
|
444
|
+
};
|
|
445
|
+
if (text.length <= PREVIEW_TITLE_LENGTH) {
|
|
446
|
+
const responseText2 = assistantResponse ? getMessageText(assistantResponse) : "";
|
|
447
|
+
return {
|
|
448
|
+
label: text,
|
|
449
|
+
description: responseText2 ? truncateMessageText(responseText2, PREVIEW_DESCRIPTION_LENGTH) : void 0
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
const titleBoundary = text.slice(0, PREVIEW_TITLE_LENGTH).lastIndexOf(" ");
|
|
453
|
+
const titleEnd = titleBoundary > PREVIEW_TITLE_LENGTH * 0.65 ? titleBoundary : PREVIEW_TITLE_LENGTH;
|
|
454
|
+
const label = `${text.slice(0, titleEnd).trim()}\u2026`;
|
|
455
|
+
const responseText = assistantResponse ? getMessageText(assistantResponse) : text.slice(titleEnd).trim();
|
|
456
|
+
return {
|
|
457
|
+
label,
|
|
458
|
+
description: responseText ? truncateMessageText(responseText, PREVIEW_DESCRIPTION_LENGTH) : void 0
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
function useMessageScrollerRail({ navigation, followThreshold, contentRef, viewportRef, reduce, smooth, scrollToEnd, setFollowing, markProgrammaticScroll }) {
|
|
462
|
+
const railFrameRef = useRef(void 0);
|
|
463
|
+
const railIdRef = useRef(/* @__PURE__ */ new WeakMap());
|
|
464
|
+
const railIdCounterRef = useRef(0);
|
|
465
|
+
const railTargetsRef = useRef(/* @__PURE__ */ new Map());
|
|
466
|
+
const [railItems, setRailItems] = useState([]);
|
|
467
|
+
const [activeRailId, setActiveRailId] = useState("");
|
|
468
|
+
const [railOverflowing, setRailOverflowing] = useState(false);
|
|
469
|
+
const updateActiveRailItem = useCallback(() => {
|
|
470
|
+
if (navigation !== "rail") return;
|
|
471
|
+
const viewport = viewportRef.current;
|
|
472
|
+
const targets = [...railTargetsRef.current.entries()];
|
|
473
|
+
if (!viewport || targets.length === 0) return;
|
|
474
|
+
const viewportRect = viewport.getBoundingClientRect();
|
|
475
|
+
if (viewport.scrollTop <= followThreshold) {
|
|
476
|
+
const firstId = targets[0]?.[0] ?? "";
|
|
477
|
+
setActiveRailId((current) => current === firstId ? current : firstId);
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
if (viewport.scrollHeight - viewport.scrollTop - viewport.clientHeight <= followThreshold) {
|
|
481
|
+
const lastId = targets.at(-1)?.[0] ?? "";
|
|
482
|
+
setActiveRailId((current) => current === lastId ? current : lastId);
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
const viewportCenter = viewportRect.top + viewportRect.height / 2;
|
|
486
|
+
let nearestId = targets[0]?.[0] ?? "";
|
|
487
|
+
let nearestDistance = Number.POSITIVE_INFINITY;
|
|
488
|
+
for (const [id, element] of targets) {
|
|
489
|
+
const rect = element.getBoundingClientRect();
|
|
490
|
+
const distance = Math.abs(rect.top + rect.height / 2 - viewportCenter);
|
|
491
|
+
if (distance < nearestDistance) {
|
|
492
|
+
nearestDistance = distance;
|
|
493
|
+
nearestId = id;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
setActiveRailId((current) => current === nearestId ? current : nearestId);
|
|
497
|
+
}, [
|
|
498
|
+
followThreshold,
|
|
499
|
+
navigation,
|
|
500
|
+
viewportRef
|
|
501
|
+
]);
|
|
502
|
+
const syncRailItems = useCallback(() => {
|
|
503
|
+
if (navigation !== "rail") return;
|
|
504
|
+
const content = contentRef.current;
|
|
505
|
+
const viewport = viewportRef.current;
|
|
506
|
+
if (!content || !viewport) return;
|
|
507
|
+
const messages = Array.from(content.querySelectorAll('[data-slot="message"]'));
|
|
508
|
+
const targets = /* @__PURE__ */ new Map();
|
|
509
|
+
const nextItems = messages.map((message, index) => {
|
|
510
|
+
let id = railIdRef.current.get(message);
|
|
511
|
+
if (!id) {
|
|
512
|
+
railIdCounterRef.current += 1;
|
|
513
|
+
id = `message-rail-${railIdCounterRef.current}`;
|
|
514
|
+
railIdRef.current.set(message, id);
|
|
515
|
+
}
|
|
516
|
+
targets.set(id, message);
|
|
517
|
+
const sender = message.dataset.from ?? "conversation";
|
|
518
|
+
const preview = getMessagePreview(message, sender === "user" ? messages.slice(index + 1).find((candidate) => candidate.dataset.from === "assistant") : void 0);
|
|
519
|
+
return {
|
|
520
|
+
id,
|
|
521
|
+
label: preview.label,
|
|
522
|
+
description: preview.description,
|
|
523
|
+
ariaLabel: `Go to ${sender} message ${index + 1} of ${messages.length}`
|
|
524
|
+
};
|
|
525
|
+
});
|
|
526
|
+
railTargetsRef.current = targets;
|
|
527
|
+
setRailItems((current) => {
|
|
528
|
+
return current.length === nextItems.length && current.every((item, index) => item.id === nextItems[index]?.id && item.label === nextItems[index]?.label && item.description === nextItems[index]?.description && item.ariaLabel === nextItems[index]?.ariaLabel) ? current : nextItems;
|
|
529
|
+
});
|
|
530
|
+
setRailOverflowing(viewport.scrollHeight > viewport.clientHeight + 1 && messages.length > 1);
|
|
531
|
+
}, [
|
|
532
|
+
contentRef,
|
|
533
|
+
navigation,
|
|
534
|
+
viewportRef
|
|
535
|
+
]);
|
|
536
|
+
const scheduleRailSync = useCallback(() => {
|
|
537
|
+
if (navigation !== "rail") return;
|
|
538
|
+
if (railFrameRef.current) cancelAnimationFrame(railFrameRef.current);
|
|
539
|
+
railFrameRef.current = requestAnimationFrame(() => {
|
|
540
|
+
syncRailItems();
|
|
541
|
+
updateActiveRailItem();
|
|
542
|
+
});
|
|
543
|
+
}, [
|
|
544
|
+
navigation,
|
|
545
|
+
syncRailItems,
|
|
546
|
+
updateActiveRailItem
|
|
547
|
+
]);
|
|
548
|
+
useEffect(() => {
|
|
549
|
+
if (navigation !== "rail") {
|
|
550
|
+
railTargetsRef.current.clear();
|
|
551
|
+
setRailItems([]);
|
|
552
|
+
setRailOverflowing(false);
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
const content = contentRef.current;
|
|
556
|
+
const viewport = viewportRef.current;
|
|
557
|
+
if (!content || !viewport) return;
|
|
558
|
+
scheduleRailSync();
|
|
559
|
+
const mutationObserver = typeof MutationObserver === "undefined" ? null : new MutationObserver(scheduleRailSync);
|
|
560
|
+
mutationObserver?.observe(content, {
|
|
561
|
+
childList: true,
|
|
562
|
+
characterData: true,
|
|
563
|
+
subtree: true
|
|
564
|
+
});
|
|
565
|
+
const resizeObserver = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(scheduleRailSync);
|
|
566
|
+
resizeObserver?.observe(content);
|
|
567
|
+
resizeObserver?.observe(viewport);
|
|
568
|
+
return () => {
|
|
569
|
+
mutationObserver?.disconnect();
|
|
570
|
+
resizeObserver?.disconnect();
|
|
571
|
+
};
|
|
572
|
+
}, [
|
|
573
|
+
contentRef,
|
|
574
|
+
navigation,
|
|
575
|
+
scheduleRailSync,
|
|
576
|
+
viewportRef
|
|
577
|
+
]);
|
|
578
|
+
useEffect(() => () => {
|
|
579
|
+
if (railFrameRef.current) cancelAnimationFrame(railFrameRef.current);
|
|
580
|
+
}, []);
|
|
581
|
+
return {
|
|
582
|
+
activeRailId,
|
|
583
|
+
railItems,
|
|
584
|
+
railOverflowing,
|
|
585
|
+
scheduleRailSync,
|
|
586
|
+
scrollToRailItem: useCallback((item) => {
|
|
587
|
+
const viewport = viewportRef.current;
|
|
588
|
+
const target2 = railTargetsRef.current.get(item.id);
|
|
589
|
+
if (!viewport || !target2) return;
|
|
590
|
+
const lastItem = railItems.at(-1)?.id === item.id;
|
|
591
|
+
setActiveRailId(item.id);
|
|
592
|
+
if (lastItem) {
|
|
593
|
+
setFollowing(true);
|
|
594
|
+
scrollToEnd(reduce || !smooth ? "auto" : "smooth");
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
setFollowing(false);
|
|
598
|
+
markProgrammaticScroll(reduce || !smooth ? "auto" : "smooth");
|
|
599
|
+
const viewportRect = viewport.getBoundingClientRect();
|
|
600
|
+
const targetRect = target2.getBoundingClientRect();
|
|
601
|
+
const top = viewport.scrollTop + targetRect.top - viewportRect.top - (viewport.clientHeight - targetRect.height) / 2;
|
|
602
|
+
const behavior = reduce || !smooth ? "auto" : "smooth";
|
|
603
|
+
if (typeof viewport.scrollTo === "function") viewport.scrollTo({
|
|
604
|
+
top,
|
|
605
|
+
behavior
|
|
606
|
+
});
|
|
607
|
+
else viewport.scrollTop = top;
|
|
608
|
+
}, [
|
|
609
|
+
markProgrammaticScroll,
|
|
610
|
+
railItems,
|
|
611
|
+
reduce,
|
|
612
|
+
scrollToEnd,
|
|
613
|
+
setFollowing,
|
|
614
|
+
smooth,
|
|
615
|
+
viewportRef
|
|
616
|
+
])
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
function MessageScroller({ followOutput = true, followThreshold = 56, smooth = true, onFollowChange, label = "Conversation", busy, navigation, navigationLabel = "Message navigation", viewportClassName, contentClassName, railClassName, viewportRef: externalViewportRef, viewportProps, contentProps, className, children, ...props }) {
|
|
620
|
+
const reduce = useReducedMotion() ?? false;
|
|
621
|
+
const viewportRef = useRef(null);
|
|
622
|
+
const contentRef = useRef(null);
|
|
623
|
+
const followingRef = useRef(followOutput);
|
|
624
|
+
const programmaticScrollRef = useRef(false);
|
|
625
|
+
const scrollTimerRef = useRef(void 0);
|
|
626
|
+
const frameRef = useRef(void 0);
|
|
627
|
+
const { onScroll: onViewportScroll, onWheel: onViewportWheel, onTouchStart: onViewportTouchStart, onKeyDown: onViewportKeyDown, ...restViewportProps } = viewportProps ?? {};
|
|
628
|
+
const setViewportRef = useCallback((node) => {
|
|
629
|
+
viewportRef.current = node;
|
|
630
|
+
if (typeof externalViewportRef === "function") externalViewportRef(node);
|
|
631
|
+
else if (externalViewportRef) externalViewportRef.current = node;
|
|
632
|
+
}, [externalViewportRef]);
|
|
633
|
+
const setFollowing = useCallback((next) => {
|
|
634
|
+
if (followingRef.current === next) return;
|
|
635
|
+
followingRef.current = next;
|
|
636
|
+
onFollowChange?.(next);
|
|
637
|
+
}, [onFollowChange]);
|
|
638
|
+
const markProgrammaticScroll = useCallback((behavior) => {
|
|
639
|
+
programmaticScrollRef.current = true;
|
|
640
|
+
if (scrollTimerRef.current) window.clearTimeout(scrollTimerRef.current);
|
|
641
|
+
scrollTimerRef.current = window.setTimeout(() => {
|
|
642
|
+
programmaticScrollRef.current = false;
|
|
643
|
+
}, behavior === "smooth" ? 320 : 0);
|
|
644
|
+
}, []);
|
|
645
|
+
const scrollToEnd = useCallback((behavior) => {
|
|
646
|
+
const viewport2 = viewportRef.current;
|
|
647
|
+
if (!viewport2) return;
|
|
648
|
+
markProgrammaticScroll(behavior);
|
|
649
|
+
if (typeof viewport2.scrollTo === "function") viewport2.scrollTo({
|
|
650
|
+
top: viewport2.scrollHeight,
|
|
651
|
+
behavior
|
|
652
|
+
});
|
|
653
|
+
else viewport2.scrollTop = viewport2.scrollHeight;
|
|
654
|
+
}, [markProgrammaticScroll]);
|
|
655
|
+
const { activeRailId, railItems, railOverflowing, scheduleRailSync, scrollToRailItem } = useMessageScrollerRail({
|
|
656
|
+
navigation,
|
|
657
|
+
followThreshold,
|
|
658
|
+
contentRef,
|
|
659
|
+
viewportRef,
|
|
660
|
+
reduce,
|
|
661
|
+
smooth,
|
|
662
|
+
scrollToEnd,
|
|
663
|
+
setFollowing,
|
|
664
|
+
markProgrammaticScroll
|
|
665
|
+
});
|
|
666
|
+
const scheduleRailSyncEvent = useEffectEvent(scheduleRailSync);
|
|
667
|
+
const scrollToEndEvent = useEffectEvent(scrollToEnd);
|
|
668
|
+
const handleScroll = useCallback(() => {
|
|
669
|
+
const viewport2 = viewportRef.current;
|
|
670
|
+
if (!viewport2 || programmaticScrollRef.current) return;
|
|
671
|
+
const distance = viewport2.scrollHeight - viewport2.scrollTop - viewport2.clientHeight;
|
|
672
|
+
setFollowing(distance <= followThreshold);
|
|
673
|
+
scheduleRailSync();
|
|
674
|
+
}, [
|
|
675
|
+
followThreshold,
|
|
676
|
+
scheduleRailSync,
|
|
677
|
+
setFollowing
|
|
678
|
+
]);
|
|
679
|
+
const leaveLiveEdge = useCallback(() => {
|
|
680
|
+
programmaticScrollRef.current = false;
|
|
681
|
+
}, []);
|
|
682
|
+
useLayoutEffect(() => {
|
|
683
|
+
followingRef.current = followOutput;
|
|
684
|
+
if (!followOutput) return;
|
|
685
|
+
frameRef.current = requestAnimationFrame(() => scrollToEndEvent("auto"));
|
|
686
|
+
return () => {
|
|
687
|
+
if (frameRef.current) cancelAnimationFrame(frameRef.current);
|
|
688
|
+
};
|
|
689
|
+
}, [followOutput]);
|
|
690
|
+
useEffect(() => {
|
|
691
|
+
const content = contentRef.current;
|
|
692
|
+
if (!content || typeof ResizeObserver === "undefined") return;
|
|
693
|
+
const observer = new ResizeObserver(() => {
|
|
694
|
+
scheduleRailSyncEvent();
|
|
695
|
+
if (!followOutput || !followingRef.current) return;
|
|
696
|
+
scrollToEndEvent(reduce || !smooth ? "auto" : "smooth");
|
|
697
|
+
});
|
|
698
|
+
observer.observe(content);
|
|
699
|
+
return () => observer.disconnect();
|
|
700
|
+
}, [
|
|
701
|
+
followOutput,
|
|
702
|
+
reduce,
|
|
703
|
+
smooth
|
|
704
|
+
]);
|
|
705
|
+
useEffect(() => () => {
|
|
706
|
+
if (scrollTimerRef.current) window.clearTimeout(scrollTimerRef.current);
|
|
707
|
+
if (frameRef.current) cancelAnimationFrame(frameRef.current);
|
|
708
|
+
}, []);
|
|
709
|
+
const viewport = /* @__PURE__ */ jsx("section", {
|
|
710
|
+
ref: setViewportRef,
|
|
711
|
+
"aria-label": label,
|
|
712
|
+
...restViewportProps,
|
|
713
|
+
onScroll: (event) => {
|
|
714
|
+
handleScroll();
|
|
715
|
+
onViewportScroll?.(event);
|
|
716
|
+
},
|
|
717
|
+
onWheel: (event) => {
|
|
718
|
+
leaveLiveEdge();
|
|
719
|
+
onViewportWheel?.(event);
|
|
720
|
+
},
|
|
721
|
+
onTouchStart: (event) => {
|
|
722
|
+
leaveLiveEdge();
|
|
723
|
+
onViewportTouchStart?.(event);
|
|
724
|
+
},
|
|
725
|
+
onKeyDown: (event) => {
|
|
726
|
+
if ([
|
|
727
|
+
"ArrowUp",
|
|
728
|
+
"PageUp",
|
|
729
|
+
"Home"
|
|
730
|
+
].includes(event.key)) leaveLiveEdge();
|
|
731
|
+
onViewportKeyDown?.(event);
|
|
732
|
+
},
|
|
733
|
+
className: cn("h-full overflow-y-auto overscroll-contain outline-none [overflow-anchor:none] focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring", navigation === "rail" ? "[-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden" : "[scrollbar-gutter:stable]", viewportClassName, navigation === "rail" && railOverflowing && "pr-10"),
|
|
734
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
735
|
+
ref: contentRef,
|
|
736
|
+
role: "log",
|
|
737
|
+
"aria-live": "polite",
|
|
738
|
+
"aria-relevant": "additions text",
|
|
739
|
+
"aria-busy": busy,
|
|
740
|
+
className: contentClassName,
|
|
741
|
+
...contentProps,
|
|
742
|
+
children
|
|
743
|
+
})
|
|
744
|
+
});
|
|
745
|
+
return /* @__PURE__ */ jsx("div", {
|
|
746
|
+
"data-slot": "message-scroller",
|
|
747
|
+
className: cn("min-h-0", className),
|
|
748
|
+
...props,
|
|
749
|
+
children: navigation === "rail" ? /* @__PURE__ */ jsx(PreviewRail, {
|
|
750
|
+
items: railOverflowing ? railItems : [],
|
|
751
|
+
label: navigationLabel,
|
|
752
|
+
activeId: activeRailId,
|
|
753
|
+
onItemSelect: scrollToRailItem,
|
|
754
|
+
previewSide: "before",
|
|
755
|
+
highlightActive: true,
|
|
756
|
+
itemSize: 14,
|
|
757
|
+
className: "h-full min-h-0 overflow-hidden",
|
|
758
|
+
previewContainerClassName: "right-8 left-3",
|
|
759
|
+
previewClassName: "mr-1 w-64 max-w-full [&_[data-slot=preview-rail-card]]:h-20 [&_[data-slot=preview-rail-card]]:overflow-hidden [&_[data-slot=preview-rail-card]]:p-3 [&_[data-slot=preview-rail-title]]:line-clamp-1 [&_[data-slot=preview-rail-title]]:text-xs [&_[data-slot=preview-rail-title]]:leading-4 [&_[data-slot=preview-rail-description]]:line-clamp-2 [&_[data-slot=preview-rail-description]]:text-xs [&_[data-slot=preview-rail-description]]:leading-4",
|
|
760
|
+
railClassName: cn("absolute inset-y-3 right-1 w-7 content-center py-1 [&_[data-slot=preview-rail-item]]:w-7 [&_[data-slot=preview-rail-item]]:justify-end [&_[data-slot=preview-rail-tick]]:h-px [&_[data-slot=preview-rail-tick]]:w-4 [&_[data-slot=preview-rail-tick]]:origin-right", railOverflowing ? "pointer-events-auto opacity-100" : "pointer-events-none opacity-0", railClassName),
|
|
761
|
+
children: viewport
|
|
762
|
+
}) : viewport
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
var MessageContext = createContext({ from: "assistant" });
|
|
766
|
+
var MESSAGE_POP_UP = {
|
|
767
|
+
type: "spring",
|
|
768
|
+
stiffness: 480,
|
|
769
|
+
damping: 32,
|
|
770
|
+
mass: 0.62
|
|
771
|
+
};
|
|
772
|
+
function Message({ from, animateIn = false, children, className, initial, animate, transition, exit, style, ...props }) {
|
|
773
|
+
const reduce = useReducedMotion() ?? false;
|
|
774
|
+
const messageContextValue = useMemo(() => ({ from }), [from]);
|
|
775
|
+
return /* @__PURE__ */ jsx(MessageSideContext.Provider, {
|
|
776
|
+
value: from === "user" ? "end" : "start",
|
|
777
|
+
children: /* @__PURE__ */ jsx(MessageContext.Provider, {
|
|
778
|
+
value: messageContextValue,
|
|
779
|
+
children: /* @__PURE__ */ jsx(m.article, {
|
|
780
|
+
"data-slot": "message",
|
|
781
|
+
"data-from": from,
|
|
782
|
+
"aria-label": props["aria-label"] ?? `${from} message`,
|
|
783
|
+
initial: initial ?? (animateIn && !reduce ? {
|
|
784
|
+
opacity: 0,
|
|
785
|
+
transform: "translateY(8px) scale(0.95)"
|
|
786
|
+
} : false),
|
|
787
|
+
animate: animate ?? (animateIn && !reduce ? {
|
|
788
|
+
opacity: 1,
|
|
789
|
+
transform: "translateY(0px) scale(1)"
|
|
790
|
+
} : { opacity: 1 }),
|
|
791
|
+
exit: exit ?? (reduce ? { opacity: 0 } : {
|
|
792
|
+
opacity: 0,
|
|
793
|
+
transform: "translateY(-3px) scale(0.99)"
|
|
794
|
+
}),
|
|
795
|
+
transition: transition ?? (reduce ? { duration: 0.12 } : MESSAGE_POP_UP),
|
|
796
|
+
style: {
|
|
797
|
+
transformOrigin: from === "user" ? "100% 100%" : "0% 100%",
|
|
798
|
+
...style
|
|
799
|
+
},
|
|
800
|
+
className: cn("group/message flex w-full items-start gap-2", from === "user" ? "flex-row-reverse" : "flex-row", className),
|
|
801
|
+
...props,
|
|
802
|
+
children
|
|
803
|
+
})
|
|
804
|
+
})
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
function MessageContent({ className, ...props }) {
|
|
808
|
+
const { from } = useContext(MessageContext);
|
|
809
|
+
return /* @__PURE__ */ jsx("div", {
|
|
810
|
+
"data-slot": "message-content",
|
|
811
|
+
className: cn("flex min-w-0 flex-1 flex-col gap-1.5", from === "user" ? "items-end" : "items-start", className),
|
|
812
|
+
...props
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
var ErrorMessage = memo(function ErrorMessage2({ title = "Something went wrong", message, className }) {
|
|
816
|
+
return /* @__PURE__ */ jsx("div", {
|
|
817
|
+
className: cn("flex justify-start", className),
|
|
818
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
819
|
+
className: "rounded-[8px] border border-red-500/30 bg-red-500/10 px-4 py-2.5 text-sm text-an-foreground",
|
|
820
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
821
|
+
className: "font-medium text-an-foreground",
|
|
822
|
+
children: title
|
|
823
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
824
|
+
className: "mt-0.5 text-an-foreground-muted",
|
|
825
|
+
children: message
|
|
826
|
+
})]
|
|
827
|
+
})
|
|
828
|
+
});
|
|
829
|
+
});
|
|
830
|
+
function formatPayload(value) {
|
|
831
|
+
if (typeof value === "string") return value;
|
|
832
|
+
if (value === void 0) return "";
|
|
833
|
+
try {
|
|
834
|
+
return JSON.stringify(value, null, 2) ?? "";
|
|
835
|
+
} catch {
|
|
836
|
+
return String(value);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
function PayloadPart({ part }) {
|
|
840
|
+
const payload = formatPayload(part.payload);
|
|
841
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
842
|
+
"data-payload-kind": part.kind,
|
|
843
|
+
className: "w-full min-w-0 overflow-hidden rounded-xl border border-border/60 bg-muted/40 text-sm",
|
|
844
|
+
children: [
|
|
845
|
+
/* @__PURE__ */ jsx("div", {
|
|
846
|
+
className: "border-b border-border/50 px-3 py-2 text-xs font-medium text-muted-foreground",
|
|
847
|
+
children: part.title
|
|
848
|
+
}),
|
|
849
|
+
part.text ? /* @__PURE__ */ jsx("pre", {
|
|
850
|
+
className: "m-0 whitespace-pre-wrap break-words px-3 py-3 font-mono text-xs leading-5 text-foreground/80",
|
|
851
|
+
children: part.text
|
|
852
|
+
}) : null,
|
|
853
|
+
payload ? /* @__PURE__ */ jsxs("details", {
|
|
854
|
+
open: true,
|
|
855
|
+
className: "border-t border-border/50 px-3 py-2",
|
|
856
|
+
children: [/* @__PURE__ */ jsx("summary", {
|
|
857
|
+
className: "cursor-pointer text-xs font-medium text-muted-foreground",
|
|
858
|
+
children: "Payload"
|
|
859
|
+
}), /* @__PURE__ */ jsx("pre", {
|
|
860
|
+
className: "m-0 mt-2 max-w-full whitespace-pre-wrap break-words font-mono text-xs leading-5 text-foreground/70",
|
|
861
|
+
children: payload
|
|
862
|
+
})]
|
|
863
|
+
}) : null
|
|
864
|
+
]
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
function isRecord$2(value) {
|
|
868
|
+
return typeof value === "object" && value !== null;
|
|
869
|
+
}
|
|
870
|
+
function parseStructuredJson(value) {
|
|
871
|
+
if (typeof value !== "string") return value;
|
|
872
|
+
const trimmed = value.trim();
|
|
873
|
+
if (!trimmed) return value;
|
|
874
|
+
try {
|
|
875
|
+
const parsed = JSON.parse(trimmed);
|
|
876
|
+
return isRecord$2(parsed) || Array.isArray(parsed) ? parsed : value;
|
|
877
|
+
} catch {
|
|
878
|
+
return value;
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
function normalizeToolPart(part) {
|
|
882
|
+
if (!isRecord$2(part)) return part;
|
|
883
|
+
if (typeof part.type !== "string" || !part.type.startsWith("tool-")) return part;
|
|
884
|
+
const normalizedInput = parseStructuredJson(part.input);
|
|
885
|
+
const normalizedOutput = parseStructuredJson(part.output);
|
|
886
|
+
const normalizedResult = parseStructuredJson(part.result);
|
|
887
|
+
const inputChanged = normalizedInput !== part.input;
|
|
888
|
+
const outputChanged = normalizedOutput !== part.output;
|
|
889
|
+
const resultChanged = normalizedResult !== part.result;
|
|
890
|
+
if (!inputChanged && !outputChanged && !resultChanged) return part;
|
|
891
|
+
const normalizedPart = { ...part };
|
|
892
|
+
if (inputChanged) normalizedPart.input = normalizedInput;
|
|
893
|
+
if (outputChanged) normalizedPart.output = normalizedOutput;
|
|
894
|
+
if (resultChanged) normalizedPart.result = normalizedResult;
|
|
895
|
+
return normalizedPart;
|
|
896
|
+
}
|
|
897
|
+
function normalizeAssistantToolParts(parts) {
|
|
898
|
+
let changed = false;
|
|
899
|
+
const normalizedParts = parts.map((part) => {
|
|
900
|
+
const normalizedPart = normalizeToolPart(part);
|
|
901
|
+
if (normalizedPart !== part) changed = true;
|
|
902
|
+
return normalizedPart;
|
|
903
|
+
});
|
|
904
|
+
return changed ? normalizedParts : parts;
|
|
905
|
+
}
|
|
906
|
+
var timeFormatter = new Intl.DateTimeFormat("en-US", {
|
|
907
|
+
hour: "numeric",
|
|
908
|
+
minute: "2-digit",
|
|
909
|
+
hour12: true
|
|
910
|
+
});
|
|
911
|
+
var dateFormatter = new Intl.DateTimeFormat("en-US", {
|
|
912
|
+
month: "short",
|
|
913
|
+
day: "numeric"
|
|
914
|
+
});
|
|
915
|
+
function formatTimestamp(date) {
|
|
916
|
+
const now = /* @__PURE__ */ new Date();
|
|
917
|
+
if (date.getFullYear() === now.getFullYear() && date.getMonth() === now.getMonth() && date.getDate() === now.getDate()) return timeFormatter.format(date);
|
|
918
|
+
return dateFormatter.format(date);
|
|
919
|
+
}
|
|
920
|
+
function CopyButton({ text, onCopied }) {
|
|
921
|
+
const [copied, setCopied] = useState(false);
|
|
922
|
+
const copiedTimerRef = useRef(null);
|
|
923
|
+
useEffect(() => {
|
|
924
|
+
return () => {
|
|
925
|
+
if (copiedTimerRef.current) window.clearTimeout(copiedTimerRef.current);
|
|
926
|
+
};
|
|
927
|
+
}, []);
|
|
928
|
+
const handleCopy = async () => {
|
|
929
|
+
try {
|
|
930
|
+
await navigator.clipboard.writeText(text);
|
|
931
|
+
setCopied(true);
|
|
932
|
+
if (copiedTimerRef.current) window.clearTimeout(copiedTimerRef.current);
|
|
933
|
+
copiedTimerRef.current = window.setTimeout(() => {
|
|
934
|
+
setCopied(false);
|
|
935
|
+
copiedTimerRef.current = null;
|
|
936
|
+
}, 2e3);
|
|
937
|
+
notify.success("Copied to clipboard");
|
|
938
|
+
onCopied?.();
|
|
939
|
+
} catch {
|
|
940
|
+
notify.error("Failed to copy to clipboard");
|
|
941
|
+
}
|
|
942
|
+
};
|
|
943
|
+
return /* @__PURE__ */ jsx("button", {
|
|
944
|
+
type: "button",
|
|
945
|
+
tabIndex: -1,
|
|
946
|
+
"aria-label": copied ? "Copied" : "Copy message",
|
|
947
|
+
onClick: handleCopy,
|
|
948
|
+
onPointerDown: (event) => {
|
|
949
|
+
event.stopPropagation();
|
|
950
|
+
},
|
|
951
|
+
onMouseDown: (event) => event.stopPropagation(),
|
|
952
|
+
className: cn("flex size-6 items-center justify-center rounded-md transition-[background-color,opacity,transform] duration-150 ease-out active:scale-[0.97]", "bg-transparent opacity-50 hover:bg-an-foreground/10 hover:opacity-100"),
|
|
953
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
954
|
+
className: "relative h-3.5 w-3.5",
|
|
955
|
+
children: [/* @__PURE__ */ jsx(Copy, { className: cn("absolute inset-0 h-3.5 w-3.5 text-an-foreground-muted transition-[opacity,transform] duration-150 ease-out", copied ? "scale-50 opacity-0" : "scale-100 opacity-100") }), /* @__PURE__ */ jsx(Check, { className: cn("absolute inset-0 h-3.5 w-3.5 text-an-foreground-muted transition-[opacity,transform] duration-150 ease-out", copied ? "scale-100 opacity-100" : "scale-50 opacity-0") })]
|
|
956
|
+
})
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
function MessageToolbar({ text, timestamp, heightClass, hoverClass, isVisible, alignClass, onCopied }) {
|
|
960
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
961
|
+
className: cn("pointer-events-none flex items-center gap-1 pt-1 text-xs text-an-foreground-muted/70 opacity-0 transition-opacity duration-100", heightClass, alignClass, hoverClass, isVisible && "pointer-events-auto opacity-100"),
|
|
962
|
+
onMouseDown: (event) => event.stopPropagation(),
|
|
963
|
+
onPointerDown: (event) => event.stopPropagation(),
|
|
964
|
+
children: [timestamp && /* @__PURE__ */ jsx("span", { children: timestamp }), text && /* @__PURE__ */ jsx(CopyButton, {
|
|
965
|
+
text,
|
|
966
|
+
onCopied
|
|
967
|
+
})]
|
|
968
|
+
});
|
|
969
|
+
}
|
|
970
|
+
function isRecord$1(value) {
|
|
971
|
+
return typeof value === "object" && value !== null;
|
|
972
|
+
}
|
|
973
|
+
function isTextPart(part) {
|
|
974
|
+
return isRecord$1(part) && part.type === "text" && typeof part.text === "string";
|
|
975
|
+
}
|
|
976
|
+
function isErrorPart(part) {
|
|
977
|
+
return isRecord$1(part) && part.type === "error" && typeof part.message === "string";
|
|
978
|
+
}
|
|
979
|
+
function isPayloadPart(part) {
|
|
980
|
+
return isRecord$1(part) && part.type === "payload" && typeof part.kind === "string" && typeof part.title === "string";
|
|
981
|
+
}
|
|
982
|
+
function isV5ToolPart(part) {
|
|
983
|
+
if (!isRecord$1(part)) return false;
|
|
984
|
+
const partType = part.type;
|
|
985
|
+
return partType === "dynamic-tool" || typeof partType === "string" && partType.startsWith("tool-");
|
|
986
|
+
}
|
|
987
|
+
function getTextFromParts(parts, joiner) {
|
|
988
|
+
const texts = [];
|
|
989
|
+
for (const part of parts) if (isTextPart(part)) texts.push(part.text);
|
|
990
|
+
return texts.join(joiner);
|
|
991
|
+
}
|
|
992
|
+
memo(function UserTurn(props) {
|
|
993
|
+
const { message, UserMessageComponent, userMessageClassName, onCopied, display } = props;
|
|
994
|
+
const { enableImagePreview, showCopyToolbar, isMounted, isCopyVisible } = display;
|
|
995
|
+
const text = getTextFromParts(message.parts ?? [], "");
|
|
996
|
+
const hasParts = (message.parts ?? []).length > 0;
|
|
997
|
+
if (!text && !hasParts) return null;
|
|
998
|
+
const userCreatedAt = message?.createdAt;
|
|
999
|
+
const userCopyKey = `user-${message.id}`;
|
|
1000
|
+
const userTimestamp = isMounted && userCreatedAt ? formatTimestamp(new Date(userCreatedAt)) : void 0;
|
|
1001
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1002
|
+
className: "group/user-message",
|
|
1003
|
+
children: [/* @__PURE__ */ jsx(UserMessageComponent, {
|
|
1004
|
+
message,
|
|
1005
|
+
className: userMessageClassName,
|
|
1006
|
+
enableImagePreview
|
|
1007
|
+
}), (showCopyToolbar && Boolean(text) || Boolean(userTimestamp)) && /* @__PURE__ */ jsx(MessageToolbar, {
|
|
1008
|
+
text: showCopyToolbar ? text : "",
|
|
1009
|
+
timestamp: userTimestamp,
|
|
1010
|
+
heightClass: "h-[28px]",
|
|
1011
|
+
hoverClass: "group-hover/user-message:opacity-100 group-hover/user-message:pointer-events-auto",
|
|
1012
|
+
isVisible: isCopyVisible,
|
|
1013
|
+
alignClass: "justify-end",
|
|
1014
|
+
onCopied: () => onCopied(userCopyKey)
|
|
1015
|
+
})]
|
|
1016
|
+
});
|
|
1017
|
+
});
|
|
1018
|
+
memo(function AssistantTurn(props) {
|
|
1019
|
+
const { assistantMsgs, turnKey, ToolRendererComponent, TextRendererComponent, toolRenderers, onOpenUIAction, onOpenUIArtifactReady, onOpenArtifact, onCopied, stream, copy, display } = props;
|
|
1020
|
+
const { isLast: isLastTurn, isStreaming } = stream;
|
|
1021
|
+
const { enabled: showCopyToolbar, isVisible: isCopyVisible } = copy;
|
|
1022
|
+
const { suppressQuestionTool } = display;
|
|
1023
|
+
const assistantText = getTextFromParts(assistantMsgs.flatMap((msg) => msg.parts ?? []), "\n\n");
|
|
1024
|
+
const isTurnStreaming = isStreaming && isLastTurn;
|
|
1025
|
+
const showToolbar = showCopyToolbar && Boolean(assistantText.trim()) && !isTurnStreaming;
|
|
1026
|
+
const copyKey = `assistant-${turnKey}-all`;
|
|
1027
|
+
const toolbarText = showCopyToolbar ? assistantText : "";
|
|
1028
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1029
|
+
className: "group/assistant-turn",
|
|
1030
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
1031
|
+
className: "flex flex-col gap-3",
|
|
1032
|
+
children: assistantMsgs.map((msg, i) => {
|
|
1033
|
+
const isLastMsg = isLastTurn && i === assistantMsgs.length - 1;
|
|
1034
|
+
return /* @__PURE__ */ jsx(AssistantParts, {
|
|
1035
|
+
msg,
|
|
1036
|
+
isLast: isLastMsg,
|
|
1037
|
+
isStreaming,
|
|
1038
|
+
suppressQuestionTool,
|
|
1039
|
+
ToolRendererComponent,
|
|
1040
|
+
TextRendererComponent,
|
|
1041
|
+
toolRenderers,
|
|
1042
|
+
onOpenUIAction,
|
|
1043
|
+
onOpenUIArtifactReady,
|
|
1044
|
+
onOpenArtifact
|
|
1045
|
+
}, msg.id);
|
|
1046
|
+
})
|
|
1047
|
+
}), (showToolbar || isCopyVisible) && /* @__PURE__ */ jsx(MessageToolbar, {
|
|
1048
|
+
text: toolbarText,
|
|
1049
|
+
heightClass: "h-[48px] flex items-start w-full",
|
|
1050
|
+
hoverClass: "group-hover/assistant-turn:opacity-100 group-hover/assistant-turn:pointer-events-auto",
|
|
1051
|
+
isVisible: isCopyVisible,
|
|
1052
|
+
alignClass: "justify-start",
|
|
1053
|
+
onCopied: () => onCopied(copyKey)
|
|
1054
|
+
})]
|
|
1055
|
+
});
|
|
1056
|
+
});
|
|
1057
|
+
var TOOL_CALL_KEY_PREFIX = "tool-call:";
|
|
1058
|
+
var UNKEYED_TOOL_KEY_PREFIX = "unkeyed-tool:";
|
|
1059
|
+
function getAssistantToolElementKey(messageId, part, occurrence) {
|
|
1060
|
+
if (part.toolCallId) return `${TOOL_CALL_KEY_PREFIX}${part.toolCallId}`;
|
|
1061
|
+
return `${UNKEYED_TOOL_KEY_PREFIX}${messageId}:${part.type}:${occurrence}`;
|
|
1062
|
+
}
|
|
1063
|
+
function buildAssistantElements(parts, opts) {
|
|
1064
|
+
const { messageId, isLast, isStreaming, suppressQuestionTool, suppressTextWhenPlanWrite = false, ToolRendererComponent, TextRendererComponent, toolRenderers, onOpenUIAction, onOpenUIArtifactReady, onOpenArtifact } = opts;
|
|
1065
|
+
const elems = [];
|
|
1066
|
+
const textChunks = [];
|
|
1067
|
+
const hasPlanWritePresentation = suppressTextWhenPlanWrite && parts.some((part) => isV5ToolPart(part) && part.type === "tool-PlanWrite");
|
|
1068
|
+
const taskPartIds = new Set(parts.filter((p) => isV5ToolPart(p) && (p.type === "tool-Task" || p.type === "tool-Agent") && typeof p.toolCallId === "string").map((p) => p.toolCallId));
|
|
1069
|
+
const nestedToolsMap = /* @__PURE__ */ new Map();
|
|
1070
|
+
const nestedToolIds = /* @__PURE__ */ new Set();
|
|
1071
|
+
const unkeyedToolOccurrences = /* @__PURE__ */ new Map();
|
|
1072
|
+
for (const part of parts) {
|
|
1073
|
+
if (!isV5ToolPart(part)) continue;
|
|
1074
|
+
if (part.type === "tool-TaskOutput") continue;
|
|
1075
|
+
if (!part.toolCallId || !part.toolCallId.includes(":")) continue;
|
|
1076
|
+
const parentId = part.toolCallId.split(":")[0];
|
|
1077
|
+
if (!taskPartIds.has(parentId)) continue;
|
|
1078
|
+
if (!nestedToolsMap.has(parentId)) nestedToolsMap.set(parentId, []);
|
|
1079
|
+
nestedToolsMap.get(parentId).push(part);
|
|
1080
|
+
nestedToolIds.add(part.toolCallId);
|
|
1081
|
+
}
|
|
1082
|
+
let i = 0;
|
|
1083
|
+
while (i < parts.length) {
|
|
1084
|
+
const part = parts[i];
|
|
1085
|
+
if (isV5ToolPart(part) && part.type === "tool-TaskOutput") {
|
|
1086
|
+
i++;
|
|
1087
|
+
continue;
|
|
1088
|
+
}
|
|
1089
|
+
if (isTextPart(part)) {
|
|
1090
|
+
const text2 = part.text;
|
|
1091
|
+
if (text2 && !hasPlanWritePresentation) textChunks.push(text2);
|
|
1092
|
+
i++;
|
|
1093
|
+
continue;
|
|
1094
|
+
}
|
|
1095
|
+
if (isErrorPart(part)) {
|
|
1096
|
+
elems.push(/* @__PURE__ */ jsx(ErrorMessage, {
|
|
1097
|
+
title: part.title,
|
|
1098
|
+
message: part.message
|
|
1099
|
+
}, `${messageId}-error-${part.title}-${part.message}`));
|
|
1100
|
+
i++;
|
|
1101
|
+
continue;
|
|
1102
|
+
}
|
|
1103
|
+
if (isPayloadPart(part)) {
|
|
1104
|
+
elems.push(/* @__PURE__ */ jsx(PayloadPart, { part }, `${messageId}-payload-${i}`));
|
|
1105
|
+
i++;
|
|
1106
|
+
continue;
|
|
1107
|
+
}
|
|
1108
|
+
if (isV5ToolPart(part)) {
|
|
1109
|
+
if (suppressQuestionTool && part.type === "tool-Question") {
|
|
1110
|
+
i++;
|
|
1111
|
+
continue;
|
|
1112
|
+
}
|
|
1113
|
+
if (part.toolCallId && nestedToolIds.has(part.toolCallId)) {
|
|
1114
|
+
i++;
|
|
1115
|
+
continue;
|
|
1116
|
+
}
|
|
1117
|
+
const chatStreamingStatus = isLast && isStreaming ? "streaming" : void 0;
|
|
1118
|
+
const toolCallId = part.toolCallId;
|
|
1119
|
+
const nestedTools = (part.type === "tool-Task" || part.type === "tool-Agent") && toolCallId ? nestedToolsMap.get(toolCallId) || [] : void 0;
|
|
1120
|
+
const occurrence = part.toolCallId ? 0 : unkeyedToolOccurrences.get(part.type) ?? 0;
|
|
1121
|
+
if (!part.toolCallId) unkeyedToolOccurrences.set(part.type, occurrence + 1);
|
|
1122
|
+
elems.push(/* @__PURE__ */ jsx(ToolRendererComponent, {
|
|
1123
|
+
part,
|
|
1124
|
+
nestedTools,
|
|
1125
|
+
chatStatus: chatStreamingStatus,
|
|
1126
|
+
toolRenderers
|
|
1127
|
+
}, getAssistantToolElementKey(messageId, part, occurrence)));
|
|
1128
|
+
i++;
|
|
1129
|
+
continue;
|
|
1130
|
+
}
|
|
1131
|
+
i++;
|
|
1132
|
+
}
|
|
1133
|
+
const text = textChunks.join("\n\n");
|
|
1134
|
+
if (text) elems.push(/* @__PURE__ */ jsx("div", {
|
|
1135
|
+
className: "group/assistant-text text-[14px]",
|
|
1136
|
+
children: /* @__PURE__ */ jsx(TextRendererComponent, {
|
|
1137
|
+
content: text,
|
|
1138
|
+
isStreaming,
|
|
1139
|
+
messageId,
|
|
1140
|
+
onOpenUIAction,
|
|
1141
|
+
onOpenUIArtifactReady,
|
|
1142
|
+
onOpenArtifact,
|
|
1143
|
+
className: "leading-relaxed [&_p]:leading-relaxed"
|
|
1144
|
+
})
|
|
1145
|
+
}, `${messageId}-text-final`));
|
|
1146
|
+
return elems;
|
|
1147
|
+
}
|
|
1148
|
+
function AssistantParts({ msg, isLast, isStreaming, suppressQuestionTool, ToolRendererComponent, TextRendererComponent, toolRenderers, onOpenUIAction, onOpenUIArtifactReady, onOpenArtifact }) {
|
|
1149
|
+
const parts = useMemo(() => normalizeAssistantToolParts(msg.parts ?? []), [msg.parts]);
|
|
1150
|
+
const elements = useMemo(() => buildAssistantElements(parts, {
|
|
1151
|
+
messageId: msg.id,
|
|
1152
|
+
isLast,
|
|
1153
|
+
isStreaming,
|
|
1154
|
+
suppressQuestionTool,
|
|
1155
|
+
ToolRendererComponent,
|
|
1156
|
+
TextRendererComponent,
|
|
1157
|
+
toolRenderers,
|
|
1158
|
+
onOpenUIAction,
|
|
1159
|
+
onOpenUIArtifactReady,
|
|
1160
|
+
onOpenArtifact
|
|
1161
|
+
}), [
|
|
1162
|
+
parts,
|
|
1163
|
+
msg.id,
|
|
1164
|
+
isLast,
|
|
1165
|
+
isStreaming,
|
|
1166
|
+
suppressQuestionTool,
|
|
1167
|
+
ToolRendererComponent,
|
|
1168
|
+
TextRendererComponent,
|
|
1169
|
+
toolRenderers,
|
|
1170
|
+
onOpenUIAction,
|
|
1171
|
+
onOpenUIArtifactReady,
|
|
1172
|
+
onOpenArtifact
|
|
1173
|
+
]);
|
|
1174
|
+
if (elements.length > 1) return /* @__PURE__ */ jsx("div", {
|
|
1175
|
+
className: "group/assistant-turn flex flex-col gap-3",
|
|
1176
|
+
children: elements
|
|
1177
|
+
});
|
|
1178
|
+
return /* @__PURE__ */ jsx("div", {
|
|
1179
|
+
className: "group/assistant-turn",
|
|
1180
|
+
children: elements
|
|
1181
|
+
});
|
|
1182
|
+
}
|
|
1183
|
+
function ImageLightbox({ open, onClose, images, initialIndex = 0 }) {
|
|
1184
|
+
const [currentIndex, setCurrentIndex] = useState(initialIndex);
|
|
1185
|
+
const hasMultipleImages = images.length > 1;
|
|
1186
|
+
useEffect(() => {
|
|
1187
|
+
if (open) setCurrentIndex(initialIndex);
|
|
1188
|
+
}, [open, initialIndex]);
|
|
1189
|
+
const goToPrevious = useCallback((event) => {
|
|
1190
|
+
event?.stopPropagation();
|
|
1191
|
+
setCurrentIndex((prev) => prev > 0 ? prev - 1 : images.length - 1);
|
|
1192
|
+
}, [images.length]);
|
|
1193
|
+
const goToNext = useCallback((event) => {
|
|
1194
|
+
event?.stopPropagation();
|
|
1195
|
+
setCurrentIndex((prev) => prev < images.length - 1 ? prev + 1 : 0);
|
|
1196
|
+
}, [images.length]);
|
|
1197
|
+
const onLightboxKeyDown = useEffectEvent((event) => {
|
|
1198
|
+
switch (event.key) {
|
|
1199
|
+
case "ArrowLeft":
|
|
1200
|
+
event.preventDefault();
|
|
1201
|
+
if (hasMultipleImages) goToPrevious();
|
|
1202
|
+
break;
|
|
1203
|
+
case "ArrowRight":
|
|
1204
|
+
event.preventDefault();
|
|
1205
|
+
if (hasMultipleImages) goToNext();
|
|
1206
|
+
}
|
|
1207
|
+
});
|
|
1208
|
+
useEffect(() => {
|
|
1209
|
+
if (!open) return;
|
|
1210
|
+
const handler = (event) => onLightboxKeyDown(event);
|
|
1211
|
+
window.addEventListener("keydown", handler, true);
|
|
1212
|
+
return () => window.removeEventListener("keydown", handler, true);
|
|
1213
|
+
}, [open]);
|
|
1214
|
+
useEffect(() => {
|
|
1215
|
+
if (!open) return;
|
|
1216
|
+
const previousOverflow = document.body.style.overflow;
|
|
1217
|
+
document.body.style.overflow = "hidden";
|
|
1218
|
+
return () => {
|
|
1219
|
+
document.body.style.overflow = previousOverflow;
|
|
1220
|
+
};
|
|
1221
|
+
}, [open]);
|
|
1222
|
+
const dialogRef = useRef(null);
|
|
1223
|
+
const onLightDismiss = useEffectEvent((event) => {
|
|
1224
|
+
if (dialogRef.current && event.target === dialogRef.current) onClose();
|
|
1225
|
+
});
|
|
1226
|
+
useEffect(() => {
|
|
1227
|
+
if (!open) return;
|
|
1228
|
+
const dialog = dialogRef.current;
|
|
1229
|
+
if (!dialog) return;
|
|
1230
|
+
if (!dialog.open) dialog.showModal();
|
|
1231
|
+
const handler = (event) => onLightDismiss(event);
|
|
1232
|
+
dialog.addEventListener("click", handler);
|
|
1233
|
+
return () => {
|
|
1234
|
+
dialog.removeEventListener("click", handler);
|
|
1235
|
+
if (dialog.open) dialog.close();
|
|
1236
|
+
};
|
|
1237
|
+
}, [open]);
|
|
1238
|
+
if (typeof document === "undefined") return null;
|
|
1239
|
+
if (!open) return null;
|
|
1240
|
+
const currentImage = images[currentIndex] ?? images[0];
|
|
1241
|
+
if (!currentImage?.url) return null;
|
|
1242
|
+
return createPortal(/* @__PURE__ */ jsxs("dialog", {
|
|
1243
|
+
ref: dialogRef,
|
|
1244
|
+
"aria-label": "Image viewer",
|
|
1245
|
+
onClose: () => onClose(),
|
|
1246
|
+
className: "fixed inset-0 z-50 m-0 flex h-full max-h-none w-full max-w-none items-center justify-center border-0 bg-black/90 p-0 backdrop:bg-transparent",
|
|
1247
|
+
children: [
|
|
1248
|
+
/* @__PURE__ */ jsx("button", {
|
|
1249
|
+
type: "button",
|
|
1250
|
+
onClick: onClose,
|
|
1251
|
+
"aria-label": "Close fullscreen (Esc)",
|
|
1252
|
+
className: "absolute top-4 right-4 z-10 inline-flex size-9 items-center justify-center rounded-full bg-black/50 text-white transition-colors hover:bg-black/70",
|
|
1253
|
+
children: /* @__PURE__ */ jsx(X, { className: "size-5" })
|
|
1254
|
+
}),
|
|
1255
|
+
hasMultipleImages && /* @__PURE__ */ jsx("button", {
|
|
1256
|
+
type: "button",
|
|
1257
|
+
onClick: goToPrevious,
|
|
1258
|
+
"aria-label": "Previous image (\u2190)",
|
|
1259
|
+
className: "absolute top-1/2 left-4 z-10 inline-flex size-10 -translate-y-1/2 items-center justify-center rounded-full bg-black/50 text-white transition-colors hover:bg-black/70",
|
|
1260
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-6" })
|
|
1261
|
+
}),
|
|
1262
|
+
/* @__PURE__ */ jsx("img", {
|
|
1263
|
+
src: currentImage.url,
|
|
1264
|
+
alt: currentImage.filename ?? "Image preview",
|
|
1265
|
+
className: "max-h-[85vh] max-w-[90vw] object-contain select-none",
|
|
1266
|
+
onClick: (event) => event.stopPropagation(),
|
|
1267
|
+
draggable: false
|
|
1268
|
+
}),
|
|
1269
|
+
hasMultipleImages && /* @__PURE__ */ jsx("button", {
|
|
1270
|
+
type: "button",
|
|
1271
|
+
onClick: goToNext,
|
|
1272
|
+
"aria-label": "Next image (\u2192)",
|
|
1273
|
+
className: "absolute top-1/2 right-4 z-10 inline-flex size-10 -translate-y-1/2 items-center justify-center rounded-full bg-black/50 text-white transition-colors hover:bg-black/70",
|
|
1274
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-6" })
|
|
1275
|
+
}),
|
|
1276
|
+
hasMultipleImages && /* @__PURE__ */ jsxs("div", {
|
|
1277
|
+
className: "absolute bottom-6 left-1/2 flex -translate-x-1/2 flex-col items-center gap-3",
|
|
1278
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
1279
|
+
className: "flex gap-2",
|
|
1280
|
+
children: images.map((image, idx) => /* @__PURE__ */ jsx("button", {
|
|
1281
|
+
type: "button",
|
|
1282
|
+
onClick: (event) => {
|
|
1283
|
+
event.stopPropagation();
|
|
1284
|
+
setCurrentIndex(idx);
|
|
1285
|
+
},
|
|
1286
|
+
"aria-label": `Go to image ${idx + 1}`,
|
|
1287
|
+
className: cn("size-2 rounded-full transition-all", idx === currentIndex ? "scale-125 bg-white" : "bg-white/40 hover:bg-white/60")
|
|
1288
|
+
}, image.url))
|
|
1289
|
+
}), /* @__PURE__ */ jsxs("span", {
|
|
1290
|
+
className: "text-sm text-white/70",
|
|
1291
|
+
children: [
|
|
1292
|
+
currentIndex + 1,
|
|
1293
|
+
" / ",
|
|
1294
|
+
images.length
|
|
1295
|
+
]
|
|
1296
|
+
})]
|
|
1297
|
+
})
|
|
1298
|
+
]
|
|
1299
|
+
}), document.body);
|
|
1300
|
+
}
|
|
1301
|
+
function formatFileSize(bytes) {
|
|
1302
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
1303
|
+
if (bytes < 1048576) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
1304
|
+
return `${(bytes / 1048576).toFixed(1)} MB`;
|
|
1305
|
+
}
|
|
1306
|
+
function getFileIconName(filename, isImage) {
|
|
1307
|
+
if (isImage) return "image";
|
|
1308
|
+
const ext = filename.split(".").pop()?.toLowerCase();
|
|
1309
|
+
if ([
|
|
1310
|
+
"js",
|
|
1311
|
+
"ts",
|
|
1312
|
+
"jsx",
|
|
1313
|
+
"tsx",
|
|
1314
|
+
"py",
|
|
1315
|
+
"rb",
|
|
1316
|
+
"go",
|
|
1317
|
+
"rs",
|
|
1318
|
+
"java",
|
|
1319
|
+
"kt",
|
|
1320
|
+
"swift",
|
|
1321
|
+
"c",
|
|
1322
|
+
"cpp",
|
|
1323
|
+
"h",
|
|
1324
|
+
"hpp",
|
|
1325
|
+
"cs",
|
|
1326
|
+
"php"
|
|
1327
|
+
].includes(ext || "")) return "code";
|
|
1328
|
+
if ([
|
|
1329
|
+
"json",
|
|
1330
|
+
"yaml",
|
|
1331
|
+
"yml",
|
|
1332
|
+
"xml"
|
|
1333
|
+
].includes(ext || "")) return "data";
|
|
1334
|
+
return "text";
|
|
1335
|
+
}
|
|
1336
|
+
function renderFileIcon(iconName) {
|
|
1337
|
+
switch (iconName) {
|
|
1338
|
+
case "image":
|
|
1339
|
+
return /* @__PURE__ */ jsx(Image, { className: "size-4 text-muted-foreground" });
|
|
1340
|
+
case "code":
|
|
1341
|
+
return /* @__PURE__ */ jsx(FileCode, { className: "size-4 text-muted-foreground" });
|
|
1342
|
+
case "data":
|
|
1343
|
+
return /* @__PURE__ */ jsx(FileBraces, { className: "size-4 text-muted-foreground" });
|
|
1344
|
+
default:
|
|
1345
|
+
return /* @__PURE__ */ jsx(FileText, { className: "size-4 text-muted-foreground" });
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
function FileAttachment({ id, filename, size, isImage, url, onRemove, className, display = "chip", enableImagePreview = true }) {
|
|
1349
|
+
const [isHovered, setIsHovered] = useState(false);
|
|
1350
|
+
const [isLightboxOpen, setIsLightboxOpen] = useState(false);
|
|
1351
|
+
const iconName = getFileIconName(filename, isImage);
|
|
1352
|
+
const isImageOnly = display === "image-only" && isImage && !!url;
|
|
1353
|
+
const canPreview = Boolean(enableImagePreview && isImage && url);
|
|
1354
|
+
const openLightbox = (event) => {
|
|
1355
|
+
event?.stopPropagation();
|
|
1356
|
+
setIsLightboxOpen(true);
|
|
1357
|
+
};
|
|
1358
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1359
|
+
className: cn("relative rounded-[calc(var(--an-input-border-radius)-var(--an-context-padding))] bg-muted/50", isImageOnly ? "flex size-10 items-center justify-center" : "flex max-w-[200px] min-w-[120px] items-center gap-2 py-1 pr-2 pl-1", className),
|
|
1360
|
+
onMouseEnter: () => setIsHovered(true),
|
|
1361
|
+
onMouseLeave: () => setIsHovered(false),
|
|
1362
|
+
children: [
|
|
1363
|
+
isImageOnly ? /* @__PURE__ */ jsx("div", {
|
|
1364
|
+
className: cn("size-8 shrink-0 overflow-hidden rounded-[calc(var(--an-input-border-radius)-var(--an-context-padding)-2px)]", canPreview && "cursor-pointer"),
|
|
1365
|
+
onClick: canPreview ? openLightbox : void 0,
|
|
1366
|
+
role: canPreview ? "button" : void 0,
|
|
1367
|
+
tabIndex: canPreview ? 0 : void 0,
|
|
1368
|
+
"aria-label": canPreview ? `Preview ${filename}` : void 0,
|
|
1369
|
+
onKeyDown: canPreview ? (event) => {
|
|
1370
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
1371
|
+
event.preventDefault();
|
|
1372
|
+
openLightbox();
|
|
1373
|
+
}
|
|
1374
|
+
} : void 0,
|
|
1375
|
+
children: /* @__PURE__ */ jsx("img", {
|
|
1376
|
+
src: url,
|
|
1377
|
+
alt: filename,
|
|
1378
|
+
className: "h-full w-full object-cover"
|
|
1379
|
+
})
|
|
1380
|
+
}) : /* @__PURE__ */ jsxs(Fragment$1, { children: [isImage && url ? /* @__PURE__ */ jsx("div", {
|
|
1381
|
+
className: cn("w-8 shrink-0 self-stretch overflow-hidden rounded-[calc(var(--an-input-border-radius)-var(--an-context-padding)-2px)]", canPreview && "cursor-pointer"),
|
|
1382
|
+
onClick: canPreview ? openLightbox : void 0,
|
|
1383
|
+
role: canPreview ? "button" : void 0,
|
|
1384
|
+
tabIndex: canPreview ? 0 : void 0,
|
|
1385
|
+
"aria-label": canPreview ? `Preview ${filename}` : void 0,
|
|
1386
|
+
onKeyDown: canPreview ? (event) => {
|
|
1387
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
1388
|
+
event.preventDefault();
|
|
1389
|
+
openLightbox();
|
|
1390
|
+
}
|
|
1391
|
+
} : void 0,
|
|
1392
|
+
children: /* @__PURE__ */ jsx("img", {
|
|
1393
|
+
src: url,
|
|
1394
|
+
alt: filename,
|
|
1395
|
+
className: "aspect-square h-full w-full object-cover"
|
|
1396
|
+
})
|
|
1397
|
+
}) : /* @__PURE__ */ jsx("div", {
|
|
1398
|
+
className: "flex w-8 shrink-0 items-center justify-center self-stretch rounded-[calc(var(--an-input-border-radius)-var(--an-context-padding)-2px)] bg-muted",
|
|
1399
|
+
children: renderFileIcon(iconName)
|
|
1400
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
1401
|
+
className: "flex min-w-0 flex-col",
|
|
1402
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
1403
|
+
className: "truncate text-sm font-medium text-foreground",
|
|
1404
|
+
title: filename,
|
|
1405
|
+
children: filename
|
|
1406
|
+
}), size !== void 0 && /* @__PURE__ */ jsx("span", {
|
|
1407
|
+
className: "text-[10px] text-muted-foreground",
|
|
1408
|
+
children: formatFileSize(size)
|
|
1409
|
+
})]
|
|
1410
|
+
})] }),
|
|
1411
|
+
onRemove && /* @__PURE__ */ jsx("button", {
|
|
1412
|
+
onClick: (e) => {
|
|
1413
|
+
e.stopPropagation();
|
|
1414
|
+
onRemove();
|
|
1415
|
+
},
|
|
1416
|
+
"aria-label": `Remove ${filename}`,
|
|
1417
|
+
className: `absolute -top-1.5 -right-1.5 z-10 flex size-4 items-center justify-center rounded-full border border-border bg-background text-muted-foreground transition-[opacity,transform] duration-150 ease-out hover:text-foreground active:scale-[0.97] ${isHovered ? "opacity-100" : "opacity-0"}`,
|
|
1418
|
+
type: "button",
|
|
1419
|
+
children: /* @__PURE__ */ jsx(X, { className: "size-3" })
|
|
1420
|
+
}),
|
|
1421
|
+
canPreview && url && /* @__PURE__ */ jsx(ImageLightbox, {
|
|
1422
|
+
open: isLightboxOpen,
|
|
1423
|
+
onClose: () => setIsLightboxOpen(false),
|
|
1424
|
+
images: [{
|
|
1425
|
+
id,
|
|
1426
|
+
url,
|
|
1427
|
+
filename
|
|
1428
|
+
}]
|
|
1429
|
+
})
|
|
1430
|
+
]
|
|
1431
|
+
});
|
|
1432
|
+
}
|
|
1433
|
+
function isRecord(value) {
|
|
1434
|
+
return typeof value === "object" && value !== null;
|
|
1435
|
+
}
|
|
1436
|
+
function getImageUrlFromPart(part) {
|
|
1437
|
+
if (!isRecord(part)) return null;
|
|
1438
|
+
const type = part.type;
|
|
1439
|
+
if (typeof type !== "string") return null;
|
|
1440
|
+
if (type === "image") {
|
|
1441
|
+
const imagePart = part;
|
|
1442
|
+
return imagePart.url ?? imagePart.image ?? null;
|
|
1443
|
+
}
|
|
1444
|
+
if (type === "data-image") return part.data?.url ?? null;
|
|
1445
|
+
if (type === "file") {
|
|
1446
|
+
const filePart = part;
|
|
1447
|
+
if (filePart.mimeType?.startsWith("image/")) {
|
|
1448
|
+
if (filePart.url) return filePart.url;
|
|
1449
|
+
if (filePart.data) return `data:${filePart.mimeType};base64,${filePart.data}`;
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
return null;
|
|
1453
|
+
}
|
|
1454
|
+
function getFileFromPart(part) {
|
|
1455
|
+
if (!isRecord(part)) return null;
|
|
1456
|
+
if (part.type !== "file") return null;
|
|
1457
|
+
const filePart = part;
|
|
1458
|
+
const filename = filePart.filename || filePart.name || filePart.fileName || "Attachment";
|
|
1459
|
+
if (filePart.mimeType?.startsWith("image/") ?? false) return null;
|
|
1460
|
+
return {
|
|
1461
|
+
filename,
|
|
1462
|
+
size: filePart.size
|
|
1463
|
+
};
|
|
1464
|
+
}
|
|
1465
|
+
var UserMessage = memo(function UserMessage2({ message, className, enableImagePreview = true }) {
|
|
1466
|
+
const [lightboxIndex, setLightboxIndex] = useState(null);
|
|
1467
|
+
const text = (message.parts?.filter(isTextPart) ?? []).map((p) => p.text).join("");
|
|
1468
|
+
const images = [];
|
|
1469
|
+
const files = [];
|
|
1470
|
+
for (const part of message.parts ?? []) {
|
|
1471
|
+
const imageUrl = getImageUrlFromPart(part);
|
|
1472
|
+
if (imageUrl) images.push(imageUrl);
|
|
1473
|
+
const file = getFileFromPart(part);
|
|
1474
|
+
if (file) files.push(file);
|
|
1475
|
+
}
|
|
1476
|
+
if (isRecord(message) && Array.isArray(message.experimental_attachments)) {
|
|
1477
|
+
for (const att of message.experimental_attachments) if (att.contentType?.startsWith("image/") && att.url) images.push(att.url);
|
|
1478
|
+
}
|
|
1479
|
+
if (!text && images.length === 0 && files.length === 0) return null;
|
|
1480
|
+
const lightboxImages = images.map((url, i) => ({
|
|
1481
|
+
id: `${message.id}-img-${i}`,
|
|
1482
|
+
url,
|
|
1483
|
+
filename: `image-${i + 1}`
|
|
1484
|
+
}));
|
|
1485
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1486
|
+
className: cn("flex flex-col items-end gap-1", className),
|
|
1487
|
+
children: [
|
|
1488
|
+
images.length > 0 && images.map((url, i) => /* @__PURE__ */ jsx("div", {
|
|
1489
|
+
className: cn("max-w-[200px] rounded-an-message bg-an-foreground/4 p-1.5", enableImagePreview && "cursor-pointer"),
|
|
1490
|
+
onClick: enableImagePreview ? () => setLightboxIndex(i) : void 0,
|
|
1491
|
+
role: enableImagePreview ? "button" : void 0,
|
|
1492
|
+
tabIndex: enableImagePreview ? 0 : void 0,
|
|
1493
|
+
onKeyDown: enableImagePreview ? (event) => {
|
|
1494
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
1495
|
+
event.preventDefault();
|
|
1496
|
+
setLightboxIndex(i);
|
|
1497
|
+
}
|
|
1498
|
+
} : void 0,
|
|
1499
|
+
children: /* @__PURE__ */ jsx("img", {
|
|
1500
|
+
src: url,
|
|
1501
|
+
alt: "attachment",
|
|
1502
|
+
className: "block max-h-[120px] max-w-[184px] rounded-an-message-inner object-cover"
|
|
1503
|
+
})
|
|
1504
|
+
}, url)),
|
|
1505
|
+
enableImagePreview && lightboxImages.length > 0 && /* @__PURE__ */ jsx(ImageLightbox, {
|
|
1506
|
+
open: lightboxIndex !== null,
|
|
1507
|
+
onClose: () => setLightboxIndex(null),
|
|
1508
|
+
images: lightboxImages,
|
|
1509
|
+
initialIndex: lightboxIndex ?? 0
|
|
1510
|
+
}),
|
|
1511
|
+
files.length > 0 && /* @__PURE__ */ jsx("div", {
|
|
1512
|
+
className: "flex flex-col items-end gap-2",
|
|
1513
|
+
children: files.map((file, i) => /* @__PURE__ */ jsx(FileAttachment, {
|
|
1514
|
+
id: `${file.filename}-${i}`,
|
|
1515
|
+
filename: file.filename,
|
|
1516
|
+
size: file.size
|
|
1517
|
+
}, `${file.filename}-${file.size}`))
|
|
1518
|
+
}),
|
|
1519
|
+
text && /* @__PURE__ */ jsx("div", {
|
|
1520
|
+
className: "ms-[70px] max-w-[calc(95%-40px)]",
|
|
1521
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
1522
|
+
className: "rounded-an-message bg-an-user-message-bg px-3.5 py-1.5 text-sm text-an-user-message-text transition-colors",
|
|
1523
|
+
children: /* @__PURE__ */ jsx("p", {
|
|
1524
|
+
className: "leading-5 wrap-break-word whitespace-pre-wrap",
|
|
1525
|
+
children: text
|
|
1526
|
+
})
|
|
1527
|
+
})
|
|
1528
|
+
})
|
|
1529
|
+
]
|
|
1530
|
+
});
|
|
1531
|
+
});
|
|
1532
|
+
var LazyGenerativeTextRenderer = lazy(() => import("./openui-renderer-Cg4RSE9k-7BTJIYZJ.js").then((n) => n.n).then(({ GenerativeTextRenderer }) => ({ default: GenerativeTextRenderer })));
|
|
1533
|
+
function PlainTextFallback({ className, content }) {
|
|
1534
|
+
return /* @__PURE__ */ jsx("div", {
|
|
1535
|
+
className: className ? `${className} whitespace-pre-wrap` : "whitespace-pre-wrap",
|
|
1536
|
+
children: content
|
|
1537
|
+
});
|
|
1538
|
+
}
|
|
1539
|
+
function FleetGenerativeTextRenderer(props) {
|
|
1540
|
+
if (!useMemo(() => segmentOpenUIContent(props.content).some((segment) => segment.type === "openui"), [props.content])) return /* @__PURE__ */ jsx(LazyMarkdown, {
|
|
1541
|
+
className: props.className,
|
|
1542
|
+
content: props.content
|
|
1543
|
+
});
|
|
1544
|
+
return /* @__PURE__ */ jsx(Suspense, {
|
|
1545
|
+
fallback: /* @__PURE__ */ jsx(PlainTextFallback, {
|
|
1546
|
+
className: props.className,
|
|
1547
|
+
content: props.content
|
|
1548
|
+
}),
|
|
1549
|
+
children: /* @__PURE__ */ jsx(LazyGenerativeTextRenderer, { ...props })
|
|
1550
|
+
});
|
|
1551
|
+
}
|
|
1552
|
+
function orderedRlmChildren(children, tree) {
|
|
1553
|
+
const byId = new Map(children.map((child) => [child.id, child]));
|
|
1554
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1555
|
+
const ordered = [];
|
|
1556
|
+
const visit = (id) => {
|
|
1557
|
+
if (visited.has(id)) return;
|
|
1558
|
+
visited.add(id);
|
|
1559
|
+
const child = byId.get(id);
|
|
1560
|
+
if (child) ordered.push(child);
|
|
1561
|
+
for (const nextId of tree?.nodes[id]?.childrenIds ?? []) visit(nextId);
|
|
1562
|
+
};
|
|
1563
|
+
for (const id of tree?.rootChildrenIds ?? []) visit(id);
|
|
1564
|
+
for (const child of children) visit(child.id);
|
|
1565
|
+
return ordered;
|
|
1566
|
+
}
|
|
1567
|
+
function rlmStatusIcon(status2) {
|
|
1568
|
+
if (status2 === "done") return /* @__PURE__ */ jsx(CircleCheck, { className: "size-3.5 text-emerald-600 dark:text-emerald-400" });
|
|
1569
|
+
if (status2 === "error") return /* @__PURE__ */ jsx(CircleAlert, { className: "size-3.5 text-destructive" });
|
|
1570
|
+
if (status2 === "cancelled") return /* @__PURE__ */ jsx(CircleX, { className: "size-3.5 text-muted-foreground" });
|
|
1571
|
+
return /* @__PURE__ */ jsx(LoaderCircle, { className: "size-3.5 animate-spin text-blue-600 dark:text-blue-400" });
|
|
1572
|
+
}
|
|
1573
|
+
function FleetSubagentList({ children, tree, className }) {
|
|
1574
|
+
const agents = useMemo(() => orderedRlmChildren(children, tree), [children, tree]);
|
|
1575
|
+
const active = agents.some((child) => child.status === "queued" || child.status === "running");
|
|
1576
|
+
const [open, setOpen] = useState(active);
|
|
1577
|
+
if (agents.length === 0) return null;
|
|
1578
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
1579
|
+
className: cn("mt-2 rounded-xl border border-border/60 bg-muted/20", className),
|
|
1580
|
+
children: [/* @__PURE__ */ jsxs("button", {
|
|
1581
|
+
type: "button",
|
|
1582
|
+
"aria-expanded": open || active,
|
|
1583
|
+
onClick: () => setOpen((current) => !current),
|
|
1584
|
+
className: "flex w-full items-center gap-2 px-3 py-2 text-left text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
1585
|
+
children: [
|
|
1586
|
+
active ? /* @__PURE__ */ jsx(LoaderCircle, { className: "size-3.5 animate-spin text-blue-600 dark:text-blue-400" }) : /* @__PURE__ */ jsx(CircleCheck, { className: "size-3.5 text-emerald-600 dark:text-emerald-400" }),
|
|
1587
|
+
/* @__PURE__ */ jsx("span", {
|
|
1588
|
+
className: "flex-1 font-medium",
|
|
1589
|
+
children: active ? "Subagents working" : "Subagents completed"
|
|
1590
|
+
}),
|
|
1591
|
+
/* @__PURE__ */ jsx("span", {
|
|
1592
|
+
className: "text-xs text-muted-foreground",
|
|
1593
|
+
children: agents.length
|
|
1594
|
+
}),
|
|
1595
|
+
/* @__PURE__ */ jsx(ChevronDown, { className: cn("size-3.5 text-muted-foreground transition-transform", (open || active) && "rotate-180") })
|
|
1596
|
+
]
|
|
1597
|
+
}), open || active ? /* @__PURE__ */ jsx("div", {
|
|
1598
|
+
className: "space-y-1 border-t border-border/50 px-3 py-2",
|
|
1599
|
+
children: agents.map((child) => /* @__PURE__ */ jsxs("div", {
|
|
1600
|
+
className: "flex min-w-0 items-start gap-2",
|
|
1601
|
+
style: { paddingLeft: `${Math.max(0, child.depth ?? 0) * 12}px` },
|
|
1602
|
+
children: [
|
|
1603
|
+
/* @__PURE__ */ jsx("span", {
|
|
1604
|
+
className: "mt-0.5 shrink-0",
|
|
1605
|
+
children: rlmStatusIcon(child.status)
|
|
1606
|
+
}),
|
|
1607
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1608
|
+
className: "min-w-0 flex-1",
|
|
1609
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
1610
|
+
className: "flex min-w-0 items-baseline gap-2",
|
|
1611
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
1612
|
+
className: "truncate text-sm text-foreground/85",
|
|
1613
|
+
children: child.label
|
|
1614
|
+
}), child.model ? /* @__PURE__ */ jsx("span", {
|
|
1615
|
+
className: "truncate font-mono text-[10px] text-muted-foreground",
|
|
1616
|
+
children: child.model
|
|
1617
|
+
}) : null]
|
|
1618
|
+
}), child.answerPreview || child.activity?.toolName || child.error ? /* @__PURE__ */ jsx("p", {
|
|
1619
|
+
className: "truncate text-xs text-muted-foreground",
|
|
1620
|
+
children: child.error ?? child.answerPreview ?? child.activity?.toolName
|
|
1621
|
+
}) : null]
|
|
1622
|
+
}),
|
|
1623
|
+
/* @__PURE__ */ jsx("span", {
|
|
1624
|
+
className: "shrink-0 text-[10px] capitalize text-muted-foreground",
|
|
1625
|
+
children: child.status
|
|
1626
|
+
})
|
|
1627
|
+
]
|
|
1628
|
+
}, child.id))
|
|
1629
|
+
}) : null]
|
|
1630
|
+
});
|
|
1631
|
+
}
|
|
1632
|
+
function record(value) {
|
|
1633
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
1634
|
+
}
|
|
1635
|
+
function target(input, fallback) {
|
|
1636
|
+
const source = record(input);
|
|
1637
|
+
for (const key of [
|
|
1638
|
+
"path",
|
|
1639
|
+
"filePath",
|
|
1640
|
+
"command",
|
|
1641
|
+
"cmd",
|
|
1642
|
+
"query",
|
|
1643
|
+
"pattern",
|
|
1644
|
+
"code"
|
|
1645
|
+
]) if (typeof source?.[key] === "string" && source[key]) return source[key];
|
|
1646
|
+
return fallback;
|
|
1647
|
+
}
|
|
1648
|
+
function status(part, streaming) {
|
|
1649
|
+
const state = part.state;
|
|
1650
|
+
if (state === "output-error" || state === "error") return "error";
|
|
1651
|
+
if (state === "cancelled" || state === "canceled" || state === "aborted") return "cancelled";
|
|
1652
|
+
if (state === "input-streaming" || state === "streaming") return streaming ? "running" : "cancelled";
|
|
1653
|
+
return streaming && part.output === void 0 && part.result === void 0 ? "running" : "complete";
|
|
1654
|
+
}
|
|
1655
|
+
function toolStep(part, fallbackId, streaming) {
|
|
1656
|
+
const type = part.type;
|
|
1657
|
+
if (typeof type !== "string" || !type.startsWith("tool-") || type === "tool-FleetReasoning" || type === "tool-Thinking") return void 0;
|
|
1658
|
+
const name = type.slice(5);
|
|
1659
|
+
const lower = name.toLowerCase();
|
|
1660
|
+
const icon = lower.includes("edit") || lower.includes("write") || lower.includes("patch") ? FilePenLine : lower.includes("read") || lower.includes("search") || lower.includes("grep") || lower.includes("glob") ? FileSearch : lower.includes("bash") || lower.includes("shell") || lower.includes("python") || lower.includes("ipython") ? Terminal : Wrench;
|
|
1661
|
+
const verb = lower.includes("edit") || lower.includes("write") || lower.includes("patch") ? "Edited" : lower.includes("read") ? "Read" : lower.includes("search") || lower.includes("grep") || lower.includes("glob") ? "Searched" : lower.includes("bash") || lower.includes("shell") || lower.includes("python") || lower.includes("ipython") ? "Ran" : name;
|
|
1662
|
+
return {
|
|
1663
|
+
id: typeof part.toolCallId === "string" ? part.toolCallId : fallbackId,
|
|
1664
|
+
icon,
|
|
1665
|
+
verb,
|
|
1666
|
+
target: target(part.input ?? part.args, name),
|
|
1667
|
+
status: status(part, streaming)
|
|
1668
|
+
};
|
|
1669
|
+
}
|
|
1670
|
+
function artifactStep(artifact) {
|
|
1671
|
+
const icon = artifact.kind === "diff" ? FilePenLine : artifact.kind === "bash" || artifact.kind === "ipython" ? Terminal : Wrench;
|
|
1672
|
+
return {
|
|
1673
|
+
id: `artifact:${artifact.id}`,
|
|
1674
|
+
icon,
|
|
1675
|
+
verb: artifact.kind === "compaction" ? "Compacted" : artifact.kind === "refinement" ? "Refined" : artifact.kind === "recap" ? "Recapped" : artifact.kind === "rlm" ? "Delegated" : artifact.kind === "diff" ? "Edited" : "Completed",
|
|
1676
|
+
target: artifact.title,
|
|
1677
|
+
status: artifact.status === "success" ? "complete" : artifact.status
|
|
1678
|
+
};
|
|
1679
|
+
}
|
|
1680
|
+
function statusClass(status2) {
|
|
1681
|
+
if (status2 === "running") return "text-blue-600 dark:text-blue-400";
|
|
1682
|
+
if (status2 === "error") return "text-destructive";
|
|
1683
|
+
if (status2 === "cancelled") return "text-muted-foreground";
|
|
1684
|
+
return "text-emerald-600 dark:text-emerald-400";
|
|
1685
|
+
}
|
|
1686
|
+
function FleetToolTimeline({ messages, artifacts = [], streaming, className }) {
|
|
1687
|
+
const steps = useMemo(() => {
|
|
1688
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1689
|
+
const fromMessages = messages.flatMap((message) => message.parts.flatMap((part, index) => {
|
|
1690
|
+
const step = toolStep(record(part) ?? {}, `${message.id}:${index}`, streaming);
|
|
1691
|
+
if (!step || seen.has(step.id)) return [];
|
|
1692
|
+
seen.add(step.id);
|
|
1693
|
+
return [step];
|
|
1694
|
+
}));
|
|
1695
|
+
const fromArtifacts = artifacts.flatMap((artifact) => {
|
|
1696
|
+
if (artifact.sourceToolCallId && seen.has(artifact.sourceToolCallId)) return [];
|
|
1697
|
+
const step = artifactStep(artifact);
|
|
1698
|
+
if (seen.has(step.id)) return [];
|
|
1699
|
+
seen.add(step.id);
|
|
1700
|
+
return [step];
|
|
1701
|
+
});
|
|
1702
|
+
return [...fromMessages, ...fromArtifacts];
|
|
1703
|
+
}, [
|
|
1704
|
+
artifacts,
|
|
1705
|
+
messages,
|
|
1706
|
+
streaming
|
|
1707
|
+
]);
|
|
1708
|
+
const [open, setOpen] = useState(true);
|
|
1709
|
+
if (steps.length === 0) return null;
|
|
1710
|
+
const active = steps.at(-1);
|
|
1711
|
+
const ActiveIcon = active?.icon;
|
|
1712
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
1713
|
+
className: cn("mb-2 rounded-xl border border-border/60 bg-muted/20", className),
|
|
1714
|
+
children: [/* @__PURE__ */ jsxs("button", {
|
|
1715
|
+
type: "button",
|
|
1716
|
+
"aria-expanded": open,
|
|
1717
|
+
onClick: () => setOpen((current) => !current),
|
|
1718
|
+
className: "flex w-full items-center gap-2 px-3 py-2 text-left text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
1719
|
+
children: [
|
|
1720
|
+
active && ActiveIcon ? /* @__PURE__ */ jsx(ActiveIcon, { className: cn("size-3.5 shrink-0", statusClass(active.status), active.status === "running" && "animate-pulse") }) : null,
|
|
1721
|
+
/* @__PURE__ */ jsx("span", {
|
|
1722
|
+
className: "min-w-0 flex-1 truncate font-medium",
|
|
1723
|
+
children: streaming && active ? `${active.verb} ${active.target}` : `${steps.length} tool ${steps.length === 1 ? "action" : "actions"}`
|
|
1724
|
+
}),
|
|
1725
|
+
/* @__PURE__ */ jsx(ChevronDown, { className: cn("size-3.5 text-muted-foreground transition-transform", open && "rotate-180") })
|
|
1726
|
+
]
|
|
1727
|
+
}), open ? /* @__PURE__ */ jsx("div", {
|
|
1728
|
+
className: "space-y-1 border-t border-border/50 px-3 py-2",
|
|
1729
|
+
children: steps.map((step) => {
|
|
1730
|
+
const Icon = step.icon;
|
|
1731
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1732
|
+
className: "flex min-w-0 items-center gap-2 text-xs",
|
|
1733
|
+
children: [
|
|
1734
|
+
/* @__PURE__ */ jsx(Icon, { className: cn("size-3.5 shrink-0", statusClass(step.status)) }),
|
|
1735
|
+
/* @__PURE__ */ jsx("span", {
|
|
1736
|
+
className: "shrink-0 text-muted-foreground",
|
|
1737
|
+
children: step.verb
|
|
1738
|
+
}),
|
|
1739
|
+
/* @__PURE__ */ jsx("span", {
|
|
1740
|
+
className: "min-w-0 flex-1 truncate font-mono text-foreground/75",
|
|
1741
|
+
children: step.target
|
|
1742
|
+
})
|
|
1743
|
+
]
|
|
1744
|
+
}, step.id);
|
|
1745
|
+
})
|
|
1746
|
+
}) : null]
|
|
1747
|
+
});
|
|
1748
|
+
}
|
|
1749
|
+
function groupMessages(messages) {
|
|
1750
|
+
const turns = [];
|
|
1751
|
+
let current;
|
|
1752
|
+
for (const message of messages) {
|
|
1753
|
+
if (message.role === "user") {
|
|
1754
|
+
if (current) turns.push(current);
|
|
1755
|
+
current = {
|
|
1756
|
+
user: message,
|
|
1757
|
+
assistants: []
|
|
1758
|
+
};
|
|
1759
|
+
continue;
|
|
1760
|
+
}
|
|
1761
|
+
if (message.role !== "assistant") continue;
|
|
1762
|
+
if (!current || message.source === "local") {
|
|
1763
|
+
if (current) turns.push(current);
|
|
1764
|
+
current = { assistants: [message] };
|
|
1765
|
+
continue;
|
|
1766
|
+
}
|
|
1767
|
+
current.assistants.push(message);
|
|
1768
|
+
}
|
|
1769
|
+
if (current) turns.push(current);
|
|
1770
|
+
return turns;
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
export {
|
|
1774
|
+
ChevronLeft,
|
|
1775
|
+
Image,
|
|
1776
|
+
MessageBubble,
|
|
1777
|
+
MessageBubbleContent,
|
|
1778
|
+
MessageScroller,
|
|
1779
|
+
Message,
|
|
1780
|
+
MessageContent,
|
|
1781
|
+
normalizeAssistantToolParts,
|
|
1782
|
+
buildAssistantElements,
|
|
1783
|
+
FileAttachment,
|
|
1784
|
+
UserMessage,
|
|
1785
|
+
FleetGenerativeTextRenderer,
|
|
1786
|
+
orderedRlmChildren,
|
|
1787
|
+
rlmStatusIcon,
|
|
1788
|
+
FleetSubagentList,
|
|
1789
|
+
FleetToolTimeline,
|
|
1790
|
+
groupMessages
|
|
1791
|
+
};
|
|
1792
|
+
/**
|
|
1793
|
+
* @license lucide-react v1.33.0 - ISC
|
|
1794
|
+
*
|
|
1795
|
+
* This source code is licensed under the ISC license.
|
|
1796
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
1797
|
+
*/
|