@invergent/agent-chat-react 1.5.2 → 1.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,13 +1,13 @@
1
1
  'use strict';
2
2
 
3
+ var clsx = require('clsx');
4
+ var tailwindMerge = require('tailwind-merge');
3
5
  var react = require('react');
4
- var reactVega = require('react-vega');
6
+ var auto = require('chart.js/auto');
5
7
  var nextThemes = require('next-themes');
6
8
  var jsxRuntime = require('react/jsx-runtime');
7
9
  var classVarianceAuthority = require('class-variance-authority');
8
10
  var radixUi = require('radix-ui');
9
- var clsx = require('clsx');
10
- var tailwindMerge = require('tailwind-merge');
11
11
  var lucideReact = require('lucide-react');
12
12
  var useStickToBottom = require('use-stick-to-bottom');
13
13
  var cjk = require('@streamdown/cjk');
@@ -22,8 +22,10 @@ var Ansi = require('ansi-to-react');
22
22
  var tokenlens = require('tokenlens');
23
23
  var cmdk = require('cmdk');
24
24
  var nanoid = require('nanoid');
25
+ require('pdfjs-dist/web/pdf_viewer.css');
25
26
  var dateFns = require('date-fns');
26
27
 
28
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
27
29
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
28
30
 
29
31
  var Ansi__default = /*#__PURE__*/_interopDefault(Ansi);
@@ -37,56 +39,63 @@ var __export = (target, all) => {
37
39
  for (var name in all)
38
40
  __defProp(target, name, { get: all[name], enumerable: true });
39
41
  };
42
+ function cn(...inputs) {
43
+ return tailwindMerge.twMerge(clsx.clsx(inputs));
44
+ }
45
+ var init_utils = __esm({
46
+ "src/lib/utils.ts"() {
47
+ }
48
+ });
40
49
 
41
50
  // src/components/chat/artifacts/artifact-chart.tsx
42
51
  var artifact_chart_exports = {};
43
52
  __export(artifact_chart_exports, {
44
53
  ArtifactChart: () => ArtifactChart
45
54
  });
46
- function ArtifactChart({ spec }) {
55
+ function ArtifactChart({
56
+ spec,
57
+ fill = false
58
+ }) {
47
59
  const { resolvedTheme } = nextThemes.useTheme();
48
60
  const isDark = resolvedTheme === "dark";
49
- const containerRef = react.useRef(null);
50
- const [width, setWidth] = react.useState(null);
51
- react.useEffect(() => {
52
- const el = containerRef.current;
53
- if (!el) return;
54
- const observer = new ResizeObserver((entries) => {
55
- const w = entries[0]?.contentRect.width ?? el.offsetWidth;
56
- if (w >= MIN_CHART_WIDTH) setWidth(Math.floor(w));
57
- });
58
- observer.observe(el);
59
- if (el.offsetWidth >= MIN_CHART_WIDTH) setWidth(el.offsetWidth);
60
- return () => observer.disconnect();
61
- }, []);
61
+ const canvasRef = react.useRef(null);
62
+ const chartRef = react.useRef(null);
62
63
  const [error, setError] = react.useState(null);
63
- const merged = react.useMemo(() => {
64
- if (width == null) return null;
65
- const base = spec.vega_lite ?? {};
66
- const themeConfig = isDark ? DARK_CONFIG : LIGHT_CONFIG;
64
+ const config = react.useMemo(() => {
65
+ const base = cloneChartConfig(spec.chart_js ?? {});
67
66
  return {
68
- $schema: VEGA_LITE_SCHEMA,
69
- width,
70
- height: DEFAULT_CHART_HEIGHT,
71
67
  ...base,
72
- config: {
73
- ...themeConfig,
74
- ...base.config ?? {}
75
- }
68
+ options: mergeThemeOptions(base.options, base.type, isDark)
76
69
  };
77
- }, [spec.vega_lite, isDark, width]);
70
+ }, [spec.chart_js, isDark]);
78
71
  react.useEffect(() => {
72
+ const canvas = canvasRef.current;
73
+ if (!canvas) return;
74
+ chartRef.current?.destroy();
75
+ chartRef.current = null;
79
76
  setError(null);
80
- }, [spec.vega_lite]);
77
+ try {
78
+ chartRef.current = new auto.Chart(canvas, config);
79
+ } catch (e) {
80
+ setError(e instanceof Error ? e.message : String(e));
81
+ }
82
+ return () => {
83
+ chartRef.current?.destroy();
84
+ chartRef.current = null;
85
+ };
86
+ }, [config]);
81
87
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
82
- /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "w-full", children: merged && /* @__PURE__ */ jsxRuntime.jsx(
83
- reactVega.VegaEmbed,
88
+ /* @__PURE__ */ jsxRuntime.jsx(
89
+ "div",
84
90
  {
85
- spec: merged,
86
- options: { actions: false, renderer: "svg" },
87
- onError: (e) => setError(e instanceof Error ? e.message : String(e))
91
+ className: cn("relative w-full", fill && "h-full min-h-80"),
92
+ style: fill ? void 0 : {
93
+ height: DEFAULT_CHART_HEIGHT,
94
+ maxHeight: MAX_INLINE_CHART_HEIGHT
95
+ },
96
+ children: /* @__PURE__ */ jsxRuntime.jsx("canvas", { ref: canvasRef })
88
97
  }
89
- ) }),
98
+ ),
90
99
  error && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-destructive", children: [
91
100
  "Chart error: ",
92
101
  error
@@ -94,35 +103,101 @@ function ArtifactChart({ spec }) {
94
103
  spec.caption && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: spec.caption })
95
104
  ] });
96
105
  }
97
- var VEGA_LITE_SCHEMA, DEFAULT_CHART_HEIGHT, MIN_CHART_WIDTH, LIGHT_CONFIG, DARK_CONFIG;
106
+ function cloneChartConfig(value) {
107
+ return JSON.parse(JSON.stringify(value));
108
+ }
109
+ function mergeThemeOptions(options, chartType, isDark) {
110
+ const theme = isDark ? DARK_THEME : LIGHT_THEME;
111
+ const base = isRecord(options) ? options : {};
112
+ const plugins = isRecord(base.plugins) ? base.plugins : {};
113
+ const legend = isRecord(plugins.legend) ? plugins.legend : {};
114
+ const legendLabels = isRecord(legend.labels) ? legend.labels : {};
115
+ const title = isRecord(plugins.title) ? plugins.title : {};
116
+ const scales = isRecord(base.scales) ? base.scales : {};
117
+ return {
118
+ responsive: true,
119
+ maintainAspectRatio: false,
120
+ color: theme.text,
121
+ ...base,
122
+ plugins: {
123
+ ...plugins,
124
+ legend: {
125
+ ...legend,
126
+ labels: {
127
+ color: theme.text,
128
+ ...legendLabels
129
+ }
130
+ },
131
+ title: {
132
+ color: theme.title,
133
+ ...title
134
+ }
135
+ },
136
+ scales: mergeScales(scales, chartType, theme)
137
+ };
138
+ }
139
+ function mergeScales(scales, chartType, theme) {
140
+ const names = /* @__PURE__ */ new Set([...defaultScaleNames(chartType), ...Object.keys(scales)]);
141
+ const merged = {};
142
+ for (const name of names) {
143
+ const scale = isRecord(scales[name]) ? scales[name] : {};
144
+ const ticks = isRecord(scale.ticks) ? scale.ticks : {};
145
+ const grid = isRecord(scale.grid) ? scale.grid : {};
146
+ const title = isRecord(scale.title) ? scale.title : {};
147
+ merged[name] = {
148
+ ...scale,
149
+ ticks: {
150
+ color: theme.text,
151
+ ...ticks
152
+ },
153
+ grid: {
154
+ color: theme.grid,
155
+ ...grid
156
+ },
157
+ border: {
158
+ color: theme.border,
159
+ ...isRecord(scale.border) ? scale.border : {}
160
+ },
161
+ title: {
162
+ color: theme.title,
163
+ ...title
164
+ }
165
+ };
166
+ }
167
+ return merged;
168
+ }
169
+ function defaultScaleNames(chartType) {
170
+ if (chartType === "bar" || chartType === "line" || chartType === "scatter") {
171
+ return ["x", "y"];
172
+ }
173
+ if (chartType === "bubble") {
174
+ return ["x", "y"];
175
+ }
176
+ if (chartType === "radar" || chartType === "polarArea") {
177
+ return ["r"];
178
+ }
179
+ return [];
180
+ }
181
+ function isRecord(value) {
182
+ return typeof value === "object" && value !== null && !Array.isArray(value);
183
+ }
184
+ var DEFAULT_CHART_HEIGHT, MAX_INLINE_CHART_HEIGHT, LIGHT_THEME, DARK_THEME;
98
185
  var init_artifact_chart = __esm({
99
186
  "src/components/chat/artifacts/artifact-chart.tsx"() {
100
- VEGA_LITE_SCHEMA = "https://vega.github.io/schema/vega-lite/v5.json";
187
+ init_utils();
101
188
  DEFAULT_CHART_HEIGHT = 320;
102
- MIN_CHART_WIDTH = 120;
103
- LIGHT_CONFIG = {
104
- background: "transparent",
105
- axis: {
106
- labelColor: "#444",
107
- titleColor: "#111",
108
- gridColor: "#e5e7eb",
109
- domainColor: "#d1d5db",
110
- tickColor: "#d1d5db"
111
- },
112
- view: { stroke: "transparent" },
113
- legend: { labelColor: "#444", titleColor: "#111" }
189
+ MAX_INLINE_CHART_HEIGHT = 800;
190
+ LIGHT_THEME = {
191
+ text: "#444",
192
+ title: "#111",
193
+ grid: "#e5e7eb",
194
+ border: "#d1d5db"
114
195
  };
115
- DARK_CONFIG = {
116
- background: "transparent",
117
- axis: {
118
- labelColor: "#cbd5e1",
119
- titleColor: "#f1f5f9",
120
- gridColor: "#374151",
121
- domainColor: "#4b5563",
122
- tickColor: "#4b5563"
123
- },
124
- view: { stroke: "transparent" },
125
- legend: { labelColor: "#cbd5e1", titleColor: "#f1f5f9" }
196
+ DARK_THEME = {
197
+ text: "#cbd5e1",
198
+ title: "#f1f5f9",
199
+ grid: "#374151",
200
+ border: "#4b5563"
126
201
  };
127
202
  }
128
203
  });
@@ -137,9 +212,9 @@ function useAgentChatAdapterContext() {
137
212
  }
138
213
  return value;
139
214
  }
140
- function cn(...inputs) {
141
- return tailwindMerge.twMerge(clsx.clsx(inputs));
142
- }
215
+
216
+ // src/components/ui/button.tsx
217
+ init_utils();
143
218
  var buttonVariants = classVarianceAuthority.cva(
144
219
  "group/button inline-flex shrink-0 items-center justify-center rounded-none border border-transparent bg-clip-padding text-xs font-semibold tracking-widest whitespace-nowrap uppercase transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/30 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5",
145
220
  {
@@ -188,10 +263,13 @@ function Button({
188
263
  }
189
264
  );
190
265
  }
266
+
267
+ // src/components/ai-elements/conversation.tsx
268
+ init_utils();
191
269
  var Conversation = ({ className, ...props }) => /* @__PURE__ */ jsxRuntime.jsx(
192
270
  useStickToBottom.StickToBottom,
193
271
  {
194
- className: cn("relative flex-1 overflow-y-auto", className),
272
+ className: cn("relative flex-1", className),
195
273
  initial: "smooth",
196
274
  resize: "smooth",
197
275
  role: "log",
@@ -256,6 +334,9 @@ var ConversationScrollButton = ({
256
334
  }
257
335
  );
258
336
  };
337
+
338
+ // src/components/ui/tooltip.tsx
339
+ init_utils();
259
340
  function TooltipProvider({
260
341
  delayDuration = 0,
261
342
  ...props
@@ -302,6 +383,9 @@ function TooltipContent({
302
383
  }
303
384
  ) });
304
385
  }
386
+
387
+ // src/components/ai-elements/message.tsx
388
+ init_utils();
305
389
  var Message = ({ className, from, ...props }) => /* @__PURE__ */ jsxRuntime.jsx(
306
390
  "div",
307
391
  {
@@ -376,6 +460,12 @@ function CollapsibleContent({
376
460
  }
377
461
  );
378
462
  }
463
+
464
+ // src/components/ai-elements/reasoning.tsx
465
+ init_utils();
466
+
467
+ // src/components/ai-elements/shimmer.tsx
468
+ init_utils();
379
469
  var ShimmerComponent = ({
380
470
  children,
381
471
  as: Component = "p",
@@ -552,6 +642,9 @@ var ReasoningContent = react.memo(
552
642
  Reasoning.displayName = "Reasoning";
553
643
  ReasoningTrigger.displayName = "ReasoningTrigger";
554
644
  ReasoningContent.displayName = "ReasoningContent";
645
+
646
+ // src/components/reui/timeline.tsx
647
+ init_utils();
555
648
  var TimelineContext = react.createContext(
556
649
  void 0
557
650
  );
@@ -674,6 +767,62 @@ function TimelineSeparator({
674
767
  }
675
768
  );
676
769
  }
770
+ init_utils();
771
+
772
+ // src/components/chat/tools/shared.ts
773
+ function statusColorClass(status) {
774
+ if (status === "running") return "bg-primary animate-pulse";
775
+ if (status === "error") return "bg-red-500";
776
+ if (status === "cancelled") return "bg-muted-foreground/40";
777
+ return "bg-emerald-500";
778
+ }
779
+ function effectiveStatus(tc) {
780
+ if (tc.cancelled) return "cancelled";
781
+ if (tc.status !== "complete" || !tc.result) return tc.status;
782
+ try {
783
+ const parsed = JSON.parse(tc.result);
784
+ if (parsed?.exit_code !== void 0 && parsed.exit_code !== 0) return "error";
785
+ if (parsed?.error) return "error";
786
+ if (parsed?.status === "blocked" || parsed?.status === "error") return "error";
787
+ if (parsed?.success === false) return "error";
788
+ } catch {
789
+ }
790
+ return "complete";
791
+ }
792
+ function formatArgs(args) {
793
+ try {
794
+ return JSON.stringify(JSON.parse(args), null, 2);
795
+ } catch {
796
+ return args;
797
+ }
798
+ }
799
+ function parseArgs(args) {
800
+ try {
801
+ return JSON.parse(args);
802
+ } catch {
803
+ return null;
804
+ }
805
+ }
806
+ function truncate(s, max) {
807
+ return s.length > max ? s.slice(0, max) + "\n... (truncated)" : s;
808
+ }
809
+ function toolErrorSummary(result, max = 180) {
810
+ if (!result) return "";
811
+ try {
812
+ const parsed = JSON.parse(result);
813
+ const error = typeof parsed?.error === "string" ? parsed.error : "";
814
+ if (error === "sandbox_unavailable") {
815
+ return "Sandbox is unavailable. Workspace commands cannot run right now.";
816
+ }
817
+ const reason = typeof parsed?.reason === "string" ? parsed.reason : "";
818
+ const message = typeof parsed?.message === "string" ? parsed.message : "";
819
+ const detail = reason || message;
820
+ const summary = error && detail && detail !== error ? `${error}: ${detail}` : error || detail;
821
+ return summary ? summary.replace(/\s+/g, " ").slice(0, max) : "";
822
+ } catch {
823
+ return "";
824
+ }
825
+ }
677
826
  function CopyButton({ text }) {
678
827
  const [copied, setCopied] = react.useState(false);
679
828
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -709,7 +858,8 @@ function parseTerminalResult(result, args) {
709
858
  const hasOutput = typeof parsed?.output === "string";
710
859
  const hasStdout = typeof parsed?.stdout === "string";
711
860
  const hasExitCode = typeof parsed?.exit_code === "number";
712
- if (!hasOutput && !hasStdout && !hasExitCode) {
861
+ const errorSummary = toolErrorSummary(result, 400);
862
+ if (!hasOutput && !hasStdout && !hasExitCode && !errorSummary) {
713
863
  return null;
714
864
  }
715
865
  let command = "";
@@ -719,20 +869,20 @@ function parseTerminalResult(result, args) {
719
869
  } catch {
720
870
  }
721
871
  const output = parsed.output ?? parsed.stdout ?? "";
722
- const stderr = parsed.stderr ?? parsed.error ?? "";
872
+ const stderr = parsed.stderr ?? errorSummary;
723
873
  const combined = stderr ? `${output}
724
874
  ${stderr}`.trim() : output;
725
875
  return {
726
876
  output: combined,
727
- exit_code: parsed.exit_code ?? 0,
728
- error: parsed.error ?? null,
877
+ exit_code: parsed.exit_code ?? (errorSummary ? 1 : 0),
878
+ error: errorSummary || null,
729
879
  command
730
880
  };
731
881
  } catch {
732
882
  return null;
733
883
  }
734
884
  }
735
- function TerminalCollapsible({ command, output, isRunning }) {
885
+ function TerminalCollapsible({ command, output, isRunning, hasError }) {
736
886
  const [isOpen, setIsOpen] = react.useState(false);
737
887
  return /* @__PURE__ */ jsxRuntime.jsxs(
738
888
  Collapsible,
@@ -742,7 +892,7 @@ function TerminalCollapsible({ command, output, isRunning }) {
742
892
  className: "not-prose w-full",
743
893
  children: [
744
894
  /* @__PURE__ */ jsxRuntime.jsxs(CollapsibleTrigger, { className: "group/trigger flex w-fit items-center gap-2 text-sm transition-colors", children: [
745
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-left", children: isRunning ? /* @__PURE__ */ jsxRuntime.jsx(Shimmer, { as: "span", duration: 1, children: "Running command..." }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-foreground", children: "Command result" }) }),
895
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-left", children: isRunning ? /* @__PURE__ */ jsxRuntime.jsx(Shimmer, { as: "span", duration: 1, children: "Running command..." }) : hasError ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-destructive", children: "Command failed" }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-foreground", children: "Command result" }) }),
746
896
  /* @__PURE__ */ jsxRuntime.jsx(
747
897
  lucideReact.ChevronDownIcon,
748
898
  {
@@ -799,7 +949,8 @@ function TerminalToolBlock({ tc }) {
799
949
  {
800
950
  command: result.command,
801
951
  output,
802
- isRunning
952
+ isRunning,
953
+ hasError: result.exit_code !== 0 || Boolean(result.error)
803
954
  }
804
955
  );
805
956
  }
@@ -814,6 +965,9 @@ function TerminalToolBlock({ tc }) {
814
965
  }
815
966
  return null;
816
967
  }
968
+
969
+ // src/components/ui/scroll-area.tsx
970
+ init_utils();
817
971
  function ScrollArea({
818
972
  className,
819
973
  children,
@@ -866,6 +1020,9 @@ function ScrollBar({
866
1020
  }
867
1021
  );
868
1022
  }
1023
+
1024
+ // src/components/ai-elements/queue.tsx
1025
+ init_utils();
869
1026
  var QueueItem = ({ className, ...props }) => /* @__PURE__ */ jsxRuntime.jsx(
870
1027
  "li",
871
1028
  {
@@ -1018,6 +1175,9 @@ function TodoToolBlock({ tc }) {
1018
1175
  }) }) })
1019
1176
  ] }) });
1020
1177
  }
1178
+
1179
+ // src/components/ai-elements/code-block.tsx
1180
+ init_utils();
1021
1181
  var isItalic = (fontStyle) => fontStyle && fontStyle & 1;
1022
1182
  var isBold = (fontStyle) => fontStyle && fontStyle & 2;
1023
1183
  var isUnderline = (fontStyle) => (
@@ -1307,6 +1467,9 @@ var CodeBlockCopyButton = ({
1307
1467
  }
1308
1468
  );
1309
1469
  };
1470
+
1471
+ // src/components/ui/tabs.tsx
1472
+ init_utils();
1310
1473
  function Tabs({
1311
1474
  className,
1312
1475
  orientation = "horizontal",
@@ -1386,6 +1549,12 @@ function TabsContent({
1386
1549
  }
1387
1550
  );
1388
1551
  }
1552
+
1553
+ // src/components/ai-elements/sandbox.tsx
1554
+ init_utils();
1555
+
1556
+ // src/components/ui/badge.tsx
1557
+ init_utils();
1389
1558
  var badgeVariants = classVarianceAuthority.cva(
1390
1559
  "group/badge inline-flex w-fit shrink-0 items-center justify-center gap-1.5 overflow-hidden rounded-none border-0 bg-transparent px-0 py-0 text-[0.625rem] font-semibold tracking-widest whitespace-nowrap uppercase transition-colors focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-0 has-data-[icon=inline-start]:pl-0 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!",
1391
1560
  {
@@ -1643,6 +1812,9 @@ function WebToolBlock({ tc }) {
1643
1812
  displayText && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/70 truncate text-xs", children: displayText })
1644
1813
  ] });
1645
1814
  }
1815
+
1816
+ // src/components/ui/dialog.tsx
1817
+ init_utils();
1646
1818
  function Dialog({
1647
1819
  ...props
1648
1820
  }) {
@@ -1772,6 +1944,12 @@ function DialogDescription({
1772
1944
  }
1773
1945
  );
1774
1946
  }
1947
+
1948
+ // src/components/chat/tools/file-tools.tsx
1949
+ init_utils();
1950
+
1951
+ // src/components/chat/diff-viewer.tsx
1952
+ init_utils();
1775
1953
  function splitChangeLines(change) {
1776
1954
  const v = change.value;
1777
1955
  if (v === "") return [];
@@ -2250,6 +2428,9 @@ function ListFilesBlock({ tc }) {
2250
2428
  ] })
2251
2429
  ] });
2252
2430
  }
2431
+
2432
+ // src/components/ai-elements/terminal.tsx
2433
+ init_utils();
2253
2434
  var TerminalContext = react.createContext({
2254
2435
  autoScroll: true,
2255
2436
  isStreaming: false,
@@ -2444,7 +2625,10 @@ var Terminal = ({
2444
2625
  }
2445
2626
  ) });
2446
2627
  };
2447
- function parseArgs(args) {
2628
+
2629
+ // src/components/chat/tools/process-tool.tsx
2630
+ init_utils();
2631
+ function parseArgs2(args) {
2448
2632
  try {
2449
2633
  return JSON.parse(args);
2450
2634
  } catch {
@@ -2668,7 +2852,7 @@ function OutputPreview({ output }) {
2668
2852
  }
2669
2853
  function ProcessToolBlock({ tc }) {
2670
2854
  const isRunning = tc.status === "running";
2671
- const args = parseArgs(tc.args);
2855
+ const args = parseArgs2(tc.args);
2672
2856
  const result = parseResult(tc.result);
2673
2857
  const action = args.action || "unknown";
2674
2858
  const actionLabel = {
@@ -2738,6 +2922,12 @@ function ProcessToolBlock({ tc }) {
2738
2922
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-background pb-1", children: content })
2739
2923
  ] });
2740
2924
  }
2925
+
2926
+ // src/components/chat/tools/expert-tool.tsx
2927
+ init_utils();
2928
+
2929
+ // src/components/ui/textarea.tsx
2930
+ init_utils();
2741
2931
  function Textarea({ className, ...props }) {
2742
2932
  return /* @__PURE__ */ jsxRuntime.jsx(
2743
2933
  "textarea",
@@ -2751,44 +2941,6 @@ function Textarea({ className, ...props }) {
2751
2941
  }
2752
2942
  );
2753
2943
  }
2754
-
2755
- // src/components/chat/tools/shared.ts
2756
- function statusColorClass(status) {
2757
- if (status === "running") return "bg-primary animate-pulse";
2758
- if (status === "error") return "bg-red-500";
2759
- if (status === "cancelled") return "bg-muted-foreground/40";
2760
- return "bg-emerald-500";
2761
- }
2762
- function effectiveStatus(tc) {
2763
- if (tc.cancelled) return "cancelled";
2764
- if (tc.status !== "complete" || !tc.result) return tc.status;
2765
- try {
2766
- const parsed = JSON.parse(tc.result);
2767
- if (parsed?.exit_code !== void 0 && parsed.exit_code !== 0) return "error";
2768
- if (parsed?.error) return "error";
2769
- if (parsed?.status === "blocked" || parsed?.status === "error") return "error";
2770
- if (parsed?.success === false) return "error";
2771
- } catch {
2772
- }
2773
- return "complete";
2774
- }
2775
- function formatArgs(args) {
2776
- try {
2777
- return JSON.stringify(JSON.parse(args), null, 2);
2778
- } catch {
2779
- return args;
2780
- }
2781
- }
2782
- function parseArgs2(args) {
2783
- try {
2784
- return JSON.parse(args);
2785
- } catch {
2786
- return null;
2787
- }
2788
- }
2789
- function truncate(s, max) {
2790
- return s.length > max ? s.slice(0, max) + "\n... (truncated)" : s;
2791
- }
2792
2944
  var MAX_REASON_LENGTH = 500;
2793
2945
  function ExpertToolBlock({ tc }) {
2794
2946
  const [expanded, setExpanded] = react.useState(false);
@@ -2824,7 +2976,7 @@ function ExpertToolBlock({ tc }) {
2824
2976
  }
2825
2977
  void submit("up");
2826
2978
  };
2827
- const args = parseArgs2(tc.args) ?? {};
2979
+ const args = parseArgs(tc.args) ?? {};
2828
2980
  const expertName = args.expert ?? null;
2829
2981
  const question = args.question ?? args.prompt ?? "";
2830
2982
  const result = parseExpertResult(tc.result);
@@ -2936,7 +3088,7 @@ function ExpertToolBlock({ tc }) {
2936
3088
  }
2937
3089
  function parseExpertResult(result) {
2938
3090
  if (!result) return null;
2939
- const parsed = parseArgs2(result);
3091
+ const parsed = parseArgs(result);
2940
3092
  if (parsed) {
2941
3093
  return {
2942
3094
  ...parsed,
@@ -3043,11 +3195,11 @@ function ReasonForm({
3043
3195
  ] });
3044
3196
  }
3045
3197
  function SkillsListBlock({ tc }) {
3046
- const args = parseArgs2(tc.args) ?? {};
3198
+ const args = parseArgs(tc.args) ?? {};
3047
3199
  const filter = args.category ? `category: ${args.category}` : "all";
3048
3200
  let summary = "";
3049
3201
  if (tc.result) {
3050
- const parsed = parseArgs2(tc.result);
3202
+ const parsed = parseArgs(tc.result);
3051
3203
  if (parsed?.count !== void 0) {
3052
3204
  summary = `${parsed.count} skill${parsed.count === 1 ? "" : "s"}`;
3053
3205
  }
@@ -3062,11 +3214,11 @@ function SkillsListBlock({ tc }) {
3062
3214
  ] });
3063
3215
  }
3064
3216
  function SkillViewBlock({ tc }) {
3065
- const args = parseArgs2(tc.args) ?? {};
3217
+ const args = parseArgs(tc.args) ?? {};
3066
3218
  const target = args.file_path ? `${args.name ?? "?"}/${args.file_path}` : args.name ?? "?";
3067
3219
  let summary = "";
3068
3220
  if (tc.result) {
3069
- const parsed = parseArgs2(tc.result);
3221
+ const parsed = parseArgs(tc.result);
3070
3222
  if (parsed?.staged_at) {
3071
3223
  summary = `staged at ${parsed.staged_at}`;
3072
3224
  } else if (parsed?.token_estimate) {
@@ -3082,6 +3234,12 @@ function SkillViewBlock({ tc }) {
3082
3234
  ] })
3083
3235
  ] });
3084
3236
  }
3237
+
3238
+ // src/components/chat/tools/clarify-tool.tsx
3239
+ init_utils();
3240
+
3241
+ // src/components/ui/input.tsx
3242
+ init_utils();
3085
3243
  function Input({ className, type, ...props }) {
3086
3244
  return /* @__PURE__ */ jsxRuntime.jsx(
3087
3245
  "input",
@@ -3113,7 +3271,7 @@ function buildAnswer(q, sel) {
3113
3271
  }
3114
3272
  function ClarifyToolBlock({ tc }) {
3115
3273
  const { adapter, sessionId } = useAgentChatAdapterContext();
3116
- const args = react.useMemo(() => parseArgs2(tc.args), [tc.args]);
3274
+ const args = react.useMemo(() => parseArgs(tc.args), [tc.args]);
3117
3275
  const questions = args?.questions ?? [];
3118
3276
  const [active, setActive] = react.useState(0);
3119
3277
  const [selections, setSelections] = react.useState(
@@ -3395,7 +3553,7 @@ function ClarifyLocked({
3395
3553
  ] });
3396
3554
  }
3397
3555
  function ArtifactToolBlock({ tc }) {
3398
- const args = parseArgs2(tc.args) ?? {};
3556
+ const args = parseArgs(tc.args) ?? {};
3399
3557
  const status = effectiveStatus(tc);
3400
3558
  const label = status === "running" ? "Creating artifact\u2026" : status === "error" ? "Tried to create" : "Created";
3401
3559
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 text-sm ", children: [
@@ -3403,13 +3561,16 @@ function ArtifactToolBlock({ tc }) {
3403
3561
  args.name && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground truncate", children: args.name })
3404
3562
  ] });
3405
3563
  }
3564
+
3565
+ // src/components/chat/tools/delegate-tool.tsx
3566
+ init_utils();
3406
3567
  function firstLine(s) {
3407
3568
  const idx = s.indexOf("\n");
3408
3569
  return idx === -1 ? s : s.slice(0, idx);
3409
3570
  }
3410
3571
  function DelegateToolBlock({ tc }) {
3411
3572
  const [expanded, setExpanded] = react.useState(false);
3412
- const args = parseArgs2(tc.args);
3573
+ const args = parseArgs(tc.args);
3413
3574
  const goal = args?.goal ?? "";
3414
3575
  const context = args?.context ?? "";
3415
3576
  const agentType = args?.agent_type;
@@ -3503,6 +3664,7 @@ function DelegateToolBlock({ tc }) {
3503
3664
  ] })
3504
3665
  ] });
3505
3666
  }
3667
+ init_utils();
3506
3668
  var ACTION_VERB = {
3507
3669
  add: "Saved",
3508
3670
  replace: "Updated",
@@ -3519,8 +3681,8 @@ var TARGET_LABEL = {
3519
3681
  };
3520
3682
  function MemoryToolBlock({ tc }) {
3521
3683
  const [isOpen, setIsOpen] = react.useState(false);
3522
- const args = parseArgs2(tc.args) ?? {};
3523
- const result = tc.result ? parseArgs2(tc.result) : null;
3684
+ const args = parseArgs(tc.args) ?? {};
3685
+ const result = tc.result ? parseArgs(tc.result) : null;
3524
3686
  const action = args.action ?? "add";
3525
3687
  const target = args.target ?? "memory";
3526
3688
  const verb = ACTION_VERB[action] ?? action;
@@ -3598,8 +3760,8 @@ var ACTION_LABEL = {
3598
3760
  remove_file: "Remove skill file"
3599
3761
  };
3600
3762
  function SkillManageToolBlock({ tc }) {
3601
- const args = parseArgs2(tc.args) ?? {};
3602
- const result = tc.result ? parseArgs2(tc.result) : null;
3763
+ const args = parseArgs(tc.args) ?? {};
3764
+ const result = tc.result ? parseArgs(tc.result) : null;
3603
3765
  const action = args.action ?? "manage";
3604
3766
  const label = ACTION_LABEL[action] ?? "Manage skill";
3605
3767
  const target = args.file_path ? `${args.name ?? "?"}/${args.file_path}` : args.name ?? "?";
@@ -3620,8 +3782,8 @@ function firstLine2(value) {
3620
3782
  return (index === -1 ? value : value.slice(0, index)).trim();
3621
3783
  }
3622
3784
  function CoordinatorToolBlock({ tc }) {
3623
- const args = parseArgs2(tc.args) ?? {};
3624
- const result = tc.result ? parseArgs2(tc.result) : null;
3785
+ const args = parseArgs(tc.args) ?? {};
3786
+ const result = tc.result ? parseArgs(tc.result) : null;
3625
3787
  const failed = Boolean(result?.error);
3626
3788
  let label = "Worker";
3627
3789
  let target = "";
@@ -3654,6 +3816,9 @@ function CoordinatorToolBlock({ tc }) {
3654
3816
  ] })
3655
3817
  ] });
3656
3818
  }
3819
+
3820
+ // src/components/chat/tools/default-tool.tsx
3821
+ init_utils();
3657
3822
  var TOOL_LABELS = {
3658
3823
  kb_list_pages: "Knowledge Base",
3659
3824
  kb_read_page: "Read KB Page",
@@ -3808,6 +3973,9 @@ function AssistantMessage({
3808
3973
  ] })
3809
3974
  ] }) });
3810
3975
  }
3976
+
3977
+ // src/components/ui/hover-card.tsx
3978
+ init_utils();
3811
3979
  function HoverCard({
3812
3980
  ...props
3813
3981
  }) {
@@ -3838,6 +4006,9 @@ function HoverCardContent({
3838
4006
  }
3839
4007
  ) });
3840
4008
  }
4009
+
4010
+ // src/components/ui/progress.tsx
4011
+ init_utils();
3841
4012
  function Progress({
3842
4013
  className,
3843
4014
  value,
@@ -3863,6 +4034,9 @@ function Progress({
3863
4034
  }
3864
4035
  );
3865
4036
  }
4037
+
4038
+ // src/components/ai-elements/context.tsx
4039
+ init_utils();
3866
4040
  var PERCENT_MAX = 100;
3867
4041
  var ICON_RADIUS = 10;
3868
4042
  var ICON_VIEWBOX = 24;
@@ -4127,6 +4301,12 @@ var ContextCacheUsage = ({
4127
4301
  }
4128
4302
  );
4129
4303
  };
4304
+
4305
+ // src/components/ui/command.tsx
4306
+ init_utils();
4307
+
4308
+ // src/components/ui/input-group.tsx
4309
+ init_utils();
4130
4310
  function InputGroup({ className, ...props }) {
4131
4311
  return /* @__PURE__ */ jsxRuntime.jsx(
4132
4312
  "div",
@@ -4311,6 +4491,9 @@ function CommandItem({
4311
4491
  }
4312
4492
  );
4313
4493
  }
4494
+
4495
+ // src/components/ui/dropdown-menu.tsx
4496
+ init_utils();
4314
4497
  function DropdownMenu({
4315
4498
  ...props
4316
4499
  }) {
@@ -4364,9 +4547,15 @@ function DropdownMenuItem({
4364
4547
  }
4365
4548
  );
4366
4549
  }
4550
+
4551
+ // src/components/ui/spinner.tsx
4552
+ init_utils();
4367
4553
  function Spinner({ className, ...props }) {
4368
4554
  return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2Icon, { role: "status", "aria-label": "Loading", className: cn("size-4 animate-spin", className), ...props });
4369
4555
  }
4556
+
4557
+ // src/components/ai-elements/prompt-input.tsx
4558
+ init_utils();
4370
4559
  var convertBlobUrlToDataUrl = async (url) => {
4371
4560
  try {
4372
4561
  const response = await fetch(url);
@@ -5064,6 +5253,9 @@ var PromptInputSubmit = ({
5064
5253
  }
5065
5254
  );
5066
5255
  };
5256
+
5257
+ // src/components/ui/popover.tsx
5258
+ init_utils();
5067
5259
  function Popover({
5068
5260
  ...props
5069
5261
  }) {
@@ -5311,6 +5503,9 @@ function ChatComposerInner({
5311
5503
  )
5312
5504
  ] });
5313
5505
  }
5506
+
5507
+ // src/components/ai-elements/artifact.tsx
5508
+ init_utils();
5314
5509
  var Artifact = ({ className, ...props }) => /* @__PURE__ */ jsxRuntime.jsx(
5315
5510
  "div",
5316
5511
  {
@@ -5431,6 +5626,9 @@ function ArtifactTable({ spec }) {
5431
5626
  )) })
5432
5627
  ] }) });
5433
5628
  }
5629
+
5630
+ // src/components/chat/artifacts/artifact-html.tsx
5631
+ init_utils();
5434
5632
  var DEFAULT_HEIGHT_PX = 480;
5435
5633
  var EXPANDED_HEIGHT_PX = 800;
5436
5634
  var MIN_ZOOM = 0.5;
@@ -5572,7 +5770,7 @@ function exportArtifact(payload) {
5572
5770
  };
5573
5771
  case "chart":
5574
5772
  return {
5575
- text: JSON.stringify(payload.spec.vega_lite, null, 2),
5773
+ text: JSON.stringify(payload.spec.chart_js, null, 2),
5576
5774
  mime: "application/json",
5577
5775
  extension: "json"
5578
5776
  };
@@ -5758,7 +5956,7 @@ function ArtifactBody({
5758
5956
  react.Suspense,
5759
5957
  {
5760
5958
  fallback: /* @__PURE__ */ jsxRuntime.jsx(Shimmer, { duration: 5, className: "text-sm text-muted-foreground", children: "Loading chart\u2026" }),
5761
- children: /* @__PURE__ */ jsxRuntime.jsx(ArtifactChart2, { spec: payload.spec })
5959
+ children: /* @__PURE__ */ jsxRuntime.jsx(ArtifactChart2, { spec: payload.spec, fill })
5762
5960
  }
5763
5961
  );
5764
5962
  case "html":
@@ -5767,6 +5965,7 @@ function ArtifactBody({
5767
5965
  return /* @__PURE__ */ jsxRuntime.jsx(ArtifactSvg, { spec: payload.spec });
5768
5966
  }
5769
5967
  }
5968
+ init_utils();
5770
5969
  var ErrorMessage = react.memo(function ErrorMessage2({
5771
5970
  errorInfo,
5772
5971
  onRetry,
@@ -5847,6 +6046,9 @@ var ErrorMessage = react.memo(function ErrorMessage2({
5847
6046
  }
5848
6047
  );
5849
6048
  });
6049
+
6050
+ // src/components/chat/chat-thread.tsx
6051
+ init_utils();
5850
6052
  function messageToEntries(msg, isLast) {
5851
6053
  if (msg.role === "system") {
5852
6054
  if (msg.systemKind === "skill_invoked") {
@@ -5978,7 +6180,7 @@ function OrphanSystemMarker({
5978
6180
  );
5979
6181
  }
5980
6182
  if (message.systemKind === "error" && message.errorInfo) {
5981
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-auto my-2 w-full max-w-4xl px-4", children: /* @__PURE__ */ jsxRuntime.jsx(ErrorMessage, { errorInfo: message.errorInfo, onRetry }) });
6183
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-auto my-2 w-full max-w-4xl", children: /* @__PURE__ */ jsxRuntime.jsx(ErrorMessage, { errorInfo: message.errorInfo, onRetry }) });
5982
6184
  }
5983
6185
  return null;
5984
6186
  }
@@ -6032,6 +6234,7 @@ function TimelineEntryItem({
6032
6234
  ] });
6033
6235
  }
6034
6236
  if (entry.kind === "tool") {
6237
+ const failureSummary = effectiveStatus(entry.tc) === "error" ? toolErrorSummary(entry.tc.result) : "";
6035
6238
  return /* @__PURE__ */ jsxRuntime.jsxs(TimelineItem, { step, children: [
6036
6239
  /* @__PURE__ */ jsxRuntime.jsxs(TimelineHeader, { children: [
6037
6240
  /* @__PURE__ */ jsxRuntime.jsx(TimelineSeparator, { style: { backgroundColor: "var(--color-border)" } }),
@@ -6042,7 +6245,10 @@ function TimelineEntryItem({
6042
6245
  }
6043
6246
  )
6044
6247
  ] }),
6045
- /* @__PURE__ */ jsxRuntime.jsx(TimelineContent, { children: entry.tc.cancelled ? /* @__PURE__ */ jsxRuntime.jsx(CancelledToolRow, { tc: entry.tc }) : /* @__PURE__ */ jsxRuntime.jsx(ToolCallBlock, { tc: entry.tc, onFileSelect }) })
6248
+ /* @__PURE__ */ jsxRuntime.jsx(TimelineContent, { children: entry.tc.cancelled ? /* @__PURE__ */ jsxRuntime.jsx(CancelledToolRow, { tc: entry.tc }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
6249
+ /* @__PURE__ */ jsxRuntime.jsx(ToolCallBlock, { tc: entry.tc, onFileSelect }),
6250
+ failureSummary ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-full truncate text-xs text-destructive", title: failureSummary, children: failureSummary }) : null
6251
+ ] }) })
6046
6252
  ] });
6047
6253
  }
6048
6254
  if (entry.kind === "text") {
@@ -6160,6 +6366,7 @@ function ChatThread({
6160
6366
  sessionId,
6161
6367
  messages,
6162
6368
  isRunning,
6369
+ isLoadingHistory = false,
6163
6370
  onSend,
6164
6371
  onStop,
6165
6372
  onFileSelect,
@@ -6182,7 +6389,14 @@ function ChatThread({
6182
6389
  }, [messages]);
6183
6390
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col overflow-hidden bg-background text-sm", children: [
6184
6391
  /* @__PURE__ */ jsxRuntime.jsxs(Conversation, { className: "relative flex-1 min-h-0", children: [
6185
- /* @__PURE__ */ jsxRuntime.jsx(ConversationContent, { className: "mx-auto w-full max-w-3xl", children: messages.length === 0 && !disabled ? /* @__PURE__ */ jsxRuntime.jsx(
6392
+ /* @__PURE__ */ jsxRuntime.jsx(ConversationContent, { className: "mx-auto w-full max-w-4xl", children: messages.length === 0 && isLoadingHistory ? /* @__PURE__ */ jsxRuntime.jsx(
6393
+ ConversationEmptyState,
6394
+ {
6395
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageSquareIcon, { className: "size-8 opacity-40" }),
6396
+ title: "Loading conversation",
6397
+ description: "Fetching the session history."
6398
+ }
6399
+ ) : messages.length === 0 && !disabled ? /* @__PURE__ */ jsxRuntime.jsx(
6186
6400
  ConversationEmptyState,
6187
6401
  {
6188
6402
  icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageSquareIcon, { className: "size-8 opacity-40" }),
@@ -6235,7 +6449,7 @@ function ChatThread({
6235
6449
  ] }) }),
6236
6450
  /* @__PURE__ */ jsxRuntime.jsx(ConversationScrollButton, {})
6237
6451
  ] }),
6238
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto w-full max-w-3xl px-6 pb-5 pt-3", children: [
6452
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto w-full max-w-4xl px-6 pb-5 pt-3", children: [
6239
6453
  retryIndicator && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-2", children: /* @__PURE__ */ jsxRuntime.jsx(RetryBanner, { indicator: retryIndicator }) }),
6240
6454
  /* @__PURE__ */ jsxRuntime.jsx(
6241
6455
  ChatComposer,
@@ -6250,6 +6464,9 @@ function ChatThread({
6250
6464
  ] })
6251
6465
  ] });
6252
6466
  }
6467
+
6468
+ // src/components/ai-elements/file-tree.tsx
6469
+ init_utils();
6253
6470
  var noop = () => {
6254
6471
  };
6255
6472
  var FileTreeContext = react.createContext({
@@ -6416,6 +6633,9 @@ function FileTreeFile({
6416
6633
  }
6417
6634
  );
6418
6635
  }
6636
+
6637
+ // src/components/ui/skeleton.tsx
6638
+ init_utils();
6419
6639
  function Skeleton({ className, ...props }) {
6420
6640
  return /* @__PURE__ */ jsxRuntime.jsx(
6421
6641
  "div",
@@ -6426,6 +6646,9 @@ function Skeleton({ className, ...props }) {
6426
6646
  );
6427
6647
  }
6428
6648
 
6649
+ // src/components/workspace/workspace-panel.tsx
6650
+ init_utils();
6651
+
6429
6652
  // src/lib/format.ts
6430
6653
  function formatFileSize(bytes) {
6431
6654
  if (bytes < 1024) return `${bytes} B`;
@@ -6465,17 +6688,24 @@ function getLanguageHint(path) {
6465
6688
  return map[ext] ?? "plaintext";
6466
6689
  }
6467
6690
  var SKELETON_WIDTHS = [70, 85, 55, 90, 60, 78, 45, 82, 65, 72];
6691
+ var PDF_WORKER_SRC = new URL(
6692
+ "pdfjs-dist/legacy/build/pdf.worker.mjs",
6693
+ (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))
6694
+ ).toString();
6468
6695
  function FileViewer({
6469
6696
  file,
6470
6697
  loading,
6471
6698
  error,
6699
+ downloadUrl,
6700
+ onDelete,
6472
6701
  onClose
6473
6702
  }) {
6474
6703
  const visible = loading || file !== null || error !== null;
6475
6704
  if (!visible) return null;
6476
6705
  const fileName2 = file?.path.split("/").pop() ?? "File";
6477
6706
  const lang = file ? getLanguageHint(file.path) : "";
6478
- const isImage = file?.encoding === "base64";
6707
+ const isPdf = file?.mime_type === "application/pdf" || file?.path.toLowerCase().endsWith(".pdf") || false;
6708
+ const isImage = file?.encoding === "base64" && !isPdf;
6479
6709
  const HeaderIcon = isImage ? lucideReact.ImageIcon : lucideReact.FileTextIcon;
6480
6710
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-0 flex-1 flex-col border-t border-line", children: [
6481
6711
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 items-center gap-2 border-b border-line px-3 py-2", children: [
@@ -6490,6 +6720,28 @@ function FileViewer({
6490
6720
  ] })
6491
6721
  ] })
6492
6722
  ] }),
6723
+ downloadUrl && /* @__PURE__ */ jsxRuntime.jsx(
6724
+ "a",
6725
+ {
6726
+ href: downloadUrl,
6727
+ download: fileName2,
6728
+ className: "shrink-0 rounded p-0.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
6729
+ "aria-label": `Download ${fileName2}`,
6730
+ title: "Download",
6731
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.DownloadIcon, { className: "size-3.5" })
6732
+ }
6733
+ ),
6734
+ onDelete && /* @__PURE__ */ jsxRuntime.jsx(
6735
+ "button",
6736
+ {
6737
+ type: "button",
6738
+ onClick: onDelete,
6739
+ className: "shrink-0 rounded p-0.5 text-muted-foreground transition-colors hover:bg-destructive/10 hover:text-destructive",
6740
+ "aria-label": `Delete ${fileName2}`,
6741
+ title: "Delete",
6742
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.TrashIcon, { className: "size-3.5" })
6743
+ }
6744
+ ),
6493
6745
  /* @__PURE__ */ jsxRuntime.jsx(
6494
6746
  "button",
6495
6747
  {
@@ -6516,6 +6768,8 @@ function FileViewer({
6516
6768
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircleIcon, { className: "mt-0.5 size-3.5 shrink-0" }),
6517
6769
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: error })
6518
6770
  ] }),
6771
+ file && isPdf && file.encoding !== "base64" && /* @__PURE__ */ jsxRuntime.jsx(FileViewerError, { message: "PDF preview requires base64 file content." }),
6772
+ file && isPdf && file.encoding === "base64" && /* @__PURE__ */ jsxRuntime.jsx(PdfPreview, { file, fileName: fileName2 }),
6519
6773
  file && isImage && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center p-4", children: /* @__PURE__ */ jsxRuntime.jsx(
6520
6774
  "img",
6521
6775
  {
@@ -6524,66 +6778,290 @@ function FileViewer({
6524
6778
  className: "max-h-[60vh] max-w-full rounded object-contain"
6525
6779
  }
6526
6780
  ) }),
6527
- file && !isImage && /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "wrap-break-word whitespace-pre-wrap p-3 text-[11px] leading-relaxed text-foreground", children: /* @__PURE__ */ jsxRuntime.jsx("code", { "data-language": lang, children: file.content }) })
6781
+ file && !isImage && !isPdf && /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "wrap-break-word whitespace-pre-wrap p-3 text-[11px] leading-relaxed text-foreground", children: /* @__PURE__ */ jsxRuntime.jsx("code", { "data-language": lang, children: file.content }) })
6528
6782
  ] })
6529
6783
  ] });
6530
6784
  }
6531
- var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
6532
- ".py",
6533
- ".js",
6534
- ".ts",
6535
- ".tsx",
6536
- ".jsx",
6537
- ".json",
6538
- ".md",
6539
- ".yaml",
6540
- ".yml",
6541
- ".toml",
6542
- ".sql",
6543
- ".sh",
6544
- ".css",
6545
- ".html",
6546
- ".txt",
6547
- ".csv",
6548
- ".rst",
6549
- ".cfg",
6550
- ".ini",
6551
- ".env",
6552
- ".rs",
6553
- ".go",
6554
- ".java",
6555
- ".rb",
6556
- ".php",
6557
- ".c",
6558
- ".cpp",
6559
- ".h",
6560
- ".lock",
6561
- ".gitignore",
6562
- ".dockerignore",
6563
- ".editorconfig"
6564
- ]);
6565
- var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
6566
- ".png",
6567
- ".jpg",
6568
- ".jpeg",
6569
- ".gif",
6570
- ".webp",
6571
- ".svg",
6572
- ".bmp",
6573
- ".ico",
6574
- ".avif",
6575
- ".tiff",
6576
- ".tif"
6577
- ]);
6785
+ function FileViewerError({ message }) {
6786
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-2 p-3 text-xs text-destructive", children: [
6787
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircleIcon, { className: "mt-0.5 size-3.5 shrink-0" }),
6788
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: message })
6789
+ ] });
6790
+ }
6791
+ function PdfPreview({
6792
+ file,
6793
+ fileName: fileName2
6794
+ }) {
6795
+ const containerRef = react.useRef(null);
6796
+ const viewerRef = react.useRef(null);
6797
+ const pdfViewerRef = react.useRef(null);
6798
+ const eventBusRef = react.useRef(null);
6799
+ const findControllerRef = react.useRef(null);
6800
+ const [error, setError] = react.useState(null);
6801
+ const [pageNumber, setPageNumber] = react.useState(1);
6802
+ const [pagesCount, setPagesCount] = react.useState(0);
6803
+ const [scale, setScale] = react.useState(1);
6804
+ const [query, setQuery] = react.useState("");
6805
+ react.useEffect(() => {
6806
+ let cancelled = false;
6807
+ let loadingTask = null;
6808
+ let pdfDocument = null;
6809
+ const renderPdf = async () => {
6810
+ setError(null);
6811
+ const container = containerRef.current;
6812
+ const viewerElement = viewerRef.current;
6813
+ if (!container || !viewerElement) return;
6814
+ const pdfBytes = decodeBase64(file.content);
6815
+ const pdfjs = await import('pdfjs-dist/legacy/build/pdf.mjs');
6816
+ pdfjs.GlobalWorkerOptions.workerSrc = PDF_WORKER_SRC;
6817
+ globalThis.pdfjsLib = pdfjs;
6818
+ const pdfViewerModule = await import('pdfjs-dist/web/pdf_viewer.mjs');
6819
+ loadingTask = pdfjs.getDocument({ data: pdfBytes });
6820
+ const pdf = await loadingTask.promise;
6821
+ pdfDocument = pdf;
6822
+ if (cancelled) return;
6823
+ const eventBus = new pdfViewerModule.EventBus();
6824
+ const linkService = new pdfViewerModule.PDFLinkService({ eventBus });
6825
+ const findController = new pdfViewerModule.PDFFindController({
6826
+ eventBus,
6827
+ linkService
6828
+ });
6829
+ const pdfViewer = new pdfViewerModule.PDFViewer({
6830
+ container,
6831
+ viewer: viewerElement,
6832
+ eventBus,
6833
+ linkService,
6834
+ findController,
6835
+ removePageBorders: true
6836
+ });
6837
+ linkService.setViewer(pdfViewer);
6838
+ linkService.setDocument(pdf);
6839
+ findController.setDocument(pdf);
6840
+ const onPagesInit = () => {
6841
+ pdfViewer.currentScaleValue = "page-width";
6842
+ setScale(pdfViewer.currentScale || 1);
6843
+ setPagesCount(pdfViewer.pagesCount);
6844
+ setPageNumber(pdfViewer.currentPageNumber);
6845
+ };
6846
+ const onPageChanging = (event) => {
6847
+ if (typeof event.pageNumber === "number") {
6848
+ setPageNumber(event.pageNumber);
6849
+ }
6850
+ };
6851
+ const onScaleChanging = (event) => {
6852
+ if (typeof event.scale === "number") {
6853
+ setScale(event.scale);
6854
+ }
6855
+ };
6856
+ eventBus.on("pagesinit", onPagesInit);
6857
+ eventBus.on("pagechanging", onPageChanging);
6858
+ eventBus.on("scalechanging", onScaleChanging);
6859
+ pdfViewerRef.current = pdfViewer;
6860
+ eventBusRef.current = eventBus;
6861
+ findControllerRef.current = findController;
6862
+ setPagesCount(pdf.numPages);
6863
+ pdfViewer.setDocument(pdf);
6864
+ };
6865
+ void renderPdf().catch((nextError) => {
6866
+ if (cancelled) return;
6867
+ if (nextError instanceof Error && nextError.name === "RenderingCancelledException") {
6868
+ return;
6869
+ }
6870
+ setError(formatPdfPreviewError(nextError));
6871
+ });
6872
+ return () => {
6873
+ cancelled = true;
6874
+ loadingTask?.destroy();
6875
+ pdfDocument?.destroy();
6876
+ pdfViewerRef.current?.cleanup();
6877
+ pdfViewerRef.current = null;
6878
+ eventBusRef.current = null;
6879
+ findControllerRef.current = null;
6880
+ };
6881
+ }, [file.content]);
6882
+ const setViewerPage = (nextPage) => {
6883
+ const viewer = pdfViewerRef.current;
6884
+ if (!viewer) return;
6885
+ const clampedPage = Math.min(
6886
+ Math.max(1, nextPage),
6887
+ Math.max(1, viewer.pagesCount)
6888
+ );
6889
+ viewer.currentPageNumber = clampedPage;
6890
+ setPageNumber(clampedPage);
6891
+ };
6892
+ const setViewerScale = (nextScale) => {
6893
+ const viewer = pdfViewerRef.current;
6894
+ if (!viewer) return;
6895
+ const clampedScale = Math.min(3, Math.max(0.5, nextScale));
6896
+ viewer.currentScale = clampedScale;
6897
+ setScale(clampedScale);
6898
+ };
6899
+ const runFind = (findPrevious = false) => {
6900
+ const eventBus = eventBusRef.current;
6901
+ if (!eventBus || !query.trim()) return;
6902
+ eventBus.dispatch("find", {
6903
+ source: eventBus,
6904
+ type: "again",
6905
+ query,
6906
+ phraseSearch: true,
6907
+ caseSensitive: false,
6908
+ entireWord: false,
6909
+ highlightAll: true,
6910
+ findPrevious,
6911
+ matchDiacritics: true
6912
+ });
6913
+ };
6914
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-full flex-col", children: [
6915
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 flex-wrap items-center gap-1.5 border-b border-line px-2 py-1.5", children: [
6916
+ /* @__PURE__ */ jsxRuntime.jsx(
6917
+ Button,
6918
+ {
6919
+ type: "button",
6920
+ variant: "ghost",
6921
+ size: "icon-xs",
6922
+ onClick: () => setViewerPage(pageNumber - 1),
6923
+ disabled: pageNumber <= 1,
6924
+ "aria-label": "Previous PDF page",
6925
+ title: "Previous page",
6926
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeftIcon, { className: "size-3.5" })
6927
+ }
6928
+ ),
6929
+ /* @__PURE__ */ jsxRuntime.jsx(
6930
+ Input,
6931
+ {
6932
+ type: "number",
6933
+ min: 1,
6934
+ max: pagesCount || 1,
6935
+ value: pageNumber,
6936
+ onChange: (event) => setViewerPage(Number(event.target.value)),
6937
+ "aria-label": "PDF page number",
6938
+ className: "h-7 w-12 border-input px-1 text-center text-xs"
6939
+ }
6940
+ ),
6941
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground", children: [
6942
+ "/ ",
6943
+ pagesCount || "-"
6944
+ ] }),
6945
+ /* @__PURE__ */ jsxRuntime.jsx(
6946
+ Button,
6947
+ {
6948
+ type: "button",
6949
+ variant: "ghost",
6950
+ size: "icon-xs",
6951
+ onClick: () => setViewerPage(pageNumber + 1),
6952
+ disabled: pagesCount > 0 && pageNumber >= pagesCount,
6953
+ "aria-label": "Next PDF page",
6954
+ title: "Next page",
6955
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRightIcon, { className: "size-3.5" })
6956
+ }
6957
+ ),
6958
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-1 h-5 w-px bg-line" }),
6959
+ /* @__PURE__ */ jsxRuntime.jsx(
6960
+ Button,
6961
+ {
6962
+ type: "button",
6963
+ variant: "ghost",
6964
+ size: "icon-xs",
6965
+ onClick: () => setViewerScale(scale - 0.1),
6966
+ disabled: scale <= 0.5,
6967
+ "aria-label": "Zoom out PDF",
6968
+ title: "Zoom out",
6969
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MinusIcon, { className: "size-3.5" })
6970
+ }
6971
+ ),
6972
+ /* @__PURE__ */ jsxRuntime.jsxs(
6973
+ "button",
6974
+ {
6975
+ type: "button",
6976
+ className: "h-7 min-w-12 px-1 text-xs text-muted-foreground hover:text-foreground",
6977
+ onClick: () => {
6978
+ const viewer = pdfViewerRef.current;
6979
+ if (!viewer) return;
6980
+ viewer.currentScaleValue = "page-width";
6981
+ setScale(viewer.currentScale || 1);
6982
+ },
6983
+ "aria-label": "Fit PDF to width",
6984
+ title: "Fit width",
6985
+ children: [
6986
+ Math.round(scale * 100),
6987
+ "%"
6988
+ ]
6989
+ }
6990
+ ),
6991
+ /* @__PURE__ */ jsxRuntime.jsx(
6992
+ Button,
6993
+ {
6994
+ type: "button",
6995
+ variant: "ghost",
6996
+ size: "icon-xs",
6997
+ onClick: () => setViewerScale(scale + 0.1),
6998
+ disabled: scale >= 3,
6999
+ "aria-label": "Zoom in PDF",
7000
+ title: "Zoom in",
7001
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PlusIcon, { className: "size-3.5" })
7002
+ }
7003
+ ),
7004
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-1 h-5 w-px bg-line" }),
7005
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.SearchIcon, { className: "size-3.5 text-muted-foreground" }),
7006
+ /* @__PURE__ */ jsxRuntime.jsx(
7007
+ Input,
7008
+ {
7009
+ type: "search",
7010
+ value: query,
7011
+ onChange: (event) => setQuery(event.target.value),
7012
+ onKeyDown: (event) => {
7013
+ if (event.key === "Enter") runFind(event.shiftKey);
7014
+ },
7015
+ placeholder: "Find",
7016
+ "aria-label": "Find in PDF",
7017
+ className: "h-7 w-28 border-input px-1 text-xs"
7018
+ }
7019
+ ),
7020
+ /* @__PURE__ */ jsxRuntime.jsx(
7021
+ Button,
7022
+ {
7023
+ type: "button",
7024
+ variant: "ghost",
7025
+ size: "xs",
7026
+ onClick: () => runFind(false),
7027
+ disabled: !query.trim(),
7028
+ children: "Find"
7029
+ }
7030
+ )
7031
+ ] }),
7032
+ error && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex w-full items-start gap-2 p-3 text-xs text-destructive", children: [
7033
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircleIcon, { className: "mt-0.5 size-3.5 shrink-0" }),
7034
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: error })
7035
+ ] }),
7036
+ !error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative h-[70vh] min-h-[420px] flex-1 bg-muted/20", children: /* @__PURE__ */ jsxRuntime.jsx(
7037
+ "div",
7038
+ {
7039
+ ref: containerRef,
7040
+ "aria-label": `PDF viewer for ${fileName2}`,
7041
+ className: "absolute inset-0 overflow-auto",
7042
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: viewerRef, className: "pdfViewer" })
7043
+ }
7044
+ ) })
7045
+ ] });
7046
+ }
7047
+ function decodeBase64(content) {
7048
+ const binary = globalThis.atob(content);
7049
+ const bytes = new Uint8Array(binary.length);
7050
+ for (let index = 0; index < binary.length; index += 1) {
7051
+ bytes[index] = binary.charCodeAt(index);
7052
+ }
7053
+ return bytes;
7054
+ }
7055
+ function formatPdfPreviewError(error) {
7056
+ if (error instanceof Error && (error.name === "InvalidCharacterError" || error.message === "Invalid character")) {
7057
+ return "PDF preview data is not valid base64.";
7058
+ }
7059
+ return error instanceof Error ? error.message : "Failed to render PDF preview.";
7060
+ }
6578
7061
  var SKELETON_WIDTHS2 = [75, 60, 90, 65, 80, 70, 85, 55];
6579
- var DEFAULT_WIDTH = 500;
7062
+ var DEFAULT_WIDTH = 400;
6580
7063
  var MIN_WIDTH = 300;
6581
7064
  var MAX_WIDTH = 900;
6582
- function isViewable(name) {
6583
- const dot = name.lastIndexOf(".");
6584
- const ext = dot >= 0 ? name.slice(dot).toLowerCase() : "";
6585
- return TEXT_EXTENSIONS.has(ext) || IMAGE_EXTENSIONS.has(ext);
6586
- }
6587
7065
  function collectExpandedPaths(entries, depth = 0) {
6588
7066
  const paths = [];
6589
7067
  for (const entry of entries) {
@@ -6608,8 +7086,8 @@ function findEntry(entries, path) {
6608
7086
  }
6609
7087
  function RenderEntries({
6610
7088
  entries,
6611
- onFileSelect,
6612
- onDelete
7089
+ onDelete,
7090
+ downloadUrlFor
6613
7091
  }) {
6614
7092
  return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: entries.map((entry) => {
6615
7093
  if (entry.kind === "dir") {
@@ -6617,27 +7095,26 @@ function RenderEntries({
6617
7095
  RenderEntries,
6618
7096
  {
6619
7097
  entries: entry.children,
6620
- onFileSelect,
6621
- onDelete
7098
+ onDelete,
7099
+ downloadUrlFor
6622
7100
  }
6623
7101
  ) }, entry.path);
6624
7102
  }
6625
- const viewable = isViewable(entry.name);
7103
+ const fileName2 = entry.name;
6626
7104
  return /* @__PURE__ */ jsxRuntime.jsxs(FileTreeFile, { name: entry.name, path: entry.path, children: [
6627
7105
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "size-4 shrink-0" }),
6628
7106
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate", children: entry.name }),
6629
7107
  entry.size != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-1 shrink-0 text-xs text-muted-foreground/60", children: formatFileSize(entry.size) }),
6630
7108
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ml-1 flex shrink-0 items-center gap-0 opacity-0 transition-opacity group-hover:opacity-100", children: [
6631
- viewable && /* @__PURE__ */ jsxRuntime.jsx(
6632
- "button",
7109
+ /* @__PURE__ */ jsxRuntime.jsx(
7110
+ "a",
6633
7111
  {
6634
- type: "button",
7112
+ href: downloadUrlFor(entry.path),
7113
+ download: fileName2,
6635
7114
  className: "rounded p-0.5 text-muted-foreground hover:bg-muted hover:text-foreground",
6636
- onClick: (event) => {
6637
- event.stopPropagation();
6638
- onFileSelect(entry.path);
6639
- },
6640
- title: "View",
7115
+ onClick: (event) => event.stopPropagation(),
7116
+ title: "Download",
7117
+ "aria-label": `Download ${fileName2}`,
6641
7118
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.DownloadIcon, { className: "size-3" })
6642
7119
  }
6643
7120
  ),
@@ -6651,6 +7128,7 @@ function RenderEntries({
6651
7128
  onDelete(entry.path);
6652
7129
  },
6653
7130
  title: "Delete",
7131
+ "aria-label": `Delete ${fileName2}`,
6654
7132
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.TrashIcon, { className: "size-3" })
6655
7133
  }
6656
7134
  )
@@ -6663,6 +7141,8 @@ function WorkspacePanel({
6663
7141
  sessionId,
6664
7142
  selectedPath,
6665
7143
  onSelectedPathChange,
7144
+ collapsed = false,
7145
+ onCollapsedChange,
6666
7146
  disabled = false
6667
7147
  }) {
6668
7148
  const fileInputRef = react.useRef(null);
@@ -6857,6 +7337,25 @@ function WorkspacePanel({
6857
7337
  window.removeEventListener("mouseup", onMouseUp);
6858
7338
  };
6859
7339
  }, []);
7340
+ if (collapsed) {
7341
+ return /* @__PURE__ */ jsxRuntime.jsx(
7342
+ "aside",
7343
+ {
7344
+ role: "button",
7345
+ tabIndex: 0,
7346
+ className: "relative z-10 flex min-h-0 w-10 shrink-0 cursor-pointer items-center justify-center border-l border-muted-foreground/20 bg-card text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/30",
7347
+ "aria-label": "Expand workspace",
7348
+ onClick: () => onCollapsedChange?.(false),
7349
+ onKeyDown: (event) => {
7350
+ if (event.key === "Enter" || event.key === " ") {
7351
+ event.preventDefault();
7352
+ onCollapsedChange?.(false);
7353
+ }
7354
+ },
7355
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FolderOpenIcon, { className: "size-4 text-amber-500" })
7356
+ }
7357
+ );
7358
+ }
6860
7359
  return /* @__PURE__ */ jsxRuntime.jsxs(
6861
7360
  "aside",
6862
7361
  {
@@ -6889,6 +7388,19 @@ function WorkspacePanel({
6889
7388
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate text-sm font-medium text-foreground", children: rootName }),
6890
7389
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate text-xs text-faint", children: "Workspace" })
6891
7390
  ] }),
7391
+ /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
7392
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
7393
+ Button,
7394
+ {
7395
+ variant: "ghost",
7396
+ size: "icon-sm",
7397
+ onClick: () => onCollapsedChange?.(true),
7398
+ "aria-label": "Collapse workspace",
7399
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRightIcon, { className: "size-4" })
7400
+ }
7401
+ ) }),
7402
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "bottom", children: "Collapse" })
7403
+ ] }),
6892
7404
  /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
6893
7405
  /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
6894
7406
  Button,
@@ -6968,8 +7480,8 @@ function WorkspacePanel({
6968
7480
  RenderEntries,
6969
7481
  {
6970
7482
  entries,
6971
- onFileSelect: handleSelect,
6972
- onDelete: setDeleteTarget
7483
+ onDelete: setDeleteTarget,
7484
+ downloadUrlFor: (path) => sessionId ? adapter.getWorkspaceDownloadUrl({ sessionId, path }) : "#"
6973
7485
  }
6974
7486
  )
6975
7487
  }
@@ -6981,7 +7493,13 @@ function WorkspacePanel({
6981
7493
  file,
6982
7494
  loading: fileLoading,
6983
7495
  error: fileError,
7496
+ downloadUrl: file && sessionId ? adapter.getWorkspaceDownloadUrl({
7497
+ sessionId,
7498
+ path: file.path
7499
+ }) : null,
7500
+ onDelete: file ? () => setDeleteTarget(file.path) : null,
6984
7501
  onClose: () => {
7502
+ onSelectedPathChange(null);
6985
7503
  setFile(null);
6986
7504
  setFileError(null);
6987
7505
  }
@@ -7082,10 +7600,11 @@ var EMPTY_TOKEN_USAGE = {
7082
7600
  contextWindow: 0,
7083
7601
  model: ""
7084
7602
  };
7085
- function createInitialAgentChatState() {
7603
+ function createInitialAgentChatState(options = {}) {
7086
7604
  return {
7087
7605
  messages: [],
7088
7606
  isRunning: false,
7607
+ isLoadingHistory: options.isLoadingHistory ?? false,
7089
7608
  tokenUsage: EMPTY_TOKEN_USAGE,
7090
7609
  retryIndicator: null,
7091
7610
  lastEventId: 0,
@@ -7097,6 +7616,7 @@ function createInitialAgentChatState() {
7097
7616
  function applyAgentChatEvent(state, event) {
7098
7617
  let nextState = {
7099
7618
  ...state,
7619
+ isLoadingHistory: false,
7100
7620
  lastEventId: Math.max(state.lastEventId, event.eventId),
7101
7621
  sessionDone: state.sessionDone || event.type === "session.done"
7102
7622
  };
@@ -7630,11 +8150,12 @@ function useAgentChatRuntime({
7630
8150
  onSessionChange
7631
8151
  }) {
7632
8152
  const [state, setState] = react.useState(
7633
- () => createInitialAgentChatState()
8153
+ () => createInitialAgentChatState({ isLoadingHistory: Boolean(sessionId) })
7634
8154
  );
7635
8155
  const stateRef = react.useRef(state);
7636
8156
  const streamRef = react.useRef(null);
7637
8157
  const reconnectTimerRef = react.useRef(null);
8158
+ const previousSessionIdRef = react.useRef(sessionId);
7638
8159
  react.useEffect(() => {
7639
8160
  stateRef.current = state;
7640
8161
  }, [state]);
@@ -7650,6 +8171,8 @@ function useAgentChatRuntime({
7650
8171
  stream?.close();
7651
8172
  }, []);
7652
8173
  react.useEffect(() => {
8174
+ const previousSessionId = previousSessionIdRef.current;
8175
+ previousSessionIdRef.current = sessionId;
7653
8176
  clearReconnectTimer();
7654
8177
  closeStream();
7655
8178
  if (!sessionId) {
@@ -7657,11 +8180,11 @@ function useAgentChatRuntime({
7657
8180
  return;
7658
8181
  }
7659
8182
  let cancelled = false;
7660
- const connect = () => {
8183
+ const connect = (after) => {
7661
8184
  if (cancelled) return;
7662
8185
  const stream = adapter.openEventStream({
7663
8186
  sessionId,
7664
- after: stateRef.current.lastEventId
8187
+ after: after ?? stateRef.current.lastEventId
7665
8188
  });
7666
8189
  streamRef.current = stream;
7667
8190
  for (const eventType of AGENT_CHAT_LISTENED_EVENTS) {
@@ -7684,14 +8207,32 @@ function useAgentChatRuntime({
7684
8207
  streamRef.current = null;
7685
8208
  }
7686
8209
  if (!stateRef.current.sessionDone && !cancelled) {
7687
- reconnectTimerRef.current = setTimeout(connect, 3e3);
8210
+ reconnectTimerRef.current = setTimeout(() => connect(), 3e3);
7688
8211
  }
7689
8212
  };
7690
8213
  };
7691
- setState(createInitialAgentChatState());
7692
- connect();
8214
+ const currentState = stateRef.current;
8215
+ const preservePendingFirstMessage = previousSessionId === null && currentState.isRunning && currentState.messages.some(
8216
+ (message) => message.role === "user" && message.id.startsWith("local-")
8217
+ );
8218
+ const initialState = preservePendingFirstMessage ? {
8219
+ ...createInitialAgentChatState({ isLoadingHistory: false }),
8220
+ messages: currentState.messages,
8221
+ isRunning: true
8222
+ } : createInitialAgentChatState({
8223
+ isLoadingHistory: true
8224
+ });
8225
+ stateRef.current = initialState;
8226
+ setState(initialState);
8227
+ connect(0);
7693
8228
  adapter.getSession({ sessionId }).then((session) => {
7694
8229
  if (cancelled) return;
8230
+ if (session.messageCount === 0) {
8231
+ setState((prev) => ({
8232
+ ...prev,
8233
+ isLoadingHistory: false
8234
+ }));
8235
+ }
7695
8236
  if (isTerminalStatus(session.status)) {
7696
8237
  setState((prev) => ({
7697
8238
  ...prev,
@@ -7774,13 +8315,18 @@ function useAgentChatRuntime({
7774
8315
  }, []);
7775
8316
  const send = react.useCallback(
7776
8317
  async (content) => {
8318
+ markSending(content);
7777
8319
  if (!sessionId) {
7778
- const session = await adapter.createSession({ agentId });
7779
- await adapter.sendMessage({ sessionId: session.id, content });
7780
- onSessionChange?.(session.id);
8320
+ try {
8321
+ const session = await adapter.createSession({ agentId });
8322
+ onSessionChange?.(session.id);
8323
+ await adapter.sendMessage({ sessionId: session.id, content });
8324
+ } catch (error) {
8325
+ markSendError(error instanceof Error ? error.message : "send failed");
8326
+ throw error;
8327
+ }
7781
8328
  return;
7782
8329
  }
7783
- markSending(content);
7784
8330
  try {
7785
8331
  await adapter.sendMessage({ sessionId, content });
7786
8332
  } catch (error) {
@@ -7817,6 +8363,7 @@ function useAgentChatRuntime({
7817
8363
  return {
7818
8364
  messages: state.messages,
7819
8365
  isRunning: state.isRunning,
8366
+ isLoadingHistory: state.isLoadingHistory,
7820
8367
  tokenUsage: state.tokenUsage,
7821
8368
  retryIndicator: state.retryIndicator,
7822
8369
  send,
@@ -7853,6 +8400,7 @@ function AgentChat({
7853
8400
  disabled
7854
8401
  }) {
7855
8402
  const [workspacePath, setWorkspacePath] = react.useState(null);
8403
+ const [workspaceCollapsed, setWorkspaceCollapsed] = react.useState(false);
7856
8404
  const runtime = useAgentChatRuntime({
7857
8405
  adapter,
7858
8406
  agentId,
@@ -7884,6 +8432,7 @@ function AgentChat({
7884
8432
  sessionId,
7885
8433
  messages: runtime.messages,
7886
8434
  isRunning: runtime.isRunning,
8435
+ isLoadingHistory: runtime.isLoadingHistory,
7887
8436
  onSend: (content) => void runtime.send(content),
7888
8437
  onStop: () => void runtime.stop(),
7889
8438
  onRetry: runtime.retry,
@@ -7900,6 +8449,8 @@ function AgentChat({
7900
8449
  sessionId,
7901
8450
  selectedPath: workspacePath,
7902
8451
  onSelectedPathChange: setWorkspacePath,
8452
+ collapsed: workspaceCollapsed,
8453
+ onCollapsedChange: setWorkspaceCollapsed,
7903
8454
  disabled
7904
8455
  }
7905
8456
  )
@@ -7907,6 +8458,7 @@ function AgentChat({
7907
8458
  }
7908
8459
  );
7909
8460
  }
8461
+ init_utils();
7910
8462
  var POLL_INTERVAL_ACTIVE_MS = 4e3;
7911
8463
  var POLL_INTERVAL_IDLE_MS = 3e4;
7912
8464
  var DEFAULT_SESSION_LIST_LIMIT = 50;
@@ -7970,6 +8522,20 @@ function mergeTreeNodes(groups) {
7970
8522
  }
7971
8523
  return Array.from(byId.values());
7972
8524
  }
8525
+ function pruneDeletedSessionNodes(nodes, deletedSessionId) {
8526
+ const deletedIds = /* @__PURE__ */ new Set([deletedSessionId]);
8527
+ let changed = true;
8528
+ while (changed) {
8529
+ changed = false;
8530
+ for (const node of nodes) {
8531
+ if (node.parentId && deletedIds.has(node.parentId) && !deletedIds.has(node.id)) {
8532
+ deletedIds.add(node.id);
8533
+ changed = true;
8534
+ }
8535
+ }
8536
+ }
8537
+ return nodes.filter((node) => !deletedIds.has(node.id));
8538
+ }
7973
8539
  function formatSessionTime(value) {
7974
8540
  const date = new Date(value);
7975
8541
  if (Number.isNaN(date.getTime())) return "";
@@ -8086,11 +8652,12 @@ function SessionTreePanel({
8086
8652
  title = "Running",
8087
8653
  sessionListLimit = DEFAULT_SESSION_LIST_LIMIT,
8088
8654
  hideRoot = false,
8655
+ hideHeader = false,
8656
+ loadList = false,
8089
8657
  onSessionSelect,
8090
8658
  onSessionDelete
8091
8659
  }) {
8092
8660
  const [nodes, setNodes] = react.useState([]);
8093
- const [loading, setLoading] = react.useState(false);
8094
8661
  const [error, setError] = react.useState(null);
8095
8662
  const [hasEverLoaded, setHasEverLoaded] = react.useState(false);
8096
8663
  const [deletingSessionId, setDeletingSessionId] = react.useState(
@@ -8102,18 +8669,16 @@ function SessionTreePanel({
8102
8669
  const resetContext = react.useRef(null);
8103
8670
  const refetch = react.useCallback(
8104
8671
  async (opts) => {
8105
- const canLoadSessionList = Boolean(agentId && adapter.listSessions);
8672
+ const canLoadSessionList = Boolean(loadList && adapter.listSessions);
8106
8673
  const canLoadSessionTree = Boolean(sessionId && adapter.getSessionTree);
8107
8674
  if (!canLoadSessionList && !canLoadSessionTree) {
8108
8675
  setNodes([]);
8109
8676
  setHasEverLoaded(true);
8110
- setLoading(false);
8111
8677
  return;
8112
8678
  }
8113
8679
  const currentRequestId = ++requestId.current;
8114
- if (!opts?.silent) setLoading(true);
8115
8680
  try {
8116
- const sessionListPromise = agentId && adapter.listSessions ? adapter.listSessions({
8681
+ const sessionListPromise = loadList && adapter.listSessions ? adapter.listSessions({
8117
8682
  agentId,
8118
8683
  limit: sessionListLimit
8119
8684
  }) : Promise.resolve(null);
@@ -8139,13 +8704,9 @@ function SessionTreePanel({
8139
8704
  if (!opts?.silent) {
8140
8705
  setError(e instanceof Error ? e.message : "Failed to load tree");
8141
8706
  }
8142
- } finally {
8143
- if (mounted.current && currentRequestId === requestId.current && !opts?.silent) {
8144
- setLoading(false);
8145
- }
8146
8707
  }
8147
8708
  },
8148
- [adapter, agentId, sessionId, sessionListLimit]
8709
+ [adapter, agentId, loadList, sessionId, sessionListLimit]
8149
8710
  );
8150
8711
  react.useEffect(() => {
8151
8712
  mounted.current = true;
@@ -8155,8 +8716,8 @@ function SessionTreePanel({
8155
8716
  }, []);
8156
8717
  react.useEffect(() => {
8157
8718
  const previous = resetContext.current;
8158
- const shouldReset = !previous || previous.adapter !== adapter || previous.agentId !== agentId || previous.sessionListLimit !== sessionListLimit;
8159
- resetContext.current = { adapter, agentId, sessionListLimit };
8719
+ const shouldReset = !previous || previous.loadList !== loadList || previous.agentId !== agentId || previous.sessionListLimit !== sessionListLimit;
8720
+ resetContext.current = { loadList, agentId, sessionListLimit };
8160
8721
  if (shouldReset) {
8161
8722
  setNodes([]);
8162
8723
  setHasEverLoaded(false);
@@ -8164,7 +8725,7 @@ function SessionTreePanel({
8164
8725
  }
8165
8726
  setError(null);
8166
8727
  void refetch();
8167
- }, [adapter, agentId, refetch, sessionListLimit]);
8728
+ }, [adapter, agentId, loadList, refetch, sessionListLimit]);
8168
8729
  const runningCount = react.useMemo(
8169
8730
  () => nodes.filter((n) => n.status === "active").length,
8170
8731
  [nodes]
@@ -8201,6 +8762,11 @@ function SessionTreePanel({
8201
8762
  setDeletingSessionId(id);
8202
8763
  try {
8203
8764
  await adapter.deleteSession({ sessionId: id });
8765
+ setNodes((current) => {
8766
+ const next = pruneDeletedSessionNodes(current, id);
8767
+ lastFingerprint.current = treeFingerprint(next);
8768
+ return next;
8769
+ });
8204
8770
  onSessionDelete?.(id);
8205
8771
  await refetch({ silent: true });
8206
8772
  } catch (e) {
@@ -8215,24 +8781,12 @@ function SessionTreePanel({
8215
8781
  if (nodes.length === 0) return null;
8216
8782
  const topLevel = hideRoot ? roots.flatMap((r) => r.children) : roots;
8217
8783
  if (topLevel.length === 0) return null;
8218
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-line", children: [
8219
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 px-3 py-2 text-xs font-semibold uppercase tracking-wide", children: [
8784
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
8785
+ !hideHeader && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-line flex items-center gap-1.5 px-3 py-2 text-xs font-semibold uppercase tracking-wide", children: [
8220
8786
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.UsersIcon, { className: "w-3.5 h-3.5" }),
8221
8787
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: title }),
8222
8788
  runningCount > 0 && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", className: "h-4 px-1.5 text-[10px] ml-auto", children: runningCount })
8223
8789
  ] }),
8224
- loading && /* @__PURE__ */ jsxRuntime.jsx(
8225
- "div",
8226
- {
8227
- className: "px-3 py-2",
8228
- role: "status",
8229
- "aria-label": "Loading sessions",
8230
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
8231
- /* @__PURE__ */ jsxRuntime.jsx(Skeleton, { className: "h-3.5 w-28" }),
8232
- /* @__PURE__ */ jsxRuntime.jsx(Skeleton, { className: "h-3 w-20" })
8233
- ] })
8234
- }
8235
- ),
8236
8790
  error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-2 text-xs text-destructive", children: error }),
8237
8791
  !error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-1 pb-2", children: topLevel.map((entry) => /* @__PURE__ */ jsxRuntime.jsx(
8238
8792
  TreeNodeRow,