@nextclaw/agent-chat-ui 0.6.5 → 0.6.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +18 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +392 -367
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { Fragment, createContext, forwardRef, memo, useCallback, useContext, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { AlertTriangle, AppWindow, ArrowLeft, ArrowUp, ArrowUpRight, Bot, Brain, BrainCircuit, CalendarClock, Check, ChevronDown, ChevronRight, ChevronUp, CircleAlert, Code2, Command, Copy, Expand, ExternalLink, Eye, File, FileArchive, FileAudio2, FileCode2, FileImage, FileJson2, FileSpreadsheet, FileText, FileVideo2, Files, Folder, FolderTree, Globe, Globe2, ImageIcon, ListChecks, Loader2, MessageSquare, Minus, PanelsTopLeft, Paperclip, Puzzle, Search, Send, Settings, Sparkles, Star, Terminal, User, Workflow, Wrench, X } from "lucide-react";
|
|
3
4
|
import { clsx } from "clsx";
|
|
4
5
|
import { twMerge } from "tailwind-merge";
|
|
5
6
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
6
7
|
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
7
8
|
import * as SelectPrimitive from "@radix-ui/react-select";
|
|
8
|
-
import { AlertTriangle, AppWindow, ArrowUp, ArrowUpRight, Bot, Brain, BrainCircuit, CalendarClock, Check, ChevronDown, ChevronRight, ChevronUp, Code2, Copy, Expand, ExternalLink, Eye, File, FileArchive, FileAudio2, FileCode2, FileImage, FileJson2, FileSpreadsheet, FileText, FileVideo2, FolderTree, Globe, Globe2, ImageIcon, ListChecks, Loader2, MessageSquare, Minus, Paperclip, Puzzle, Search, Send, Settings, Sparkles, Star, Terminal, User, Workflow, Wrench, X } 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
13
|
import { LexicalErrorBoundary } from "@lexical/react/LexicalErrorBoundary";
|
|
14
14
|
import { PlainTextPlugin } from "@lexical/react/LexicalPlainTextPlugin";
|
|
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, SKIP_DOM_SELECTION_TAG, mergeRegister } from "lexical";
|
|
15
|
+
import { $applyNodeReplacement, $createLineBreakNode, $createParagraphNode, $createRangeSelection, $createTextNode, $getRoot, $getSelection, $isElementNode, $isRangeSelection, $isTextNode, $setSelection, BLUR_COMMAND, COMMAND_PRIORITY_EDITOR, COMMAND_PRIORITY_HIGH, COMPOSITION_END_TAG, DecoratorNode, KEY_DOWN_COMMAND, SELECTION_CHANGE_COMMAND, SKIP_DOM_SELECTION_TAG, mergeRegister } from "lexical";
|
|
16
16
|
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
|
|
17
17
|
import ReactMarkdown from "react-markdown";
|
|
18
18
|
import remarkGfm from "remark-gfm";
|
|
@@ -394,16 +394,142 @@ const ChatUiPrimitives = {
|
|
|
394
394
|
TooltipTrigger: ChatTooltipTrigger
|
|
395
395
|
};
|
|
396
396
|
//#endregion
|
|
397
|
+
//#region src/components/chat/ui/input-surface/chat-input-surface-path-preview.tsx
|
|
398
|
+
function ChatInputSurfacePathPreview({ pathPreview }) {
|
|
399
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
400
|
+
className: "rounded-lg border border-gray-200 bg-gray-50/70 px-3 py-2.5",
|
|
401
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
402
|
+
className: "flex items-center gap-2 text-xs font-medium text-gray-600",
|
|
403
|
+
children: [/* @__PURE__ */ jsx(Folder, {
|
|
404
|
+
"aria-hidden": "true",
|
|
405
|
+
className: "h-4 w-4 text-gray-400"
|
|
406
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
407
|
+
className: "truncate",
|
|
408
|
+
children: pathPreview.rootLabel
|
|
409
|
+
})]
|
|
410
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
411
|
+
className: "mt-2",
|
|
412
|
+
children: pathPreview.segments.map((segment, index) => {
|
|
413
|
+
const Icon = segment.kind === "directory" ? Folder : File;
|
|
414
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
415
|
+
className: "relative flex min-w-0 items-center gap-2 py-1 text-xs text-gray-700",
|
|
416
|
+
style: { paddingLeft: `${(index + 1) * 18}px` },
|
|
417
|
+
children: [
|
|
418
|
+
/* @__PURE__ */ jsx("span", {
|
|
419
|
+
"aria-hidden": "true",
|
|
420
|
+
className: "absolute bottom-1/2 top-[-0.25rem] w-px bg-gray-200",
|
|
421
|
+
style: { left: `${index * 18 + 7}px` }
|
|
422
|
+
}),
|
|
423
|
+
/* @__PURE__ */ jsx(Icon, {
|
|
424
|
+
"aria-hidden": "true",
|
|
425
|
+
className: "h-4 w-4 shrink-0 text-gray-400"
|
|
426
|
+
}),
|
|
427
|
+
/* @__PURE__ */ jsx("span", {
|
|
428
|
+
className: "truncate font-medium",
|
|
429
|
+
children: segment.label
|
|
430
|
+
})
|
|
431
|
+
]
|
|
432
|
+
}, `${index}:${segment.label}`);
|
|
433
|
+
})
|
|
434
|
+
})]
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
//#endregion
|
|
397
438
|
//#region src/components/chat/ui/input-surface/chat-input-surface-menu.tsx
|
|
398
439
|
const INPUT_SURFACE_PANEL_MAX_WIDTH = 680;
|
|
399
440
|
const INPUT_SURFACE_PANEL_DESKTOP_SHRINK_RATIO = .82;
|
|
400
441
|
const INPUT_SURFACE_PANEL_DESKTOP_MIN_WIDTH = 560;
|
|
401
442
|
const INPUT_SURFACE_PANEL_MAX_HEIGHT = createChatPopoverAvailableHeightLimit("24rem");
|
|
402
443
|
const INPUT_SURFACE_PANEL_MIN_HEIGHT = createChatPopoverAvailableHeightLimit("240px");
|
|
444
|
+
const INPUT_SURFACE_ITEM_ICONS = {
|
|
445
|
+
back: ArrowLeft,
|
|
446
|
+
command: Command,
|
|
447
|
+
file: File,
|
|
448
|
+
files: Files,
|
|
449
|
+
folder: Folder,
|
|
450
|
+
"panel-app": PanelsTopLeft,
|
|
451
|
+
skill: Puzzle
|
|
452
|
+
};
|
|
403
453
|
function itemMatchesFilter(item, filter) {
|
|
404
454
|
if (!filter.sectionKeys || filter.sectionKeys.length === 0) return true;
|
|
405
455
|
return Boolean(item.sectionKey && filter.sectionKeys.includes(item.sectionKey));
|
|
406
456
|
}
|
|
457
|
+
function resolveActiveFilterKey(filters, activeFilterKey) {
|
|
458
|
+
if (!filters?.length) return null;
|
|
459
|
+
return filters.some((filter) => filter.key === activeFilterKey) ? activeFilterKey : filters[0].key;
|
|
460
|
+
}
|
|
461
|
+
function InputSurfaceItemIcon({ icon }) {
|
|
462
|
+
const Icon = INPUT_SURFACE_ITEM_ICONS[icon];
|
|
463
|
+
return /* @__PURE__ */ jsx("span", {
|
|
464
|
+
"aria-hidden": "true",
|
|
465
|
+
"data-input-surface-icon": icon,
|
|
466
|
+
className: "inline-flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-gray-100 text-gray-500",
|
|
467
|
+
children: /* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5" })
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
function InputSurfaceItemDetails({ item, texts }) {
|
|
471
|
+
if (!item) return /* @__PURE__ */ jsx("div", {
|
|
472
|
+
className: "text-xs text-gray-500",
|
|
473
|
+
children: texts.hintLabel
|
|
474
|
+
});
|
|
475
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
476
|
+
className: "space-y-3",
|
|
477
|
+
children: [
|
|
478
|
+
/* @__PURE__ */ jsxs("div", {
|
|
479
|
+
className: "flex items-center gap-2",
|
|
480
|
+
children: [item.subtitle ? /* @__PURE__ */ jsx("span", {
|
|
481
|
+
className: "inline-flex rounded-full bg-primary/10 px-2 py-0.5 text-[11px] font-semibold text-primary",
|
|
482
|
+
children: item.subtitle
|
|
483
|
+
}) : null, /* @__PURE__ */ jsx("span", {
|
|
484
|
+
className: "text-sm font-semibold text-gray-900",
|
|
485
|
+
children: item.title
|
|
486
|
+
})]
|
|
487
|
+
}),
|
|
488
|
+
/* @__PURE__ */ jsx("p", {
|
|
489
|
+
className: "text-xs leading-5 text-gray-600",
|
|
490
|
+
children: item.description
|
|
491
|
+
}),
|
|
492
|
+
item.pathPreview ? /* @__PURE__ */ jsx(ChatInputSurfacePathPreview, { pathPreview: item.pathPreview }) : null,
|
|
493
|
+
/* @__PURE__ */ jsx("div", {
|
|
494
|
+
className: "space-y-1",
|
|
495
|
+
children: item.detailLines.map((line) => /* @__PURE__ */ jsx("div", {
|
|
496
|
+
className: "min-w-0 break-all rounded-md bg-gray-50 px-2 py-1 text-[11px] leading-5 text-gray-600",
|
|
497
|
+
children: line
|
|
498
|
+
}, line))
|
|
499
|
+
}),
|
|
500
|
+
/* @__PURE__ */ jsx("div", {
|
|
501
|
+
className: "pt-1 text-[11px] text-gray-500",
|
|
502
|
+
children: item.hintLabel ?? texts.itemHintLabel
|
|
503
|
+
})
|
|
504
|
+
]
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
function handleInputSurfaceMenuKeyDown(params) {
|
|
508
|
+
const { activeIndex, event, isOpen, items, onOpenChange, onSelectItem, setActiveIndex } = params;
|
|
509
|
+
if (!isOpen) return false;
|
|
510
|
+
if (event.key === "Escape") {
|
|
511
|
+
event.preventDefault();
|
|
512
|
+
onOpenChange(false);
|
|
513
|
+
return true;
|
|
514
|
+
}
|
|
515
|
+
if (items.length === 0) return false;
|
|
516
|
+
if (event.key === "ArrowDown") {
|
|
517
|
+
event.preventDefault();
|
|
518
|
+
setActiveIndex((index) => (index + 1) % items.length);
|
|
519
|
+
return true;
|
|
520
|
+
}
|
|
521
|
+
if (event.key === "ArrowUp") {
|
|
522
|
+
event.preventDefault();
|
|
523
|
+
setActiveIndex((index) => (index - 1 + items.length) % items.length);
|
|
524
|
+
return true;
|
|
525
|
+
}
|
|
526
|
+
if (event.key === "Enter" && !event.shiftKey || event.key === "Tab") {
|
|
527
|
+
event.preventDefault();
|
|
528
|
+
onSelectItem(items[activeIndex]);
|
|
529
|
+
return true;
|
|
530
|
+
}
|
|
531
|
+
return false;
|
|
532
|
+
}
|
|
407
533
|
const ChatInputSurfaceMenu = forwardRef(function ChatInputSurfaceMenu(props, ref) {
|
|
408
534
|
const { Popover, PopoverAnchor, PopoverContent } = ChatUiPrimitives;
|
|
409
535
|
const { elementRef: anchorRef, width: panelWidth } = useElementWidth();
|
|
@@ -412,12 +538,9 @@ const ChatInputSurfaceMenu = forwardRef(function ChatInputSurfaceMenu(props, ref
|
|
|
412
538
|
index: 0,
|
|
413
539
|
itemsSignature: ""
|
|
414
540
|
});
|
|
415
|
-
const { isOpen, isLoading, filterOptions, items, texts, onSelectItem, onOpenChange, onDetailsPointerDown } = props;
|
|
541
|
+
const { isOpen, isLoading, filterOptions, items, notice, texts, onSelectItem, onOpenChange, onDetailsPointerDown } = props;
|
|
416
542
|
const [activeFilterKey, setActiveFilterKey] = useState(filterOptions?.[0]?.key ?? null);
|
|
417
|
-
const resolvedActiveFilterKey = useMemo(() =>
|
|
418
|
-
if (!filterOptions?.length) return null;
|
|
419
|
-
return filterOptions.some((filter) => filter.key === activeFilterKey) ? activeFilterKey : filterOptions[0].key;
|
|
420
|
-
}, [activeFilterKey, filterOptions]);
|
|
543
|
+
const resolvedActiveFilterKey = useMemo(() => resolveActiveFilterKey(filterOptions, activeFilterKey), [activeFilterKey, filterOptions]);
|
|
421
544
|
const activeFilter = useMemo(() => filterOptions?.find((filter) => filter.key === resolvedActiveFilterKey) ?? null, [filterOptions, resolvedActiveFilterKey]);
|
|
422
545
|
const filterViews = useMemo(() => filterOptions?.map((filter) => ({
|
|
423
546
|
...filter,
|
|
@@ -449,31 +572,15 @@ const ChatInputSurfaceMenu = forwardRef(function ChatInputSurfaceMenu(props, ref
|
|
|
449
572
|
if (!panelWidth) return;
|
|
450
573
|
return Math.min(panelWidth > INPUT_SURFACE_PANEL_DESKTOP_MIN_WIDTH ? panelWidth * INPUT_SURFACE_PANEL_DESKTOP_SHRINK_RATIO : panelWidth, INPUT_SURFACE_PANEL_MAX_WIDTH);
|
|
451
574
|
}, [panelWidth]);
|
|
452
|
-
useImperativeHandle(ref, () => ({ handleKeyDown: (event) => {
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
event.preventDefault();
|
|
462
|
-
setActiveIndexForCurrentItems((index) => (index + 1) % visibleItems.length);
|
|
463
|
-
return true;
|
|
464
|
-
}
|
|
465
|
-
if (event.key === "ArrowUp") {
|
|
466
|
-
event.preventDefault();
|
|
467
|
-
setActiveIndexForCurrentItems((index) => (index - 1 + visibleItems.length) % visibleItems.length);
|
|
468
|
-
return true;
|
|
469
|
-
}
|
|
470
|
-
if (event.key === "Enter" && !event.shiftKey || event.key === "Tab") {
|
|
471
|
-
event.preventDefault();
|
|
472
|
-
onSelectItem(visibleItems[activeIndexInRange]);
|
|
473
|
-
return true;
|
|
474
|
-
}
|
|
475
|
-
return false;
|
|
476
|
-
} }), [
|
|
575
|
+
useImperativeHandle(ref, () => ({ handleKeyDown: (event) => handleInputSurfaceMenuKeyDown({
|
|
576
|
+
activeIndex: activeIndexInRange,
|
|
577
|
+
event,
|
|
578
|
+
isOpen,
|
|
579
|
+
items: visibleItems,
|
|
580
|
+
onOpenChange,
|
|
581
|
+
onSelectItem,
|
|
582
|
+
setActiveIndex: setActiveIndexForCurrentItems
|
|
583
|
+
}) }), [
|
|
477
584
|
activeIndexInRange,
|
|
478
585
|
isOpen,
|
|
479
586
|
onOpenChange,
|
|
@@ -512,105 +619,90 @@ const ChatInputSurfaceMenu = forwardRef(function ChatInputSurfaceMenu(props, ref
|
|
|
512
619
|
style: { minHeight: INPUT_SURFACE_PANEL_MIN_HEIGHT },
|
|
513
620
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
514
621
|
className: "flex min-h-0 flex-col border-r border-gray-200",
|
|
515
|
-
children: [
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
622
|
+
children: [
|
|
623
|
+
!isLoading && filterViews.length > 0 ? /* @__PURE__ */ jsx("div", {
|
|
624
|
+
className: "flex shrink-0 gap-0.5 overflow-x-auto px-2 pb-1.5 pt-2",
|
|
625
|
+
children: filterViews.map(({ count, key, label }) => {
|
|
626
|
+
const isActive = key === resolvedActiveFilterKey;
|
|
627
|
+
return /* @__PURE__ */ jsxs("button", {
|
|
628
|
+
type: "button",
|
|
629
|
+
"aria-pressed": isActive,
|
|
630
|
+
onPointerDown: (event) => event.preventDefault(),
|
|
631
|
+
onClick: () => handleFilterSelect(key),
|
|
632
|
+
className: `inline-flex h-7 shrink-0 items-center gap-1 rounded-md px-2 text-[11px] font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40 ${isActive ? "bg-gray-100 text-gray-900" : "text-gray-600 hover:bg-gray-100 hover:text-gray-900"}`,
|
|
633
|
+
children: [/* @__PURE__ */ jsx("span", { children: label }), /* @__PURE__ */ jsx("span", {
|
|
634
|
+
className: isActive ? "text-gray-500" : "text-gray-400",
|
|
635
|
+
children: count
|
|
636
|
+
})]
|
|
637
|
+
}, key);
|
|
638
|
+
})
|
|
639
|
+
}) : null,
|
|
640
|
+
!isLoading && notice ? /* @__PURE__ */ jsxs("div", {
|
|
641
|
+
className: "mx-2 mb-1.5 flex items-start gap-2 rounded-lg border border-red-200 bg-red-50 px-2.5 py-2 text-xs leading-4 text-red-700",
|
|
642
|
+
children: [/* @__PURE__ */ jsx(CircleAlert, {
|
|
643
|
+
"aria-hidden": "true",
|
|
644
|
+
className: "mt-0.5 h-3.5 w-3.5 shrink-0"
|
|
645
|
+
}), /* @__PURE__ */ jsx("span", { children: notice.message })]
|
|
646
|
+
}) : null,
|
|
647
|
+
/* @__PURE__ */ jsx("div", {
|
|
648
|
+
ref: listRef,
|
|
649
|
+
role: "listbox",
|
|
650
|
+
"aria-label": texts.sectionLabel,
|
|
651
|
+
className: `custom-scrollbar min-h-0 flex-1 overflow-y-auto overscroll-contain ${!isLoading && filterViews.length > 0 ? "px-2 pb-2" : "p-2"}`,
|
|
652
|
+
children: isLoading ? /* @__PURE__ */ jsx("div", {
|
|
653
|
+
className: "p-2 text-xs text-gray-500",
|
|
654
|
+
children: texts.loadingLabel
|
|
655
|
+
}) : /* @__PURE__ */ jsxs(Fragment$1, { children: [!hasItemSections ? /* @__PURE__ */ jsx("div", {
|
|
656
|
+
className: "mb-1 px-1.5 text-[11px] font-semibold uppercase tracking-wide text-gray-500",
|
|
657
|
+
children: texts.sectionLabel
|
|
658
|
+
}) : null, visibleItems.length === 0 ? /* @__PURE__ */ jsx("div", {
|
|
659
|
+
className: "px-1.5 text-xs text-gray-400",
|
|
660
|
+
children: texts.emptyLabel
|
|
661
|
+
}) : /* @__PURE__ */ jsx("div", { children: visibleItems.map((item, index) => {
|
|
662
|
+
const { icon, key, sectionKey, sectionLabel, title, subtitle } = item;
|
|
663
|
+
const isActive = index === activeIndexInRange;
|
|
664
|
+
const previousItem = visibleItems[index - 1];
|
|
665
|
+
return /* @__PURE__ */ jsxs("div", { children: [hasItemSections && Boolean(sectionLabel?.trim()) && previousItem?.sectionKey !== sectionKey ? /* @__PURE__ */ jsx("div", {
|
|
666
|
+
className: "px-1.5 pb-1 pt-1.5 text-[11px] font-semibold uppercase tracking-wide text-gray-500",
|
|
667
|
+
children: sectionLabel
|
|
668
|
+
}) : null, /* @__PURE__ */ jsxs("button", {
|
|
669
|
+
type: "button",
|
|
670
|
+
role: "option",
|
|
671
|
+
"aria-selected": isActive,
|
|
672
|
+
"data-input-surface-index": index,
|
|
673
|
+
onPointerMove: (event) => {
|
|
674
|
+
if (event.pointerType !== "touch") setActiveIndexForCurrentItems(index);
|
|
675
|
+
},
|
|
676
|
+
onPointerDown: (event) => {
|
|
677
|
+
if (event.button > 0) return;
|
|
678
|
+
event.preventDefault();
|
|
679
|
+
onSelectItem(item);
|
|
680
|
+
},
|
|
681
|
+
onClick: (event) => {
|
|
682
|
+
if (event.detail === 0) onSelectItem(item);
|
|
683
|
+
},
|
|
684
|
+
className: `flex w-full items-center gap-1.5 rounded-md px-1.5 py-1 text-left leading-4 transition-colors ${isActive ? "bg-gray-100 text-gray-900" : "text-gray-700 hover:bg-gray-100"}`,
|
|
685
|
+
children: [
|
|
686
|
+
icon ? /* @__PURE__ */ jsx(InputSurfaceItemIcon, { icon }) : null,
|
|
687
|
+
/* @__PURE__ */ jsx("span", {
|
|
688
|
+
className: "truncate text-xs font-medium",
|
|
689
|
+
children: title
|
|
690
|
+
}),
|
|
691
|
+
/* @__PURE__ */ jsx("span", {
|
|
692
|
+
className: "truncate text-xs text-gray-500",
|
|
693
|
+
children: subtitle
|
|
694
|
+
})
|
|
695
|
+
]
|
|
696
|
+
})] }, key);
|
|
697
|
+
}) })] })
|
|
530
698
|
})
|
|
531
|
-
|
|
532
|
-
ref: listRef,
|
|
533
|
-
role: "listbox",
|
|
534
|
-
"aria-label": texts.sectionLabel,
|
|
535
|
-
className: `custom-scrollbar min-h-0 flex-1 overflow-y-auto overscroll-contain ${!isLoading && filterViews.length > 0 ? "px-2 pb-2" : "p-2"}`,
|
|
536
|
-
children: isLoading ? /* @__PURE__ */ jsx("div", {
|
|
537
|
-
className: "p-2 text-xs text-gray-500",
|
|
538
|
-
children: texts.loadingLabel
|
|
539
|
-
}) : /* @__PURE__ */ jsxs(Fragment$1, { children: [!hasItemSections ? /* @__PURE__ */ jsx("div", {
|
|
540
|
-
className: "mb-1 px-1.5 text-[11px] font-semibold uppercase tracking-wide text-gray-500",
|
|
541
|
-
children: texts.sectionLabel
|
|
542
|
-
}) : null, visibleItems.length === 0 ? /* @__PURE__ */ jsx("div", {
|
|
543
|
-
className: "px-1.5 text-xs text-gray-400",
|
|
544
|
-
children: texts.emptyLabel
|
|
545
|
-
}) : /* @__PURE__ */ jsx("div", { children: visibleItems.map((item, index) => {
|
|
546
|
-
const { key, sectionKey, sectionLabel, title, subtitle } = item;
|
|
547
|
-
const isActive = index === activeIndexInRange;
|
|
548
|
-
const previousItem = visibleItems[index - 1];
|
|
549
|
-
return /* @__PURE__ */ jsxs("div", { children: [hasItemSections && Boolean(sectionLabel?.trim()) && previousItem?.sectionKey !== sectionKey ? /* @__PURE__ */ jsx("div", {
|
|
550
|
-
className: "px-1.5 pb-1 pt-1.5 text-[11px] font-semibold uppercase tracking-wide text-gray-500",
|
|
551
|
-
children: sectionLabel
|
|
552
|
-
}) : null, /* @__PURE__ */ jsxs("button", {
|
|
553
|
-
type: "button",
|
|
554
|
-
role: "option",
|
|
555
|
-
"aria-selected": isActive,
|
|
556
|
-
"data-input-surface-index": index,
|
|
557
|
-
onPointerMove: (event) => {
|
|
558
|
-
if (event.pointerType !== "touch") setActiveIndexForCurrentItems(index);
|
|
559
|
-
},
|
|
560
|
-
onPointerDown: (event) => {
|
|
561
|
-
if (event.button > 0) return;
|
|
562
|
-
event.preventDefault();
|
|
563
|
-
onSelectItem(item);
|
|
564
|
-
},
|
|
565
|
-
onClick: (event) => {
|
|
566
|
-
if (event.detail === 0) onSelectItem(item);
|
|
567
|
-
},
|
|
568
|
-
className: `flex w-full items-center gap-1.5 rounded-md px-1.5 py-1 text-left leading-4 transition-colors ${isActive ? "bg-gray-100 text-gray-900" : "text-gray-700 hover:bg-gray-100"}`,
|
|
569
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
570
|
-
className: "truncate text-xs font-medium",
|
|
571
|
-
children: title
|
|
572
|
-
}), /* @__PURE__ */ jsx("span", {
|
|
573
|
-
className: "truncate text-xs text-gray-500",
|
|
574
|
-
children: subtitle
|
|
575
|
-
})]
|
|
576
|
-
})] }, key);
|
|
577
|
-
}) })] })
|
|
578
|
-
})]
|
|
699
|
+
]
|
|
579
700
|
}), /* @__PURE__ */ jsx("div", {
|
|
580
701
|
className: "custom-scrollbar min-h-0 min-w-0 select-text overflow-y-auto overscroll-contain p-2.5",
|
|
581
702
|
onPointerDown: onDetailsPointerDown,
|
|
582
|
-
children:
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
/* @__PURE__ */ jsxs("div", {
|
|
586
|
-
className: "flex items-center gap-2",
|
|
587
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
588
|
-
className: "inline-flex rounded-full bg-primary/10 px-2 py-0.5 text-[11px] font-semibold text-primary",
|
|
589
|
-
children: activeItem.subtitle
|
|
590
|
-
}), /* @__PURE__ */ jsx("span", {
|
|
591
|
-
className: "text-sm font-semibold text-gray-900",
|
|
592
|
-
children: activeItem.title
|
|
593
|
-
})]
|
|
594
|
-
}),
|
|
595
|
-
/* @__PURE__ */ jsx("p", {
|
|
596
|
-
className: "text-xs leading-5 text-gray-600",
|
|
597
|
-
children: activeItem.description
|
|
598
|
-
}),
|
|
599
|
-
/* @__PURE__ */ jsx("div", {
|
|
600
|
-
className: "space-y-1",
|
|
601
|
-
children: activeItem.detailLines.map((line) => /* @__PURE__ */ jsx("div", {
|
|
602
|
-
className: "min-w-0 break-all rounded-md bg-gray-50 px-2 py-1 text-[11px] leading-5 text-gray-600",
|
|
603
|
-
children: line
|
|
604
|
-
}, line))
|
|
605
|
-
}),
|
|
606
|
-
/* @__PURE__ */ jsx("div", {
|
|
607
|
-
className: "pt-1 text-[11px] text-gray-500",
|
|
608
|
-
children: activeItem.hintLabel ?? texts.itemHintLabel
|
|
609
|
-
})
|
|
610
|
-
]
|
|
611
|
-
}) : /* @__PURE__ */ jsx("div", {
|
|
612
|
-
className: "text-xs text-gray-500",
|
|
613
|
-
children: texts.hintLabel
|
|
703
|
+
children: /* @__PURE__ */ jsx(InputSurfaceItemDetails, {
|
|
704
|
+
item: activeItem,
|
|
705
|
+
texts
|
|
614
706
|
})
|
|
615
707
|
})]
|
|
616
708
|
})
|
|
@@ -623,7 +715,7 @@ function getInputSurfaceTriggerIdentity(trigger) {
|
|
|
623
715
|
return `${trigger.key}:${trigger.marker}:${trigger.start}`;
|
|
624
716
|
}
|
|
625
717
|
function isInputSurfaceCreateEvent(trigger, reason) {
|
|
626
|
-
return reason.type === "insert-text" &&
|
|
718
|
+
return reason.type === "insert-text" && trigger.query === "" && trigger.end === trigger.start + trigger.marker.length;
|
|
627
719
|
}
|
|
628
720
|
function resolveInputSurfaceTriggerIdentity(currentIdentity, trigger, reason) {
|
|
629
721
|
if (!trigger) return null;
|
|
@@ -635,21 +727,19 @@ function ChatInputSurfaceHost(props) {
|
|
|
635
727
|
const { children, inputSurface, onInputSurfaceTriggerChange, onSelectItem, triggerSpecs = [CHAT_INPUT_SURFACE_SLASH_TRIGGER_SPEC] } = props;
|
|
636
728
|
const menuRef = useRef(null);
|
|
637
729
|
const isDetailsInteractionRef = useRef(false);
|
|
730
|
+
const activeTriggerIdentityRef = useRef(null);
|
|
638
731
|
const [activeTriggerIdentity, setActiveTriggerIdentity] = useState(null);
|
|
639
732
|
const isOpen = Boolean(inputSurface) && activeTriggerIdentity !== null;
|
|
640
733
|
const setActiveTrigger = useCallback((identity, trigger) => {
|
|
734
|
+
activeTriggerIdentityRef.current = identity;
|
|
641
735
|
setActiveTriggerIdentity(identity);
|
|
642
736
|
onInputSurfaceTriggerChange?.(identity ? trigger : null);
|
|
643
737
|
}, [onInputSurfaceTriggerChange]);
|
|
644
738
|
const handleInputSurfaceSnapshotChange = useCallback((nodes, selection, reason) => {
|
|
645
739
|
const trigger = resolveChatComposerActiveInputSurfaceTrigger(nodes, selection, triggerSpecs);
|
|
646
|
-
const nextIdentity = resolveInputSurfaceTriggerIdentity(
|
|
740
|
+
const nextIdentity = resolveInputSurfaceTriggerIdentity(activeTriggerIdentityRef.current, trigger, reason);
|
|
647
741
|
setActiveTrigger(nextIdentity, nextIdentity ? trigger : null);
|
|
648
|
-
}, [
|
|
649
|
-
activeTriggerIdentity,
|
|
650
|
-
setActiveTrigger,
|
|
651
|
-
triggerSpecs
|
|
652
|
-
]);
|
|
742
|
+
}, [setActiveTrigger, triggerSpecs]);
|
|
653
743
|
const handleInputSurfaceKeyDown = useCallback((event) => {
|
|
654
744
|
return menuRef.current?.handleKeyDown(event) ?? false;
|
|
655
745
|
}, []);
|
|
@@ -670,9 +760,10 @@ function ChatInputSurfaceHost(props) {
|
|
|
670
760
|
isLoading: inputSurface.isLoading,
|
|
671
761
|
filterOptions: inputSurface.filterOptions,
|
|
672
762
|
items: inputSurface.items,
|
|
763
|
+
notice: inputSurface.notice,
|
|
673
764
|
texts: inputSurface.texts,
|
|
674
765
|
onSelectItem: (item) => {
|
|
675
|
-
setActiveTrigger(null, null);
|
|
766
|
+
if (item.selectionBehavior !== "navigate") setActiveTrigger(null, null);
|
|
676
767
|
onSelectItem(item);
|
|
677
768
|
},
|
|
678
769
|
onOpenChange: handleInputSurfaceOpenChange,
|
|
@@ -824,42 +915,42 @@ function filterOptions(options, query) {
|
|
|
824
915
|
].filter((value) => typeof value === "string" && value.trim().length > 0).join(" ").toLowerCase().includes(keyword);
|
|
825
916
|
});
|
|
826
917
|
}
|
|
827
|
-
const
|
|
918
|
+
const SKILL_PICKER_HEIGHT = createChatPopoverAvailableHeightLimit("20rem");
|
|
828
919
|
function ChatInputBarSkillPicker(props) {
|
|
829
920
|
const { Input, Popover, PopoverContent, PopoverTrigger } = ChatUiPrimitives;
|
|
830
921
|
const { picker } = props;
|
|
831
922
|
const listRef = useRef(null);
|
|
832
923
|
const listId = useId();
|
|
833
924
|
const [query, setQuery] = useState("");
|
|
925
|
+
const [activeGroupKey, setActiveGroupKey] = useState(null);
|
|
834
926
|
const [activeIndex, setActiveIndex] = useState(0);
|
|
835
927
|
const selectedSet = useMemo(() => new Set(picker.selectedKeys), [picker.selectedKeys]);
|
|
836
928
|
const selectedCount = picker.selectedKeys.length;
|
|
837
|
-
const
|
|
929
|
+
const availableGroups = useMemo(() => picker.groups?.filter((group) => group.options.length > 0) ?? [], [picker.groups]);
|
|
930
|
+
const resolvedActiveGroupKey = availableGroups.some((group) => group.key === activeGroupKey) ? activeGroupKey : null;
|
|
838
931
|
const groupedOptions = useMemo(() => {
|
|
839
|
-
if (
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
932
|
+
if (availableGroups.length === 0) return null;
|
|
933
|
+
return (resolvedActiveGroupKey ? availableGroups.filter((group) => group.key === resolvedActiveGroupKey) : availableGroups).map((group) => ({
|
|
934
|
+
...group,
|
|
935
|
+
options: filterOptions(group.options, query)
|
|
936
|
+
})).filter((group) => group.options.length > 0);
|
|
937
|
+
}, [
|
|
938
|
+
availableGroups,
|
|
939
|
+
query,
|
|
940
|
+
resolvedActiveGroupKey
|
|
941
|
+
]);
|
|
843
942
|
const visibleOptions = useMemo(() => {
|
|
844
|
-
if (groupedOptions) return groupedOptions.flatMap((group) => group.options);
|
|
943
|
+
if (groupedOptions !== null) return groupedOptions.flatMap((group) => group.options);
|
|
845
944
|
return filterOptions(picker.options, query);
|
|
846
945
|
}, [
|
|
847
946
|
groupedOptions,
|
|
848
947
|
picker.options,
|
|
849
948
|
query
|
|
850
949
|
]);
|
|
851
|
-
|
|
852
|
-
setActiveIndex(0);
|
|
853
|
-
}, [query]);
|
|
854
|
-
useEffect(() => {
|
|
855
|
-
setActiveIndex((current) => {
|
|
856
|
-
if (visibleOptions.length === 0) return 0;
|
|
857
|
-
return Math.min(current, visibleOptions.length - 1);
|
|
858
|
-
});
|
|
859
|
-
}, [visibleOptions.length]);
|
|
950
|
+
const resolvedActiveIndex = visibleOptions.length === 0 ? 0 : Math.min(activeIndex, visibleOptions.length - 1);
|
|
860
951
|
useActiveItemScroll({
|
|
861
952
|
containerRef: listRef,
|
|
862
|
-
activeIndex,
|
|
953
|
+
activeIndex: resolvedActiveIndex,
|
|
863
954
|
itemCount: visibleOptions.length,
|
|
864
955
|
isEnabled: visibleOptions.length > 0,
|
|
865
956
|
getItemSelector: (index) => `[data-skill-index="${index}"]`
|
|
@@ -875,17 +966,17 @@ function ChatInputBarSkillPicker(props) {
|
|
|
875
966
|
if (visibleOptions.length === 0) return;
|
|
876
967
|
if (event.key === "ArrowDown") {
|
|
877
968
|
event.preventDefault();
|
|
878
|
-
setActiveIndex(
|
|
969
|
+
setActiveIndex(Math.min(resolvedActiveIndex + 1, visibleOptions.length - 1));
|
|
879
970
|
return;
|
|
880
971
|
}
|
|
881
972
|
if (event.key === "ArrowUp") {
|
|
882
973
|
event.preventDefault();
|
|
883
|
-
setActiveIndex(
|
|
974
|
+
setActiveIndex(Math.max(resolvedActiveIndex - 1, 0));
|
|
884
975
|
return;
|
|
885
976
|
}
|
|
886
977
|
if (event.key === "Enter") {
|
|
887
978
|
event.preventDefault();
|
|
888
|
-
const activeOption = visibleOptions[
|
|
979
|
+
const activeOption = visibleOptions[resolvedActiveIndex];
|
|
889
980
|
if (activeOption) onToggleOption(activeOption.key);
|
|
890
981
|
}
|
|
891
982
|
};
|
|
@@ -912,28 +1003,63 @@ function ChatInputBarSkillPicker(props) {
|
|
|
912
1003
|
side: "top",
|
|
913
1004
|
align: "start",
|
|
914
1005
|
className: "flex w-[min(360px,calc(100vw-1rem))] flex-col overflow-hidden p-0",
|
|
915
|
-
style: {
|
|
1006
|
+
style: { height: SKILL_PICKER_HEIGHT },
|
|
916
1007
|
children: [
|
|
917
1008
|
/* @__PURE__ */ jsxs("div", {
|
|
918
1009
|
className: "shrink-0 space-y-2 border-b border-border px-4 py-3",
|
|
919
|
-
children: [
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
1010
|
+
children: [
|
|
1011
|
+
/* @__PURE__ */ jsx("div", {
|
|
1012
|
+
className: "text-sm font-semibold text-foreground",
|
|
1013
|
+
children: picker.title
|
|
1014
|
+
}),
|
|
1015
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1016
|
+
className: "relative",
|
|
1017
|
+
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, {
|
|
1018
|
+
value: query,
|
|
1019
|
+
onChange: (event) => {
|
|
1020
|
+
setQuery(event.target.value);
|
|
1021
|
+
setActiveIndex(0);
|
|
1022
|
+
},
|
|
1023
|
+
onKeyDown: onSearchKeyDown,
|
|
1024
|
+
placeholder: picker.searchPlaceholder,
|
|
1025
|
+
role: "combobox",
|
|
1026
|
+
"aria-controls": listId,
|
|
1027
|
+
"aria-expanded": "true",
|
|
1028
|
+
"aria-autocomplete": "list",
|
|
1029
|
+
"aria-activedescendant": visibleOptions[resolvedActiveIndex] ? `${listId}-option-${resolvedActiveIndex}` : void 0,
|
|
1030
|
+
className: "h-8 rounded-lg pl-8 text-xs"
|
|
1031
|
+
})]
|
|
1032
|
+
}),
|
|
1033
|
+
availableGroups.length > 0 ? /* @__PURE__ */ jsx("div", {
|
|
1034
|
+
className: "flex flex-wrap gap-1",
|
|
1035
|
+
"aria-label": picker.allGroupsLabel,
|
|
1036
|
+
children: [{
|
|
1037
|
+
key: null,
|
|
1038
|
+
label: picker.allGroupsLabel,
|
|
1039
|
+
count: picker.options.length
|
|
1040
|
+
}, ...availableGroups.map((group) => ({
|
|
1041
|
+
key: group.key,
|
|
1042
|
+
label: group.label || group.key,
|
|
1043
|
+
count: group.options.length
|
|
1044
|
+
}))].map((group) => {
|
|
1045
|
+
const isActive = group.key === resolvedActiveGroupKey;
|
|
1046
|
+
return /* @__PURE__ */ jsxs("button", {
|
|
1047
|
+
type: "button",
|
|
1048
|
+
"aria-pressed": isActive,
|
|
1049
|
+
onMouseDown: (event) => event.preventDefault(),
|
|
1050
|
+
onClick: () => {
|
|
1051
|
+
setActiveGroupKey(group.key);
|
|
1052
|
+
setActiveIndex(0);
|
|
1053
|
+
},
|
|
1054
|
+
className: `inline-flex h-6 items-center gap-1 rounded-md px-2 text-[10px] font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40 ${isActive ? "bg-primary/10 text-primary" : "bg-muted text-muted-foreground hover:text-foreground"}`,
|
|
1055
|
+
children: [/* @__PURE__ */ jsx("span", { children: group.label }), /* @__PURE__ */ jsx("span", {
|
|
1056
|
+
className: "opacity-70",
|
|
1057
|
+
children: group.count
|
|
1058
|
+
})]
|
|
1059
|
+
}, group.key ?? "all-skills");
|
|
1060
|
+
})
|
|
1061
|
+
}) : null
|
|
1062
|
+
]
|
|
937
1063
|
}),
|
|
938
1064
|
/* @__PURE__ */ jsx("div", {
|
|
939
1065
|
ref: listRef,
|
|
@@ -962,7 +1088,7 @@ function ChatInputBarSkillPicker(props) {
|
|
|
962
1088
|
const index = visibleIndex;
|
|
963
1089
|
visibleIndex += 1;
|
|
964
1090
|
const isSelected = selectedSet.has(option.key);
|
|
965
|
-
const isActive = index ===
|
|
1091
|
+
const isActive = index === resolvedActiveIndex;
|
|
966
1092
|
return /* @__PURE__ */ jsxs("div", {
|
|
967
1093
|
id: `${listId}-option-${index}`,
|
|
968
1094
|
role: "option",
|
|
@@ -1316,11 +1442,18 @@ function buildTokenClassName(tokenKind) {
|
|
|
1316
1442
|
].join(" ");
|
|
1317
1443
|
}
|
|
1318
1444
|
function ChatComposerTokenChip({ label, tokenKind }) {
|
|
1445
|
+
const isWorkspaceReference = tokenKind === "workspace_file" || tokenKind === "workspace_directory";
|
|
1319
1446
|
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
|
|
1320
1447
|
className: tokenKind === "file" ? "inline-flex h-4.5 w-4.5 shrink-0 items-center justify-center rounded-md bg-card text-muted-foreground ring-1 ring-border" : "inline-flex h-3.5 w-3.5 shrink-0 items-center justify-center text-primary/70",
|
|
1321
1448
|
children: tokenKind === "file" ? /* @__PURE__ */ jsx(ImageIcon, {
|
|
1322
1449
|
"aria-hidden": "true",
|
|
1323
1450
|
className: "h-3 w-3"
|
|
1451
|
+
}) : tokenKind === "workspace_file" ? /* @__PURE__ */ jsx(FileText, {
|
|
1452
|
+
"aria-hidden": "true",
|
|
1453
|
+
className: "h-3 w-3"
|
|
1454
|
+
}) : tokenKind === "workspace_directory" ? /* @__PURE__ */ jsx(Folder, {
|
|
1455
|
+
"aria-hidden": "true",
|
|
1456
|
+
className: "h-3 w-3"
|
|
1324
1457
|
}) : tokenKind === "panel_app" ? /* @__PURE__ */ jsx(AppWindow, {
|
|
1325
1458
|
"aria-hidden": "true",
|
|
1326
1459
|
className: "h-3 w-3"
|
|
@@ -1329,7 +1462,7 @@ function ChatComposerTokenChip({ label, tokenKind }) {
|
|
|
1329
1462
|
className: "h-3 w-3"
|
|
1330
1463
|
})
|
|
1331
1464
|
}), /* @__PURE__ */ jsx("span", {
|
|
1332
|
-
className: tokenKind === "file" ? "min-w-0 flex-1 truncate text-[12px] font-medium text-foreground" : "truncate",
|
|
1465
|
+
className: tokenKind === "file" ? "min-w-0 flex-1 truncate text-[12px] font-medium text-foreground" : isWorkspaceReference ? "max-w-[16rem] truncate" : "truncate",
|
|
1333
1466
|
children: label
|
|
1334
1467
|
})] });
|
|
1335
1468
|
}
|
|
@@ -1424,6 +1557,7 @@ function $isChatComposerTokenNode(node) {
|
|
|
1424
1557
|
}
|
|
1425
1558
|
//#endregion
|
|
1426
1559
|
//#region src/components/chat/ui/chat-input-bar/lexical/chat-composer-lexical-editor-state.ts
|
|
1560
|
+
const CHAT_COMPOSER_EXTERNAL_UPDATE_TAG = "nextclaw-chat-composer-external-update";
|
|
1427
1561
|
function getComposerLeafDescriptors() {
|
|
1428
1562
|
const root = $getRoot();
|
|
1429
1563
|
const paragraph = root.getFirstChild();
|
|
@@ -1628,7 +1762,7 @@ function writeChatComposerStateToLexicalRoot(nodes, selection) {
|
|
|
1628
1762
|
function syncLexicalEditorFromChatComposerState(editor, nodes, selection, preserveDomSelection = false) {
|
|
1629
1763
|
editor.update(() => {
|
|
1630
1764
|
writeChatComposerStateToLexicalRoot(nodes, selection);
|
|
1631
|
-
}, preserveDomSelection ?
|
|
1765
|
+
}, { tag: preserveDomSelection ? [CHAT_COMPOSER_EXTERNAL_UPDATE_TAG, SKIP_DOM_SELECTION_TAG] : CHAT_COMPOSER_EXTERNAL_UPDATE_TAG });
|
|
1632
1766
|
}
|
|
1633
1767
|
function syncLexicalSelectionFromChatComposerSelection(editor, selection, preserveDomSelection = false) {
|
|
1634
1768
|
editor.update(() => {
|
|
@@ -1638,7 +1772,7 @@ function syncLexicalSelectionFromChatComposerSelection(editor, selection, preser
|
|
|
1638
1772
|
nextSelection.anchor.set(anchor.key, anchor.offset, anchor.type);
|
|
1639
1773
|
nextSelection.focus.set(focus.key, focus.offset, focus.type);
|
|
1640
1774
|
$setSelection(nextSelection);
|
|
1641
|
-
}, preserveDomSelection ?
|
|
1775
|
+
}, { tag: preserveDomSelection ? [CHAT_COMPOSER_EXTERNAL_UPDATE_TAG, SKIP_DOM_SELECTION_TAG] : CHAT_COMPOSER_EXTERNAL_UPDATE_TAG });
|
|
1642
1776
|
}
|
|
1643
1777
|
//#endregion
|
|
1644
1778
|
//#region src/components/chat/ui/chat-input-bar/lexical/chat-composer-lexical-operations.ts
|
|
@@ -1707,19 +1841,6 @@ function insertInputSurfaceItemIntoChatComposer(params) {
|
|
|
1707
1841
|
function getChatComposerNodesSignature(nodes) {
|
|
1708
1842
|
return nodes.map((node) => node.type === "text" ? `text:${node.text}` : `token:${node.tokenKind}:${node.tokenKey}:${node.label}`).join("");
|
|
1709
1843
|
}
|
|
1710
|
-
function replaceChatComposerSelectionWithText(params) {
|
|
1711
|
-
const { nodes, selection, text } = params;
|
|
1712
|
-
const documentLength = getDocumentLength(nodes);
|
|
1713
|
-
const [selectionStart, selectionEnd] = selection ? [Math.min(selection.start, selection.end), Math.max(selection.start, selection.end)] : [documentLength, documentLength];
|
|
1714
|
-
const nextOffset = selectionStart + text.length;
|
|
1715
|
-
return {
|
|
1716
|
-
nodes: replaceChatComposerRange(nodes, selectionStart, selectionEnd, [createChatComposerTextNode(text)]),
|
|
1717
|
-
selection: {
|
|
1718
|
-
start: nextOffset,
|
|
1719
|
-
end: nextOffset
|
|
1720
|
-
}
|
|
1721
|
-
};
|
|
1722
|
-
}
|
|
1723
1844
|
function insertFileTokenIntoChatComposer(params) {
|
|
1724
1845
|
const { label, nodes, selection, tokenKey } = params;
|
|
1725
1846
|
return insertToken({
|
|
@@ -1766,93 +1887,43 @@ function syncSelectedSkillsIntoChatComposer(params) {
|
|
|
1766
1887
|
selection
|
|
1767
1888
|
};
|
|
1768
1889
|
}
|
|
1769
|
-
function deleteChatComposerContent(params) {
|
|
1770
|
-
const { direction, nodes, selection: currentSelection } = params;
|
|
1771
|
-
const documentLength = getDocumentLength(nodes);
|
|
1772
|
-
const [selectionStart, selectionEnd] = currentSelection ? [Math.min(currentSelection.start, currentSelection.end), Math.max(currentSelection.start, currentSelection.end)] : [documentLength, documentLength];
|
|
1773
|
-
let rangeStart = selectionStart;
|
|
1774
|
-
let rangeEnd = selectionEnd;
|
|
1775
|
-
if (rangeStart === rangeEnd) if (direction === "backward" && rangeStart > 0) rangeStart -= 1;
|
|
1776
|
-
else if (direction === "forward" && rangeEnd < documentLength) rangeEnd += 1;
|
|
1777
|
-
else return {
|
|
1778
|
-
nodes: normalizeChatComposerNodes(nodes),
|
|
1779
|
-
selection: {
|
|
1780
|
-
start: rangeStart,
|
|
1781
|
-
end: rangeEnd
|
|
1782
|
-
}
|
|
1783
|
-
};
|
|
1784
|
-
return {
|
|
1785
|
-
nodes: replaceChatComposerRange(nodes, rangeStart, rangeEnd, []),
|
|
1786
|
-
selection: {
|
|
1787
|
-
start: rangeStart,
|
|
1788
|
-
end: rangeStart
|
|
1789
|
-
}
|
|
1790
|
-
};
|
|
1791
|
-
}
|
|
1792
1890
|
//#endregion
|
|
1793
1891
|
//#region src/components/chat/ui/chat-input-bar/lexical/chat-composer-lexical-controller.ts
|
|
1892
|
+
function deleteAdjacentChatComposerToken(event) {
|
|
1893
|
+
if (event.key !== "Backspace" && event.key !== "Delete") return false;
|
|
1894
|
+
const selection = $getSelection();
|
|
1895
|
+
if ($isRangeSelection(selection) && !selection.isCollapsed()) return false;
|
|
1896
|
+
let candidate = null;
|
|
1897
|
+
if ($isRangeSelection(selection)) {
|
|
1898
|
+
const { anchor } = selection;
|
|
1899
|
+
const anchorNode = anchor.getNode();
|
|
1900
|
+
if (anchor.type === "element" && $isElementNode(anchorNode)) {
|
|
1901
|
+
const candidateIndex = event.key === "Backspace" ? anchor.offset - 1 : anchor.offset;
|
|
1902
|
+
candidate = anchorNode.getChildAtIndex(candidateIndex);
|
|
1903
|
+
} else if (event.key === "Backspace" && anchor.offset === 0) candidate = anchorNode.getPreviousSibling();
|
|
1904
|
+
else if (event.key === "Delete" && anchor.offset === anchorNode.getTextContentSize()) candidate = anchorNode.getNextSibling();
|
|
1905
|
+
} else if (event.key === "Backspace") {
|
|
1906
|
+
const paragraph = $getRoot().getFirstChild();
|
|
1907
|
+
candidate = $isElementNode(paragraph) ? paragraph.getLastChild() : null;
|
|
1908
|
+
}
|
|
1909
|
+
if ($isTextNode(candidate) && candidate.getTextContentSize() === 0) candidate = event.key === "Backspace" ? candidate.getPreviousSibling() : candidate.getNextSibling();
|
|
1910
|
+
if (!$isChatComposerTokenNode(candidate)) return false;
|
|
1911
|
+
event.preventDefault();
|
|
1912
|
+
candidate.remove();
|
|
1913
|
+
return true;
|
|
1914
|
+
}
|
|
1794
1915
|
function resolveLexicalComposerKeyboardAction(params) {
|
|
1795
1916
|
const { canStopGeneration, isComposing, isSending, key, shiftKey } = params;
|
|
1796
|
-
if (
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
}
|
|
1800
|
-
if (key === "Enter" && shiftKey) return { type: "insert-line-break" };
|
|
1801
|
-
if (key === "Enter") return { type: "send-message" };
|
|
1802
|
-
if (!isComposing && (key === "Backspace" || key === "Delete")) return {
|
|
1803
|
-
type: "delete-content",
|
|
1804
|
-
direction: key === "Backspace" ? "backward" : "forward"
|
|
1805
|
-
};
|
|
1917
|
+
if (isComposing) return { type: "noop" };
|
|
1918
|
+
if (key === "Escape") return isSending && canStopGeneration ? { type: "stop-generation" } : { type: "noop" };
|
|
1919
|
+
if (key === "Enter" && !shiftKey) return { type: "send-message" };
|
|
1806
1920
|
return { type: "noop" };
|
|
1807
1921
|
}
|
|
1808
|
-
function getChatComposerContentSignature(nodes) {
|
|
1809
|
-
return JSON.stringify(nodes.map((node) => node.type === "text" ? {
|
|
1810
|
-
text: node.text,
|
|
1811
|
-
type: node.type
|
|
1812
|
-
} : {
|
|
1813
|
-
label: node.label,
|
|
1814
|
-
tokenKey: node.tokenKey,
|
|
1815
|
-
tokenKind: node.tokenKind,
|
|
1816
|
-
type: node.type
|
|
1817
|
-
}));
|
|
1818
|
-
}
|
|
1819
|
-
function handleLexicalComposerBeforeInput(params) {
|
|
1820
|
-
const { disabled, event, isComposing, publishSnapshot, snapshotReader } = params;
|
|
1821
|
-
const nativeEvent = event.nativeEvent;
|
|
1822
|
-
const shouldInsertText = nativeEvent.inputType === "insertText" || nativeEvent.inputType === "insertReplacementText";
|
|
1823
|
-
if (disabled || isComposing || nativeEvent.isComposing || !shouldInsertText || !nativeEvent.data) return;
|
|
1824
|
-
event.preventDefault();
|
|
1825
|
-
publishSnapshot(replaceChatComposerSelectionWithText({
|
|
1826
|
-
nodes: snapshotReader().nodes,
|
|
1827
|
-
selection: snapshotReader().selection,
|
|
1828
|
-
text: nativeEvent.data
|
|
1829
|
-
}), { inputSurfaceReason: {
|
|
1830
|
-
type: "insert-text",
|
|
1831
|
-
text: nativeEvent.data
|
|
1832
|
-
} });
|
|
1833
|
-
}
|
|
1834
|
-
function handleLexicalComposerCompositionEnd(params) {
|
|
1835
|
-
const { compositionStartSnapshot, data, fallbackSnapshot, publishSnapshot, snapshotReader } = params;
|
|
1836
|
-
const currentSnapshot = snapshotReader();
|
|
1837
|
-
const editorSnapshot = fallbackSnapshot();
|
|
1838
|
-
const baseSnapshot = compositionStartSnapshot ?? currentSnapshot;
|
|
1839
|
-
publishSnapshot(getChatComposerContentSignature(editorSnapshot.nodes) !== getChatComposerContentSignature(baseSnapshot.nodes) ? editorSnapshot : data.length > 0 ? replaceChatComposerSelectionWithText({
|
|
1840
|
-
nodes: baseSnapshot.nodes,
|
|
1841
|
-
selection: baseSnapshot.selection,
|
|
1842
|
-
text: data
|
|
1843
|
-
}) : editorSnapshot, {
|
|
1844
|
-
forcePublish: true,
|
|
1845
|
-
inputSurfaceReason: {
|
|
1846
|
-
type: "insert-text",
|
|
1847
|
-
text: data
|
|
1848
|
-
}
|
|
1849
|
-
});
|
|
1850
|
-
}
|
|
1851
1922
|
function handleLexicalComposerKeyboardCommand(params) {
|
|
1852
|
-
const { actions,
|
|
1923
|
+
const { actions, isComposing, nativeEvent } = params;
|
|
1853
1924
|
const action = resolveLexicalComposerKeyboardAction({
|
|
1854
1925
|
canStopGeneration: actions.canStopGeneration,
|
|
1855
|
-
isComposing
|
|
1926
|
+
isComposing,
|
|
1856
1927
|
isSending: actions.isSending,
|
|
1857
1928
|
key: nativeEvent.key,
|
|
1858
1929
|
shiftKey: nativeEvent.shiftKey
|
|
@@ -1862,26 +1933,9 @@ function handleLexicalComposerKeyboardCommand(params) {
|
|
|
1862
1933
|
case "stop-generation":
|
|
1863
1934
|
actions.onStop();
|
|
1864
1935
|
return true;
|
|
1865
|
-
case "insert-line-break":
|
|
1866
|
-
publishSnapshot(replaceChatComposerSelectionWithText({
|
|
1867
|
-
nodes: snapshot.nodes,
|
|
1868
|
-
selection: snapshot.selection,
|
|
1869
|
-
text: "\n"
|
|
1870
|
-
}), { inputSurfaceReason: {
|
|
1871
|
-
type: "insert-text",
|
|
1872
|
-
text: "\n"
|
|
1873
|
-
} });
|
|
1874
|
-
return true;
|
|
1875
1936
|
case "send-message":
|
|
1876
1937
|
actions.onSend();
|
|
1877
1938
|
return true;
|
|
1878
|
-
case "delete-content":
|
|
1879
|
-
publishSnapshot(deleteChatComposerContent({
|
|
1880
|
-
direction: action.direction,
|
|
1881
|
-
nodes: snapshot.nodes,
|
|
1882
|
-
selection: snapshot.selection
|
|
1883
|
-
}), { inputSurfaceReason: { type: "delete-content" } });
|
|
1884
|
-
return true;
|
|
1885
1939
|
case "noop": return false;
|
|
1886
1940
|
}
|
|
1887
1941
|
}
|
|
@@ -1895,13 +1949,10 @@ function getChatComposerDocumentLength(nodes) {
|
|
|
1895
1949
|
}
|
|
1896
1950
|
var ChatComposerLexicalOwner = class {
|
|
1897
1951
|
constructor() {
|
|
1898
|
-
this.isApplyingExternalUpdateRef = createMutableRef(false);
|
|
1899
|
-
this.isComposingRef = createMutableRef(false);
|
|
1900
1952
|
this.pendingOwnerSignatureRef = createMutableRef(null);
|
|
1901
1953
|
this.pendingSelectionRef = createMutableRef(null);
|
|
1902
1954
|
this.selectionRef = createMutableRef(null);
|
|
1903
1955
|
this.shouldFocusAfterSyncRef = createMutableRef(false);
|
|
1904
|
-
this.compositionStartSnapshotRef = createMutableRef(null);
|
|
1905
1956
|
this.editorSignatureRef = createMutableRef("");
|
|
1906
1957
|
this.lastPublishedSignatureRef = createMutableRef("");
|
|
1907
1958
|
this.pendingInputSurfaceReasonRef = createMutableRef(null);
|
|
@@ -1912,14 +1963,17 @@ var ChatComposerLexicalOwner = class {
|
|
|
1912
1963
|
};
|
|
1913
1964
|
this.bindEditor = (editor) => {
|
|
1914
1965
|
this.editor = editor;
|
|
1966
|
+
const signature = getChatComposerNodesSignature(readChatComposerSnapshotFromEditorState(editor.getEditorState()).nodes);
|
|
1967
|
+
this.editorSignatureRef.current = signature;
|
|
1968
|
+
this.lastPublishedSignatureRef.current = signature;
|
|
1915
1969
|
return () => {
|
|
1916
1970
|
if (this.editor === editor) this.editor = null;
|
|
1917
1971
|
};
|
|
1918
1972
|
};
|
|
1919
1973
|
this.registerEditorListeners = (editor) => {
|
|
1920
|
-
return mergeRegister(editor.registerUpdateListener(({ editorState }) => {
|
|
1974
|
+
return mergeRegister(editor.registerUpdateListener(({ editorState, tags }) => {
|
|
1921
1975
|
const runtime = this.getRuntime();
|
|
1922
|
-
this.handleEditorUpdate(editorState, runtime.callbacks);
|
|
1976
|
+
this.handleEditorUpdate(editorState, runtime.callbacks, tags);
|
|
1923
1977
|
}), editor.registerCommand(SELECTION_CHANGE_COMMAND, () => {
|
|
1924
1978
|
const runtime = this.getRuntime();
|
|
1925
1979
|
this.handleSelectionChange(editor, runtime.callbacks);
|
|
@@ -1932,13 +1986,12 @@ var ChatComposerLexicalOwner = class {
|
|
|
1932
1986
|
return this.handleKeyDown({
|
|
1933
1987
|
actions: runtime.actions,
|
|
1934
1988
|
callbacks: runtime.callbacks,
|
|
1935
|
-
event
|
|
1936
|
-
fallbackNodes: runtime.fallbackNodes
|
|
1989
|
+
event
|
|
1937
1990
|
});
|
|
1938
1991
|
}, COMMAND_PRIORITY_HIGH));
|
|
1939
1992
|
};
|
|
1940
1993
|
this.syncExternalState = (editor, nodes) => {
|
|
1941
|
-
if (
|
|
1994
|
+
if (editor.isComposing()) return;
|
|
1942
1995
|
const nextSignature = getChatComposerNodesSignature(nodes);
|
|
1943
1996
|
const pendingSelection = this.pendingSelectionRef.current;
|
|
1944
1997
|
const pendingOwnerSignature = this.pendingOwnerSignatureRef.current;
|
|
@@ -1948,12 +2001,11 @@ var ChatComposerLexicalOwner = class {
|
|
|
1948
2001
|
}
|
|
1949
2002
|
const shouldSyncDocument = nextSignature !== this.editorSignatureRef.current;
|
|
1950
2003
|
if (!shouldSyncDocument && !pendingSelection) return;
|
|
1951
|
-
this.startApplyingExternalUpdate();
|
|
1952
2004
|
const preserveDomSelection = editor.getRootElement() !== document.activeElement;
|
|
1953
2005
|
if (shouldSyncDocument) {
|
|
1954
|
-
syncLexicalEditorFromChatComposerState(editor, nodes, pendingSelection, preserveDomSelection);
|
|
1955
2006
|
this.editorSignatureRef.current = nextSignature;
|
|
1956
2007
|
this.lastPublishedSignatureRef.current = nextSignature;
|
|
2008
|
+
syncLexicalEditorFromChatComposerState(editor, nodes, pendingSelection, preserveDomSelection);
|
|
1957
2009
|
} else if (pendingSelection) syncLexicalSelectionFromChatComposerSelection(editor, pendingSelection, preserveDomSelection);
|
|
1958
2010
|
if (pendingSelection) {
|
|
1959
2011
|
this.selectionRef.current = pendingSelection;
|
|
@@ -1975,12 +2027,11 @@ var ChatComposerLexicalOwner = class {
|
|
|
1975
2027
|
const { editor } = this;
|
|
1976
2028
|
this.pendingOwnerSignatureRef.current = signature;
|
|
1977
2029
|
if (editor) {
|
|
1978
|
-
this.startApplyingExternalUpdate();
|
|
1979
|
-
syncLexicalEditorFromChatComposerState(editor, snapshot.nodes, snapshot.selection);
|
|
1980
2030
|
this.editorSignatureRef.current = signature;
|
|
2031
|
+
syncLexicalEditorFromChatComposerState(editor, snapshot.nodes, snapshot.selection);
|
|
1981
2032
|
}
|
|
1982
2033
|
callbacks.onInputSurfaceSnapshotChange?.(snapshot.nodes, snapshot.selection, options?.inputSurfaceReason ?? { type: "programmatic" });
|
|
1983
|
-
if (
|
|
2034
|
+
if (signature !== this.lastPublishedSignatureRef.current) {
|
|
1984
2035
|
this.lastPublishedSignatureRef.current = signature;
|
|
1985
2036
|
callbacks.onNodesChange(snapshot.nodes);
|
|
1986
2037
|
}
|
|
@@ -2005,11 +2056,10 @@ var ChatComposerLexicalOwner = class {
|
|
|
2005
2056
|
this.pendingSelectionRef.current = targetSelection;
|
|
2006
2057
|
editor.getRootElement()?.focus({ preventScroll: true });
|
|
2007
2058
|
if (nodes) {
|
|
2008
|
-
this.startApplyingExternalUpdate();
|
|
2009
2059
|
const signature = getChatComposerNodesSignature(nodes);
|
|
2010
|
-
syncLexicalEditorFromChatComposerState(editor, nodes, targetSelection);
|
|
2011
2060
|
this.editorSignatureRef.current = signature;
|
|
2012
2061
|
this.lastPublishedSignatureRef.current = signature;
|
|
2062
|
+
syncLexicalEditorFromChatComposerState(editor, nodes, targetSelection);
|
|
2013
2063
|
} else syncLexicalSelectionFromChatComposerSelection(editor, targetSelection);
|
|
2014
2064
|
editor.focus(() => {
|
|
2015
2065
|
syncLexicalSelectionFromChatComposerSelection(editor, targetSelection);
|
|
@@ -2069,55 +2119,35 @@ var ChatComposerLexicalOwner = class {
|
|
|
2069
2119
|
}
|
|
2070
2120
|
};
|
|
2071
2121
|
};
|
|
2072
|
-
this.handleBeforeInput = (params) => {
|
|
2073
|
-
const { disabled, event } = params;
|
|
2074
|
-
const { callbacks, fallbackNodes } = this.getRuntime();
|
|
2075
|
-
handleLexicalComposerBeforeInput({
|
|
2076
|
-
disabled,
|
|
2077
|
-
event,
|
|
2078
|
-
isComposing: this.isComposingRef.current,
|
|
2079
|
-
publishSnapshot: (snapshot, options) => this.publishSnapshot(snapshot, callbacks, options),
|
|
2080
|
-
snapshotReader: () => this.readComposerSnapshot(fallbackNodes)
|
|
2081
|
-
});
|
|
2082
|
-
};
|
|
2083
|
-
this.handleCompositionStart = () => {
|
|
2084
|
-
const { fallbackNodes } = this.getRuntime();
|
|
2085
|
-
this.compositionStartSnapshotRef.current = this.readComposerSnapshot(fallbackNodes);
|
|
2086
|
-
this.isComposingRef.current = true;
|
|
2087
|
-
};
|
|
2088
|
-
this.handleCompositionEnd = (params) => {
|
|
2089
|
-
const { data } = params;
|
|
2090
|
-
const { callbacks, fallbackNodes } = this.getRuntime();
|
|
2091
|
-
this.isComposingRef.current = false;
|
|
2092
|
-
handleLexicalComposerCompositionEnd({
|
|
2093
|
-
compositionStartSnapshot: this.compositionStartSnapshotRef.current,
|
|
2094
|
-
data,
|
|
2095
|
-
fallbackSnapshot: () => this.readComposerSnapshot(fallbackNodes),
|
|
2096
|
-
publishSnapshot: (snapshot, options) => this.publishSnapshot(snapshot, callbacks, options),
|
|
2097
|
-
snapshotReader: () => this.readComposerSnapshot(fallbackNodes)
|
|
2098
|
-
});
|
|
2099
|
-
this.compositionStartSnapshotRef.current = null;
|
|
2100
|
-
};
|
|
2101
2122
|
this.handleKeyDown = (params) => {
|
|
2102
|
-
const { actions, callbacks, event
|
|
2103
|
-
|
|
2123
|
+
const { actions, callbacks, event } = params;
|
|
2124
|
+
const isComposing = event.isComposing || (this.editor?.isComposing() ?? false);
|
|
2125
|
+
if (event.key.length === 1 && !isComposing && !event.altKey && !event.ctrlKey && !event.metaKey) this.pendingInputSurfaceReasonRef.current = {
|
|
2104
2126
|
type: "insert-text",
|
|
2105
2127
|
text: event.key
|
|
2106
2128
|
};
|
|
2107
|
-
|
|
2108
|
-
if (
|
|
2129
|
+
else if (!isComposing && (event.key === "Backspace" || event.key === "Delete")) this.pendingInputSurfaceReasonRef.current = { type: "delete-content" };
|
|
2130
|
+
else if (!isComposing && event.key === "Enter" && event.shiftKey) this.pendingInputSurfaceReasonRef.current = {
|
|
2131
|
+
type: "insert-text",
|
|
2132
|
+
text: "\n"
|
|
2133
|
+
};
|
|
2134
|
+
if (callbacks.onInputSurfaceKeyDown?.(event)) {
|
|
2135
|
+
this.pendingInputSurfaceReasonRef.current = null;
|
|
2136
|
+
return true;
|
|
2137
|
+
}
|
|
2138
|
+
if (!isComposing && deleteAdjacentChatComposerToken(event)) return true;
|
|
2109
2139
|
return handleLexicalComposerKeyboardCommand({
|
|
2110
2140
|
actions,
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
snapshot
|
|
2141
|
+
isComposing,
|
|
2142
|
+
nativeEvent: event
|
|
2114
2143
|
});
|
|
2115
2144
|
};
|
|
2116
|
-
this.handleEditorUpdate = (editorState, callbacks) => {
|
|
2145
|
+
this.handleEditorUpdate = (editorState, callbacks, tags) => {
|
|
2117
2146
|
const snapshot = readChatComposerSnapshotFromEditorState(editorState);
|
|
2118
2147
|
const signature = getChatComposerNodesSignature(snapshot.nodes);
|
|
2148
|
+
const expectedExternalSignature = this.editorSignatureRef.current;
|
|
2119
2149
|
this.editorSignatureRef.current = signature;
|
|
2120
|
-
if (
|
|
2150
|
+
if (tags.has("nextclaw-chat-composer-external-update") && signature === expectedExternalSignature || this.editor?.isComposing() && !tags.has(COMPOSITION_END_TAG)) return;
|
|
2121
2151
|
this.selectionRef.current = snapshot.selection;
|
|
2122
2152
|
callbacks.onInputSurfaceSnapshotChange?.(snapshot.nodes, snapshot.selection, this.consumeInputSurfaceReason() ?? { type: "sync" });
|
|
2123
2153
|
if (signature === this.lastPublishedSignatureRef.current) return;
|
|
@@ -2126,7 +2156,7 @@ var ChatComposerLexicalOwner = class {
|
|
|
2126
2156
|
};
|
|
2127
2157
|
this.handleSelectionChange = (editor, callbacks) => {
|
|
2128
2158
|
const snapshot = readChatComposerSnapshotFromEditorState(editor.getEditorState());
|
|
2129
|
-
if (
|
|
2159
|
+
if (editor.isComposing()) return;
|
|
2130
2160
|
this.selectionRef.current = snapshot.selection;
|
|
2131
2161
|
callbacks.onInputSurfaceSnapshotChange?.(snapshot.nodes, snapshot.selection, { type: "selection" });
|
|
2132
2162
|
};
|
|
@@ -2139,12 +2169,6 @@ var ChatComposerLexicalOwner = class {
|
|
|
2139
2169
|
const { callbacks, fallbackNodes } = this.getRuntime();
|
|
2140
2170
|
this.publishSnapshot(createSnapshot(this.readComposerSnapshot(fallbackNodes)), callbacks, options);
|
|
2141
2171
|
};
|
|
2142
|
-
this.startApplyingExternalUpdate = () => {
|
|
2143
|
-
this.isApplyingExternalUpdateRef.current = true;
|
|
2144
|
-
requestAnimationFrame(() => {
|
|
2145
|
-
this.isApplyingExternalUpdateRef.current = false;
|
|
2146
|
-
});
|
|
2147
|
-
};
|
|
2148
2172
|
this.getRuntime = () => {
|
|
2149
2173
|
if (!this.runtime) throw new Error("ChatComposerLexicalOwner runtime has not been configured.");
|
|
2150
2174
|
return this.runtime;
|
|
@@ -2215,19 +2239,6 @@ const ChatInputBarTokenizedComposer = forwardRef(function ChatInputBarTokenizedC
|
|
|
2215
2239
|
children: /* @__PURE__ */ jsx(PlainTextPlugin, {
|
|
2216
2240
|
contentEditable: /* @__PURE__ */ jsx(ContentEditable, {
|
|
2217
2241
|
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",
|
|
2218
|
-
onBeforeInput: (event) => {
|
|
2219
|
-
owner.handleBeforeInput({
|
|
2220
|
-
disabled,
|
|
2221
|
-
event
|
|
2222
|
-
});
|
|
2223
|
-
},
|
|
2224
|
-
onCompositionEnd: (event) => {
|
|
2225
|
-
const nativeEvent = event.nativeEvent;
|
|
2226
|
-
owner.handleCompositionEnd({ data: typeof nativeEvent.data === "string" ? nativeEvent.data : "" });
|
|
2227
|
-
},
|
|
2228
|
-
onCompositionStart: () => {
|
|
2229
|
-
owner.handleCompositionStart();
|
|
2230
|
-
},
|
|
2231
2242
|
onPaste: (event) => {
|
|
2232
2243
|
const files = Array.from(event.clipboardData.files ?? []);
|
|
2233
2244
|
if (files.length > 0 && onFilesAdd) {
|
|
@@ -2367,7 +2378,13 @@ const ChatInputBar = forwardRef(function ChatInputBar({ composer, hint, inputSur
|
|
|
2367
2378
|
children: /* @__PURE__ */ jsx(ChatInputSurfaceHost, {
|
|
2368
2379
|
inputSurface: resolvedInputSurface,
|
|
2369
2380
|
onInputSurfaceTriggerChange: composer.onInputSurfaceTriggerChange,
|
|
2370
|
-
onSelectItem: (item) =>
|
|
2381
|
+
onSelectItem: (item) => {
|
|
2382
|
+
if (item.selectionBehavior === "navigate") {
|
|
2383
|
+
resolvedInputSurface?.onSelectItem?.(item);
|
|
2384
|
+
return;
|
|
2385
|
+
}
|
|
2386
|
+
composerRef.current?.insertInputSurfaceItem(item, composer.inputSurfaceTriggerSpecs);
|
|
2387
|
+
},
|
|
2371
2388
|
triggerSpecs: composer.inputSurfaceTriggerSpecs,
|
|
2372
2389
|
children: ({ onInputSurfaceKeyDown, onInputSurfaceOpenChange, onInputSurfaceSnapshotChange }) => /* @__PURE__ */ jsx(ChatInputBarTokenizedComposer, {
|
|
2373
2390
|
ref: composerRef,
|
|
@@ -2426,6 +2443,7 @@ function ChatMessageAvatar({ role, size = "default" }) {
|
|
|
2426
2443
|
function resolveInlineTokenTone(kind) {
|
|
2427
2444
|
if (kind === "skill") return "skill";
|
|
2428
2445
|
if (kind === "panel_app") return "panel_app";
|
|
2446
|
+
if (kind === "workspace_file" || kind === "workspace_directory") return "workspace";
|
|
2429
2447
|
return "default";
|
|
2430
2448
|
}
|
|
2431
2449
|
function resolveInlineTokenBadgeClassName(tone, isUser, interactive) {
|
|
@@ -2438,10 +2456,16 @@ function resolveInlineTokenIconClassName(tone, isUser) {
|
|
|
2438
2456
|
if (tone === "panel_app") return isUser ? "text-primary-foreground/80" : "text-muted-foreground";
|
|
2439
2457
|
return isUser ? "text-primary-foreground/80" : "text-muted-foreground";
|
|
2440
2458
|
}
|
|
2441
|
-
function renderInlineTokenIcon(tone) {
|
|
2459
|
+
function renderInlineTokenIcon(tone, kind) {
|
|
2442
2460
|
return tone === "panel_app" ? /* @__PURE__ */ jsx(AppWindow, {
|
|
2443
2461
|
"aria-hidden": "true",
|
|
2444
2462
|
className: "h-3 w-3"
|
|
2463
|
+
}) : kind === "workspace_file" ? /* @__PURE__ */ jsx(FileText, {
|
|
2464
|
+
"aria-hidden": "true",
|
|
2465
|
+
className: "h-3 w-3"
|
|
2466
|
+
}) : kind === "workspace_directory" ? /* @__PURE__ */ jsx(Folder, {
|
|
2467
|
+
"aria-hidden": "true",
|
|
2468
|
+
className: "h-3 w-3"
|
|
2445
2469
|
}) : /* @__PURE__ */ jsx(Puzzle, {
|
|
2446
2470
|
"aria-hidden": "true",
|
|
2447
2471
|
className: "h-3 w-3"
|
|
@@ -2453,7 +2477,7 @@ function ChatInlineTokenBadge({ kind, label, isUser, onClick }) {
|
|
|
2453
2477
|
const className = cn("nextclaw-chat-inline-token mx-[2px] inline-flex max-w-full items-center gap-1 align-baseline text-[11px] font-medium", tone === "skill" ? "h-auto rounded-none px-0 py-0" : "h-7 rounded-xl border px-2.5", resolveInlineTokenBadgeClassName(tone, isUser, interactive));
|
|
2454
2478
|
const content = /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
|
|
2455
2479
|
className: cn("inline-flex h-3.5 w-3.5 shrink-0 items-center justify-center", resolveInlineTokenIconClassName(tone, isUser)),
|
|
2456
|
-
children: renderInlineTokenIcon(tone)
|
|
2480
|
+
children: renderInlineTokenIcon(tone, kind)
|
|
2457
2481
|
}), /* @__PURE__ */ jsx("span", {
|
|
2458
2482
|
className: "truncate",
|
|
2459
2483
|
children: label
|
|
@@ -4389,8 +4413,7 @@ const FILE_TEXT_CLASS_NAME = "font-mono text-[11px] leading-5";
|
|
|
4389
4413
|
const FILE_ROW_CLASS_NAME = `flex h-5 w-full ${FILE_TEXT_CLASS_NAME}`;
|
|
4390
4414
|
const FILE_LINE_NUMBER_CELL_CLASS_NAME = "flex h-5 items-center justify-center px-2.5 tabular-nums select-none";
|
|
4391
4415
|
function readVisibleLineNumber(line) {
|
|
4392
|
-
|
|
4393
|
-
return typeof value === "number" ? String(value) : "";
|
|
4416
|
+
return String(line.newLineNumber ?? line.oldLineNumber ?? "");
|
|
4394
4417
|
}
|
|
4395
4418
|
function isTargetLine(line, targetLine) {
|
|
4396
4419
|
return typeof targetLine === "number" && (line.newLineNumber ?? line.oldLineNumber) === targetLine;
|
|
@@ -4438,8 +4461,9 @@ function getCodeRowTone(line) {
|
|
|
4438
4461
|
if (line.kind === "add") return "bg-emerald-50 text-emerald-950";
|
|
4439
4462
|
return "bg-card text-foreground";
|
|
4440
4463
|
}
|
|
4441
|
-
function FileOperationLineNumberCell({ layout, line, lineNumberColumnWidth, target }) {
|
|
4464
|
+
function FileOperationLineNumberCell({ layout, line, lineNumberColumnWidth, target, targetRef }) {
|
|
4442
4465
|
return /* @__PURE__ */ jsx("span", {
|
|
4466
|
+
ref: targetRef,
|
|
4443
4467
|
"data-file-line-number-cell": "true",
|
|
4444
4468
|
style: layout === "compact" ? {
|
|
4445
4469
|
width: lineNumberColumnWidth,
|
|
@@ -4513,7 +4537,8 @@ function FileOperationWorkspaceSurface({ block, targetColumn, targetLine }) {
|
|
|
4513
4537
|
layout: "workspace",
|
|
4514
4538
|
line,
|
|
4515
4539
|
lineNumberColumnWidth,
|
|
4516
|
-
target: isTargetLine(line, targetLine)
|
|
4540
|
+
target: isTargetLine(line, targetLine),
|
|
4541
|
+
targetRef: !targetColumn && isTargetLine(line, targetLine) ? targetRef : void 0
|
|
4517
4542
|
}, readLineKey("gutter", line, index))), /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 border-r border-border bg-muted" })]
|
|
4518
4543
|
}) : null, /* @__PURE__ */ jsxs("div", {
|
|
4519
4544
|
"data-file-code-canvas": "true",
|
|
@@ -4534,7 +4559,7 @@ function FileOperationWorkspaceSurface({ block, targetColumn, targetLine }) {
|
|
|
4534
4559
|
line,
|
|
4535
4560
|
target,
|
|
4536
4561
|
targetColumn: target ? targetColumn : null,
|
|
4537
|
-
targetRef: target ? targetRef : void 0
|
|
4562
|
+
targetRef: target && (targetColumn || !showLineNumbers) ? targetRef : void 0
|
|
4538
4563
|
})
|
|
4539
4564
|
}, readLineKey("code", line, index));
|
|
4540
4565
|
})
|