@nextclaw/agent-chat-ui 0.5.1 → 0.5.3
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 +1143 -851
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5,12 +5,11 @@ 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";
|
|
12
12
|
import { LexicalComposer } from "@lexical/react/LexicalComposer";
|
|
13
|
-
import { EditorRefPlugin } from "@lexical/react/LexicalEditorRefPlugin";
|
|
14
13
|
import { LexicalErrorBoundary } from "@lexical/react/LexicalErrorBoundary";
|
|
15
14
|
import { PlainTextPlugin } from "@lexical/react/LexicalPlainTextPlugin";
|
|
16
15
|
import { $applyNodeReplacement, $createLineBreakNode, $createParagraphNode, $createRangeSelection, $createTextNode, $getRoot, $getSelection, $isElementNode, $isRangeSelection, $isTextNode, $setSelection, BLUR_COMMAND, COMMAND_PRIORITY_EDITOR, COMMAND_PRIORITY_HIGH, DecoratorNode, KEY_DOWN_COMMAND, SELECTION_CHANGE_COMMAND, mergeRegister } from "lexical";
|
|
@@ -30,6 +29,180 @@ import sql from "highlight.js/lib/languages/sql";
|
|
|
30
29
|
import typescript from "highlight.js/lib/languages/typescript";
|
|
31
30
|
import xml from "highlight.js/lib/languages/xml";
|
|
32
31
|
import yaml from "highlight.js/lib/languages/yaml";
|
|
32
|
+
const CHAT_INPUT_SURFACE_SLASH_TRIGGER_SPEC = {
|
|
33
|
+
key: "slash",
|
|
34
|
+
marker: "/"
|
|
35
|
+
};
|
|
36
|
+
function createComposerNodeId() {
|
|
37
|
+
return `composer-${Math.random().toString(36).slice(2, 10)}`;
|
|
38
|
+
}
|
|
39
|
+
function createChatComposerTextNode(text = "") {
|
|
40
|
+
return {
|
|
41
|
+
id: createComposerNodeId(),
|
|
42
|
+
type: "text",
|
|
43
|
+
text
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function createChatComposerTokenNode(params) {
|
|
47
|
+
return {
|
|
48
|
+
id: createComposerNodeId(),
|
|
49
|
+
type: "token",
|
|
50
|
+
tokenKind: params.tokenKind,
|
|
51
|
+
tokenKey: params.tokenKey,
|
|
52
|
+
label: params.label
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function getChatComposerNodeLength(node) {
|
|
56
|
+
return node.type === "text" ? node.text.length : 1;
|
|
57
|
+
}
|
|
58
|
+
function createEmptyChatComposerNodes() {
|
|
59
|
+
return [createChatComposerTextNode("")];
|
|
60
|
+
}
|
|
61
|
+
function createChatComposerNodesFromText(text) {
|
|
62
|
+
return [createChatComposerTextNode(text)];
|
|
63
|
+
}
|
|
64
|
+
function normalizeChatComposerNodes(nodes) {
|
|
65
|
+
const normalized = [];
|
|
66
|
+
for (const node of nodes) {
|
|
67
|
+
if (node.type === "text") {
|
|
68
|
+
if (node.text.length === 0) continue;
|
|
69
|
+
const previous = normalized[normalized.length - 1];
|
|
70
|
+
if (previous?.type === "text") {
|
|
71
|
+
normalized[normalized.length - 1] = {
|
|
72
|
+
...previous,
|
|
73
|
+
text: previous.text + node.text
|
|
74
|
+
};
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
normalized.push(node);
|
|
79
|
+
}
|
|
80
|
+
if (normalized.length === 0) return createEmptyChatComposerNodes();
|
|
81
|
+
return normalized;
|
|
82
|
+
}
|
|
83
|
+
function serializeChatComposerDocument(nodes) {
|
|
84
|
+
return nodes.map((node) => node.type === "text" ? node.text : "").join("");
|
|
85
|
+
}
|
|
86
|
+
function serializeChatComposerPlainText(nodes) {
|
|
87
|
+
return nodes.filter((node) => node.type === "text").map((node) => node.text).join("");
|
|
88
|
+
}
|
|
89
|
+
function extractChatComposerTokenKeys(nodes, tokenKind) {
|
|
90
|
+
const keys = [];
|
|
91
|
+
const keySet = /* @__PURE__ */ new Set();
|
|
92
|
+
for (const node of nodes) {
|
|
93
|
+
if (node.type !== "token" || node.tokenKind !== tokenKind || keySet.has(node.tokenKey)) continue;
|
|
94
|
+
keySet.add(node.tokenKey);
|
|
95
|
+
keys.push(node.tokenKey);
|
|
96
|
+
}
|
|
97
|
+
return keys;
|
|
98
|
+
}
|
|
99
|
+
function buildTrimmedTextEdges(node, nodeStart, rangeStart, rangeEnd) {
|
|
100
|
+
const prefixLength = Math.max(0, rangeStart - nodeStart);
|
|
101
|
+
const suffixLength = Math.max(0, nodeStart + node.text.length - rangeEnd);
|
|
102
|
+
const edges = [];
|
|
103
|
+
if (prefixLength > 0) edges.push({
|
|
104
|
+
...node,
|
|
105
|
+
text: node.text.slice(0, prefixLength)
|
|
106
|
+
});
|
|
107
|
+
if (suffixLength > 0) edges.push({
|
|
108
|
+
...node,
|
|
109
|
+
text: node.text.slice(node.text.length - suffixLength)
|
|
110
|
+
});
|
|
111
|
+
return edges;
|
|
112
|
+
}
|
|
113
|
+
function isNodeOutsideComposerRange(nodeStart, nodeEnd, boundedStart, boundedEnd) {
|
|
114
|
+
return nodeEnd <= boundedStart || nodeStart >= boundedEnd;
|
|
115
|
+
}
|
|
116
|
+
function appendReplacementBeforeNode(nextNodes, replacement, inserted, nodeStart, boundedEnd) {
|
|
117
|
+
if (!inserted && nodeStart >= boundedEnd) {
|
|
118
|
+
nextNodes.push(...replacement);
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
return inserted;
|
|
122
|
+
}
|
|
123
|
+
function appendTextNodeWithReplacement(nextNodes, node, nodeStart, boundedStart, boundedEnd, replacement, inserted) {
|
|
124
|
+
const edges = buildTrimmedTextEdges(node, nodeStart, boundedStart, boundedEnd);
|
|
125
|
+
if (edges[0]) nextNodes.push(edges[0]);
|
|
126
|
+
let didInsert = inserted;
|
|
127
|
+
if (!didInsert) {
|
|
128
|
+
nextNodes.push(...replacement);
|
|
129
|
+
didInsert = true;
|
|
130
|
+
}
|
|
131
|
+
if (edges[1]) nextNodes.push(edges[1]);
|
|
132
|
+
return didInsert;
|
|
133
|
+
}
|
|
134
|
+
function appendTokenNodeWithReplacement(nextNodes, node, replacement, inserted, boundedStart, nodeStart) {
|
|
135
|
+
if (!inserted && boundedStart <= nodeStart) {
|
|
136
|
+
nextNodes.push(...replacement);
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
nextNodes.push(node);
|
|
140
|
+
return inserted;
|
|
141
|
+
}
|
|
142
|
+
function replaceChatComposerRange(nodes, start, end, replacement) {
|
|
143
|
+
const boundedStart = Math.max(0, start);
|
|
144
|
+
const boundedEnd = Math.max(boundedStart, end);
|
|
145
|
+
const nextNodes = [];
|
|
146
|
+
let cursor = 0;
|
|
147
|
+
let inserted = false;
|
|
148
|
+
for (const node of nodes) {
|
|
149
|
+
const nodeLength = getChatComposerNodeLength(node);
|
|
150
|
+
const nodeStart = cursor;
|
|
151
|
+
const nodeEnd = cursor + nodeLength;
|
|
152
|
+
if (isNodeOutsideComposerRange(nodeStart, nodeEnd, boundedStart, boundedEnd)) {
|
|
153
|
+
inserted = appendReplacementBeforeNode(nextNodes, replacement, inserted, nodeStart, boundedEnd);
|
|
154
|
+
nextNodes.push(node);
|
|
155
|
+
cursor = nodeEnd;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
if (node.type === "text") inserted = appendTextNodeWithReplacement(nextNodes, node, nodeStart, boundedStart, boundedEnd, replacement, inserted);
|
|
159
|
+
else inserted = appendTokenNodeWithReplacement(nextNodes, node, replacement, inserted, boundedStart, nodeStart);
|
|
160
|
+
cursor = nodeEnd;
|
|
161
|
+
}
|
|
162
|
+
if (!inserted) nextNodes.push(...replacement);
|
|
163
|
+
return normalizeChatComposerNodes(nextNodes);
|
|
164
|
+
}
|
|
165
|
+
function removeChatComposerTokenNodes(nodes, predicate) {
|
|
166
|
+
return normalizeChatComposerNodes(nodes.filter((node) => node.type !== "token" || !predicate(node)));
|
|
167
|
+
}
|
|
168
|
+
function hasTriggerBoundary(prefix, markerStart) {
|
|
169
|
+
return markerStart === 0 || /\s/.test(prefix[markerStart - 1] ?? "");
|
|
170
|
+
}
|
|
171
|
+
function resolveChatComposerInputSurfaceTrigger(nodes, selection, triggerSpec) {
|
|
172
|
+
if (!selection || selection.start !== selection.end) return null;
|
|
173
|
+
const documentText = serializeChatComposerDocument(nodes);
|
|
174
|
+
const caret = selection.end;
|
|
175
|
+
const prefix = documentText.slice(0, caret);
|
|
176
|
+
const markerStart = prefix.lastIndexOf(triggerSpec.marker);
|
|
177
|
+
if (markerStart < 0 || !hasTriggerBoundary(prefix, markerStart)) return null;
|
|
178
|
+
const rawQuery = prefix.slice(markerStart + triggerSpec.marker.length);
|
|
179
|
+
if (/[\s\uFFFC]/.test(rawQuery)) return null;
|
|
180
|
+
return {
|
|
181
|
+
key: triggerSpec.key,
|
|
182
|
+
marker: triggerSpec.marker,
|
|
183
|
+
query: rawQuery.trim().toLowerCase(),
|
|
184
|
+
start: markerStart,
|
|
185
|
+
end: caret
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
function resolveChatComposerActiveInputSurfaceTrigger(nodes, selection, triggerSpecs = [CHAT_INPUT_SURFACE_SLASH_TRIGGER_SPEC]) {
|
|
189
|
+
let activeTrigger = null;
|
|
190
|
+
for (const triggerSpec of triggerSpecs) {
|
|
191
|
+
const trigger = resolveChatComposerInputSurfaceTrigger(nodes, selection, triggerSpec);
|
|
192
|
+
if (!trigger) continue;
|
|
193
|
+
if (!activeTrigger || trigger.start > activeTrigger.start) activeTrigger = trigger;
|
|
194
|
+
}
|
|
195
|
+
return activeTrigger;
|
|
196
|
+
}
|
|
197
|
+
function resolveChatComposerSlashTrigger(nodes, selection) {
|
|
198
|
+
const trigger = resolveChatComposerInputSurfaceTrigger(nodes, selection, CHAT_INPUT_SURFACE_SLASH_TRIGGER_SPEC);
|
|
199
|
+
return trigger ? {
|
|
200
|
+
query: trigger.query,
|
|
201
|
+
start: trigger.start,
|
|
202
|
+
end: trigger.end
|
|
203
|
+
} : null;
|
|
204
|
+
}
|
|
205
|
+
//#endregion
|
|
33
206
|
//#region src/components/chat/hooks/use-active-item-scroll.ts
|
|
34
207
|
const defaultGetItemSelector = (index) => `[data-item-index="${index}"]`;
|
|
35
208
|
function useActiveItemScroll(params) {
|
|
@@ -82,7 +255,7 @@ function cn(...inputs) {
|
|
|
82
255
|
//#region src/components/chat/default-skin/input.tsx
|
|
83
256
|
const ChatInput = React.forwardRef(({ className, type, ...props }, ref) => /* @__PURE__ */ jsx("input", {
|
|
84
257
|
type,
|
|
85
|
-
className: cn("flex h-9 w-full rounded-xl border border-
|
|
258
|
+
className: cn("flex h-9 w-full rounded-xl border border-border bg-card px-3.5 py-2 text-sm text-foreground file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground/55 placeholder:font-normal focus:outline-none focus:ring-1 focus:ring-primary/40 focus:border-primary/40 transition-colors disabled:cursor-not-allowed disabled:opacity-50", className),
|
|
86
259
|
ref,
|
|
87
260
|
...props
|
|
88
261
|
}));
|
|
@@ -102,7 +275,7 @@ const ChatPopoverContent = React.forwardRef(({ className, sideOffset = 8, align
|
|
|
102
275
|
sideOffset,
|
|
103
276
|
align,
|
|
104
277
|
collisionPadding,
|
|
105
|
-
className: cn("z-[var(--z-popover,50)] w-72 overflow-x-hidden overflow-y-auto rounded-2xl border border-
|
|
278
|
+
className: cn("z-[var(--z-popover,50)] w-72 overflow-x-hidden overflow-y-auto rounded-2xl border border-border bg-popover p-4 text-popover-foreground shadow-lg animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=top]:slide-in-from-bottom-2 data-[side=right]:slide-in-from-left-2", className),
|
|
106
279
|
style: {
|
|
107
280
|
maxHeight: CHAT_POPOVER_CONTENT_MAX_HEIGHT,
|
|
108
281
|
...style
|
|
@@ -122,7 +295,7 @@ function createChatSelectAvailableHeightLimit(limit) {
|
|
|
122
295
|
const CHAT_SELECT_CONTENT_MAX_HEIGHT = createChatSelectAvailableHeightLimit("24rem");
|
|
123
296
|
const ChatSelectTrigger = React.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(SelectPrimitive.Trigger, {
|
|
124
297
|
ref,
|
|
125
|
-
className: cn("flex h-9 w-full items-center justify-between whitespace-nowrap rounded-xl border border-
|
|
298
|
+
className: cn("flex h-9 w-full items-center justify-between whitespace-nowrap rounded-xl border border-border bg-card px-3 py-2 text-sm text-foreground shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-primary/40 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1", className),
|
|
126
299
|
...props,
|
|
127
300
|
children: [children, /* @__PURE__ */ jsx(SelectPrimitive.Icon, {
|
|
128
301
|
asChild: true,
|
|
@@ -146,7 +319,7 @@ const ChatSelectScrollDownButton = React.forwardRef(({ className, ...props }, re
|
|
|
146
319
|
ChatSelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
|
|
147
320
|
const ChatSelectContent = React.forwardRef(({ className, children, collisionPadding = 12, position = "popper", style, viewportClassName, ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(SelectPrimitive.Content, {
|
|
148
321
|
ref,
|
|
149
|
-
className: cn("relative z-50 flex max-h-96 min-w-[8rem] flex-col overflow-hidden rounded-md bg-
|
|
322
|
+
className: cn("relative z-50 flex max-h-96 min-w-[8rem] flex-col overflow-hidden rounded-md border border-border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", className),
|
|
150
323
|
collisionPadding,
|
|
151
324
|
position,
|
|
152
325
|
style: {
|
|
@@ -166,13 +339,13 @@ const ChatSelectContent = React.forwardRef(({ className, children, collisionPadd
|
|
|
166
339
|
ChatSelectContent.displayName = SelectPrimitive.Content.displayName;
|
|
167
340
|
const ChatSelectLabel = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Label, {
|
|
168
341
|
ref,
|
|
169
|
-
className: cn("px-2 py-1 text-[11px] font-semibold uppercase tracking-[0.08em] text-
|
|
342
|
+
className: cn("px-2 py-1 text-[11px] font-semibold uppercase tracking-[0.08em] text-muted-foreground", className),
|
|
170
343
|
...props
|
|
171
344
|
}));
|
|
172
345
|
ChatSelectLabel.displayName = SelectPrimitive.Label.displayName;
|
|
173
346
|
const ChatSelectItem = React.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(SelectPrimitive.Item, {
|
|
174
347
|
ref,
|
|
175
|
-
className: cn("relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-
|
|
348
|
+
className: cn("relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 hover:bg-accent hover:text-accent-foreground", className),
|
|
176
349
|
...props,
|
|
177
350
|
children: [/* @__PURE__ */ jsx("span", {
|
|
178
351
|
className: "absolute right-2 flex h-3.5 w-3.5 items-center justify-center",
|
|
@@ -182,7 +355,7 @@ const ChatSelectItem = React.forwardRef(({ className, children, ...props }, ref)
|
|
|
182
355
|
ChatSelectItem.displayName = SelectPrimitive.Item.displayName;
|
|
183
356
|
const ChatSelectSeparator = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Separator, {
|
|
184
357
|
ref,
|
|
185
|
-
className: cn("-mx-1 my-1 h-px bg-
|
|
358
|
+
className: cn("-mx-1 my-1 h-px bg-border", className),
|
|
186
359
|
...props
|
|
187
360
|
}));
|
|
188
361
|
ChatSelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
|
@@ -220,27 +393,77 @@ const ChatUiPrimitives = {
|
|
|
220
393
|
TooltipTrigger: ChatTooltipTrigger
|
|
221
394
|
};
|
|
222
395
|
//#endregion
|
|
223
|
-
//#region src/components/chat/ui/
|
|
224
|
-
const
|
|
225
|
-
const
|
|
226
|
-
const
|
|
227
|
-
const
|
|
228
|
-
const
|
|
229
|
-
function
|
|
396
|
+
//#region src/components/chat/ui/input-surface/chat-input-surface-menu.tsx
|
|
397
|
+
const INPUT_SURFACE_PANEL_MAX_WIDTH = 680;
|
|
398
|
+
const INPUT_SURFACE_PANEL_DESKTOP_SHRINK_RATIO = .82;
|
|
399
|
+
const INPUT_SURFACE_PANEL_DESKTOP_MIN_WIDTH = 560;
|
|
400
|
+
const INPUT_SURFACE_PANEL_MAX_HEIGHT = createChatPopoverAvailableHeightLimit("24rem");
|
|
401
|
+
const INPUT_SURFACE_PANEL_MIN_HEIGHT = createChatPopoverAvailableHeightLimit("240px");
|
|
402
|
+
const ChatInputSurfaceMenu = forwardRef(function ChatInputSurfaceMenu(props, ref) {
|
|
230
403
|
const { Popover, PopoverAnchor, PopoverContent } = ChatUiPrimitives;
|
|
231
404
|
const { elementRef: anchorRef, width: panelWidth } = useElementWidth();
|
|
232
405
|
const listRef = useRef(null);
|
|
233
|
-
const
|
|
406
|
+
const [activeState, setActiveState] = useState({
|
|
407
|
+
index: 0,
|
|
408
|
+
itemsSignature: ""
|
|
409
|
+
});
|
|
410
|
+
const { isOpen, isLoading, items, texts, onSelectItem, onOpenChange, onDetailsPointerDown } = props;
|
|
411
|
+
const itemsSignature = useMemo(() => items.map((item) => item.key).join(""), [items]);
|
|
412
|
+
const hasItemSections = useMemo(() => items.some((item) => Boolean(item.sectionLabel?.trim())), [items]);
|
|
413
|
+
const activeIndex = activeState.itemsSignature === itemsSignature ? activeState.index : 0;
|
|
414
|
+
const activeIndexInRange = items.length === 0 ? 0 : Math.min(activeIndex, items.length - 1);
|
|
415
|
+
const activeItem = items[activeIndexInRange] ?? null;
|
|
416
|
+
const setActiveIndexForCurrentItems = useCallback((nextIndex) => {
|
|
417
|
+
setActiveState((currentState) => {
|
|
418
|
+
const currentIndex = currentState.itemsSignature === itemsSignature ? currentState.index : 0;
|
|
419
|
+
return {
|
|
420
|
+
index: typeof nextIndex === "function" ? nextIndex(currentIndex) : nextIndex,
|
|
421
|
+
itemsSignature
|
|
422
|
+
};
|
|
423
|
+
});
|
|
424
|
+
}, [itemsSignature]);
|
|
234
425
|
const resolvedWidth = useMemo(() => {
|
|
235
426
|
if (!panelWidth) return;
|
|
236
|
-
return Math.min(panelWidth >
|
|
427
|
+
return Math.min(panelWidth > INPUT_SURFACE_PANEL_DESKTOP_MIN_WIDTH ? panelWidth * INPUT_SURFACE_PANEL_DESKTOP_SHRINK_RATIO : panelWidth, INPUT_SURFACE_PANEL_MAX_WIDTH);
|
|
237
428
|
}, [panelWidth]);
|
|
429
|
+
useImperativeHandle(ref, () => ({ handleKeyDown: (event) => {
|
|
430
|
+
if (!isOpen) return false;
|
|
431
|
+
if (event.key === "Escape") {
|
|
432
|
+
event.preventDefault();
|
|
433
|
+
onOpenChange(false);
|
|
434
|
+
return true;
|
|
435
|
+
}
|
|
436
|
+
if (items.length === 0) return false;
|
|
437
|
+
if (event.key === "ArrowDown") {
|
|
438
|
+
event.preventDefault();
|
|
439
|
+
setActiveIndexForCurrentItems((index) => (index + 1) % items.length);
|
|
440
|
+
return true;
|
|
441
|
+
}
|
|
442
|
+
if (event.key === "ArrowUp") {
|
|
443
|
+
event.preventDefault();
|
|
444
|
+
setActiveIndexForCurrentItems((index) => (index - 1 + items.length) % items.length);
|
|
445
|
+
return true;
|
|
446
|
+
}
|
|
447
|
+
if (event.key === "Enter" && !event.shiftKey || event.key === "Tab") {
|
|
448
|
+
event.preventDefault();
|
|
449
|
+
onSelectItem(items[activeIndexInRange]);
|
|
450
|
+
return true;
|
|
451
|
+
}
|
|
452
|
+
return false;
|
|
453
|
+
} }), [
|
|
454
|
+
activeIndexInRange,
|
|
455
|
+
isOpen,
|
|
456
|
+
items,
|
|
457
|
+
onOpenChange,
|
|
458
|
+
onSelectItem,
|
|
459
|
+
setActiveIndexForCurrentItems
|
|
460
|
+
]);
|
|
238
461
|
useActiveItemScroll({
|
|
239
462
|
containerRef: listRef,
|
|
240
|
-
activeIndex,
|
|
463
|
+
activeIndex: activeIndexInRange,
|
|
241
464
|
itemCount: items.length,
|
|
242
465
|
isEnabled: isOpen && !isLoading,
|
|
243
|
-
getItemSelector: (index) => `[data-
|
|
466
|
+
getItemSelector: (index) => `[data-input-surface-index="${index}"]`
|
|
244
467
|
});
|
|
245
468
|
return /* @__PURE__ */ jsxs(Popover, {
|
|
246
469
|
open: isOpen,
|
|
@@ -249,7 +472,7 @@ function ChatSlashMenu(props) {
|
|
|
249
472
|
asChild: true,
|
|
250
473
|
children: /* @__PURE__ */ jsx("div", {
|
|
251
474
|
ref: anchorRef,
|
|
252
|
-
className: "pointer-events-none absolute bottom-
|
|
475
|
+
className: "pointer-events-none absolute bottom-0 left-3 right-3 top-0"
|
|
253
476
|
})
|
|
254
477
|
}), /* @__PURE__ */ jsx(PopoverContent, {
|
|
255
478
|
side: "top",
|
|
@@ -258,46 +481,63 @@ function ChatSlashMenu(props) {
|
|
|
258
481
|
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
482
|
onOpenAutoFocus: (event) => event.preventDefault(),
|
|
260
483
|
style: {
|
|
261
|
-
maxHeight:
|
|
484
|
+
maxHeight: INPUT_SURFACE_PANEL_MAX_HEIGHT,
|
|
262
485
|
width: resolvedWidth ? `${resolvedWidth}px` : void 0
|
|
263
486
|
},
|
|
264
487
|
children: /* @__PURE__ */ jsxs("div", {
|
|
265
488
|
className: "grid min-h-0 flex-1 grid-cols-[minmax(220px,300px)_minmax(0,1fr)]",
|
|
266
|
-
style: { minHeight:
|
|
489
|
+
style: { minHeight: INPUT_SURFACE_PANEL_MIN_HEIGHT },
|
|
267
490
|
children: [/* @__PURE__ */ jsx("div", {
|
|
268
491
|
ref: listRef,
|
|
269
492
|
role: "listbox",
|
|
270
|
-
"aria-label": texts.
|
|
493
|
+
"aria-label": texts.sectionLabel,
|
|
271
494
|
className: "custom-scrollbar min-h-0 overflow-y-auto overscroll-contain border-r border-gray-200 p-2.5",
|
|
272
495
|
children: isLoading ? /* @__PURE__ */ jsx("div", {
|
|
273
496
|
className: "p-2 text-xs text-gray-500",
|
|
274
|
-
children: texts.
|
|
275
|
-
}) : /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
|
|
497
|
+
children: texts.loadingLabel
|
|
498
|
+
}) : /* @__PURE__ */ jsxs(Fragment$1, { children: [!hasItemSections ? /* @__PURE__ */ jsx("div", {
|
|
276
499
|
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", {
|
|
500
|
+
children: texts.sectionLabel
|
|
501
|
+
}) : null, items.length === 0 ? /* @__PURE__ */ jsx("div", {
|
|
279
502
|
className: "px-2 text-xs text-gray-400",
|
|
280
|
-
children: texts.
|
|
503
|
+
children: texts.emptyLabel
|
|
281
504
|
}) : /* @__PURE__ */ jsx("div", {
|
|
282
505
|
className: "space-y-1",
|
|
283
506
|
children: items.map((item, index) => {
|
|
284
|
-
const
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
"
|
|
289
|
-
"
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
507
|
+
const { key, sectionKey, sectionLabel, title, subtitle } = item;
|
|
508
|
+
const isActive = index === activeIndexInRange;
|
|
509
|
+
const previousItem = items[index - 1];
|
|
510
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
511
|
+
className: "space-y-1",
|
|
512
|
+
children: [hasItemSections && Boolean(sectionLabel?.trim()) && previousItem?.sectionKey !== sectionKey ? /* @__PURE__ */ jsx("div", {
|
|
513
|
+
className: "px-2 pb-0.5 pt-1.5 text-[11px] font-semibold uppercase tracking-wide text-gray-500",
|
|
514
|
+
children: sectionLabel
|
|
515
|
+
}) : null, /* @__PURE__ */ jsxs("button", {
|
|
516
|
+
type: "button",
|
|
517
|
+
role: "option",
|
|
518
|
+
"aria-selected": isActive,
|
|
519
|
+
"data-input-surface-index": index,
|
|
520
|
+
onPointerMove: (event) => {
|
|
521
|
+
if (event.pointerType !== "touch") setActiveIndexForCurrentItems(index);
|
|
522
|
+
},
|
|
523
|
+
onPointerDown: (event) => {
|
|
524
|
+
if (event.button > 0) return;
|
|
525
|
+
event.preventDefault();
|
|
526
|
+
onSelectItem(item);
|
|
527
|
+
},
|
|
528
|
+
onClick: (event) => {
|
|
529
|
+
if (event.detail === 0) onSelectItem(item);
|
|
530
|
+
},
|
|
531
|
+
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"}`,
|
|
532
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
533
|
+
className: "truncate text-xs font-semibold",
|
|
534
|
+
children: title
|
|
535
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
536
|
+
className: "truncate text-xs text-gray-500",
|
|
537
|
+
children: subtitle
|
|
538
|
+
})]
|
|
299
539
|
})]
|
|
300
|
-
},
|
|
540
|
+
}, key);
|
|
301
541
|
})
|
|
302
542
|
})] })
|
|
303
543
|
}), /* @__PURE__ */ jsx("div", {
|
|
@@ -329,17 +569,80 @@ function ChatSlashMenu(props) {
|
|
|
329
569
|
}),
|
|
330
570
|
/* @__PURE__ */ jsx("div", {
|
|
331
571
|
className: "pt-1 text-[11px] text-gray-500",
|
|
332
|
-
children: texts.
|
|
572
|
+
children: activeItem.hintLabel ?? texts.itemHintLabel
|
|
333
573
|
})
|
|
334
574
|
]
|
|
335
575
|
}) : /* @__PURE__ */ jsx("div", {
|
|
336
576
|
className: "text-xs text-gray-500",
|
|
337
|
-
children: texts.
|
|
577
|
+
children: texts.hintLabel
|
|
338
578
|
})
|
|
339
579
|
})]
|
|
340
580
|
})
|
|
341
581
|
})]
|
|
342
582
|
});
|
|
583
|
+
});
|
|
584
|
+
//#endregion
|
|
585
|
+
//#region src/components/chat/ui/input-surface/chat-input-surface-host.tsx
|
|
586
|
+
function getInputSurfaceTriggerIdentity(trigger) {
|
|
587
|
+
return `${trigger.key}:${trigger.marker}:${trigger.start}`;
|
|
588
|
+
}
|
|
589
|
+
function isInputSurfaceCreateEvent(trigger, reason) {
|
|
590
|
+
return reason.type === "insert-text" && reason.text === trigger.marker && trigger.query === "" && trigger.end === trigger.start + trigger.marker.length;
|
|
591
|
+
}
|
|
592
|
+
function resolveInputSurfaceTriggerIdentity(currentIdentity, trigger, reason) {
|
|
593
|
+
if (!trigger) return null;
|
|
594
|
+
const nextIdentity = getInputSurfaceTriggerIdentity(trigger);
|
|
595
|
+
if (currentIdentity === nextIdentity) return currentIdentity;
|
|
596
|
+
return isInputSurfaceCreateEvent(trigger, reason) ? nextIdentity : null;
|
|
597
|
+
}
|
|
598
|
+
function ChatInputSurfaceHost(props) {
|
|
599
|
+
const { children, inputSurface, onInputSurfaceTriggerChange, onSelectItem, triggerSpecs = [CHAT_INPUT_SURFACE_SLASH_TRIGGER_SPEC] } = props;
|
|
600
|
+
const menuRef = useRef(null);
|
|
601
|
+
const [activeTriggerIdentity, setActiveTriggerIdentity] = useState(null);
|
|
602
|
+
const isOpen = Boolean(inputSurface) && activeTriggerIdentity !== null;
|
|
603
|
+
const setActiveTrigger = useCallback((identity, trigger) => {
|
|
604
|
+
setActiveTriggerIdentity(identity);
|
|
605
|
+
onInputSurfaceTriggerChange?.(identity ? trigger : null);
|
|
606
|
+
}, [onInputSurfaceTriggerChange]);
|
|
607
|
+
const closeInputSurface = useCallback(() => {
|
|
608
|
+
setActiveTrigger(null, null);
|
|
609
|
+
}, [setActiveTrigger]);
|
|
610
|
+
const handleInputSurfaceSnapshotChange = useCallback((nodes, selection, reason) => {
|
|
611
|
+
const trigger = resolveChatComposerActiveInputSurfaceTrigger(nodes, selection, triggerSpecs);
|
|
612
|
+
const nextIdentity = resolveInputSurfaceTriggerIdentity(activeTriggerIdentity, trigger, reason);
|
|
613
|
+
setActiveTrigger(nextIdentity, nextIdentity ? trigger : null);
|
|
614
|
+
}, [
|
|
615
|
+
activeTriggerIdentity,
|
|
616
|
+
setActiveTrigger,
|
|
617
|
+
triggerSpecs
|
|
618
|
+
]);
|
|
619
|
+
const handleInputSurfaceKeyDown = useCallback((event) => {
|
|
620
|
+
return menuRef.current?.handleKeyDown(event) ?? false;
|
|
621
|
+
}, []);
|
|
622
|
+
const handleInputSurfaceOpenChange = useCallback((open) => {
|
|
623
|
+
if (!open) closeInputSurface();
|
|
624
|
+
}, [closeInputSurface]);
|
|
625
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [children(useMemo(() => ({
|
|
626
|
+
onInputSurfaceKeyDown: handleInputSurfaceKeyDown,
|
|
627
|
+
onInputSurfaceOpenChange: handleInputSurfaceOpenChange,
|
|
628
|
+
onInputSurfaceSnapshotChange: handleInputSurfaceSnapshotChange
|
|
629
|
+
}), [
|
|
630
|
+
handleInputSurfaceKeyDown,
|
|
631
|
+
handleInputSurfaceOpenChange,
|
|
632
|
+
handleInputSurfaceSnapshotChange
|
|
633
|
+
])), inputSurface && isOpen ? /* @__PURE__ */ jsx(ChatInputSurfaceMenu, {
|
|
634
|
+
ref: menuRef,
|
|
635
|
+
isOpen,
|
|
636
|
+
isLoading: inputSurface.isLoading,
|
|
637
|
+
items: inputSurface.items,
|
|
638
|
+
texts: inputSurface.texts,
|
|
639
|
+
onSelectItem: (item) => {
|
|
640
|
+
closeInputSurface();
|
|
641
|
+
onSelectItem(item);
|
|
642
|
+
},
|
|
643
|
+
onOpenChange: handleInputSurfaceOpenChange,
|
|
644
|
+
onDetailsPointerDown: (event) => event.preventDefault()
|
|
645
|
+
}, activeTriggerIdentity) : null] });
|
|
343
646
|
}
|
|
344
647
|
//#endregion
|
|
345
648
|
//#region src/components/chat/default-skin/button.tsx
|
|
@@ -348,12 +651,12 @@ const buttonVariants = cva("inline-flex items-center justify-center whitespace-n
|
|
|
348
651
|
variant: {
|
|
349
652
|
default: "bg-primary text-primary-foreground hover:bg-primary-600 active:bg-primary-700 shadow-sm",
|
|
350
653
|
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
351
|
-
outline: "border border-
|
|
352
|
-
secondary: "bg-
|
|
353
|
-
ghost: "hover:bg-
|
|
654
|
+
outline: "border border-border bg-card text-muted-foreground hover:bg-accent hover:text-accent-foreground",
|
|
655
|
+
secondary: "bg-muted text-foreground hover:bg-accent hover:text-accent-foreground",
|
|
656
|
+
ghost: "text-muted-foreground hover:bg-accent hover:text-accent-foreground",
|
|
354
657
|
link: "text-primary underline-offset-4 hover:underline",
|
|
355
658
|
primary: "bg-primary text-primary-foreground hover:bg-primary-600 active:bg-primary-700 shadow-sm",
|
|
356
|
-
subtle: "bg-
|
|
659
|
+
subtle: "bg-muted text-muted-foreground hover:bg-accent hover:text-accent-foreground",
|
|
357
660
|
"primary-outline": "border border-primary/30 text-primary hover:bg-primary hover:text-primary-foreground"
|
|
358
661
|
},
|
|
359
662
|
size: {
|
|
@@ -391,30 +694,30 @@ function StopIcon() {
|
|
|
391
694
|
return /* @__PURE__ */ jsx("span", {
|
|
392
695
|
"aria-hidden": "true",
|
|
393
696
|
"data-testid": "chat-stop-icon",
|
|
394
|
-
className: "block h-3 w-3 rounded-[2px] bg-
|
|
697
|
+
className: "block h-3 w-3 rounded-[2px] bg-foreground shadow-[inset_0_0_0_1px_hsl(var(--border))]"
|
|
395
698
|
});
|
|
396
699
|
}
|
|
397
700
|
function ContextWindowIndicator({ contextWindow }) {
|
|
398
701
|
const { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } = ChatUiPrimitives;
|
|
399
702
|
const clampedRatio = Math.max(0, Math.min(1, contextWindow.ratio));
|
|
400
703
|
const angle = Math.round(clampedRatio * 360);
|
|
401
|
-
const ringColor = "
|
|
704
|
+
const ringColor = "hsl(var(--muted-foreground))";
|
|
402
705
|
return /* @__PURE__ */ jsx(TooltipProvider, {
|
|
403
706
|
delayDuration: 0,
|
|
404
707
|
children: /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
|
|
405
708
|
asChild: true,
|
|
406
709
|
children: /* @__PURE__ */ jsxs("button", {
|
|
407
710
|
type: "button",
|
|
408
|
-
className: "relative inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-lg text-
|
|
711
|
+
className: "relative inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground",
|
|
409
712
|
"aria-label": contextWindow.label,
|
|
410
713
|
title: contextWindow.label,
|
|
411
714
|
children: [/* @__PURE__ */ jsx("span", {
|
|
412
715
|
"aria-hidden": "true",
|
|
413
716
|
className: "absolute inset-[7px] rounded-full",
|
|
414
|
-
style: { background: `conic-gradient(${ringColor} ${angle}deg,
|
|
717
|
+
style: { background: `conic-gradient(${ringColor} ${angle}deg, hsl(var(--border)) 0deg)` }
|
|
415
718
|
}), /* @__PURE__ */ jsx("span", {
|
|
416
719
|
"aria-hidden": "true",
|
|
417
|
-
className: "absolute inset-[10px] rounded-full bg-
|
|
720
|
+
className: "absolute inset-[10px] rounded-full bg-card"
|
|
418
721
|
})]
|
|
419
722
|
})
|
|
420
723
|
}), /* @__PURE__ */ jsx(TooltipContent, {
|
|
@@ -423,12 +726,12 @@ function ContextWindowIndicator({ contextWindow }) {
|
|
|
423
726
|
children: /* @__PURE__ */ jsxs("div", {
|
|
424
727
|
className: "space-y-1.5 text-xs",
|
|
425
728
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
426
|
-
className: "flex items-center justify-between gap-5 font-semibold text-
|
|
729
|
+
className: "flex items-center justify-between gap-5 font-semibold text-foreground",
|
|
427
730
|
children: [/* @__PURE__ */ jsx("span", { children: contextWindow.label }), /* @__PURE__ */ jsx("span", { children: contextWindow.percentLabel })]
|
|
428
731
|
}), contextWindow.details.map((detail) => /* @__PURE__ */ jsxs("div", {
|
|
429
|
-
className: "flex items-center justify-between gap-5 text-
|
|
732
|
+
className: "flex items-center justify-between gap-5 text-muted-foreground",
|
|
430
733
|
children: [/* @__PURE__ */ jsx("span", { children: detail.label }), /* @__PURE__ */ jsx("span", {
|
|
431
|
-
className: "font-medium text-
|
|
734
|
+
className: "font-medium text-foreground",
|
|
432
735
|
children: detail.value
|
|
433
736
|
})]
|
|
434
737
|
}, detail.label))]
|
|
@@ -591,7 +894,7 @@ function ChatInputBarSkillPicker(props) {
|
|
|
591
894
|
type: "button",
|
|
592
895
|
"aria-haspopup": "listbox",
|
|
593
896
|
"aria-label": picker.title,
|
|
594
|
-
className: "relative inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-lg px-0 text-xs font-medium text-
|
|
897
|
+
className: "relative inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-lg px-0 text-xs font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground sm:w-auto sm:gap-1.5 sm:px-3",
|
|
595
898
|
children: [
|
|
596
899
|
/* @__PURE__ */ jsx(BrainCircuit, { className: "h-4 w-4" }),
|
|
597
900
|
/* @__PURE__ */ jsx("span", {
|
|
@@ -611,13 +914,13 @@ function ChatInputBarSkillPicker(props) {
|
|
|
611
914
|
style: { maxHeight: SKILL_PICKER_MAX_HEIGHT },
|
|
612
915
|
children: [
|
|
613
916
|
/* @__PURE__ */ jsxs("div", {
|
|
614
|
-
className: "shrink-0 space-y-2 border-b border-
|
|
917
|
+
className: "shrink-0 space-y-2 border-b border-border px-4 py-3",
|
|
615
918
|
children: [/* @__PURE__ */ jsx("div", {
|
|
616
|
-
className: "text-sm font-semibold text-
|
|
919
|
+
className: "text-sm font-semibold text-foreground",
|
|
617
920
|
children: picker.title
|
|
618
921
|
}), /* @__PURE__ */ jsxs("div", {
|
|
619
922
|
className: "relative",
|
|
620
|
-
children: [/* @__PURE__ */ jsx(Search, { className: "pointer-events-none absolute left-3 top-2.5 h-3.5 w-3.5 text-
|
|
923
|
+
children: [/* @__PURE__ */ jsx(Search, { className: "pointer-events-none absolute left-3 top-2.5 h-3.5 w-3.5 text-muted-foreground/70" }), /* @__PURE__ */ jsx(Input, {
|
|
621
924
|
value: query,
|
|
622
925
|
onChange: (event) => setQuery(event.target.value),
|
|
623
926
|
onKeyDown: onSearchKeyDown,
|
|
@@ -638,10 +941,10 @@ function ChatInputBarSkillPicker(props) {
|
|
|
638
941
|
"aria-multiselectable": "true",
|
|
639
942
|
className: "custom-scrollbar min-h-0 flex-1 overflow-y-auto overscroll-contain",
|
|
640
943
|
children: picker.isLoading ? /* @__PURE__ */ jsx("div", {
|
|
641
|
-
className: "p-4 text-xs text-
|
|
944
|
+
className: "p-4 text-xs text-muted-foreground",
|
|
642
945
|
children: picker.loadingLabel
|
|
643
946
|
}) : visibleOptions.length === 0 ? /* @__PURE__ */ jsx("div", {
|
|
644
|
-
className: "p-4 text-center text-xs text-
|
|
947
|
+
className: "p-4 text-center text-xs text-muted-foreground",
|
|
645
948
|
children: picker.emptyLabel
|
|
646
949
|
}) : /* @__PURE__ */ jsx("div", {
|
|
647
950
|
className: "py-1",
|
|
@@ -652,7 +955,7 @@ function ChatInputBarSkillPicker(props) {
|
|
|
652
955
|
}];
|
|
653
956
|
let visibleIndex = 0;
|
|
654
957
|
return groups.map((group) => /* @__PURE__ */ jsxs("div", { children: [group.label ? /* @__PURE__ */ jsx("div", {
|
|
655
|
-
className: "px-4 pb-1 pt-2 text-[11px] font-semibold uppercase tracking-wide text-
|
|
958
|
+
className: "px-4 pb-1 pt-2 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground",
|
|
656
959
|
children: group.label
|
|
657
960
|
}) : null, group.options.map((option) => {
|
|
658
961
|
const index = visibleIndex;
|
|
@@ -665,10 +968,10 @@ function ChatInputBarSkillPicker(props) {
|
|
|
665
968
|
"aria-selected": isSelected,
|
|
666
969
|
"data-skill-index": index,
|
|
667
970
|
onMouseEnter: () => setActiveIndex(index),
|
|
668
|
-
className: `flex w-full items-center gap-3 px-4 py-2.5 text-left transition-colors ${isActive ? "bg-
|
|
971
|
+
className: `flex w-full items-center gap-3 px-4 py-2.5 text-left transition-colors ${isActive ? "bg-accent" : "hover:bg-accent"}`,
|
|
669
972
|
children: [
|
|
670
973
|
/* @__PURE__ */ jsx("div", {
|
|
671
|
-
className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-
|
|
974
|
+
className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-muted text-muted-foreground",
|
|
672
975
|
children: /* @__PURE__ */ jsx(Puzzle, { className: "h-4 w-4" })
|
|
673
976
|
}),
|
|
674
977
|
/* @__PURE__ */ jsxs("div", {
|
|
@@ -676,14 +979,14 @@ function ChatInputBarSkillPicker(props) {
|
|
|
676
979
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
677
980
|
className: "flex items-center gap-1.5",
|
|
678
981
|
children: [/* @__PURE__ */ jsx("span", {
|
|
679
|
-
className: "truncate text-sm text-
|
|
982
|
+
className: "truncate text-sm text-foreground",
|
|
680
983
|
children: option.label
|
|
681
984
|
}), option.badgeLabel ? /* @__PURE__ */ jsx("span", {
|
|
682
985
|
className: "shrink-0 rounded-full bg-primary/10 px-1.5 py-0.5 text-[10px] font-medium text-primary",
|
|
683
986
|
children: option.badgeLabel
|
|
684
987
|
}) : null]
|
|
685
988
|
}), /* @__PURE__ */ jsx("div", {
|
|
686
|
-
className: "mt-0.5 truncate text-xs text-
|
|
989
|
+
className: "mt-0.5 truncate text-xs text-muted-foreground",
|
|
687
990
|
children: option.description || option.key
|
|
688
991
|
})]
|
|
689
992
|
}),
|
|
@@ -693,7 +996,7 @@ function ChatInputBarSkillPicker(props) {
|
|
|
693
996
|
type: "button",
|
|
694
997
|
"aria-label": `${isSelected ? "Remove" : "Add"} ${option.label}`,
|
|
695
998
|
onClick: () => onToggleOption(option.key),
|
|
696
|
-
className: isSelected ? "inline-flex h-5 w-5 items-center justify-center rounded-full bg-primary text-white" : "inline-flex h-5 w-5 items-center justify-center rounded-full border border-
|
|
999
|
+
className: isSelected ? "inline-flex h-5 w-5 items-center justify-center rounded-full bg-primary text-white" : "inline-flex h-5 w-5 items-center justify-center rounded-full border border-border bg-card",
|
|
697
1000
|
children: isSelected ? /* @__PURE__ */ jsx(Check, { className: "h-3 w-3" }) : null
|
|
698
1001
|
})
|
|
699
1002
|
})
|
|
@@ -704,7 +1007,7 @@ function ChatInputBarSkillPicker(props) {
|
|
|
704
1007
|
})
|
|
705
1008
|
}),
|
|
706
1009
|
picker.manageHref && picker.manageLabel ? /* @__PURE__ */ jsx("div", {
|
|
707
|
-
className: "shrink-0 border-t border-
|
|
1010
|
+
className: "shrink-0 border-t border-border px-4 py-2.5",
|
|
708
1011
|
children: /* @__PURE__ */ jsxs("a", {
|
|
709
1012
|
href: picker.manageHref,
|
|
710
1013
|
className: "inline-flex items-center gap-1.5 text-xs font-medium text-primary transition-colors hover:text-primary/80",
|
|
@@ -717,7 +1020,7 @@ function ChatInputBarSkillPicker(props) {
|
|
|
717
1020
|
//#endregion
|
|
718
1021
|
//#region src/components/chat/ui/chat-input-bar/chat-input-bar-toolbar.tsx
|
|
719
1022
|
function ToolbarIcon({ icon }) {
|
|
720
|
-
return icon === "sparkles" ? /* @__PURE__ */ jsx(Sparkles, { className: "h-3.5 w-3.5 shrink-0 text-primary" }) : icon === "brain" ? /* @__PURE__ */ jsx(Brain, { className: "h-3.5 w-3.5 shrink-0 text-
|
|
1023
|
+
return icon === "sparkles" ? /* @__PURE__ */ jsx(Sparkles, { className: "h-3.5 w-3.5 shrink-0 text-primary" }) : icon === "brain" ? /* @__PURE__ */ jsx(Brain, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }) : null;
|
|
721
1024
|
}
|
|
722
1025
|
function AccessoryIcon({ icon }) {
|
|
723
1026
|
return icon === "paperclip" ? /* @__PURE__ */ jsx(Paperclip, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(ToolbarIcon, { icon });
|
|
@@ -750,16 +1053,16 @@ function ToolbarSelectTriggerContent({ item }) {
|
|
|
750
1053
|
children: [
|
|
751
1054
|
/* @__PURE__ */ jsx(ToolbarIcon, { icon: item.icon }),
|
|
752
1055
|
/* @__PURE__ */ jsx("span", {
|
|
753
|
-
className: "nextclaw-chat-toolbar-mobile-label truncate text-xs font-semibold text-
|
|
1056
|
+
className: "nextclaw-chat-toolbar-mobile-label truncate text-xs font-semibold text-foreground sm:hidden [@container_nextclaw-chat-input-bar_(max-width:440px)]:hidden",
|
|
754
1057
|
children: mobileSelectedLabel
|
|
755
1058
|
}),
|
|
756
1059
|
/* @__PURE__ */ jsx("span", {
|
|
757
|
-
className: "nextclaw-chat-toolbar-label hidden truncate text-xs font-semibold text-
|
|
1060
|
+
className: "nextclaw-chat-toolbar-label hidden truncate text-xs font-semibold text-foreground sm:inline [@container_nextclaw-chat-input-bar_(max-width:440px)]:hidden",
|
|
758
1061
|
children: item.selectedLabel
|
|
759
1062
|
})
|
|
760
1063
|
]
|
|
761
1064
|
});
|
|
762
|
-
if (item.loading) return /* @__PURE__ */ jsx("div", { className: "h-3 w-24 animate-pulse rounded bg-
|
|
1065
|
+
if (item.loading) return /* @__PURE__ */ jsx("div", { className: "h-3 w-24 animate-pulse rounded bg-muted" });
|
|
763
1066
|
return /* @__PURE__ */ jsx("span", {
|
|
764
1067
|
className: "truncate",
|
|
765
1068
|
children: item.placeholder
|
|
@@ -769,14 +1072,14 @@ function ToolbarSelectOptionContent({ option }) {
|
|
|
769
1072
|
return option.description ? /* @__PURE__ */ jsxs("div", {
|
|
770
1073
|
className: "flex min-w-0 flex-col gap-0.5",
|
|
771
1074
|
children: [/* @__PURE__ */ jsx("span", {
|
|
772
|
-
className: "truncate text-xs font-semibold text-
|
|
1075
|
+
className: "truncate text-xs font-semibold text-foreground",
|
|
773
1076
|
children: option.label
|
|
774
1077
|
}), /* @__PURE__ */ jsx("span", {
|
|
775
|
-
className: "truncate text-[11px] text-
|
|
1078
|
+
className: "truncate text-[11px] text-muted-foreground",
|
|
776
1079
|
children: option.description
|
|
777
1080
|
})]
|
|
778
1081
|
}) : /* @__PURE__ */ jsx("span", {
|
|
779
|
-
className: "truncate text-xs font-semibold text-
|
|
1082
|
+
className: "truncate text-xs font-semibold text-foreground",
|
|
780
1083
|
children: option.label
|
|
781
1084
|
});
|
|
782
1085
|
}
|
|
@@ -810,8 +1113,8 @@ function ToolbarSearchableSelect({ item }) {
|
|
|
810
1113
|
type: "button",
|
|
811
1114
|
"aria-label": item.selectedLabel ? `${item.placeholder}: ${item.selectedLabel}` : item.placeholder,
|
|
812
1115
|
disabled: item.disabled,
|
|
813
|
-
className: `nextclaw-chat-toolbar-select-trigger inline-flex h-8 w-auto items-center justify-between rounded-lg border-0 bg-transparent px-2 text-xs font-medium text-
|
|
814
|
-
children: [/* @__PURE__ */ jsx(ToolbarSelectTriggerContent, { item }), /* @__PURE__ */ jsx(ChevronDown, { className: "ml-1 h-3.5 w-3.5 shrink-0 text-
|
|
1116
|
+
className: `nextclaw-chat-toolbar-select-trigger inline-flex h-8 w-auto items-center justify-between rounded-lg border-0 bg-transparent px-2 text-xs font-medium text-muted-foreground shadow-none hover:bg-accent hover:text-accent-foreground focus:outline-none focus:ring-0 disabled:cursor-not-allowed disabled:opacity-50 sm:px-3 [@container_nextclaw-chat-input-bar_(max-width:440px)]:!basis-8 [@container_nextclaw-chat-input-bar_(max-width:440px)]:!justify-center [@container_nextclaw-chat-input-bar_(max-width:440px)]:!max-w-8 [@container_nextclaw-chat-input-bar_(max-width:440px)]:!min-w-8 [@container_nextclaw-chat-input-bar_(max-width:440px)]:!px-0 ${TRIGGER_WIDTH_BY_KEY[item.key] ?? ""}`,
|
|
1117
|
+
children: [/* @__PURE__ */ jsx(ToolbarSelectTriggerContent, { item }), /* @__PURE__ */ jsx(ChevronDown, { className: "ml-1 h-3.5 w-3.5 shrink-0 text-muted-foreground/70 [@container_nextclaw-chat-input-bar_(max-width:440px)]:hidden" })]
|
|
815
1118
|
})
|
|
816
1119
|
}), /* @__PURE__ */ jsxs(PopoverContent, {
|
|
817
1120
|
className: `flex flex-col overflow-hidden p-2 ${CONTENT_WIDTH_BY_KEY[item.key] ?? ""}`,
|
|
@@ -819,7 +1122,7 @@ function ToolbarSearchableSelect({ item }) {
|
|
|
819
1122
|
children: [
|
|
820
1123
|
/* @__PURE__ */ jsxs("div", {
|
|
821
1124
|
className: "relative mb-2 shrink-0",
|
|
822
|
-
children: [/* @__PURE__ */ jsx(Search, { className: "pointer-events-none absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-
|
|
1125
|
+
children: [/* @__PURE__ */ jsx(Search, { className: "pointer-events-none absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground/70" }), /* @__PURE__ */ jsx(Input, {
|
|
823
1126
|
value: query,
|
|
824
1127
|
onChange: (event) => setQuery(event.currentTarget.value),
|
|
825
1128
|
placeholder: item.search?.placeholder ?? item.placeholder,
|
|
@@ -829,31 +1132,31 @@ function ToolbarSearchableSelect({ item }) {
|
|
|
829
1132
|
!hasOptions ? item.loading ? /* @__PURE__ */ jsxs("div", {
|
|
830
1133
|
className: "space-y-2 px-2 py-1",
|
|
831
1134
|
children: [
|
|
832
|
-
/* @__PURE__ */ jsx("div", { className: "h-3 w-36 animate-pulse rounded bg-
|
|
833
|
-
/* @__PURE__ */ jsx("div", { className: "h-3 w-28 animate-pulse rounded bg-
|
|
834
|
-
/* @__PURE__ */ jsx("div", { className: "h-3 w-32 animate-pulse rounded bg-
|
|
1135
|
+
/* @__PURE__ */ jsx("div", { className: "h-3 w-36 animate-pulse rounded bg-muted" }),
|
|
1136
|
+
/* @__PURE__ */ jsx("div", { className: "h-3 w-28 animate-pulse rounded bg-muted" }),
|
|
1137
|
+
/* @__PURE__ */ jsx("div", { className: "h-3 w-32 animate-pulse rounded bg-muted" })
|
|
835
1138
|
]
|
|
836
1139
|
}) : item.emptyLabel ? /* @__PURE__ */ jsx("div", {
|
|
837
|
-
className: "px-2 py-1 text-xs text-
|
|
1140
|
+
className: "px-2 py-1 text-xs text-muted-foreground",
|
|
838
1141
|
children: item.emptyLabel
|
|
839
1142
|
}) : null : null,
|
|
840
1143
|
hasOptions && !hasFilteredOptions ? /* @__PURE__ */ jsx("div", {
|
|
841
|
-
className: "px-2 py-1 text-xs text-
|
|
1144
|
+
className: "px-2 py-1 text-xs text-muted-foreground",
|
|
842
1145
|
children: item.search?.emptyLabel ?? item.emptyLabel
|
|
843
1146
|
}) : null,
|
|
844
1147
|
/* @__PURE__ */ jsx("div", {
|
|
845
1148
|
className: "min-h-0 flex-1 overflow-y-auto overscroll-contain",
|
|
846
1149
|
children: filteredGroups.map((group, groupIndex) => /* @__PURE__ */ jsxs("div", {
|
|
847
|
-
className: groupIndex > 0 ? "border-t border-
|
|
1150
|
+
className: groupIndex > 0 ? "border-t border-border pt-1" : void 0,
|
|
848
1151
|
children: [group.label ? /* @__PURE__ */ jsx("div", {
|
|
849
|
-
className: "px-2 py-1 text-[11px] font-semibold uppercase tracking-[0.08em] text-
|
|
1152
|
+
className: "px-2 py-1 text-[11px] font-semibold uppercase tracking-[0.08em] text-muted-foreground",
|
|
850
1153
|
children: group.label
|
|
851
1154
|
}) : null, group.options.map((option) => {
|
|
852
1155
|
const isSelected = item.value === option.value;
|
|
853
1156
|
const isActive = activeValues.has(option.value);
|
|
854
1157
|
const actionLabel = isActive ? action?.activeLabel : action?.inactiveLabel;
|
|
855
1158
|
return /* @__PURE__ */ jsxs("div", {
|
|
856
|
-
className: "group flex items-center gap-1 rounded-md hover:bg-
|
|
1159
|
+
className: "group flex items-center gap-1 rounded-md hover:bg-accent",
|
|
857
1160
|
children: [/* @__PURE__ */ jsxs("button", {
|
|
858
1161
|
type: "button",
|
|
859
1162
|
className: "flex min-w-0 flex-1 items-center gap-2 rounded-md px-2 py-2 text-left",
|
|
@@ -872,7 +1175,7 @@ function ToolbarSearchableSelect({ item }) {
|
|
|
872
1175
|
type: "button",
|
|
873
1176
|
"aria-label": actionLabel,
|
|
874
1177
|
"aria-pressed": isActive,
|
|
875
|
-
className: "mr-1 inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-
|
|
1178
|
+
className: "mr-1 inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-muted-foreground/70 transition-colors hover:bg-card hover:text-amber-500 focus:outline-none focus:ring-1 focus:ring-primary/40",
|
|
876
1179
|
onClick: (event) => {
|
|
877
1180
|
event.stopPropagation();
|
|
878
1181
|
action.onToggle(option.value, !isActive);
|
|
@@ -906,7 +1209,7 @@ function ToolbarSelect({ item }) {
|
|
|
906
1209
|
children: [/* @__PURE__ */ jsx(SelectTrigger, {
|
|
907
1210
|
"aria-label": item.selectedLabel ? `${item.placeholder}: ${item.selectedLabel}` : item.placeholder,
|
|
908
1211
|
title: item.selectedLabel,
|
|
909
|
-
className: `nextclaw-chat-toolbar-select-trigger h-8 w-auto rounded-lg border-0 bg-transparent px-2 text-xs font-medium text-
|
|
1212
|
+
className: `nextclaw-chat-toolbar-select-trigger h-8 w-auto rounded-lg border-0 bg-transparent px-2 text-xs font-medium text-muted-foreground shadow-none hover:bg-accent hover:text-accent-foreground focus:ring-0 sm:px-3 [@container_nextclaw-chat-input-bar_(max-width:440px)]:!basis-8 [@container_nextclaw-chat-input-bar_(max-width:440px)]:!justify-center [@container_nextclaw-chat-input-bar_(max-width:440px)]:!max-w-8 [@container_nextclaw-chat-input-bar_(max-width:440px)]:!min-w-8 [@container_nextclaw-chat-input-bar_(max-width:440px)]:!px-0 ${TRIGGER_WIDTH_BY_KEY[item.key] ?? ""}`,
|
|
910
1213
|
children: item.selectedLabel || item.loading ? /* @__PURE__ */ jsx(ToolbarSelectTriggerContent, { item }) : /* @__PURE__ */ jsx(SelectValue, { placeholder: item.placeholder })
|
|
911
1214
|
}), /* @__PURE__ */ jsxs(SelectContent, {
|
|
912
1215
|
className: CONTENT_WIDTH_BY_KEY[item.key] ?? "",
|
|
@@ -914,12 +1217,12 @@ function ToolbarSelect({ item }) {
|
|
|
914
1217
|
children: [!hasOptions ? item.loading ? /* @__PURE__ */ jsxs("div", {
|
|
915
1218
|
className: "space-y-2 px-3 py-2",
|
|
916
1219
|
children: [
|
|
917
|
-
/* @__PURE__ */ jsx("div", { className: "h-3 w-36 animate-pulse rounded bg-
|
|
918
|
-
/* @__PURE__ */ jsx("div", { className: "h-3 w-28 animate-pulse rounded bg-
|
|
919
|
-
/* @__PURE__ */ jsx("div", { className: "h-3 w-32 animate-pulse rounded bg-
|
|
1220
|
+
/* @__PURE__ */ jsx("div", { className: "h-3 w-36 animate-pulse rounded bg-muted" }),
|
|
1221
|
+
/* @__PURE__ */ jsx("div", { className: "h-3 w-28 animate-pulse rounded bg-muted" }),
|
|
1222
|
+
/* @__PURE__ */ jsx("div", { className: "h-3 w-32 animate-pulse rounded bg-muted" })
|
|
920
1223
|
]
|
|
921
1224
|
}) : item.emptyLabel ? /* @__PURE__ */ jsx("div", {
|
|
922
|
-
className: "px-3 py-2 text-xs text-
|
|
1225
|
+
className: "px-3 py-2 text-xs text-muted-foreground",
|
|
923
1226
|
children: item.emptyLabel
|
|
924
1227
|
}) : null : null, groups.map((group, groupIndex) => /* @__PURE__ */ jsxs("div", { children: [groupIndex > 0 ? /* @__PURE__ */ jsx(SelectSeparator, {}) : null, /* @__PURE__ */ jsxs(SelectGroup, { children: [group.label ? /* @__PURE__ */ jsx(SelectLabel, { children: group.label }) : null, group.options.map((option) => /* @__PURE__ */ jsx(SelectItem, {
|
|
925
1228
|
value: option.value,
|
|
@@ -941,7 +1244,7 @@ function ChatInputBarToolbar({ actions, accessories, selects, skillPicker }) {
|
|
|
941
1244
|
accessories?.map((item) => {
|
|
942
1245
|
const button = /* @__PURE__ */ jsxs("button", {
|
|
943
1246
|
type: "button",
|
|
944
|
-
className: `inline-flex items-center rounded-lg py-1.5 text-xs font-medium text-
|
|
1247
|
+
className: `inline-flex items-center rounded-lg py-1.5 text-xs font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground disabled:cursor-not-allowed disabled:text-muted-foreground/50 ${item.iconOnly ? "h-8 w-8 justify-center px-0" : "gap-1.5 px-3"}`,
|
|
945
1248
|
onClick: item.onClick,
|
|
946
1249
|
disabled: item.disabled,
|
|
947
1250
|
"aria-label": item.label,
|
|
@@ -966,152 +1269,6 @@ function ChatInputBarToolbar({ actions, accessories, selects, skillPicker }) {
|
|
|
966
1269
|
}), /* @__PURE__ */ jsx(ChatInputBarActions, { ...actions })]
|
|
967
1270
|
});
|
|
968
1271
|
}
|
|
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
1272
|
//#endregion
|
|
1116
1273
|
//#region src/components/chat/ui/chat-input-bar/lexical/chat-composer-token-node.tsx
|
|
1117
1274
|
function buildTokenClassName(tokenKind) {
|
|
@@ -1154,35 +1311,15 @@ function buildTokenClassName(tokenKind) {
|
|
|
1154
1311
|
function ChatComposerTokenChip({ label, tokenKind }) {
|
|
1155
1312
|
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
|
|
1156
1313
|
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",
|
|
1314
|
+
children: tokenKind === "file" ? /* @__PURE__ */ jsx(ImageIcon, {
|
|
1164
1315
|
"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",
|
|
1316
|
+
className: "h-3 w-3"
|
|
1317
|
+
}) : tokenKind === "panel_app" ? /* @__PURE__ */ jsx(AppWindow, {
|
|
1178
1318
|
"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
|
-
]
|
|
1319
|
+
className: "h-3 w-3"
|
|
1320
|
+
}) : /* @__PURE__ */ jsx(Puzzle, {
|
|
1321
|
+
"aria-hidden": "true",
|
|
1322
|
+
className: "h-3 w-3"
|
|
1186
1323
|
})
|
|
1187
1324
|
}), /* @__PURE__ */ jsx("span", {
|
|
1188
1325
|
className: tokenKind === "file" ? "min-w-0 flex-1 truncate text-[12px] font-medium text-slate-700" : "truncate",
|
|
@@ -1518,8 +1655,50 @@ function insertToken(params) {
|
|
|
1518
1655
|
}
|
|
1519
1656
|
};
|
|
1520
1657
|
}
|
|
1658
|
+
function insertChatComposerTokenIntoChatComposer(params) {
|
|
1659
|
+
const { label, nodes, selection, tokenKey, tokenKind, triggerSpecs } = params;
|
|
1660
|
+
if (extractChatComposerTokenKeys(nodes, tokenKind).includes(tokenKey)) return {
|
|
1661
|
+
nodes: normalizeChatComposerNodes(nodes),
|
|
1662
|
+
selection
|
|
1663
|
+
};
|
|
1664
|
+
return insertToken({
|
|
1665
|
+
label,
|
|
1666
|
+
nodes,
|
|
1667
|
+
selection,
|
|
1668
|
+
tokenKey,
|
|
1669
|
+
tokenKind,
|
|
1670
|
+
trigger: resolveChatComposerActiveInputSurfaceTrigger(nodes, selection, triggerSpecs ?? [CHAT_INPUT_SURFACE_SLASH_TRIGGER_SPEC])
|
|
1671
|
+
});
|
|
1672
|
+
}
|
|
1673
|
+
function insertInputSurfaceItemIntoChatComposer(params) {
|
|
1674
|
+
const { item, nodes, selection, triggerSpecs } = params;
|
|
1675
|
+
const tokenKind = item.tokenKind ?? (item.value ? "skill" : void 0);
|
|
1676
|
+
const tokenKey = item.tokenKey ?? item.value;
|
|
1677
|
+
if (!tokenKind || !tokenKey) {
|
|
1678
|
+
const trigger = resolveChatComposerActiveInputSurfaceTrigger(nodes, selection, triggerSpecs ?? [CHAT_INPUT_SURFACE_SLASH_TRIGGER_SPEC]);
|
|
1679
|
+
if (trigger) return {
|
|
1680
|
+
nodes: normalizeChatComposerNodes(replaceChatComposerRange(nodes, trigger.start, trigger.end, [])),
|
|
1681
|
+
selection: {
|
|
1682
|
+
start: trigger.start,
|
|
1683
|
+
end: trigger.start
|
|
1684
|
+
}
|
|
1685
|
+
};
|
|
1686
|
+
return {
|
|
1687
|
+
nodes: normalizeChatComposerNodes(nodes),
|
|
1688
|
+
selection
|
|
1689
|
+
};
|
|
1690
|
+
}
|
|
1691
|
+
return insertChatComposerTokenIntoChatComposer({
|
|
1692
|
+
label: item.title,
|
|
1693
|
+
nodes,
|
|
1694
|
+
selection,
|
|
1695
|
+
tokenKey,
|
|
1696
|
+
tokenKind,
|
|
1697
|
+
triggerSpecs
|
|
1698
|
+
});
|
|
1699
|
+
}
|
|
1521
1700
|
function getChatComposerNodesSignature(nodes) {
|
|
1522
|
-
return nodes.map((node) => node.type === "text" ? `text:${node.
|
|
1701
|
+
return nodes.map((node) => node.type === "text" ? `text:${node.text}` : `token:${node.tokenKind}:${node.tokenKey}:${node.label}`).join("");
|
|
1523
1702
|
}
|
|
1524
1703
|
function replaceChatComposerSelectionWithText(params) {
|
|
1525
1704
|
const { nodes, selection, text } = params;
|
|
@@ -1606,21 +1785,9 @@ function deleteChatComposerContent(params) {
|
|
|
1606
1785
|
//#endregion
|
|
1607
1786
|
//#region src/components/chat/ui/chat-input-bar/lexical/chat-composer-lexical-controller.ts
|
|
1608
1787
|
function resolveLexicalComposerKeyboardAction(params) {
|
|
1609
|
-
const {
|
|
1788
|
+
const { canStopGeneration, isComposing, isSending, key, shiftKey } = params;
|
|
1610
1789
|
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
1790
|
if (key === "Escape") {
|
|
1623
|
-
if (isSlashMenuOpen) return { type: "close-slash" };
|
|
1624
1791
|
if (isSending && canStopGeneration) return { type: "stop-generation" };
|
|
1625
1792
|
return { type: "noop" };
|
|
1626
1793
|
}
|
|
@@ -1632,64 +1799,6 @@ function resolveLexicalComposerKeyboardAction(params) {
|
|
|
1632
1799
|
};
|
|
1633
1800
|
return { type: "noop" };
|
|
1634
1801
|
}
|
|
1635
|
-
var LexicalComposerHandleOwner = class {
|
|
1636
|
-
constructor(params) {
|
|
1637
|
-
this.params = params;
|
|
1638
|
-
this.insertSlashItem = (item) => {
|
|
1639
|
-
if (!item.value) return;
|
|
1640
|
-
this.params.onSlashItemSelect?.(item);
|
|
1641
|
-
this.params.publishSnapshot(insertSkillTokenIntoChatComposer({
|
|
1642
|
-
label: item.title,
|
|
1643
|
-
nodes: this.params.optionsReader().nodes,
|
|
1644
|
-
selection: this.params.optionsReader().selection,
|
|
1645
|
-
tokenKey: item.value
|
|
1646
|
-
}), { focusAfterSync: true });
|
|
1647
|
-
};
|
|
1648
|
-
this.insertFileToken = (tokenKey, label) => {
|
|
1649
|
-
this.params.publishSnapshot(insertFileTokenIntoChatComposer({
|
|
1650
|
-
label,
|
|
1651
|
-
nodes: this.params.optionsReader().nodes,
|
|
1652
|
-
selection: this.params.optionsReader().selection,
|
|
1653
|
-
tokenKey
|
|
1654
|
-
}), { focusAfterSync: true });
|
|
1655
|
-
};
|
|
1656
|
-
this.insertFileTokens = (tokens) => {
|
|
1657
|
-
let nextNodes = this.params.optionsReader().nodes;
|
|
1658
|
-
let nextSelection = this.params.optionsReader().selection;
|
|
1659
|
-
for (const token of tokens) {
|
|
1660
|
-
const snapshot = insertFileTokenIntoChatComposer({
|
|
1661
|
-
label: token.label,
|
|
1662
|
-
nodes: nextNodes,
|
|
1663
|
-
selection: nextSelection,
|
|
1664
|
-
tokenKey: token.tokenKey
|
|
1665
|
-
});
|
|
1666
|
-
nextNodes = snapshot.nodes;
|
|
1667
|
-
nextSelection = snapshot.selection;
|
|
1668
|
-
}
|
|
1669
|
-
this.params.publishSnapshot({
|
|
1670
|
-
nodes: nextNodes,
|
|
1671
|
-
selection: nextSelection
|
|
1672
|
-
}, { focusAfterSync: true });
|
|
1673
|
-
};
|
|
1674
|
-
this.focusComposer = () => {
|
|
1675
|
-
this.params.focusComposer();
|
|
1676
|
-
};
|
|
1677
|
-
this.focusComposerAtEnd = (nodes) => {
|
|
1678
|
-
this.params.focusComposerAtEnd(nodes);
|
|
1679
|
-
};
|
|
1680
|
-
this.syncSelectedSkills = (nextKeys, options) => {
|
|
1681
|
-
this.params.publishSnapshot(syncSelectedSkillsIntoChatComposer({
|
|
1682
|
-
nextKeys,
|
|
1683
|
-
nodes: this.params.optionsReader().nodes,
|
|
1684
|
-
options,
|
|
1685
|
-
selection: this.params.optionsReader().selection
|
|
1686
|
-
}), { focusAfterSync: true });
|
|
1687
|
-
};
|
|
1688
|
-
}
|
|
1689
|
-
};
|
|
1690
|
-
function createLexicalComposerHandle(params) {
|
|
1691
|
-
return new LexicalComposerHandleOwner(params);
|
|
1692
|
-
}
|
|
1693
1802
|
function getChatComposerContentSignature(nodes) {
|
|
1694
1803
|
return JSON.stringify(nodes.map((node) => node.type === "text" ? {
|
|
1695
1804
|
text: node.text,
|
|
@@ -1711,50 +1820,39 @@ function handleLexicalComposerBeforeInput(params) {
|
|
|
1711
1820
|
nodes: snapshotReader().nodes,
|
|
1712
1821
|
selection: snapshotReader().selection,
|
|
1713
1822
|
text: nativeEvent.data
|
|
1714
|
-
})
|
|
1823
|
+
}), { inputSurfaceReason: {
|
|
1824
|
+
type: "insert-text",
|
|
1825
|
+
text: nativeEvent.data
|
|
1826
|
+
} });
|
|
1715
1827
|
}
|
|
1716
1828
|
function handleLexicalComposerCompositionEnd(params) {
|
|
1717
|
-
const { data, fallbackSnapshot, publishSnapshot, snapshotReader } = params;
|
|
1829
|
+
const { compositionStartSnapshot, data, fallbackSnapshot, publishSnapshot, snapshotReader } = params;
|
|
1718
1830
|
const currentSnapshot = snapshotReader();
|
|
1719
1831
|
const editorSnapshot = fallbackSnapshot();
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1832
|
+
const baseSnapshot = compositionStartSnapshot ?? currentSnapshot;
|
|
1833
|
+
publishSnapshot(getChatComposerContentSignature(editorSnapshot.nodes) !== getChatComposerContentSignature(baseSnapshot.nodes) ? editorSnapshot : data.length > 0 ? replaceChatComposerSelectionWithText({
|
|
1834
|
+
nodes: baseSnapshot.nodes,
|
|
1835
|
+
selection: baseSnapshot.selection,
|
|
1723
1836
|
text: data
|
|
1724
|
-
}) : editorSnapshot, {
|
|
1837
|
+
}) : editorSnapshot, {
|
|
1838
|
+
forcePublish: true,
|
|
1839
|
+
inputSurfaceReason: {
|
|
1840
|
+
type: "insert-text",
|
|
1841
|
+
text: data
|
|
1842
|
+
}
|
|
1843
|
+
});
|
|
1725
1844
|
}
|
|
1726
1845
|
function handleLexicalComposerKeyboardCommand(params) {
|
|
1727
|
-
const { actions,
|
|
1846
|
+
const { actions, nativeEvent, publishSnapshot, snapshot } = params;
|
|
1728
1847
|
const action = resolveLexicalComposerKeyboardAction({
|
|
1729
|
-
activeSlashIndex,
|
|
1730
1848
|
canStopGeneration: actions.canStopGeneration,
|
|
1731
1849
|
isComposing: nativeEvent.isComposing,
|
|
1732
1850
|
isSending: actions.isSending,
|
|
1733
|
-
isSlashMenuOpen: resolveChatComposerSlashTrigger(snapshot.nodes, snapshot.selection) !== null,
|
|
1734
1851
|
key: nativeEvent.key,
|
|
1735
|
-
shiftKey: nativeEvent.shiftKey
|
|
1736
|
-
slashItemCount: slashItems.length
|
|
1852
|
+
shiftKey: nativeEvent.shiftKey
|
|
1737
1853
|
});
|
|
1738
|
-
const activeSlashItem = slashItems[activeSlashIndex] ?? null;
|
|
1739
1854
|
if (action.type !== "noop") nativeEvent.preventDefault();
|
|
1740
1855
|
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
1856
|
case "consume": return true;
|
|
1759
1857
|
case "stop-generation":
|
|
1760
1858
|
actions.onStop();
|
|
@@ -1764,7 +1862,10 @@ function handleLexicalComposerKeyboardCommand(params) {
|
|
|
1764
1862
|
nodes: snapshot.nodes,
|
|
1765
1863
|
selection: snapshot.selection,
|
|
1766
1864
|
text: "\n"
|
|
1767
|
-
})
|
|
1865
|
+
}), { inputSurfaceReason: {
|
|
1866
|
+
type: "insert-text",
|
|
1867
|
+
text: "\n"
|
|
1868
|
+
} });
|
|
1768
1869
|
return true;
|
|
1769
1870
|
case "send-message":
|
|
1770
1871
|
actions.onSend();
|
|
@@ -1774,188 +1875,320 @@ function handleLexicalComposerKeyboardCommand(params) {
|
|
|
1774
1875
|
direction: action.direction,
|
|
1775
1876
|
nodes: snapshot.nodes,
|
|
1776
1877
|
selection: snapshot.selection
|
|
1777
|
-
}));
|
|
1878
|
+
}), { inputSurfaceReason: { type: "delete-content" } });
|
|
1778
1879
|
return true;
|
|
1779
1880
|
case "noop": return false;
|
|
1780
1881
|
}
|
|
1781
1882
|
}
|
|
1782
1883
|
//#endregion
|
|
1884
|
+
//#region src/components/chat/ui/chat-input-bar/lexical/owners/chat-composer-lexical-owner.ts
|
|
1885
|
+
function createMutableRef(value) {
|
|
1886
|
+
return { current: value };
|
|
1887
|
+
}
|
|
1888
|
+
function getChatComposerDocumentLength(nodes) {
|
|
1889
|
+
return nodes.reduce((cursor, node) => cursor + (node.type === "text" ? node.text.length : 1), 0);
|
|
1890
|
+
}
|
|
1891
|
+
var ChatComposerLexicalOwner = class {
|
|
1892
|
+
constructor() {
|
|
1893
|
+
this.isApplyingExternalUpdateRef = createMutableRef(false);
|
|
1894
|
+
this.isComposingRef = createMutableRef(false);
|
|
1895
|
+
this.pendingOwnerSignatureRef = createMutableRef(null);
|
|
1896
|
+
this.pendingSelectionRef = createMutableRef(null);
|
|
1897
|
+
this.selectionRef = createMutableRef(null);
|
|
1898
|
+
this.shouldFocusAfterSyncRef = createMutableRef(false);
|
|
1899
|
+
this.compositionStartSnapshotRef = createMutableRef(null);
|
|
1900
|
+
this.editorSignatureRef = createMutableRef("");
|
|
1901
|
+
this.lastPublishedSignatureRef = createMutableRef("");
|
|
1902
|
+
this.pendingInputSurfaceReasonRef = createMutableRef(null);
|
|
1903
|
+
this.editor = null;
|
|
1904
|
+
this.runtime = null;
|
|
1905
|
+
this.configureRuntime = (runtime) => {
|
|
1906
|
+
this.runtime = runtime;
|
|
1907
|
+
};
|
|
1908
|
+
this.bindEditor = (editor) => {
|
|
1909
|
+
this.editor = editor;
|
|
1910
|
+
return () => {
|
|
1911
|
+
if (this.editor === editor) this.editor = null;
|
|
1912
|
+
};
|
|
1913
|
+
};
|
|
1914
|
+
this.registerEditorListeners = (editor) => {
|
|
1915
|
+
return mergeRegister(editor.registerUpdateListener(({ editorState }) => {
|
|
1916
|
+
const runtime = this.getRuntime();
|
|
1917
|
+
this.handleEditorUpdate(editorState, runtime.callbacks);
|
|
1918
|
+
}), editor.registerCommand(SELECTION_CHANGE_COMMAND, () => {
|
|
1919
|
+
const runtime = this.getRuntime();
|
|
1920
|
+
this.handleSelectionChange(editor, runtime.callbacks);
|
|
1921
|
+
return false;
|
|
1922
|
+
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(BLUR_COMMAND, () => {
|
|
1923
|
+
this.getRuntime().callbacks.onInputSurfaceOpenChange?.(false);
|
|
1924
|
+
return false;
|
|
1925
|
+
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(KEY_DOWN_COMMAND, (event) => {
|
|
1926
|
+
const runtime = this.getRuntime();
|
|
1927
|
+
return this.handleKeyDown({
|
|
1928
|
+
actions: runtime.actions,
|
|
1929
|
+
callbacks: runtime.callbacks,
|
|
1930
|
+
event,
|
|
1931
|
+
fallbackNodes: runtime.fallbackNodes
|
|
1932
|
+
});
|
|
1933
|
+
}, COMMAND_PRIORITY_HIGH));
|
|
1934
|
+
};
|
|
1935
|
+
this.syncExternalState = (editor, nodes) => {
|
|
1936
|
+
if (this.isComposingRef.current) return;
|
|
1937
|
+
const nextSignature = getChatComposerNodesSignature(nodes);
|
|
1938
|
+
const pendingSelection = this.pendingSelectionRef.current;
|
|
1939
|
+
const pendingOwnerSignature = this.pendingOwnerSignatureRef.current;
|
|
1940
|
+
if (pendingOwnerSignature) {
|
|
1941
|
+
if (nextSignature === pendingOwnerSignature) this.pendingOwnerSignatureRef.current = null;
|
|
1942
|
+
else if (nextSignature !== this.editorSignatureRef.current) return;
|
|
1943
|
+
}
|
|
1944
|
+
const shouldSyncDocument = nextSignature !== this.editorSignatureRef.current;
|
|
1945
|
+
if (!shouldSyncDocument && !pendingSelection) return;
|
|
1946
|
+
this.startApplyingExternalUpdate();
|
|
1947
|
+
if (shouldSyncDocument) {
|
|
1948
|
+
syncLexicalEditorFromChatComposerState(editor, nodes, pendingSelection);
|
|
1949
|
+
this.editorSignatureRef.current = nextSignature;
|
|
1950
|
+
this.lastPublishedSignatureRef.current = nextSignature;
|
|
1951
|
+
} else if (pendingSelection) syncLexicalSelectionFromChatComposerSelection(editor, pendingSelection);
|
|
1952
|
+
if (pendingSelection) {
|
|
1953
|
+
this.selectionRef.current = pendingSelection;
|
|
1954
|
+
this.pendingSelectionRef.current = null;
|
|
1955
|
+
}
|
|
1956
|
+
if (this.shouldFocusAfterSyncRef.current) {
|
|
1957
|
+
this.shouldFocusAfterSyncRef.current = false;
|
|
1958
|
+
const targetSelection = this.selectionRef.current;
|
|
1959
|
+
editor.focus(() => {
|
|
1960
|
+
if (targetSelection) syncLexicalSelectionFromChatComposerSelection(editor, targetSelection);
|
|
1961
|
+
});
|
|
1962
|
+
}
|
|
1963
|
+
};
|
|
1964
|
+
this.publishSnapshot = (snapshot, callbacks, options) => {
|
|
1965
|
+
this.selectionRef.current = snapshot.selection;
|
|
1966
|
+
this.pendingSelectionRef.current = snapshot.selection;
|
|
1967
|
+
if (options?.focusAfterSync) this.shouldFocusAfterSyncRef.current = true;
|
|
1968
|
+
const signature = getChatComposerNodesSignature(snapshot.nodes);
|
|
1969
|
+
const { editor } = this;
|
|
1970
|
+
this.pendingOwnerSignatureRef.current = signature;
|
|
1971
|
+
if (editor) {
|
|
1972
|
+
this.startApplyingExternalUpdate();
|
|
1973
|
+
syncLexicalEditorFromChatComposerState(editor, snapshot.nodes, snapshot.selection);
|
|
1974
|
+
this.editorSignatureRef.current = signature;
|
|
1975
|
+
}
|
|
1976
|
+
callbacks.onInputSurfaceSnapshotChange?.(snapshot.nodes, snapshot.selection, options?.inputSurfaceReason ?? { type: "programmatic" });
|
|
1977
|
+
if (options?.forcePublish || signature !== this.lastPublishedSignatureRef.current) {
|
|
1978
|
+
this.lastPublishedSignatureRef.current = signature;
|
|
1979
|
+
callbacks.onNodesChange(snapshot.nodes);
|
|
1980
|
+
}
|
|
1981
|
+
};
|
|
1982
|
+
this.focusComposer = () => {
|
|
1983
|
+
const { editor } = this;
|
|
1984
|
+
if (!editor) return;
|
|
1985
|
+
editor.getRootElement()?.focus({ preventScroll: true });
|
|
1986
|
+
const targetSelection = this.selectionRef.current;
|
|
1987
|
+
if (targetSelection) syncLexicalSelectionFromChatComposerSelection(editor, targetSelection);
|
|
1988
|
+
editor.focus();
|
|
1989
|
+
};
|
|
1990
|
+
this.focusComposerAtEnd = (nodes) => {
|
|
1991
|
+
const { editor } = this;
|
|
1992
|
+
if (!editor) return;
|
|
1993
|
+
const end = getChatComposerDocumentLength(nodes ?? readChatComposerSnapshotFromEditorState(editor.getEditorState()).nodes);
|
|
1994
|
+
const targetSelection = {
|
|
1995
|
+
start: end,
|
|
1996
|
+
end
|
|
1997
|
+
};
|
|
1998
|
+
this.selectionRef.current = targetSelection;
|
|
1999
|
+
this.pendingSelectionRef.current = targetSelection;
|
|
2000
|
+
editor.getRootElement()?.focus({ preventScroll: true });
|
|
2001
|
+
if (nodes) {
|
|
2002
|
+
this.startApplyingExternalUpdate();
|
|
2003
|
+
const signature = getChatComposerNodesSignature(nodes);
|
|
2004
|
+
syncLexicalEditorFromChatComposerState(editor, nodes, targetSelection);
|
|
2005
|
+
this.editorSignatureRef.current = signature;
|
|
2006
|
+
this.lastPublishedSignatureRef.current = signature;
|
|
2007
|
+
} else syncLexicalSelectionFromChatComposerSelection(editor, targetSelection);
|
|
2008
|
+
editor.focus(() => {
|
|
2009
|
+
syncLexicalSelectionFromChatComposerSelection(editor, targetSelection);
|
|
2010
|
+
});
|
|
2011
|
+
};
|
|
2012
|
+
this.readComposerSnapshot = (fallbackNodes) => {
|
|
2013
|
+
const { editor } = this;
|
|
2014
|
+
if (!editor) return {
|
|
2015
|
+
nodes: fallbackNodes,
|
|
2016
|
+
selection: this.selectionRef.current
|
|
2017
|
+
};
|
|
2018
|
+
const snapshot = readChatComposerSnapshotFromEditorState(editor.getEditorState());
|
|
2019
|
+
this.selectionRef.current = snapshot.selection;
|
|
2020
|
+
return snapshot;
|
|
2021
|
+
};
|
|
2022
|
+
this.createHandle = () => {
|
|
2023
|
+
const insertInputSurfaceItem = (item, triggerSpecs = [CHAT_INPUT_SURFACE_SLASH_TRIGGER_SPEC]) => {
|
|
2024
|
+
const { callbacks } = this.getRuntime();
|
|
2025
|
+
callbacks.onInputSurfaceItemSelect?.(item);
|
|
2026
|
+
this.publishRuntimeSnapshot((snapshot) => insertInputSurfaceItemIntoChatComposer({
|
|
2027
|
+
item,
|
|
2028
|
+
nodes: snapshot.nodes,
|
|
2029
|
+
selection: snapshot.selection,
|
|
2030
|
+
triggerSpecs
|
|
2031
|
+
}), { focusAfterSync: true });
|
|
2032
|
+
};
|
|
2033
|
+
return {
|
|
2034
|
+
insertInputSurfaceItem,
|
|
2035
|
+
insertSlashItem: (item) => {
|
|
2036
|
+
insertInputSurfaceItem(item);
|
|
2037
|
+
},
|
|
2038
|
+
insertFileToken: (tokenKey, label) => {
|
|
2039
|
+
this.publishRuntimeSnapshot((snapshot) => insertFileTokenIntoChatComposer({
|
|
2040
|
+
label,
|
|
2041
|
+
nodes: snapshot.nodes,
|
|
2042
|
+
selection: snapshot.selection,
|
|
2043
|
+
tokenKey
|
|
2044
|
+
}), { focusAfterSync: true });
|
|
2045
|
+
},
|
|
2046
|
+
insertFileTokens: (tokens) => {
|
|
2047
|
+
this.publishRuntimeSnapshot((snapshot) => tokens.reduce((nextSnapshot, token) => insertFileTokenIntoChatComposer({
|
|
2048
|
+
label: token.label,
|
|
2049
|
+
nodes: nextSnapshot.nodes,
|
|
2050
|
+
selection: nextSnapshot.selection,
|
|
2051
|
+
tokenKey: token.tokenKey
|
|
2052
|
+
}), snapshot), { focusAfterSync: true });
|
|
2053
|
+
},
|
|
2054
|
+
focusComposer: this.focusComposer,
|
|
2055
|
+
focusComposerAtEnd: this.focusComposerAtEnd,
|
|
2056
|
+
syncSelectedSkills: (nextKeys, options) => {
|
|
2057
|
+
this.publishRuntimeSnapshot((snapshot) => syncSelectedSkillsIntoChatComposer({
|
|
2058
|
+
nextKeys,
|
|
2059
|
+
nodes: snapshot.nodes,
|
|
2060
|
+
options,
|
|
2061
|
+
selection: snapshot.selection
|
|
2062
|
+
}), { focusAfterSync: true });
|
|
2063
|
+
}
|
|
2064
|
+
};
|
|
2065
|
+
};
|
|
2066
|
+
this.handleBeforeInput = (params) => {
|
|
2067
|
+
const { disabled, event } = params;
|
|
2068
|
+
const { callbacks, fallbackNodes } = this.getRuntime();
|
|
2069
|
+
handleLexicalComposerBeforeInput({
|
|
2070
|
+
disabled,
|
|
2071
|
+
event,
|
|
2072
|
+
isComposing: this.isComposingRef.current,
|
|
2073
|
+
publishSnapshot: (snapshot, options) => this.publishSnapshot(snapshot, callbacks, options),
|
|
2074
|
+
snapshotReader: () => this.readComposerSnapshot(fallbackNodes)
|
|
2075
|
+
});
|
|
2076
|
+
};
|
|
2077
|
+
this.handleCompositionStart = () => {
|
|
2078
|
+
const { fallbackNodes } = this.getRuntime();
|
|
2079
|
+
this.compositionStartSnapshotRef.current = this.readComposerSnapshot(fallbackNodes);
|
|
2080
|
+
this.isComposingRef.current = true;
|
|
2081
|
+
};
|
|
2082
|
+
this.handleCompositionEnd = (params) => {
|
|
2083
|
+
const { data } = params;
|
|
2084
|
+
const { callbacks, fallbackNodes } = this.getRuntime();
|
|
2085
|
+
this.isComposingRef.current = false;
|
|
2086
|
+
handleLexicalComposerCompositionEnd({
|
|
2087
|
+
compositionStartSnapshot: this.compositionStartSnapshotRef.current,
|
|
2088
|
+
data,
|
|
2089
|
+
fallbackSnapshot: () => this.readComposerSnapshot(fallbackNodes),
|
|
2090
|
+
publishSnapshot: (snapshot, options) => this.publishSnapshot(snapshot, callbacks, options),
|
|
2091
|
+
snapshotReader: () => this.readComposerSnapshot(fallbackNodes)
|
|
2092
|
+
});
|
|
2093
|
+
this.compositionStartSnapshotRef.current = null;
|
|
2094
|
+
};
|
|
2095
|
+
this.handleKeyDown = (params) => {
|
|
2096
|
+
const { actions, callbacks, event, fallbackNodes } = params;
|
|
2097
|
+
if (event.key.length === 1 && !event.isComposing && !event.altKey && !event.ctrlKey && !event.metaKey) this.pendingInputSurfaceReasonRef.current = {
|
|
2098
|
+
type: "insert-text",
|
|
2099
|
+
text: event.key
|
|
2100
|
+
};
|
|
2101
|
+
const snapshot = this.readComposerSnapshot(fallbackNodes);
|
|
2102
|
+
if (callbacks.onInputSurfaceKeyDown?.(event)) return true;
|
|
2103
|
+
return handleLexicalComposerKeyboardCommand({
|
|
2104
|
+
actions,
|
|
2105
|
+
nativeEvent: event,
|
|
2106
|
+
publishSnapshot: (nextSnapshot, options) => this.publishSnapshot(nextSnapshot, callbacks, options),
|
|
2107
|
+
snapshot
|
|
2108
|
+
});
|
|
2109
|
+
};
|
|
2110
|
+
this.handleEditorUpdate = (editorState, callbacks) => {
|
|
2111
|
+
const snapshot = readChatComposerSnapshotFromEditorState(editorState);
|
|
2112
|
+
const signature = getChatComposerNodesSignature(snapshot.nodes);
|
|
2113
|
+
this.editorSignatureRef.current = signature;
|
|
2114
|
+
if (this.isApplyingExternalUpdateRef.current || this.isComposingRef.current) return;
|
|
2115
|
+
this.selectionRef.current = snapshot.selection;
|
|
2116
|
+
callbacks.onInputSurfaceSnapshotChange?.(snapshot.nodes, snapshot.selection, this.consumeInputSurfaceReason() ?? { type: "sync" });
|
|
2117
|
+
if (signature === this.lastPublishedSignatureRef.current) return;
|
|
2118
|
+
this.lastPublishedSignatureRef.current = signature;
|
|
2119
|
+
callbacks.onNodesChange(snapshot.nodes);
|
|
2120
|
+
};
|
|
2121
|
+
this.handleSelectionChange = (editor, callbacks) => {
|
|
2122
|
+
const snapshot = readChatComposerSnapshotFromEditorState(editor.getEditorState());
|
|
2123
|
+
if (this.isComposingRef.current) return;
|
|
2124
|
+
this.selectionRef.current = snapshot.selection;
|
|
2125
|
+
callbacks.onInputSurfaceSnapshotChange?.(snapshot.nodes, snapshot.selection, { type: "selection" });
|
|
2126
|
+
};
|
|
2127
|
+
this.consumeInputSurfaceReason = () => {
|
|
2128
|
+
const reason = this.pendingInputSurfaceReasonRef.current;
|
|
2129
|
+
this.pendingInputSurfaceReasonRef.current = null;
|
|
2130
|
+
return reason;
|
|
2131
|
+
};
|
|
2132
|
+
this.publishRuntimeSnapshot = (createSnapshot, options) => {
|
|
2133
|
+
const { callbacks, fallbackNodes } = this.getRuntime();
|
|
2134
|
+
this.publishSnapshot(createSnapshot(this.readComposerSnapshot(fallbackNodes)), callbacks, options);
|
|
2135
|
+
};
|
|
2136
|
+
this.startApplyingExternalUpdate = () => {
|
|
2137
|
+
this.isApplyingExternalUpdateRef.current = true;
|
|
2138
|
+
requestAnimationFrame(() => {
|
|
2139
|
+
this.isApplyingExternalUpdateRef.current = false;
|
|
2140
|
+
});
|
|
2141
|
+
};
|
|
2142
|
+
this.getRuntime = () => {
|
|
2143
|
+
if (!this.runtime) throw new Error("ChatComposerLexicalOwner runtime has not been configured.");
|
|
2144
|
+
return this.runtime;
|
|
2145
|
+
};
|
|
2146
|
+
}
|
|
2147
|
+
};
|
|
2148
|
+
//#endregion
|
|
1783
2149
|
//#region src/components/chat/ui/chat-input-bar/lexical/chat-composer-plugins.tsx
|
|
1784
|
-
function ChatComposerBindingsPlugin({ disabled,
|
|
2150
|
+
function ChatComposerBindingsPlugin({ disabled, nodes, owner }) {
|
|
1785
2151
|
const [editor] = useLexicalComposerContext();
|
|
1786
2152
|
useLayoutEffect(() => {
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
if (editorRef.current === editor) editorRef.current = null;
|
|
1790
|
-
};
|
|
1791
|
-
}, [editor, editorRef]);
|
|
2153
|
+
return owner.bindEditor(editor);
|
|
2154
|
+
}, [editor, owner]);
|
|
1792
2155
|
useLayoutEffect(() => {
|
|
1793
2156
|
editor.setEditable(!disabled);
|
|
1794
|
-
|
|
1795
|
-
useLayoutEffect(() => {
|
|
1796
|
-
const nextSignature = getChatComposerNodesSignature(nodes);
|
|
1797
|
-
const pendingSelection = pendingSelectionRef.current;
|
|
1798
|
-
const shouldSyncDocument = nextSignature !== editorSignatureRef.current;
|
|
1799
|
-
if (!shouldSyncDocument && !pendingSelection) return;
|
|
1800
|
-
isApplyingExternalUpdateRef.current = true;
|
|
1801
|
-
if (shouldSyncDocument) {
|
|
1802
|
-
syncLexicalEditorFromChatComposerState(editor, nodes, pendingSelection);
|
|
1803
|
-
editorSignatureRef.current = nextSignature;
|
|
1804
|
-
lastPublishedSignatureRef.current = nextSignature;
|
|
1805
|
-
} else if (pendingSelection) syncLexicalSelectionFromChatComposerSelection(editor, pendingSelection);
|
|
1806
|
-
if (pendingSelection) {
|
|
1807
|
-
selectionRef.current = pendingSelection;
|
|
1808
|
-
pendingSelectionRef.current = null;
|
|
1809
|
-
}
|
|
1810
|
-
if (shouldFocusAfterSyncRef.current) {
|
|
1811
|
-
shouldFocusAfterSyncRef.current = false;
|
|
1812
|
-
const targetSelection = selectionRef.current;
|
|
1813
|
-
editor.focus(() => {
|
|
1814
|
-
if (targetSelection) syncLexicalSelectionFromChatComposerSelection(editor, targetSelection);
|
|
1815
|
-
});
|
|
1816
|
-
}
|
|
1817
|
-
requestAnimationFrame(() => {
|
|
1818
|
-
isApplyingExternalUpdateRef.current = false;
|
|
1819
|
-
});
|
|
2157
|
+
owner.syncExternalState(editor, nodes);
|
|
1820
2158
|
}, [
|
|
2159
|
+
disabled,
|
|
1821
2160
|
editor,
|
|
1822
|
-
editorSignatureRef,
|
|
1823
|
-
isApplyingExternalUpdateRef,
|
|
1824
|
-
lastPublishedSignatureRef,
|
|
1825
2161
|
nodes,
|
|
1826
|
-
|
|
1827
|
-
selectionRef,
|
|
1828
|
-
shouldFocusAfterSyncRef
|
|
2162
|
+
owner
|
|
1829
2163
|
]);
|
|
1830
2164
|
useEffect(() => {
|
|
1831
|
-
return
|
|
1832
|
-
|
|
1833
|
-
const signature = getChatComposerNodesSignature(snapshot.nodes);
|
|
1834
|
-
selectionRef.current = snapshot.selection;
|
|
1835
|
-
editorSignatureRef.current = signature;
|
|
1836
|
-
syncSlashState(snapshot.nodes, snapshot.selection);
|
|
1837
|
-
if (isApplyingExternalUpdateRef.current || isComposingRef.current) return;
|
|
1838
|
-
if (signature === lastPublishedSignatureRef.current) return;
|
|
1839
|
-
lastPublishedSignatureRef.current = signature;
|
|
1840
|
-
onNodesChange(snapshot.nodes);
|
|
1841
|
-
}), editor.registerCommand(SELECTION_CHANGE_COMMAND, () => {
|
|
1842
|
-
const snapshot = readChatComposerSnapshotFromEditorState(editor.getEditorState());
|
|
1843
|
-
selectionRef.current = snapshot.selection;
|
|
1844
|
-
if (!isComposingRef.current) syncSlashState(snapshot.nodes, snapshot.selection);
|
|
1845
|
-
return false;
|
|
1846
|
-
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(BLUR_COMMAND, () => {
|
|
1847
|
-
onBlur();
|
|
1848
|
-
return false;
|
|
1849
|
-
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(KEY_DOWN_COMMAND, (event) => onKeyDown(event), COMMAND_PRIORITY_HIGH));
|
|
1850
|
-
}, [
|
|
1851
|
-
editor,
|
|
1852
|
-
editorSignatureRef,
|
|
1853
|
-
isApplyingExternalUpdateRef,
|
|
1854
|
-
isComposingRef,
|
|
1855
|
-
lastPublishedSignatureRef,
|
|
1856
|
-
onBlur,
|
|
1857
|
-
onKeyDown,
|
|
1858
|
-
onNodesChange,
|
|
1859
|
-
selectionRef,
|
|
1860
|
-
syncSlashState
|
|
1861
|
-
]);
|
|
2165
|
+
return owner.registerEditorListeners(editor);
|
|
2166
|
+
}, [editor, owner]);
|
|
1862
2167
|
return null;
|
|
1863
2168
|
}
|
|
1864
2169
|
//#endregion
|
|
1865
2170
|
//#region src/components/chat/ui/chat-input-bar/lexical/chat-input-bar-tokenized-composer.tsx
|
|
1866
|
-
function
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
const isComposingRef = useRef(false);
|
|
1875
|
-
const isApplyingExternalUpdateRef = useRef(false);
|
|
1876
|
-
const editorSignatureRef = useRef("");
|
|
1877
|
-
const lastPublishedSignatureRef = useRef("");
|
|
1878
|
-
const syncSlashState = useCallback((nodes, selection) => {
|
|
1879
|
-
const trigger = resolveChatComposerSlashTrigger(nodes, selection);
|
|
1880
|
-
onSlashTriggerChange?.(trigger);
|
|
1881
|
-
onSlashQueryChange?.(trigger?.query ?? null);
|
|
1882
|
-
onSlashOpenChange(trigger !== null);
|
|
1883
|
-
}, [
|
|
1884
|
-
onSlashOpenChange,
|
|
1885
|
-
onSlashQueryChange,
|
|
1886
|
-
onSlashTriggerChange
|
|
1887
|
-
]);
|
|
1888
|
-
const readCurrentNodes = useCallback(() => {
|
|
1889
|
-
return nodes;
|
|
1890
|
-
}, [nodes]);
|
|
1891
|
-
const readCurrentSelection = useCallback(() => {
|
|
1892
|
-
if (selectionRef.current) return selectionRef.current;
|
|
1893
|
-
if (!editorRef.current) return null;
|
|
1894
|
-
const snapshot = readChatComposerSnapshotFromEditorState(editorRef.current.getEditorState());
|
|
1895
|
-
selectionRef.current = snapshot.selection;
|
|
1896
|
-
return snapshot.selection;
|
|
1897
|
-
}, []);
|
|
1898
|
-
const publishSnapshot = useCallback((snapshot, options) => {
|
|
1899
|
-
selectionRef.current = snapshot.selection;
|
|
1900
|
-
pendingSelectionRef.current = snapshot.selection;
|
|
1901
|
-
if (options?.focusAfterSync) shouldFocusAfterSyncRef.current = true;
|
|
1902
|
-
const signature = getChatComposerNodesSignature(snapshot.nodes);
|
|
1903
|
-
syncSlashState(snapshot.nodes, snapshot.selection);
|
|
1904
|
-
if (options?.forcePublish || signature !== lastPublishedSignatureRef.current) {
|
|
1905
|
-
lastPublishedSignatureRef.current = signature;
|
|
1906
|
-
onNodesChange(snapshot.nodes);
|
|
1907
|
-
}
|
|
1908
|
-
}, [onNodesChange, syncSlashState]);
|
|
1909
|
-
const focusComposer = useCallback(() => {
|
|
1910
|
-
const editor = editorRef.current;
|
|
1911
|
-
if (!editor) return;
|
|
1912
|
-
editor.getRootElement()?.focus({ preventScroll: true });
|
|
1913
|
-
const targetSelection = selectionRef.current;
|
|
1914
|
-
if (targetSelection) syncLexicalSelectionFromChatComposerSelection(editor, targetSelection);
|
|
1915
|
-
editor.focus();
|
|
1916
|
-
}, []);
|
|
1917
|
-
const focusComposerAtEnd = useCallback((nodes) => {
|
|
1918
|
-
const editor = editorRef.current;
|
|
1919
|
-
if (!editor) return;
|
|
1920
|
-
const end = getChatComposerDocumentLength(nodes ?? readChatComposerSnapshotFromEditorState(editor.getEditorState()).nodes);
|
|
1921
|
-
const targetSelection = {
|
|
1922
|
-
start: end,
|
|
1923
|
-
end
|
|
1924
|
-
};
|
|
1925
|
-
selectionRef.current = targetSelection;
|
|
1926
|
-
pendingSelectionRef.current = targetSelection;
|
|
1927
|
-
editor.getRootElement()?.focus({ preventScroll: true });
|
|
1928
|
-
if (nodes) {
|
|
1929
|
-
isApplyingExternalUpdateRef.current = true;
|
|
1930
|
-
const signature = getChatComposerNodesSignature(nodes);
|
|
1931
|
-
syncLexicalEditorFromChatComposerState(editor, nodes, targetSelection);
|
|
1932
|
-
editorSignatureRef.current = signature;
|
|
1933
|
-
lastPublishedSignatureRef.current = signature;
|
|
1934
|
-
requestAnimationFrame(() => {
|
|
1935
|
-
isApplyingExternalUpdateRef.current = false;
|
|
1936
|
-
});
|
|
1937
|
-
} else syncLexicalSelectionFromChatComposerSelection(editor, targetSelection);
|
|
1938
|
-
editor.focus(() => {
|
|
1939
|
-
syncLexicalSelectionFromChatComposerSelection(editor, targetSelection);
|
|
1940
|
-
});
|
|
1941
|
-
}, []);
|
|
1942
|
-
const readComposerSnapshot = useCallback(() => ({
|
|
1943
|
-
nodes: readCurrentNodes(),
|
|
1944
|
-
selection: readCurrentSelection()
|
|
1945
|
-
}), [readCurrentNodes, readCurrentSelection]);
|
|
1946
|
-
useImperativeHandle(ref, () => createLexicalComposerHandle({
|
|
1947
|
-
focusComposer,
|
|
1948
|
-
focusComposerAtEnd,
|
|
1949
|
-
onSlashItemSelect,
|
|
1950
|
-
optionsReader: readComposerSnapshot,
|
|
1951
|
-
publishSnapshot
|
|
2171
|
+
const ChatInputBarTokenizedComposer = forwardRef(function ChatInputBarTokenizedComposer({ actions, disabled, nodes, onFilesAdd, onInputSurfaceItemSelect, onInputSurfaceKeyDown, onInputSurfaceOpenChange, onInputSurfaceSnapshotChange, onNodesChange, placeholder }, ref) {
|
|
2172
|
+
const [owner] = useState(() => new ChatComposerLexicalOwner());
|
|
2173
|
+
const ownerCallbacks = useMemo(() => ({
|
|
2174
|
+
onInputSurfaceItemSelect,
|
|
2175
|
+
onInputSurfaceKeyDown,
|
|
2176
|
+
onInputSurfaceOpenChange,
|
|
2177
|
+
onInputSurfaceSnapshotChange,
|
|
2178
|
+
onNodesChange
|
|
1952
2179
|
}), [
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
2180
|
+
onInputSurfaceItemSelect,
|
|
2181
|
+
onInputSurfaceKeyDown,
|
|
2182
|
+
onInputSurfaceOpenChange,
|
|
2183
|
+
onInputSurfaceSnapshotChange,
|
|
2184
|
+
onNodesChange
|
|
1958
2185
|
]);
|
|
2186
|
+
owner.configureRuntime({
|
|
2187
|
+
actions,
|
|
2188
|
+
callbacks: ownerCallbacks,
|
|
2189
|
+
fallbackNodes: nodes
|
|
2190
|
+
});
|
|
2191
|
+
useImperativeHandle(ref, () => owner.createHandle(), [owner]);
|
|
1959
2192
|
return /* @__PURE__ */ jsxs(LexicalComposer, {
|
|
1960
2193
|
initialConfig: useMemo(() => ({
|
|
1961
2194
|
editable: !disabled,
|
|
@@ -1969,93 +2202,46 @@ const ChatInputBarTokenizedComposer = forwardRef(function ChatInputBarTokenizedC
|
|
|
1969
2202
|
},
|
|
1970
2203
|
theme: {}
|
|
1971
2204
|
}), [disabled, nodes]),
|
|
1972
|
-
children: [
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
},
|
|
1998
|
-
publishSnapshot,
|
|
1999
|
-
snapshotReader: readComposerSnapshot
|
|
2000
|
-
});
|
|
2001
|
-
},
|
|
2002
|
-
onCompositionStart: () => {
|
|
2003
|
-
isComposingRef.current = true;
|
|
2004
|
-
},
|
|
2005
|
-
onPaste: (event) => {
|
|
2006
|
-
const files = Array.from(event.clipboardData.files ?? []);
|
|
2007
|
-
if (files.length > 0 && onFilesAdd) {
|
|
2008
|
-
event.preventDefault();
|
|
2009
|
-
onFilesAdd(files);
|
|
2010
|
-
}
|
|
2205
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
2206
|
+
className: "px-3 py-2 sm:px-4 sm:py-2.5",
|
|
2207
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
2208
|
+
className: "min-h-11 sm:min-h-[60px]",
|
|
2209
|
+
children: /* @__PURE__ */ jsx(PlainTextPlugin, {
|
|
2210
|
+
contentEditable: /* @__PURE__ */ jsx(ContentEditable, {
|
|
2211
|
+
className: "min-h-7 max-h-[188px] w-full overflow-y-auto whitespace-pre-wrap break-words bg-transparent py-0.5 text-sm leading-6 text-foreground outline-none",
|
|
2212
|
+
onBeforeInput: (event) => {
|
|
2213
|
+
owner.handleBeforeInput({
|
|
2214
|
+
disabled,
|
|
2215
|
+
event
|
|
2216
|
+
});
|
|
2217
|
+
},
|
|
2218
|
+
onCompositionEnd: (event) => {
|
|
2219
|
+
const nativeEvent = event.nativeEvent;
|
|
2220
|
+
owner.handleCompositionEnd({ data: typeof nativeEvent.data === "string" ? nativeEvent.data : "" });
|
|
2221
|
+
},
|
|
2222
|
+
onCompositionStart: () => {
|
|
2223
|
+
owner.handleCompositionStart();
|
|
2224
|
+
},
|
|
2225
|
+
onPaste: (event) => {
|
|
2226
|
+
const files = Array.from(event.clipboardData.files ?? []);
|
|
2227
|
+
if (files.length > 0 && onFilesAdd) {
|
|
2228
|
+
event.preventDefault();
|
|
2229
|
+
onFilesAdd(files);
|
|
2011
2230
|
}
|
|
2012
|
-
}
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2231
|
+
}
|
|
2232
|
+
}),
|
|
2233
|
+
placeholder: /* @__PURE__ */ jsx("div", {
|
|
2234
|
+
className: "pointer-events-none absolute left-3 top-2 select-none text-sm leading-6 text-muted-foreground/60 sm:left-4 sm:top-2.5",
|
|
2235
|
+
children: placeholder
|
|
2236
|
+
}),
|
|
2237
|
+
ErrorBoundary: LexicalErrorBoundary
|
|
2019
2238
|
})
|
|
2020
|
-
}),
|
|
2021
|
-
/* @__PURE__ */ jsx(EditorRefPlugin, { editorRef }),
|
|
2022
|
-
/* @__PURE__ */ jsx(ChatComposerBindingsPlugin, {
|
|
2023
|
-
disabled,
|
|
2024
|
-
editorRef,
|
|
2025
|
-
editorSignatureRef,
|
|
2026
|
-
isApplyingExternalUpdateRef,
|
|
2027
|
-
isComposingRef,
|
|
2028
|
-
lastPublishedSignatureRef,
|
|
2029
|
-
nodes,
|
|
2030
|
-
onBlur: () => {
|
|
2031
|
-
onSlashQueryChange?.(null);
|
|
2032
|
-
onSlashOpenChange(false);
|
|
2033
|
-
},
|
|
2034
|
-
onKeyDown: (event) => {
|
|
2035
|
-
const editor = editorRef.current;
|
|
2036
|
-
if (!editor) return false;
|
|
2037
|
-
const snapshot = readChatComposerSnapshotFromEditorState(editor.getEditorState());
|
|
2038
|
-
selectionRef.current = snapshot.selection;
|
|
2039
|
-
return handleLexicalComposerKeyboardCommand({
|
|
2040
|
-
actions,
|
|
2041
|
-
activeSlashIndex,
|
|
2042
|
-
nativeEvent: event,
|
|
2043
|
-
onSlashActiveIndexChange,
|
|
2044
|
-
onSlashItemSelect,
|
|
2045
|
-
onSlashOpenChange,
|
|
2046
|
-
onSlashQueryChange,
|
|
2047
|
-
publishSnapshot,
|
|
2048
|
-
slashItems,
|
|
2049
|
-
snapshot
|
|
2050
|
-
});
|
|
2051
|
-
},
|
|
2052
|
-
onNodesChange,
|
|
2053
|
-
pendingSelectionRef,
|
|
2054
|
-
selectionRef,
|
|
2055
|
-
shouldFocusAfterSyncRef,
|
|
2056
|
-
syncSlashState
|
|
2057
2239
|
})
|
|
2058
|
-
|
|
2240
|
+
}), /* @__PURE__ */ jsx(ChatComposerBindingsPlugin, {
|
|
2241
|
+
disabled,
|
|
2242
|
+
nodes,
|
|
2243
|
+
owner
|
|
2244
|
+
})]
|
|
2059
2245
|
});
|
|
2060
2246
|
});
|
|
2061
2247
|
ChatInputBarTokenizedComposer.displayName = "LexicalChatInputBarTokenizedComposer";
|
|
@@ -2066,14 +2252,14 @@ function InputBarHint({ hint }) {
|
|
|
2066
2252
|
if (hint.loading) return /* @__PURE__ */ jsx("div", {
|
|
2067
2253
|
className: "px-4 pb-2",
|
|
2068
2254
|
children: /* @__PURE__ */ jsxs("div", {
|
|
2069
|
-
className: "inline-flex items-center gap-2 rounded-lg border border-
|
|
2070
|
-
children: [/* @__PURE__ */ jsx("span", { className: "h-3 w-28 animate-pulse rounded bg-
|
|
2255
|
+
className: "inline-flex items-center gap-2 rounded-lg border border-border bg-muted px-3 py-2",
|
|
2256
|
+
children: [/* @__PURE__ */ jsx("span", { className: "h-3 w-28 animate-pulse rounded bg-muted-foreground/20" }), /* @__PURE__ */ jsx("span", { className: "h-3 w-16 animate-pulse rounded bg-muted-foreground/20" })]
|
|
2071
2257
|
})
|
|
2072
2258
|
});
|
|
2073
2259
|
return /* @__PURE__ */ jsx("div", {
|
|
2074
2260
|
className: "px-4 pb-2",
|
|
2075
2261
|
children: /* @__PURE__ */ jsxs("div", {
|
|
2076
|
-
className: `inline-flex items-center gap-2 rounded-lg px-3 py-1.5 text-xs ${hint.tone === "warning" ? "border-amber-200 bg-amber-50 text-amber-800" : "border-
|
|
2262
|
+
className: `inline-flex items-center gap-2 rounded-lg px-3 py-1.5 text-xs ${hint.tone === "warning" ? "border-amber-200 bg-amber-50 text-amber-800" : "border-border bg-muted text-muted-foreground"}`,
|
|
2077
2263
|
children: [hint.text ? /* @__PURE__ */ jsx("span", { children: hint.text }) : null, hint.actionLabel && hint.onAction ? /* @__PURE__ */ jsx("button", {
|
|
2078
2264
|
type: "button",
|
|
2079
2265
|
onClick: hint.onAction,
|
|
@@ -2083,16 +2269,20 @@ function InputBarHint({ hint }) {
|
|
|
2083
2269
|
})
|
|
2084
2270
|
});
|
|
2085
2271
|
}
|
|
2086
|
-
const ChatInputBar = forwardRef(function ChatInputBar({ composer, hint, slashMenu, surface, toolbar: toolbarProps }, ref) {
|
|
2272
|
+
const ChatInputBar = forwardRef(function ChatInputBar({ composer, hint, inputSurface, slashMenu, surface, toolbar: toolbarProps }, ref) {
|
|
2087
2273
|
const composerRef = useRef(null);
|
|
2088
|
-
const
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2274
|
+
const resolvedInputSurface = inputSurface ?? (slashMenu ? {
|
|
2275
|
+
isLoading: slashMenu.isLoading,
|
|
2276
|
+
items: slashMenu.items,
|
|
2277
|
+
onSelectItem: slashMenu.onSelectItem,
|
|
2278
|
+
texts: {
|
|
2279
|
+
loadingLabel: slashMenu.texts.slashLoadingLabel,
|
|
2280
|
+
sectionLabel: slashMenu.texts.slashSectionLabel,
|
|
2281
|
+
emptyLabel: slashMenu.texts.slashEmptyLabel,
|
|
2282
|
+
hintLabel: slashMenu.texts.slashHintLabel,
|
|
2283
|
+
itemHintLabel: slashMenu.texts.slashSkillHintLabel
|
|
2284
|
+
}
|
|
2285
|
+
} : null);
|
|
2096
2286
|
const toolbar = useMemo(() => {
|
|
2097
2287
|
if (!toolbarProps.skillPicker) return toolbarProps;
|
|
2098
2288
|
return {
|
|
@@ -2113,61 +2303,33 @@ const ChatInputBar = forwardRef(function ChatInputBar({ composer, hint, slashMen
|
|
|
2113
2303
|
focusComposerAtEnd: (nodes) => composerRef.current?.focusComposerAtEnd(nodes)
|
|
2114
2304
|
}), []);
|
|
2115
2305
|
return /* @__PURE__ */ jsx("div", {
|
|
2116
|
-
className: surface === "embedded" ? "bg-transparent px-0 py-0" : "
|
|
2306
|
+
className: surface === "embedded" ? "bg-transparent px-0 py-0" : "bg-background px-3 pb-3 pt-2 sm:px-4 sm:pb-4 sm:pt-2",
|
|
2117
2307
|
children: /* @__PURE__ */ jsx("div", {
|
|
2118
2308
|
className: "nextclaw-chat-input-bar-shell mx-auto w-full max-w-[min(1120px,100%)] [container:nextclaw-chat-input-bar/inline-size]",
|
|
2119
2309
|
children: /* @__PURE__ */ jsxs("div", {
|
|
2120
|
-
className: "overflow-hidden rounded-2xl border border-
|
|
2310
|
+
className: "overflow-hidden rounded-2xl border border-border bg-card shadow-card",
|
|
2121
2311
|
children: [
|
|
2122
|
-
/* @__PURE__ */
|
|
2312
|
+
/* @__PURE__ */ jsx("div", {
|
|
2123
2313
|
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
|
-
})]
|
|
2314
|
+
children: /* @__PURE__ */ jsx(ChatInputSurfaceHost, {
|
|
2315
|
+
inputSurface: resolvedInputSurface,
|
|
2316
|
+
onInputSurfaceTriggerChange: composer.onInputSurfaceTriggerChange,
|
|
2317
|
+
onSelectItem: (item) => composerRef.current?.insertInputSurfaceItem(item, composer.inputSurfaceTriggerSpecs),
|
|
2318
|
+
triggerSpecs: composer.inputSurfaceTriggerSpecs,
|
|
2319
|
+
children: ({ onInputSurfaceKeyDown, onInputSurfaceOpenChange, onInputSurfaceSnapshotChange }) => /* @__PURE__ */ jsx(ChatInputBarTokenizedComposer, {
|
|
2320
|
+
ref: composerRef,
|
|
2321
|
+
nodes: composer.nodes,
|
|
2322
|
+
placeholder: composer.placeholder,
|
|
2323
|
+
disabled: composer.disabled,
|
|
2324
|
+
onInputSurfaceItemSelect: resolvedInputSurface?.onSelectItem,
|
|
2325
|
+
actions: toolbarProps.actions,
|
|
2326
|
+
onNodesChange: composer.onNodesChange,
|
|
2327
|
+
onFilesAdd: composer.onFilesAdd,
|
|
2328
|
+
onInputSurfaceSnapshotChange,
|
|
2329
|
+
onInputSurfaceOpenChange,
|
|
2330
|
+
onInputSurfaceKeyDown
|
|
2331
|
+
})
|
|
2332
|
+
})
|
|
2171
2333
|
}),
|
|
2172
2334
|
/* @__PURE__ */ jsx(InputBarHint, { hint }),
|
|
2173
2335
|
/* @__PURE__ */ jsx(ChatInputBarToolbar, { ...toolbar })
|
|
@@ -2182,24 +2344,64 @@ ChatInputBar.displayName = "ChatInputBar";
|
|
|
2182
2344
|
function ChatMessageAvatar({ role }) {
|
|
2183
2345
|
if (role === "user") return /* @__PURE__ */ jsx("div", {
|
|
2184
2346
|
"data-testid": "chat-message-avatar-user",
|
|
2185
|
-
className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-primary text-
|
|
2347
|
+
className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-primary text-primary-foreground shadow-sm",
|
|
2186
2348
|
children: /* @__PURE__ */ jsx(User, { className: "h-4 w-4" })
|
|
2187
2349
|
});
|
|
2188
2350
|
if (role === "tool") return /* @__PURE__ */ jsx("div", {
|
|
2189
2351
|
"data-testid": "chat-message-avatar-tool",
|
|
2190
|
-
className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-
|
|
2352
|
+
className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-accent text-accent-foreground shadow-sm ring-1 ring-border",
|
|
2191
2353
|
children: /* @__PURE__ */ jsx(Wrench, { className: "h-4 w-4" })
|
|
2192
2354
|
});
|
|
2193
2355
|
return /* @__PURE__ */ jsx("div", {
|
|
2194
2356
|
"data-testid": "chat-message-avatar-assistant",
|
|
2195
|
-
className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-
|
|
2357
|
+
className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-muted text-foreground shadow-sm ring-1 ring-border",
|
|
2196
2358
|
children: /* @__PURE__ */ jsx(Bot, {
|
|
2197
|
-
className: "h-[18px] w-[18px] text-
|
|
2359
|
+
className: "h-[18px] w-[18px] text-current",
|
|
2198
2360
|
strokeWidth: 2.5
|
|
2199
2361
|
})
|
|
2200
2362
|
});
|
|
2201
2363
|
}
|
|
2202
2364
|
//#endregion
|
|
2365
|
+
//#region src/components/chat/ui/chat-message-list/chat-inline-token-badge.tsx
|
|
2366
|
+
function resolveInlineTokenTone(kind) {
|
|
2367
|
+
if (kind === "skill") return "skill";
|
|
2368
|
+
if (kind === "panel_app") return "panel_app";
|
|
2369
|
+
return "default";
|
|
2370
|
+
}
|
|
2371
|
+
function resolveInlineTokenBadgeClassName(tone, isUser) {
|
|
2372
|
+
if (tone === "skill") return isUser ? "border-primary-foreground/30 bg-primary-foreground/18 text-primary-foreground" : "border-border bg-accent text-accent-foreground";
|
|
2373
|
+
if (tone === "panel_app") return isUser ? "border-primary-foreground/30 bg-primary-foreground/18 text-primary-foreground" : "border-border bg-accent text-accent-foreground";
|
|
2374
|
+
return isUser ? "border-primary-foreground/30 bg-primary-foreground/18 text-primary-foreground" : "border-border bg-muted text-muted-foreground";
|
|
2375
|
+
}
|
|
2376
|
+
function resolveInlineTokenIconClassName(tone, isUser) {
|
|
2377
|
+
if (tone === "skill") return isUser ? "text-primary-foreground/70" : "text-muted-foreground";
|
|
2378
|
+
if (tone === "panel_app") return isUser ? "text-primary-foreground/70" : "text-muted-foreground";
|
|
2379
|
+
return isUser ? "text-primary-foreground/70" : "text-muted-foreground";
|
|
2380
|
+
}
|
|
2381
|
+
function renderInlineTokenIcon(tone) {
|
|
2382
|
+
return tone === "panel_app" ? /* @__PURE__ */ jsx(AppWindow, {
|
|
2383
|
+
"aria-hidden": "true",
|
|
2384
|
+
className: "h-3 w-3"
|
|
2385
|
+
}) : /* @__PURE__ */ jsx(Puzzle, {
|
|
2386
|
+
"aria-hidden": "true",
|
|
2387
|
+
className: "h-3 w-3"
|
|
2388
|
+
});
|
|
2389
|
+
}
|
|
2390
|
+
function ChatInlineTokenBadge({ kind, label, isUser }) {
|
|
2391
|
+
const tone = resolveInlineTokenTone(kind);
|
|
2392
|
+
return /* @__PURE__ */ jsxs("span", {
|
|
2393
|
+
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)),
|
|
2394
|
+
title: label,
|
|
2395
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
2396
|
+
className: cn("inline-flex h-3.5 w-3.5 shrink-0 items-center justify-center", resolveInlineTokenIconClassName(tone, isUser)),
|
|
2397
|
+
children: renderInlineTokenIcon(tone)
|
|
2398
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
2399
|
+
className: "truncate",
|
|
2400
|
+
children: label
|
|
2401
|
+
})]
|
|
2402
|
+
});
|
|
2403
|
+
}
|
|
2404
|
+
//#endregion
|
|
2203
2405
|
//#region src/components/chat/utils/copy-text.utils.ts
|
|
2204
2406
|
function canUseClipboardApi() {
|
|
2205
2407
|
return typeof navigator !== "undefined" && typeof navigator.clipboard?.writeText === "function";
|
|
@@ -2499,6 +2701,10 @@ const SAFE_LINK_PROTOCOLS = new Set([
|
|
|
2499
2701
|
"mailto:",
|
|
2500
2702
|
"tel:"
|
|
2501
2703
|
]);
|
|
2704
|
+
const INLINE_TOKEN_KIND_ATTR = "data-chat-inline-token-kind";
|
|
2705
|
+
const INLINE_TOKEN_KEY_ATTR = "data-chat-inline-token-key";
|
|
2706
|
+
const INLINE_TOKEN_LABEL_ATTR = "data-chat-inline-token-label";
|
|
2707
|
+
const INLINE_TOKEN_RAW_TEXT_ATTR = "data-chat-inline-token-raw-text";
|
|
2502
2708
|
const PROJECT_RELATIVE_FILE_EXTENSIONS = [
|
|
2503
2709
|
"cjs",
|
|
2504
2710
|
"css",
|
|
@@ -2553,10 +2759,116 @@ function parseLocalFileAction(href) {
|
|
|
2553
2759
|
...typeof column === "number" ? { column } : {}
|
|
2554
2760
|
};
|
|
2555
2761
|
}
|
|
2556
|
-
function
|
|
2762
|
+
function prepareInlineTokens(inlineTokens) {
|
|
2763
|
+
if (!inlineTokens || inlineTokens.length === 0) return [];
|
|
2764
|
+
const seenRawTexts = /* @__PURE__ */ new Set();
|
|
2765
|
+
const tokens = [];
|
|
2766
|
+
for (const token of inlineTokens) {
|
|
2767
|
+
if (!token.rawText || seenRawTexts.has(token.rawText)) continue;
|
|
2768
|
+
seenRawTexts.add(token.rawText);
|
|
2769
|
+
tokens.push(token);
|
|
2770
|
+
}
|
|
2771
|
+
return tokens.sort((left, right) => right.rawText.length - left.rawText.length);
|
|
2772
|
+
}
|
|
2773
|
+
function createInlineTokenNode(token) {
|
|
2774
|
+
return {
|
|
2775
|
+
type: "chatInlineToken",
|
|
2776
|
+
data: {
|
|
2777
|
+
hName: "span",
|
|
2778
|
+
hProperties: {
|
|
2779
|
+
[INLINE_TOKEN_KIND_ATTR]: token.kind,
|
|
2780
|
+
[INLINE_TOKEN_KEY_ATTR]: token.key,
|
|
2781
|
+
[INLINE_TOKEN_LABEL_ATTR]: token.label,
|
|
2782
|
+
[INLINE_TOKEN_RAW_TEXT_ATTR]: token.rawText
|
|
2783
|
+
},
|
|
2784
|
+
hChildren: [{
|
|
2785
|
+
type: "text",
|
|
2786
|
+
value: token.label
|
|
2787
|
+
}]
|
|
2788
|
+
}
|
|
2789
|
+
};
|
|
2790
|
+
}
|
|
2791
|
+
function findNextInlineToken(value, cursor, tokens) {
|
|
2792
|
+
let next = null;
|
|
2793
|
+
for (const token of tokens) {
|
|
2794
|
+
const index = value.indexOf(token.rawText, cursor);
|
|
2795
|
+
if (index < 0) continue;
|
|
2796
|
+
if (!next || index < next.index || index === next.index && token.rawText.length > next.token.rawText.length) next = {
|
|
2797
|
+
index,
|
|
2798
|
+
token
|
|
2799
|
+
};
|
|
2800
|
+
}
|
|
2801
|
+
return next;
|
|
2802
|
+
}
|
|
2803
|
+
function splitTextNodeByInlineTokens(value, tokens) {
|
|
2804
|
+
const output = [];
|
|
2805
|
+
let cursor = 0;
|
|
2806
|
+
while (cursor < value.length) {
|
|
2807
|
+
const next = findNextInlineToken(value, cursor, tokens);
|
|
2808
|
+
if (!next) {
|
|
2809
|
+
output.push({
|
|
2810
|
+
type: "text",
|
|
2811
|
+
value: value.slice(cursor)
|
|
2812
|
+
});
|
|
2813
|
+
break;
|
|
2814
|
+
}
|
|
2815
|
+
if (next.index > cursor) output.push({
|
|
2816
|
+
type: "text",
|
|
2817
|
+
value: value.slice(cursor, next.index)
|
|
2818
|
+
});
|
|
2819
|
+
output.push(createInlineTokenNode(next.token));
|
|
2820
|
+
cursor = next.index + next.token.rawText.length;
|
|
2821
|
+
}
|
|
2822
|
+
return output.length > 0 ? output : [{
|
|
2823
|
+
type: "text",
|
|
2824
|
+
value
|
|
2825
|
+
}];
|
|
2826
|
+
}
|
|
2827
|
+
function transformInlineTokenTextNodes(node, tokens) {
|
|
2828
|
+
if (node.type === "code" || node.type === "inlineCode" || !node.children) return;
|
|
2829
|
+
const nextChildren = [];
|
|
2830
|
+
for (const child of node.children) {
|
|
2831
|
+
if (child.type === "text" && typeof child.value === "string") {
|
|
2832
|
+
nextChildren.push(...splitTextNodeByInlineTokens(child.value, tokens));
|
|
2833
|
+
continue;
|
|
2834
|
+
}
|
|
2835
|
+
transformInlineTokenTextNodes(child, tokens);
|
|
2836
|
+
nextChildren.push(child);
|
|
2837
|
+
}
|
|
2838
|
+
node.children = nextChildren;
|
|
2839
|
+
}
|
|
2840
|
+
function createRemarkInlineTokenPlugin(inlineTokens) {
|
|
2841
|
+
const tokens = prepareInlineTokens(inlineTokens);
|
|
2842
|
+
return () => (tree) => {
|
|
2843
|
+
if (tokens.length === 0) return;
|
|
2844
|
+
transformInlineTokenTextNodes(tree, tokens);
|
|
2845
|
+
};
|
|
2846
|
+
}
|
|
2847
|
+
function readStringProp(props, key) {
|
|
2848
|
+
const value = props[key];
|
|
2849
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
2850
|
+
}
|
|
2851
|
+
function ChatMessageMarkdown({ text, role, texts, inline = false, inlineTokens, onFileOpen }) {
|
|
2557
2852
|
const isUser = role === "user";
|
|
2853
|
+
const remarkPlugins = useMemo(() => inlineTokens && inlineTokens.length > 0 ? [remarkGfm, createRemarkInlineTokenPlugin(inlineTokens)] : [remarkGfm], [inlineTokens]);
|
|
2558
2854
|
const markdownComponents = useMemo(() => ({
|
|
2559
2855
|
p: ({ children }) => inline ? /* @__PURE__ */ jsx(Fragment$1, { children }) : /* @__PURE__ */ jsx("p", { children }),
|
|
2856
|
+
span: ({ node: _node, children, ...rest }) => {
|
|
2857
|
+
const restProps = rest;
|
|
2858
|
+
const kind = readStringProp(restProps, INLINE_TOKEN_KIND_ATTR);
|
|
2859
|
+
const key = readStringProp(restProps, INLINE_TOKEN_KEY_ATTR);
|
|
2860
|
+
const label = readStringProp(restProps, INLINE_TOKEN_LABEL_ATTR);
|
|
2861
|
+
const rawText = readStringProp(restProps, INLINE_TOKEN_RAW_TEXT_ATTR);
|
|
2862
|
+
if (kind && key && label && rawText) return /* @__PURE__ */ jsx(ChatInlineTokenBadge, {
|
|
2863
|
+
kind,
|
|
2864
|
+
label,
|
|
2865
|
+
isUser
|
|
2866
|
+
});
|
|
2867
|
+
return /* @__PURE__ */ jsx("span", {
|
|
2868
|
+
...rest,
|
|
2869
|
+
children
|
|
2870
|
+
});
|
|
2871
|
+
},
|
|
2560
2872
|
a: ({ href, children, ...rest }) => {
|
|
2561
2873
|
const safeHref = resolveSafeHref(href);
|
|
2562
2874
|
if (!safeHref) return /* @__PURE__ */ jsx("span", {
|
|
@@ -2626,6 +2938,7 @@ function ChatMessageMarkdown({ text, role, texts, inline = false, onFileOpen })
|
|
|
2626
2938
|
}
|
|
2627
2939
|
}), [
|
|
2628
2940
|
inline,
|
|
2941
|
+
isUser,
|
|
2629
2942
|
onFileOpen,
|
|
2630
2943
|
texts
|
|
2631
2944
|
]);
|
|
@@ -2633,86 +2946,25 @@ function ChatMessageMarkdown({ text, role, texts, inline = false, onFileOpen })
|
|
|
2633
2946
|
className: cn("chat-markdown", isUser ? "chat-markdown-user" : "chat-markdown-assistant"),
|
|
2634
2947
|
children: /* @__PURE__ */ jsx(ReactMarkdown, {
|
|
2635
2948
|
skipHtml: true,
|
|
2636
|
-
remarkPlugins
|
|
2949
|
+
remarkPlugins,
|
|
2637
2950
|
components: markdownComponents,
|
|
2638
2951
|
children: trimMarkdown(text)
|
|
2639
2952
|
})
|
|
2640
2953
|
});
|
|
2641
2954
|
}
|
|
2642
2955
|
//#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
2956
|
//#region src/components/chat/ui/chat-message-list/chat-message-file/meta.ts
|
|
2705
2957
|
const FILE_CATEGORY_TILE_CLASSES = {
|
|
2706
|
-
archive: "border-
|
|
2707
|
-
audio: "border-
|
|
2708
|
-
code: "border-
|
|
2709
|
-
data: "border-
|
|
2710
|
-
document: "border-
|
|
2711
|
-
generic: "border-
|
|
2712
|
-
image: "border-
|
|
2713
|
-
pdf: "border-
|
|
2714
|
-
sheet: "border-
|
|
2715
|
-
video: "border-
|
|
2958
|
+
archive: "border-border bg-muted text-muted-foreground",
|
|
2959
|
+
audio: "border-border bg-muted text-muted-foreground",
|
|
2960
|
+
code: "border-border bg-muted text-muted-foreground",
|
|
2961
|
+
data: "border-border bg-muted text-muted-foreground",
|
|
2962
|
+
document: "border-border bg-muted text-muted-foreground",
|
|
2963
|
+
generic: "border-border bg-muted text-muted-foreground",
|
|
2964
|
+
image: "border-border bg-muted text-muted-foreground",
|
|
2965
|
+
pdf: "border-border bg-muted text-muted-foreground",
|
|
2966
|
+
sheet: "border-border bg-muted text-muted-foreground",
|
|
2967
|
+
video: "border-border bg-muted text-muted-foreground"
|
|
2716
2968
|
};
|
|
2717
2969
|
const CODE_EXTENSIONS = new Set([
|
|
2718
2970
|
"c",
|
|
@@ -2922,13 +3174,13 @@ function readFileCategoryLabel(category, texts) {
|
|
|
2922
3174
|
}
|
|
2923
3175
|
function renderMetaLine(categoryLabel, sizeLabel, isUser) {
|
|
2924
3176
|
return /* @__PURE__ */ jsx("div", {
|
|
2925
|
-
className: cn("mt-1 text-xs leading-5", isUser ? "text-
|
|
3177
|
+
className: cn("mt-1 text-xs leading-5", isUser ? "text-primary-foreground/70" : "text-muted-foreground"),
|
|
2926
3178
|
children: sizeLabel ? `${categoryLabel} · ${sizeLabel}` : categoryLabel
|
|
2927
3179
|
});
|
|
2928
3180
|
}
|
|
2929
3181
|
function renderActionPill(label, isUser, isInteractive) {
|
|
2930
3182
|
return /* @__PURE__ */ jsx("span", {
|
|
2931
|
-
className: cn("inline-flex items-center rounded-full border px-2.5 py-1 text-[11px] font-medium", isInteractive ? isUser ? "border-
|
|
3183
|
+
className: cn("inline-flex items-center rounded-full border px-2.5 py-1 text-[11px] font-medium", isInteractive ? isUser ? "border-primary-foreground/14 bg-primary-foreground/12 text-primary-foreground" : "border-border bg-card text-foreground" : isUser ? "border-primary-foreground/10 bg-primary-foreground/6 text-primary-foreground/62" : "border-border bg-muted text-muted-foreground"),
|
|
2932
3184
|
children: label
|
|
2933
3185
|
});
|
|
2934
3186
|
}
|
|
@@ -2937,16 +3189,16 @@ function renderActionLink(label, href, isUser) {
|
|
|
2937
3189
|
href,
|
|
2938
3190
|
target: "_blank",
|
|
2939
3191
|
rel: "noreferrer",
|
|
2940
|
-
className: cn("inline-flex items-center rounded-full border px-2.5 py-1 text-[11px] font-medium transition duration-200", isUser ? "border-
|
|
3192
|
+
className: cn("inline-flex items-center rounded-full border px-2.5 py-1 text-[11px] font-medium transition duration-200", isUser ? "border-primary-foreground/14 bg-primary-foreground/12 text-primary-foreground hover:border-primary-foreground/20 hover:bg-primary-foreground/16" : "border-border bg-card text-foreground hover:border-border-hover hover:bg-accent"),
|
|
2941
3193
|
children: label
|
|
2942
3194
|
});
|
|
2943
3195
|
}
|
|
2944
3196
|
function FileCategoryGlyph({ category, isUser }) {
|
|
2945
3197
|
const Icon = FILE_CATEGORY_ICONS[category];
|
|
2946
3198
|
return /* @__PURE__ */ jsx("div", {
|
|
2947
|
-
className: cn("flex h-14 w-14 shrink-0 items-center justify-center rounded-[1rem] border", isUser ? "border-
|
|
3199
|
+
className: cn("flex h-14 w-14 shrink-0 items-center justify-center rounded-[1rem] border", isUser ? "border-primary-foreground/12 bg-primary-foreground/10 text-primary-foreground" : FILE_CATEGORY_TILE_CLASSES[category]),
|
|
2948
3200
|
children: /* @__PURE__ */ jsx(Icon, {
|
|
2949
|
-
className: cn("h-7 w-7", isUser ? "text-
|
|
3201
|
+
className: cn("h-7 w-7", isUser ? "text-primary-foreground/92" : "text-current"),
|
|
2950
3202
|
strokeWidth: 2.2
|
|
2951
3203
|
})
|
|
2952
3204
|
});
|
|
@@ -2960,7 +3212,7 @@ function renderImagePreview(params) {
|
|
|
2960
3212
|
rel: "noreferrer",
|
|
2961
3213
|
className: "group block",
|
|
2962
3214
|
children: /* @__PURE__ */ jsxs("div", {
|
|
2963
|
-
className: cn("relative overflow-hidden rounded-[1rem]", isUser ? "ring-1 ring-
|
|
3215
|
+
className: cn("relative overflow-hidden rounded-[1rem]", isUser ? "ring-1 ring-primary-foreground/10" : "bg-muted ring-1 ring-border"),
|
|
2964
3216
|
children: [/* @__PURE__ */ jsx("img", {
|
|
2965
3217
|
src: file.dataUrl,
|
|
2966
3218
|
alt: file.label,
|
|
@@ -2968,10 +3220,10 @@ function renderImagePreview(params) {
|
|
|
2968
3220
|
}), /* @__PURE__ */ jsxs("div", {
|
|
2969
3221
|
className: "pointer-events-none absolute right-3 top-3 flex items-center gap-2",
|
|
2970
3222
|
children: [/* @__PURE__ */ jsx("span", {
|
|
2971
|
-
className: cn("inline-flex items-center rounded-full px-2 py-1 text-[10px] font-semibold tracking-[0.18em] text-white backdrop-blur-sm", isUser ? "bg-
|
|
3223
|
+
className: cn("inline-flex items-center rounded-full px-2 py-1 text-[10px] font-semibold tracking-[0.18em] text-white backdrop-blur-sm", isUser ? "bg-black/36" : "bg-black/58"),
|
|
2972
3224
|
children: categoryLabel
|
|
2973
3225
|
}), sizeLabel ? /* @__PURE__ */ jsx("span", {
|
|
2974
|
-
className: cn("inline-flex items-center rounded-full px-2 py-1 text-[10px] font-medium text-white/92 backdrop-blur-sm", isUser ? "bg-
|
|
3226
|
+
className: cn("inline-flex items-center rounded-full px-2 py-1 text-[10px] font-medium text-white/92 backdrop-blur-sm", isUser ? "bg-black/28" : "bg-black/46"),
|
|
2975
3227
|
children: sizeLabel
|
|
2976
3228
|
}) : null]
|
|
2977
3229
|
})]
|
|
@@ -3025,7 +3277,7 @@ function renderInlineMediaCard(params) {
|
|
|
3025
3277
|
...mediaMimeType ? { type: mediaMimeType } : {}
|
|
3026
3278
|
})
|
|
3027
3279
|
}) : /* @__PURE__ */ jsx("div", {
|
|
3028
|
-
className: cn("overflow-hidden rounded-[1rem] border", isUser ? "border-
|
|
3280
|
+
className: cn("overflow-hidden rounded-[1rem] border", isUser ? "border-primary-foreground/10 bg-black/26" : "border-border bg-muted"),
|
|
3029
3281
|
children: /* @__PURE__ */ jsx("video", {
|
|
3030
3282
|
controls: true,
|
|
3031
3283
|
preload: "metadata",
|
|
@@ -3049,7 +3301,7 @@ function ChatMessageFile({ file, isUser = false, texts }) {
|
|
|
3049
3301
|
const isInteractive = Boolean(file.dataUrl);
|
|
3050
3302
|
const actionLabel = isInteractive ? texts?.attachmentOpenLabel ?? "Open" : texts?.attachmentAttachedLabel ?? "Attached";
|
|
3051
3303
|
const categoryLabel = readFileCategoryLabel(category, texts);
|
|
3052
|
-
const shellClasses = cn("block overflow-hidden rounded-[1.25rem] border transition duration-200", isUser ? "border-
|
|
3304
|
+
const shellClasses = cn("block overflow-hidden rounded-[1.25rem] border transition duration-200", isUser ? "border-primary-foreground/12 bg-primary-foreground/10 text-primary-foreground" : "border-border bg-card text-card-foreground", isInteractive && (isUser ? "hover:border-primary-foreground/18 hover:bg-primary-foreground/13" : "hover:border-border-hover hover:bg-accent"));
|
|
3053
3305
|
if (renderAsImage) return renderImagePreview({
|
|
3054
3306
|
file,
|
|
3055
3307
|
categoryLabel,
|
|
@@ -3205,14 +3457,14 @@ function ChatReasoningBlock({ label, text, isUser, isInProgress }) {
|
|
|
3205
3457
|
className: "mt-2",
|
|
3206
3458
|
open: isOpen,
|
|
3207
3459
|
children: [/* @__PURE__ */ jsx("summary", {
|
|
3208
|
-
className: cn("cursor-pointer text-xs", isUser ? "text-primary-100" : "text-
|
|
3460
|
+
className: cn("cursor-pointer text-xs", isUser ? "text-primary-100" : "text-muted-foreground"),
|
|
3209
3461
|
onClick: onSummaryClick,
|
|
3210
3462
|
children: displayLabel
|
|
3211
3463
|
}), /* @__PURE__ */ jsx("div", {
|
|
3212
3464
|
ref: scrollRef,
|
|
3213
3465
|
onScroll,
|
|
3214
3466
|
"data-reasoning-scroll": "true",
|
|
3215
|
-
className: cn("mt-2 w-fit max-w-[500px] max-h-56 overflow-y-auto rounded-lg custom-scrollbar
|
|
3467
|
+
className: cn("mt-2 w-fit max-w-[500px] max-h-56 overflow-y-auto rounded-lg custom-scrollbar", isUser ? "bg-primary/70" : "bg-muted"),
|
|
3216
3468
|
children: /* @__PURE__ */ jsx("pre", {
|
|
3217
3469
|
className: "min-w-0 whitespace-pre-wrap break-all p-2 text-[11px]",
|
|
3218
3470
|
children: text
|
|
@@ -3224,13 +3476,13 @@ function ChatReasoningBlock({ label, text, isUser, isInProgress }) {
|
|
|
3224
3476
|
//#region src/components/chat/ui/chat-message-list/tool-card/tool-card-root.tsx
|
|
3225
3477
|
function ToolCardRoot({ children, className }) {
|
|
3226
3478
|
return /* @__PURE__ */ jsx("div", {
|
|
3227
|
-
className: cn("my-2 rounded-lg border border-
|
|
3479
|
+
className: cn("my-2 rounded-lg border border-border bg-card shadow-sm overflow-hidden text-[12px] text-card-foreground", "w-[280px] sm:w-[360px] md:w-[480px] min-w-full max-w-full transition-all flex flex-col", className),
|
|
3228
3480
|
children
|
|
3229
3481
|
});
|
|
3230
3482
|
}
|
|
3231
3483
|
function ToolCardContent({ children, className }) {
|
|
3232
3484
|
return /* @__PURE__ */ jsx("div", {
|
|
3233
|
-
className: cn("border-t border-
|
|
3485
|
+
className: cn("border-t border-border bg-muted/35 px-3 pt-1 pb-2 w-full overflow-hidden", className),
|
|
3234
3486
|
children
|
|
3235
3487
|
});
|
|
3236
3488
|
}
|
|
@@ -3238,22 +3490,22 @@ function ToolCardContent({ children, className }) {
|
|
|
3238
3490
|
//#region src/components/chat/ui/chat-message-list/tool-card/tool-card-status.tsx
|
|
3239
3491
|
const STATUS_STYLES = {
|
|
3240
3492
|
running: {
|
|
3241
|
-
text: "text-
|
|
3493
|
+
text: "text-primary/70",
|
|
3242
3494
|
icon: Loader2,
|
|
3243
3495
|
spin: true
|
|
3244
3496
|
},
|
|
3245
3497
|
success: {
|
|
3246
|
-
text: "text-
|
|
3498
|
+
text: "text-primary/70",
|
|
3247
3499
|
icon: Check,
|
|
3248
3500
|
spin: false
|
|
3249
3501
|
},
|
|
3250
3502
|
error: {
|
|
3251
|
-
text: "text-
|
|
3503
|
+
text: "text-destructive",
|
|
3252
3504
|
icon: AlertTriangle,
|
|
3253
3505
|
spin: false
|
|
3254
3506
|
},
|
|
3255
3507
|
cancelled: {
|
|
3256
|
-
text: "text-
|
|
3508
|
+
text: "text-muted-foreground",
|
|
3257
3509
|
icon: Minus,
|
|
3258
3510
|
spin: false
|
|
3259
3511
|
}
|
|
@@ -3275,12 +3527,12 @@ function resolveToolCardActionView(action) {
|
|
|
3275
3527
|
if (action.kind === "show-content") return {
|
|
3276
3528
|
icon: Eye,
|
|
3277
3529
|
label: action.label,
|
|
3278
|
-
toneClassName: "border-
|
|
3530
|
+
toneClassName: "border-border bg-card text-foreground hover:bg-accent hover:text-accent-foreground focus-visible:ring-primary/35"
|
|
3279
3531
|
};
|
|
3280
3532
|
return {
|
|
3281
3533
|
icon: ArrowUpRight,
|
|
3282
3534
|
label: action.label ?? (action.sessionKind === "child" ? "Open child session" : "Open session"),
|
|
3283
|
-
toneClassName: "border-
|
|
3535
|
+
toneClassName: "border-border bg-card text-foreground hover:bg-accent hover:text-accent-foreground focus-visible:ring-primary/35"
|
|
3284
3536
|
};
|
|
3285
3537
|
}
|
|
3286
3538
|
function ToolCardActionButton({ action, onAction }) {
|
|
@@ -3316,12 +3568,12 @@ function ToolCardActionButton({ action, onAction }) {
|
|
|
3316
3568
|
function ToolCardHeader({ card, icon: Icon, expanded, canExpand, hideSummary = false, actionSlot, onToggle }) {
|
|
3317
3569
|
const summaryPart = hideSummary ? "" : card.summary?.replace(/^(command|path|args|query|input):\s*/i, "") ?? "";
|
|
3318
3570
|
return /* @__PURE__ */ jsxs("div", {
|
|
3319
|
-
className: cn("flex items-center gap-3 px-3 py-2.5 transition-colors bg-transparent", canExpand ? "cursor-pointer hover:bg-
|
|
3571
|
+
className: cn("flex items-center gap-3 px-3 py-2.5 transition-colors bg-transparent", canExpand ? "cursor-pointer hover:bg-accent/70" : ""),
|
|
3320
3572
|
onClick: onToggle,
|
|
3321
3573
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
3322
|
-
className: "flex min-w-0 flex-1 items-center gap-2 overflow-hidden font-mono text-
|
|
3574
|
+
className: "flex min-w-0 flex-1 items-center gap-2 overflow-hidden font-mono text-foreground",
|
|
3323
3575
|
children: [/* @__PURE__ */ jsx(Icon, {
|
|
3324
|
-
className: "h-4 w-4 text-
|
|
3576
|
+
className: "h-4 w-4 text-primary/70 shrink-0",
|
|
3325
3577
|
strokeWidth: 3
|
|
3326
3578
|
}), /* @__PURE__ */ jsxs("div", {
|
|
3327
3579
|
className: "flex min-w-0 flex-1 items-center gap-1.5 overflow-hidden",
|
|
@@ -3329,10 +3581,10 @@ function ToolCardHeader({ card, icon: Icon, expanded, canExpand, hideSummary = f
|
|
|
3329
3581
|
className: "font-bold shrink-0 tracking-tight",
|
|
3330
3582
|
children: card.toolName
|
|
3331
3583
|
}), summaryPart && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
|
|
3332
|
-
className: "text-
|
|
3584
|
+
className: "text-muted-foreground/45 font-bold select-none shrink-0",
|
|
3333
3585
|
children: "›"
|
|
3334
3586
|
}), /* @__PURE__ */ jsx("span", {
|
|
3335
|
-
className: "block min-w-0 flex-1 truncate font-normal",
|
|
3587
|
+
className: "block min-w-0 flex-1 truncate font-normal text-muted-foreground",
|
|
3336
3588
|
title: summaryPart,
|
|
3337
3589
|
children: summaryPart
|
|
3338
3590
|
})] })]
|
|
@@ -3343,10 +3595,10 @@ function ToolCardHeader({ card, icon: Icon, expanded, canExpand, hideSummary = f
|
|
|
3343
3595
|
actionSlot,
|
|
3344
3596
|
/* @__PURE__ */ jsx(ToolStatusLabel, { card }),
|
|
3345
3597
|
canExpand && (expanded ? /* @__PURE__ */ jsx(ChevronDown, {
|
|
3346
|
-
className: "h-4 w-4 text-
|
|
3598
|
+
className: "h-4 w-4 text-muted-foreground",
|
|
3347
3599
|
strokeWidth: 3
|
|
3348
3600
|
}) : /* @__PURE__ */ jsx(ChevronRight, {
|
|
3349
|
-
className: "h-4 w-4 text-
|
|
3601
|
+
className: "h-4 w-4 text-muted-foreground",
|
|
3350
3602
|
strokeWidth: 3
|
|
3351
3603
|
}))
|
|
3352
3604
|
]
|
|
@@ -3390,12 +3642,12 @@ function readCodeColumnWidth(block) {
|
|
|
3390
3642
|
function getLineNumberTone(line) {
|
|
3391
3643
|
if (line.kind === "remove") return "border-r border-rose-200 bg-rose-50 text-rose-700";
|
|
3392
3644
|
if (line.kind === "add") return "border-r border-emerald-200 bg-emerald-50 text-emerald-700";
|
|
3393
|
-
return "border-r border-
|
|
3645
|
+
return "border-r border-border bg-muted text-muted-foreground";
|
|
3394
3646
|
}
|
|
3395
3647
|
function getCodeRowTone(line) {
|
|
3396
3648
|
if (line.kind === "remove") return "bg-rose-50 text-rose-950";
|
|
3397
3649
|
if (line.kind === "add") return "bg-emerald-50 text-emerald-950";
|
|
3398
|
-
return "bg-
|
|
3650
|
+
return "bg-card text-foreground";
|
|
3399
3651
|
}
|
|
3400
3652
|
function FileOperationLineNumberCell({ layout, line, lineNumberColumnWidth }) {
|
|
3401
3653
|
return /* @__PURE__ */ jsx("span", {
|
|
@@ -3433,7 +3685,7 @@ function FileOperationWorkspaceSurface({ block }) {
|
|
|
3433
3685
|
return /* @__PURE__ */ jsxs("div", {
|
|
3434
3686
|
"data-file-code-surface": "true",
|
|
3435
3687
|
"data-file-code-surface-layout": "workspace",
|
|
3436
|
-
className: "flex h-full min-h-full min-w-full bg-
|
|
3688
|
+
className: "flex h-full min-h-full min-w-full bg-card",
|
|
3437
3689
|
children: [showLineNumbers ? /* @__PURE__ */ jsxs("div", {
|
|
3438
3690
|
"data-file-code-gutter": "true",
|
|
3439
3691
|
style: {
|
|
@@ -3445,10 +3697,10 @@ function FileOperationWorkspaceSurface({ block }) {
|
|
|
3445
3697
|
layout: "workspace",
|
|
3446
3698
|
line,
|
|
3447
3699
|
lineNumberColumnWidth
|
|
3448
|
-
}, readLineKey("gutter", line, index))), /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 border-r border-
|
|
3700
|
+
}, readLineKey("gutter", line, index))), /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 border-r border-border bg-muted" })]
|
|
3449
3701
|
}) : null, /* @__PURE__ */ jsxs("div", {
|
|
3450
3702
|
"data-file-code-canvas": "true",
|
|
3451
|
-
className: "flex h-full min-h-full min-w-0 flex-1 flex-col bg-
|
|
3703
|
+
className: "flex h-full min-h-full min-w-0 flex-1 flex-col bg-card",
|
|
3452
3704
|
children: [/* @__PURE__ */ jsx("div", {
|
|
3453
3705
|
"data-file-code-stack": "true",
|
|
3454
3706
|
className: "min-w-full",
|
|
@@ -3458,7 +3710,7 @@ function FileOperationWorkspaceSurface({ block }) {
|
|
|
3458
3710
|
className: FILE_ROW_CLASS_NAME,
|
|
3459
3711
|
children: /* @__PURE__ */ jsx(FileOperationCodeCell, { line })
|
|
3460
3712
|
}, readLineKey("code", line, index)))
|
|
3461
|
-
}), /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 bg-
|
|
3713
|
+
}), /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 bg-card" })]
|
|
3462
3714
|
})]
|
|
3463
3715
|
});
|
|
3464
3716
|
}
|
|
@@ -3471,7 +3723,7 @@ function FileOperationCodeSurface({ block, layout = "compact" }) {
|
|
|
3471
3723
|
return /* @__PURE__ */ jsx("div", {
|
|
3472
3724
|
"data-file-code-surface": "true",
|
|
3473
3725
|
"data-file-code-surface-layout": "compact",
|
|
3474
|
-
className: "overflow-x-auto custom-scrollbar
|
|
3726
|
+
className: "overflow-x-auto custom-scrollbar bg-card",
|
|
3475
3727
|
children: /* @__PURE__ */ jsx("div", {
|
|
3476
3728
|
"data-file-code-stack": "true",
|
|
3477
3729
|
className: "min-w-full",
|
|
@@ -3507,7 +3759,7 @@ function readFileOperationContentVersion(block) {
|
|
|
3507
3759
|
function getCaptionTone(part) {
|
|
3508
3760
|
if (/^\+\d+$/.test(part)) return "text-emerald-700";
|
|
3509
3761
|
if (/^-\d+$/.test(part)) return "text-rose-700";
|
|
3510
|
-
return "text-
|
|
3762
|
+
return "text-muted-foreground";
|
|
3511
3763
|
}
|
|
3512
3764
|
function renderCaption(caption) {
|
|
3513
3765
|
const parts = caption.split("·").map((part) => part.trim()).filter((part) => /^\+\d+$/.test(part) || /^-\d+$/.test(part));
|
|
@@ -3515,7 +3767,7 @@ function renderCaption(caption) {
|
|
|
3515
3767
|
return /* @__PURE__ */ jsx("div", {
|
|
3516
3768
|
className: "flex shrink-0 items-center gap-x-1 whitespace-nowrap text-[10px] font-medium uppercase tracking-[0.08em]",
|
|
3517
3769
|
children: parts.map((part, index) => /* @__PURE__ */ jsxs(Fragment, { children: [index > 0 ? /* @__PURE__ */ jsx("span", {
|
|
3518
|
-
className: "text-
|
|
3770
|
+
className: "text-muted-foreground/45",
|
|
3519
3771
|
children: "·"
|
|
3520
3772
|
}) : null, /* @__PURE__ */ jsx("span", {
|
|
3521
3773
|
className: cn(getCaptionTone(part)),
|
|
@@ -3551,7 +3803,7 @@ function StickyFileOperationScrollArea({ children, contentVersion, resetKey, cla
|
|
|
3551
3803
|
ref: scrollRef,
|
|
3552
3804
|
onScroll,
|
|
3553
3805
|
"data-file-scroll-kind": scrollKind,
|
|
3554
|
-
className: cn("overflow-y-auto bg-
|
|
3806
|
+
className: cn("overflow-y-auto bg-card custom-scrollbar", className),
|
|
3555
3807
|
children
|
|
3556
3808
|
});
|
|
3557
3809
|
}
|
|
@@ -3564,10 +3816,10 @@ function FileOperationBlock({ block, showPathRow, isFirst, onFileOpen }) {
|
|
|
3564
3816
|
onFileOpen(buildFileOpenAction(block));
|
|
3565
3817
|
};
|
|
3566
3818
|
return /* @__PURE__ */ jsxs("section", {
|
|
3567
|
-
className: cn("overflow-hidden bg-
|
|
3819
|
+
className: cn("overflow-hidden bg-card", !isFirst && "border-t border-border"),
|
|
3568
3820
|
children: [
|
|
3569
3821
|
showMetaRow ? /* @__PURE__ */ jsxs("div", {
|
|
3570
|
-
className: cn("flex items-center justify-between gap-4 border-b border-
|
|
3822
|
+
className: cn("flex items-center justify-between gap-4 border-b border-border px-4 text-muted-foreground", showPathRow ? "py-2" : "py-1.5"),
|
|
3571
3823
|
children: [/* @__PURE__ */ jsx("div", {
|
|
3572
3824
|
className: "min-w-0 flex-1",
|
|
3573
3825
|
children: showPathRow ? /* @__PURE__ */ jsx(TooltipProvider, {
|
|
@@ -3577,7 +3829,7 @@ function FileOperationBlock({ block, showPathRow, isFirst, onFileOpen }) {
|
|
|
3577
3829
|
children: /* @__PURE__ */ jsx("button", {
|
|
3578
3830
|
type: "button",
|
|
3579
3831
|
onClick: handlePathClick,
|
|
3580
|
-
className: cn("w-full truncate whitespace-nowrap text-left font-mono text-[12px] font-medium text-
|
|
3832
|
+
className: cn("w-full truncate whitespace-nowrap text-left font-mono text-[12px] font-medium text-foreground", onFileOpen && "transition-colors hover:text-primary hover:underline"),
|
|
3581
3833
|
title: block.path,
|
|
3582
3834
|
children: block.path
|
|
3583
3835
|
})
|
|
@@ -3601,15 +3853,15 @@ function FileOperationBlock({ block, showPathRow, isFirst, onFileOpen }) {
|
|
|
3601
3853
|
className: "max-h-72",
|
|
3602
3854
|
scrollKind: "raw",
|
|
3603
3855
|
children: /* @__PURE__ */ jsx("div", {
|
|
3604
|
-
className: "overflow-x-auto custom-scrollbar
|
|
3856
|
+
className: "overflow-x-auto custom-scrollbar",
|
|
3605
3857
|
children: /* @__PURE__ */ jsx("pre", {
|
|
3606
|
-
className: "min-w-max whitespace-pre bg-
|
|
3858
|
+
className: "min-w-max whitespace-pre bg-card px-4 py-2 font-mono text-[11px] leading-5 text-foreground",
|
|
3607
3859
|
children: block.rawText
|
|
3608
3860
|
})
|
|
3609
3861
|
})
|
|
3610
3862
|
}) : null,
|
|
3611
3863
|
block.truncated && !previewBlock ? /* @__PURE__ */ jsx("div", {
|
|
3612
|
-
className: "border-t border-
|
|
3864
|
+
className: "border-t border-border bg-muted px-4 py-2 text-[10px] text-muted-foreground",
|
|
3613
3865
|
children: "Showing a shortened diff preview."
|
|
3614
3866
|
}) : null
|
|
3615
3867
|
]
|
|
@@ -3620,7 +3872,7 @@ function ToolCardFileOperationContent({ card, className, onFileOpen }) {
|
|
|
3620
3872
|
const output = card.output?.trim() ?? "";
|
|
3621
3873
|
if (blocks.length === 0 && !output) return null;
|
|
3622
3874
|
return /* @__PURE__ */ jsxs("div", {
|
|
3623
|
-
className: cn("overflow-hidden bg-
|
|
3875
|
+
className: cn("overflow-hidden bg-card", className),
|
|
3624
3876
|
children: [blocks.map((block, index) => {
|
|
3625
3877
|
return /* @__PURE__ */ jsx(FileOperationBlock, {
|
|
3626
3878
|
block,
|
|
@@ -3631,12 +3883,12 @@ function ToolCardFileOperationContent({ card, className, onFileOpen }) {
|
|
|
3631
3883
|
}), output ? /* @__PURE__ */ jsx(StickyFileOperationScrollArea, {
|
|
3632
3884
|
resetKey: `output:${card.toolName}:${card.summary ?? "none"}`,
|
|
3633
3885
|
contentVersion: output,
|
|
3634
|
-
className: cn("max-h-56", blocks.length > 0 && "border-t border-
|
|
3886
|
+
className: cn("max-h-56", blocks.length > 0 && "border-t border-border"),
|
|
3635
3887
|
scrollKind: "output",
|
|
3636
3888
|
children: /* @__PURE__ */ jsx("div", {
|
|
3637
|
-
className: "overflow-x-auto custom-scrollbar
|
|
3889
|
+
className: "overflow-x-auto custom-scrollbar",
|
|
3638
3890
|
children: /* @__PURE__ */ jsx("pre", {
|
|
3639
|
-
className: "min-w-max whitespace-pre bg-
|
|
3891
|
+
className: "min-w-max whitespace-pre bg-card px-4 py-2 font-mono text-[11px] leading-5 text-foreground",
|
|
3640
3892
|
children: output
|
|
3641
3893
|
})
|
|
3642
3894
|
})
|
|
@@ -3752,16 +4004,16 @@ function useToolCardExpandedState({ canExpand, isRunning, autoExpandWhileRunning
|
|
|
3752
4004
|
function GenericToolSection({ label, tone, children }) {
|
|
3753
4005
|
const style = {
|
|
3754
4006
|
input: {
|
|
3755
|
-
shell: "border-
|
|
3756
|
-
header: "border-
|
|
3757
|
-
dot: "bg-
|
|
3758
|
-
body: "text-
|
|
4007
|
+
shell: "border-border bg-card",
|
|
4008
|
+
header: "border-border bg-muted/55 text-muted-foreground",
|
|
4009
|
+
dot: "bg-muted-foreground/60",
|
|
4010
|
+
body: "text-foreground"
|
|
3759
4011
|
},
|
|
3760
4012
|
output: {
|
|
3761
|
-
shell: "border-
|
|
3762
|
-
header: "border-
|
|
3763
|
-
dot: "bg-
|
|
3764
|
-
body: "text-
|
|
4013
|
+
shell: "border-border bg-card",
|
|
4014
|
+
header: "border-border bg-muted/55 text-muted-foreground",
|
|
4015
|
+
dot: "bg-primary/70",
|
|
4016
|
+
body: "text-foreground"
|
|
3765
4017
|
},
|
|
3766
4018
|
error: {
|
|
3767
4019
|
shell: "border-rose-200/80 bg-rose-50/85",
|
|
@@ -3778,7 +4030,7 @@ function GenericToolSection({ label, tone, children }) {
|
|
|
3778
4030
|
}), /* @__PURE__ */ jsx("div", {
|
|
3779
4031
|
className: "w-full overflow-hidden",
|
|
3780
4032
|
children: /* @__PURE__ */ jsx("pre", {
|
|
3781
|
-
className: cn("w-full max-w-full min-w-0 max-h-64 overflow-x-auto overflow-y-auto px-3 py-2.5 font-mono text-[12px] leading-relaxed whitespace-pre custom-scrollbar
|
|
4033
|
+
className: cn("w-full max-w-full min-w-0 max-h-64 overflow-x-auto overflow-y-auto px-3 py-2.5 font-mono text-[12px] leading-relaxed whitespace-pre custom-scrollbar", style.body),
|
|
3782
4034
|
children
|
|
3783
4035
|
})
|
|
3784
4036
|
})]
|
|
@@ -3803,23 +4055,23 @@ function TerminalExecutionView({ card }) {
|
|
|
3803
4055
|
canExpand: output.trim().length > 0 || isRunning,
|
|
3804
4056
|
onToggle
|
|
3805
4057
|
}), expanded && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
|
|
3806
|
-
className: "px-3 pb-2 font-mono w-full max-h-48 overflow-y-auto custom-scrollbar
|
|
4058
|
+
className: "px-3 pb-2 font-mono w-full max-h-48 overflow-y-auto custom-scrollbar min-h-0 text-[12px]",
|
|
3807
4059
|
children: /* @__PURE__ */ jsxs("div", {
|
|
3808
4060
|
className: "flex items-start gap-2 leading-relaxed",
|
|
3809
4061
|
children: [/* @__PURE__ */ jsx("span", {
|
|
3810
|
-
className: "text-
|
|
4062
|
+
className: "text-primary/55 font-medium shrink-0 select-none mt-[1px]",
|
|
3811
4063
|
children: "$"
|
|
3812
4064
|
}), /* @__PURE__ */ jsx("div", {
|
|
3813
4065
|
className: "flex-1 min-w-0",
|
|
3814
4066
|
children: commandPart ? /* @__PURE__ */ jsxs("div", {
|
|
3815
|
-
className: "text-
|
|
3816
|
-
children: [commandPart, isRunning && !output && /* @__PURE__ */ jsx("span", { className: "inline-block w-1.5 h-3 ml-1 bg-
|
|
3817
|
-
}) : /* @__PURE__ */ jsx("div", { className: "h-3 w-32 bg-
|
|
4067
|
+
className: "text-foreground break-words whitespace-pre-wrap tracking-tight font-medium inline-block",
|
|
4068
|
+
children: [commandPart, isRunning && !output && /* @__PURE__ */ jsx("span", { className: "inline-block w-1.5 h-3 ml-1 bg-primary/60 animate-pulse align-middle" })]
|
|
4069
|
+
}) : /* @__PURE__ */ jsx("div", { className: "h-3 w-32 bg-muted rounded animate-pulse mt-2" })
|
|
3818
4070
|
})]
|
|
3819
4071
|
})
|
|
3820
4072
|
}), output && /* @__PURE__ */ jsx(ToolCardContent, { children: /* @__PURE__ */ jsxs("pre", {
|
|
3821
|
-
className: "font-mono text-[12px] text-
|
|
3822
|
-
children: [output, isRunning && /* @__PURE__ */ jsx("span", { className: "inline-block w-1.5 h-3 ml-1 bg-
|
|
4073
|
+
className: "font-mono text-[12px] text-muted-foreground whitespace-pre-wrap break-all w-full max-w-full max-h-64 overflow-y-auto overflow-x-hidden min-w-0 custom-scrollbar leading-relaxed px-0",
|
|
4074
|
+
children: [output, isRunning && /* @__PURE__ */ jsx("span", { className: "inline-block w-1.5 h-3 ml-1 bg-primary/60 animate-pulse align-middle" })]
|
|
3823
4075
|
}) })] })] });
|
|
3824
4076
|
}
|
|
3825
4077
|
function FileOperationView({ card, onFileOpen }) {
|
|
@@ -3849,7 +4101,7 @@ function FileOperationView({ card, onFileOpen }) {
|
|
|
3849
4101
|
hideSummary: expanded && hasStructuredPreview,
|
|
3850
4102
|
onToggle
|
|
3851
4103
|
}), expanded && hasContent && /* @__PURE__ */ jsx(ToolCardContent, {
|
|
3852
|
-
className: "border-t border-
|
|
4104
|
+
className: "border-t border-border bg-transparent px-0 pt-0 pb-0",
|
|
3853
4105
|
children: /* @__PURE__ */ jsx(ToolCardFileOperationContent, {
|
|
3854
4106
|
card,
|
|
3855
4107
|
onFileOpen
|
|
@@ -3872,7 +4124,7 @@ function SearchSnippetView({ card }) {
|
|
|
3872
4124
|
canExpand: !!output || isRunning,
|
|
3873
4125
|
onToggle
|
|
3874
4126
|
}), expanded && output && /* @__PURE__ */ jsx(ToolCardContent, { children: /* @__PURE__ */ jsx("pre", {
|
|
3875
|
-
className: "font-mono text-[12px] text-
|
|
4127
|
+
className: "font-mono text-[12px] text-muted-foreground whitespace-pre-wrap break-all w-full max-w-full max-h-64 overflow-y-auto overflow-x-hidden min-w-0 custom-scrollbar leading-relaxed",
|
|
3876
4128
|
children: output
|
|
3877
4129
|
}) })] });
|
|
3878
4130
|
}
|
|
@@ -3971,29 +4223,21 @@ const ChatMessage = memo(function ChatMessage({ message, texts, onToolAction, on
|
|
|
3971
4223
|
const isUser = role === "user";
|
|
3972
4224
|
const isMessageInProgress = message.status === "pending" || message.status === "streaming";
|
|
3973
4225
|
return /* @__PURE__ */ jsx("div", {
|
|
3974
|
-
className: cn("inline-block w-fit max-w-full rounded-2xl border px-4 shadow-sm", isUser ? "border-primary bg-primary py-3 text-
|
|
4226
|
+
className: cn("inline-block w-fit max-w-full rounded-2xl border px-4 shadow-sm", isUser ? "border-primary bg-primary py-3 text-primary-foreground" : role === "assistant" ? "border-border bg-card pb-3 pt-4 text-card-foreground" : "border-border bg-muted/45 py-3 text-foreground"),
|
|
3975
4227
|
children: /* @__PURE__ */ jsx("div", {
|
|
3976
4228
|
className: "space-y-2",
|
|
3977
4229
|
children: message.parts.map((part, index) => {
|
|
3978
4230
|
const { type } = part;
|
|
3979
4231
|
if (type === "markdown") {
|
|
3980
|
-
const { text } = part;
|
|
4232
|
+
const { inlineTokens, text } = part;
|
|
3981
4233
|
return /* @__PURE__ */ jsx(ChatMessageMarkdown, {
|
|
3982
4234
|
text,
|
|
3983
4235
|
role,
|
|
3984
4236
|
texts,
|
|
4237
|
+
inlineTokens,
|
|
3985
4238
|
onFileOpen
|
|
3986
4239
|
}, `markdown-${index}`);
|
|
3987
4240
|
}
|
|
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
4241
|
if (type === "reasoning") {
|
|
3998
4242
|
const { label, text } = part;
|
|
3999
4243
|
return /* @__PURE__ */ jsx(ChatReasoningBlock, {
|
|
@@ -4027,16 +4271,16 @@ const ChatMessage = memo(function ChatMessage({ message, texts, onToolAction, on
|
|
|
4027
4271
|
if (type === "unknown") {
|
|
4028
4272
|
const { label, rawType, text } = part;
|
|
4029
4273
|
return /* @__PURE__ */ jsxs("div", {
|
|
4030
|
-
className: "rounded-lg border border-
|
|
4274
|
+
className: "rounded-lg border border-border bg-muted/60 px-2.5 py-2 text-xs text-muted-foreground",
|
|
4031
4275
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
4032
|
-
className: "font-semibold text-
|
|
4276
|
+
className: "font-semibold text-foreground",
|
|
4033
4277
|
children: [
|
|
4034
4278
|
label,
|
|
4035
4279
|
": ",
|
|
4036
4280
|
rawType
|
|
4037
4281
|
]
|
|
4038
4282
|
}), text ? /* @__PURE__ */ jsx("pre", {
|
|
4039
|
-
className: "mt-1 whitespace-pre-wrap break-words text-[11px] text-
|
|
4283
|
+
className: "mt-1 whitespace-pre-wrap break-words text-[11px] text-muted-foreground",
|
|
4040
4284
|
children: text
|
|
4041
4285
|
}) : null]
|
|
4042
4286
|
}, `unknown-${index}`);
|
|
@@ -4061,7 +4305,7 @@ function ChatMessageActionCopy({ message, texts }) {
|
|
|
4061
4305
|
return /* @__PURE__ */ jsx("button", {
|
|
4062
4306
|
type: "button",
|
|
4063
4307
|
onClick: () => void copy(),
|
|
4064
|
-
className: "text-
|
|
4308
|
+
className: "text-muted-foreground hover:text-foreground transition-colors p-1 rounded-md hover:bg-accent flex items-center justify-center",
|
|
4065
4309
|
"aria-label": copied ? texts.copiedMessageLabel : texts.copyMessageLabel,
|
|
4066
4310
|
title: copied ? texts.copiedMessageLabel : texts.copyMessageLabel,
|
|
4067
4311
|
children: copied ? /* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(Copy, { className: "h-3.5 w-3.5" })
|
|
@@ -4083,13 +4327,13 @@ const TYPING_TEXT_SHEEN_CSS = `
|
|
|
4083
4327
|
.nextclaw-chat-typing-indicator__text {
|
|
4084
4328
|
background-image: linear-gradient(
|
|
4085
4329
|
100deg,
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4330
|
+
hsl(var(--muted-foreground)) 0%,
|
|
4331
|
+
hsl(var(--muted-foreground)) 34%,
|
|
4332
|
+
hsl(var(--foreground-tertiary)) 43%,
|
|
4333
|
+
hsl(var(--foreground-muted)) 50%,
|
|
4334
|
+
hsl(var(--foreground-tertiary)) 57%,
|
|
4335
|
+
hsl(var(--muted-foreground)) 66%,
|
|
4336
|
+
hsl(var(--muted-foreground)) 100%
|
|
4093
4337
|
);
|
|
4094
4338
|
background-size: 240% 100%;
|
|
4095
4339
|
background-position: 160% 0;
|
|
@@ -4104,7 +4348,7 @@ const TYPING_TEXT_SHEEN_CSS = `
|
|
|
4104
4348
|
.nextclaw-chat-typing-indicator__text {
|
|
4105
4349
|
animation: none;
|
|
4106
4350
|
background-image: none;
|
|
4107
|
-
color:
|
|
4351
|
+
color: hsl(var(--muted-foreground));
|
|
4108
4352
|
-webkit-text-fill-color: currentColor;
|
|
4109
4353
|
}
|
|
4110
4354
|
}
|
|
@@ -4122,20 +4366,20 @@ function hasRenderableMessageContent(message) {
|
|
|
4122
4366
|
}
|
|
4123
4367
|
function ChatMessageTypingFooter() {
|
|
4124
4368
|
return /* @__PURE__ */ jsx("div", {
|
|
4125
|
-
className: "flex items-center gap-2 px-1 py-0.5 text-[11px] text-
|
|
4369
|
+
className: "flex items-center gap-2 px-1 py-0.5 text-[11px] text-muted-foreground",
|
|
4126
4370
|
children: /* @__PURE__ */ jsxs("div", {
|
|
4127
4371
|
className: "flex space-x-1 items-center h-full",
|
|
4128
4372
|
children: [
|
|
4129
|
-
/* @__PURE__ */ jsx("div", { className: "h-1.5 w-1.5 rounded-full bg-
|
|
4130
|
-
/* @__PURE__ */ jsx("div", { className: "h-1.5 w-1.5 rounded-full bg-
|
|
4131
|
-
/* @__PURE__ */ jsx("div", { className: "h-1.5 w-1.5 rounded-full bg-
|
|
4373
|
+
/* @__PURE__ */ jsx("div", { className: "h-1.5 w-1.5 rounded-full bg-muted-foreground animate-pulse" }),
|
|
4374
|
+
/* @__PURE__ */ jsx("div", { className: "h-1.5 w-1.5 rounded-full bg-muted-foreground animate-pulse [animation-delay:200ms]" }),
|
|
4375
|
+
/* @__PURE__ */ jsx("div", { className: "h-1.5 w-1.5 rounded-full bg-muted-foreground animate-pulse [animation-delay:400ms]" })
|
|
4132
4376
|
]
|
|
4133
4377
|
})
|
|
4134
4378
|
});
|
|
4135
4379
|
}
|
|
4136
4380
|
function ChatTypingIndicator({ label }) {
|
|
4137
4381
|
return /* @__PURE__ */ jsxs("div", {
|
|
4138
|
-
className: "rounded-2xl border border-
|
|
4382
|
+
className: "rounded-2xl border border-border bg-card px-4 py-3 text-sm text-muted-foreground shadow-sm",
|
|
4139
4383
|
children: [/* @__PURE__ */ jsx("style", { children: TYPING_TEXT_SHEEN_CSS }), /* @__PURE__ */ jsx("span", {
|
|
4140
4384
|
className: "nextclaw-chat-typing-indicator__text",
|
|
4141
4385
|
children: label
|
|
@@ -4166,7 +4410,7 @@ function ChatMessageList({ className, isSending, messages, onFileOpen, onToolAct
|
|
|
4166
4410
|
}), /* @__PURE__ */ jsx("div", {
|
|
4167
4411
|
className: cn("flex items-center gap-2", isUser && "justify-end"),
|
|
4168
4412
|
children: isGenerating ? /* @__PURE__ */ jsx(ChatMessageTypingFooter, {}) : /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("div", {
|
|
4169
|
-
className: cn("px-1 text-[11px] leading-4 text-
|
|
4413
|
+
className: cn("px-1 text-[11px] leading-4 text-muted-foreground", isUser ? "text-right" : "text-left"),
|
|
4170
4414
|
children: [
|
|
4171
4415
|
message.roleLabel,
|
|
4172
4416
|
" · ",
|
|
@@ -4188,6 +4432,54 @@ function ChatMessageList({ className, isSending, messages, onFileOpen, onToolAct
|
|
|
4188
4432
|
});
|
|
4189
4433
|
}
|
|
4190
4434
|
//#endregion
|
|
4191
|
-
|
|
4435
|
+
//#region src/lib/input-surface/input-surface-plugin.utils.ts
|
|
4436
|
+
function createInputSurfaceTriggeredPanelPlugin(params) {
|
|
4437
|
+
return {
|
|
4438
|
+
key: params.key,
|
|
4439
|
+
triggerSpecs: [params.trigger],
|
|
4440
|
+
resolvePanel: (context) => context.trigger.key === params.trigger.key ? params.resolvePanel(context) : null
|
|
4441
|
+
};
|
|
4442
|
+
}
|
|
4443
|
+
function createInputSurfaceReferenceTokenPlugin(params) {
|
|
4444
|
+
return createInputSurfaceTriggeredPanelPlugin({
|
|
4445
|
+
key: params.key,
|
|
4446
|
+
trigger: params.trigger,
|
|
4447
|
+
resolvePanel: (context) => ({
|
|
4448
|
+
isLoading: params.getIsLoading?.(context) ?? false,
|
|
4449
|
+
items: params.getItems({
|
|
4450
|
+
context,
|
|
4451
|
+
records: params.getRecords(context),
|
|
4452
|
+
query: context.trigger.query,
|
|
4453
|
+
tokenKind: params.tokenKind
|
|
4454
|
+
}),
|
|
4455
|
+
onSelectItem: params.onSelectItem,
|
|
4456
|
+
texts: params.texts
|
|
4457
|
+
})
|
|
4458
|
+
});
|
|
4459
|
+
}
|
|
4460
|
+
function resolveChatInputSurfaceState(params) {
|
|
4461
|
+
const { data, plugins, trigger } = params;
|
|
4462
|
+
const triggerSpecs = plugins.flatMap((plugin) => [...plugin.triggerSpecs]);
|
|
4463
|
+
if (!trigger) return {
|
|
4464
|
+
triggerSpecs,
|
|
4465
|
+
panel: null
|
|
4466
|
+
};
|
|
4467
|
+
for (const plugin of plugins) {
|
|
4468
|
+
const panel = plugin.resolvePanel({
|
|
4469
|
+
data,
|
|
4470
|
+
trigger
|
|
4471
|
+
});
|
|
4472
|
+
if (panel) return {
|
|
4473
|
+
triggerSpecs,
|
|
4474
|
+
panel
|
|
4475
|
+
};
|
|
4476
|
+
}
|
|
4477
|
+
return {
|
|
4478
|
+
triggerSpecs,
|
|
4479
|
+
panel: null
|
|
4480
|
+
};
|
|
4481
|
+
}
|
|
4482
|
+
//#endregion
|
|
4483
|
+
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
4484
|
|
|
4193
4485
|
//# sourceMappingURL=index.js.map
|