opencami 1.3.2 → 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 (75) 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-CtDn993i.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-N9-2R5hZ.js → index-36G0WCxU.js} +1 -1
  14. package/dist/client/assets/index-BXkRE220.js +153 -0
  15. package/dist/client/assets/keyboard-shortcuts-dialog-BdCeXRjD.js +1 -0
  16. package/dist/client/assets/{main-DrVY5UJU.js → main-B3N0eQFg.js} +129 -17
  17. package/dist/client/assets/opencami-logo-DD0DPFRQ.js +1 -0
  18. package/dist/client/assets/{react-CzqI3gbN.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-BhUuiHAm.js +1 -0
  32. package/dist/client/assets/useMutation-CFmVaBag.js +1 -0
  33. package/dist/client/assets/visuallyHidden-DCCICp6T.js +9 -0
  34. package/dist/server/assets/{_sessionKey-DVNrEYFh.js → _sessionKey-tRze5NLR.js} +493 -92
  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-Byt6jv0a.js +11 -0
  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-CIDOw12K.js → connect-d3AqjAqe.js} +2 -2
  42. package/dist/server/assets/{file-explorer-screen-Cx0jiLRU.js → file-explorer-screen-CVlFiAFu.js} +39 -40
  43. package/dist/server/assets/{files-Trs1M5ba.js → files-BIEcSPGp.js} +1 -1
  44. package/dist/server/assets/{index-CmbNTqa2.js → index-CNIATlJ9.js} +93 -38
  45. package/dist/server/assets/{index-Dv2RXDa2.js → index-CRfLKh30.js} +2 -1
  46. package/dist/server/assets/{keyboard-shortcuts-dialog-7OEtXUlW.js → keyboard-shortcuts-dialog-CsNP85q8.js} +2 -2
  47. package/dist/server/assets/{router-OoQe2c20.js → router-rn7pJO_D.js} +515 -78
  48. package/dist/server/assets/{search-dialog-Bq0Pnxdb.js → search-dialog-Bz4Cu0KW.js} +23 -6
  49. package/dist/server/assets/{session-export-dialog-DRVbC8Q-.js → session-export-dialog-CwclV0Aj.js} +2 -2
  50. package/dist/server/assets/{settings-dialog-BsJsnMiu.js → settings-dialog-BBM7jCjE.js} +409 -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-DMHdtb7D.js → use-file-explorer-state-Il1LlBAe.js} +13 -1
  57. package/dist/server/server.js +2 -2
  58. package/package.json +6 -2
  59. package/dist/client/assets/_sessionKey-Z6Wcnj0N.js +0 -97
  60. package/dist/client/assets/agents-D8ZHVQ1Z.js +0 -2
  61. package/dist/client/assets/agents-screen-BVK0QTRH.js +0 -1
  62. package/dist/client/assets/button-CuH8u1uR.js +0 -1
  63. package/dist/client/assets/file-explorer-screen-DMUuR1uG.js +0 -1
  64. package/dist/client/assets/files-CDMLoJ0u.js +0 -2
  65. package/dist/client/assets/index-IGP-Igwt.js +0 -153
  66. package/dist/client/assets/keyboard-shortcuts-dialog-DW--4YLF.js +0 -1
  67. package/dist/client/assets/opencami-logo-ChD2XR2j.js +0 -1
  68. package/dist/client/assets/search-dialog-DYyFYJmw.js +0 -1
  69. package/dist/client/assets/session-export-dialog-BFizBmGb.js +0 -1
  70. package/dist/client/assets/settings-dialog-UvgVi2It.js +0 -1
  71. package/dist/client/assets/styles-BGTCU8mq.css +0 -1
  72. package/dist/client/assets/switch-DxW2OWPG.js +0 -1
  73. package/dist/client/assets/use-file-explorer-state-Cii59H70.js +0 -12
  74. package/dist/client/assets/useButton-hZdvKtl_.js +0 -9
  75. package/dist/server/assets/_tanstack-start-manifest_v-PwRq_yJS.js +0 -4
@@ -4,10 +4,10 @@ import { useQuery, useQueryClient, useMutation } from "@tanstack/react-query";
4
4
  import { HugeiconsIcon } from "@hugeicons/react";
5
5
  import { Home09Icon, SidebarLeft01Icon, ArrowLeft01Icon, Settings01Icon, ArrowRight01Icon, Home02Icon, Menu01Icon, GridViewIcon, ArrowUp01Icon, ArrowDown01Icon, Upload04Icon, FolderAddIcon, FolderOpenIcon, Download04Icon, FileEditIcon, Edit02Icon, Delete02Icon, Folder01Icon, Image01Icon, Video01Icon, MusicNote01Icon, Archive01Icon, Doc01Icon, CodeIcon, File01Icon, FloppyDiskIcon, Cancel01Icon } from "@hugeicons/core-free-icons";
6
6
  import { motion, AnimatePresence } from "motion/react";
7
- import { T as TooltipProvider, a as TooltipRoot, b as TooltipTrigger, c as TooltipContent, d as chatUiQueryKey, g as getChatUiState, s as setChatUiState } from "./tooltip-gbV6rEVv.js";
8
- import { c as cn, b as buttonVariants, B as Button } from "./button-DtQ3rV1m.js";
7
+ import { T as TooltipProvider, a as TooltipRoot, b as TooltipTrigger, c as TooltipContent, d as chatUiQueryKey, g as getChatUiState, s as setChatUiState } from "./tooltip-DgsSPocE.js";
8
+ import { c as cn, b as buttonVariants, B as Button } from "./button-CwY2OHFj.js";
9
9
  import { Link } from "@tanstack/react-router";
10
- import { u as useFileExplorerState, M as MenuRoot, e as MenuTrigger, f as MenuContent, g as MenuItem, D as DialogRoot, a as DialogContent, b as DialogTitle, c as DialogDescription, d as DialogClose } from "./use-file-explorer-state-DMHdtb7D.js";
10
+ import { u as useFileExplorerState, M as MenuRoot, e as MenuTrigger, f as MenuContent, g as MenuItem, D as DialogRoot, a as DialogContent, b as DialogTitle, c as DialogDescription, d as DialogClose } from "./use-file-explorer-state-Il1LlBAe.js";
11
11
  import "@base-ui/react/tooltip";
12
12
  import "@base-ui/react/merge-props";
13
13
  import "@base-ui/react/use-render";
@@ -1509,35 +1509,35 @@ const GridItem = memo(function GridItem2({
1509
1509
  type: "button",
1510
1510
  whileHover: reduceMotion ? {} : { scale: 1.02 },
1511
1511
  whileTap: reduceMotion ? {} : { scale: 0.98 },
1512
- transition: { duration: reduceMotion ? 0 : 0.1 },
1512
+ transition: { duration: reduceMotion ? 0 : 0.15 },
1513
1513
  onClick: handleClick,
1514
1514
  onDoubleClick: handleDoubleClick,
1515
1515
  onKeyDown: handleKeyDown,
1516
1516
  "aria-label": getItemAriaLabel(),
1517
1517
  className: cn(
1518
- "flex flex-col items-center p-3 rounded-lg w-full",
1519
- "transition-colors duration-150 ease-out",
1520
- "hover:bg-primary-100 focus-visible:bg-primary-100",
1518
+ "flex flex-col items-center p-3 rounded-lg w-full border border-transparent",
1519
+ "transition-all duration-150 ease-out",
1520
+ "hover:bg-primary-50 hover:border-primary-100 focus-visible:bg-primary-50",
1521
1521
  "focus-visible:ring-2 focus-visible:ring-primary-300 focus-visible:outline-none",
1522
1522
  "touch-manipulation",
1523
- isSelected && "bg-primary-200"
1523
+ isSelected && "bg-primary-100 border-primary-200"
1524
1524
  ),
1525
1525
  children: [
1526
1526
  /* @__PURE__ */ jsx(
1527
1527
  HugeiconsIcon,
1528
1528
  {
1529
1529
  icon: getFileIcon(item),
1530
- size: 48,
1530
+ size: 40,
1531
1531
  strokeWidth: 1,
1532
1532
  className: cn(
1533
1533
  "mb-2",
1534
- item.isDir ? "text-blue-500" : "text-primary-600"
1534
+ item.isDir ? "text-sky-500" : "text-primary-500"
1535
1535
  ),
1536
1536
  "aria-hidden": "true"
1537
1537
  }
1538
1538
  ),
1539
- /* @__PURE__ */ jsx("span", { className: "text-xs text-center text-primary-900 font-medium truncate w-full min-w-0", children: item.name }),
1540
- !item.isDir ? /* @__PURE__ */ jsx("span", { className: "text-xs text-primary-500 mt-1 tabular-nums", children: formatFileSize$1(item.size) }) : null
1539
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] text-center text-primary-900 font-medium truncate w-full min-w-0", children: item.name }),
1540
+ !item.isDir ? /* @__PURE__ */ jsx("span", { className: "text-[10px] text-primary-400 mt-0.5 tabular-nums", children: formatFileSize$1(item.size) }) : null
1541
1541
  ]
1542
1542
  }
1543
1543
  );
@@ -1572,54 +1572,54 @@ const ListItem = memo(function ListItem2({
1572
1572
  {
1573
1573
  type: "button",
1574
1574
  whileHover: reduceMotion ? {} : { backgroundColor: "rgb(248 250 252)" },
1575
- transition: { duration: reduceMotion ? 0 : 0.1 },
1575
+ transition: { duration: reduceMotion ? 0 : 0.15 },
1576
1576
  onClick: handleClick,
1577
1577
  onDoubleClick: handleDoubleClick,
1578
1578
  onKeyDown: handleKeyDown,
1579
1579
  "aria-label": getItemAriaLabel(),
1580
1580
  className: cn(
1581
- "w-full grid grid-cols-[auto_1fr_auto_auto] gap-4 px-4 py-3 text-left",
1582
- "transition-colors duration-150 ease-out",
1583
- "hover:bg-primary-100 focus-visible:bg-primary-100",
1581
+ "w-full grid grid-cols-[auto_1fr_auto_auto] gap-4 px-4 py-2.5 text-left",
1582
+ "transition-all duration-150 ease-out",
1583
+ "hover:bg-primary-50 focus-visible:bg-primary-50",
1584
1584
  "focus-visible:ring-2 focus-visible:ring-primary-300 focus-visible:outline-none",
1585
1585
  "touch-manipulation",
1586
- isSelected && "bg-primary-200"
1586
+ isSelected && "bg-primary-100"
1587
1587
  ),
1588
1588
  children: [
1589
1589
  /* @__PURE__ */ jsx(
1590
1590
  HugeiconsIcon,
1591
1591
  {
1592
1592
  icon: getFileIcon(item),
1593
- size: 20,
1593
+ size: 18,
1594
1594
  strokeWidth: 1.5,
1595
1595
  className: cn(
1596
1596
  "mt-0.5 shrink-0",
1597
- item.isDir ? "text-blue-500" : "text-primary-600"
1597
+ item.isDir ? "text-sky-500" : "text-primary-500"
1598
1598
  ),
1599
1599
  "aria-hidden": "true"
1600
1600
  }
1601
1601
  ),
1602
- /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx("span", { className: "text-primary-900 font-medium truncate block", children: item.name }) }),
1603
- /* @__PURE__ */ jsx("div", { className: "text-sm text-primary-600 text-right w-16 tabular-nums shrink-0", children: item.isDir ? "—" : formatFileSize$1(item.size) }),
1604
- /* @__PURE__ */ jsx("div", { className: "text-sm text-primary-600 text-right w-20 tabular-nums shrink-0", children: formatRelativeTime(item.modified) })
1602
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx("span", { className: "text-[13px] text-primary-900 font-medium truncate block", children: item.name }) }),
1603
+ /* @__PURE__ */ jsx("div", { className: "text-xs text-primary-500 text-right w-16 tabular-nums shrink-0", children: item.isDir ? "—" : formatFileSize$1(item.size) }),
1604
+ /* @__PURE__ */ jsx("div", { className: "text-xs text-primary-500 text-right w-20 tabular-nums shrink-0", children: formatRelativeTime(item.modified) })
1605
1605
  ]
1606
1606
  }
1607
1607
  );
1608
1608
  return /* @__PURE__ */ jsx(FileContextMenu, { item, onOpenFile, children: itemComponent });
1609
1609
  });
1610
- const emptyState = /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-full", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
1611
- /* @__PURE__ */ jsx(
1610
+ const emptyState = /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-full py-12", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
1611
+ /* @__PURE__ */ jsx("div", { className: "inline-flex items-center justify-center w-12 h-12 rounded-lg bg-primary-50 mb-3", children: /* @__PURE__ */ jsx(
1612
1612
  HugeiconsIcon,
1613
1613
  {
1614
1614
  icon: Folder01Icon,
1615
- size: 48,
1616
- strokeWidth: 1,
1617
- className: "mx-auto text-primary-400 mb-3",
1615
+ size: 24,
1616
+ strokeWidth: 1.5,
1617
+ className: "text-primary-400",
1618
1618
  "aria-hidden": "true"
1619
1619
  }
1620
- ),
1621
- /* @__PURE__ */ jsx("p", { className: "text-primary-600 font-medium", children: "This folder is empty" }),
1622
- /* @__PURE__ */ jsx("p", { className: "text-sm text-primary-500 mt-1", children: "Upload files or create a new folder to get started" })
1620
+ ) }),
1621
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-primary-500", children: "This folder is empty" }),
1622
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-primary-400 mt-1", children: "Upload files or create a new folder to get started" })
1623
1623
  ] }) });
1624
1624
  function FileList({ listing, loading, onOpenFile }) {
1625
1625
  const {
@@ -1727,17 +1727,17 @@ function FileList({ listing, loading, onOpenFile }) {
1727
1727
  role: "table",
1728
1728
  "aria-label": "Files list",
1729
1729
  tabIndex: 0,
1730
- children: /* @__PURE__ */ jsxs("div", { className: "divide-y divide-primary-200", children: [
1730
+ children: /* @__PURE__ */ jsxs("div", { className: "divide-y divide-primary-100", children: [
1731
1731
  /* @__PURE__ */ jsxs(
1732
1732
  "div",
1733
1733
  {
1734
- className: "grid grid-cols-[auto_1fr_auto_auto] gap-4 px-4 py-2 text-sm font-medium text-primary-600 bg-primary-50",
1734
+ className: "grid grid-cols-[auto_1fr_auto_auto] gap-4 px-4 py-2 text-xs font-medium text-primary-400 uppercase tracking-wider bg-primary-50/50",
1735
1735
  role: "row",
1736
1736
  children: [
1737
1737
  /* @__PURE__ */ jsx("div", { className: "w-6", role: "columnheader", "aria-label": "File type" }),
1738
1738
  /* @__PURE__ */ jsx("div", { role: "columnheader", children: "Name" }),
1739
- /* @__PURE__ */ jsx("div", { className: "w-16 text-right tabular-nums", role: "columnheader", children: "Size" }),
1740
- /* @__PURE__ */ jsx("div", { className: "w-20 text-right tabular-nums", role: "columnheader", children: "Modified" })
1739
+ /* @__PURE__ */ jsx("div", { className: "w-16 text-right", role: "columnheader", children: "Size" }),
1740
+ /* @__PURE__ */ jsx("div", { className: "w-20 text-right", role: "columnheader", children: "Modified" })
1741
1741
  ]
1742
1742
  }
1743
1743
  ),
@@ -2347,10 +2347,9 @@ function formatSize(bytes) {
2347
2347
  }
2348
2348
  function FileExplorerScreen() {
2349
2349
  const queryClient = useQueryClient();
2350
- const { currentPath } = useFileExplorerState();
2350
+ const { currentPath, editingFile } = useFileExplorerState();
2351
2351
  const [uploadDialogOpen, setUploadDialogOpen] = useState(false);
2352
2352
  const [createFolderDialogOpen, setCreateFolderDialogOpen] = useState(false);
2353
- const [editingFile, setEditingFile] = useState(null);
2354
2353
  const listingQuery = useFileListing(currentPath);
2355
2354
  const uiQuery = useQuery({
2356
2355
  queryKey: chatUiQueryKey,
@@ -2375,10 +2374,10 @@ function FileExplorerScreen() {
2375
2374
  setCreateFolderDialogOpen(true);
2376
2375
  }, []);
2377
2376
  const handleOpenFile = useCallback((filePath) => {
2378
- setEditingFile(filePath);
2377
+ useFileExplorerState.getState().openInEditor(filePath);
2379
2378
  }, []);
2380
2379
  const handleCloseEditor = useCallback(() => {
2381
- setEditingFile(null);
2380
+ useFileExplorerState.getState().closeEditor();
2382
2381
  }, []);
2383
2382
  const mainStyles = useMemo(() => ({
2384
2383
  display: "flex",
@@ -2399,8 +2398,8 @@ function FileExplorerScreen() {
2399
2398
  /* @__PURE__ */ jsxs("div", { className: cn("h-full overflow-hidden grid grid-cols-[auto_1fr]"), children: [
2400
2399
  sidebar,
2401
2400
  /* @__PURE__ */ jsxs("main", { "aria-label": "File explorer", style: mainStyles, children: [
2402
- /* @__PURE__ */ jsxs("header", { className: "border-b border-primary-200 px-4 py-3", children: [
2403
- /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between mb-2", children: /* @__PURE__ */ jsx(FileBreadcrumb, { path: currentPath }) }),
2401
+ /* @__PURE__ */ jsxs("header", { className: "border-b border-primary-100 px-4 py-3", children: [
2402
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between mb-3", children: /* @__PURE__ */ jsx(FileBreadcrumb, { path: currentPath }) }),
2404
2403
  /* @__PURE__ */ jsx(
2405
2404
  FileToolbar,
2406
2405
  {
@@ -1,6 +1,6 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import { Suspense, lazy } from "react";
3
- const FileExplorerScreen = lazy(() => import("./file-explorer-screen-Cx0jiLRU.js").then((m) => ({
3
+ const FileExplorerScreen = lazy(() => import("./file-explorer-screen-CVlFiAFu.js").then((m) => ({
4
4
  default: m.FileExplorerScreen
5
5
  })));
6
6
  function FilesRoute() {
@@ -1,7 +1,7 @@
1
- import { jsxs, jsx } from "react/jsx-runtime";
1
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
2
  import { useState, useEffect, useMemo } from "react";
3
3
  import { HugeiconsIcon } from "@hugeicons/react";
4
- import { Tick02Icon, Copy01Icon } from "@hugeicons/core-free-icons";
4
+ import { File01Icon, TextWrapIcon, Tick02Icon, Copy01Icon } from "@hugeicons/core-free-icons";
5
5
  import { createHighlighterCore } from "shiki/core";
6
6
  import { createJavaScriptRegexEngine } from "shiki/engine/javascript";
7
7
  import vitesseDark from "@shikijs/themes/vitesse-dark";
@@ -37,7 +37,7 @@ import langXml from "@shikijs/langs/xml";
37
37
  import langYaml from "@shikijs/langs/yaml";
38
38
  import { create } from "zustand";
39
39
  import { persist } from "zustand/middleware";
40
- import { B as Button, c as cn } from "./button-DtQ3rV1m.js";
40
+ import { B as Button, c as cn } from "./button-CwY2OHFj.js";
41
41
  const useChatSettingsStore = create()(
42
42
  persist(
43
43
  (set) => ({
@@ -46,14 +46,31 @@ const useChatSettingsStore = create()(
46
46
  showReasoningBlocks: true,
47
47
  showSearchSources: true,
48
48
  inlineFilePreview: false,
49
- theme: "system"
49
+ theme: "system",
50
+ fontFamily: "system",
51
+ density: "comfortable",
52
+ accentColor: "green",
53
+ chatWidth: "wide",
54
+ sidebarWidth: "normal",
55
+ bubbleStyle: "default"
50
56
  },
51
57
  updateSettings: (updates) => set((state) => ({
52
58
  settings: { ...state.settings, ...updates }
53
59
  }))
54
60
  }),
55
61
  {
56
- name: "chat-settings"
62
+ name: "chat-settings",
63
+ merge: (persistedState, currentState) => {
64
+ const persisted = persistedState;
65
+ return {
66
+ ...currentState,
67
+ ...persisted,
68
+ settings: {
69
+ ...currentState.settings,
70
+ ...persisted?.settings ?? {}
71
+ }
72
+ };
73
+ }
57
74
  }
58
75
  )
59
76
  );
@@ -81,6 +98,8 @@ function useResolvedTheme() {
81
98
  return useMemo(() => {
82
99
  if (theme === "dark") return "dark";
83
100
  if (theme === "light") return "light";
101
+ if (theme === "frost-light") return "light";
102
+ if (theme === "frost-dark") return "dark";
84
103
  return systemIsDark ? "dark" : "light";
85
104
  }, [theme, systemIsDark]);
86
105
  }
@@ -201,6 +220,7 @@ function CodeBlock({
201
220
  content,
202
221
  ariaLabel,
203
222
  language = "text",
223
+ filename,
204
224
  className
205
225
  }) {
206
226
  const resolvedTheme = useResolvedTheme();
@@ -208,6 +228,7 @@ function CodeBlock({
208
228
  const [html, setHtml] = useState(null);
209
229
  const [resolvedLanguage, setResolvedLanguage] = useState("text");
210
230
  const [headerBg, setHeaderBg] = useState();
231
+ const [wrap, setWrap] = useState(false);
211
232
  const fallback = useMemo(() => {
212
233
  return content;
213
234
  }, [content]);
@@ -234,6 +255,20 @@ function CodeBlock({
234
255
  active = false;
235
256
  };
236
257
  }, [content, normalizedLanguage, themeName]);
258
+ useEffect(() => {
259
+ if (typeof window === "undefined") return;
260
+ const saved = window.localStorage.getItem("opencami-code-wrap");
261
+ setWrap(saved === "1");
262
+ }, []);
263
+ function toggleWrap() {
264
+ setWrap((current) => {
265
+ const next = !current;
266
+ if (typeof window !== "undefined") {
267
+ window.localStorage.setItem("opencami-code-wrap", next ? "1" : "0");
268
+ }
269
+ return next;
270
+ });
271
+ }
237
272
  async function handleCopy() {
238
273
  try {
239
274
  await navigator.clipboard.writeText(content);
@@ -243,13 +278,15 @@ function CodeBlock({
243
278
  setCopied(false);
244
279
  }
245
280
  }
246
- const isSingleLine = content.split("\n").length === 1;
281
+ const lineCount = content.replace(/\n$/, "").split("\n").length;
282
+ const isSingleLine = lineCount <= 1;
283
+ const showLineNumbers = !isSingleLine;
247
284
  const displayLanguage = formatLanguageName(resolvedLanguage);
248
285
  return /* @__PURE__ */ jsxs(
249
286
  "div",
250
287
  {
251
288
  className: cn(
252
- "group relative min-w-0 overflow-hidden rounded-lg border border-primary-200",
289
+ "code-block group relative w-full min-w-0 max-w-full overflow-hidden rounded-lg border border-primary-200",
253
290
  className
254
291
  ),
255
292
  children: [
@@ -259,30 +296,48 @@ function CodeBlock({
259
296
  className: cn("flex items-center justify-between px-3 pt-2"),
260
297
  style: { backgroundColor: headerBg },
261
298
  children: [
262
- /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-primary-500", children: displayLanguage }),
263
- /* @__PURE__ */ jsxs(
264
- Button,
265
- {
266
- variant: "ghost",
267
- "aria-label": ariaLabel ?? "Copy code",
268
- className: "h-auto px-0 text-xs font-medium text-primary-500 hover:text-primary-800 hover:bg-transparent",
269
- onClick: () => {
270
- handleCopy().catch(() => {
271
- });
272
- },
273
- children: [
274
- /* @__PURE__ */ jsx(
275
- HugeiconsIcon,
276
- {
277
- icon: copied ? Tick02Icon : Copy01Icon,
278
- size: 14,
279
- strokeWidth: 1.8
280
- }
281
- ),
282
- copied ? "Copied" : "Copy"
283
- ]
284
- }
285
- )
299
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-primary-500 flex items-center gap-1.5 min-w-0", children: filename ? /* @__PURE__ */ jsxs(Fragment, { children: [
300
+ /* @__PURE__ */ jsx(HugeiconsIcon, { icon: File01Icon, size: 14, strokeWidth: 1.8 }),
301
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: filename })
302
+ ] }) : displayLanguage }),
303
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
304
+ /* @__PURE__ */ jsxs(
305
+ Button,
306
+ {
307
+ variant: "ghost",
308
+ "aria-label": wrap ? "Disable wrap" : "Enable wrap",
309
+ className: "h-auto px-0 text-xs font-medium text-primary-500 hover:text-primary-800 hover:bg-transparent",
310
+ onClick: toggleWrap,
311
+ children: [
312
+ /* @__PURE__ */ jsx(HugeiconsIcon, { icon: TextWrapIcon, size: 14, strokeWidth: 1.8 }),
313
+ wrap ? "No wrap" : "Wrap"
314
+ ]
315
+ }
316
+ ),
317
+ /* @__PURE__ */ jsxs(
318
+ Button,
319
+ {
320
+ variant: "ghost",
321
+ "aria-label": ariaLabel ?? "Copy code",
322
+ className: "h-auto px-0 text-xs font-medium text-primary-500 hover:text-primary-800 hover:bg-transparent",
323
+ onClick: () => {
324
+ handleCopy().catch(() => {
325
+ });
326
+ },
327
+ children: [
328
+ /* @__PURE__ */ jsx(
329
+ HugeiconsIcon,
330
+ {
331
+ icon: copied ? Tick02Icon : Copy01Icon,
332
+ size: 14,
333
+ strokeWidth: 1.8
334
+ }
335
+ ),
336
+ copied ? "Copied" : "Copy"
337
+ ]
338
+ }
339
+ )
340
+ ] })
286
341
  ]
287
342
  }
288
343
  ),
@@ -290,8 +345,9 @@ function CodeBlock({
290
345
  "div",
291
346
  {
292
347
  className: cn(
293
- "text-sm text-primary-900 [&>pre]:overflow-x-auto",
294
- isSingleLine ? "[&>pre]:whitespace-pre [&>pre]:px-3 [&>pre]:py-2" : "[&>pre]:px-3 [&>pre]:py-3"
348
+ "w-full min-w-0 max-w-full text-sm text-primary-900 overflow-x-hidden [&>pre]:w-full [&>pre]:min-w-0 [&>pre]:max-w-full [&>pre]:px-3 [&>pre]:py-3",
349
+ wrap ? "[&>pre]:whitespace-pre-wrap [&>pre]:break-words [&_.line]:whitespace-pre-wrap [&_.line]:break-words" : "overflow-x-auto [&>pre]:whitespace-pre [&>pre]:overflow-x-auto",
350
+ showLineNumbers && "[&>pre]:[counter-reset:line] [&_.line]:before:content-[counter(line)] [&_.line]:before:[counter-increment:line] [&_.line]:before:inline-block [&_.line]:before:w-8 [&_.line]:before:mr-4 [&_.line]:before:text-right [&_.line]:before:select-none [&_.line]:before:text-primary-500/60"
295
351
  ),
296
352
  dangerouslySetInnerHTML: { __html: html }
297
353
  }
@@ -299,10 +355,10 @@ function CodeBlock({
299
355
  "pre",
300
356
  {
301
357
  className: cn(
302
- "text-sm",
303
- isSingleLine ? "whitespace-pre px-3 py-2" : "px-3 py-3"
358
+ "w-full min-w-0 max-w-full text-sm px-3 py-3 overflow-x-auto",
359
+ wrap ? "whitespace-pre-wrap break-words" : "whitespace-pre overflow-x-auto"
304
360
  ),
305
- children: /* @__PURE__ */ jsx("code", { className: "overflow-x-auto", children: fallback })
361
+ children: /* @__PURE__ */ jsx("code", { className: "block", children: fallback })
306
362
  }
307
363
  )
308
364
  ]
@@ -311,7 +367,6 @@ function CodeBlock({
311
367
  }
312
368
  export {
313
369
  CodeBlock as C,
314
- useChatSettings as a,
315
370
  resolveLanguage as r,
316
- useChatSettingsStore as u
371
+ useChatSettings as u
317
372
  };
@@ -1,6 +1,6 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import { useEffect } from "react";
3
- import { R as Route } from "./router-OoQe2c20.js";
3
+ import { R as Route } from "./router-rn7pJO_D.js";
4
4
  import "@tanstack/react-router";
5
5
  import "@tanstack/react-query";
6
6
  import "node:crypto";
@@ -9,6 +9,7 @@ import "node:fs";
9
9
  import "node:path";
10
10
  import "node:os";
11
11
  import "@tanstack/router-core/ssr/client";
12
+ import "node:child_process";
12
13
  import "node:fs/promises";
13
14
  import "path";
14
15
  function IndexRoute() {
@@ -1,6 +1,6 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
- import { D as DialogRoot, a as DialogContent, b as DialogTitle, c as DialogDescription, d as DialogClose } from "./use-file-explorer-state-DMHdtb7D.js";
3
- import { B as Button } from "./button-DtQ3rV1m.js";
2
+ import { D as DialogRoot, a as DialogContent, b as DialogTitle, c as DialogDescription, d as DialogClose } from "./use-file-explorer-state-Il1LlBAe.js";
3
+ import { B as Button } from "./button-CwY2OHFj.js";
4
4
  import "@base-ui/react/dialog";
5
5
  import "@base-ui/react/menu";
6
6
  import "zustand";