@invergent/agent-chat-react 2.8.1 → 2.8.4

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.cjs CHANGED
@@ -553,20 +553,69 @@ function BrowserControlBar({
553
553
  )
554
554
  ] });
555
555
  }
556
+ function toWsUrl(src) {
557
+ const url = new URL(src, window.location.href);
558
+ if (url.protocol === "http:") {
559
+ url.protocol = "ws:";
560
+ } else if (url.protocol === "https:") {
561
+ url.protocol = "wss:";
562
+ }
563
+ return url.toString();
564
+ }
556
565
  function BrowserLiveView({
557
566
  src,
558
- testId = "browser-iframe"
567
+ testId = "browser-rfb",
568
+ onDisconnect
559
569
  }) {
560
- return /* @__PURE__ */ jsxRuntime.jsx(
561
- "iframe",
562
- {
563
- "data-testid": testId,
564
- title: "Browser live view",
565
- src,
566
- className: "h-full w-full border-0 bg-black",
567
- allow: "clipboard-read; clipboard-write"
568
- }
569
- );
570
+ const containerRef = react.useRef(null);
571
+ const onDisconnectRef = react.useRef(onDisconnect);
572
+ onDisconnectRef.current = onDisconnect;
573
+ const [state, setState] = react.useState("connecting");
574
+ react.useEffect(() => {
575
+ const container = containerRef.current;
576
+ if (!container) return;
577
+ setState("connecting");
578
+ let rfb;
579
+ let disposed = false;
580
+ const handleConnect = () => setState("connected");
581
+ const handleDisconnect = (event) => {
582
+ const detail = event.detail;
583
+ setState("disconnected");
584
+ onDisconnectRef.current?.(detail?.clean ?? false);
585
+ };
586
+ void import('@novnc/novnc').then((mod) => {
587
+ if (disposed || !containerRef.current) return;
588
+ const instance = new mod.default(containerRef.current, toWsUrl(src), {
589
+ wsProtocols: ["binary"]
590
+ });
591
+ instance.viewOnly = false;
592
+ instance.scaleViewport = true;
593
+ instance.addEventListener("connect", handleConnect);
594
+ instance.addEventListener("disconnect", handleDisconnect);
595
+ rfb = instance;
596
+ }).catch(() => {
597
+ if (!disposed) setState("disconnected");
598
+ });
599
+ return () => {
600
+ disposed = true;
601
+ if (rfb) {
602
+ rfb.removeEventListener("connect", handleConnect);
603
+ rfb.removeEventListener("disconnect", handleDisconnect);
604
+ rfb.disconnect();
605
+ }
606
+ };
607
+ }, [src]);
608
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative h-full w-full bg-black", children: [
609
+ /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, "data-testid": testId, className: "h-full w-full" }),
610
+ state !== "connected" && /* @__PURE__ */ jsxRuntime.jsx(
611
+ "div",
612
+ {
613
+ "data-testid": "browser-rfb-overlay",
614
+ className: "pointer-events-none absolute inset-0 flex items-center justify-center bg-black/70 text-sm text-muted-foreground",
615
+ children: state === "connecting" ? "Connecting to browser\u2026" : "Live view disconnected \u2014 take control again to reconnect."
616
+ }
617
+ )
618
+ ] });
570
619
  }
571
620
 
572
621
  // src/components/browser/browser-status-dot.tsx
@@ -767,7 +816,13 @@ function BrowserPane({
767
816
  /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "bottom", children: "Full screen" })
768
817
  ] }) }) })
769
818
  ] }),
770
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-0 flex-1 bg-black", children: state.status === "provisioning" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: "Starting browser..." }) : state.status === "closed" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: "Browser closed." }) : canUseLiveView ? /* @__PURE__ */ jsxRuntime.jsx(BrowserLiveView, { src: liveViewUrl }) : previewSnapshot ? /* @__PURE__ */ jsxRuntime.jsx(
819
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-0 flex-1 bg-black", children: state.status === "provisioning" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: "Starting browser..." }) : state.status === "closed" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: "Browser closed." }) : canUseLiveView ? /* @__PURE__ */ jsxRuntime.jsx(
820
+ BrowserLiveView,
821
+ {
822
+ src: liveViewUrl,
823
+ onDisconnect: () => setLocalControlActive(false)
824
+ }
825
+ ) : previewSnapshot ? /* @__PURE__ */ jsxRuntime.jsx(
771
826
  BrowserPreviewImage,
772
827
  {
773
828
  src: previewSnapshot.src,
@@ -811,7 +866,8 @@ function BrowserPane({
811
866
  BrowserLiveView,
812
867
  {
813
868
  src: liveViewUrl,
814
- testId: "browser-fullscreen-iframe"
869
+ testId: "browser-fullscreen-rfb",
870
+ onDisconnect: () => setLocalControlActive(false)
815
871
  }
816
872
  ) : previewSnapshot ? /* @__PURE__ */ jsxRuntime.jsx(
817
873
  BrowserPreviewImage,
@@ -914,6 +970,39 @@ var ConversationScrollButton = ({
914
970
  );
915
971
  };
916
972
 
973
+ // src/components/ui/button-group.tsx
974
+ init_utils();
975
+ var buttonGroupVariants = classVarianceAuthority.cva(
976
+ "group/button-group flex w-fit items-stretch *:focus-visible:relative *:focus-visible:z-10 has-[>[data-slot=button-group]]:gap-2 *:data-[slot=input]:px-4 has-[>[data-variant=outline]]:*:data-[slot=input-group]:border-border has-[>[data-variant=outline]]:*:data-[slot=input-group]:px-2.5 has-[>[data-variant=outline]]:*:data-[slot=select-trigger]:border-border has-[>[data-variant=outline]]:*:[[role=combobox]]:px-2.5 has-[>[data-variant=outline]]:[&>[data-slot=input-group]:has(:focus-visible)]:border-ring has-[>[data-variant=outline]]:[&>[data-slot=select-trigger]:focus-visible]:border-ring has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-none [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1 has-[>[data-variant=outline]]:[&>input]:border-border has-[>[data-variant=outline]]:[&>input:focus-visible]:border-ring",
977
+ {
978
+ variants: {
979
+ orientation: {
980
+ horizontal: "[&>*:not(:first-child)]:rounded-l-none [&>*:not(:first-child)]:border-l-0 [&>*:not(:last-child)]:rounded-r-none [&>[data-slot]:not(:has(~[data-slot]))]:rounded-none!",
981
+ vertical: "flex-col [&>*:not(:first-child)]:rounded-t-none [&>*:not(:first-child)]:border-t-0 [&>*:not(:last-child)]:rounded-b-none [&>[data-slot]:not(:has(~[data-slot]))]:rounded-none!"
982
+ }
983
+ },
984
+ defaultVariants: {
985
+ orientation: "horizontal"
986
+ }
987
+ }
988
+ );
989
+ function ButtonGroup({
990
+ className,
991
+ orientation,
992
+ ...props
993
+ }) {
994
+ return /* @__PURE__ */ jsxRuntime.jsx(
995
+ "div",
996
+ {
997
+ role: "group",
998
+ "data-slot": "button-group",
999
+ "data-orientation": orientation,
1000
+ className: cn(buttonGroupVariants({ orientation }), className),
1001
+ ...props
1002
+ }
1003
+ );
1004
+ }
1005
+
917
1006
  // src/components/ai-elements/message.tsx
918
1007
  init_utils();
919
1008
 
@@ -978,8 +1067,8 @@ var Message = ({ className, from, ...props }) => /* @__PURE__ */ jsxRuntime.jsx(
978
1067
  "div",
979
1068
  {
980
1069
  className: cn(
981
- "group flex w-full flex-col gap-2",
982
- from === "user" ? "is-user max-w-[85%]" : "is-assistant max-w-full",
1070
+ "group flex flex-col gap-2",
1071
+ from === "user" ? "is-user ml-auto max-w-[85%]" : "is-assistant max-w-full",
983
1072
  className
984
1073
  ),
985
1074
  ...props
@@ -5117,25 +5206,6 @@ function Command({
5117
5206
  }
5118
5207
  );
5119
5208
  }
5120
- function CommandInput({
5121
- className,
5122
- ...props
5123
- }) {
5124
- return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-slot": "command-input-wrapper", className: "p-3", children: /* @__PURE__ */ jsxRuntime.jsxs(InputGroup, { className: "rounded-lg border-transparent bg-muted px-3", children: [
5125
- /* @__PURE__ */ jsxRuntime.jsx(
5126
- cmdk.Command.Input,
5127
- {
5128
- "data-slot": "command-input",
5129
- className: cn(
5130
- "w-full px-2 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50",
5131
- className
5132
- ),
5133
- ...props
5134
- }
5135
- ),
5136
- /* @__PURE__ */ jsxRuntime.jsx(InputGroupAddon, { children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.SearchIcon, { className: "size-3.5 shrink-0 opacity-50" }) })
5137
- ] }) });
5138
- }
5139
5209
  function CommandList({
5140
5210
  className,
5141
5211
  ...props
@@ -5667,7 +5737,7 @@ var PromptInput = ({
5667
5737
  onSubmit: handleSubmit,
5668
5738
  ref: formRef,
5669
5739
  ...props,
5670
- children: /* @__PURE__ */ jsxRuntime.jsx(InputGroup, { className: "overflow-hidden rounded-3xl border border-input bg-white px-3 py-1 shadow-sm dark:bg-card dark:shadow-none dark:ring-1 dark:ring-white/5 has-[textarea]:rounded-3xl has-[>[data-align=block-end]]:rounded-3xl has-[>[data-align=block-start]]:rounded-3xl", children })
5740
+ children: /* @__PURE__ */ jsxRuntime.jsx(InputGroup, { className: "overflow-hidden rounded-3xl border border-input bg-white px-4 py-2 shadow-sm dark:bg-card dark:shadow-none dark:ring-1 dark:ring-white/5 has-[textarea]:rounded-3xl has-[>[data-align=block-end]]:rounded-3xl has-[>[data-align=block-start]]:rounded-3xl", children })
5671
5741
  }
5672
5742
  )
5673
5743
  ] });
@@ -5802,7 +5872,7 @@ var PromptInputButton = ({
5802
5872
  InputGroupButton,
5803
5873
  {
5804
5874
  className: cn(
5805
- "rounded-sm border border-input/70 text-foreground hover:bg-accent",
5875
+ "rounded-sm border border-input/70 text-foreground bg-accent",
5806
5876
  newSize === "icon-sm" && "size-9",
5807
5877
  className
5808
5878
  ),
@@ -5860,7 +5930,7 @@ var PromptInputSubmit = ({
5860
5930
  InputGroupButton,
5861
5931
  {
5862
5932
  "aria-label": isGenerating ? "Stop" : "Submit",
5863
- className: cn("rounded-full", className),
5933
+ className: cn("rounded-full bg-amber-400 hover:bg-amber-500 text-black", className),
5864
5934
  onClick: handleClick,
5865
5935
  size,
5866
5936
  type: isGenerating && onStop ? "button" : "submit",
@@ -6358,6 +6428,168 @@ function Skeleton({ className, ...props }) {
6358
6428
  }
6359
6429
  );
6360
6430
  }
6431
+ var SEARCH_PLACEHOLDER = {
6432
+ commands: "Search commands\u2026",
6433
+ skills: "Search skills\u2026",
6434
+ scheduled: "Search scheduled tasks\u2026",
6435
+ all: "Type a command or search\u2026"
6436
+ };
6437
+ var NAV_HINT = "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc dismiss";
6438
+ var SEARCH_HINT = {
6439
+ commands: NAV_HINT,
6440
+ skills: "Esc dismiss",
6441
+ scheduled: NAV_HINT,
6442
+ all: NAV_HINT
6443
+ };
6444
+ var ROW_CLASS = "grid grid-cols-[120px_1fr] items-baseline gap-3 rounded-md px-3 py-[9px] data-[selected=true]:bg-foreground/[0.06] [&_svg]:hidden";
6445
+ function ComposerCommandMenu({
6446
+ menuMode,
6447
+ searchQuery,
6448
+ onSearchChange,
6449
+ skillsLoading,
6450
+ builtinCommands,
6451
+ adapterCommands,
6452
+ scheduledExamples,
6453
+ onCommandSelect,
6454
+ onEscapeDismiss
6455
+ }) {
6456
+ const controlledSearch = menuMode === "commands" || menuMode === "all";
6457
+ const showCommands = menuMode === "commands" || menuMode === "all";
6458
+ const showSkills = menuMode === "skills" || menuMode === "all";
6459
+ const skillsUnattached = menuMode === "skills" && !skillsLoading && adapterCommands.length === 0;
6460
+ return /* @__PURE__ */ jsxRuntime.jsx(
6461
+ PopoverContent,
6462
+ {
6463
+ side: "top",
6464
+ align: "start",
6465
+ className: "overflow-hidden rounded-xl p-0",
6466
+ style: { width: "var(--radix-popover-trigger-width)" },
6467
+ onCloseAutoFocus: (e) => e.preventDefault(),
6468
+ onEscapeKeyDown: () => {
6469
+ onEscapeDismiss();
6470
+ },
6471
+ children: /* @__PURE__ */ jsxRuntime.jsxs(Command, { children: [
6472
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2.5 px-4 py-[13px]", children: [
6473
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.SearchIcon, { className: "size-3.5 shrink-0 text-muted-foreground" }),
6474
+ /* @__PURE__ */ jsxRuntime.jsx(
6475
+ cmdk.Command.Input,
6476
+ {
6477
+ "data-slot": "command-input",
6478
+ placeholder: SEARCH_PLACEHOLDER[menuMode],
6479
+ className: "min-w-0 flex-1 bg-transparent text-[13.5px] text-foreground outline-hidden placeholder:text-muted-foreground",
6480
+ ...controlledSearch ? { value: searchQuery, onValueChange: onSearchChange } : {}
6481
+ }
6482
+ ),
6483
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 whitespace-nowrap text-[11px] text-muted-foreground", children: SEARCH_HINT[menuMode] })
6484
+ ] }),
6485
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-px w-full bg-foreground/[0.07]" }),
6486
+ skillsUnattached ? /* @__PURE__ */ jsxRuntime.jsx(SkillsEmptyState, {}) : /* @__PURE__ */ jsxRuntime.jsxs(CommandList, { className: "p-2", children: [
6487
+ !(skillsLoading && showSkills) && /* @__PURE__ */ jsxRuntime.jsx(CommandEmpty, { className: "px-3 py-8 text-center text-sm text-muted-foreground", children: menuMode === "skills" ? "No skills match your search." : menuMode === "scheduled" ? "No scheduled tasks found." : "No commands found." }),
6488
+ showSkills && skillsLoading && adapterCommands.length === 0 && [0, 1, 2].map((i) => /* @__PURE__ */ jsxRuntime.jsxs(
6489
+ "div",
6490
+ {
6491
+ className: "grid grid-cols-[120px_1fr] items-baseline gap-3 px-3 py-[9px]",
6492
+ "aria-hidden": "true",
6493
+ children: [
6494
+ /* @__PURE__ */ jsxRuntime.jsx(Skeleton, { className: "h-4 w-20" }),
6495
+ /* @__PURE__ */ jsxRuntime.jsx(Skeleton, { className: "h-3 w-full max-w-[16rem]" })
6496
+ ]
6497
+ },
6498
+ `skill-skeleton-${i}`
6499
+ )),
6500
+ menuMode === "scheduled" && scheduledExamples.map((cmd) => /* @__PURE__ */ jsxRuntime.jsx(
6501
+ MenuRow,
6502
+ {
6503
+ cmd,
6504
+ onSelect: onCommandSelect
6505
+ },
6506
+ cmd.value
6507
+ )),
6508
+ showCommands && builtinCommands.map((cmd) => /* @__PURE__ */ jsxRuntime.jsx(
6509
+ MenuRow,
6510
+ {
6511
+ cmd,
6512
+ onSelect: onCommandSelect
6513
+ },
6514
+ cmd.value
6515
+ )),
6516
+ showSkills && adapterCommands.map((cmd) => /* @__PURE__ */ jsxRuntime.jsx(
6517
+ MenuRow,
6518
+ {
6519
+ cmd,
6520
+ keywords: cmd.isExpert ? [cmd.description, "expert"] : void 0,
6521
+ badge: cmd.isExpert ? "expert" : void 0,
6522
+ onSelect: onCommandSelect
6523
+ },
6524
+ cmd.value
6525
+ ))
6526
+ ] })
6527
+ ] })
6528
+ }
6529
+ );
6530
+ }
6531
+ function MenuRow({
6532
+ cmd,
6533
+ keywords,
6534
+ badge,
6535
+ onSelect
6536
+ }) {
6537
+ return /* @__PURE__ */ jsxRuntime.jsxs(
6538
+ CommandItem,
6539
+ {
6540
+ value: cmd.value,
6541
+ keywords: keywords ?? [cmd.description],
6542
+ onSelect: () => onSelect(cmd.value),
6543
+ className: ROW_CLASS,
6544
+ children: [
6545
+ /* @__PURE__ */ jsxRuntime.jsxs(
6546
+ "span",
6547
+ {
6548
+ className: "inline-flex min-w-0 max-w-full items-center gap-1.5",
6549
+ title: cmd.label,
6550
+ children: [
6551
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate font-mono text-[13px] font-medium text-amber-500", children: cmd.label }),
6552
+ badge && /* @__PURE__ */ jsxRuntime.jsx(
6553
+ "span",
6554
+ {
6555
+ className: "shrink-0 rounded-sm bg-amber-500/10 px-1 text-[9px] font-semibold uppercase tracking-wider text-amber-500",
6556
+ "aria-label": "Expert specialist",
6557
+ children: badge
6558
+ }
6559
+ )
6560
+ ]
6561
+ }
6562
+ ),
6563
+ /* @__PURE__ */ jsxRuntime.jsx(
6564
+ "span",
6565
+ {
6566
+ className: "min-w-0 truncate text-[12.5px] text-muted-foreground",
6567
+ title: cmd.description,
6568
+ children: cmd.description
6569
+ }
6570
+ )
6571
+ ]
6572
+ }
6573
+ );
6574
+ }
6575
+ function SkillsEmptyState() {
6576
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-2.5 px-6 py-[30px] text-center", children: [
6577
+ /* @__PURE__ */ jsxRuntime.jsx(
6578
+ "span",
6579
+ {
6580
+ className: "text-2xl leading-none text-muted-foreground/70",
6581
+ "aria-hidden": "true",
6582
+ children: "\u2726"
6583
+ }
6584
+ ),
6585
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-foreground", children: "No skills attached" }),
6586
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-[12.5px] text-muted-foreground", children: [
6587
+ "Add them in",
6588
+ " ",
6589
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-amber-500", children: "Configure \u2192 Skills & Tools" })
6590
+ ] })
6591
+ ] });
6592
+ }
6361
6593
  function ChatComposer(props) {
6362
6594
  return /* @__PURE__ */ jsxRuntime.jsx(PromptInputProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(ChatComposerInner, { ...props }) });
6363
6595
  }
@@ -6388,6 +6620,26 @@ function formatBytes2(n) {
6388
6620
  }
6389
6621
  return `${n} B`;
6390
6622
  }
6623
+ function ComposerMenuButton({
6624
+ icon: Icon,
6625
+ label,
6626
+ onClick
6627
+ }) {
6628
+ return /* @__PURE__ */ jsxRuntime.jsxs(
6629
+ Button,
6630
+ {
6631
+ type: "button",
6632
+ variant: "secondary",
6633
+ size: "sm",
6634
+ className: "rounded-sm -uppercase font-display bg-white dark:bg-accent border-2 border-accent cursor-pointer",
6635
+ onClick,
6636
+ children: [
6637
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, {}),
6638
+ label
6639
+ ]
6640
+ }
6641
+ );
6642
+ }
6391
6643
  function AttachmentPreviewStrip() {
6392
6644
  const attachments = useProviderAttachments();
6393
6645
  if (attachments.files.length === 0) return null;
@@ -6592,6 +6844,11 @@ function ChatComposerInner({
6592
6844
  textarea.setSelectionRange(end, end);
6593
6845
  });
6594
6846
  }, []);
6847
+ const openMenu = react.useCallback((mode) => {
6848
+ setMenuMode(mode);
6849
+ setMenuDismissed(false);
6850
+ setButtonMenuOpen(true);
6851
+ }, []);
6595
6852
  const handleCommandSelect = react.useCallback(
6596
6853
  (commandValue) => {
6597
6854
  textInput.setInput(`${commandValue} `);
@@ -6683,58 +6940,28 @@ function ChatComposerInner({
6683
6940
  children: [
6684
6941
  /* @__PURE__ */ jsxRuntime.jsx(AttachmentPreviewStrip, {}),
6685
6942
  viewMode === "expert" && !disabled && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-end gap-2 px-1 pb-2", children: [
6686
- /* @__PURE__ */ jsxRuntime.jsxs(
6687
- Button,
6943
+ /* @__PURE__ */ jsxRuntime.jsx(
6944
+ ComposerMenuButton,
6688
6945
  {
6689
- type: "button",
6690
- variant: "secondary",
6691
- size: "sm",
6692
- className: "rounded-sm -uppercase font-display bg-white dark:bg-accent border-2 border-accent cursor-pointer",
6693
- onClick: () => {
6694
- setMenuMode("commands");
6695
- setMenuDismissed(false);
6696
- setButtonMenuOpen(true);
6697
- },
6698
- children: [
6699
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.TerminalIcon, {}),
6700
- "Commands"
6701
- ]
6946
+ icon: lucideReact.TerminalIcon,
6947
+ label: "Commands",
6948
+ onClick: () => openMenu("commands")
6702
6949
  }
6703
6950
  ),
6704
- /* @__PURE__ */ jsxRuntime.jsxs(
6705
- Button,
6951
+ /* @__PURE__ */ jsxRuntime.jsx(
6952
+ ComposerMenuButton,
6706
6953
  {
6707
- type: "button",
6708
- variant: "secondary",
6709
- size: "sm",
6710
- className: "rounded-sm -uppercase font-display bg-white dark:bg-accent border-2 border-accent cursor-pointer",
6711
- onClick: () => {
6712
- setMenuMode("skills");
6713
- setMenuDismissed(false);
6714
- setButtonMenuOpen(true);
6715
- },
6716
- children: [
6717
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.SparklesIcon, {}),
6718
- "Skills"
6719
- ]
6954
+ icon: lucideReact.SparklesIcon,
6955
+ label: "Skills",
6956
+ onClick: () => openMenu("skills")
6720
6957
  }
6721
6958
  ),
6722
- loopsEnabled && /* @__PURE__ */ jsxRuntime.jsxs(
6723
- Button,
6959
+ loopsEnabled && /* @__PURE__ */ jsxRuntime.jsx(
6960
+ ComposerMenuButton,
6724
6961
  {
6725
- type: "button",
6726
- variant: "secondary",
6727
- size: "sm",
6728
- className: "rounded-sm -uppercase font-display bg-white dark:bg-accent border-2 border-accent cursor-pointer",
6729
- onClick: () => {
6730
- setMenuMode("scheduled");
6731
- setMenuDismissed(false);
6732
- setButtonMenuOpen(true);
6733
- },
6734
- children: [
6735
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ClockIcon, {}),
6736
- "Scheduled Tasks"
6737
- ]
6962
+ icon: lucideReact.ClockIcon,
6963
+ label: "Scheduled Tasks",
6964
+ onClick: () => openMenu("scheduled")
6738
6965
  }
6739
6966
  )
6740
6967
  ] }),
@@ -6888,29 +7115,32 @@ function ChatComposerInner({
6888
7115
  ] }),
6889
7116
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
6890
7117
  onViewModeChange && /* @__PURE__ */ jsxRuntime.jsxs(
6891
- "div",
7118
+ ButtonGroup,
6892
7119
  {
6893
- role: "group",
6894
7120
  "aria-label": "Chat view mode",
6895
- className: "inline-flex overflow-hidden rounded-md border border-border",
7121
+ className: "overflow-hidden rounded-md border border-border",
6896
7122
  children: [
6897
7123
  /* @__PURE__ */ jsxRuntime.jsx(
6898
- "button",
7124
+ Button,
6899
7125
  {
6900
7126
  type: "button",
7127
+ size: "xs",
7128
+ className: "capitalize font-normal",
7129
+ variant: viewMode === "simple" ? "secondary" : "ghost",
6901
7130
  "aria-pressed": viewMode === "simple",
6902
7131
  onClick: () => onViewModeChange("simple"),
6903
- className: viewMode === "simple" ? "bg-accent px-2 py-1 text-xs text-foreground" : "px-2 py-1 text-xs text-muted-foreground hover:text-foreground",
6904
7132
  children: "Simple"
6905
7133
  }
6906
7134
  ),
6907
7135
  /* @__PURE__ */ jsxRuntime.jsx(
6908
- "button",
7136
+ Button,
6909
7137
  {
6910
7138
  type: "button",
7139
+ size: "xs",
7140
+ className: "capitalize font-normal",
7141
+ variant: viewMode === "expert" ? "secondary" : "ghost",
6911
7142
  "aria-pressed": viewMode === "expert",
6912
7143
  onClick: () => onViewModeChange("expert"),
6913
- className: viewMode === "expert" ? "bg-accent px-2 py-1 text-xs text-foreground" : "px-2 py-1 text-xs text-muted-foreground hover:text-foreground",
6914
7144
  children: "Advanced"
6915
7145
  }
6916
7146
  )
@@ -6922,7 +7152,7 @@ function ChatComposerInner({
6922
7152
  {
6923
7153
  status,
6924
7154
  onStop,
6925
- className: "md:size-8"
7155
+ className: "size-10"
6926
7156
  }
6927
7157
  )
6928
7158
  ] })
@@ -6931,135 +7161,17 @@ function ChatComposerInner({
6931
7161
  }
6932
7162
  ) }),
6933
7163
  /* @__PURE__ */ jsxRuntime.jsx(
6934
- PopoverContent,
7164
+ ComposerCommandMenu,
6935
7165
  {
6936
- side: "top",
6937
- align: "start",
6938
- className: "overflow-hidden rounded-xl p-0",
6939
- style: { width: "var(--radix-popover-trigger-width)" },
6940
- onCloseAutoFocus: (e) => e.preventDefault(),
6941
- onEscapeKeyDown: () => {
6942
- focusTextareaAtEnd();
6943
- },
6944
- children: /* @__PURE__ */ jsxRuntime.jsxs(Command, { children: [
6945
- menuMode === "skills" ? /* @__PURE__ */ jsxRuntime.jsx(CommandInput, { placeholder: "Search skills..." }) : menuMode === "scheduled" ? /* @__PURE__ */ jsxRuntime.jsx(CommandInput, { placeholder: "Search scheduled tasks..." }) : /* @__PURE__ */ jsxRuntime.jsx(
6946
- CommandInput,
6947
- {
6948
- placeholder: "Type a command or search...",
6949
- value: searchQuery,
6950
- onValueChange: handleSearchChange
6951
- }
6952
- ),
6953
- /* @__PURE__ */ jsxRuntime.jsxs(CommandList, { children: [
6954
- !(skillsLoading && (menuMode === "skills" || menuMode === "all")) && /* @__PURE__ */ jsxRuntime.jsx(CommandEmpty, { children: menuMode === "skills" ? "No skills found." : menuMode === "scheduled" ? "No scheduled tasks found." : "No commands found." }),
6955
- (menuMode === "skills" || menuMode === "all") && skillsLoading && adapterCommands.length === 0 && [0, 1, 2].map((i) => /* @__PURE__ */ jsxRuntime.jsxs(
6956
- "div",
6957
- {
6958
- className: "grid grid-cols-[12rem_1fr] items-baseline gap-3 px-2 py-2",
6959
- "aria-hidden": "true",
6960
- children: [
6961
- /* @__PURE__ */ jsxRuntime.jsx(Skeleton, { className: "h-4 w-28" }),
6962
- /* @__PURE__ */ jsxRuntime.jsx(Skeleton, { className: "h-3 w-full max-w-[16rem]" })
6963
- ]
6964
- },
6965
- `skill-skeleton-${i}`
6966
- )),
6967
- menuMode === "scheduled" && scheduledExamples.map((cmd) => /* @__PURE__ */ jsxRuntime.jsxs(
6968
- CommandItem,
6969
- {
6970
- value: cmd.value,
6971
- keywords: [cmd.description],
6972
- onSelect: () => handleCommandSelect(cmd.value),
6973
- className: "grid grid-cols-[14rem_1fr] items-baseline gap-3 [&_svg]:hidden",
6974
- children: [
6975
- /* @__PURE__ */ jsxRuntime.jsx(
6976
- "span",
6977
- {
6978
- className: "font-mono font-semibold text-foreground truncate",
6979
- title: cmd.label,
6980
- children: cmd.label
6981
- }
6982
- ),
6983
- /* @__PURE__ */ jsxRuntime.jsx(
6984
- "span",
6985
- {
6986
- className: "min-w-0 truncate text-sm text-muted-foreground",
6987
- title: cmd.description,
6988
- children: cmd.description
6989
- }
6990
- )
6991
- ]
6992
- },
6993
- cmd.value
6994
- )),
6995
- (menuMode === "commands" || menuMode === "all") && builtinCommands.map((cmd) => /* @__PURE__ */ jsxRuntime.jsxs(
6996
- CommandItem,
6997
- {
6998
- value: cmd.value,
6999
- keywords: [cmd.description],
7000
- onSelect: () => handleCommandSelect(cmd.value),
7001
- className: "grid grid-cols-[12rem_1fr] items-baseline gap-3 [&_svg]:hidden",
7002
- children: [
7003
- /* @__PURE__ */ jsxRuntime.jsx(
7004
- "span",
7005
- {
7006
- className: "font-mono font-semibold text-foreground truncate",
7007
- title: cmd.label,
7008
- children: cmd.label
7009
- }
7010
- ),
7011
- /* @__PURE__ */ jsxRuntime.jsx(
7012
- "span",
7013
- {
7014
- className: "min-w-0 truncate text-sm text-muted-foreground",
7015
- title: cmd.description,
7016
- children: cmd.description
7017
- }
7018
- )
7019
- ]
7020
- },
7021
- cmd.value
7022
- )),
7023
- (menuMode === "skills" || menuMode === "all") && adapterCommands.map((cmd) => /* @__PURE__ */ jsxRuntime.jsxs(
7024
- CommandItem,
7025
- {
7026
- value: cmd.value,
7027
- keywords: cmd.isExpert ? [cmd.description, "expert"] : [cmd.description],
7028
- onSelect: () => handleCommandSelect(cmd.value),
7029
- className: "grid grid-cols-[12rem_1fr] items-baseline gap-3 [&_svg]:hidden",
7030
- children: [
7031
- /* @__PURE__ */ jsxRuntime.jsxs(
7032
- "span",
7033
- {
7034
- className: "inline-flex items-center gap-1.5 min-w-0 max-w-full",
7035
- title: cmd.label,
7036
- children: [
7037
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono font-semibold text-foreground truncate", children: cmd.label }),
7038
- cmd.isExpert && /* @__PURE__ */ jsxRuntime.jsx(
7039
- "span",
7040
- {
7041
- className: "shrink-0 rounded-sm bg-primary/10 px-1 text-[9px] font-semibold uppercase tracking-wider text-primary",
7042
- "aria-label": "Expert specialist",
7043
- children: "expert"
7044
- }
7045
- )
7046
- ]
7047
- }
7048
- ),
7049
- /* @__PURE__ */ jsxRuntime.jsx(
7050
- "span",
7051
- {
7052
- className: "min-w-0 truncate text-sm text-muted-foreground",
7053
- title: cmd.description,
7054
- children: cmd.description
7055
- }
7056
- )
7057
- ]
7058
- },
7059
- cmd.value
7060
- ))
7061
- ] })
7062
- ] })
7166
+ menuMode,
7167
+ searchQuery,
7168
+ onSearchChange: handleSearchChange,
7169
+ skillsLoading,
7170
+ builtinCommands,
7171
+ adapterCommands,
7172
+ scheduledExamples,
7173
+ onCommandSelect: handleCommandSelect,
7174
+ onEscapeDismiss: focusTextareaAtEnd
7063
7175
  }
7064
7176
  )
7065
7177
  ]