@nextclaw/agent-chat-ui 0.5.1 → 0.5.2
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/dist/index.d.ts +115 -31
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +683 -417
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { twMerge } from "tailwind-merge";
|
|
|
5
5
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
7
7
|
import * as SelectPrimitive from "@radix-ui/react-select";
|
|
8
|
-
import { AlertTriangle, ArrowUp, ArrowUpRight, Bot, Brain, BrainCircuit, Check, ChevronDown, ChevronRight, ChevronUp, Code2, Copy, ExternalLink, Eye, File, FileArchive, FileAudio2, FileCode2, FileImage, FileJson2, FileSpreadsheet, FileText, FileVideo2, Globe, Loader2, Minus, Paperclip, Puzzle, Search, Sparkles, Star, Terminal, User, Wrench } from "lucide-react";
|
|
8
|
+
import { AlertTriangle, AppWindow, ArrowUp, ArrowUpRight, Bot, Brain, BrainCircuit, Check, ChevronDown, ChevronRight, ChevronUp, Code2, Copy, ExternalLink, Eye, File, FileArchive, FileAudio2, FileCode2, FileImage, FileJson2, FileSpreadsheet, FileText, FileVideo2, Globe, ImageIcon, Loader2, Minus, Paperclip, Puzzle, Search, Sparkles, Star, Terminal, User, Wrench } from "lucide-react";
|
|
9
9
|
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
10
10
|
import { cva } from "class-variance-authority";
|
|
11
11
|
import { ContentEditable } from "@lexical/react/LexicalContentEditable";
|
|
@@ -30,6 +30,180 @@ import sql from "highlight.js/lib/languages/sql";
|
|
|
30
30
|
import typescript from "highlight.js/lib/languages/typescript";
|
|
31
31
|
import xml from "highlight.js/lib/languages/xml";
|
|
32
32
|
import yaml from "highlight.js/lib/languages/yaml";
|
|
33
|
+
const CHAT_INPUT_SURFACE_SLASH_TRIGGER_SPEC = {
|
|
34
|
+
key: "slash",
|
|
35
|
+
marker: "/"
|
|
36
|
+
};
|
|
37
|
+
function createComposerNodeId() {
|
|
38
|
+
return `composer-${Math.random().toString(36).slice(2, 10)}`;
|
|
39
|
+
}
|
|
40
|
+
function createChatComposerTextNode(text = "") {
|
|
41
|
+
return {
|
|
42
|
+
id: createComposerNodeId(),
|
|
43
|
+
type: "text",
|
|
44
|
+
text
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function createChatComposerTokenNode(params) {
|
|
48
|
+
return {
|
|
49
|
+
id: createComposerNodeId(),
|
|
50
|
+
type: "token",
|
|
51
|
+
tokenKind: params.tokenKind,
|
|
52
|
+
tokenKey: params.tokenKey,
|
|
53
|
+
label: params.label
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function getChatComposerNodeLength(node) {
|
|
57
|
+
return node.type === "text" ? node.text.length : 1;
|
|
58
|
+
}
|
|
59
|
+
function createEmptyChatComposerNodes() {
|
|
60
|
+
return [createChatComposerTextNode("")];
|
|
61
|
+
}
|
|
62
|
+
function createChatComposerNodesFromText(text) {
|
|
63
|
+
return [createChatComposerTextNode(text)];
|
|
64
|
+
}
|
|
65
|
+
function normalizeChatComposerNodes(nodes) {
|
|
66
|
+
const normalized = [];
|
|
67
|
+
for (const node of nodes) {
|
|
68
|
+
if (node.type === "text") {
|
|
69
|
+
if (node.text.length === 0) continue;
|
|
70
|
+
const previous = normalized[normalized.length - 1];
|
|
71
|
+
if (previous?.type === "text") {
|
|
72
|
+
normalized[normalized.length - 1] = {
|
|
73
|
+
...previous,
|
|
74
|
+
text: previous.text + node.text
|
|
75
|
+
};
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
normalized.push(node);
|
|
80
|
+
}
|
|
81
|
+
if (normalized.length === 0) return createEmptyChatComposerNodes();
|
|
82
|
+
return normalized;
|
|
83
|
+
}
|
|
84
|
+
function serializeChatComposerDocument(nodes) {
|
|
85
|
+
return nodes.map((node) => node.type === "text" ? node.text : "").join("");
|
|
86
|
+
}
|
|
87
|
+
function serializeChatComposerPlainText(nodes) {
|
|
88
|
+
return nodes.filter((node) => node.type === "text").map((node) => node.text).join("");
|
|
89
|
+
}
|
|
90
|
+
function extractChatComposerTokenKeys(nodes, tokenKind) {
|
|
91
|
+
const keys = [];
|
|
92
|
+
const keySet = /* @__PURE__ */ new Set();
|
|
93
|
+
for (const node of nodes) {
|
|
94
|
+
if (node.type !== "token" || node.tokenKind !== tokenKind || keySet.has(node.tokenKey)) continue;
|
|
95
|
+
keySet.add(node.tokenKey);
|
|
96
|
+
keys.push(node.tokenKey);
|
|
97
|
+
}
|
|
98
|
+
return keys;
|
|
99
|
+
}
|
|
100
|
+
function buildTrimmedTextEdges(node, nodeStart, rangeStart, rangeEnd) {
|
|
101
|
+
const prefixLength = Math.max(0, rangeStart - nodeStart);
|
|
102
|
+
const suffixLength = Math.max(0, nodeStart + node.text.length - rangeEnd);
|
|
103
|
+
const edges = [];
|
|
104
|
+
if (prefixLength > 0) edges.push({
|
|
105
|
+
...node,
|
|
106
|
+
text: node.text.slice(0, prefixLength)
|
|
107
|
+
});
|
|
108
|
+
if (suffixLength > 0) edges.push({
|
|
109
|
+
...node,
|
|
110
|
+
text: node.text.slice(node.text.length - suffixLength)
|
|
111
|
+
});
|
|
112
|
+
return edges;
|
|
113
|
+
}
|
|
114
|
+
function isNodeOutsideComposerRange(nodeStart, nodeEnd, boundedStart, boundedEnd) {
|
|
115
|
+
return nodeEnd <= boundedStart || nodeStart >= boundedEnd;
|
|
116
|
+
}
|
|
117
|
+
function appendReplacementBeforeNode(nextNodes, replacement, inserted, nodeStart, boundedEnd) {
|
|
118
|
+
if (!inserted && nodeStart >= boundedEnd) {
|
|
119
|
+
nextNodes.push(...replacement);
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
return inserted;
|
|
123
|
+
}
|
|
124
|
+
function appendTextNodeWithReplacement(nextNodes, node, nodeStart, boundedStart, boundedEnd, replacement, inserted) {
|
|
125
|
+
const edges = buildTrimmedTextEdges(node, nodeStart, boundedStart, boundedEnd);
|
|
126
|
+
if (edges[0]) nextNodes.push(edges[0]);
|
|
127
|
+
let didInsert = inserted;
|
|
128
|
+
if (!didInsert) {
|
|
129
|
+
nextNodes.push(...replacement);
|
|
130
|
+
didInsert = true;
|
|
131
|
+
}
|
|
132
|
+
if (edges[1]) nextNodes.push(edges[1]);
|
|
133
|
+
return didInsert;
|
|
134
|
+
}
|
|
135
|
+
function appendTokenNodeWithReplacement(nextNodes, node, replacement, inserted, boundedStart, nodeStart) {
|
|
136
|
+
if (!inserted && boundedStart <= nodeStart) {
|
|
137
|
+
nextNodes.push(...replacement);
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
nextNodes.push(node);
|
|
141
|
+
return inserted;
|
|
142
|
+
}
|
|
143
|
+
function replaceChatComposerRange(nodes, start, end, replacement) {
|
|
144
|
+
const boundedStart = Math.max(0, start);
|
|
145
|
+
const boundedEnd = Math.max(boundedStart, end);
|
|
146
|
+
const nextNodes = [];
|
|
147
|
+
let cursor = 0;
|
|
148
|
+
let inserted = false;
|
|
149
|
+
for (const node of nodes) {
|
|
150
|
+
const nodeLength = getChatComposerNodeLength(node);
|
|
151
|
+
const nodeStart = cursor;
|
|
152
|
+
const nodeEnd = cursor + nodeLength;
|
|
153
|
+
if (isNodeOutsideComposerRange(nodeStart, nodeEnd, boundedStart, boundedEnd)) {
|
|
154
|
+
inserted = appendReplacementBeforeNode(nextNodes, replacement, inserted, nodeStart, boundedEnd);
|
|
155
|
+
nextNodes.push(node);
|
|
156
|
+
cursor = nodeEnd;
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (node.type === "text") inserted = appendTextNodeWithReplacement(nextNodes, node, nodeStart, boundedStart, boundedEnd, replacement, inserted);
|
|
160
|
+
else inserted = appendTokenNodeWithReplacement(nextNodes, node, replacement, inserted, boundedStart, nodeStart);
|
|
161
|
+
cursor = nodeEnd;
|
|
162
|
+
}
|
|
163
|
+
if (!inserted) nextNodes.push(...replacement);
|
|
164
|
+
return normalizeChatComposerNodes(nextNodes);
|
|
165
|
+
}
|
|
166
|
+
function removeChatComposerTokenNodes(nodes, predicate) {
|
|
167
|
+
return normalizeChatComposerNodes(nodes.filter((node) => node.type !== "token" || !predicate(node)));
|
|
168
|
+
}
|
|
169
|
+
function hasTriggerBoundary(prefix, markerStart) {
|
|
170
|
+
return markerStart === 0 || /\s/.test(prefix[markerStart - 1] ?? "");
|
|
171
|
+
}
|
|
172
|
+
function resolveChatComposerInputSurfaceTrigger(nodes, selection, triggerSpec) {
|
|
173
|
+
if (!selection || selection.start !== selection.end) return null;
|
|
174
|
+
const documentText = serializeChatComposerDocument(nodes);
|
|
175
|
+
const caret = selection.end;
|
|
176
|
+
const prefix = documentText.slice(0, caret);
|
|
177
|
+
const markerStart = prefix.lastIndexOf(triggerSpec.marker);
|
|
178
|
+
if (markerStart < 0 || !hasTriggerBoundary(prefix, markerStart)) return null;
|
|
179
|
+
const rawQuery = prefix.slice(markerStart + triggerSpec.marker.length);
|
|
180
|
+
if (/[\s\uFFFC]/.test(rawQuery)) return null;
|
|
181
|
+
return {
|
|
182
|
+
key: triggerSpec.key,
|
|
183
|
+
marker: triggerSpec.marker,
|
|
184
|
+
query: rawQuery.trim().toLowerCase(),
|
|
185
|
+
start: markerStart,
|
|
186
|
+
end: caret
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
function resolveChatComposerActiveInputSurfaceTrigger(nodes, selection, triggerSpecs = [CHAT_INPUT_SURFACE_SLASH_TRIGGER_SPEC]) {
|
|
190
|
+
let activeTrigger = null;
|
|
191
|
+
for (const triggerSpec of triggerSpecs) {
|
|
192
|
+
const trigger = resolveChatComposerInputSurfaceTrigger(nodes, selection, triggerSpec);
|
|
193
|
+
if (!trigger) continue;
|
|
194
|
+
if (!activeTrigger || trigger.start > activeTrigger.start) activeTrigger = trigger;
|
|
195
|
+
}
|
|
196
|
+
return activeTrigger;
|
|
197
|
+
}
|
|
198
|
+
function resolveChatComposerSlashTrigger(nodes, selection) {
|
|
199
|
+
const trigger = resolveChatComposerInputSurfaceTrigger(nodes, selection, CHAT_INPUT_SURFACE_SLASH_TRIGGER_SPEC);
|
|
200
|
+
return trigger ? {
|
|
201
|
+
query: trigger.query,
|
|
202
|
+
start: trigger.start,
|
|
203
|
+
end: trigger.end
|
|
204
|
+
} : null;
|
|
205
|
+
}
|
|
206
|
+
//#endregion
|
|
33
207
|
//#region src/components/chat/hooks/use-active-item-scroll.ts
|
|
34
208
|
const defaultGetItemSelector = (index) => `[data-item-index="${index}"]`;
|
|
35
209
|
function useActiveItemScroll(params) {
|
|
@@ -220,27 +394,77 @@ const ChatUiPrimitives = {
|
|
|
220
394
|
TooltipTrigger: ChatTooltipTrigger
|
|
221
395
|
};
|
|
222
396
|
//#endregion
|
|
223
|
-
//#region src/components/chat/ui/
|
|
224
|
-
const
|
|
225
|
-
const
|
|
226
|
-
const
|
|
227
|
-
const
|
|
228
|
-
const
|
|
229
|
-
function
|
|
397
|
+
//#region src/components/chat/ui/input-surface/chat-input-surface-menu.tsx
|
|
398
|
+
const INPUT_SURFACE_PANEL_MAX_WIDTH = 680;
|
|
399
|
+
const INPUT_SURFACE_PANEL_DESKTOP_SHRINK_RATIO = .82;
|
|
400
|
+
const INPUT_SURFACE_PANEL_DESKTOP_MIN_WIDTH = 560;
|
|
401
|
+
const INPUT_SURFACE_PANEL_MAX_HEIGHT = createChatPopoverAvailableHeightLimit("24rem");
|
|
402
|
+
const INPUT_SURFACE_PANEL_MIN_HEIGHT = createChatPopoverAvailableHeightLimit("240px");
|
|
403
|
+
const ChatInputSurfaceMenu = forwardRef(function ChatInputSurfaceMenu(props, ref) {
|
|
230
404
|
const { Popover, PopoverAnchor, PopoverContent } = ChatUiPrimitives;
|
|
231
405
|
const { elementRef: anchorRef, width: panelWidth } = useElementWidth();
|
|
232
406
|
const listRef = useRef(null);
|
|
233
|
-
const
|
|
407
|
+
const [activeState, setActiveState] = useState({
|
|
408
|
+
index: 0,
|
|
409
|
+
itemsSignature: ""
|
|
410
|
+
});
|
|
411
|
+
const { isOpen, isLoading, items, texts, onSelectItem, onOpenChange, onDetailsPointerDown } = props;
|
|
412
|
+
const itemsSignature = useMemo(() => items.map((item) => item.key).join(""), [items]);
|
|
413
|
+
const hasItemSections = useMemo(() => items.some((item) => Boolean(item.sectionLabel?.trim())), [items]);
|
|
414
|
+
const activeIndex = activeState.itemsSignature === itemsSignature ? activeState.index : 0;
|
|
415
|
+
const activeIndexInRange = items.length === 0 ? 0 : Math.min(activeIndex, items.length - 1);
|
|
416
|
+
const activeItem = items[activeIndexInRange] ?? null;
|
|
417
|
+
const setActiveIndexForCurrentItems = useCallback((nextIndex) => {
|
|
418
|
+
setActiveState((currentState) => {
|
|
419
|
+
const currentIndex = currentState.itemsSignature === itemsSignature ? currentState.index : 0;
|
|
420
|
+
return {
|
|
421
|
+
index: typeof nextIndex === "function" ? nextIndex(currentIndex) : nextIndex,
|
|
422
|
+
itemsSignature
|
|
423
|
+
};
|
|
424
|
+
});
|
|
425
|
+
}, [itemsSignature]);
|
|
234
426
|
const resolvedWidth = useMemo(() => {
|
|
235
427
|
if (!panelWidth) return;
|
|
236
|
-
return Math.min(panelWidth >
|
|
428
|
+
return Math.min(panelWidth > INPUT_SURFACE_PANEL_DESKTOP_MIN_WIDTH ? panelWidth * INPUT_SURFACE_PANEL_DESKTOP_SHRINK_RATIO : panelWidth, INPUT_SURFACE_PANEL_MAX_WIDTH);
|
|
237
429
|
}, [panelWidth]);
|
|
430
|
+
useImperativeHandle(ref, () => ({ handleKeyDown: (event) => {
|
|
431
|
+
if (!isOpen) return false;
|
|
432
|
+
if (event.key === "Escape") {
|
|
433
|
+
event.preventDefault();
|
|
434
|
+
onOpenChange(false);
|
|
435
|
+
return true;
|
|
436
|
+
}
|
|
437
|
+
if (items.length === 0) return false;
|
|
438
|
+
if (event.key === "ArrowDown") {
|
|
439
|
+
event.preventDefault();
|
|
440
|
+
setActiveIndexForCurrentItems((index) => (index + 1) % items.length);
|
|
441
|
+
return true;
|
|
442
|
+
}
|
|
443
|
+
if (event.key === "ArrowUp") {
|
|
444
|
+
event.preventDefault();
|
|
445
|
+
setActiveIndexForCurrentItems((index) => (index - 1 + items.length) % items.length);
|
|
446
|
+
return true;
|
|
447
|
+
}
|
|
448
|
+
if (event.key === "Enter" && !event.shiftKey || event.key === "Tab") {
|
|
449
|
+
event.preventDefault();
|
|
450
|
+
onSelectItem(items[activeIndexInRange]);
|
|
451
|
+
return true;
|
|
452
|
+
}
|
|
453
|
+
return false;
|
|
454
|
+
} }), [
|
|
455
|
+
activeIndexInRange,
|
|
456
|
+
isOpen,
|
|
457
|
+
items,
|
|
458
|
+
onOpenChange,
|
|
459
|
+
onSelectItem,
|
|
460
|
+
setActiveIndexForCurrentItems
|
|
461
|
+
]);
|
|
238
462
|
useActiveItemScroll({
|
|
239
463
|
containerRef: listRef,
|
|
240
|
-
activeIndex,
|
|
464
|
+
activeIndex: activeIndexInRange,
|
|
241
465
|
itemCount: items.length,
|
|
242
466
|
isEnabled: isOpen && !isLoading,
|
|
243
|
-
getItemSelector: (index) => `[data-
|
|
467
|
+
getItemSelector: (index) => `[data-input-surface-index="${index}"]`
|
|
244
468
|
});
|
|
245
469
|
return /* @__PURE__ */ jsxs(Popover, {
|
|
246
470
|
open: isOpen,
|
|
@@ -249,7 +473,7 @@ function ChatSlashMenu(props) {
|
|
|
249
473
|
asChild: true,
|
|
250
474
|
children: /* @__PURE__ */ jsx("div", {
|
|
251
475
|
ref: anchorRef,
|
|
252
|
-
className: "pointer-events-none absolute bottom-
|
|
476
|
+
className: "pointer-events-none absolute bottom-0 left-3 right-3 top-0"
|
|
253
477
|
})
|
|
254
478
|
}), /* @__PURE__ */ jsx(PopoverContent, {
|
|
255
479
|
side: "top",
|
|
@@ -258,46 +482,63 @@ function ChatSlashMenu(props) {
|
|
|
258
482
|
className: "z-[70] flex max-w-[calc(100vw-1.5rem)] flex-col overflow-hidden rounded-2xl border border-gray-200 bg-white/95 p-0 shadow-2xl backdrop-blur-md",
|
|
259
483
|
onOpenAutoFocus: (event) => event.preventDefault(),
|
|
260
484
|
style: {
|
|
261
|
-
maxHeight:
|
|
485
|
+
maxHeight: INPUT_SURFACE_PANEL_MAX_HEIGHT,
|
|
262
486
|
width: resolvedWidth ? `${resolvedWidth}px` : void 0
|
|
263
487
|
},
|
|
264
488
|
children: /* @__PURE__ */ jsxs("div", {
|
|
265
489
|
className: "grid min-h-0 flex-1 grid-cols-[minmax(220px,300px)_minmax(0,1fr)]",
|
|
266
|
-
style: { minHeight:
|
|
490
|
+
style: { minHeight: INPUT_SURFACE_PANEL_MIN_HEIGHT },
|
|
267
491
|
children: [/* @__PURE__ */ jsx("div", {
|
|
268
492
|
ref: listRef,
|
|
269
493
|
role: "listbox",
|
|
270
|
-
"aria-label": texts.
|
|
494
|
+
"aria-label": texts.sectionLabel,
|
|
271
495
|
className: "custom-scrollbar min-h-0 overflow-y-auto overscroll-contain border-r border-gray-200 p-2.5",
|
|
272
496
|
children: isLoading ? /* @__PURE__ */ jsx("div", {
|
|
273
497
|
className: "p-2 text-xs text-gray-500",
|
|
274
|
-
children: texts.
|
|
275
|
-
}) : /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
|
|
498
|
+
children: texts.loadingLabel
|
|
499
|
+
}) : /* @__PURE__ */ jsxs(Fragment$1, { children: [!hasItemSections ? /* @__PURE__ */ jsx("div", {
|
|
276
500
|
className: "mb-2 px-2 text-[11px] font-semibold uppercase tracking-wide text-gray-500",
|
|
277
|
-
children: texts.
|
|
278
|
-
}), items.length === 0 ? /* @__PURE__ */ jsx("div", {
|
|
501
|
+
children: texts.sectionLabel
|
|
502
|
+
}) : null, items.length === 0 ? /* @__PURE__ */ jsx("div", {
|
|
279
503
|
className: "px-2 text-xs text-gray-400",
|
|
280
|
-
children: texts.
|
|
504
|
+
children: texts.emptyLabel
|
|
281
505
|
}) : /* @__PURE__ */ jsx("div", {
|
|
282
506
|
className: "space-y-1",
|
|
283
507
|
children: items.map((item, index) => {
|
|
284
|
-
const
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
"
|
|
289
|
-
"
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
508
|
+
const { key, sectionKey, sectionLabel, title, subtitle } = item;
|
|
509
|
+
const isActive = index === activeIndexInRange;
|
|
510
|
+
const previousItem = items[index - 1];
|
|
511
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
512
|
+
className: "space-y-1",
|
|
513
|
+
children: [hasItemSections && Boolean(sectionLabel?.trim()) && previousItem?.sectionKey !== sectionKey ? /* @__PURE__ */ jsx("div", {
|
|
514
|
+
className: "px-2 pb-0.5 pt-1.5 text-[11px] font-semibold uppercase tracking-wide text-gray-500",
|
|
515
|
+
children: sectionLabel
|
|
516
|
+
}) : null, /* @__PURE__ */ jsxs("button", {
|
|
517
|
+
type: "button",
|
|
518
|
+
role: "option",
|
|
519
|
+
"aria-selected": isActive,
|
|
520
|
+
"data-input-surface-index": index,
|
|
521
|
+
onPointerMove: (event) => {
|
|
522
|
+
if (event.pointerType !== "touch") setActiveIndexForCurrentItems(index);
|
|
523
|
+
},
|
|
524
|
+
onPointerDown: (event) => {
|
|
525
|
+
if (event.button > 0) return;
|
|
526
|
+
event.preventDefault();
|
|
527
|
+
onSelectItem(item);
|
|
528
|
+
},
|
|
529
|
+
onClick: (event) => {
|
|
530
|
+
if (event.detail === 0) onSelectItem(item);
|
|
531
|
+
},
|
|
532
|
+
className: `flex w-full items-start gap-2 rounded-lg px-2 py-1.5 text-left transition ${isActive ? "bg-gray-100 text-gray-900" : "text-gray-700 hover:bg-gray-50"}`,
|
|
533
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
534
|
+
className: "truncate text-xs font-semibold",
|
|
535
|
+
children: title
|
|
536
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
537
|
+
className: "truncate text-xs text-gray-500",
|
|
538
|
+
children: subtitle
|
|
539
|
+
})]
|
|
299
540
|
})]
|
|
300
|
-
},
|
|
541
|
+
}, key);
|
|
301
542
|
})
|
|
302
543
|
})] })
|
|
303
544
|
}), /* @__PURE__ */ jsx("div", {
|
|
@@ -329,17 +570,80 @@ function ChatSlashMenu(props) {
|
|
|
329
570
|
}),
|
|
330
571
|
/* @__PURE__ */ jsx("div", {
|
|
331
572
|
className: "pt-1 text-[11px] text-gray-500",
|
|
332
|
-
children: texts.
|
|
573
|
+
children: activeItem.hintLabel ?? texts.itemHintLabel
|
|
333
574
|
})
|
|
334
575
|
]
|
|
335
576
|
}) : /* @__PURE__ */ jsx("div", {
|
|
336
577
|
className: "text-xs text-gray-500",
|
|
337
|
-
children: texts.
|
|
578
|
+
children: texts.hintLabel
|
|
338
579
|
})
|
|
339
580
|
})]
|
|
340
581
|
})
|
|
341
582
|
})]
|
|
342
583
|
});
|
|
584
|
+
});
|
|
585
|
+
//#endregion
|
|
586
|
+
//#region src/components/chat/ui/input-surface/chat-input-surface-host.tsx
|
|
587
|
+
function getInputSurfaceTriggerIdentity(trigger) {
|
|
588
|
+
return `${trigger.key}:${trigger.marker}:${trigger.start}`;
|
|
589
|
+
}
|
|
590
|
+
function isInputSurfaceCreateEvent(trigger, reason) {
|
|
591
|
+
return reason.type === "insert-text" && reason.text === trigger.marker && trigger.query === "" && trigger.end === trigger.start + trigger.marker.length;
|
|
592
|
+
}
|
|
593
|
+
function resolveInputSurfaceTriggerIdentity(currentIdentity, trigger, reason) {
|
|
594
|
+
if (!trigger) return null;
|
|
595
|
+
const nextIdentity = getInputSurfaceTriggerIdentity(trigger);
|
|
596
|
+
if (currentIdentity === nextIdentity) return currentIdentity;
|
|
597
|
+
return isInputSurfaceCreateEvent(trigger, reason) ? nextIdentity : null;
|
|
598
|
+
}
|
|
599
|
+
function ChatInputSurfaceHost(props) {
|
|
600
|
+
const { children, inputSurface, onInputSurfaceTriggerChange, onSelectItem, triggerSpecs = [CHAT_INPUT_SURFACE_SLASH_TRIGGER_SPEC] } = props;
|
|
601
|
+
const menuRef = useRef(null);
|
|
602
|
+
const [activeTriggerIdentity, setActiveTriggerIdentity] = useState(null);
|
|
603
|
+
const isOpen = Boolean(inputSurface) && activeTriggerIdentity !== null;
|
|
604
|
+
const setActiveTrigger = useCallback((identity, trigger) => {
|
|
605
|
+
setActiveTriggerIdentity(identity);
|
|
606
|
+
onInputSurfaceTriggerChange?.(identity ? trigger : null);
|
|
607
|
+
}, [onInputSurfaceTriggerChange]);
|
|
608
|
+
const closeInputSurface = useCallback(() => {
|
|
609
|
+
setActiveTrigger(null, null);
|
|
610
|
+
}, [setActiveTrigger]);
|
|
611
|
+
const handleInputSurfaceSnapshotChange = useCallback((nodes, selection, reason) => {
|
|
612
|
+
const trigger = resolveChatComposerActiveInputSurfaceTrigger(nodes, selection, triggerSpecs);
|
|
613
|
+
const nextIdentity = resolveInputSurfaceTriggerIdentity(activeTriggerIdentity, trigger, reason);
|
|
614
|
+
setActiveTrigger(nextIdentity, nextIdentity ? trigger : null);
|
|
615
|
+
}, [
|
|
616
|
+
activeTriggerIdentity,
|
|
617
|
+
setActiveTrigger,
|
|
618
|
+
triggerSpecs
|
|
619
|
+
]);
|
|
620
|
+
const handleInputSurfaceKeyDown = useCallback((event) => {
|
|
621
|
+
return menuRef.current?.handleKeyDown(event) ?? false;
|
|
622
|
+
}, []);
|
|
623
|
+
const handleInputSurfaceOpenChange = useCallback((open) => {
|
|
624
|
+
if (!open) closeInputSurface();
|
|
625
|
+
}, [closeInputSurface]);
|
|
626
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [children(useMemo(() => ({
|
|
627
|
+
onInputSurfaceKeyDown: handleInputSurfaceKeyDown,
|
|
628
|
+
onInputSurfaceOpenChange: handleInputSurfaceOpenChange,
|
|
629
|
+
onInputSurfaceSnapshotChange: handleInputSurfaceSnapshotChange
|
|
630
|
+
}), [
|
|
631
|
+
handleInputSurfaceKeyDown,
|
|
632
|
+
handleInputSurfaceOpenChange,
|
|
633
|
+
handleInputSurfaceSnapshotChange
|
|
634
|
+
])), inputSurface && isOpen ? /* @__PURE__ */ jsx(ChatInputSurfaceMenu, {
|
|
635
|
+
ref: menuRef,
|
|
636
|
+
isOpen,
|
|
637
|
+
isLoading: inputSurface.isLoading,
|
|
638
|
+
items: inputSurface.items,
|
|
639
|
+
texts: inputSurface.texts,
|
|
640
|
+
onSelectItem: (item) => {
|
|
641
|
+
closeInputSurface();
|
|
642
|
+
onSelectItem(item);
|
|
643
|
+
},
|
|
644
|
+
onOpenChange: handleInputSurfaceOpenChange,
|
|
645
|
+
onDetailsPointerDown: (event) => event.preventDefault()
|
|
646
|
+
}, activeTriggerIdentity) : null] });
|
|
343
647
|
}
|
|
344
648
|
//#endregion
|
|
345
649
|
//#region src/components/chat/default-skin/button.tsx
|
|
@@ -966,152 +1270,6 @@ function ChatInputBarToolbar({ actions, accessories, selects, skillPicker }) {
|
|
|
966
1270
|
}), /* @__PURE__ */ jsx(ChatInputBarActions, { ...actions })]
|
|
967
1271
|
});
|
|
968
1272
|
}
|
|
969
|
-
function createComposerNodeId() {
|
|
970
|
-
return `composer-${Math.random().toString(36).slice(2, 10)}`;
|
|
971
|
-
}
|
|
972
|
-
function createChatComposerTextNode(text = "") {
|
|
973
|
-
return {
|
|
974
|
-
id: createComposerNodeId(),
|
|
975
|
-
type: "text",
|
|
976
|
-
text
|
|
977
|
-
};
|
|
978
|
-
}
|
|
979
|
-
function createChatComposerTokenNode(params) {
|
|
980
|
-
return {
|
|
981
|
-
id: createComposerNodeId(),
|
|
982
|
-
type: "token",
|
|
983
|
-
tokenKind: params.tokenKind,
|
|
984
|
-
tokenKey: params.tokenKey,
|
|
985
|
-
label: params.label
|
|
986
|
-
};
|
|
987
|
-
}
|
|
988
|
-
function getChatComposerNodeLength(node) {
|
|
989
|
-
return node.type === "text" ? node.text.length : 1;
|
|
990
|
-
}
|
|
991
|
-
function createEmptyChatComposerNodes() {
|
|
992
|
-
return [createChatComposerTextNode("")];
|
|
993
|
-
}
|
|
994
|
-
function createChatComposerNodesFromText(text) {
|
|
995
|
-
return [createChatComposerTextNode(text)];
|
|
996
|
-
}
|
|
997
|
-
function normalizeChatComposerNodes(nodes) {
|
|
998
|
-
const normalized = [];
|
|
999
|
-
for (const node of nodes) {
|
|
1000
|
-
if (node.type === "text") {
|
|
1001
|
-
if (node.text.length === 0) continue;
|
|
1002
|
-
const previous = normalized[normalized.length - 1];
|
|
1003
|
-
if (previous?.type === "text") {
|
|
1004
|
-
normalized[normalized.length - 1] = {
|
|
1005
|
-
...previous,
|
|
1006
|
-
text: previous.text + node.text
|
|
1007
|
-
};
|
|
1008
|
-
continue;
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
|
-
normalized.push(node);
|
|
1012
|
-
}
|
|
1013
|
-
if (normalized.length === 0) return createEmptyChatComposerNodes();
|
|
1014
|
-
return normalized;
|
|
1015
|
-
}
|
|
1016
|
-
function serializeChatComposerDocument(nodes) {
|
|
1017
|
-
return nodes.map((node) => node.type === "text" ? node.text : "").join("");
|
|
1018
|
-
}
|
|
1019
|
-
function serializeChatComposerPlainText(nodes) {
|
|
1020
|
-
return nodes.filter((node) => node.type === "text").map((node) => node.text).join("");
|
|
1021
|
-
}
|
|
1022
|
-
function extractChatComposerTokenKeys(nodes, tokenKind) {
|
|
1023
|
-
const keys = [];
|
|
1024
|
-
const keySet = /* @__PURE__ */ new Set();
|
|
1025
|
-
for (const node of nodes) {
|
|
1026
|
-
if (node.type !== "token" || node.tokenKind !== tokenKind || keySet.has(node.tokenKey)) continue;
|
|
1027
|
-
keySet.add(node.tokenKey);
|
|
1028
|
-
keys.push(node.tokenKey);
|
|
1029
|
-
}
|
|
1030
|
-
return keys;
|
|
1031
|
-
}
|
|
1032
|
-
function buildTrimmedTextEdges(node, nodeStart, rangeStart, rangeEnd) {
|
|
1033
|
-
const prefixLength = Math.max(0, rangeStart - nodeStart);
|
|
1034
|
-
const suffixLength = Math.max(0, nodeStart + node.text.length - rangeEnd);
|
|
1035
|
-
const edges = [];
|
|
1036
|
-
if (prefixLength > 0) edges.push({
|
|
1037
|
-
...node,
|
|
1038
|
-
text: node.text.slice(0, prefixLength)
|
|
1039
|
-
});
|
|
1040
|
-
if (suffixLength > 0) edges.push({
|
|
1041
|
-
...node,
|
|
1042
|
-
text: node.text.slice(node.text.length - suffixLength)
|
|
1043
|
-
});
|
|
1044
|
-
return edges;
|
|
1045
|
-
}
|
|
1046
|
-
function isNodeOutsideComposerRange(nodeStart, nodeEnd, boundedStart, boundedEnd) {
|
|
1047
|
-
return nodeEnd <= boundedStart || nodeStart >= boundedEnd;
|
|
1048
|
-
}
|
|
1049
|
-
function appendReplacementBeforeNode(nextNodes, replacement, inserted, nodeStart, boundedEnd) {
|
|
1050
|
-
if (!inserted && nodeStart >= boundedEnd) {
|
|
1051
|
-
nextNodes.push(...replacement);
|
|
1052
|
-
return true;
|
|
1053
|
-
}
|
|
1054
|
-
return inserted;
|
|
1055
|
-
}
|
|
1056
|
-
function appendTextNodeWithReplacement(nextNodes, node, nodeStart, boundedStart, boundedEnd, replacement, inserted) {
|
|
1057
|
-
const edges = buildTrimmedTextEdges(node, nodeStart, boundedStart, boundedEnd);
|
|
1058
|
-
if (edges[0]) nextNodes.push(edges[0]);
|
|
1059
|
-
let didInsert = inserted;
|
|
1060
|
-
if (!didInsert) {
|
|
1061
|
-
nextNodes.push(...replacement);
|
|
1062
|
-
didInsert = true;
|
|
1063
|
-
}
|
|
1064
|
-
if (edges[1]) nextNodes.push(edges[1]);
|
|
1065
|
-
return didInsert;
|
|
1066
|
-
}
|
|
1067
|
-
function appendTokenNodeWithReplacement(nextNodes, node, replacement, inserted, boundedStart, nodeStart) {
|
|
1068
|
-
if (!inserted && boundedStart <= nodeStart) {
|
|
1069
|
-
nextNodes.push(...replacement);
|
|
1070
|
-
return true;
|
|
1071
|
-
}
|
|
1072
|
-
nextNodes.push(node);
|
|
1073
|
-
return inserted;
|
|
1074
|
-
}
|
|
1075
|
-
function replaceChatComposerRange(nodes, start, end, replacement) {
|
|
1076
|
-
const boundedStart = Math.max(0, start);
|
|
1077
|
-
const boundedEnd = Math.max(boundedStart, end);
|
|
1078
|
-
const nextNodes = [];
|
|
1079
|
-
let cursor = 0;
|
|
1080
|
-
let inserted = false;
|
|
1081
|
-
for (const node of nodes) {
|
|
1082
|
-
const nodeLength = getChatComposerNodeLength(node);
|
|
1083
|
-
const nodeStart = cursor;
|
|
1084
|
-
const nodeEnd = cursor + nodeLength;
|
|
1085
|
-
if (isNodeOutsideComposerRange(nodeStart, nodeEnd, boundedStart, boundedEnd)) {
|
|
1086
|
-
inserted = appendReplacementBeforeNode(nextNodes, replacement, inserted, nodeStart, boundedEnd);
|
|
1087
|
-
nextNodes.push(node);
|
|
1088
|
-
cursor = nodeEnd;
|
|
1089
|
-
continue;
|
|
1090
|
-
}
|
|
1091
|
-
if (node.type === "text") inserted = appendTextNodeWithReplacement(nextNodes, node, nodeStart, boundedStart, boundedEnd, replacement, inserted);
|
|
1092
|
-
else inserted = appendTokenNodeWithReplacement(nextNodes, node, replacement, inserted, boundedStart, nodeStart);
|
|
1093
|
-
cursor = nodeEnd;
|
|
1094
|
-
}
|
|
1095
|
-
if (!inserted) nextNodes.push(...replacement);
|
|
1096
|
-
return normalizeChatComposerNodes(nextNodes);
|
|
1097
|
-
}
|
|
1098
|
-
function removeChatComposerTokenNodes(nodes, predicate) {
|
|
1099
|
-
return normalizeChatComposerNodes(nodes.filter((node) => node.type !== "token" || !predicate(node)));
|
|
1100
|
-
}
|
|
1101
|
-
function resolveChatComposerSlashTrigger(nodes, selection) {
|
|
1102
|
-
if (!selection || selection.start !== selection.end) return null;
|
|
1103
|
-
const documentText = serializeChatComposerDocument(nodes);
|
|
1104
|
-
const caret = selection.end;
|
|
1105
|
-
const prefix = documentText.slice(0, caret);
|
|
1106
|
-
const match = /(?:^|\s)\/([^\s\uFFFC]*)$/.exec(prefix);
|
|
1107
|
-
if (!match) return null;
|
|
1108
|
-
const slashStart = caret - match[0].length + (match[0].startsWith("/") ? 0 : 1);
|
|
1109
|
-
return {
|
|
1110
|
-
query: (match[1] ?? "").trim().toLowerCase(),
|
|
1111
|
-
start: slashStart,
|
|
1112
|
-
end: caret
|
|
1113
|
-
};
|
|
1114
|
-
}
|
|
1115
1273
|
//#endregion
|
|
1116
1274
|
//#region src/components/chat/ui/chat-input-bar/lexical/chat-composer-token-node.tsx
|
|
1117
1275
|
function buildTokenClassName(tokenKind) {
|
|
@@ -1154,35 +1312,15 @@ function buildTokenClassName(tokenKind) {
|
|
|
1154
1312
|
function ChatComposerTokenChip({ label, tokenKind }) {
|
|
1155
1313
|
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
|
|
1156
1314
|
className: tokenKind === "file" ? "inline-flex h-4.5 w-4.5 shrink-0 items-center justify-center rounded-md bg-white text-slate-500 ring-1 ring-black/5" : "inline-flex h-3.5 w-3.5 shrink-0 items-center justify-center text-primary/70",
|
|
1157
|
-
children: tokenKind === "file" ? /* @__PURE__ */
|
|
1158
|
-
viewBox: "0 0 16 16",
|
|
1159
|
-
fill: "none",
|
|
1160
|
-
stroke: "currentColor",
|
|
1161
|
-
strokeWidth: "1.25",
|
|
1162
|
-
strokeLinecap: "round",
|
|
1163
|
-
strokeLinejoin: "round",
|
|
1315
|
+
children: tokenKind === "file" ? /* @__PURE__ */ jsx(ImageIcon, {
|
|
1164
1316
|
"aria-hidden": "true",
|
|
1165
|
-
className: "h-3 w-3"
|
|
1166
|
-
|
|
1167
|
-
/* @__PURE__ */ jsx("path", { d: "M3.25 4.25A1.5 1.5 0 0 1 4.75 2.75h6.5a1.5 1.5 0 0 1 1.5 1.5v7.5a1.5 1.5 0 0 1-1.5 1.5h-6.5a1.5 1.5 0 0 1-1.5-1.5v-7.5Z" }),
|
|
1168
|
-
/* @__PURE__ */ jsx("path", { d: "m4.75 10 2.25-2.5 1.75 1.75 1.25-1.25 2 2" }),
|
|
1169
|
-
/* @__PURE__ */ jsx("path", { d: "M9.75 6.25h.01" })
|
|
1170
|
-
]
|
|
1171
|
-
}) : /* @__PURE__ */ jsxs("svg", {
|
|
1172
|
-
viewBox: "0 0 16 16",
|
|
1173
|
-
fill: "none",
|
|
1174
|
-
stroke: "currentColor",
|
|
1175
|
-
strokeWidth: "1.25",
|
|
1176
|
-
strokeLinecap: "round",
|
|
1177
|
-
strokeLinejoin: "round",
|
|
1317
|
+
className: "h-3 w-3"
|
|
1318
|
+
}) : tokenKind === "panel_app" ? /* @__PURE__ */ jsx(AppWindow, {
|
|
1178
1319
|
"aria-hidden": "true",
|
|
1179
|
-
className: "h-3 w-3"
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
/* @__PURE__ */ jsx("path", { d: "M2.75 6v4l5.75 3.25V9.25L2.75 6Z" }),
|
|
1184
|
-
/* @__PURE__ */ jsx("path", { d: "M14.25 6v4L8.5 13.25V9.25L14.25 6Z" })
|
|
1185
|
-
]
|
|
1320
|
+
className: "h-3 w-3"
|
|
1321
|
+
}) : /* @__PURE__ */ jsx(Puzzle, {
|
|
1322
|
+
"aria-hidden": "true",
|
|
1323
|
+
className: "h-3 w-3"
|
|
1186
1324
|
})
|
|
1187
1325
|
}), /* @__PURE__ */ jsx("span", {
|
|
1188
1326
|
className: tokenKind === "file" ? "min-w-0 flex-1 truncate text-[12px] font-medium text-slate-700" : "truncate",
|
|
@@ -1518,6 +1656,48 @@ function insertToken(params) {
|
|
|
1518
1656
|
}
|
|
1519
1657
|
};
|
|
1520
1658
|
}
|
|
1659
|
+
function insertChatComposerTokenIntoChatComposer(params) {
|
|
1660
|
+
const { label, nodes, selection, tokenKey, tokenKind, triggerSpecs } = params;
|
|
1661
|
+
if (extractChatComposerTokenKeys(nodes, tokenKind).includes(tokenKey)) return {
|
|
1662
|
+
nodes: normalizeChatComposerNodes(nodes),
|
|
1663
|
+
selection
|
|
1664
|
+
};
|
|
1665
|
+
return insertToken({
|
|
1666
|
+
label,
|
|
1667
|
+
nodes,
|
|
1668
|
+
selection,
|
|
1669
|
+
tokenKey,
|
|
1670
|
+
tokenKind,
|
|
1671
|
+
trigger: resolveChatComposerActiveInputSurfaceTrigger(nodes, selection, triggerSpecs ?? [CHAT_INPUT_SURFACE_SLASH_TRIGGER_SPEC])
|
|
1672
|
+
});
|
|
1673
|
+
}
|
|
1674
|
+
function insertInputSurfaceItemIntoChatComposer(params) {
|
|
1675
|
+
const { item, nodes, selection, triggerSpecs } = params;
|
|
1676
|
+
const tokenKind = item.tokenKind ?? (item.value ? "skill" : void 0);
|
|
1677
|
+
const tokenKey = item.tokenKey ?? item.value;
|
|
1678
|
+
if (!tokenKind || !tokenKey) {
|
|
1679
|
+
const trigger = resolveChatComposerActiveInputSurfaceTrigger(nodes, selection, triggerSpecs ?? [CHAT_INPUT_SURFACE_SLASH_TRIGGER_SPEC]);
|
|
1680
|
+
if (trigger) return {
|
|
1681
|
+
nodes: normalizeChatComposerNodes(replaceChatComposerRange(nodes, trigger.start, trigger.end, [])),
|
|
1682
|
+
selection: {
|
|
1683
|
+
start: trigger.start,
|
|
1684
|
+
end: trigger.start
|
|
1685
|
+
}
|
|
1686
|
+
};
|
|
1687
|
+
return {
|
|
1688
|
+
nodes: normalizeChatComposerNodes(nodes),
|
|
1689
|
+
selection
|
|
1690
|
+
};
|
|
1691
|
+
}
|
|
1692
|
+
return insertChatComposerTokenIntoChatComposer({
|
|
1693
|
+
label: item.title,
|
|
1694
|
+
nodes,
|
|
1695
|
+
selection,
|
|
1696
|
+
tokenKey,
|
|
1697
|
+
tokenKind,
|
|
1698
|
+
triggerSpecs
|
|
1699
|
+
});
|
|
1700
|
+
}
|
|
1521
1701
|
function getChatComposerNodesSignature(nodes) {
|
|
1522
1702
|
return nodes.map((node) => node.type === "text" ? `text:${node.id}:${node.text}` : `token:${node.id}:${node.tokenKind}:${node.tokenKey}:${node.label}`).join("");
|
|
1523
1703
|
}
|
|
@@ -1606,21 +1786,9 @@ function deleteChatComposerContent(params) {
|
|
|
1606
1786
|
//#endregion
|
|
1607
1787
|
//#region src/components/chat/ui/chat-input-bar/lexical/chat-composer-lexical-controller.ts
|
|
1608
1788
|
function resolveLexicalComposerKeyboardAction(params) {
|
|
1609
|
-
const {
|
|
1789
|
+
const { canStopGeneration, isComposing, isSending, key, shiftKey } = params;
|
|
1610
1790
|
if (key === "Enter" && !shiftKey && isSending) return { type: "consume" };
|
|
1611
|
-
if (isSlashMenuOpen && slashItemCount > 0) {
|
|
1612
|
-
if (key === "ArrowDown") return {
|
|
1613
|
-
type: "move-slash-index",
|
|
1614
|
-
index: (activeSlashIndex + 1) % slashItemCount
|
|
1615
|
-
};
|
|
1616
|
-
if (key === "ArrowUp") return {
|
|
1617
|
-
type: "move-slash-index",
|
|
1618
|
-
index: (activeSlashIndex - 1 + slashItemCount) % slashItemCount
|
|
1619
|
-
};
|
|
1620
|
-
if (key === "Enter" && !shiftKey || key === "Tab") return { type: "insert-active-slash-item" };
|
|
1621
|
-
}
|
|
1622
1791
|
if (key === "Escape") {
|
|
1623
|
-
if (isSlashMenuOpen) return { type: "close-slash" };
|
|
1624
1792
|
if (isSending && canStopGeneration) return { type: "stop-generation" };
|
|
1625
1793
|
return { type: "noop" };
|
|
1626
1794
|
}
|
|
@@ -1635,16 +1803,18 @@ function resolveLexicalComposerKeyboardAction(params) {
|
|
|
1635
1803
|
var LexicalComposerHandleOwner = class {
|
|
1636
1804
|
constructor(params) {
|
|
1637
1805
|
this.params = params;
|
|
1638
|
-
this.
|
|
1639
|
-
|
|
1640
|
-
this.params.
|
|
1641
|
-
|
|
1642
|
-
label: item.title,
|
|
1806
|
+
this.insertInputSurfaceItem = (item, triggerSpecs = [CHAT_INPUT_SURFACE_SLASH_TRIGGER_SPEC]) => {
|
|
1807
|
+
this.params.onInputSurfaceItemSelect?.(item);
|
|
1808
|
+
this.params.publishSnapshot(insertInputSurfaceItemIntoChatComposer({
|
|
1809
|
+
item,
|
|
1643
1810
|
nodes: this.params.optionsReader().nodes,
|
|
1644
1811
|
selection: this.params.optionsReader().selection,
|
|
1645
|
-
|
|
1812
|
+
triggerSpecs
|
|
1646
1813
|
}), { focusAfterSync: true });
|
|
1647
1814
|
};
|
|
1815
|
+
this.insertSlashItem = (item) => {
|
|
1816
|
+
this.insertInputSurfaceItem(item);
|
|
1817
|
+
};
|
|
1648
1818
|
this.insertFileToken = (tokenKey, label) => {
|
|
1649
1819
|
this.params.publishSnapshot(insertFileTokenIntoChatComposer({
|
|
1650
1820
|
label,
|
|
@@ -1711,7 +1881,10 @@ function handleLexicalComposerBeforeInput(params) {
|
|
|
1711
1881
|
nodes: snapshotReader().nodes,
|
|
1712
1882
|
selection: snapshotReader().selection,
|
|
1713
1883
|
text: nativeEvent.data
|
|
1714
|
-
})
|
|
1884
|
+
}), { inputSurfaceReason: {
|
|
1885
|
+
type: "insert-text",
|
|
1886
|
+
text: nativeEvent.data
|
|
1887
|
+
} });
|
|
1715
1888
|
}
|
|
1716
1889
|
function handleLexicalComposerCompositionEnd(params) {
|
|
1717
1890
|
const { data, fallbackSnapshot, publishSnapshot, snapshotReader } = params;
|
|
@@ -1721,40 +1894,25 @@ function handleLexicalComposerCompositionEnd(params) {
|
|
|
1721
1894
|
nodes: currentSnapshot.nodes,
|
|
1722
1895
|
selection: currentSnapshot.selection,
|
|
1723
1896
|
text: data
|
|
1724
|
-
}) : editorSnapshot, {
|
|
1897
|
+
}) : editorSnapshot, {
|
|
1898
|
+
forcePublish: true,
|
|
1899
|
+
inputSurfaceReason: {
|
|
1900
|
+
type: "insert-text",
|
|
1901
|
+
text: data
|
|
1902
|
+
}
|
|
1903
|
+
});
|
|
1725
1904
|
}
|
|
1726
1905
|
function handleLexicalComposerKeyboardCommand(params) {
|
|
1727
|
-
const { actions,
|
|
1906
|
+
const { actions, nativeEvent, publishSnapshot, snapshot } = params;
|
|
1728
1907
|
const action = resolveLexicalComposerKeyboardAction({
|
|
1729
|
-
activeSlashIndex,
|
|
1730
1908
|
canStopGeneration: actions.canStopGeneration,
|
|
1731
1909
|
isComposing: nativeEvent.isComposing,
|
|
1732
1910
|
isSending: actions.isSending,
|
|
1733
|
-
isSlashMenuOpen: resolveChatComposerSlashTrigger(snapshot.nodes, snapshot.selection) !== null,
|
|
1734
1911
|
key: nativeEvent.key,
|
|
1735
|
-
shiftKey: nativeEvent.shiftKey
|
|
1736
|
-
slashItemCount: slashItems.length
|
|
1912
|
+
shiftKey: nativeEvent.shiftKey
|
|
1737
1913
|
});
|
|
1738
|
-
const activeSlashItem = slashItems[activeSlashIndex] ?? null;
|
|
1739
1914
|
if (action.type !== "noop") nativeEvent.preventDefault();
|
|
1740
1915
|
switch (action.type) {
|
|
1741
|
-
case "move-slash-index":
|
|
1742
|
-
onSlashActiveIndexChange(action.index);
|
|
1743
|
-
return true;
|
|
1744
|
-
case "insert-active-slash-item":
|
|
1745
|
-
if (!activeSlashItem) return true;
|
|
1746
|
-
onSlashItemSelect?.(activeSlashItem);
|
|
1747
|
-
publishSnapshot(insertSkillTokenIntoChatComposer({
|
|
1748
|
-
label: activeSlashItem.title,
|
|
1749
|
-
nodes: snapshot.nodes,
|
|
1750
|
-
selection: snapshot.selection,
|
|
1751
|
-
tokenKey: activeSlashItem.value ?? activeSlashItem.key
|
|
1752
|
-
}), { focusAfterSync: true });
|
|
1753
|
-
return true;
|
|
1754
|
-
case "close-slash":
|
|
1755
|
-
onSlashQueryChange?.(null);
|
|
1756
|
-
onSlashOpenChange(false);
|
|
1757
|
-
return true;
|
|
1758
1916
|
case "consume": return true;
|
|
1759
1917
|
case "stop-generation":
|
|
1760
1918
|
actions.onStop();
|
|
@@ -1764,7 +1922,10 @@ function handleLexicalComposerKeyboardCommand(params) {
|
|
|
1764
1922
|
nodes: snapshot.nodes,
|
|
1765
1923
|
selection: snapshot.selection,
|
|
1766
1924
|
text: "\n"
|
|
1767
|
-
})
|
|
1925
|
+
}), { inputSurfaceReason: {
|
|
1926
|
+
type: "insert-text",
|
|
1927
|
+
text: "\n"
|
|
1928
|
+
} });
|
|
1768
1929
|
return true;
|
|
1769
1930
|
case "send-message":
|
|
1770
1931
|
actions.onSend();
|
|
@@ -1774,14 +1935,14 @@ function handleLexicalComposerKeyboardCommand(params) {
|
|
|
1774
1935
|
direction: action.direction,
|
|
1775
1936
|
nodes: snapshot.nodes,
|
|
1776
1937
|
selection: snapshot.selection
|
|
1777
|
-
}));
|
|
1938
|
+
}), { inputSurfaceReason: { type: "delete-content" } });
|
|
1778
1939
|
return true;
|
|
1779
1940
|
case "noop": return false;
|
|
1780
1941
|
}
|
|
1781
1942
|
}
|
|
1782
1943
|
//#endregion
|
|
1783
1944
|
//#region src/components/chat/ui/chat-input-bar/lexical/chat-composer-plugins.tsx
|
|
1784
|
-
function ChatComposerBindingsPlugin({ disabled, editorRef, editorSignatureRef, isApplyingExternalUpdateRef, isComposingRef, lastPublishedSignatureRef, nodes, onBlur, onKeyDown, onNodesChange, pendingSelectionRef, selectionRef, shouldFocusAfterSyncRef,
|
|
1945
|
+
function ChatComposerBindingsPlugin({ disabled, editorRef, editorSignatureRef, isApplyingExternalUpdateRef, isComposingRef, lastPublishedSignatureRef, nodes, onBlur, consumeInputSurfaceReason, onKeyDown, onNodesChange, pendingSelectionRef, selectionRef, shouldFocusAfterSyncRef, syncInputSurfaceSnapshot }) {
|
|
1785
1946
|
const [editor] = useLexicalComposerContext();
|
|
1786
1947
|
useLayoutEffect(() => {
|
|
1787
1948
|
editorRef.current = editor;
|
|
@@ -1833,15 +1994,15 @@ function ChatComposerBindingsPlugin({ disabled, editorRef, editorSignatureRef, i
|
|
|
1833
1994
|
const signature = getChatComposerNodesSignature(snapshot.nodes);
|
|
1834
1995
|
selectionRef.current = snapshot.selection;
|
|
1835
1996
|
editorSignatureRef.current = signature;
|
|
1836
|
-
syncSlashState(snapshot.nodes, snapshot.selection);
|
|
1837
1997
|
if (isApplyingExternalUpdateRef.current || isComposingRef.current) return;
|
|
1998
|
+
syncInputSurfaceSnapshot(snapshot.nodes, snapshot.selection, consumeInputSurfaceReason() ?? { type: "sync" });
|
|
1838
1999
|
if (signature === lastPublishedSignatureRef.current) return;
|
|
1839
2000
|
lastPublishedSignatureRef.current = signature;
|
|
1840
2001
|
onNodesChange(snapshot.nodes);
|
|
1841
2002
|
}), editor.registerCommand(SELECTION_CHANGE_COMMAND, () => {
|
|
1842
2003
|
const snapshot = readChatComposerSnapshotFromEditorState(editor.getEditorState());
|
|
1843
2004
|
selectionRef.current = snapshot.selection;
|
|
1844
|
-
if (!isComposingRef.current)
|
|
2005
|
+
if (!isComposingRef.current) syncInputSurfaceSnapshot(snapshot.nodes, snapshot.selection, { type: "selection" });
|
|
1845
2006
|
return false;
|
|
1846
2007
|
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(BLUR_COMMAND, () => {
|
|
1847
2008
|
onBlur();
|
|
@@ -1854,10 +2015,11 @@ function ChatComposerBindingsPlugin({ disabled, editorRef, editorSignatureRef, i
|
|
|
1854
2015
|
isComposingRef,
|
|
1855
2016
|
lastPublishedSignatureRef,
|
|
1856
2017
|
onBlur,
|
|
2018
|
+
consumeInputSurfaceReason,
|
|
1857
2019
|
onKeyDown,
|
|
1858
2020
|
onNodesChange,
|
|
1859
2021
|
selectionRef,
|
|
1860
|
-
|
|
2022
|
+
syncInputSurfaceSnapshot
|
|
1861
2023
|
]);
|
|
1862
2024
|
return null;
|
|
1863
2025
|
}
|
|
@@ -1866,25 +2028,19 @@ function ChatComposerBindingsPlugin({ disabled, editorRef, editorSignatureRef, i
|
|
|
1866
2028
|
function getChatComposerDocumentLength(nodes) {
|
|
1867
2029
|
return nodes.reduce((cursor, node) => cursor + (node.type === "text" ? node.text.length : 1), 0);
|
|
1868
2030
|
}
|
|
1869
|
-
const ChatInputBarTokenizedComposer = forwardRef(function ChatInputBarTokenizedComposer({ actions,
|
|
2031
|
+
const ChatInputBarTokenizedComposer = forwardRef(function ChatInputBarTokenizedComposer({ actions, disabled, nodes, onFilesAdd, onInputSurfaceItemSelect, onInputSurfaceKeyDown, onInputSurfaceOpenChange, onInputSurfaceSnapshotChange, onNodesChange, placeholder }, ref) {
|
|
1870
2032
|
const editorRef = useRef(null);
|
|
1871
2033
|
const selectionRef = useRef(null);
|
|
1872
2034
|
const pendingSelectionRef = useRef(null);
|
|
1873
2035
|
const shouldFocusAfterSyncRef = useRef(false);
|
|
1874
2036
|
const isComposingRef = useRef(false);
|
|
1875
2037
|
const isApplyingExternalUpdateRef = useRef(false);
|
|
2038
|
+
const pendingInputSurfaceReasonRef = useRef(null);
|
|
1876
2039
|
const editorSignatureRef = useRef("");
|
|
1877
2040
|
const lastPublishedSignatureRef = useRef("");
|
|
1878
|
-
const
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
onSlashQueryChange?.(trigger?.query ?? null);
|
|
1882
|
-
onSlashOpenChange(trigger !== null);
|
|
1883
|
-
}, [
|
|
1884
|
-
onSlashOpenChange,
|
|
1885
|
-
onSlashQueryChange,
|
|
1886
|
-
onSlashTriggerChange
|
|
1887
|
-
]);
|
|
2041
|
+
const syncInputSurfaceSnapshot = useCallback((nodes, selection, reason) => {
|
|
2042
|
+
onInputSurfaceSnapshotChange?.(nodes, selection, reason);
|
|
2043
|
+
}, [onInputSurfaceSnapshotChange]);
|
|
1888
2044
|
const readCurrentNodes = useCallback(() => {
|
|
1889
2045
|
return nodes;
|
|
1890
2046
|
}, [nodes]);
|
|
@@ -1900,12 +2056,12 @@ const ChatInputBarTokenizedComposer = forwardRef(function ChatInputBarTokenizedC
|
|
|
1900
2056
|
pendingSelectionRef.current = snapshot.selection;
|
|
1901
2057
|
if (options?.focusAfterSync) shouldFocusAfterSyncRef.current = true;
|
|
1902
2058
|
const signature = getChatComposerNodesSignature(snapshot.nodes);
|
|
1903
|
-
|
|
2059
|
+
syncInputSurfaceSnapshot(snapshot.nodes, snapshot.selection, options?.inputSurfaceReason ?? { type: "programmatic" });
|
|
1904
2060
|
if (options?.forcePublish || signature !== lastPublishedSignatureRef.current) {
|
|
1905
2061
|
lastPublishedSignatureRef.current = signature;
|
|
1906
2062
|
onNodesChange(snapshot.nodes);
|
|
1907
2063
|
}
|
|
1908
|
-
}, [onNodesChange,
|
|
2064
|
+
}, [onNodesChange, syncInputSurfaceSnapshot]);
|
|
1909
2065
|
const focusComposer = useCallback(() => {
|
|
1910
2066
|
const editor = editorRef.current;
|
|
1911
2067
|
if (!editor) return;
|
|
@@ -1943,16 +2099,21 @@ const ChatInputBarTokenizedComposer = forwardRef(function ChatInputBarTokenizedC
|
|
|
1943
2099
|
nodes: readCurrentNodes(),
|
|
1944
2100
|
selection: readCurrentSelection()
|
|
1945
2101
|
}), [readCurrentNodes, readCurrentSelection]);
|
|
2102
|
+
const consumeInputSurfaceReason = useCallback(() => {
|
|
2103
|
+
const reason = pendingInputSurfaceReasonRef.current;
|
|
2104
|
+
pendingInputSurfaceReasonRef.current = null;
|
|
2105
|
+
return reason;
|
|
2106
|
+
}, []);
|
|
1946
2107
|
useImperativeHandle(ref, () => createLexicalComposerHandle({
|
|
1947
2108
|
focusComposer,
|
|
1948
2109
|
focusComposerAtEnd,
|
|
1949
|
-
|
|
2110
|
+
onInputSurfaceItemSelect,
|
|
1950
2111
|
optionsReader: readComposerSnapshot,
|
|
1951
2112
|
publishSnapshot
|
|
1952
2113
|
}), [
|
|
1953
2114
|
focusComposer,
|
|
1954
2115
|
focusComposerAtEnd,
|
|
1955
|
-
|
|
2116
|
+
onInputSurfaceItemSelect,
|
|
1956
2117
|
publishSnapshot,
|
|
1957
2118
|
readComposerSnapshot
|
|
1958
2119
|
]);
|
|
@@ -2028,24 +2189,23 @@ const ChatInputBarTokenizedComposer = forwardRef(function ChatInputBarTokenizedC
|
|
|
2028
2189
|
lastPublishedSignatureRef,
|
|
2029
2190
|
nodes,
|
|
2030
2191
|
onBlur: () => {
|
|
2031
|
-
|
|
2032
|
-
onSlashOpenChange(false);
|
|
2192
|
+
onInputSurfaceOpenChange?.(false);
|
|
2033
2193
|
},
|
|
2194
|
+
consumeInputSurfaceReason,
|
|
2034
2195
|
onKeyDown: (event) => {
|
|
2196
|
+
if (event.key.length === 1 && !event.isComposing && !event.altKey && !event.ctrlKey && !event.metaKey) pendingInputSurfaceReasonRef.current = {
|
|
2197
|
+
type: "insert-text",
|
|
2198
|
+
text: event.key
|
|
2199
|
+
};
|
|
2035
2200
|
const editor = editorRef.current;
|
|
2036
2201
|
if (!editor) return false;
|
|
2037
2202
|
const snapshot = readChatComposerSnapshotFromEditorState(editor.getEditorState());
|
|
2038
2203
|
selectionRef.current = snapshot.selection;
|
|
2204
|
+
if (onInputSurfaceKeyDown?.(event)) return true;
|
|
2039
2205
|
return handleLexicalComposerKeyboardCommand({
|
|
2040
2206
|
actions,
|
|
2041
|
-
activeSlashIndex,
|
|
2042
2207
|
nativeEvent: event,
|
|
2043
|
-
onSlashActiveIndexChange,
|
|
2044
|
-
onSlashItemSelect,
|
|
2045
|
-
onSlashOpenChange,
|
|
2046
|
-
onSlashQueryChange,
|
|
2047
2208
|
publishSnapshot,
|
|
2048
|
-
slashItems,
|
|
2049
2209
|
snapshot
|
|
2050
2210
|
});
|
|
2051
2211
|
},
|
|
@@ -2053,7 +2213,7 @@ const ChatInputBarTokenizedComposer = forwardRef(function ChatInputBarTokenizedC
|
|
|
2053
2213
|
pendingSelectionRef,
|
|
2054
2214
|
selectionRef,
|
|
2055
2215
|
shouldFocusAfterSyncRef,
|
|
2056
|
-
|
|
2216
|
+
syncInputSurfaceSnapshot
|
|
2057
2217
|
})
|
|
2058
2218
|
]
|
|
2059
2219
|
});
|
|
@@ -2083,16 +2243,20 @@ function InputBarHint({ hint }) {
|
|
|
2083
2243
|
})
|
|
2084
2244
|
});
|
|
2085
2245
|
}
|
|
2086
|
-
const ChatInputBar = forwardRef(function ChatInputBar({ composer, hint, slashMenu, surface, toolbar: toolbarProps }, ref) {
|
|
2246
|
+
const ChatInputBar = forwardRef(function ChatInputBar({ composer, hint, inputSurface, slashMenu, surface, toolbar: toolbarProps }, ref) {
|
|
2087
2247
|
const composerRef = useRef(null);
|
|
2088
|
-
const
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2248
|
+
const resolvedInputSurface = inputSurface ?? (slashMenu ? {
|
|
2249
|
+
isLoading: slashMenu.isLoading,
|
|
2250
|
+
items: slashMenu.items,
|
|
2251
|
+
onSelectItem: slashMenu.onSelectItem,
|
|
2252
|
+
texts: {
|
|
2253
|
+
loadingLabel: slashMenu.texts.slashLoadingLabel,
|
|
2254
|
+
sectionLabel: slashMenu.texts.slashSectionLabel,
|
|
2255
|
+
emptyLabel: slashMenu.texts.slashEmptyLabel,
|
|
2256
|
+
hintLabel: slashMenu.texts.slashHintLabel,
|
|
2257
|
+
itemHintLabel: slashMenu.texts.slashSkillHintLabel
|
|
2258
|
+
}
|
|
2259
|
+
} : null);
|
|
2096
2260
|
const toolbar = useMemo(() => {
|
|
2097
2261
|
if (!toolbarProps.skillPicker) return toolbarProps;
|
|
2098
2262
|
return {
|
|
@@ -2119,55 +2283,27 @@ const ChatInputBar = forwardRef(function ChatInputBar({ composer, hint, slashMen
|
|
|
2119
2283
|
children: /* @__PURE__ */ jsxs("div", {
|
|
2120
2284
|
className: "overflow-hidden rounded-2xl border border-gray-200 bg-white shadow-card",
|
|
2121
2285
|
children: [
|
|
2122
|
-
/* @__PURE__ */
|
|
2286
|
+
/* @__PURE__ */ jsx("div", {
|
|
2123
2287
|
className: "relative",
|
|
2124
|
-
children:
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
if (nextTriggerStart === null) setDismissedSlashTriggerStart(null);
|
|
2144
|
-
},
|
|
2145
|
-
onSlashOpenChange: (open) => {
|
|
2146
|
-
if (!open) dismissSlashTrigger();
|
|
2147
|
-
},
|
|
2148
|
-
onSlashActiveIndexChange: setActiveSlashIndex
|
|
2149
|
-
}), /* @__PURE__ */ jsx(ChatSlashMenu, {
|
|
2150
|
-
isOpen: isSlashPanelOpen,
|
|
2151
|
-
isLoading: slashMenu.isLoading,
|
|
2152
|
-
items: slashMenu.items,
|
|
2153
|
-
activeIndex: activeSlashIndexInRange,
|
|
2154
|
-
activeItem: activeSlashItem,
|
|
2155
|
-
texts: slashMenu.texts,
|
|
2156
|
-
onSelectItem: (item) => {
|
|
2157
|
-
setDismissedSlashTriggerStart(null);
|
|
2158
|
-
composerRef.current?.insertSlashItem(item);
|
|
2159
|
-
},
|
|
2160
|
-
onOpenChange: (open) => {
|
|
2161
|
-
if (!open) dismissSlashTrigger();
|
|
2162
|
-
},
|
|
2163
|
-
onDetailsPointerDown: () => {
|
|
2164
|
-
isSlashMenuInteractionRef.current = true;
|
|
2165
|
-
requestAnimationFrame(() => {
|
|
2166
|
-
isSlashMenuInteractionRef.current = false;
|
|
2167
|
-
});
|
|
2168
|
-
},
|
|
2169
|
-
onSetActiveIndex: setActiveSlashIndex
|
|
2170
|
-
})]
|
|
2288
|
+
children: /* @__PURE__ */ jsx(ChatInputSurfaceHost, {
|
|
2289
|
+
inputSurface: resolvedInputSurface,
|
|
2290
|
+
onInputSurfaceTriggerChange: composer.onInputSurfaceTriggerChange,
|
|
2291
|
+
onSelectItem: (item) => composerRef.current?.insertInputSurfaceItem(item, composer.inputSurfaceTriggerSpecs),
|
|
2292
|
+
triggerSpecs: composer.inputSurfaceTriggerSpecs,
|
|
2293
|
+
children: ({ onInputSurfaceKeyDown, onInputSurfaceOpenChange, onInputSurfaceSnapshotChange }) => /* @__PURE__ */ jsx(ChatInputBarTokenizedComposer, {
|
|
2294
|
+
ref: composerRef,
|
|
2295
|
+
nodes: composer.nodes,
|
|
2296
|
+
placeholder: composer.placeholder,
|
|
2297
|
+
disabled: composer.disabled,
|
|
2298
|
+
onInputSurfaceItemSelect: resolvedInputSurface?.onSelectItem,
|
|
2299
|
+
actions: toolbarProps.actions,
|
|
2300
|
+
onNodesChange: composer.onNodesChange,
|
|
2301
|
+
onFilesAdd: composer.onFilesAdd,
|
|
2302
|
+
onInputSurfaceSnapshotChange,
|
|
2303
|
+
onInputSurfaceOpenChange,
|
|
2304
|
+
onInputSurfaceKeyDown
|
|
2305
|
+
})
|
|
2306
|
+
})
|
|
2171
2307
|
}),
|
|
2172
2308
|
/* @__PURE__ */ jsx(InputBarHint, { hint }),
|
|
2173
2309
|
/* @__PURE__ */ jsx(ChatInputBarToolbar, { ...toolbar })
|
|
@@ -2200,6 +2336,46 @@ function ChatMessageAvatar({ role }) {
|
|
|
2200
2336
|
});
|
|
2201
2337
|
}
|
|
2202
2338
|
//#endregion
|
|
2339
|
+
//#region src/components/chat/ui/chat-message-list/chat-inline-token-badge.tsx
|
|
2340
|
+
function resolveInlineTokenTone(kind) {
|
|
2341
|
+
if (kind === "skill") return "skill";
|
|
2342
|
+
if (kind === "panel_app") return "panel_app";
|
|
2343
|
+
return "default";
|
|
2344
|
+
}
|
|
2345
|
+
function resolveInlineTokenBadgeClassName(tone, isUser) {
|
|
2346
|
+
if (tone === "skill") return isUser ? "border-emerald-200/35 bg-emerald-400/18 text-emerald-50/95" : "border-emerald-200/70 bg-emerald-50 text-emerald-700";
|
|
2347
|
+
if (tone === "panel_app") return isUser ? "border-sky-200/35 bg-sky-400/18 text-sky-50/95" : "border-sky-200/70 bg-sky-50 text-sky-700";
|
|
2348
|
+
return isUser ? "border-white/30 bg-white/18 text-white" : "border-slate-200/80 bg-slate-100 text-slate-700";
|
|
2349
|
+
}
|
|
2350
|
+
function resolveInlineTokenIconClassName(tone, isUser) {
|
|
2351
|
+
if (tone === "skill") return isUser ? "text-emerald-100/90" : "text-emerald-600";
|
|
2352
|
+
if (tone === "panel_app") return isUser ? "text-sky-100/90" : "text-sky-600";
|
|
2353
|
+
return isUser ? "text-white/70" : "text-slate-500";
|
|
2354
|
+
}
|
|
2355
|
+
function renderInlineTokenIcon(tone) {
|
|
2356
|
+
return tone === "panel_app" ? /* @__PURE__ */ jsx(AppWindow, {
|
|
2357
|
+
"aria-hidden": "true",
|
|
2358
|
+
className: "h-3 w-3"
|
|
2359
|
+
}) : /* @__PURE__ */ jsx(Puzzle, {
|
|
2360
|
+
"aria-hidden": "true",
|
|
2361
|
+
className: "h-3 w-3"
|
|
2362
|
+
});
|
|
2363
|
+
}
|
|
2364
|
+
function ChatInlineTokenBadge({ kind, label, isUser }) {
|
|
2365
|
+
const tone = resolveInlineTokenTone(kind);
|
|
2366
|
+
return /* @__PURE__ */ jsxs("span", {
|
|
2367
|
+
className: cn("mx-[2px] inline-flex h-7 max-w-full items-center gap-1.5 rounded-xl border px-2.5 align-baseline text-[11px] font-medium shadow-[0_0_0_1px_rgba(15,23,42,0.03)]", resolveInlineTokenBadgeClassName(tone, isUser)),
|
|
2368
|
+
title: label,
|
|
2369
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
2370
|
+
className: cn("inline-flex h-3.5 w-3.5 shrink-0 items-center justify-center", resolveInlineTokenIconClassName(tone, isUser)),
|
|
2371
|
+
children: renderInlineTokenIcon(tone)
|
|
2372
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
2373
|
+
className: "truncate",
|
|
2374
|
+
children: label
|
|
2375
|
+
})]
|
|
2376
|
+
});
|
|
2377
|
+
}
|
|
2378
|
+
//#endregion
|
|
2203
2379
|
//#region src/components/chat/utils/copy-text.utils.ts
|
|
2204
2380
|
function canUseClipboardApi() {
|
|
2205
2381
|
return typeof navigator !== "undefined" && typeof navigator.clipboard?.writeText === "function";
|
|
@@ -2499,6 +2675,10 @@ const SAFE_LINK_PROTOCOLS = new Set([
|
|
|
2499
2675
|
"mailto:",
|
|
2500
2676
|
"tel:"
|
|
2501
2677
|
]);
|
|
2678
|
+
const INLINE_TOKEN_KIND_ATTR = "data-chat-inline-token-kind";
|
|
2679
|
+
const INLINE_TOKEN_KEY_ATTR = "data-chat-inline-token-key";
|
|
2680
|
+
const INLINE_TOKEN_LABEL_ATTR = "data-chat-inline-token-label";
|
|
2681
|
+
const INLINE_TOKEN_RAW_TEXT_ATTR = "data-chat-inline-token-raw-text";
|
|
2502
2682
|
const PROJECT_RELATIVE_FILE_EXTENSIONS = [
|
|
2503
2683
|
"cjs",
|
|
2504
2684
|
"css",
|
|
@@ -2553,10 +2733,116 @@ function parseLocalFileAction(href) {
|
|
|
2553
2733
|
...typeof column === "number" ? { column } : {}
|
|
2554
2734
|
};
|
|
2555
2735
|
}
|
|
2556
|
-
function
|
|
2736
|
+
function prepareInlineTokens(inlineTokens) {
|
|
2737
|
+
if (!inlineTokens || inlineTokens.length === 0) return [];
|
|
2738
|
+
const seenRawTexts = /* @__PURE__ */ new Set();
|
|
2739
|
+
const tokens = [];
|
|
2740
|
+
for (const token of inlineTokens) {
|
|
2741
|
+
if (!token.rawText || seenRawTexts.has(token.rawText)) continue;
|
|
2742
|
+
seenRawTexts.add(token.rawText);
|
|
2743
|
+
tokens.push(token);
|
|
2744
|
+
}
|
|
2745
|
+
return tokens.sort((left, right) => right.rawText.length - left.rawText.length);
|
|
2746
|
+
}
|
|
2747
|
+
function createInlineTokenNode(token) {
|
|
2748
|
+
return {
|
|
2749
|
+
type: "chatInlineToken",
|
|
2750
|
+
data: {
|
|
2751
|
+
hName: "span",
|
|
2752
|
+
hProperties: {
|
|
2753
|
+
[INLINE_TOKEN_KIND_ATTR]: token.kind,
|
|
2754
|
+
[INLINE_TOKEN_KEY_ATTR]: token.key,
|
|
2755
|
+
[INLINE_TOKEN_LABEL_ATTR]: token.label,
|
|
2756
|
+
[INLINE_TOKEN_RAW_TEXT_ATTR]: token.rawText
|
|
2757
|
+
},
|
|
2758
|
+
hChildren: [{
|
|
2759
|
+
type: "text",
|
|
2760
|
+
value: token.label
|
|
2761
|
+
}]
|
|
2762
|
+
}
|
|
2763
|
+
};
|
|
2764
|
+
}
|
|
2765
|
+
function findNextInlineToken(value, cursor, tokens) {
|
|
2766
|
+
let next = null;
|
|
2767
|
+
for (const token of tokens) {
|
|
2768
|
+
const index = value.indexOf(token.rawText, cursor);
|
|
2769
|
+
if (index < 0) continue;
|
|
2770
|
+
if (!next || index < next.index || index === next.index && token.rawText.length > next.token.rawText.length) next = {
|
|
2771
|
+
index,
|
|
2772
|
+
token
|
|
2773
|
+
};
|
|
2774
|
+
}
|
|
2775
|
+
return next;
|
|
2776
|
+
}
|
|
2777
|
+
function splitTextNodeByInlineTokens(value, tokens) {
|
|
2778
|
+
const output = [];
|
|
2779
|
+
let cursor = 0;
|
|
2780
|
+
while (cursor < value.length) {
|
|
2781
|
+
const next = findNextInlineToken(value, cursor, tokens);
|
|
2782
|
+
if (!next) {
|
|
2783
|
+
output.push({
|
|
2784
|
+
type: "text",
|
|
2785
|
+
value: value.slice(cursor)
|
|
2786
|
+
});
|
|
2787
|
+
break;
|
|
2788
|
+
}
|
|
2789
|
+
if (next.index > cursor) output.push({
|
|
2790
|
+
type: "text",
|
|
2791
|
+
value: value.slice(cursor, next.index)
|
|
2792
|
+
});
|
|
2793
|
+
output.push(createInlineTokenNode(next.token));
|
|
2794
|
+
cursor = next.index + next.token.rawText.length;
|
|
2795
|
+
}
|
|
2796
|
+
return output.length > 0 ? output : [{
|
|
2797
|
+
type: "text",
|
|
2798
|
+
value
|
|
2799
|
+
}];
|
|
2800
|
+
}
|
|
2801
|
+
function transformInlineTokenTextNodes(node, tokens) {
|
|
2802
|
+
if (node.type === "code" || node.type === "inlineCode" || !node.children) return;
|
|
2803
|
+
const nextChildren = [];
|
|
2804
|
+
for (const child of node.children) {
|
|
2805
|
+
if (child.type === "text" && typeof child.value === "string") {
|
|
2806
|
+
nextChildren.push(...splitTextNodeByInlineTokens(child.value, tokens));
|
|
2807
|
+
continue;
|
|
2808
|
+
}
|
|
2809
|
+
transformInlineTokenTextNodes(child, tokens);
|
|
2810
|
+
nextChildren.push(child);
|
|
2811
|
+
}
|
|
2812
|
+
node.children = nextChildren;
|
|
2813
|
+
}
|
|
2814
|
+
function createRemarkInlineTokenPlugin(inlineTokens) {
|
|
2815
|
+
const tokens = prepareInlineTokens(inlineTokens);
|
|
2816
|
+
return () => (tree) => {
|
|
2817
|
+
if (tokens.length === 0) return;
|
|
2818
|
+
transformInlineTokenTextNodes(tree, tokens);
|
|
2819
|
+
};
|
|
2820
|
+
}
|
|
2821
|
+
function readStringProp(props, key) {
|
|
2822
|
+
const value = props[key];
|
|
2823
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
2824
|
+
}
|
|
2825
|
+
function ChatMessageMarkdown({ text, role, texts, inline = false, inlineTokens, onFileOpen }) {
|
|
2557
2826
|
const isUser = role === "user";
|
|
2827
|
+
const remarkPlugins = useMemo(() => inlineTokens && inlineTokens.length > 0 ? [remarkGfm, createRemarkInlineTokenPlugin(inlineTokens)] : [remarkGfm], [inlineTokens]);
|
|
2558
2828
|
const markdownComponents = useMemo(() => ({
|
|
2559
2829
|
p: ({ children }) => inline ? /* @__PURE__ */ jsx(Fragment$1, { children }) : /* @__PURE__ */ jsx("p", { children }),
|
|
2830
|
+
span: ({ node: _node, children, ...rest }) => {
|
|
2831
|
+
const restProps = rest;
|
|
2832
|
+
const kind = readStringProp(restProps, INLINE_TOKEN_KIND_ATTR);
|
|
2833
|
+
const key = readStringProp(restProps, INLINE_TOKEN_KEY_ATTR);
|
|
2834
|
+
const label = readStringProp(restProps, INLINE_TOKEN_LABEL_ATTR);
|
|
2835
|
+
const rawText = readStringProp(restProps, INLINE_TOKEN_RAW_TEXT_ATTR);
|
|
2836
|
+
if (kind && key && label && rawText) return /* @__PURE__ */ jsx(ChatInlineTokenBadge, {
|
|
2837
|
+
kind,
|
|
2838
|
+
label,
|
|
2839
|
+
isUser
|
|
2840
|
+
});
|
|
2841
|
+
return /* @__PURE__ */ jsx("span", {
|
|
2842
|
+
...rest,
|
|
2843
|
+
children
|
|
2844
|
+
});
|
|
2845
|
+
},
|
|
2560
2846
|
a: ({ href, children, ...rest }) => {
|
|
2561
2847
|
const safeHref = resolveSafeHref(href);
|
|
2562
2848
|
if (!safeHref) return /* @__PURE__ */ jsx("span", {
|
|
@@ -2626,6 +2912,7 @@ function ChatMessageMarkdown({ text, role, texts, inline = false, onFileOpen })
|
|
|
2626
2912
|
}
|
|
2627
2913
|
}), [
|
|
2628
2914
|
inline,
|
|
2915
|
+
isUser,
|
|
2629
2916
|
onFileOpen,
|
|
2630
2917
|
texts
|
|
2631
2918
|
]);
|
|
@@ -2633,74 +2920,13 @@ function ChatMessageMarkdown({ text, role, texts, inline = false, onFileOpen })
|
|
|
2633
2920
|
className: cn("chat-markdown", isUser ? "chat-markdown-user" : "chat-markdown-assistant"),
|
|
2634
2921
|
children: /* @__PURE__ */ jsx(ReactMarkdown, {
|
|
2635
2922
|
skipHtml: true,
|
|
2636
|
-
remarkPlugins
|
|
2923
|
+
remarkPlugins,
|
|
2637
2924
|
components: markdownComponents,
|
|
2638
2925
|
children: trimMarkdown(text)
|
|
2639
2926
|
})
|
|
2640
2927
|
});
|
|
2641
2928
|
}
|
|
2642
2929
|
//#endregion
|
|
2643
|
-
//#region src/components/chat/ui/chat-message-list/chat-message-inline-content.tsx
|
|
2644
|
-
function hasVisibleInlineText(value) {
|
|
2645
|
-
return value.split("").join("").split("").join("").split("").join("").split("").join("").split("").join("").length > 0;
|
|
2646
|
-
}
|
|
2647
|
-
function ChatInlineSkillIcon() {
|
|
2648
|
-
return /* @__PURE__ */ jsxs("svg", {
|
|
2649
|
-
viewBox: "0 0 16 16",
|
|
2650
|
-
fill: "none",
|
|
2651
|
-
stroke: "currentColor",
|
|
2652
|
-
strokeWidth: "1.25",
|
|
2653
|
-
strokeLinecap: "round",
|
|
2654
|
-
strokeLinejoin: "round",
|
|
2655
|
-
"aria-hidden": "true",
|
|
2656
|
-
className: "h-3 w-3",
|
|
2657
|
-
children: [
|
|
2658
|
-
/* @__PURE__ */ jsx("path", { d: "M8.5 2.75 2.75 6l5.75 3.25L14.25 6 8.5 2.75Z" }),
|
|
2659
|
-
/* @__PURE__ */ jsx("path", { d: "M2.75 10 8.5 13.25 14.25 10" }),
|
|
2660
|
-
/* @__PURE__ */ jsx("path", { d: "M2.75 6v4l5.75 3.25V9.25L2.75 6Z" }),
|
|
2661
|
-
/* @__PURE__ */ jsx("path", { d: "M14.25 6v4L8.5 13.25V9.25L14.25 6Z" })
|
|
2662
|
-
]
|
|
2663
|
-
});
|
|
2664
|
-
}
|
|
2665
|
-
function ChatInlineTokenBadge({ kind, label, isUser }) {
|
|
2666
|
-
const isSkill = kind === "skill";
|
|
2667
|
-
return /* @__PURE__ */ jsxs("span", {
|
|
2668
|
-
className: cn("mx-[2px] inline-flex h-7 max-w-full items-center gap-1.5 rounded-xl border px-2.5 align-baseline text-[11px] font-medium shadow-[0_0_0_1px_rgba(15,23,42,0.03)]", isSkill ? isUser ? "border-emerald-200/35 bg-emerald-400/18 text-emerald-50/95" : "border-emerald-200/70 bg-emerald-50 text-emerald-700" : isUser ? "border-white/30 bg-white/18 text-white" : "border-slate-200/80 bg-slate-100 text-slate-700"),
|
|
2669
|
-
title: label,
|
|
2670
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
2671
|
-
className: cn("inline-flex h-3.5 w-3.5 shrink-0 items-center justify-center", isSkill ? isUser ? "text-emerald-100/90" : "text-emerald-600" : isUser ? "text-white/70" : "text-slate-500"),
|
|
2672
|
-
children: /* @__PURE__ */ jsx(ChatInlineSkillIcon, {})
|
|
2673
|
-
}), /* @__PURE__ */ jsx("span", {
|
|
2674
|
-
className: "truncate",
|
|
2675
|
-
children: label
|
|
2676
|
-
})]
|
|
2677
|
-
});
|
|
2678
|
-
}
|
|
2679
|
-
function ChatMessageInlineContent({ segments, role, texts, onFileOpen }) {
|
|
2680
|
-
const isUser = role === "user";
|
|
2681
|
-
return /* @__PURE__ */ jsx("div", {
|
|
2682
|
-
className: "whitespace-pre-wrap break-words leading-6",
|
|
2683
|
-
children: segments.map((segment, index) => {
|
|
2684
|
-
if (segment.type === "token") return /* @__PURE__ */ jsx(ChatInlineTokenBadge, {
|
|
2685
|
-
kind: segment.token.kind,
|
|
2686
|
-
label: segment.token.label,
|
|
2687
|
-
isUser
|
|
2688
|
-
}, `token-${index}-${segment.token.kind}-${segment.token.key}`);
|
|
2689
|
-
if (!hasVisibleInlineText(segment.text)) return /* @__PURE__ */ jsx("span", {
|
|
2690
|
-
className: "whitespace-pre-wrap",
|
|
2691
|
-
children: segment.text
|
|
2692
|
-
}, `space-${index}`);
|
|
2693
|
-
return /* @__PURE__ */ jsx(ChatMessageMarkdown, {
|
|
2694
|
-
text: segment.text,
|
|
2695
|
-
role,
|
|
2696
|
-
texts,
|
|
2697
|
-
inline: true,
|
|
2698
|
-
onFileOpen
|
|
2699
|
-
}, `markdown-${index}`);
|
|
2700
|
-
})
|
|
2701
|
-
});
|
|
2702
|
-
}
|
|
2703
|
-
//#endregion
|
|
2704
2930
|
//#region src/components/chat/ui/chat-message-list/chat-message-file/meta.ts
|
|
2705
2931
|
const FILE_CATEGORY_TILE_CLASSES = {
|
|
2706
2932
|
archive: "border-amber-200/80 bg-amber-50 text-amber-700",
|
|
@@ -3977,23 +4203,15 @@ const ChatMessage = memo(function ChatMessage({ message, texts, onToolAction, on
|
|
|
3977
4203
|
children: message.parts.map((part, index) => {
|
|
3978
4204
|
const { type } = part;
|
|
3979
4205
|
if (type === "markdown") {
|
|
3980
|
-
const { text } = part;
|
|
4206
|
+
const { inlineTokens, text } = part;
|
|
3981
4207
|
return /* @__PURE__ */ jsx(ChatMessageMarkdown, {
|
|
3982
4208
|
text,
|
|
3983
4209
|
role,
|
|
3984
4210
|
texts,
|
|
4211
|
+
inlineTokens,
|
|
3985
4212
|
onFileOpen
|
|
3986
4213
|
}, `markdown-${index}`);
|
|
3987
4214
|
}
|
|
3988
|
-
if (type === "inline-content") {
|
|
3989
|
-
const { segments } = part;
|
|
3990
|
-
return /* @__PURE__ */ jsx(ChatMessageInlineContent, {
|
|
3991
|
-
segments,
|
|
3992
|
-
role,
|
|
3993
|
-
texts,
|
|
3994
|
-
onFileOpen
|
|
3995
|
-
}, `inline-content-${index}`);
|
|
3996
|
-
}
|
|
3997
4215
|
if (type === "reasoning") {
|
|
3998
4216
|
const { label, text } = part;
|
|
3999
4217
|
return /* @__PURE__ */ jsx(ChatReasoningBlock, {
|
|
@@ -4188,6 +4406,54 @@ function ChatMessageList({ className, isSending, messages, onFileOpen, onToolAct
|
|
|
4188
4406
|
});
|
|
4189
4407
|
}
|
|
4190
4408
|
//#endregion
|
|
4191
|
-
|
|
4409
|
+
//#region src/lib/input-surface/input-surface-plugin.utils.ts
|
|
4410
|
+
function createInputSurfaceTriggeredPanelPlugin(params) {
|
|
4411
|
+
return {
|
|
4412
|
+
key: params.key,
|
|
4413
|
+
triggerSpecs: [params.trigger],
|
|
4414
|
+
resolvePanel: (context) => context.trigger.key === params.trigger.key ? params.resolvePanel(context) : null
|
|
4415
|
+
};
|
|
4416
|
+
}
|
|
4417
|
+
function createInputSurfaceReferenceTokenPlugin(params) {
|
|
4418
|
+
return createInputSurfaceTriggeredPanelPlugin({
|
|
4419
|
+
key: params.key,
|
|
4420
|
+
trigger: params.trigger,
|
|
4421
|
+
resolvePanel: (context) => ({
|
|
4422
|
+
isLoading: params.getIsLoading?.(context) ?? false,
|
|
4423
|
+
items: params.getItems({
|
|
4424
|
+
context,
|
|
4425
|
+
records: params.getRecords(context),
|
|
4426
|
+
query: context.trigger.query,
|
|
4427
|
+
tokenKind: params.tokenKind
|
|
4428
|
+
}),
|
|
4429
|
+
onSelectItem: params.onSelectItem,
|
|
4430
|
+
texts: params.texts
|
|
4431
|
+
})
|
|
4432
|
+
});
|
|
4433
|
+
}
|
|
4434
|
+
function resolveChatInputSurfaceState(params) {
|
|
4435
|
+
const { data, plugins, trigger } = params;
|
|
4436
|
+
const triggerSpecs = plugins.flatMap((plugin) => [...plugin.triggerSpecs]);
|
|
4437
|
+
if (!trigger) return {
|
|
4438
|
+
triggerSpecs,
|
|
4439
|
+
panel: null
|
|
4440
|
+
};
|
|
4441
|
+
for (const plugin of plugins) {
|
|
4442
|
+
const panel = plugin.resolvePanel({
|
|
4443
|
+
data,
|
|
4444
|
+
trigger
|
|
4445
|
+
});
|
|
4446
|
+
if (panel) return {
|
|
4447
|
+
triggerSpecs,
|
|
4448
|
+
panel
|
|
4449
|
+
};
|
|
4450
|
+
}
|
|
4451
|
+
return {
|
|
4452
|
+
triggerSpecs,
|
|
4453
|
+
panel: null
|
|
4454
|
+
};
|
|
4455
|
+
}
|
|
4456
|
+
//#endregion
|
|
4457
|
+
export { CHAT_INPUT_SURFACE_SLASH_TRIGGER_SPEC, ChatInputBar, ChatMessageList, ChatMessageMarkdown, FileOperationCodeSurface, FileOperationLinesGrid, copyText, createChatComposerNodesFromText, createChatComposerTextNode, createChatComposerTokenNode, createEmptyChatComposerNodes, createInputSurfaceReferenceTokenPlugin, createInputSurfaceTriggeredPanelPlugin, extractChatComposerTokenKeys, normalizeChatComposerNodes, removeChatComposerTokenNodes, replaceChatComposerRange, resolveChatComposerActiveInputSurfaceTrigger, resolveChatComposerInputSurfaceTrigger, resolveChatComposerSlashTrigger, resolveChatInputSurfaceState, serializeChatComposerDocument, serializeChatComposerPlainText, useActiveItemScroll, useCopyFeedback, useElementWidth, useStickyBottomScroll };
|
|
4192
4458
|
|
|
4193
4459
|
//# sourceMappingURL=index.js.map
|