@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.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-gray-200/80 bg-white px-3.5 py-2 text-sm text-gray-900 file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-gray-300 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),
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-gray-200/50 bg-white p-4 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=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", className),
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-gray-200/80 bg-white px-3 py-2 text-sm 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),
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-white 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),
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-gray-500", className),
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-gray-100 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 hover:bg-gray-100", className),
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-gray-100", className),
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/chat-input-bar/chat-slash-menu.tsx
224
- const SLASH_PANEL_MAX_WIDTH = 680;
225
- const SLASH_PANEL_DESKTOP_SHRINK_RATIO = .82;
226
- const SLASH_PANEL_DESKTOP_MIN_WIDTH = 560;
227
- const SLASH_PANEL_MAX_HEIGHT = createChatPopoverAvailableHeightLimit("24rem");
228
- const SLASH_PANEL_MIN_HEIGHT = createChatPopoverAvailableHeightLimit("240px");
229
- function ChatSlashMenu(props) {
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 { isOpen, isLoading, items, activeIndex, activeItem, texts, onSelectItem, onOpenChange, onDetailsPointerDown, onSetActiveIndex } = props;
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 > SLASH_PANEL_DESKTOP_MIN_WIDTH ? panelWidth * SLASH_PANEL_DESKTOP_SHRINK_RATIO : panelWidth, SLASH_PANEL_MAX_WIDTH);
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-slash-index="${index}"]`
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-full left-3 right-3 h-0"
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: SLASH_PANEL_MAX_HEIGHT,
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: SLASH_PANEL_MIN_HEIGHT },
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.slashSectionLabel,
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.slashLoadingLabel
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.slashSectionLabel
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.slashEmptyLabel
503
+ children: texts.emptyLabel
281
504
  }) : /* @__PURE__ */ jsx("div", {
282
505
  className: "space-y-1",
283
506
  children: items.map((item, index) => {
284
- const isActive = index === activeIndex;
285
- return /* @__PURE__ */ jsxs("button", {
286
- type: "button",
287
- role: "option",
288
- "aria-selected": isActive,
289
- "data-slash-index": index,
290
- onMouseEnter: () => onSetActiveIndex(index),
291
- onClick: () => onSelectItem(item),
292
- 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"}`,
293
- children: [/* @__PURE__ */ jsx("span", {
294
- className: "truncate text-xs font-semibold",
295
- children: item.title
296
- }), /* @__PURE__ */ jsx("span", {
297
- className: "truncate text-xs text-gray-500",
298
- children: item.subtitle
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
- }, item.key);
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.slashSkillHintLabel
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.slashHintLabel
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-gray-200 bg-white hover:bg-gray-50 hover:text-gray-800 text-gray-600",
352
- secondary: "bg-gray-100 text-gray-700 hover:bg-gray-200/80",
353
- ghost: "hover:bg-gray-100/80 hover:text-gray-800",
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-gray-100 text-gray-600 hover:bg-gray-200/80",
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-gray-700 shadow-[inset_0_0_0_1px_rgba(17,24,39,0.06)]"
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 = "#9ca3af";
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-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-700",
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, #e5e7eb 0deg)` }
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-white"
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-gray-800",
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-gray-600",
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-gray-800",
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-gray-600 transition-colors hover:bg-gray-100 hover:text-gray-900 sm:w-auto sm:gap-1.5 sm:px-3",
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-gray-100 px-4 py-3",
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-gray-900",
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-gray-400" }), /* @__PURE__ */ jsx(Input, {
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-gray-500",
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-gray-500",
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-gray-500",
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-gray-50" : "hover:bg-gray-50"}`,
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-gray-100 text-gray-500",
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-gray-900",
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-gray-500",
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-gray-300 bg-white",
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-gray-100 px-4 py-2.5",
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-gray-500" }) : null;
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-gray-700 sm:hidden [@container_nextclaw-chat-input-bar_(max-width:440px)]:hidden",
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-gray-700 sm:inline [@container_nextclaw-chat-input-bar_(max-width:440px)]:hidden",
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-gray-200" });
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-gray-800",
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-gray-500",
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-gray-800",
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-gray-600 shadow-none hover:bg-gray-100 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] ?? ""}`,
814
- children: [/* @__PURE__ */ jsx(ToolbarSelectTriggerContent, { item }), /* @__PURE__ */ jsx(ChevronDown, { className: "ml-1 h-3.5 w-3.5 shrink-0 text-gray-400 [@container_nextclaw-chat-input-bar_(max-width:440px)]:hidden" })]
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-gray-400" }), /* @__PURE__ */ jsx(Input, {
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-gray-200" }),
833
- /* @__PURE__ */ jsx("div", { className: "h-3 w-28 animate-pulse rounded bg-gray-200" }),
834
- /* @__PURE__ */ jsx("div", { className: "h-3 w-32 animate-pulse rounded bg-gray-200" })
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-gray-500",
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-gray-500",
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-gray-100 pt-1" : void 0,
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-gray-500",
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-gray-100",
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-gray-400 transition-colors hover:bg-white hover:text-amber-500 focus:outline-none focus:ring-1 focus:ring-primary/40",
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-gray-600 shadow-none hover:bg-gray-100 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] ?? ""}`,
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-gray-200" }),
918
- /* @__PURE__ */ jsx("div", { className: "h-3 w-28 animate-pulse rounded bg-gray-200" }),
919
- /* @__PURE__ */ jsx("div", { className: "h-3 w-32 animate-pulse rounded bg-gray-200" })
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-gray-500",
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-gray-600 transition-colors hover:bg-gray-100 hover:text-gray-900 disabled:cursor-not-allowed disabled:text-gray-400 ${item.iconOnly ? "h-8 w-8 justify-center px-0" : "gap-1.5 px-3"}`,
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__ */ jsxs("svg", {
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
- children: [
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
- children: [
1181
- /* @__PURE__ */ jsx("path", { d: "M8.5 2.75 2.75 6l5.75 3.25L14.25 6 8.5 2.75Z" }),
1182
- /* @__PURE__ */ jsx("path", { d: "M2.75 10 8.5 13.25 14.25 10" }),
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.id}:${node.text}` : `token:${node.id}:${node.tokenKind}:${node.tokenKey}:${node.label}`).join("");
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 { activeSlashIndex, canStopGeneration, isComposing, isSending, isSlashMenuOpen, key, shiftKey, slashItemCount } = params;
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
- publishSnapshot(getChatComposerContentSignature(editorSnapshot.nodes) !== getChatComposerContentSignature(currentSnapshot.nodes) ? editorSnapshot : data.length > 0 ? replaceChatComposerSelectionWithText({
1721
- nodes: currentSnapshot.nodes,
1722
- selection: currentSnapshot.selection,
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, { forcePublish: true });
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, activeSlashIndex, nativeEvent, onSlashActiveIndexChange, onSlashItemSelect, onSlashOpenChange, onSlashQueryChange, publishSnapshot, slashItems, snapshot } = params;
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, editorRef, editorSignatureRef, isApplyingExternalUpdateRef, isComposingRef, lastPublishedSignatureRef, nodes, onBlur, onKeyDown, onNodesChange, pendingSelectionRef, selectionRef, shouldFocusAfterSyncRef, syncSlashState }) {
2150
+ function ChatComposerBindingsPlugin({ disabled, nodes, owner }) {
1785
2151
  const [editor] = useLexicalComposerContext();
1786
2152
  useLayoutEffect(() => {
1787
- editorRef.current = editor;
1788
- return () => {
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
- }, [disabled, editor]);
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
- pendingSelectionRef,
1827
- selectionRef,
1828
- shouldFocusAfterSyncRef
2162
+ owner
1829
2163
  ]);
1830
2164
  useEffect(() => {
1831
- return mergeRegister(editor.registerUpdateListener(({ editorState }) => {
1832
- const snapshot = readChatComposerSnapshotFromEditorState(editorState);
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 getChatComposerDocumentLength(nodes) {
1867
- return nodes.reduce((cursor, node) => cursor + (node.type === "text" ? node.text.length : 1), 0);
1868
- }
1869
- const ChatInputBarTokenizedComposer = forwardRef(function ChatInputBarTokenizedComposer({ actions, activeSlashIndex, disabled, nodes, onFilesAdd, onNodesChange, onSlashActiveIndexChange, onSlashItemSelect, onSlashOpenChange, onSlashQueryChange, onSlashTriggerChange, placeholder, slashItems }, ref) {
1870
- const editorRef = useRef(null);
1871
- const selectionRef = useRef(null);
1872
- const pendingSelectionRef = useRef(null);
1873
- const shouldFocusAfterSyncRef = useRef(false);
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
- focusComposer,
1954
- focusComposerAtEnd,
1955
- onSlashItemSelect,
1956
- publishSnapshot,
1957
- readComposerSnapshot
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
- /* @__PURE__ */ jsx("div", {
1974
- className: "px-3 py-2 sm:px-4 sm:py-2.5",
1975
- children: /* @__PURE__ */ jsx("div", {
1976
- className: "min-h-11 sm:min-h-[60px]",
1977
- children: /* @__PURE__ */ jsx(PlainTextPlugin, {
1978
- contentEditable: /* @__PURE__ */ jsx(ContentEditable, {
1979
- 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-gray-800 outline-none",
1980
- onBeforeInput: (event) => {
1981
- handleLexicalComposerBeforeInput({
1982
- disabled,
1983
- event,
1984
- isComposing: isComposingRef.current,
1985
- publishSnapshot,
1986
- snapshotReader: readComposerSnapshot
1987
- });
1988
- },
1989
- onCompositionEnd: (event) => {
1990
- isComposingRef.current = false;
1991
- const nativeEvent = event.nativeEvent;
1992
- handleLexicalComposerCompositionEnd({
1993
- data: typeof nativeEvent.data === "string" ? nativeEvent.data : "",
1994
- fallbackSnapshot: () => {
1995
- const editor = editorRef.current;
1996
- return editor ? readChatComposerSnapshotFromEditorState(editor.getEditorState()) : readComposerSnapshot();
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
- placeholder: /* @__PURE__ */ jsx("div", {
2014
- className: "pointer-events-none absolute left-3 top-2 select-none text-sm leading-6 text-gray-400 sm:left-4 sm:top-2.5",
2015
- children: placeholder
2016
- }),
2017
- ErrorBoundary: LexicalErrorBoundary
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-gray-200 bg-gray-50 px-3 py-2",
2070
- children: [/* @__PURE__ */ jsx("span", { className: "h-3 w-28 animate-pulse rounded bg-gray-200" }), /* @__PURE__ */ jsx("span", { className: "h-3 w-16 animate-pulse rounded bg-gray-200" })]
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-gray-200 bg-gray-50 text-gray-700"}`,
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 isSlashMenuInteractionRef = useRef(false);
2089
- const [activeSlashIndex, setActiveSlashIndex] = useState(0);
2090
- const [activeSlashTriggerStart, setActiveSlashTriggerStart] = useState(null);
2091
- const [dismissedSlashTriggerStart, setDismissedSlashTriggerStart] = useState(null);
2092
- const isSlashPanelOpen = activeSlashTriggerStart !== null && dismissedSlashTriggerStart !== activeSlashTriggerStart;
2093
- const activeSlashIndexInRange = slashMenu.items.length === 0 ? 0 : Math.min(activeSlashIndex, slashMenu.items.length - 1);
2094
- const activeSlashItem = slashMenu.items[activeSlashIndexInRange] ?? null;
2095
- const dismissSlashTrigger = () => activeSlashTriggerStart !== null && !isSlashMenuInteractionRef.current ? setDismissedSlashTriggerStart(activeSlashTriggerStart) : void 0;
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" : "border-t border-gray-200/80 bg-white px-3 py-3 sm:px-4 sm:py-4",
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-gray-200 bg-white shadow-card",
2310
+ className: "overflow-hidden rounded-2xl border border-border bg-card shadow-card",
2121
2311
  children: [
2122
- /* @__PURE__ */ jsxs("div", {
2312
+ /* @__PURE__ */ jsx("div", {
2123
2313
  className: "relative",
2124
- children: [/* @__PURE__ */ jsx(ChatInputBarTokenizedComposer, {
2125
- ref: composerRef,
2126
- nodes: composer.nodes,
2127
- placeholder: composer.placeholder,
2128
- disabled: composer.disabled,
2129
- slashItems: slashMenu.items,
2130
- onSlashItemSelect: slashMenu.onSelectItem,
2131
- actions: toolbarProps.actions,
2132
- activeSlashIndex: activeSlashIndexInRange,
2133
- onNodesChange: composer.onNodesChange,
2134
- onFilesAdd: composer.onFilesAdd,
2135
- onSlashQueryChange: (query) => {
2136
- if (query === null && isSlashMenuInteractionRef.current) return;
2137
- if (query !== null) setActiveSlashIndex(0);
2138
- composer.onSlashQueryChange?.(query);
2139
- },
2140
- onSlashTriggerChange: (trigger) => {
2141
- const nextTriggerStart = trigger?.start ?? null;
2142
- setActiveSlashTriggerStart(nextTriggerStart);
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-white shadow-sm",
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-amber-100 text-amber-700 shadow-sm",
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-gradient-to-b from-gray-700 to-gray-950 text-white shadow-md ring-1 ring-inset ring-white/20",
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-white/95",
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 ChatMessageMarkdown({ text, role, texts, inline = false, onFileOpen }) {
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: [remarkGfm],
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-amber-200/80 bg-amber-50 text-amber-700",
2707
- audio: "border-fuchsia-200/80 bg-fuchsia-50 text-fuchsia-700",
2708
- code: "border-cyan-200/80 bg-cyan-50 text-cyan-700",
2709
- data: "border-slate-200/80 bg-slate-50 text-slate-700",
2710
- document: "border-blue-200/80 bg-blue-50 text-blue-700",
2711
- generic: "border-slate-200/80 bg-slate-50 text-slate-700",
2712
- image: "border-emerald-200/80 bg-emerald-50 text-emerald-700",
2713
- pdf: "border-rose-200/80 bg-rose-50 text-rose-700",
2714
- sheet: "border-lime-200/80 bg-lime-50 text-lime-700",
2715
- video: "border-violet-200/80 bg-violet-50 text-violet-700"
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-white/70" : "text-slate-500"),
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-white/14 bg-white/12 text-white" : "border-slate-200/80 bg-white text-slate-700" : isUser ? "border-white/10 bg-white/6 text-white/62" : "border-slate-200/70 bg-slate-100/80 text-slate-500"),
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-white/14 bg-white/12 text-white hover:border-white/20 hover:bg-white/16" : "border-slate-200/80 bg-white text-slate-700 hover:border-slate-300 hover:bg-slate-50"),
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-white/12 bg-white/10 text-white" : FILE_CATEGORY_TILE_CLASSES[category]),
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-white/92" : "text-current"),
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-white/10" : "bg-slate-100/80 ring-1 ring-slate-200/80"),
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-slate-950/36" : "bg-slate-950/58"),
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-slate-950/28" : "bg-slate-950/46"),
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-white/10 bg-slate-950/26" : "border-slate-200/80 bg-slate-100/80"),
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-white/12 bg-white/10 text-white" : "border-slate-200/80 bg-white/95 text-slate-900", isInteractive && (isUser ? "hover:border-white/18 hover:bg-white/13" : "hover:border-slate-300 hover:bg-white"));
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-gray-500"),
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-amber", isUser ? "bg-primary-700/60" : "bg-gray-100"),
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-amber-200/50 bg-amber-100/30 shadow-sm overflow-hidden text-[12px]", "w-[280px] sm:w-[360px] md:w-[480px] min-w-full max-w-full transition-all flex flex-col", className),
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-amber-200/15 bg-amber-50/50 px-3 pt-1 pb-2 w-full overflow-hidden", className),
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-amber-500/80",
3493
+ text: "text-primary/70",
3242
3494
  icon: Loader2,
3243
3495
  spin: true
3244
3496
  },
3245
3497
  success: {
3246
- text: "text-amber-500/80",
3498
+ text: "text-primary/70",
3247
3499
  icon: Check,
3248
3500
  spin: false
3249
3501
  },
3250
3502
  error: {
3251
- text: "text-amber-500/80",
3503
+ text: "text-destructive",
3252
3504
  icon: AlertTriangle,
3253
3505
  spin: false
3254
3506
  },
3255
3507
  cancelled: {
3256
- text: "text-amber-500/80",
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-sky-200/80 bg-white/85 text-sky-800 hover:bg-sky-50 focus-visible:ring-sky-300"
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-amber-200/80 bg-white/80 text-amber-800 hover:bg-amber-50 focus-visible:ring-amber-300"
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-amber-100/30" : ""),
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-amber-950/80",
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-amber-600/80 shrink-0",
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-amber-300 font-bold select-none shrink-0",
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-amber-400/80",
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-amber-400/80",
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-stone-200 bg-stone-100 text-stone-500";
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-white text-amber-950/80";
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-white",
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-stone-200 bg-stone-100" })]
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-white",
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-white" })]
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-amber bg-white",
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-stone-500";
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-stone-300",
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-white custom-scrollbar-amber", className),
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-white", !isFirst && "border-t border-stone-200/80"),
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-stone-200/80 px-4 text-stone-700", showPathRow ? "py-2" : "py-1.5"),
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-stone-700", onFileOpen && "transition-colors hover:text-amber-900 hover:underline"),
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-amber",
3856
+ className: "overflow-x-auto custom-scrollbar",
3605
3857
  children: /* @__PURE__ */ jsx("pre", {
3606
- className: "min-w-max whitespace-pre bg-white px-4 py-2 font-mono text-[11px] leading-5 text-amber-950/80",
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-stone-200/85 bg-stone-50 px-4 py-2 text-[10px] text-stone-500",
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-white", className),
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-stone-200/80"),
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-amber",
3889
+ className: "overflow-x-auto custom-scrollbar",
3638
3890
  children: /* @__PURE__ */ jsx("pre", {
3639
- className: "min-w-max whitespace-pre bg-white px-4 py-2 font-mono text-[11px] leading-5 text-amber-950/80",
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-stone-200/80 bg-stone-50/90",
3756
- header: "border-stone-200/80 bg-stone-100/85 text-stone-500",
3757
- dot: "bg-stone-400/80",
3758
- body: "text-stone-700"
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-amber-200/70 bg-white/90",
3762
- header: "border-amber-200/70 bg-amber-50/90 text-amber-700",
3763
- dot: "bg-amber-500/80",
3764
- body: "text-amber-950/80"
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-amber", style.body),
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-amber min-h-0 text-[12px]",
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-amber-500/50 font-medium shrink-0 select-none mt-[1px]",
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-amber-950/80 break-words whitespace-pre-wrap tracking-tight font-medium inline-block",
3816
- children: [commandPart, isRunning && !output && /* @__PURE__ */ jsx("span", { className: "inline-block w-1.5 h-3 ml-1 bg-amber-500/60 animate-pulse align-middle" })]
3817
- }) : /* @__PURE__ */ jsx("div", { className: "h-3 w-32 bg-amber-200/30 rounded animate-pulse mt-2" })
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-amber-950/70 whitespace-pre-wrap break-all w-full max-w-full max-h-64 overflow-y-auto overflow-x-hidden min-w-0 custom-scrollbar-amber leading-relaxed px-0",
3822
- children: [output, isRunning && /* @__PURE__ */ jsx("span", { className: "inline-block w-1.5 h-3 ml-1 bg-amber-500/60 animate-pulse align-middle" })]
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-amber-200/20 bg-transparent px-0 pt-0 pb-0",
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-amber-950/70 whitespace-pre-wrap break-all w-full max-w-full max-h-64 overflow-y-auto overflow-x-hidden min-w-0 custom-scrollbar-amber leading-relaxed",
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-white" : role === "assistant" ? "border-gray-200 bg-white pb-3 pt-4 text-gray-900" : "border-orange-200/80 bg-orange-50/70 py-3 text-gray-900"),
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-gray-200 bg-gray-50 px-2.5 py-2 text-xs text-gray-600",
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-gray-700",
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-gray-500",
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-gray-400 hover:text-gray-600 transition-colors p-1 rounded-md hover:bg-gray-100 flex items-center justify-center",
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
- #6b7280 0%,
4087
- #6b7280 34%,
4088
- #a6adba 43%,
4089
- #f8fafc 50%,
4090
- #a6adba 57%,
4091
- #6b7280 66%,
4092
- #6b7280 100%
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: #6b7280;
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-gray-400",
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-gray-400 animate-pulse" }),
4130
- /* @__PURE__ */ jsx("div", { className: "h-1.5 w-1.5 rounded-full bg-gray-400 animate-pulse [animation-delay:200ms]" }),
4131
- /* @__PURE__ */ jsx("div", { className: "h-1.5 w-1.5 rounded-full bg-gray-400 animate-pulse [animation-delay:400ms]" })
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-gray-200 bg-white px-4 py-3 text-sm text-gray-500 shadow-sm",
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-gray-400", isUser ? "text-right" : "text-left"),
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
- export { ChatInputBar, ChatMessageList, ChatMessageMarkdown, FileOperationCodeSurface, FileOperationLinesGrid, copyText, createChatComposerNodesFromText, createChatComposerTextNode, createChatComposerTokenNode, createEmptyChatComposerNodes, extractChatComposerTokenKeys, normalizeChatComposerNodes, removeChatComposerTokenNodes, replaceChatComposerRange, resolveChatComposerSlashTrigger, serializeChatComposerDocument, serializeChatComposerPlainText, useActiveItemScroll, useCopyFeedback, useElementWidth, useStickyBottomScroll };
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