@tangle-network/sandbox-ui 0.137.2 → 0.138.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.
@@ -1,3 +1,4 @@
1
- import { A as SandboxArtifactTerminal, C as ChangesPane, D as ChangesPaneProps, E as ChangedFile, O as SandboxArtifact, S as PillTabsProps, T as ArtifactView, _ as DiffStats, a as PreviewView, b as PillTabItem, c as DiffViewProps, d as FileBreadcrumb, f as FileBreadcrumbProps, g as PanelHeaderProps, h as PanelHeader, i as resolveLanguage, k as SandboxArtifactPaneProps, l as CodeView, m as GitStatusBadge, n as CodeSurface, o as PreviewViewProps, p as DiffStatsBadge, r as CodeSurfaceProps, s as DiffView, t as BRAND_PRISM_THEME, u as CodeViewProps, v as buildUnifiedPatch, w as SandboxArtifactPane, x as PillTabs, y as computeDiffStats } from "./index-T4-5mWTq.js";
1
+ import { C as ChangedFile, D as SandboxArtifactTerminal, E as SandboxArtifactPaneProps, S as ArtifactView, T as SandboxArtifact, _ as PillTabItem, a as PreviewViewProps, b as ChangesPane, c as FileBreadcrumb, d as GitStatusBadge, f as PanelHeader, g as computeDiffStats, h as buildUnifiedPatch, i as PreviewView, l as FileBreadcrumbProps, m as DiffStats, n as CodeSurface, o as CodeView, p as PanelHeaderProps, r as CodeSurfaceProps, s as CodeViewProps, t as CODE_THEME, u as DiffStatsBadge, v as PillTabs, w as ChangesPaneProps, x as SandboxArtifactPane, y as PillTabsProps } from "./index-CvtEhIpk.js";
2
+ import { n as DiffViewProps, t as DiffView } from "./diff-view-DDHWtR6X.js";
2
3
  import { a as PreviewReadiness, i as PreviewAccess, n as EmbeddedAppView, r as EmbeddedAppViewProps, t as EmbeddedApp } from "./embedded-app-view-Ta5GI4hx.js";
3
- export { type ArtifactView, BRAND_PRISM_THEME, type ChangedFile, ChangesPane, type ChangesPaneProps, CodeSurface, type CodeSurfaceProps, CodeView, type CodeViewProps, type DiffStats, DiffStatsBadge, DiffView, type DiffViewProps, type EmbeddedApp, EmbeddedAppView, type EmbeddedAppViewProps, FileBreadcrumb, type FileBreadcrumbProps, GitStatusBadge, PanelHeader, type PanelHeaderProps, type PillTabItem, PillTabs, type PillTabsProps, type PreviewAccess, type PreviewReadiness, PreviewView, type PreviewViewProps, type SandboxArtifact, SandboxArtifactPane, type SandboxArtifactPaneProps, type SandboxArtifactTerminal, buildUnifiedPatch, computeDiffStats, resolveLanguage };
4
+ export { type ArtifactView, CODE_THEME, type ChangedFile, ChangesPane, type ChangesPaneProps, CodeSurface, type CodeSurfaceProps, CodeView, type CodeViewProps, type DiffStats, DiffStatsBadge, DiffView, type DiffViewProps, type EmbeddedApp, EmbeddedAppView, type EmbeddedAppViewProps, FileBreadcrumb, type FileBreadcrumbProps, GitStatusBadge, PanelHeader, type PanelHeaderProps, type PillTabItem, PillTabs, type PillTabsProps, type PreviewAccess, type PreviewReadiness, PreviewView, type PreviewViewProps, type SandboxArtifact, SandboxArtifactPane, type SandboxArtifactPaneProps, type SandboxArtifactTerminal, buildUnifiedPatch, computeDiffStats };
package/dist/workbench.js CHANGED
@@ -1,3 +1,4 @@
1
- import { a as computeDiffStats, c as BRAND_PRISM_THEME, d as DiffStatsBadge, f as GitStatusBadge, i as buildUnifiedPatch, l as CodeSurface, m as PillTabs, n as SandboxArtifactPane, o as CodeView, p as PanelHeader, r as DiffView, s as FileBreadcrumb, t as ChangesPane, u as resolveLanguage } from "./workbench-GDuy3rB5.js";
1
+ import { a as DiffStatsBadge, c as PillTabs, i as FileBreadcrumb, n as SandboxArtifactPane, o as GitStatusBadge, r as CodeView, s as PanelHeader, t as ChangesPane } from "./workbench-B3JL1qbt.js";
2
+ import { a as CodeSurface, i as CODE_THEME, n as buildUnifiedPatch, r as computeDiffStats, t as DiffView } from "./diff-view-A0O0Z_SR.js";
2
3
  import { n as PreviewView, t as EmbeddedAppView } from "./embedded-app-view-DXsoPale.js";
3
- export { BRAND_PRISM_THEME, ChangesPane, CodeSurface, CodeView, DiffStatsBadge, DiffView, EmbeddedAppView, FileBreadcrumb, GitStatusBadge, PanelHeader, PillTabs, PreviewView, SandboxArtifactPane, buildUnifiedPatch, computeDiffStats, resolveLanguage };
4
+ export { CODE_THEME, ChangesPane, CodeSurface, CodeView, DiffStatsBadge, DiffView, EmbeddedAppView, FileBreadcrumb, GitStatusBadge, PanelHeader, PillTabs, PreviewView, SandboxArtifactPane, buildUnifiedPatch, computeDiffStats };
@@ -8,7 +8,7 @@ import { Markdown } from "@tangle-network/ui/markdown";
8
8
  import { useCallback, useEffect, useId, useImperativeHandle, useMemo, useRef, useState } from "react";
9
9
  import { createPortal } from "react-dom";
10
10
  import * as DialogPrimitive from "@radix-ui/react-dialog";
11
- import { AlertCircle, AlertTriangle, ArrowLeft, Binary, Bot, Boxes, CheckCircle, ChevronDown, ChevronUp, FileCode2, FileText, FolderOpen, FolderTree, LayoutPanelTop, Loader2, MessageSquareText, PanelBottomClose, PanelBottomOpen, PanelLeftClose, PanelLeftOpen, PanelRightClose, PanelRightOpen, Plus, RefreshCw, Search, Settings, ShieldCheck, Sparkles, Terminal, TerminalSquare, Upload, Wifi, X, Zap } from "lucide-react";
11
+ import { AlertCircle, AlertTriangle, ArrowLeft, Binary, Bot, Boxes, CheckCircle, ChevronDown, ChevronUp, FileCode2, FileText, FolderOpen, FolderTree, LayoutPanelTop, Loader2, Maximize2, MessageSquareText, Minimize2, PanelBottomClose, PanelBottomOpen, PanelLeftClose, PanelLeftOpen, PanelRightClose, PanelRightOpen, Plus, RefreshCw, Search, Settings, ShieldCheck, Sparkles, Terminal, TerminalSquare, Upload, Wifi, X, Zap } from "lucide-react";
12
12
  import { focusField, focusRing, focusRingInset } from "@tangle-network/ui/utils";
13
13
  import { FileArtifactPane, FileTree, filterFileTree } from "@tangle-network/ui/files";
14
14
  import { useNavbarSessions } from "@tangle-network/ui/stores";
@@ -2500,6 +2500,78 @@ function ApprovalQueue({ items, className, onApprove, onReject, renderItemDetail
2500
2500
  });
2501
2501
  }
2502
2502
  // src/workspace/workspace-files-pane.tsx
2503
+ const SPLIT_MIN_PX = 640;
2504
+ const SIDEBAR_DEFAULT_PX = 280;
2505
+ const SIDEBAR_MIN_PX = 200;
2506
+ /** The sidebar's width, clamped to 200 px .. half the pane, remembered per viewer under `expansionKey` when given. */
2507
+ function useSidebarWidth(key, containerWidth) {
2508
+ const storageKey = key ? `${key}:sidebar-width` : void 0;
2509
+ const [stored, setStored] = useState(() => {
2510
+ if (!storageKey) return SIDEBAR_DEFAULT_PX;
2511
+ try {
2512
+ const value = Number(globalThis.localStorage?.getItem(storageKey));
2513
+ return Number.isFinite(value) && value > 0 ? value : SIDEBAR_DEFAULT_PX;
2514
+ } catch {
2515
+ return SIDEBAR_DEFAULT_PX;
2516
+ }
2517
+ });
2518
+ const max = Math.max(SIDEBAR_MIN_PX, containerWidth > 0 ? Math.floor(containerWidth / 2) : 560);
2519
+ const width = Math.min(max, Math.max(SIDEBAR_MIN_PX, stored));
2520
+ const save = (px) => {
2521
+ const next = Math.min(max, Math.max(SIDEBAR_MIN_PX, Math.round(px)));
2522
+ setStored(next);
2523
+ if (storageKey) try {
2524
+ globalThis.localStorage?.setItem(storageKey, String(next));
2525
+ } catch {}
2526
+ };
2527
+ return {
2528
+ width,
2529
+ min: SIDEBAR_MIN_PX,
2530
+ max,
2531
+ set: save,
2532
+ reset: () => save(SIDEBAR_DEFAULT_PX)
2533
+ };
2534
+ }
2535
+ /** Drag, arrow keys (Shift for larger steps), Home/End, or double-click to reset the sidebar width. */
2536
+ function SidebarResizer({ sidebar, label }) {
2537
+ return /* @__PURE__ */ jsx("div", {
2538
+ role: "separator",
2539
+ "aria-orientation": "vertical",
2540
+ "aria-label": `Resize ${label.toLowerCase()} sidebar`,
2541
+ "aria-valuenow": sidebar.width,
2542
+ "aria-valuemin": sidebar.min,
2543
+ "aria-valuemax": sidebar.max,
2544
+ tabIndex: 0,
2545
+ onDoubleClick: sidebar.reset,
2546
+ onKeyDown: (event) => {
2547
+ const step = event.shiftKey ? 64 : 16;
2548
+ if (event.key === "ArrowLeft") sidebar.set(sidebar.width - step);
2549
+ else if (event.key === "ArrowRight") sidebar.set(sidebar.width + step);
2550
+ else if (event.key === "Home") sidebar.set(sidebar.min);
2551
+ else if (event.key === "End") sidebar.set(sidebar.max);
2552
+ else return;
2553
+ event.preventDefault();
2554
+ },
2555
+ onPointerDown: (event) => {
2556
+ const handle = event.currentTarget;
2557
+ const startX = event.clientX;
2558
+ const startWidth = sidebar.width;
2559
+ handle.setPointerCapture(event.pointerId);
2560
+ const move = (next) => sidebar.set(startWidth + next.clientX - startX);
2561
+ const up = () => {
2562
+ handle.removeEventListener("pointermove", move);
2563
+ handle.removeEventListener("pointerup", up);
2564
+ };
2565
+ handle.addEventListener("pointermove", move);
2566
+ handle.addEventListener("pointerup", up);
2567
+ },
2568
+ className: "group relative w-1.5 shrink-0 cursor-col-resize touch-none outline-none",
2569
+ children: /* @__PURE__ */ jsx("span", {
2570
+ "aria-hidden": true,
2571
+ className: "absolute inset-y-0 left-1/2 w-px -translate-x-1/2 bg-border transition-colors group-hover:bg-primary group-focus-visible:bg-primary"
2572
+ })
2573
+ });
2574
+ }
2503
2575
  function indexNodes(nodes) {
2504
2576
  const index = /* @__PURE__ */ new Map();
2505
2577
  const visit = (node) => {
@@ -2520,8 +2592,30 @@ function activeElementWithin(element) {
2520
2592
  * The tree sits in its own surface: folders start collapsed, expansion is
2521
2593
  * remembered under `expansionKey`, and a preview replaces the tree until Back.
2522
2594
  */
2523
- function WorkspaceFilesPane({ root, paths, selectedPath, onSelect, preview, onBack, emptyState, expansionKey, search = true, label = "Files", headerActions, surface = "card", className, style }) {
2595
+ function WorkspaceFilesPane({ root, paths, selectedPath, onSelect, preview, onBack, emptyState, expansionKey, search = true, label = "Files", headerActions, surface = "card", layout = "split", tabs = true, expandable = true, className, style }) {
2524
2596
  const plain = surface === "plain";
2597
+ const rootRef = useRef(null);
2598
+ const [containerWidth, setContainerWidth] = useState(0);
2599
+ useEffect(() => {
2600
+ const element = rootRef.current;
2601
+ if (!element || typeof ResizeObserver === "undefined") return;
2602
+ const observer = new ResizeObserver(([entry]) => setContainerWidth(entry.contentRect.width));
2603
+ observer.observe(element);
2604
+ return () => observer.disconnect();
2605
+ }, []);
2606
+ const split = layout === "split" && (containerWidth === 0 || containerWidth >= SPLIT_MIN_PX);
2607
+ const sidebar = useSidebarWidth(expansionKey, containerWidth);
2608
+ const [expanded, setExpanded] = useState(false);
2609
+ useEffect(() => {
2610
+ if (!expanded) return;
2611
+ const keydown = (event) => {
2612
+ if (event.key !== "Escape" || event.defaultPrevented) return;
2613
+ event.preventDefault();
2614
+ setExpanded(false);
2615
+ };
2616
+ document.addEventListener("keydown", keydown);
2617
+ return () => document.removeEventListener("keydown", keydown);
2618
+ }, [expanded]);
2525
2619
  const nodes = useMemo(() => root ? root.path ? [root] : root.children ?? [] : fileTreeFromPaths(paths ?? []), [root, paths]);
2526
2620
  const index = useMemo(() => indexNodes(nodes), [nodes]);
2527
2621
  const [query, setQuery] = useState("");
@@ -2548,6 +2642,10 @@ function WorkspaceFilesPane({ root, paths, selectedPath, onSelect, preview, onBa
2548
2642
  wasPreviewRef.current = hasPreview;
2549
2643
  return;
2550
2644
  }
2645
+ if (split) {
2646
+ wasPreviewRef.current = hasPreview;
2647
+ return;
2648
+ }
2551
2649
  if (hasPreview && !wasPreviewRef.current && openedByReaderRef.current) backRef.current?.focus({ preventScroll: true });
2552
2650
  openedByReaderRef.current = false;
2553
2651
  if (!hasPreview && wasPreviewRef.current) {
@@ -2556,118 +2654,221 @@ function WorkspaceFilesPane({ root, paths, selectedPath, onSelect, preview, onBa
2556
2654
  else treeRef.current?.focus({ preventScroll: true });
2557
2655
  }
2558
2656
  wasPreviewRef.current = hasPreview;
2559
- }, [hasPreview]);
2657
+ }, [hasPreview, split]);
2560
2658
  return /* @__PURE__ */ jsxs("div", {
2561
- className: cn$1("flex h-full min-h-0 min-w-0 flex-col", className),
2562
- style,
2563
- children: [/* @__PURE__ */ jsx("div", {
2564
- ref: treeRef,
2565
- hidden: hasPreview,
2566
- tabIndex: -1,
2567
- role: "region",
2568
- "aria-label": "Workspace files",
2569
- className: cn$1("flex min-h-0 flex-1 flex-col", !plain && "p-2", hasPreview && "hidden"),
2570
- children: /* @__PURE__ */ jsxs("section", {
2571
- "aria-labelledby": headingId,
2572
- "data-workspace-files-surface": surface,
2573
- className: cn$1("flex min-h-0 flex-1 flex-col overflow-hidden", !plain && "rounded-xl border border-border bg-card text-card-foreground shadow-sm"),
2574
- children: [
2575
- plain ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("h2", {
2576
- id: headingId,
2577
- className: "sr-only",
2578
- children: label
2579
- }), headerActions && /* @__PURE__ */ jsx("div", {
2580
- className: "flex shrink-0 items-center justify-end gap-1 px-3 pt-3",
2581
- children: headerActions
2582
- })] }) : /* @__PURE__ */ jsxs("div", {
2583
- className: "flex shrink-0 items-center gap-2 px-3 pb-2 pt-3",
2584
- children: [/* @__PURE__ */ jsx("h2", {
2659
+ ref: rootRef,
2660
+ "data-workspace-files-layout": split ? "split" : "stack",
2661
+ "data-expanded": expanded ? "" : void 0,
2662
+ className: cn$1("flex h-full min-h-0 min-w-0", split ? "flex-row" : "flex-col", expanded && "fixed inset-0 z-50 h-auto bg-background p-2", className),
2663
+ style: expanded ? void 0 : style,
2664
+ children: [
2665
+ /* @__PURE__ */ jsx("div", {
2666
+ ref: treeRef,
2667
+ hidden: !split && hasPreview,
2668
+ tabIndex: -1,
2669
+ role: "region",
2670
+ "aria-label": "Workspace files",
2671
+ style: split ? { width: sidebar.width } : void 0,
2672
+ className: cn$1("flex min-h-0 flex-col", split ? "shrink-0" : "flex-1", !plain && "p-2", !split && hasPreview && "hidden"),
2673
+ children: /* @__PURE__ */ jsxs("section", {
2674
+ "aria-labelledby": headingId,
2675
+ "data-workspace-files-surface": surface,
2676
+ className: cn$1("flex min-h-0 flex-1 flex-col overflow-hidden", !plain && "rounded-xl border border-border bg-card text-card-foreground shadow-sm"),
2677
+ children: [
2678
+ plain ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("h2", {
2585
2679
  id: headingId,
2586
- className: "min-w-0 flex-1 truncate text-sm font-semibold text-foreground",
2680
+ className: "sr-only",
2587
2681
  children: label
2588
2682
  }), headerActions && /* @__PURE__ */ jsx("div", {
2589
- className: "flex shrink-0 items-center gap-1",
2683
+ className: "flex shrink-0 items-center justify-end gap-1 px-3 pt-3",
2590
2684
  children: headerActions
2591
- })]
2592
- }),
2593
- search && !isEmpty && /* @__PURE__ */ jsx("div", {
2594
- className: cn$1("shrink-0 px-3 pb-2", plain && "pt-3"),
2595
- children: /* @__PURE__ */ jsx(Input, {
2596
- type: "search",
2597
- size: "sm",
2598
- value: query,
2599
- onChange: (event) => setQuery(event.target.value),
2600
- placeholder: `Search ${label.toLowerCase()}…`,
2601
- "aria-label": `Search ${label.toLowerCase()}`
2685
+ })] }) : /* @__PURE__ */ jsxs("div", {
2686
+ className: "flex shrink-0 items-center gap-2 px-3 pb-2 pt-3",
2687
+ children: [/* @__PURE__ */ jsx("h2", {
2688
+ id: headingId,
2689
+ className: "min-w-0 flex-1 truncate text-sm font-semibold text-foreground",
2690
+ children: label
2691
+ }), headerActions && /* @__PURE__ */ jsx("div", {
2692
+ className: "flex shrink-0 items-center gap-1",
2693
+ children: headerActions
2694
+ })]
2695
+ }),
2696
+ search && !isEmpty && /* @__PURE__ */ jsx("div", {
2697
+ className: cn$1("shrink-0 px-3 pb-2", plain && "pt-3"),
2698
+ children: /* @__PURE__ */ jsx(Input, {
2699
+ type: "search",
2700
+ size: "sm",
2701
+ value: query,
2702
+ onChange: (event) => setQuery(event.target.value),
2703
+ placeholder: `Search ${label.toLowerCase()}…`,
2704
+ "aria-label": `Search ${label.toLowerCase()}`
2705
+ })
2706
+ }),
2707
+ /* @__PURE__ */ jsx("div", {
2708
+ className: cn$1("min-h-0 flex-1 overflow-y-auto px-1.5 py-1.5", (!plain || search && !isEmpty) && "border-t border-border"),
2709
+ children: isEmpty ? emptyState !== void 0 ? emptyState : /* @__PURE__ */ jsx(EmptyState, {
2710
+ icon: /* @__PURE__ */ jsx(FolderOpen, { className: "h-6 w-6" }),
2711
+ title: "No files yet"
2712
+ }) : visible.length === 0 ? /* @__PURE__ */ jsxs("div", {
2713
+ className: "flex flex-col items-center gap-3 p-6 text-center",
2714
+ children: [/* @__PURE__ */ jsxs("p", {
2715
+ className: "max-w-xs break-words text-xs text-muted-foreground",
2716
+ children: [
2717
+ "No files match “",
2718
+ query.trim(),
2719
+ "”."
2720
+ ]
2721
+ }), /* @__PURE__ */ jsx(Button, {
2722
+ type: "button",
2723
+ variant: "outline",
2724
+ size: "sm",
2725
+ onClick: () => setQuery(""),
2726
+ children: "Clear search"
2727
+ })]
2728
+ }) : /* @__PURE__ */ jsx(VaultTree, {
2729
+ root: treeRoot,
2730
+ selectedPath: normalizedSelection,
2731
+ onSelect: (path) => {
2732
+ if (treeRef.current) returnFocusRef.current = activeElementWithin(treeRef.current);
2733
+ openedByReaderRef.current = true;
2734
+ onSelect?.(path, index.get(path));
2735
+ },
2736
+ onFolderSelect: (path) => onSelect?.(`${path}/`, index.get(path)),
2737
+ storageKey: expansionKey,
2738
+ expandAll: query.trim().length > 0,
2739
+ label
2740
+ })
2602
2741
  })
2603
- }),
2604
- /* @__PURE__ */ jsx("div", {
2605
- className: cn$1("min-h-0 flex-1 overflow-y-auto px-1.5 py-1.5", (!plain || search && !isEmpty) && "border-t border-border"),
2606
- children: isEmpty ? emptyState !== void 0 ? emptyState : /* @__PURE__ */ jsx(EmptyState, {
2607
- icon: /* @__PURE__ */ jsx(FolderOpen, { className: "h-6 w-6" }),
2608
- title: "No files yet"
2609
- }) : visible.length === 0 ? /* @__PURE__ */ jsxs("div", {
2610
- className: "flex flex-col items-center gap-3 p-6 text-center",
2611
- children: [/* @__PURE__ */ jsxs("p", {
2612
- className: "max-w-xs break-words text-xs text-muted-foreground",
2613
- children: [
2614
- "No files match “",
2615
- query.trim(),
2616
- "”."
2617
- ]
2618
- }), /* @__PURE__ */ jsx(Button, {
2742
+ ]
2743
+ })
2744
+ }),
2745
+ split && /* @__PURE__ */ jsx(SidebarResizer, {
2746
+ sidebar,
2747
+ label
2748
+ }),
2749
+ (split || preview) && /* @__PURE__ */ jsx(FilesViewer, {
2750
+ preview,
2751
+ split,
2752
+ tabs: split && tabs,
2753
+ empty: isEmpty,
2754
+ backRef,
2755
+ onBack,
2756
+ onOpen: (path) => onSelect?.(path, index.get(path)),
2757
+ expand: expandable ? {
2758
+ expanded,
2759
+ toggle: () => setExpanded((value) => !value)
2760
+ } : void 0
2761
+ })
2762
+ ]
2763
+ });
2764
+ }
2765
+ const baseName = (path) => path.replace(/\/+$/, "").split("/").pop() || path;
2766
+ /** The editor side of the pane: open-file tabs, a File | Diff toggle, the full-screen control, and the open file. */
2767
+ function FilesViewer({ preview, split, tabs, empty, backRef, onBack, onOpen, expand }) {
2768
+ const path = preview?.path;
2769
+ const [open, setOpen] = useState(() => path ? [path] : []);
2770
+ useEffect(() => {
2771
+ if (path) setOpen((paths) => paths.includes(path) ? paths : [...paths, path]);
2772
+ }, [path]);
2773
+ const [mode, setMode] = useState("file");
2774
+ useEffect(() => setMode("file"), [path]);
2775
+ const close = (target) => {
2776
+ const at = open.indexOf(target);
2777
+ const rest = open.filter((entry) => entry !== target);
2778
+ setOpen(rest);
2779
+ if (target !== path) return;
2780
+ const neighbour = rest[Math.min(at, rest.length - 1)];
2781
+ if (neighbour) onOpen(neighbour);
2782
+ else onBack?.();
2783
+ };
2784
+ const compare = preview?.compare;
2785
+ const ExpandIcon = expand?.expanded ? Minimize2 : Maximize2;
2786
+ return /* @__PURE__ */ jsxs("div", {
2787
+ className: "flex min-h-0 min-w-0 flex-1 flex-col",
2788
+ children: [/* @__PURE__ */ jsxs("div", {
2789
+ className: "sticky top-0 z-10 flex min-h-10 shrink-0 items-center gap-2 border-b border-border bg-background px-2",
2790
+ children: [
2791
+ onBack && !split && preview && /* @__PURE__ */ jsxs(Button, {
2792
+ ref: backRef,
2793
+ type: "button",
2794
+ variant: "bare",
2795
+ onClick: onBack,
2796
+ className: "inline-flex shrink-0 items-center gap-1 rounded-md px-1 py-1 text-sm text-muted-foreground hover:text-foreground",
2797
+ children: [/* @__PURE__ */ jsx(ArrowLeft, {
2798
+ "aria-hidden": "true",
2799
+ className: "h-3.5 w-3.5"
2800
+ }), "Back to files"]
2801
+ }),
2802
+ tabs && open.length > 0 ? /* @__PURE__ */ jsx("div", {
2803
+ role: "tablist",
2804
+ "aria-label": "Open files",
2805
+ className: "flex min-w-0 flex-1 items-end self-stretch overflow-x-auto",
2806
+ children: open.map((entry) => {
2807
+ const selected = entry === path;
2808
+ return /* @__PURE__ */ jsxs("div", {
2809
+ title: entry,
2810
+ className: cn$1("group flex h-full shrink-0 items-center border-b-2 text-sm", selected ? "border-primary text-foreground" : "border-transparent text-muted-foreground hover:text-foreground"),
2811
+ children: [/* @__PURE__ */ jsx("button", {
2619
2812
  type: "button",
2620
- variant: "outline",
2621
- size: "sm",
2622
- onClick: () => setQuery(""),
2623
- children: "Clear search"
2813
+ role: "tab",
2814
+ "aria-selected": selected,
2815
+ onClick: () => onOpen(entry),
2816
+ className: "max-w-48 truncate py-2 pl-3 pr-1 outline-none focus-visible:underline",
2817
+ children: baseName(entry)
2818
+ }), /* @__PURE__ */ jsx("button", {
2819
+ type: "button",
2820
+ "aria-label": `Close ${baseName(entry)}`,
2821
+ onClick: () => close(entry),
2822
+ className: cn$1("mr-1 rounded p-1 hover:bg-muted", !selected && "opacity-0 group-hover:opacity-100 focus-visible:opacity-100"),
2823
+ children: /* @__PURE__ */ jsx(X, {
2824
+ "aria-hidden": true,
2825
+ className: "h-3 w-3"
2826
+ })
2624
2827
  })]
2625
- }) : /* @__PURE__ */ jsx(VaultTree, {
2626
- root: treeRoot,
2627
- selectedPath: normalizedSelection,
2628
- onSelect: (path) => {
2629
- if (treeRef.current) returnFocusRef.current = activeElementWithin(treeRef.current);
2630
- openedByReaderRef.current = true;
2631
- onSelect?.(path, index.get(path));
2632
- },
2633
- onFolderSelect: (path) => onSelect?.(`${path}/`, index.get(path)),
2634
- storageKey: expansionKey,
2635
- expandAll: query.trim().length > 0,
2636
- label
2637
- })
2828
+ }, entry);
2638
2829
  })
2639
- ]
2640
- })
2641
- }), preview && /* @__PURE__ */ jsxs("div", {
2642
- className: "flex min-h-0 flex-1 flex-col",
2643
- children: [/* @__PURE__ */ jsxs("div", {
2644
- className: "flex min-h-10 shrink-0 flex-wrap items-center gap-2 border-b border-border px-3 py-2",
2645
- children: [
2646
- onBack && /* @__PURE__ */ jsxs(Button, {
2647
- ref: backRef,
2830
+ }) : /* @__PURE__ */ jsx("span", {
2831
+ className: "min-w-0 flex-1 truncate px-1 text-sm text-muted-foreground",
2832
+ title: path,
2833
+ children: path
2834
+ }),
2835
+ compare && /* @__PURE__ */ jsx("div", {
2836
+ role: "group",
2837
+ "aria-label": "View",
2838
+ className: "flex shrink-0 rounded-md border border-border p-0.5 text-xs",
2839
+ children: ["file", "diff"].map((value) => /* @__PURE__ */ jsx("button", {
2648
2840
  type: "button",
2649
- variant: "bare",
2650
- onClick: onBack,
2651
- className: "inline-flex shrink-0 items-center gap-1 rounded-md px-1 py-1 text-sm text-muted-foreground hover:text-foreground",
2652
- children: [/* @__PURE__ */ jsx(ArrowLeft, {
2653
- "aria-hidden": "true",
2654
- className: "h-3.5 w-3.5"
2655
- }), "Back to files"]
2656
- }),
2657
- /* @__PURE__ */ jsx("span", {
2658
- className: "min-w-0 flex-1 truncate text-sm text-muted-foreground",
2659
- title: preview.path,
2660
- children: preview.path
2661
- }),
2662
- preview.actions && /* @__PURE__ */ jsx("div", {
2663
- className: "flex shrink-0 items-center gap-2",
2664
- children: preview.actions
2841
+ "aria-pressed": mode === value,
2842
+ onClick: () => setMode(value),
2843
+ className: cn$1("rounded px-2 py-1", mode === value ? "bg-muted text-foreground" : "text-muted-foreground hover:text-foreground"),
2844
+ children: value === "file" ? "File" : `Diff vs ${compare.label}`
2845
+ }, value))
2846
+ }),
2847
+ preview?.actions && /* @__PURE__ */ jsx("div", {
2848
+ className: "flex shrink-0 items-center gap-2",
2849
+ children: preview.actions
2850
+ }),
2851
+ expand && /* @__PURE__ */ jsx(Button, {
2852
+ type: "button",
2853
+ variant: "bare",
2854
+ onClick: expand.toggle,
2855
+ "aria-pressed": expand.expanded,
2856
+ "aria-label": expand.expanded ? "Exit full screen" : "Full screen",
2857
+ title: expand.expanded ? "Exit full screen (Esc)" : "Full screen",
2858
+ className: "shrink-0 rounded-md p-1.5 text-muted-foreground hover:bg-muted hover:text-foreground",
2859
+ children: /* @__PURE__ */ jsx(ExpandIcon, {
2860
+ "aria-hidden": true,
2861
+ className: "h-4 w-4"
2665
2862
  })
2666
- ]
2667
- }), /* @__PURE__ */ jsx("div", {
2668
- className: "min-h-0 flex-1 overflow-auto",
2669
- children: preview.content
2670
- })]
2863
+ })
2864
+ ]
2865
+ }), preview ? /* @__PURE__ */ jsx("div", {
2866
+ "data-primary-content": "",
2867
+ className: "flex min-h-0 flex-1 flex-col overflow-auto [&>*]:min-h-full",
2868
+ children: mode === "diff" && compare ? compare.content : preview.content
2869
+ }) : /* @__PURE__ */ jsx("div", {
2870
+ className: "flex min-h-0 min-w-0 flex-1 items-center justify-center p-6 text-sm text-muted-foreground",
2871
+ children: empty ? null : "Select a file to open it here."
2671
2872
  })]
2672
2873
  });
2673
2874
  }
@@ -1,2 +1,2 @@
1
- import { $ as StatusBar, A as createShellShouldRevalidate, B as SessionSidebarVariant, C as SandboxWorkbenchLayoutOptions, D as SandboxWorkbenchSessionProps, E as SandboxWorkbenchProps, F as SessionSidebarFilter, G as dispatchSessionCreated, H as SessionCreatedDetail, I as SessionSidebarGroupBy, J as RuntimePaneProps, K as useOptimisticSessionItems, L as SessionSidebarItem, M as formatRelativeAge, N as SessionSidebar, O as ShellShouldRevalidateArgs, P as SessionSidebarBadge, Q as ContextBadge, R as SessionSidebarLink, S as SandboxWorkbenchFileArtifact, T as SandboxWorkbenchOpenUIArtifact, U as SessionOptimisticController, V as SESSION_CREATED_EVENT, W as UseOptimisticSessionItemsOptions, X as TerminalPanel, Y as TerminalLine, Z as TerminalProps, _ as TaskBoardItem, _t as WorkspaceSurfaceTitle, a as WorkspaceFilesPane, at as DirectoryPaneProps, b as SandboxWorkbenchArtifact, bt as WorkspaceLayoutProps, c as ApprovalConfidenceStat, ct as ShellHeader, d as ApprovalQueueProps, dt as WorkspacePaneHeaderProps, et as StatusBarProps, f as CalendarEvent, ft as WorkspaceSurface, g as TaskBoardColumn, gt as WorkspaceSurfaceProps, h as TaskBoard, ht as WorkspaceSurfaceHeadingLevel, i as WorkspaceArtifactViewProps, it as DirectoryPane, j as shellShouldRevalidate, k as ShellShouldRevalidateOptions, l as ApprovalItem, lt as ShellHeaderProps, m as CalendarViewProps, mt as WorkspaceSurfaceFrameProps, n as ArtifactPaneProps, nt as StatusBanner, o as WorkspaceFilesPaneProps, ot as SHELL_HEADER_HEIGHT, p as CalendarView, pt as WorkspaceSurfaceFrame, q as RuntimePane, r as WorkspaceArtifactView, rt as StatusBannerProps, s as WorkspaceFilesPreview, st as SHELL_INSET_GUTTER, t as ArtifactPane, tt as BannerType, u as ApprovalQueue, ut as WorkspacePaneHeader, v as TaskBoardProps, vt as WorkspaceSurfaceTitleProps, w as SandboxWorkbenchMarkdownArtifact, x as SandboxWorkbenchCustomArtifact, y as SandboxWorkbench, yt as WorkspaceLayout, z as SessionSidebarProps } from "./index-B1oEiBks.js";
1
+ import { $ as StatusBar, A as createShellShouldRevalidate, B as SessionSidebarVariant, C as SandboxWorkbenchLayoutOptions, D as SandboxWorkbenchSessionProps, E as SandboxWorkbenchProps, F as SessionSidebarFilter, G as dispatchSessionCreated, H as SessionCreatedDetail, I as SessionSidebarGroupBy, J as RuntimePaneProps, K as useOptimisticSessionItems, L as SessionSidebarItem, M as formatRelativeAge, N as SessionSidebar, O as ShellShouldRevalidateArgs, P as SessionSidebarBadge, Q as ContextBadge, R as SessionSidebarLink, S as SandboxWorkbenchFileArtifact, T as SandboxWorkbenchOpenUIArtifact, U as SessionOptimisticController, V as SESSION_CREATED_EVENT, W as UseOptimisticSessionItemsOptions, X as TerminalPanel, Y as TerminalLine, Z as TerminalProps, _ as TaskBoardItem, _t as WorkspaceSurfaceTitle, a as WorkspaceFilesPane, at as DirectoryPaneProps, b as SandboxWorkbenchArtifact, bt as WorkspaceLayoutProps, c as ApprovalConfidenceStat, ct as ShellHeader, d as ApprovalQueueProps, dt as WorkspacePaneHeaderProps, et as StatusBarProps, f as CalendarEvent, ft as WorkspaceSurface, g as TaskBoardColumn, gt as WorkspaceSurfaceProps, h as TaskBoard, ht as WorkspaceSurfaceHeadingLevel, i as WorkspaceArtifactViewProps, it as DirectoryPane, j as shellShouldRevalidate, k as ShellShouldRevalidateOptions, l as ApprovalItem, lt as ShellHeaderProps, m as CalendarViewProps, mt as WorkspaceSurfaceFrameProps, n as ArtifactPaneProps, nt as StatusBanner, o as WorkspaceFilesPaneProps, ot as SHELL_HEADER_HEIGHT, p as CalendarView, pt as WorkspaceSurfaceFrame, q as RuntimePane, r as WorkspaceArtifactView, rt as StatusBannerProps, s as WorkspaceFilesPreview, st as SHELL_INSET_GUTTER, t as ArtifactPane, tt as BannerType, u as ApprovalQueue, ut as WorkspacePaneHeader, v as TaskBoardProps, vt as WorkspaceSurfaceTitleProps, w as SandboxWorkbenchMarkdownArtifact, x as SandboxWorkbenchCustomArtifact, y as SandboxWorkbench, yt as WorkspaceLayout, z as SessionSidebarProps } from "./index-F42BOxUM.js";
2
2
  export { type ApprovalConfidenceStat, type ApprovalItem, ApprovalQueue, type ApprovalQueueProps, ArtifactPane, type ArtifactPaneProps, type BannerType, type CalendarEvent, CalendarView, type CalendarViewProps, type ContextBadge, DirectoryPane, type DirectoryPaneProps, RuntimePane, type RuntimePaneProps, SESSION_CREATED_EVENT, SHELL_HEADER_HEIGHT, SHELL_INSET_GUTTER, SandboxWorkbench, type SandboxWorkbenchArtifact, type SandboxWorkbenchCustomArtifact, type SandboxWorkbenchFileArtifact, type SandboxWorkbenchLayoutOptions, type SandboxWorkbenchMarkdownArtifact, type SandboxWorkbenchOpenUIArtifact, type SandboxWorkbenchProps, type SandboxWorkbenchSessionProps, type SessionCreatedDetail, type SessionOptimisticController, SessionSidebar, type SessionSidebarBadge, type SessionSidebarFilter, type SessionSidebarGroupBy, type SessionSidebarItem, type SessionSidebarLink, type SessionSidebarProps, type SessionSidebarVariant, ShellHeader, type ShellHeaderProps, type ShellShouldRevalidateArgs, type ShellShouldRevalidateOptions, StatusBanner, type StatusBannerProps, StatusBar, type StatusBarProps, TaskBoard, type TaskBoardColumn, type TaskBoardItem, type TaskBoardProps, type TerminalLine, TerminalPanel, type TerminalProps, type UseOptimisticSessionItemsOptions, WorkspaceArtifactView, type WorkspaceArtifactViewProps, WorkspaceFilesPane, type WorkspaceFilesPaneProps, type WorkspaceFilesPreview, WorkspaceLayout, type WorkspaceLayoutProps, WorkspacePaneHeader, type WorkspacePaneHeaderProps, WorkspaceSurface, WorkspaceSurfaceFrame, type WorkspaceSurfaceFrameProps, type WorkspaceSurfaceHeadingLevel, type WorkspaceSurfaceProps, WorkspaceSurfaceTitle, type WorkspaceSurfaceTitleProps, createShellShouldRevalidate, dispatchSessionCreated, formatRelativeAge, shellShouldRevalidate, useOptimisticSessionItems };
package/dist/workspace.js CHANGED
@@ -1,3 +1,3 @@
1
1
  import { i as WorkspacePaneHeader, n as SHELL_INSET_GUTTER, r as ShellHeader, t as SHELL_HEADER_HEIGHT } from "./shell-header-CwqDzC4I.js";
2
- import { C as WorkspaceSurfaceTitle, S as WorkspaceSurfaceFrame, _ as StatusBar, a as TaskBoard, b as WorkspaceLayout, c as createShellShouldRevalidate, d as SESSION_CREATED_EVENT, f as dispatchSessionCreated, g as TerminalPanel, h as RuntimePane, i as CalendarView, l as shellShouldRevalidate, m as formatRelativeAge, n as WorkspaceFilesPane, o as SandboxWorkbench, p as useOptimisticSessionItems, r as ApprovalQueue, s as WorkspaceArtifactView, t as ArtifactPane, u as SessionSidebar, v as StatusBanner, x as WorkspaceSurface, y as DirectoryPane } from "./workspace-Cpgyw5N7.js";
2
+ import { C as WorkspaceSurfaceTitle, S as WorkspaceSurfaceFrame, _ as StatusBar, a as TaskBoard, b as WorkspaceLayout, c as createShellShouldRevalidate, d as SESSION_CREATED_EVENT, f as dispatchSessionCreated, g as TerminalPanel, h as RuntimePane, i as CalendarView, l as shellShouldRevalidate, m as formatRelativeAge, n as WorkspaceFilesPane, o as SandboxWorkbench, p as useOptimisticSessionItems, r as ApprovalQueue, s as WorkspaceArtifactView, t as ArtifactPane, u as SessionSidebar, v as StatusBanner, x as WorkspaceSurface, y as DirectoryPane } from "./workspace-DRhi6S3o.js";
3
3
  export { ApprovalQueue, ArtifactPane, CalendarView, DirectoryPane, RuntimePane, SESSION_CREATED_EVENT, SHELL_HEADER_HEIGHT, SHELL_INSET_GUTTER, SandboxWorkbench, SessionSidebar, ShellHeader, StatusBanner, StatusBar, TaskBoard, TerminalPanel, WorkspaceArtifactView, WorkspaceFilesPane, WorkspaceLayout, WorkspacePaneHeader, WorkspaceSurface, WorkspaceSurfaceFrame, WorkspaceSurfaceTitle, createShellShouldRevalidate, dispatchSessionCreated, formatRelativeAge, shellShouldRevalidate, useOptimisticSessionItems };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/sandbox-ui",
3
- "version": "0.137.2",
3
+ "version": "0.138.0",
4
4
  "license": "MIT",
5
5
  "description": "Unified UI component library for Tangle Sandbox — primitives, chat, dashboard, terminal, editor, and workspace components",
6
6
  "repository": {
@@ -47,6 +47,10 @@
47
47
  "import": "./dist/workbench/embedded-app.js",
48
48
  "types": "./dist/workbench/embedded-app.d.ts"
49
49
  },
50
+ "./workbench/diff": {
51
+ "import": "./dist/workbench/diff.js",
52
+ "types": "./dist/workbench/diff.d.ts"
53
+ },
50
54
  "./openui": {
51
55
  "import": "./dist/openui.js",
52
56
  "types": "./dist/openui.d.ts"
@@ -134,6 +138,7 @@
134
138
  "@nanostores/react": ">=0.8.4 <2.0.0",
135
139
  "@tangle-network/agent-interface": "^1.0.0 || ^2.0.0 || ^3.0.0 || ^5.2.0",
136
140
  "@tangle-network/brand": "^1.26.2",
141
+ "@tangle-network/charts": "^0.20.3",
137
142
  "@tangle-network/ui": "^11.42.0",
138
143
  "@tanstack/react-query": "^5.0.0",
139
144
  "@tiptap/core": ">=3.20.4 <4.0.0",
@@ -153,6 +158,9 @@
153
158
  "yjs": "^13.6.0"
154
159
  },
155
160
  "peerDependenciesMeta": {
161
+ "@tangle-network/charts": {
162
+ "optional": true
163
+ },
156
164
  "@hocuspocus/provider": {
157
165
  "optional": true
158
166
  },
@@ -229,6 +237,7 @@
229
237
  "@tailwindcss/postcss": "^4.3.3",
230
238
  "@tangle-network/agent-interface": "5.7.1",
231
239
  "@tangle-network/brand": "1.26.2",
240
+ "@tangle-network/charts": "^0.20.3",
232
241
  "@tangle-network/ui": "11.42.0",
233
242
  "@tanstack/react-query": "^5.104.5",
234
243
  "@testing-library/dom": "^10.4.2",
@@ -256,7 +265,6 @@
256
265
  "postcss": "^8.5.31",
257
266
  "react": "^19.3.1",
258
267
  "react-dom": "^19.3.1",
259
- "react-syntax-highlighter": "^16.1.2",
260
268
  "storybook": "^10.6.3",
261
269
  "tailwindcss": "^4.3.3",
262
270
  "tsdown": "0.23.1",