opencami 1.3.2 → 1.4.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 (59) hide show
  1. package/dist/client/assets/_sessionKey-DB95zj1L.js +97 -0
  2. package/dist/client/assets/agents-LFrWe-HX.js +2 -0
  3. package/dist/client/assets/agents-screen-CEBBk1yO.js +1 -0
  4. package/dist/client/assets/bots-CxDwf_WK.js +2 -0
  5. package/dist/client/assets/bots-screen-D6qma1wK.js +1 -0
  6. package/dist/client/assets/button-Il3CHIzX.js +1 -0
  7. package/dist/client/assets/{connect-CtDn993i.js → connect-D3baVDFL.js} +1 -1
  8. package/dist/client/assets/file-explorer-screen-rtV6n-5_.js +1 -0
  9. package/dist/client/assets/files-DMemuq9D.js +2 -0
  10. package/dist/client/assets/{index-N9-2R5hZ.js → index-ByIsZcHh.js} +1 -1
  11. package/dist/client/assets/index-CVV4XiZo.js +153 -0
  12. package/dist/client/assets/keyboard-shortcuts-dialog-DXC0YHoy.js +1 -0
  13. package/dist/client/assets/{main-DrVY5UJU.js → main-CkIF0soY.js} +9 -9
  14. package/dist/client/assets/opencami-logo-CIxSO1oo.js +1 -0
  15. package/dist/client/assets/{react-CzqI3gbN.js → react-BhVdgA5r.js} +1 -1
  16. package/dist/client/assets/search-dialog-I1jJplIh.js +1 -0
  17. package/dist/client/assets/session-export-dialog-CH5unryw.js +1 -0
  18. package/dist/client/assets/settings-dialog-B8v-GVJ8.js +1 -0
  19. package/dist/client/assets/styles-BaTVzdPa.css +1 -0
  20. package/dist/client/assets/switch-sQnv1YsK.js +1 -0
  21. package/dist/client/assets/tooltip-j_viC_EE.js +1 -0
  22. package/dist/client/assets/use-file-explorer-state-BUH-u7Jv.js +12 -0
  23. package/dist/client/assets/useButton-9VAzplAB.js +9 -0
  24. package/dist/client/assets/useControlled-Y306krcC.js +1 -0
  25. package/dist/client/assets/useMutation-0WgW4xQJ.js +1 -0
  26. package/dist/server/assets/{_sessionKey-DVNrEYFh.js → _sessionKey-BhFH4uWY.js} +325 -56
  27. package/dist/server/assets/_tanstack-start-manifest_v-BaIrL1VQ.js +4 -0
  28. package/dist/server/assets/bots-6ryCIgKh.js +11 -0
  29. package/dist/server/assets/bots-screen-DS_ZF9Ec.js +417 -0
  30. package/dist/server/assets/{connect-CIDOw12K.js → connect-B8jpGQGK.js} +1 -1
  31. package/dist/server/assets/{file-explorer-screen-Cx0jiLRU.js → file-explorer-screen-DCfS_Ajx.js} +4 -5
  32. package/dist/server/assets/{files-Trs1M5ba.js → files-D2GIrPF4.js} +1 -1
  33. package/dist/server/assets/{index-Dv2RXDa2.js → index-B2JHn34C.js} +1 -1
  34. package/dist/server/assets/{index-CmbNTqa2.js → index-BNSsDaLb.js} +71 -35
  35. package/dist/server/assets/{keyboard-shortcuts-dialog-7OEtXUlW.js → keyboard-shortcuts-dialog-CqIm8aYF.js} +1 -1
  36. package/dist/server/assets/{router-OoQe2c20.js → router-Dme7USeO.js} +219 -74
  37. package/dist/server/assets/{search-dialog-Bq0Pnxdb.js → search-dialog-DG0D9KRN.js} +4 -4
  38. package/dist/server/assets/{session-export-dialog-DRVbC8Q-.js → session-export-dialog-DLPZVlQV.js} +1 -1
  39. package/dist/server/assets/{settings-dialog-BsJsnMiu.js → settings-dialog-BaGT4e5l.js} +27 -4
  40. package/dist/server/assets/{use-file-explorer-state-DMHdtb7D.js → use-file-explorer-state-DfAKF2gZ.js} +12 -0
  41. package/dist/server/server.js +2 -2
  42. package/package.json +1 -1
  43. package/dist/client/assets/_sessionKey-Z6Wcnj0N.js +0 -97
  44. package/dist/client/assets/agents-D8ZHVQ1Z.js +0 -2
  45. package/dist/client/assets/agents-screen-BVK0QTRH.js +0 -1
  46. package/dist/client/assets/button-CuH8u1uR.js +0 -1
  47. package/dist/client/assets/file-explorer-screen-DMUuR1uG.js +0 -1
  48. package/dist/client/assets/files-CDMLoJ0u.js +0 -2
  49. package/dist/client/assets/index-IGP-Igwt.js +0 -153
  50. package/dist/client/assets/keyboard-shortcuts-dialog-DW--4YLF.js +0 -1
  51. package/dist/client/assets/opencami-logo-ChD2XR2j.js +0 -1
  52. package/dist/client/assets/search-dialog-DYyFYJmw.js +0 -1
  53. package/dist/client/assets/session-export-dialog-BFizBmGb.js +0 -1
  54. package/dist/client/assets/settings-dialog-UvgVi2It.js +0 -1
  55. package/dist/client/assets/styles-BGTCU8mq.css +0 -1
  56. package/dist/client/assets/switch-DxW2OWPG.js +0 -1
  57. package/dist/client/assets/use-file-explorer-state-Cii59H70.js +0 -12
  58. package/dist/client/assets/useButton-hZdvKtl_.js +0 -9
  59. package/dist/server/assets/_tanstack-start-manifest_v-PwRq_yJS.js +0 -4
@@ -5,9 +5,9 @@ import React__default, { memo, useDeferredValue, useState, useMemo, useCallback,
5
5
  import { useQueryClient, useMutation, useQuery } from "@tanstack/react-query";
6
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";
7
7
  import { HugeiconsIcon } from "@hugeicons/react";
8
- import { Tick01Icon, Cancel01Icon, MoreHorizontalIcon, Pen01Icon, Upload01Icon, Delete01Icon, BotIcon, Clock01Icon, Chat01Icon, ArrowRight01Icon, SidebarLeft01Icon, PencilEdit02Icon, Folder01Icon, AiBrain01Icon, 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, 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-DMHdtb7D.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-DfAKF2gZ.js";
11
11
  import { B as Button, c as cn, b as buttonVariants } from "./button-DtQ3rV1m.js";
12
12
  import { AlertDialog } from "@base-ui/react/alert-dialog";
13
13
  import { Collapsible as Collapsible$1 } from "@base-ui/react/collapsible";
@@ -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, u as useChatSettingsStore, C as CodeBlock, a as useChatSettings$1 } from "./index-CmbNTqa2.js";
20
+ import { r as resolveLanguage, C as CodeBlock, u as useChatSettings$1 } from "./index-BNSsDaLb.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-OoQe2c20.js";
24
+ import { a as Route } from "./router-Dme7USeO.js";
25
25
  function deriveFriendlyIdFromKey(key) {
26
26
  if (!key) return "main";
27
27
  const trimmed = key.trim();
@@ -784,7 +784,7 @@ function SessionItemComponent({
784
784
  className: "text-primary-500/70 shrink-0"
785
785
  }
786
786
  ),
787
- /* @__PURE__ */ jsxs("div", { className: "text-sm font-[450] line-clamp-1 min-w-0", children: [
787
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 truncate text-sm font-[450]", title: label, children: [
788
788
  isPinned ? /* @__PURE__ */ jsx("span", { className: "mr-1 text-xs text-primary-700", "aria-hidden": "true", children: "📌" }) : null,
789
789
  label
790
790
  ] }),
@@ -1649,10 +1649,10 @@ function useRenameSession() {
1649
1649
  return { renameSession, renaming, error };
1650
1650
  }
1651
1651
  const SettingsDialog = lazy(
1652
- () => import("./settings-dialog-BsJsnMiu.js").then((m) => ({ default: m.SettingsDialog }))
1652
+ () => import("./settings-dialog-BaGT4e5l.js").then((m) => ({ default: m.SettingsDialog }))
1653
1653
  );
1654
1654
  const SessionExportDialog = lazy(
1655
- () => import("./session-export-dialog-DRVbC8Q-.js").then((m) => ({
1655
+ () => import("./session-export-dialog-DLPZVlQV.js").then((m) => ({
1656
1656
  default: m.SessionExportDialog
1657
1657
  }))
1658
1658
  );
@@ -1934,6 +1934,48 @@ function ChatSidebarComponent({
1934
1934
  }
1935
1935
  ) }),
1936
1936
  isCollapsed && /* @__PURE__ */ jsx(TooltipContent, { side: "right", children: "Agents" })
1937
+ ] }) }),
1938
+ (() => {
1939
+ try {
1940
+ return localStorage.getItem("opencami-cron-manager") === "true";
1941
+ } catch {
1942
+ return false;
1943
+ }
1944
+ })() && /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(TooltipRoot, { children: [
1945
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
1946
+ Link,
1947
+ {
1948
+ to: "/bots",
1949
+ className: cn(
1950
+ buttonVariants({ variant: "ghost", size: "sm" }),
1951
+ "w-full pl-1.5 justify-start"
1952
+ ),
1953
+ onClick: onSelectSession,
1954
+ children: [
1955
+ /* @__PURE__ */ jsx(
1956
+ HugeiconsIcon,
1957
+ {
1958
+ icon: SmartPhone01Icon,
1959
+ size: 20,
1960
+ strokeWidth: 1.5,
1961
+ className: "min-w-5"
1962
+ }
1963
+ ),
1964
+ /* @__PURE__ */ jsx(AnimatePresence, { initial: false, mode: "wait", children: !isCollapsed && /* @__PURE__ */ jsx(
1965
+ motion.span,
1966
+ {
1967
+ initial: { opacity: 0 },
1968
+ animate: { opacity: 1 },
1969
+ exit: { opacity: 0 },
1970
+ transition,
1971
+ className: "overflow-hidden whitespace-nowrap",
1972
+ children: "Cron Jobs"
1973
+ }
1974
+ ) })
1975
+ ]
1976
+ }
1977
+ ) }),
1978
+ isCollapsed && /* @__PURE__ */ jsx(TooltipContent, { side: "right", children: "Cron Jobs" })
1937
1979
  ] }) })
1938
1980
  ]
1939
1981
  }
@@ -2281,7 +2323,7 @@ function ChatHeaderComponent({
2281
2323
  "div",
2282
2324
  {
2283
2325
  ref: wrapperRef,
2284
- className: "border-b border-primary-200 px-4 h-12 flex items-center bg-surface",
2326
+ className: "border-b border-primary-200 px-4 h-12 flex min-w-0 items-center overflow-x-hidden bg-surface",
2285
2327
  children: [
2286
2328
  showSidebarButton ? /* @__PURE__ */ jsx(
2287
2329
  Button,
@@ -2294,7 +2336,7 @@ function ChatHeaderComponent({
2294
2336
  children: /* @__PURE__ */ jsx(HugeiconsIcon, { icon: Menu01Icon, size: 18, strokeWidth: 1.6 })
2295
2337
  }
2296
2338
  ) : null,
2297
- /* @__PURE__ */ jsx("div", { className: "text-sm font-medium truncate flex-1 min-w-0", children: activeTitle }),
2339
+ /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0 overflow-hidden", children: /* @__PURE__ */ jsx("div", { className: "truncate text-sm font-medium", children: activeTitle }) }),
2298
2340
  /* @__PURE__ */ jsx(
2299
2341
  MemoizedContextMeter,
2300
2342
  {
@@ -2523,6 +2565,108 @@ function MessageActionsBar({
2523
2565
  }
2524
2566
  );
2525
2567
  }
2568
+ const KNOWN_FILE_EXTENSIONS = [
2569
+ "md",
2570
+ "txt",
2571
+ "json",
2572
+ "yaml",
2573
+ "yml",
2574
+ "toml",
2575
+ "py",
2576
+ "js",
2577
+ "ts",
2578
+ "tsx",
2579
+ "jsx",
2580
+ "css",
2581
+ "scss",
2582
+ "html",
2583
+ "xml",
2584
+ "sh",
2585
+ "bash",
2586
+ "go",
2587
+ "rs",
2588
+ "rb",
2589
+ "php",
2590
+ "java",
2591
+ "kt",
2592
+ "swift",
2593
+ "c",
2594
+ "cpp",
2595
+ "h",
2596
+ "hpp",
2597
+ "sql",
2598
+ "graphql",
2599
+ "dockerfile",
2600
+ "env",
2601
+ "conf",
2602
+ "cfg",
2603
+ "ini",
2604
+ "log",
2605
+ "csv"
2606
+ ];
2607
+ const EXTENSION_PATTERN = KNOWN_FILE_EXTENSIONS.join("|");
2608
+ const ABSOLUTE_OR_HOME_PATH_PATTERN = "(?:~\\/[A-Za-z0-9._\\-\\/]*[A-Za-z0-9._\\-\\/]|\\/(?:[\\w.\\-]+\\/)*[\\w.\\-]+\\/?)";
2609
+ const RELATIVE_PATH_PATTERN = "(?:[A-Za-z0-9._\\-]+(?:\\/[A-Za-z0-9._\\-]+)+\\/?)";
2610
+ const BARE_FILENAME_PATTERN = `(?:[A-Za-z0-9_-][A-Za-z0-9._-]*[A-Za-z0-9_-]\\.(?:${EXTENSION_PATTERN})|dockerfile)`;
2611
+ const FILE_PATH_REGEX = new RegExp(
2612
+ `(^|[\\s"'(,;:])(${ABSOLUTE_OR_HOME_PATH_PATTERN}|${RELATIVE_PATH_PATTERN}|${BARE_FILENAME_PATTERN})(?=$|[\\s"'),;:!?])`,
2613
+ "gi"
2614
+ );
2615
+ function trimTrailingPunctuation(path) {
2616
+ if (!path) return { path: path ?? "", trailing: "" };
2617
+ const match = path.match(/^(.*?)([),.;:!?]+)?$/);
2618
+ if (!match) return { path, trailing: "" };
2619
+ return { path: match[1] || path, trailing: match[2] || "" };
2620
+ }
2621
+ function isLikelyFilePath(text) {
2622
+ if (!text) return false;
2623
+ FILE_PATH_REGEX.lastIndex = 0;
2624
+ const match = FILE_PATH_REGEX.exec(text);
2625
+ if (!match) return false;
2626
+ const prefix = match[1] || "";
2627
+ const value = match[2] || "";
2628
+ const matchStart = match.index + prefix.length;
2629
+ const matchEnd = matchStart + value.length;
2630
+ return matchStart === 0 && matchEnd === text.length;
2631
+ }
2632
+ function splitTextByFilePaths(text) {
2633
+ if (!text) return [{ type: "text", value: text }];
2634
+ const segments = [];
2635
+ let lastIndex = 0;
2636
+ FILE_PATH_REGEX.lastIndex = 0;
2637
+ let match;
2638
+ while ((match = FILE_PATH_REGEX.exec(text)) !== null) {
2639
+ const prefix = match[1] || "";
2640
+ const rawPath = match[2] || "";
2641
+ const fullMatch = match[0];
2642
+ const start = match.index;
2643
+ const prefixStart = start;
2644
+ const pathStart = start + prefix.length;
2645
+ if (prefixStart > lastIndex) {
2646
+ segments.push({ type: "text", value: text.slice(lastIndex, prefixStart) });
2647
+ }
2648
+ if (prefix) {
2649
+ segments.push({ type: "text", value: prefix });
2650
+ }
2651
+ const { path, trailing } = trimTrailingPunctuation(rawPath);
2652
+ if (path.length > 1) {
2653
+ segments.push({ type: "path", value: path });
2654
+ if (trailing) {
2655
+ segments.push({ type: "text", value: trailing });
2656
+ }
2657
+ } else {
2658
+ segments.push({ type: "text", value: fullMatch });
2659
+ }
2660
+ lastIndex = pathStart + rawPath.length;
2661
+ }
2662
+ if (lastIndex < text.length) {
2663
+ segments.push({ type: "text", value: text.slice(lastIndex) });
2664
+ }
2665
+ return segments.length > 0 ? segments : [{ type: "text", value: text }];
2666
+ }
2667
+ function filePathToMarkdownHref(path) {
2668
+ return `openclaw-file://${encodeURIComponent(path)}`;
2669
+ }
2526
2670
  function markdownHrefToFilePath(href) {
2527
2671
  if (!href?.startsWith("openclaw-file://")) return null;
2528
2672
  try {
@@ -2531,6 +2675,39 @@ function markdownHrefToFilePath(href) {
2531
2675
  return null;
2532
2676
  }
2533
2677
  }
2678
+ function remarkFilePathLinks() {
2679
+ return (tree) => {
2680
+ function visit(node, parent) {
2681
+ if (!node) return;
2682
+ if (node.type === "text" && parent && parent.type !== "link" && parent.type !== "inlineCode" && parent.type !== "code") {
2683
+ const segments = splitTextByFilePaths(String(node.value || ""));
2684
+ const hasPaths = segments.some((segment) => segment.type === "path");
2685
+ if (!hasPaths) return;
2686
+ const replacement = segments.map((segment) => {
2687
+ if (segment.type === "text") {
2688
+ return { type: "text", value: segment.value };
2689
+ }
2690
+ return {
2691
+ type: "link",
2692
+ url: filePathToMarkdownHref(segment.value),
2693
+ children: [{ type: "text", value: segment.value }]
2694
+ };
2695
+ });
2696
+ const index = parent.children.indexOf(node);
2697
+ if (index >= 0) {
2698
+ parent.children.splice(index, 1, ...replacement);
2699
+ }
2700
+ return;
2701
+ }
2702
+ if (Array.isArray(node.children)) {
2703
+ for (const child of [...node.children]) {
2704
+ visit(child, node);
2705
+ }
2706
+ }
2707
+ }
2708
+ visit(tree, null);
2709
+ };
2710
+ }
2534
2711
  const EXTENSION_LANGUAGE_MAP = {
2535
2712
  py: "python",
2536
2713
  ts: "typescript",
@@ -2574,27 +2751,67 @@ function languageFromFilePath(path) {
2574
2751
  return resolveLanguage(mapped);
2575
2752
  }
2576
2753
  const INLINE_PREVIEW_MAX_BYTES = 100 * 1024;
2754
+ function normalizeClickedPath(path) {
2755
+ if (!path) return "/";
2756
+ return path.includes("/") ? path : `/${path}`;
2757
+ }
2758
+ function toWorkspacePath(path) {
2759
+ let p = normalizeClickedPath(path);
2760
+ const prefixes = ["/root/clawd/", "/root/"];
2761
+ for (const prefix of prefixes) {
2762
+ if (p.startsWith(prefix)) {
2763
+ p = "/" + p.slice(prefix.length);
2764
+ break;
2765
+ }
2766
+ }
2767
+ return p.startsWith("/") ? p : `/${p}`;
2768
+ }
2769
+ function hasExtension(path) {
2770
+ const trimmed = path.replace(/\/+$/, "");
2771
+ const name = trimmed.split("/").pop() || "";
2772
+ if (!name || name.startsWith(".")) return false;
2773
+ return name.includes(".");
2774
+ }
2775
+ function isDirectoryPathHeuristic(path) {
2776
+ if (!path) return false;
2777
+ return path.endsWith("/") || !hasExtension(path);
2778
+ }
2779
+ function isDirectoryError(code, message) {
2780
+ const normalizedCode = String(code || "").toUpperCase();
2781
+ const normalizedMessage = String(message || "").toLowerCase();
2782
+ return normalizedCode.includes("DIRECTORY") || normalizedMessage.includes("is a directory") || normalizedMessage.includes("directory");
2783
+ }
2577
2784
  function parseMarkdownIntoBlocks(markdown) {
2578
2785
  const tokens = marked.lexer(markdown);
2579
2786
  return tokens.map((token) => token.raw);
2580
2787
  }
2581
2788
  function extractLanguage(className) {
2582
2789
  if (!className) return "text";
2583
- const match = className.match(/language-(\w+)/);
2790
+ const match = className.match(/language-([\w-]+)/);
2584
2791
  return match ? match[1] : "text";
2585
2792
  }
2793
+ function extractFilenameFromMeta(meta) {
2794
+ const value = meta?.trim();
2795
+ if (!value) return void 0;
2796
+ const firstToken = value.split(/\s+/)[0];
2797
+ return firstToken || void 0;
2798
+ }
2586
2799
  const BASE_COMPONENTS = {
2587
- code: function CodeComponent({ className, children }) {
2800
+ code: function CodeComponent({ className, children, node }) {
2588
2801
  const isInline = !className?.includes("language-");
2589
2802
  if (isInline) {
2590
2803
  return /* @__PURE__ */ jsx("code", { className: "rounded bg-primary-100 px-1.5 py-1 text-sm font-mono text-primary-900 border border-primary-200", children });
2591
2804
  }
2592
2805
  const language = extractLanguage(className);
2806
+ const filename = extractFilenameFromMeta(
2807
+ node?.data?.meta
2808
+ );
2593
2809
  return /* @__PURE__ */ jsx(
2594
2810
  CodeBlock,
2595
2811
  {
2596
2812
  content: String(children ?? ""),
2597
2813
  language,
2814
+ filename,
2598
2815
  className: "w-full"
2599
2816
  }
2600
2817
  );
@@ -2654,13 +2871,12 @@ const BASE_COMPONENTS = {
2654
2871
  return /* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-primary-950", children });
2655
2872
  }
2656
2873
  };
2657
- function createDefaultComponents(onOpenFilePreview, inlineFilePreviewEnabled) {
2658
- const FILE_PATH_RE = /^(?:~\/[\w.\-\/]+|\/(?:[\w.\-]+\/)+[\w.\-]+)$/;
2874
+ function createDefaultComponents(onOpenFilePreview) {
2659
2875
  return {
2660
2876
  ...BASE_COMPONENTS,
2661
2877
  a: function AComponent({ children, href }) {
2662
2878
  const filePath = markdownHrefToFilePath(href);
2663
- if (inlineFilePreviewEnabled && filePath) {
2879
+ if (filePath) {
2664
2880
  return /* @__PURE__ */ jsx(
2665
2881
  "button",
2666
2882
  {
@@ -2675,6 +2891,9 @@ function createDefaultComponents(onOpenFilePreview, inlineFilePreviewEnabled) {
2675
2891
  "a",
2676
2892
  {
2677
2893
  href,
2894
+ onClick: (event) => {
2895
+ if (href?.startsWith("openclaw-file://")) event.preventDefault();
2896
+ },
2678
2897
  className: "text-primary-950 underline decoration-primary-300 underline-offset-4 transition-colors hover:text-primary-950 hover:decoration-primary-500",
2679
2898
  target: "_blank",
2680
2899
  rel: "noopener noreferrer",
@@ -2682,10 +2901,24 @@ function createDefaultComponents(onOpenFilePreview, inlineFilePreviewEnabled) {
2682
2901
  }
2683
2902
  );
2684
2903
  },
2685
- code: function InlineCodeComponent({ children, className }) {
2686
- if (className) return /* @__PURE__ */ jsx("code", { className, children });
2904
+ code: function InlineCodeComponent({ children, className, node }) {
2905
+ if (className?.includes("language-")) {
2906
+ const language = extractLanguage(className);
2907
+ const filename = extractFilenameFromMeta(
2908
+ node?.data?.meta
2909
+ );
2910
+ return /* @__PURE__ */ jsx(
2911
+ CodeBlock,
2912
+ {
2913
+ content: String(children ?? ""),
2914
+ language,
2915
+ filename,
2916
+ className: "w-full"
2917
+ }
2918
+ );
2919
+ }
2687
2920
  const text = typeof children === "string" ? children : Array.isArray(children) ? children.filter((c) => typeof c === "string").join("") : String(children ?? "");
2688
- if (inlineFilePreviewEnabled && text && FILE_PATH_RE.test(text)) {
2921
+ if (text && isLikelyFilePath(text)) {
2689
2922
  return /* @__PURE__ */ jsx(
2690
2923
  "button",
2691
2924
  {
@@ -2708,7 +2941,7 @@ const MemoizedMarkdownBlock = memo(
2708
2941
  return /* @__PURE__ */ jsx(
2709
2942
  ReactMarkdown,
2710
2943
  {
2711
- remarkPlugins: [remarkGfm, remarkBreaks],
2944
+ remarkPlugins: [remarkGfm, remarkBreaks, remarkFilePathLinks],
2712
2945
  components,
2713
2946
  children: content
2714
2947
  }
@@ -2735,15 +2968,24 @@ function MarkdownComponent({
2735
2968
  const blockId = id ?? generatedId;
2736
2969
  const blocks = useMemo(() => parseMarkdownIntoBlocks(children), [children]);
2737
2970
  const [filePreview, setFilePreview] = useState({ status: "idle" });
2738
- const inlineFilePreviewEnabled = useChatSettingsStore(
2739
- (state) => state.settings.inlineFilePreview
2740
- );
2971
+ const navigate = useNavigate();
2972
+ const openDirectoryInExplorer = useCallback((path) => {
2973
+ const workspacePath = toWorkspacePath(path);
2974
+ useFileExplorerState.getState().navigateTo(workspacePath);
2975
+ setFilePreview({ status: "idle" });
2976
+ navigate({ to: "/files" });
2977
+ }, [navigate]);
2978
+ const onOpenFilePreview = useCallback((path) => {
2979
+ const resolvedPath = normalizeClickedPath(path);
2980
+ if (isDirectoryPathHeuristic(resolvedPath)) {
2981
+ openDirectoryInExplorer(resolvedPath);
2982
+ return;
2983
+ }
2984
+ setFilePreview({ status: "loading", path: resolvedPath });
2985
+ }, [openDirectoryInExplorer]);
2741
2986
  const defaultComponents = useMemo(
2742
- () => createDefaultComponents(
2743
- (path) => setFilePreview({ status: "loading", path }),
2744
- inlineFilePreviewEnabled
2745
- ),
2746
- [inlineFilePreviewEnabled]
2987
+ () => createDefaultComponents(onOpenFilePreview),
2988
+ [onOpenFilePreview]
2747
2989
  );
2748
2990
  const mergedComponents = useMemo(
2749
2991
  () => ({ ...defaultComponents, ...components || {} }),
@@ -2758,6 +3000,10 @@ function MarkdownComponent({
2758
3000
  }).then(async (response) => {
2759
3001
  const payload = await response.json().catch(() => ({}));
2760
3002
  if (!response.ok) {
3003
+ if (isDirectoryError(payload.code, payload.message)) {
3004
+ openDirectoryInExplorer(path);
3005
+ return;
3006
+ }
2761
3007
  const error = fileErrorMessageFromResponse(response.status, payload.code);
2762
3008
  setFilePreview({ status: "error", path, message: error });
2763
3009
  return;
@@ -2779,17 +3025,33 @@ function MarkdownComponent({
2779
3025
  setFilePreview({ status: "error", path, message: "Failed to load file preview" });
2780
3026
  });
2781
3027
  return () => controller.abort();
2782
- }, [filePreview]);
3028
+ }, [filePreview, openDirectoryInExplorer]);
2783
3029
  const previewOpen = filePreview.status !== "idle";
2784
3030
  return /* @__PURE__ */ jsxs(Fragment, { children: [
2785
- /* @__PURE__ */ jsx("div", { className: cn("flex flex-col gap-2", className), children: blocks.map((block, index) => /* @__PURE__ */ jsx(
2786
- MemoizedMarkdownBlock,
3031
+ /* @__PURE__ */ jsx(
3032
+ "div",
2787
3033
  {
2788
- content: block,
2789
- components: mergedComponents
2790
- },
2791
- `${blockId}-block-${index}`
2792
- )) }),
3034
+ className: cn("flex min-w-0 max-w-full flex-col gap-2 overflow-x-hidden", className),
3035
+ onClickCapture: (event) => {
3036
+ const target = event.target;
3037
+ const anchor = target?.closest?.('a[href^="openclaw-file://"]');
3038
+ if (!anchor) return;
3039
+ const filePath = markdownHrefToFilePath(anchor.getAttribute("href") ?? void 0);
3040
+ if (!filePath) return;
3041
+ event.preventDefault();
3042
+ event.stopPropagation();
3043
+ onOpenFilePreview(filePath);
3044
+ },
3045
+ children: blocks.map((block, index) => /* @__PURE__ */ jsx(
3046
+ MemoizedMarkdownBlock,
3047
+ {
3048
+ content: block,
3049
+ components: mergedComponents
3050
+ },
3051
+ `${blockId}-block-${index}`
3052
+ ))
3053
+ }
3054
+ ),
2793
3055
  /* @__PURE__ */ jsx(
2794
3056
  DialogRoot,
2795
3057
  {
@@ -2809,21 +3071,28 @@ function MarkdownComponent({
2809
3071
  {
2810
3072
  to: "/files",
2811
3073
  onClick: () => {
2812
- let p = filePreview.status !== "idle" ? filePreview.path : "";
3074
+ const p = filePreview.status !== "idle" ? toWorkspacePath(filePreview.path) : "";
2813
3075
  if (p) {
2814
- const prefixes = ["/root/clawd/", "/root/"];
2815
- for (const prefix of prefixes) {
2816
- if (p.startsWith(prefix)) {
2817
- p = "/" + p.slice(prefix.length);
2818
- break;
2819
- }
2820
- }
2821
3076
  const dir = p.includes("/") ? p.slice(0, p.lastIndexOf("/")) || "/" : "/";
2822
3077
  useFileExplorerState.getState().navigateTo(dir);
2823
3078
  }
2824
3079
  setFilePreview({ status: "idle" });
2825
3080
  },
2826
- children: "Open in File Explorer"
3081
+ children: "Open in Explorer"
3082
+ }
3083
+ ) }),
3084
+ filePreview.status !== "idle" && /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", asChild: true, children: /* @__PURE__ */ jsx(
3085
+ Link,
3086
+ {
3087
+ to: "/files",
3088
+ onClick: () => {
3089
+ const p = filePreview.status !== "idle" ? toWorkspacePath(filePreview.path) : "";
3090
+ if (p) {
3091
+ useFileExplorerState.getState().openInEditor(p);
3092
+ }
3093
+ setFilePreview({ status: "idle" });
3094
+ },
3095
+ children: "Open in Editor"
2827
3096
  }
2828
3097
  ) }),
2829
3098
  /* @__PURE__ */ jsx(DialogClose, { children: "Close" })
@@ -2858,7 +3127,7 @@ function MessageContent({
2858
3127
  ...props
2859
3128
  }) {
2860
3129
  const classNames = cn(
2861
- "rounded-[12px] break-words whitespace-normal min-w-0",
3130
+ "rounded-[12px] break-words whitespace-normal min-w-0 max-w-full overflow-x-hidden",
2862
3131
  className
2863
3132
  );
2864
3133
  return markdown ? /* @__PURE__ */ jsx(Markdown, { className: classNames, ...props, children }) : /* @__PURE__ */ jsx("div", { className: classNames, ...props, children });
@@ -3225,19 +3494,19 @@ function MessageItemComponent({
3225
3494
  },
3226
3495
  idx
3227
3496
  )) }),
3228
- /* @__PURE__ */ jsx(Message, { className: cn(isUser ? "flex-row-reverse" : ""), children: /* @__PURE__ */ jsx(
3497
+ /* @__PURE__ */ jsx(Message, { className: cn("min-w-0 max-w-full", isUser ? "flex-row-reverse" : ""), children: /* @__PURE__ */ jsx(
3229
3498
  MessageContent,
3230
3499
  {
3231
3500
  markdown: !isUser,
3232
3501
  className: cn(
3233
- "text-primary-900 opencami-text-size",
3502
+ "text-primary-900 opencami-text-size min-w-0 max-w-full",
3234
3503
  !isUser ? "bg-transparent w-full" : "bg-primary-100 px-4 py-2.5 max-w-[85%]",
3235
3504
  !isUser && isStreaming && "stream-fade-in"
3236
3505
  ),
3237
3506
  children: text
3238
3507
  }
3239
3508
  ) }),
3240
- hasToolCalls && settings.showToolMessages && /* @__PURE__ */ jsx("div", { className: "w-full max-w-[900px] mt-2 flex flex-col gap-3", children: toolCalls.map((toolCall) => {
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) => {
3241
3510
  const resultMessage = toolCall.id ? toolResultsByCallId?.get(toolCall.id) : void 0;
3242
3511
  const toolPart = mapToolCallToToolPart(toolCall, resultMessage);
3243
3512
  return /* @__PURE__ */ jsx(
@@ -3854,12 +4123,12 @@ function ChatContainerShell({
3854
4123
  /* @__PURE__ */ jsx(
3855
4124
  ScrollAreaViewport,
3856
4125
  {
3857
- className: "relative will-change-transform",
4126
+ className: "relative will-change-transform overflow-x-hidden",
3858
4127
  ref: viewportRef,
3859
4128
  ...viewportProps
3860
4129
  }
3861
4130
  ),
3862
- /* @__PURE__ */ jsx("div", { className: "relative mx-auto w-full max-w-full px-5 sm:max-w-[768px] sm:min-w-[400px] ", children: /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute bottom-10 right-10 z-50", children: /* @__PURE__ */ jsx(ScrollButton, { scrollRef }) }) }),
4131
+ /* @__PURE__ */ jsx("div", { className: "relative mx-auto w-full min-w-0 max-w-full px-5 sm:max-w-[768px]", children: /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute bottom-10 right-10 z-50", children: /* @__PURE__ */ jsx(ScrollButton, { scrollRef }) }) }),
3863
4132
  /* @__PURE__ */ jsx(ScrollAreaScrollbar, { orientation: "vertical", children: /* @__PURE__ */ jsx(ScrollAreaThumb, {}) }),
3864
4133
  /* @__PURE__ */ jsx(ScrollAreaCorner, {})
3865
4134
  ]
@@ -3897,7 +4166,7 @@ function ChatContainerPortal({
3897
4166
  }) {
3898
4167
  if (!viewportNode) return null;
3899
4168
  return createPortal(
3900
- /* @__PURE__ */ jsx("div", { className: "relative flex w-full flex-col", children }),
4169
+ /* @__PURE__ */ jsx("div", { className: "relative flex w-full min-w-0 max-w-full flex-col overflow-x-hidden", children }),
3901
4170
  viewportNode
3902
4171
  );
3903
4172
  }
@@ -3946,9 +4215,9 @@ function ChatContainerContent({
3946
4215
  return /* @__PURE__ */ jsx(
3947
4216
  "div",
3948
4217
  {
3949
- className: cn("flex w-full flex-col min-h-full", className),
4218
+ className: cn("flex w-full min-w-0 max-w-full flex-col min-h-full overflow-x-hidden", className),
3950
4219
  ...props,
3951
- children: /* @__PURE__ */ jsx("div", { className: "mx-auto w-full max-w-full px-2 md:px-5 sm:max-w-[768px] sm:min-w-[400px] flex flex-col flex-1 min-h-full", children: /* @__PURE__ */ jsx("div", { className: "flex flex-col space-y-3 md:space-y-6", children }) })
4220
+ children: /* @__PURE__ */ jsx("div", { className: "mx-auto w-full min-w-0 max-w-full px-2 md:px-5 sm:max-w-[768px] flex flex-col flex-1 min-h-full overflow-x-hidden", children: /* @__PURE__ */ jsx("div", { className: "flex min-w-0 max-w-full flex-col space-y-3 md:space-y-6", children }) })
3952
4221
  }
3953
4222
  );
3954
4223
  }
@@ -6265,12 +6534,12 @@ function useSwipeGesture(options) {
6265
6534
  };
6266
6535
  }
6267
6536
  const KeyboardShortcutsDialog = lazy(
6268
- () => import("./keyboard-shortcuts-dialog-7OEtXUlW.js").then((m) => ({
6537
+ () => import("./keyboard-shortcuts-dialog-CqIm8aYF.js").then((m) => ({
6269
6538
  default: m.KeyboardShortcutsDialog
6270
6539
  }))
6271
6540
  );
6272
6541
  const SearchDialog = lazy(
6273
- () => import("./search-dialog-Bq0Pnxdb.js").then((m) => ({
6542
+ () => import("./search-dialog-DG0D9KRN.js").then((m) => ({
6274
6543
  default: m.SearchDialog
6275
6544
  }))
6276
6545
  );
@@ -6945,7 +7214,7 @@ function ChatScreen({
6945
7214
  {
6946
7215
  className: cn(
6947
7216
  "h-full overflow-hidden",
6948
- isMobile ? "relative" : "grid grid-cols-[auto_1fr]"
7217
+ isMobile ? "relative" : "grid grid-cols-[auto_minmax(0,1fr)]"
6949
7218
  ),
6950
7219
  children: [
6951
7220
  hideUi ? null : isMobile ? /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -6975,7 +7244,7 @@ function ChatScreen({
6975
7244
  /* @__PURE__ */ jsxs(
6976
7245
  "main",
6977
7246
  {
6978
- className: "flex flex-col h-full min-h-0",
7247
+ className: "flex flex-col h-full min-h-0 min-w-0 overflow-x-hidden",
6979
7248
  ref: mainRef,
6980
7249
  ...sidebarSwipeHandlers,
6981
7250
  children: [
@@ -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", "/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/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-CkIF0soY.js"], "assets": [] }, "/": { "filePath": "/home/runner/work/opencami/opencami/src/routes/index.tsx", "assets": [], "preloads": ["/assets/index-ByIsZcHh.js"] }, "/agents": { "filePath": "/home/runner/work/opencami/opencami/src/routes/agents.tsx", "assets": [], "preloads": ["/assets/agents-LFrWe-HX.js"] }, "/bots": { "filePath": "/home/runner/work/opencami/opencami/src/routes/bots.tsx", "assets": [], "preloads": ["/assets/bots-CxDwf_WK.js"] }, "/connect": { "filePath": "/home/runner/work/opencami/opencami/src/routes/connect.tsx", "assets": [], "preloads": ["/assets/connect-D3baVDFL.js", "/assets/index-CVV4XiZo.js", "/assets/button-Il3CHIzX.js", "/assets/react-BhVdgA5r.js"] }, "/files": { "filePath": "/home/runner/work/opencami/opencami/src/routes/files.tsx", "assets": [], "preloads": ["/assets/files-DMemuq9D.js"] }, "/new": { "filePath": "/home/runner/work/opencami/opencami/src/routes/new.tsx", "assets": [], "preloads": ["/assets/new-DLlBm66g.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/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-DB95zj1L.js", "/assets/useButton-9VAzplAB.js", "/assets/tooltip-j_viC_EE.js", "/assets/button-Il3CHIzX.js", "/assets/use-file-explorer-state-BUH-u7Jv.js", "/assets/useControlled-Y306krcC.js", "/assets/useMutation-0WgW4xQJ.js", "/assets/opencami-logo-CIxSO1oo.js", "/assets/index-CVV4XiZo.js", "/assets/react-BhVdgA5r.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-CkIF0soY.js" });
2
+ export {
3
+ tsrStartManifest
4
+ };
@@ -0,0 +1,11 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { Suspense, lazy } from "react";
3
+ const BotsScreen = lazy(() => import("./bots-screen-DS_ZF9Ec.js").then((m) => ({
4
+ default: m.BotsScreen
5
+ })));
6
+ function BotsRoute() {
7
+ return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx("div", { className: "flex h-screen items-center justify-center text-sm text-primary-500", children: "Loading…" }), children: /* @__PURE__ */ jsx(BotsScreen, {}) });
8
+ }
9
+ export {
10
+ BotsRoute as component
11
+ };