opencami 1.4.0 → 1.5.0

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.
Files changed (77) hide show
  1. package/README.md +60 -0
  2. package/dist/client/assets/CSPContext-EgWK8bIJ.js +1 -0
  3. package/dist/client/assets/DirectionContext-DXtY05YF.js +1 -0
  4. package/dist/client/assets/_sessionKey-B89e7G3y.js +100 -0
  5. package/dist/client/assets/agents-screen-1BiEZ9od.js +1 -0
  6. package/dist/client/assets/agents-x54ocA9z.js +2 -0
  7. package/dist/client/assets/bots-screen-BNQciUeJ.js +1 -0
  8. package/dist/client/assets/bots-x86ZHG4b.js +2 -0
  9. package/dist/client/assets/button-nDcsaNPl.js +1 -0
  10. package/dist/client/assets/{connect-D3baVDFL.js → connect-w4lLOqiJ.js} +1 -1
  11. package/dist/client/assets/file-explorer-screen-CAsjd3w8.js +1 -0
  12. package/dist/client/assets/files-Bype5Mnb.js +2 -0
  13. package/dist/client/assets/{index-ByIsZcHh.js → index-36G0WCxU.js} +1 -1
  14. package/dist/client/assets/{index-CVV4XiZo.js → index-BXkRE220.js} +2 -2
  15. package/dist/client/assets/keyboard-shortcuts-dialog-BdCeXRjD.js +1 -0
  16. package/dist/client/assets/{main-CkIF0soY.js → main-B3N0eQFg.js} +129 -17
  17. package/dist/client/assets/{opencami-logo-CIxSO1oo.js → opencami-logo-DD0DPFRQ.js} +1 -1
  18. package/dist/client/assets/{react-BhVdgA5r.js → react-B16OrBeM.js} +1 -1
  19. package/dist/client/assets/search-dialog-BjTPceEl.js +1 -0
  20. package/dist/client/assets/session-export-dialog-DtHKG2zW.js +1 -0
  21. package/dist/client/assets/settings-dialog-hiqdk_UD.js +1 -0
  22. package/dist/client/assets/skills-DhwyFq3y.js +2 -0
  23. package/dist/client/assets/skills-panel-BLUjzfjJ.js +5 -0
  24. package/dist/client/assets/styles-CHP4l6vZ.css +1 -0
  25. package/dist/client/assets/switch-J6wLIVu2.js +1 -0
  26. package/dist/client/assets/tabs-DvPgTz5I.js +1 -0
  27. package/dist/client/assets/tooltip-C14vdXHK.js +1 -0
  28. package/dist/client/assets/use-file-explorer-state-BnaJEqRP.js +12 -0
  29. package/dist/client/assets/useButton-Bnnac1eR.js +1 -0
  30. package/dist/client/assets/useCompositeItem-BgiEMKAt.js +1 -0
  31. package/dist/client/assets/{useControlled-Y306krcC.js → useControlled-BhUuiHAm.js} +1 -1
  32. package/dist/client/assets/{useMutation-0WgW4xQJ.js → useMutation-CFmVaBag.js} +1 -1
  33. package/dist/client/assets/visuallyHidden-DCCICp6T.js +9 -0
  34. package/dist/server/assets/{_sessionKey-BhFH4uWY.js → _sessionKey-tRze5NLR.js} +178 -46
  35. package/dist/server/assets/_tanstack-start-manifest_v-CyfoMvUa.js +4 -0
  36. package/dist/server/assets/{agents-Dz_i76VW.js → agents-CmQ4vvXm.js} +1 -1
  37. package/dist/server/assets/{agents-screen-CqQPJndp.js → agents-screen-bmrIyFbk.js} +43 -35
  38. package/dist/server/assets/{bots-6ryCIgKh.js → bots-Byt6jv0a.js} +1 -1
  39. package/dist/server/assets/bots-screen-C2TGFv42.js +474 -0
  40. package/dist/server/assets/{button-DtQ3rV1m.js → button-CwY2OHFj.js} +2 -2
  41. package/dist/server/assets/{connect-B8jpGQGK.js → connect-d3AqjAqe.js} +2 -2
  42. package/dist/server/assets/{file-explorer-screen-DCfS_Ajx.js → file-explorer-screen-CVlFiAFu.js} +36 -36
  43. package/dist/server/assets/{files-D2GIrPF4.js → files-BIEcSPGp.js} +1 -1
  44. package/dist/server/assets/{index-BNSsDaLb.js → index-CNIATlJ9.js} +22 -3
  45. package/dist/server/assets/{index-B2JHn34C.js → index-CRfLKh30.js} +2 -1
  46. package/dist/server/assets/{keyboard-shortcuts-dialog-CqIm8aYF.js → keyboard-shortcuts-dialog-CsNP85q8.js} +2 -2
  47. package/dist/server/assets/{router-Dme7USeO.js → router-rn7pJO_D.js} +356 -64
  48. package/dist/server/assets/{search-dialog-DG0D9KRN.js → search-dialog-Bz4Cu0KW.js} +23 -6
  49. package/dist/server/assets/{session-export-dialog-DLPZVlQV.js → session-export-dialog-CwclV0Aj.js} +2 -2
  50. package/dist/server/assets/{settings-dialog-BaGT4e5l.js → settings-dialog-BBM7jCjE.js} +386 -95
  51. package/dist/server/assets/skills-Cy8xclXY.js +11 -0
  52. package/dist/server/assets/skills-panel-BnRNb7u9.js +762 -0
  53. package/dist/server/assets/{switch-DnX0MjGS.js → switch-BbkUeVDV.js} +1 -1
  54. package/dist/server/assets/tabs-DDFZob0m.js +67 -0
  55. package/dist/server/assets/{tooltip-gbV6rEVv.js → tooltip-DgsSPocE.js} +1 -1
  56. package/dist/server/assets/{use-file-explorer-state-DfAKF2gZ.js → use-file-explorer-state-Il1LlBAe.js} +1 -1
  57. package/dist/server/server.js +2 -2
  58. package/package.json +6 -2
  59. package/dist/client/assets/_sessionKey-DB95zj1L.js +0 -97
  60. package/dist/client/assets/agents-LFrWe-HX.js +0 -2
  61. package/dist/client/assets/agents-screen-CEBBk1yO.js +0 -1
  62. package/dist/client/assets/bots-CxDwf_WK.js +0 -2
  63. package/dist/client/assets/bots-screen-D6qma1wK.js +0 -1
  64. package/dist/client/assets/button-Il3CHIzX.js +0 -1
  65. package/dist/client/assets/file-explorer-screen-rtV6n-5_.js +0 -1
  66. package/dist/client/assets/files-DMemuq9D.js +0 -2
  67. package/dist/client/assets/keyboard-shortcuts-dialog-DXC0YHoy.js +0 -1
  68. package/dist/client/assets/search-dialog-I1jJplIh.js +0 -1
  69. package/dist/client/assets/session-export-dialog-CH5unryw.js +0 -1
  70. package/dist/client/assets/settings-dialog-B8v-GVJ8.js +0 -1
  71. package/dist/client/assets/styles-BaTVzdPa.css +0 -1
  72. package/dist/client/assets/switch-sQnv1YsK.js +0 -1
  73. package/dist/client/assets/tooltip-j_viC_EE.js +0 -1
  74. package/dist/client/assets/use-file-explorer-state-BUH-u7Jv.js +0 -12
  75. package/dist/client/assets/useButton-9VAzplAB.js +0 -9
  76. package/dist/server/assets/_tanstack-start-manifest_v-BaIrL1VQ.js +0 -4
  77. package/dist/server/assets/bots-screen-DS_ZF9Ec.js +0 -417
@@ -3,12 +3,12 @@ import { Link, useNavigate } from "@tanstack/react-router";
3
3
  import * as React from "react";
4
4
  import React__default, { memo, useDeferredValue, useState, useMemo, useCallback, Suspense, lazy, useRef, useEffect, useId, useLayoutEffect, createContext, useContext } from "react";
5
5
  import { useQueryClient, useMutation, useQuery } from "@tanstack/react-query";
6
- import { T as TooltipProvider, a as TooltipRoot, b as TooltipTrigger, c as TooltipContent, s as setChatUiState, d as chatUiQueryKey, g as getChatUiState } from "./tooltip-gbV6rEVv.js";
6
+ import { T as TooltipProvider, a as TooltipRoot, b as TooltipTrigger, c as TooltipContent, s as setChatUiState, d as chatUiQueryKey, g as getChatUiState } from "./tooltip-DgsSPocE.js";
7
7
  import { HugeiconsIcon } from "@hugeicons/react";
8
- import { Tick01Icon, Cancel01Icon, MoreHorizontalIcon, Pen01Icon, Upload01Icon, Delete01Icon, BotIcon, Clock01Icon, Chat01Icon, ArrowRight01Icon, SidebarLeft01Icon, PencilEdit02Icon, Folder01Icon, AiBrain01Icon, SmartPhone01Icon, Search01Icon, Settings01Icon, Menu01Icon, Tick02Icon, Copy01Icon, Loading02Icon, StopIcon, VolumeHighIcon, ArrowDown01Icon, Loading03Icon, ArtificialIntelligence02Icon, CommandIcon, Attachment01Icon, File01Icon, Mic02Icon, ArrowUp02Icon } from "@hugeicons/core-free-icons";
8
+ import { Tick01Icon, Cancel01Icon, MoreHorizontalIcon, Pen01Icon, Upload01Icon, Delete01Icon, BotIcon, Clock01Icon, Chat01Icon, ArrowRight01Icon, SidebarLeft01Icon, PencilEdit02Icon, Folder01Icon, PackageOpenIcon, AiBrain01Icon, SmartPhone01Icon, Search01Icon, Settings01Icon, Menu01Icon, Tick02Icon, Copy01Icon, Loading02Icon, StopIcon, VolumeHighIcon, ArrowDown01Icon, Loading03Icon, ArtificialIntelligence02Icon, CommandIcon, Attachment01Icon, File01Icon, Mic02Icon, ArrowUp02Icon } from "@hugeicons/core-free-icons";
9
9
  import { motion, AnimatePresence } from "motion/react";
10
- import { D as DialogRoot, a as DialogContent, b as DialogTitle, c as DialogDescription, d as DialogClose, M as MenuRoot, e as MenuTrigger, f as MenuContent, g as MenuItem, u as useFileExplorerState } from "./use-file-explorer-state-DfAKF2gZ.js";
11
- import { B as Button, c as cn, b as buttonVariants } from "./button-DtQ3rV1m.js";
10
+ import { D as DialogRoot, a as DialogContent, b as DialogTitle, c as DialogDescription, d as DialogClose, M as MenuRoot, e as MenuTrigger, f as MenuContent, g as MenuItem, u as useFileExplorerState } from "./use-file-explorer-state-Il1LlBAe.js";
11
+ import { B as Button, c as cn, b as buttonVariants } from "./button-CwY2OHFj.js";
12
12
  import { AlertDialog } from "@base-ui/react/alert-dialog";
13
13
  import { Collapsible as Collapsible$1 } from "@base-ui/react/collapsible";
14
14
  import { ScrollArea } from "@base-ui/react/scroll-area";
@@ -17,11 +17,11 @@ import { marked } from "marked";
17
17
  import ReactMarkdown from "react-markdown";
18
18
  import remarkBreaks from "remark-breaks";
19
19
  import remarkGfm from "remark-gfm";
20
- import { r as resolveLanguage, C as CodeBlock, u as useChatSettings$1 } from "./index-BNSsDaLb.js";
20
+ import { r as resolveLanguage, C as CodeBlock, u as useChatSettings$1 } from "./index-CNIATlJ9.js";
21
21
  import { create } from "zustand";
22
22
  import { persist } from "zustand/middleware";
23
23
  import { createPortal } from "react-dom";
24
- import { a as Route } from "./router-Dme7USeO.js";
24
+ import { a as Route } from "./router-rn7pJO_D.js";
25
25
  function deriveFriendlyIdFromKey(key) {
26
26
  if (!key) return "main";
27
27
  const trimmed = key.trim();
@@ -758,7 +758,7 @@ function SessionItemComponent({
758
758
  "group inline-flex items-center justify-between",
759
759
  "w-full text-left pl-1.5 pr-0.5 min-h-8 py-1 rounded-lg transition-colors duration-0",
760
760
  "select-none",
761
- active ? "bg-primary-200 text-primary-950" : "bg-transparent text-primary-950 [&:hover:not(:has(button:hover))]:bg-primary-200"
761
+ active ? "bg-[var(--opencami-accent-light)] text-[var(--opencami-accent)]" : "bg-transparent text-primary-950 [&:hover:not(:has(button:hover))]:bg-primary-200"
762
762
  ),
763
763
  children: [
764
764
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 flex-1 min-w-0", children: [
@@ -1114,13 +1114,28 @@ const SidebarSessions = memo(function SidebarSessions2({
1114
1114
  `Delete ${selectedSessions.length} sessions? This will archive them.`
1115
1115
  );
1116
1116
  if (!confirmed) return;
1117
- const errors = await runWithConcurrency(
1118
- selectedSessions.map((session) => session.key),
1117
+ const failedSessionLabels = [];
1118
+ await runWithConcurrency(
1119
+ selectedSessions,
1119
1120
  10,
1120
- deleteSessionRequest
1121
+ async (session) => {
1122
+ try {
1123
+ await deleteSessionRequest(session.key);
1124
+ } catch (err) {
1125
+ failedSessionLabels.push(
1126
+ session.label || session.title || session.derivedTitle || session.friendlyId
1127
+ );
1128
+ throw err;
1129
+ }
1130
+ }
1121
1131
  );
1122
- if (errors.length > 0) {
1123
- console.error("[sidebar] Bulk delete failed for some sessions", errors);
1132
+ if (failedSessionLabels.length > 0) {
1133
+ console.error("[sidebar] Bulk delete failed for some sessions", failedSessionLabels);
1134
+ window.alert(
1135
+ `Could not delete ${failedSessionLabels.length} session${failedSessionLabels.length !== 1 ? "s" : ""}:
1136
+
1137
+ ${failedSessionLabels.join("\n")}`
1138
+ );
1124
1139
  }
1125
1140
  setSelectionMode(false);
1126
1141
  setSelectedSessionKeys(/* @__PURE__ */ new Set());
@@ -1649,10 +1664,10 @@ function useRenameSession() {
1649
1664
  return { renameSession, renaming, error };
1650
1665
  }
1651
1666
  const SettingsDialog = lazy(
1652
- () => import("./settings-dialog-BaGT4e5l.js").then((m) => ({ default: m.SettingsDialog }))
1667
+ () => import("./settings-dialog-BBM7jCjE.js").then((m) => ({ default: m.SettingsDialog }))
1653
1668
  );
1654
1669
  const SessionExportDialog = lazy(
1655
- () => import("./session-export-dialog-DLPZVlQV.js").then((m) => ({
1670
+ () => import("./session-export-dialog-CwclV0Aj.js").then((m) => ({
1656
1671
  default: m.SessionExportDialog
1657
1672
  }))
1658
1673
  );
@@ -1762,7 +1777,7 @@ function ChatSidebarComponent({
1762
1777
  motion.aside,
1763
1778
  {
1764
1779
  initial: false,
1765
- animate: { width: isCollapsed ? 48 : 300 },
1780
+ animate: { width: isCollapsed ? 48 : "var(--opencami-sidebar-width)" },
1766
1781
  transition,
1767
1782
  className: asideProps.className,
1768
1783
  children: [
@@ -1893,6 +1908,48 @@ function ChatSidebarComponent({
1893
1908
  ) }),
1894
1909
  isCollapsed && /* @__PURE__ */ jsx(TooltipContent, { side: "right", children: "Files" })
1895
1910
  ] }) }),
1911
+ (() => {
1912
+ try {
1913
+ return localStorage.getItem("opencami-skills-browser") === "true";
1914
+ } catch {
1915
+ return false;
1916
+ }
1917
+ })() && /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(TooltipRoot, { children: [
1918
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
1919
+ Link,
1920
+ {
1921
+ to: "/skills",
1922
+ className: cn(
1923
+ buttonVariants({ variant: "ghost", size: "sm" }),
1924
+ "w-full pl-1.5 justify-start"
1925
+ ),
1926
+ onClick: onSelectSession,
1927
+ children: [
1928
+ /* @__PURE__ */ jsx(
1929
+ HugeiconsIcon,
1930
+ {
1931
+ icon: PackageOpenIcon,
1932
+ size: 20,
1933
+ strokeWidth: 1.5,
1934
+ className: "min-w-5"
1935
+ }
1936
+ ),
1937
+ /* @__PURE__ */ jsx(AnimatePresence, { initial: false, mode: "wait", children: !isCollapsed && /* @__PURE__ */ jsx(
1938
+ motion.span,
1939
+ {
1940
+ initial: { opacity: 0 },
1941
+ animate: { opacity: 1 },
1942
+ exit: { opacity: 0 },
1943
+ transition,
1944
+ className: "overflow-hidden whitespace-nowrap",
1945
+ children: "Skills"
1946
+ }
1947
+ ) })
1948
+ ]
1949
+ }
1950
+ ) }),
1951
+ isCollapsed && /* @__PURE__ */ jsx(TooltipContent, { side: "right", children: "Skills" })
1952
+ ] }) }),
1896
1953
  (() => {
1897
1954
  try {
1898
1955
  return localStorage.getItem("opencami-agent-manager") === "true";
@@ -2323,7 +2380,8 @@ function ChatHeaderComponent({
2323
2380
  "div",
2324
2381
  {
2325
2382
  ref: wrapperRef,
2326
- className: "border-b border-primary-200 px-4 h-12 flex min-w-0 items-center overflow-x-hidden bg-surface",
2383
+ "data-tauri-drag-region": true,
2384
+ className: "border-b border-primary-200 px-4 h-12 flex min-w-0 items-center overflow-x-hidden bg-surface tauri-drag-header",
2327
2385
  children: [
2328
2386
  showSidebarButton ? /* @__PURE__ */ jsx(
2329
2387
  Button,
@@ -2332,6 +2390,7 @@ function ChatHeaderComponent({
2332
2390
  variant: "ghost",
2333
2391
  onClick: onOpenSidebar,
2334
2392
  className: "mr-2 text-primary-800 hover:bg-primary-100",
2393
+ style: { WebkitAppRegion: "no-drag" },
2335
2394
  "aria-label": "Open sidebar",
2336
2395
  children: /* @__PURE__ */ jsx(HugeiconsIcon, { icon: Menu01Icon, size: 18, strokeWidth: 1.6 })
2337
2396
  }
@@ -2882,7 +2941,7 @@ function createDefaultComponents(onOpenFilePreview) {
2882
2941
  {
2883
2942
  type: "button",
2884
2943
  onClick: () => onOpenFilePreview(filePath),
2885
- className: "font-mono text-primary-900 underline decoration-primary-300 underline-offset-4 hover:decoration-primary-600 cursor-pointer",
2944
+ className: "font-mono text-[var(--opencami-accent)] underline decoration-[var(--opencami-accent-light)] underline-offset-4 hover:opacity-90 cursor-pointer",
2886
2945
  children
2887
2946
  }
2888
2947
  );
@@ -2894,7 +2953,7 @@ function createDefaultComponents(onOpenFilePreview) {
2894
2953
  onClick: (event) => {
2895
2954
  if (href?.startsWith("openclaw-file://")) event.preventDefault();
2896
2955
  },
2897
- className: "text-primary-950 underline decoration-primary-300 underline-offset-4 transition-colors hover:text-primary-950 hover:decoration-primary-500",
2956
+ className: "text-[var(--opencami-accent)] underline decoration-[var(--opencami-accent-light)] underline-offset-4 transition-opacity hover:opacity-90",
2898
2957
  target: "_blank",
2899
2958
  rel: "noopener noreferrer",
2900
2959
  children
@@ -3452,7 +3511,9 @@ function MessageItemComponent({
3452
3511
  aggregatedSearchSources,
3453
3512
  wrapperRef,
3454
3513
  wrapperClassName,
3455
- wrapperScrollMarginTop
3514
+ wrapperScrollMarginTop,
3515
+ messageDomId,
3516
+ highlighted = false
3456
3517
  }) {
3457
3518
  const { settings } = useChatSettings$1();
3458
3519
  const role = message.role || "assistant";
@@ -3468,18 +3529,21 @@ function MessageItemComponent({
3468
3529
  "div",
3469
3530
  {
3470
3531
  ref: wrapperRef,
3532
+ id: messageDomId,
3533
+ "data-message-id": messageDomId,
3471
3534
  style: {
3472
3535
  contentVisibility: "auto",
3473
3536
  containIntrinsicSize: "auto 120px",
3474
3537
  ...typeof wrapperScrollMarginTop === "number" ? { scrollMarginTop: `${wrapperScrollMarginTop}px` } : void 0
3475
3538
  },
3476
3539
  className: cn(
3477
- "group flex flex-col gap-1",
3540
+ "opencami-message-item group mx-auto flex w-full max-w-[var(--opencami-chat-width)] flex-col gap-1 py-[var(--opencami-msg-padding-y)]",
3478
3541
  wrapperClassName,
3542
+ highlighted && "opencami-message-highlight",
3479
3543
  isUser ? "items-end" : "items-start"
3480
3544
  ),
3481
3545
  children: [
3482
- thinking && settings.showReasoningBlocks && /* @__PURE__ */ jsx("div", { className: "w-full max-w-[900px]", children: /* @__PURE__ */ jsx(Thinking, { content: thinking }) }),
3546
+ thinking && settings.showReasoningBlocks && /* @__PURE__ */ jsx("div", { className: "w-full max-w-[var(--opencami-chat-width)]", children: /* @__PURE__ */ jsx(Thinking, { content: thinking }) }),
3483
3547
  images.length > 0 && /* @__PURE__ */ jsx("div", { className: cn(
3484
3548
  "flex flex-wrap gap-2 mb-2",
3485
3549
  isUser ? "justify-end" : "justify-start"
@@ -3500,13 +3564,13 @@ function MessageItemComponent({
3500
3564
  markdown: !isUser,
3501
3565
  className: cn(
3502
3566
  "text-primary-900 opencami-text-size min-w-0 max-w-full",
3503
- !isUser ? "bg-transparent w-full" : "bg-primary-100 px-4 py-2.5 max-w-[85%]",
3567
+ isUser ? "opencami-message-user bg-primary-100 px-4 py-[var(--opencami-user-bubble-py)] max-w-[85%]" : "opencami-message-assistant bg-transparent w-full",
3504
3568
  !isUser && isStreaming && "stream-fade-in"
3505
3569
  ),
3506
3570
  children: text
3507
3571
  }
3508
3572
  ) }),
3509
- hasToolCalls && settings.showToolMessages && /* @__PURE__ */ jsx("div", { className: "mt-2 flex w-full min-w-0 max-w-[900px] flex-col gap-3 overflow-x-hidden", children: toolCalls.map((toolCall) => {
3573
+ hasToolCalls && settings.showToolMessages && /* @__PURE__ */ jsx("div", { className: "mt-2 flex w-full min-w-0 max-w-[var(--opencami-chat-width)] flex-col gap-3 overflow-x-hidden", children: toolCalls.map((toolCall) => {
3510
3574
  const resultMessage = toolCall.id ? toolResultsByCallId?.get(toolCall.id) : void 0;
3511
3575
  const toolPart = mapToolCallToToolPart(toolCall, resultMessage);
3512
3576
  return /* @__PURE__ */ jsx(
@@ -3518,7 +3582,7 @@ function MessageItemComponent({
3518
3582
  toolCall.id || toolCall.name
3519
3583
  );
3520
3584
  }) }),
3521
- searchSources.length > 0 && /* @__PURE__ */ jsx("div", { className: "w-full max-w-[900px]", children: /* @__PURE__ */ jsx(SearchSourcesBadge, { sources: searchSources }) }),
3585
+ searchSources.length > 0 && /* @__PURE__ */ jsx("div", { className: "w-full max-w-[var(--opencami-chat-width)]", children: /* @__PURE__ */ jsx(SearchSourcesBadge, { sources: searchSources }) }),
3522
3586
  !hasToolCalls && /* @__PURE__ */ jsx(
3523
3587
  MessageActionsBar,
3524
3588
  {
@@ -3544,6 +3608,8 @@ function areMessagesEqual(prevProps, nextProps) {
3544
3608
  if (prevProps.wrapperScrollMarginTop !== nextProps.wrapperScrollMarginTop) {
3545
3609
  return false;
3546
3610
  }
3611
+ if (prevProps.messageDomId !== nextProps.messageDomId) return false;
3612
+ if (prevProps.highlighted !== nextProps.highlighted) return false;
3547
3613
  if ((prevProps.message.role || "assistant") !== (nextProps.message.role || "assistant")) {
3548
3614
  return false;
3549
3615
  }
@@ -4291,13 +4357,15 @@ function ChatMessageListComponent({
4291
4357
  pinGroupMinHeight,
4292
4358
  headerHeight,
4293
4359
  contentStyle,
4294
- onFollowUpClick
4360
+ onFollowUpClick,
4361
+ jumpToMessageId
4295
4362
  }) {
4296
4363
  const anchorRef = useRef(null);
4297
4364
  const lastUserRef = useRef(null);
4298
4365
  const programmaticScroll = useRef(false);
4299
4366
  const prevPinRef = useRef(pinToTop);
4300
4367
  const prevUserIndexRef = useRef(void 0);
4368
+ const [highlightedMessageId, setHighlightedMessageId] = useState(null);
4301
4369
  const displayMessages = useMemo(() => {
4302
4370
  return messages.filter((msg) => msg.role !== "toolResult");
4303
4371
  }, [messages]);
@@ -4414,13 +4482,25 @@ function ChatMessageListComponent({
4414
4482
  }, 0);
4415
4483
  }
4416
4484
  }, [loading, displayMessages.length, sessionKey, pinToTop, lastUserIndex]);
4485
+ useEffect(() => {
4486
+ if (!jumpToMessageId || loading) return;
4487
+ const target = document.getElementById(`message-${jumpToMessageId}`);
4488
+ if (!target) return;
4489
+ target.scrollIntoView({ behavior: "smooth", block: "center" });
4490
+ setHighlightedMessageId(jumpToMessageId);
4491
+ const timer = window.setTimeout(() => {
4492
+ setHighlightedMessageId((prev) => prev === jumpToMessageId ? null : prev);
4493
+ }, 1800);
4494
+ return () => window.clearTimeout(timer);
4495
+ }, [jumpToMessageId, loading, displayMessages]);
4417
4496
  return (
4418
4497
  // mt-2 is to fix the prompt-input cut off
4419
4498
  /* @__PURE__ */ jsx(MemoizedChatContainerRoot, { className: "flex-1 min-h-0 -mb-4", children: /* @__PURE__ */ jsxs(ChatContainerContent, { className: "pt-6", style: contentStyle, children: [
4420
4499
  notice && noticePosition === "start" ? notice : null,
4421
4500
  empty && !notice ? emptyState ?? /* @__PURE__ */ jsx("div", { "aria-hidden": true }) : hasGroup ? /* @__PURE__ */ jsxs(Fragment, { children: [
4422
4501
  displayMessages.slice(0, groupStartIndex).map((chatMessage, index) => {
4423
- const messageKey = index;
4502
+ const messageId = typeof chatMessage.id === "string" ? chatMessage.id : void 0;
4503
+ const messageKey = messageId || index;
4424
4504
  const forceActionsVisible = typeof lastAssistantIndex === "number" && index === lastAssistantIndex;
4425
4505
  const isLastAssistant = typeof lastAssistantIndex === "number" && index === lastAssistantIndex;
4426
4506
  const hasToolCalls = chatMessage.role === "assistant" && getToolCallsFromMessage(chatMessage).length > 0;
@@ -4432,7 +4512,9 @@ function ChatMessageListComponent({
4432
4512
  forceActionsVisible,
4433
4513
  isStreaming: isLastAssistant && isStreaming,
4434
4514
  isLastAssistant,
4435
- aggregatedSearchSources: isLastAssistant ? aggregatedSearchSources : void 0
4515
+ aggregatedSearchSources: isLastAssistant ? aggregatedSearchSources : void 0,
4516
+ messageDomId: messageId ? `message-${messageId}` : void 0,
4517
+ highlighted: highlightedMessageId === messageId
4436
4518
  },
4437
4519
  messageKey
4438
4520
  );
@@ -4440,12 +4522,13 @@ function ChatMessageListComponent({
4440
4522
  /* @__PURE__ */ jsxs(
4441
4523
  "div",
4442
4524
  {
4443
- className: "flex flex-col space-y-3 md:space-y-6",
4444
- style: { minHeight: `${Math.max(0, pinGroupMinHeight - 24)}px` },
4525
+ className: "flex flex-col gap-[var(--opencami-msg-gap)]",
4526
+ style: { minHeight: `${Math.max(0, pinGroupMinHeight)}px` },
4445
4527
  children: [
4446
4528
  displayMessages.slice(groupStartIndex).map((chatMessage, index) => {
4447
4529
  const realIndex = groupStartIndex + index;
4448
- const messageKey = realIndex;
4530
+ const messageId = typeof chatMessage.id === "string" ? chatMessage.id : void 0;
4531
+ const messageKey = messageId || realIndex;
4449
4532
  const forceActionsVisible = typeof lastAssistantIndex === "number" && realIndex === lastAssistantIndex;
4450
4533
  const isLastAssistant = typeof lastAssistantIndex === "number" && realIndex === lastAssistantIndex;
4451
4534
  const wrapperRef = realIndex === lastUserIndex ? lastUserRef : void 0;
@@ -4463,7 +4546,9 @@ function ChatMessageListComponent({
4463
4546
  aggregatedSearchSources: isLastAssistant ? aggregatedSearchSources : void 0,
4464
4547
  wrapperRef,
4465
4548
  wrapperClassName,
4466
- wrapperScrollMarginTop
4549
+ wrapperScrollMarginTop,
4550
+ messageDomId: messageId ? `message-${messageId}` : void 0,
4551
+ highlighted: highlightedMessageId === messageId
4467
4552
  },
4468
4553
  messageKey
4469
4554
  );
@@ -4482,7 +4567,8 @@ function ChatMessageListComponent({
4482
4567
  )
4483
4568
  ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
4484
4569
  displayMessages.map((chatMessage, index) => {
4485
- const messageKey = index;
4570
+ const messageId = typeof chatMessage.id === "string" ? chatMessage.id : void 0;
4571
+ const messageKey = messageId || index;
4486
4572
  const forceActionsVisible = typeof lastAssistantIndex === "number" && index === lastAssistantIndex;
4487
4573
  const isLastAssistant = typeof lastAssistantIndex === "number" && index === lastAssistantIndex;
4488
4574
  const hasToolCalls = chatMessage.role === "assistant" && getToolCallsFromMessage(chatMessage).length > 0;
@@ -4494,7 +4580,9 @@ function ChatMessageListComponent({
4494
4580
  forceActionsVisible,
4495
4581
  isStreaming: isLastAssistant && isStreaming,
4496
4582
  isLastAssistant,
4497
- aggregatedSearchSources: isLastAssistant ? aggregatedSearchSources : void 0
4583
+ aggregatedSearchSources: isLastAssistant ? aggregatedSearchSources : void 0,
4584
+ messageDomId: messageId ? `message-${messageId}` : void 0,
4585
+ highlighted: highlightedMessageId === messageId
4498
4586
  },
4499
4587
  messageKey
4500
4588
  );
@@ -4519,7 +4607,7 @@ function ChatMessageListComponent({
4519
4607
  );
4520
4608
  }
4521
4609
  function areChatMessageListEqual(prev, next) {
4522
- return prev.messages === next.messages && prev.loading === next.loading && prev.empty === next.empty && prev.emptyState === next.emptyState && prev.notice === next.notice && prev.noticePosition === next.noticePosition && prev.waitingForResponse === next.waitingForResponse && prev.isStreaming === next.isStreaming && prev.sessionKey === next.sessionKey && prev.pinToTop === next.pinToTop && prev.pinGroupMinHeight === next.pinGroupMinHeight && prev.headerHeight === next.headerHeight && prev.contentStyle === next.contentStyle && prev.onFollowUpClick === next.onFollowUpClick;
4610
+ return prev.messages === next.messages && prev.loading === next.loading && prev.empty === next.empty && prev.emptyState === next.emptyState && prev.notice === next.notice && prev.noticePosition === next.noticePosition && prev.waitingForResponse === next.waitingForResponse && prev.isStreaming === next.isStreaming && prev.sessionKey === next.sessionKey && prev.pinToTop === next.pinToTop && prev.pinGroupMinHeight === next.pinGroupMinHeight && prev.headerHeight === next.headerHeight && prev.contentStyle === next.contentStyle && prev.onFollowUpClick === next.onFollowUpClick && prev.jumpToMessageId === next.jumpToMessageId;
4523
4611
  }
4524
4612
  const MemoizedChatMessageList = memo(
4525
4613
  ChatMessageListComponent,
@@ -5853,7 +5941,7 @@ function ChatComposerComponent({
5853
5941
  return /* @__PURE__ */ jsxs(
5854
5942
  "div",
5855
5943
  {
5856
- className: "mx-auto w-full max-w-full px-2 md:px-5 sm:max-w-[768px] sm:min-w-[400px] relative pb-1 md:pb-3",
5944
+ className: "mx-auto w-full max-w-[var(--opencami-chat-width)] px-2 md:px-5 relative pb-1 md:pb-3",
5857
5945
  ref: wrapperRef,
5858
5946
  onDragOver: handleDragOver,
5859
5947
  onDragLeave: handleDragLeave,
@@ -5956,7 +6044,7 @@ function MessageStatus({
5956
6044
  onAction,
5957
6045
  className
5958
6046
  }) {
5959
- return /* @__PURE__ */ jsx("div", { className: cn("w-full max-w-[900px]", className), children: /* @__PURE__ */ jsx(Message, { children: /* @__PURE__ */ jsxs("div", { className: "w-full rounded-xl border border-primary-200 bg-primary-50 p-4 text-primary-900", children: [
6047
+ return /* @__PURE__ */ jsx("div", { className: cn("w-full max-w-[var(--opencami-chat-width)]", className), children: /* @__PURE__ */ jsx(Message, { children: /* @__PURE__ */ jsxs("div", { className: "w-full rounded-xl border border-primary-200 bg-primary-50 p-4 text-primary-900", children: [
5960
6048
  /* @__PURE__ */ jsx("div", { className: "text-balance font-medium", children: title }),
5961
6049
  /* @__PURE__ */ jsx("div", { className: "mt-2 text-pretty text-primary-700", children: description }),
5962
6050
  detail ? /* @__PURE__ */ jsx("div", { className: "mt-2 text-xs text-primary-600", children: detail }) : null,
@@ -6534,15 +6622,16 @@ function useSwipeGesture(options) {
6534
6622
  };
6535
6623
  }
6536
6624
  const KeyboardShortcutsDialog = lazy(
6537
- () => import("./keyboard-shortcuts-dialog-CqIm8aYF.js").then((m) => ({
6625
+ () => import("./keyboard-shortcuts-dialog-CsNP85q8.js").then((m) => ({
6538
6626
  default: m.KeyboardShortcutsDialog
6539
6627
  }))
6540
6628
  );
6541
6629
  const SearchDialog = lazy(
6542
- () => import("./search-dialog-DG0D9KRN.js").then((m) => ({
6630
+ () => import("./search-dialog-Bz4Cu0KW.js").then((m) => ({
6543
6631
  default: m.SearchDialog
6544
6632
  }))
6545
6633
  );
6634
+ const SEARCH_JUMP_TARGET_KEY = "opencami-search-jump-target";
6546
6635
  function ChatScreen({
6547
6636
  activeFriendlyId,
6548
6637
  isNewChat = false,
@@ -6565,6 +6654,7 @@ function ChatScreen({
6565
6654
  const [showShortcutsHelp, setShowShortcutsHelp] = useState(false);
6566
6655
  const [showSearchDialog, setShowSearchDialog] = useState(false);
6567
6656
  const [searchMode, setSearchMode] = useState("global");
6657
+ const [searchJumpMessageId, setSearchJumpMessageId] = useState(null);
6568
6658
  const [isStreaming, setIsStreaming] = useState(false);
6569
6659
  const thinkingLevel = useThinkingLevelStore((state) => state.level);
6570
6660
  const inputRef = useRef(null);
@@ -6877,6 +6967,10 @@ function ChatScreen({
6877
6967
  useEffect(() => {
6878
6968
  const resetKey = isNewChat ? "new" : activeFriendlyId;
6879
6969
  if (!resetKey) return;
6970
+ streamStop();
6971
+ stopStream();
6972
+ lastAssistantSignature.current = "";
6973
+ setIsStreaming(false);
6880
6974
  if (pendingStartRef.current) {
6881
6975
  pendingStartRef.current = false;
6882
6976
  return;
@@ -6886,13 +6980,34 @@ function ChatScreen({
6886
6980
  setPinToTop(true);
6887
6981
  return;
6888
6982
  }
6889
- streamStop();
6890
- stopStream();
6891
- lastAssistantSignature.current = "";
6892
6983
  setWaitingForResponse(false);
6893
6984
  setPinToTop(false);
6894
- setIsStreaming(false);
6895
6985
  }, [activeFriendlyId, isNewChat, streamStop, stopStream]);
6986
+ useEffect(() => {
6987
+ if (typeof window === "undefined") return;
6988
+ try {
6989
+ const raw = sessionStorage.getItem(SEARCH_JUMP_TARGET_KEY);
6990
+ if (!raw) {
6991
+ setSearchJumpMessageId(null);
6992
+ return;
6993
+ }
6994
+ const parsed = JSON.parse(raw);
6995
+ const isStale = typeof parsed.at === "number" && Date.now() - parsed.at > 5 * 60 * 1e3;
6996
+ if (isStale) {
6997
+ sessionStorage.removeItem(SEARCH_JUMP_TARGET_KEY);
6998
+ setSearchJumpMessageId(null);
6999
+ return;
7000
+ }
7001
+ if (parsed.friendlyId === activeFriendlyId && typeof parsed.messageId === "string" && parsed.messageId.length > 0) {
7002
+ setSearchJumpMessageId(parsed.messageId);
7003
+ sessionStorage.removeItem(SEARCH_JUMP_TARGET_KEY);
7004
+ return;
7005
+ }
7006
+ setSearchJumpMessageId(null);
7007
+ } catch {
7008
+ setSearchJumpMessageId(null);
7009
+ }
7010
+ }, [activeFriendlyId]);
6896
7011
  useLayoutEffect(() => {
6897
7012
  if (isNewChat) return;
6898
7013
  const pending = consumePendingSend(
@@ -7233,7 +7348,7 @@ function ChatScreen({
7233
7348
  "div",
7234
7349
  {
7235
7350
  className: cn(
7236
- "fixed inset-y-0 left-0 z-50 w-[300px] transition-transform duration-150 safe-area-top",
7351
+ "fixed inset-y-0 left-0 z-50 w-[var(--opencami-sidebar-width)] transition-transform duration-150 safe-area-top",
7237
7352
  isSidebarCollapsed ? "-translate-x-full" : "translate-x-0"
7238
7353
  ),
7239
7354
  ...sidebarCloseSwipeHandlers,
@@ -7275,7 +7390,8 @@ function ChatScreen({
7275
7390
  pinGroupMinHeight,
7276
7391
  headerHeight,
7277
7392
  contentStyle: stableContentStyle,
7278
- onFollowUpClick: handleFollowUpClick
7393
+ onFollowUpClick: handleFollowUpClick,
7394
+ jumpToMessageId: searchJumpMessageId
7279
7395
  }
7280
7396
  ),
7281
7397
  /* @__PURE__ */ jsx(
@@ -7313,9 +7429,25 @@ function ChatScreen({
7313
7429
  mode: searchMode,
7314
7430
  onJumpToMessage: (result) => {
7315
7431
  setShowSearchDialog(false);
7316
- if (result.friendlyId) {
7317
- navigate({ to: "/chat/$sessionKey", params: { sessionKey: result.friendlyId } });
7432
+ if (!result.friendlyId) return;
7433
+ if (result.messageId && result.friendlyId === activeFriendlyId) {
7434
+ setSearchJumpMessageId(result.messageId);
7435
+ return;
7436
+ }
7437
+ if (result.messageId && typeof window !== "undefined") {
7438
+ try {
7439
+ sessionStorage.setItem(
7440
+ SEARCH_JUMP_TARGET_KEY,
7441
+ JSON.stringify({
7442
+ friendlyId: result.friendlyId,
7443
+ messageId: result.messageId,
7444
+ at: Date.now()
7445
+ })
7446
+ );
7447
+ } catch {
7448
+ }
7318
7449
  }
7450
+ navigate({ to: "/chat/$sessionKey", params: { sessionKey: result.friendlyId } });
7319
7451
  }
7320
7452
  }
7321
7453
  ) })
@@ -0,0 +1,4 @@
1
+ const tsrStartManifest = () => ({ "routes": { "__root__": { "filePath": "/home/runner/work/opencami/opencami/src/routes/__root.tsx", "children": ["/", "/agents", "/bots", "/connect", "/files", "/new", "/skills", "/api/agents", "/api/cron", "/api/follow-ups", "/api/history", "/api/llm-features", "/api/models", "/api/paths", "/api/personas", "/api/ping", "/api/send", "/api/sessions", "/api/skills", "/api/stream", "/api/stt", "/api/tts", "/chat/$sessionKey", "/api/files/delete", "/api/files/download", "/api/files/info", "/api/files/list", "/api/files/mkdir", "/api/files/read", "/api/files/rename", "/api/files/save", "/api/files/upload"], "preloads": ["/assets/main-B3N0eQFg.js"], "assets": [] }, "/": { "filePath": "/home/runner/work/opencami/opencami/src/routes/index.tsx", "assets": [], "preloads": ["/assets/index-36G0WCxU.js"] }, "/agents": { "filePath": "/home/runner/work/opencami/opencami/src/routes/agents.tsx", "assets": [], "preloads": ["/assets/agents-x54ocA9z.js"] }, "/bots": { "filePath": "/home/runner/work/opencami/opencami/src/routes/bots.tsx", "assets": [], "preloads": ["/assets/bots-x86ZHG4b.js"] }, "/connect": { "filePath": "/home/runner/work/opencami/opencami/src/routes/connect.tsx", "assets": [], "preloads": ["/assets/connect-w4lLOqiJ.js", "/assets/index-BXkRE220.js", "/assets/button-nDcsaNPl.js", "/assets/react-B16OrBeM.js"] }, "/files": { "filePath": "/home/runner/work/opencami/opencami/src/routes/files.tsx", "assets": [], "preloads": ["/assets/files-Bype5Mnb.js"] }, "/new": { "filePath": "/home/runner/work/opencami/opencami/src/routes/new.tsx", "assets": [], "preloads": ["/assets/new-DLlBm66g.js"] }, "/skills": { "filePath": "/home/runner/work/opencami/opencami/src/routes/skills.tsx", "assets": [], "preloads": ["/assets/skills-DhwyFq3y.js"] }, "/api/agents": { "filePath": "/home/runner/work/opencami/opencami/src/routes/api/agents.ts" }, "/api/cron": { "filePath": "/home/runner/work/opencami/opencami/src/routes/api/cron.ts" }, "/api/follow-ups": { "filePath": "/home/runner/work/opencami/opencami/src/routes/api/follow-ups.ts" }, "/api/history": { "filePath": "/home/runner/work/opencami/opencami/src/routes/api/history.ts" }, "/api/llm-features": { "filePath": "/home/runner/work/opencami/opencami/src/routes/api/llm-features.ts" }, "/api/models": { "filePath": "/home/runner/work/opencami/opencami/src/routes/api/models.ts" }, "/api/paths": { "filePath": "/home/runner/work/opencami/opencami/src/routes/api/paths.ts" }, "/api/personas": { "filePath": "/home/runner/work/opencami/opencami/src/routes/api/personas.ts" }, "/api/ping": { "filePath": "/home/runner/work/opencami/opencami/src/routes/api/ping.ts" }, "/api/send": { "filePath": "/home/runner/work/opencami/opencami/src/routes/api/send.ts" }, "/api/sessions": { "filePath": "/home/runner/work/opencami/opencami/src/routes/api/sessions.ts" }, "/api/skills": { "filePath": "/home/runner/work/opencami/opencami/src/routes/api/skills.ts" }, "/api/stream": { "filePath": "/home/runner/work/opencami/opencami/src/routes/api/stream.ts" }, "/api/stt": { "filePath": "/home/runner/work/opencami/opencami/src/routes/api/stt.ts" }, "/api/tts": { "filePath": "/home/runner/work/opencami/opencami/src/routes/api/tts.ts" }, "/chat/$sessionKey": { "filePath": "/home/runner/work/opencami/opencami/src/routes/chat/$sessionKey.tsx", "assets": [], "preloads": ["/assets/_sessionKey-B89e7G3y.js", "/assets/visuallyHidden-DCCICp6T.js", "/assets/tooltip-C14vdXHK.js", "/assets/button-nDcsaNPl.js", "/assets/use-file-explorer-state-BnaJEqRP.js", "/assets/useButton-Bnnac1eR.js", "/assets/useControlled-BhUuiHAm.js", "/assets/DirectionContext-DXtY05YF.js", "/assets/CSPContext-EgWK8bIJ.js", "/assets/useMutation-CFmVaBag.js", "/assets/opencami-logo-DD0DPFRQ.js", "/assets/index-BXkRE220.js", "/assets/react-B16OrBeM.js"] }, "/api/files/delete": { "filePath": "/home/runner/work/opencami/opencami/src/routes/api/files/delete.ts" }, "/api/files/download": { "filePath": "/home/runner/work/opencami/opencami/src/routes/api/files/download.ts" }, "/api/files/info": { "filePath": "/home/runner/work/opencami/opencami/src/routes/api/files/info.ts" }, "/api/files/list": { "filePath": "/home/runner/work/opencami/opencami/src/routes/api/files/list.ts" }, "/api/files/mkdir": { "filePath": "/home/runner/work/opencami/opencami/src/routes/api/files/mkdir.ts" }, "/api/files/read": { "filePath": "/home/runner/work/opencami/opencami/src/routes/api/files/read.ts" }, "/api/files/rename": { "filePath": "/home/runner/work/opencami/opencami/src/routes/api/files/rename.ts" }, "/api/files/save": { "filePath": "/home/runner/work/opencami/opencami/src/routes/api/files/save.ts" }, "/api/files/upload": { "filePath": "/home/runner/work/opencami/opencami/src/routes/api/files/upload.ts" } }, "clientEntry": "/assets/main-B3N0eQFg.js" });
2
+ export {
3
+ tsrStartManifest
4
+ };
@@ -1,6 +1,6 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import { Suspense, lazy } from "react";
3
- const AgentsScreen = lazy(() => import("./agents-screen-CqQPJndp.js").then((m) => ({
3
+ const AgentsScreen = lazy(() => import("./agents-screen-bmrIyFbk.js").then((m) => ({
4
4
  default: m.AgentsScreen
5
5
  })));
6
6
  function AgentsRoute() {