@tangle-network/sandbox-ui 0.111.2 → 0.112.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.
@@ -325,8 +325,8 @@ function GitStatusBadge({ status }) {
325
325
  }
326
326
  );
327
327
  }
328
- function DiffStatsBadge({ stats }) {
329
- return /* @__PURE__ */ jsxs2("span", { className: "inline-flex items-center gap-1.5 font-mono text-xs", children: [
328
+ function DiffStatsBadge({ stats, className }) {
329
+ return /* @__PURE__ */ jsxs2("span", { className: cn("inline-flex items-center gap-1.5 font-mono text-xs", className), children: [
330
330
  /* @__PURE__ */ jsxs2("span", { className: "text-[var(--code-success,#10B981)]", children: [
331
331
  "+",
332
332
  stats.added
@@ -7280,15 +7280,19 @@ var DIFF_OPTIONS = {
7280
7280
  themeType: "dark",
7281
7281
  stickyHeader: true
7282
7282
  };
7283
- function DiffView({ filename, baseline, current, className }) {
7283
+ function DiffView({ filename, baseline, current, showFileHeader = true, className }) {
7284
7284
  const patch = React7.useMemo(
7285
7285
  () => buildUnifiedPatch(filename, baseline, current),
7286
7286
  [filename, baseline, current]
7287
7287
  );
7288
+ const options = React7.useMemo(
7289
+ () => showFileHeader ? DIFF_OPTIONS : { ...DIFF_OPTIONS, disableFileHeader: true },
7290
+ [showFileHeader]
7291
+ );
7288
7292
  if (!patch) {
7289
7293
  return /* @__PURE__ */ jsx7("div", { className: cn("flex h-full items-center justify-center bg-surface-container", className), children: /* @__PURE__ */ jsx7("p", { className: "text-sm text-muted-foreground", children: "No changes \u2014 baseline and working copy are identical." }) });
7290
7294
  }
7291
- return /* @__PURE__ */ jsx7("div", { className: cn("h-full min-h-0 overflow-auto bg-surface-container text-[13px]", className), children: /* @__PURE__ */ jsx7(PatchDiff, { patch, options: DIFF_OPTIONS }) });
7295
+ return /* @__PURE__ */ jsx7("div", { className: cn("h-full min-h-0 overflow-auto bg-surface-container text-[13px]", className), children: /* @__PURE__ */ jsx7(PatchDiff, { patch, options }) });
7292
7296
  }
7293
7297
 
7294
7298
  // src/workbench/preview-view.tsx
@@ -7511,9 +7515,322 @@ function SandboxArtifactPane({
7511
7515
  );
7512
7516
  }
7513
7517
 
7518
+ // src/workbench/changes-pane.tsx
7519
+ import * as React10 from "react";
7520
+ import { GitBranch, RefreshCw } from "lucide-react";
7521
+ import { Button, Skeleton, Textarea } from "@tangle-network/ui/primitives";
7522
+ import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
7523
+ var BORDER = "border-[var(--md3-outline-variant)]";
7524
+ function splitPath(path) {
7525
+ const at = path.lastIndexOf("/");
7526
+ if (at < 0) return { dir: "", base: path };
7527
+ return { dir: path.slice(0, at + 1), base: path.slice(at + 1) };
7528
+ }
7529
+ function resolveContents(file) {
7530
+ const { status, baseline, current } = file;
7531
+ if (status === "added" || status === "untracked") {
7532
+ return current == null ? null : { baseline: baseline ?? "", current };
7533
+ }
7534
+ if (status === "deleted") {
7535
+ return baseline == null ? null : { baseline, current: current ?? "" };
7536
+ }
7537
+ return baseline == null || current == null ? null : { baseline, current };
7538
+ }
7539
+ function errorText(err) {
7540
+ if (err instanceof Error && err.message) return err.message;
7541
+ return String(err);
7542
+ }
7543
+ var SKELETON_ROWS = [0, 1, 2, 3];
7544
+ function ChangesPane({
7545
+ branch,
7546
+ ahead = 0,
7547
+ behind = 0,
7548
+ files,
7549
+ selectedPath,
7550
+ onSelectFile,
7551
+ loading = false,
7552
+ error,
7553
+ onRefresh,
7554
+ onCommit,
7555
+ onPush,
7556
+ busy,
7557
+ onOpenFile,
7558
+ className
7559
+ }) {
7560
+ const idPrefix = React10.useId();
7561
+ const listRef = React10.useRef(null);
7562
+ const rowRefs = React10.useRef(/* @__PURE__ */ new Map());
7563
+ const [message, setMessage] = React10.useState("");
7564
+ const [actionError, setActionError] = React10.useState(null);
7565
+ const [pending, setPending] = React10.useState(null);
7566
+ const inFlight = busy ?? pending;
7567
+ const totals = React10.useMemo(() => {
7568
+ let added = 0;
7569
+ let removed = 0;
7570
+ for (const f of files) {
7571
+ added += f.additions;
7572
+ removed += f.deletions;
7573
+ }
7574
+ return { added, removed };
7575
+ }, [files]);
7576
+ const selectedIndex = selectedPath == null ? -1 : files.findIndex((f) => f.path === selectedPath);
7577
+ const selected = selectedIndex >= 0 ? files[selectedIndex] : void 0;
7578
+ React10.useEffect(() => {
7579
+ const list = listRef.current;
7580
+ if (!list || selected == null) return;
7581
+ if (!list.contains(document.activeElement)) return;
7582
+ rowRefs.current.get(selected.path)?.focus();
7583
+ }, [selected]);
7584
+ const handleListKeyDown = React10.useCallback(
7585
+ (event) => {
7586
+ if (files.length === 0 || !onSelectFile) return;
7587
+ const last = files.length - 1;
7588
+ let next = null;
7589
+ switch (event.key) {
7590
+ case "ArrowDown":
7591
+ next = selectedIndex < 0 ? 0 : Math.min(selectedIndex + 1, last);
7592
+ break;
7593
+ case "ArrowUp":
7594
+ next = selectedIndex < 0 ? last : Math.max(selectedIndex - 1, 0);
7595
+ break;
7596
+ case "Home":
7597
+ next = 0;
7598
+ break;
7599
+ case "End":
7600
+ next = last;
7601
+ break;
7602
+ case "Enter":
7603
+ case " ":
7604
+ next = selectedIndex < 0 ? 0 : selectedIndex;
7605
+ break;
7606
+ default:
7607
+ return;
7608
+ }
7609
+ event.preventDefault();
7610
+ const file = files[next];
7611
+ if (file) onSelectFile(file.path);
7612
+ },
7613
+ [files, onSelectFile, selectedIndex]
7614
+ );
7615
+ const runCommit = React10.useCallback(async () => {
7616
+ const text = message.trim();
7617
+ if (!onCommit || !text || inFlight) return;
7618
+ setActionError(null);
7619
+ setPending("commit");
7620
+ try {
7621
+ await onCommit(text);
7622
+ setMessage("");
7623
+ } catch (err) {
7624
+ setActionError(errorText(err));
7625
+ } finally {
7626
+ setPending(null);
7627
+ }
7628
+ }, [message, onCommit, inFlight]);
7629
+ const runPush = React10.useCallback(async () => {
7630
+ if (!onPush || inFlight) return;
7631
+ setActionError(null);
7632
+ setPending("push");
7633
+ try {
7634
+ await onPush();
7635
+ } catch (err) {
7636
+ setActionError(errorText(err));
7637
+ } finally {
7638
+ setPending(null);
7639
+ }
7640
+ }, [onPush, inFlight]);
7641
+ const handleMessageKeyDown = React10.useCallback(
7642
+ (event) => {
7643
+ if (event.key === "Enter" && (event.metaKey || event.ctrlKey)) {
7644
+ event.preventDefault();
7645
+ void runCommit();
7646
+ }
7647
+ },
7648
+ [runCommit]
7649
+ );
7650
+ const canCommit = files.length > 0 && message.trim().length > 0 && inFlight == null;
7651
+ const canPush = ahead > 0 && inFlight == null;
7652
+ const contents = React10.useMemo(() => selected ? resolveContents(selected) : null, [selected]);
7653
+ const stats = React10.useMemo(
7654
+ () => contents ? computeDiffStats(contents.baseline, contents.current) : void 0,
7655
+ [contents]
7656
+ );
7657
+ const patch = React10.useMemo(
7658
+ () => selected && contents ? buildUnifiedPatch(selected.path, contents.baseline, contents.current) : void 0,
7659
+ [selected, contents]
7660
+ );
7661
+ const showList = !loading && files.length > 0;
7662
+ const showEmpty = !loading && files.length === 0 && !error;
7663
+ return /* @__PURE__ */ jsxs7("div", { className: cn("flex h-full min-h-0 flex-col text-foreground", className), children: [
7664
+ /* @__PURE__ */ jsxs7(
7665
+ "div",
7666
+ {
7667
+ className: cn(
7668
+ "flex min-h-[38px] shrink-0 items-center gap-2 border-b bg-surface-container-low px-3 py-1",
7669
+ BORDER
7670
+ ),
7671
+ children: [
7672
+ /* @__PURE__ */ jsx10(GitBranch, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground", "aria-hidden": true }),
7673
+ loading && branch == null ? /* @__PURE__ */ jsx10(Skeleton, { className: "h-4 w-24 rounded-full" }) : branch == null ? /* @__PURE__ */ jsx10("span", { className: "text-xs text-muted-foreground", children: "no commits yet" }) : /* @__PURE__ */ jsx10("span", { className: "truncate rounded-full bg-surface-container-high px-2 py-0.5 font-mono text-xs text-foreground", children: branch }),
7674
+ (ahead > 0 || behind > 0) && /* @__PURE__ */ jsxs7("span", { className: "flex items-center gap-1.5 font-mono text-xs text-muted-foreground", children: [
7675
+ ahead > 0 && /* @__PURE__ */ jsxs7("span", { "aria-label": `${ahead} ahead`, children: [
7676
+ "\u2191",
7677
+ ahead
7678
+ ] }),
7679
+ behind > 0 && /* @__PURE__ */ jsxs7("span", { "aria-label": `${behind} behind`, children: [
7680
+ "\u2193",
7681
+ behind
7682
+ ] })
7683
+ ] }),
7684
+ /* @__PURE__ */ jsxs7("div", { className: "ml-auto flex items-center gap-3", children: [
7685
+ files.length > 0 && /* @__PURE__ */ jsx10(DiffStatsBadge, { stats: totals }),
7686
+ onRefresh && /* @__PURE__ */ jsx10(
7687
+ "button",
7688
+ {
7689
+ type: "button",
7690
+ onClick: onRefresh,
7691
+ "aria-label": "Refresh",
7692
+ className: "rounded-md p-1 text-muted-foreground transition-colors hover:bg-surface-container-high hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary/60",
7693
+ children: /* @__PURE__ */ jsx10(RefreshCw, { className: cn("h-3.5 w-3.5", loading && "animate-spin") })
7694
+ }
7695
+ )
7696
+ ] })
7697
+ ]
7698
+ }
7699
+ ),
7700
+ /* @__PURE__ */ jsxs7("div", { className: cn("flex max-h-[40%] shrink-0 flex-col border-b", BORDER), "aria-busy": loading || void 0, children: [
7701
+ error && /* @__PURE__ */ jsx10("p", { role: "alert", className: "px-3 py-2 text-xs text-[var(--surface-danger-text)]", children: error }),
7702
+ loading && /* @__PURE__ */ jsxs7("div", { className: "space-y-1 px-3 py-2", "data-testid": "changes-skeleton", children: [
7703
+ /* @__PURE__ */ jsx10("span", { className: "sr-only", children: "Loading changes\u2026" }),
7704
+ SKELETON_ROWS.map((row) => /* @__PURE__ */ jsx10(Skeleton, { className: "h-6 w-full" }, row))
7705
+ ] }),
7706
+ showEmpty && /* @__PURE__ */ jsx10("p", { className: "px-3 py-6 text-center text-xs text-muted-foreground", children: "No changes in the working tree." }),
7707
+ showList && /* @__PURE__ */ jsx10(
7708
+ "ul",
7709
+ {
7710
+ ref: listRef,
7711
+ role: "listbox",
7712
+ "aria-label": "Changed files",
7713
+ "aria-activedescendant": selected ? `${idPrefix}-${selectedIndex}` : void 0,
7714
+ onKeyDown: handleListKeyDown,
7715
+ className: "min-h-0 overflow-y-auto py-1 focus:outline-none",
7716
+ children: files.map((file, index) => {
7717
+ const isSelected = index === selectedIndex;
7718
+ const { dir, base } = splitPath(file.path);
7719
+ const tabbable = isSelected || selectedIndex < 0 && index === 0;
7720
+ return /* @__PURE__ */ jsxs7(
7721
+ "li",
7722
+ {
7723
+ id: `${idPrefix}-${index}`,
7724
+ ref: (el) => {
7725
+ if (el) rowRefs.current.set(file.path, el);
7726
+ else rowRefs.current.delete(file.path);
7727
+ },
7728
+ role: "option",
7729
+ "aria-selected": isSelected,
7730
+ tabIndex: tabbable ? 0 : -1,
7731
+ onClick: () => onSelectFile?.(file.path),
7732
+ className: cn(
7733
+ "flex cursor-pointer items-center gap-2 px-3 py-1.5 text-xs transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-primary/60",
7734
+ isSelected ? "bg-surface-container-high" : "hover:bg-surface-container"
7735
+ ),
7736
+ children: [
7737
+ /* @__PURE__ */ jsxs7("span", { className: "min-w-0 flex-1 truncate font-mono", title: file.path, children: [
7738
+ dir && /* @__PURE__ */ jsx10("span", { className: "text-muted-foreground", children: dir }),
7739
+ /* @__PURE__ */ jsx10("span", { className: "text-foreground", children: base })
7740
+ ] }),
7741
+ /* @__PURE__ */ jsx10(GitStatusBadge, { status: file.status }),
7742
+ /* @__PURE__ */ jsx10(
7743
+ DiffStatsBadge,
7744
+ {
7745
+ stats: { added: file.additions, removed: file.deletions },
7746
+ className: "w-[5.5rem] justify-end tabular-nums"
7747
+ }
7748
+ )
7749
+ ]
7750
+ },
7751
+ file.path
7752
+ );
7753
+ })
7754
+ }
7755
+ )
7756
+ ] }),
7757
+ /* @__PURE__ */ jsx10("div", { className: "flex min-h-0 flex-1 flex-col", children: selected ? /* @__PURE__ */ jsxs7(Fragment2, { children: [
7758
+ /* @__PURE__ */ jsx10(PanelHeader, { filename: selected.path, gitStatus: selected.status, stats, copyText: patch, children: onOpenFile && /* @__PURE__ */ jsx10(
7759
+ Button,
7760
+ {
7761
+ type: "button",
7762
+ variant: "outline",
7763
+ size: "sm",
7764
+ className: "h-7 px-2",
7765
+ onClick: () => onOpenFile(selected.path),
7766
+ children: "Open"
7767
+ }
7768
+ ) }),
7769
+ contents ? /* @__PURE__ */ jsx10(
7770
+ DiffView,
7771
+ {
7772
+ filename: selected.path,
7773
+ baseline: contents.baseline,
7774
+ current: contents.current,
7775
+ showFileHeader: false,
7776
+ className: "flex-1"
7777
+ }
7778
+ ) : /* @__PURE__ */ jsx10("p", { className: "px-3 py-3 text-xs text-muted-foreground", role: "status", children: "Loading diff\u2026" })
7779
+ ] }) : showList && /* @__PURE__ */ jsx10("p", { className: "px-3 py-3 text-xs text-muted-foreground", children: "Select a file to read its diff." }) }),
7780
+ (onCommit || onPush) && /* @__PURE__ */ jsxs7("div", { className: cn("shrink-0 space-y-2 border-t bg-surface-container-low p-3", BORDER), children: [
7781
+ onCommit && /* @__PURE__ */ jsx10(
7782
+ Textarea,
7783
+ {
7784
+ "aria-label": "Commit message",
7785
+ placeholder: "Commit message",
7786
+ rows: 2,
7787
+ value: message,
7788
+ onChange: (event) => setMessage(event.target.value),
7789
+ onKeyDown: handleMessageKeyDown,
7790
+ disabled: inFlight != null,
7791
+ className: "min-h-[3.5rem] resize-none px-3 py-2 text-xs"
7792
+ }
7793
+ ),
7794
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2", children: [
7795
+ onCommit && /* @__PURE__ */ jsx10(
7796
+ Button,
7797
+ {
7798
+ type: "button",
7799
+ size: "sm",
7800
+ disabled: !canCommit,
7801
+ loading: inFlight === "commit",
7802
+ onClick: () => void runCommit(),
7803
+ children: "Commit"
7804
+ }
7805
+ ),
7806
+ onPush && /* @__PURE__ */ jsxs7(
7807
+ Button,
7808
+ {
7809
+ type: "button",
7810
+ size: "sm",
7811
+ variant: "secondary",
7812
+ disabled: !canPush,
7813
+ loading: inFlight === "push",
7814
+ onClick: () => void runPush(),
7815
+ children: [
7816
+ "Push",
7817
+ ahead > 0 && /* @__PURE__ */ jsxs7("span", { className: "font-mono text-muted-foreground", children: [
7818
+ "\u2191",
7819
+ ahead
7820
+ ] })
7821
+ ]
7822
+ }
7823
+ )
7824
+ ] }),
7825
+ actionError && /* @__PURE__ */ jsx10("p", { role: "alert", className: "text-xs text-[var(--surface-danger-text)]", children: actionError })
7826
+ ] })
7827
+ ] });
7828
+ }
7829
+
7514
7830
  export {
7515
7831
  PillTabs,
7516
7832
  GitStatusBadge,
7833
+ DiffStatsBadge,
7517
7834
  PanelHeader,
7518
7835
  resolveLanguage,
7519
7836
  BRAND_PRISM_THEME,
@@ -7524,5 +7841,6 @@ export {
7524
7841
  buildUnifiedPatch,
7525
7842
  DiffView,
7526
7843
  PreviewView,
7527
- SandboxArtifactPane
7844
+ SandboxArtifactPane,
7845
+ ChangesPane
7528
7846
  };
package/dist/globals.css CHANGED
@@ -847,6 +847,9 @@
847
847
  .h-\[640px\] {
848
848
  height: 640px;
849
849
  }
850
+ .h-\[720px\] {
851
+ height: 720px;
852
+ }
850
853
  .h-\[var\(--avatar-size\)\] {
851
854
  height: var(--avatar-size);
852
855
  }
@@ -898,6 +901,9 @@
898
901
  .max-h-96 {
899
902
  max-height: calc(var(--spacing) * 96);
900
903
  }
904
+ .max-h-\[40\%\] {
905
+ max-height: 40%;
906
+ }
901
907
  .max-h-\[70vh\] {
902
908
  max-height: 70vh;
903
909
  }
@@ -919,6 +925,9 @@
919
925
  .min-h-\[2px\] {
920
926
  min-height: 2px;
921
927
  }
928
+ .min-h-\[3\.5rem\] {
929
+ min-height: 3.5rem;
930
+ }
922
931
  .min-h-\[6rem\] {
923
932
  min-height: 6rem;
924
933
  }
@@ -1102,6 +1111,9 @@
1102
1111
  .w-\[3px\] {
1103
1112
  width: 3px;
1104
1113
  }
1114
+ .w-\[5\.5rem\] {
1115
+ width: 5.5rem;
1116
+ }
1105
1117
  .w-\[17px\] {
1106
1118
  width: 17px;
1107
1119
  }
@@ -1135,6 +1147,9 @@
1135
1147
  .w-\[420px\] {
1136
1148
  width: 420px;
1137
1149
  }
1150
+ .w-\[440px\] {
1151
+ width: 440px;
1152
+ }
1138
1153
  .w-\[480px\] {
1139
1154
  width: 480px;
1140
1155
  }
@@ -4124,6 +4139,9 @@
4124
4139
  .hover\:bg-red-700:hover {
4125
4140
  background-color: var(--color-red-700);
4126
4141
  }
4142
+ .hover\:bg-surface-container:hover {
4143
+ background-color: var(--color-surface-container);
4144
+ }
4127
4145
  .hover\:bg-surface-container-high:hover {
4128
4146
  background-color: var(--color-surface-container-high);
4129
4147
  }
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ export { ArtifactPane, ArtifactPaneProps, Avatar, AvatarFallback, AvatarImage, B
2
2
  export { L as Logo, a as LogoProps, T as TangleKnot } from './logo-CaIvk3J1.js';
3
3
  export { CodeBlock as CodeBlockDisplay, Markdown, MarkdownProps } from '@tangle-network/ui/markdown';
4
4
  export { AgentWorkbench, ApprovalConfidenceStat, ApprovalItem, ApprovalQueue, ApprovalQueueProps, AuditCheck, AuditResults, AuditResultsProps, BannerType, CalendarEvent, CalendarView, CalendarViewProps, ContextBadge, DirectoryPane, DirectoryPaneProps, FormAudit, RuntimePane, RuntimePaneProps, SESSION_CREATED_EVENT, SandboxWorkbench, SandboxWorkbenchArtifact, SandboxWorkbenchCustomArtifact, SandboxWorkbenchFileArtifact, SandboxWorkbenchLayoutOptions, SandboxWorkbenchMarkdownArtifact, SandboxWorkbenchOpenUIArtifact, SandboxWorkbenchProps, SandboxWorkbenchSessionProps, SessionActivityMonitor, SessionActivityMonitorProps, SessionCreatedDetail, SessionOptimisticController, SessionSidebar, SessionSidebarBadge, SessionSidebarFilter, SessionSidebarItem, SessionSidebarLink, SessionSidebarProps, ShellShouldRevalidateArgs, ShellShouldRevalidateOptions, StatusBanner, StatusBannerProps, StatusBar, StatusBarProps, TaskBoard, TaskBoardColumn, TaskBoardItem, TaskBoardProps, TerminalLine, TerminalPanel, TerminalProps, UseOptimisticSessionItemsOptions, WorkspaceLayout, WorkspaceLayoutProps, WorkspacePaneHeader, WorkspacePaneHeaderProps, createShellShouldRevalidate, dispatchSessionCreated, shellShouldRevalidate, useOptimisticSessionItems } from './workspace.js';
5
- export { ArtifactView, BRAND_PRISM_THEME, CodeSurface, CodeSurfaceProps, CodeView, CodeViewProps, DiffStats, DiffView, DiffViewProps, FileBreadcrumb, FileBreadcrumbProps, GitStatusBadge, PanelHeader, PanelHeaderProps, PillTabItem, PillTabs, PillTabsProps, PreviewView, PreviewViewProps, SandboxArtifact, SandboxArtifactPane, SandboxArtifactPaneProps, SandboxArtifactTerminal, buildUnifiedPatch, computeDiffStats, resolveLanguage } from './workbench.js';
5
+ export { ArtifactView, BRAND_PRISM_THEME, ChangedFile, ChangesPane, ChangesPaneProps, CodeSurface, CodeSurfaceProps, CodeView, CodeViewProps, DiffStats, DiffStatsBadge, DiffView, DiffViewProps, FileBreadcrumb, FileBreadcrumbProps, GitStatusBadge, PanelHeader, PanelHeaderProps, PillTabItem, PillTabs, PillTabsProps, PreviewView, PreviewViewProps, SandboxArtifact, SandboxArtifactPane, SandboxArtifactPaneProps, SandboxArtifactTerminal, buildUnifiedPatch, computeDiffStats, resolveLanguage } from './workbench.js';
6
6
  export { OpenUIAction, OpenUIActionsNode, OpenUIArtifactRenderer, OpenUIArtifactRendererProps, OpenUIBadgeNode, OpenUICardNode, OpenUICodeNode, OpenUIComponentNode, OpenUIGridNode, OpenUIHeadingNode, OpenUIKeyValueNode, OpenUIMarkdownNode, OpenUIPrimitive, OpenUISeparatorNode, OpenUIStackNode, OpenUIStatNode, OpenUITableNode, OpenUITextNode } from '@tangle-network/ui/openui';
7
7
  export { AgentTimeline, AgentTimelineArtifactItem, AgentTimelineCustomItem, AgentTimelineItem, AgentTimelineMessageItem, AgentTimelineProps, AgentTimelineStatusItem, AgentTimelineTone, AgentTimelineToolGroupItem, AgentTimelineToolItem, ChatContainer, ChatContainerProps, ChatMessage, ChatMessageProps, MessageList, MessageListProps, MessageRole, ThinkingIndicator, ThinkingIndicatorProps, UserMessage, UserMessageProps } from '@tangle-network/ui/chat';
8
8
  export { AgentProfileCapability, AgentProfileDraft, AgentProfileOption, AgentProfilePicker, AgentProfilePickerProps, ArtifactAgentDock, ArtifactAgentDockProps, ArtifactAgentDockTransport, ArtifactDockMessage, ArtifactDockStreamEvent, ArtifactKind, ArtifactScope, DEFAULT_REASONING_LEVEL_OPTIONS, HARNESS_REASONING_OPTIONS, ReasoningLevel, ReasoningLevelOption, ReasoningLevelPicker, ReasoningLevelPickerProps, createFetchTransport } from './chat.js';
package/dist/index.js CHANGED
@@ -135,8 +135,10 @@ import {
135
135
  } from "./chunk-D5VXCWUG.js";
136
136
  import {
137
137
  BRAND_PRISM_THEME,
138
+ ChangesPane,
138
139
  CodeSurface,
139
140
  CodeView,
141
+ DiffStatsBadge,
140
142
  DiffView,
141
143
  FileBreadcrumb,
142
144
  GitStatusBadge,
@@ -147,7 +149,7 @@ import {
147
149
  buildUnifiedPatch,
148
150
  computeDiffStats,
149
151
  resolveLanguage
150
- } from "./chunk-5M67IFVI.js";
152
+ } from "./chunk-ISTJTUHJ.js";
151
153
  import {
152
154
  FileArtifactPane,
153
155
  FilePreview,
@@ -383,6 +385,7 @@ export {
383
385
  CardFooter,
384
386
  CardHeader,
385
387
  CardTitle,
388
+ ChangesPane,
386
389
  ChatContainer,
387
390
  ChatMessage,
388
391
  ClusterStatusBar,
@@ -406,6 +409,7 @@ export {
406
409
  DialogTitle,
407
410
  DialogTrigger,
408
411
  DiffPreview,
412
+ DiffStatsBadge,
409
413
  DiffView,
410
414
  DirectoryPane,
411
415
  DropZone,
package/dist/styles.css CHANGED
@@ -847,6 +847,9 @@
847
847
  .h-\[640px\] {
848
848
  height: 640px;
849
849
  }
850
+ .h-\[720px\] {
851
+ height: 720px;
852
+ }
850
853
  .h-\[var\(--avatar-size\)\] {
851
854
  height: var(--avatar-size);
852
855
  }
@@ -898,6 +901,9 @@
898
901
  .max-h-96 {
899
902
  max-height: calc(var(--spacing) * 96);
900
903
  }
904
+ .max-h-\[40\%\] {
905
+ max-height: 40%;
906
+ }
901
907
  .max-h-\[70vh\] {
902
908
  max-height: 70vh;
903
909
  }
@@ -919,6 +925,9 @@
919
925
  .min-h-\[2px\] {
920
926
  min-height: 2px;
921
927
  }
928
+ .min-h-\[3\.5rem\] {
929
+ min-height: 3.5rem;
930
+ }
922
931
  .min-h-\[6rem\] {
923
932
  min-height: 6rem;
924
933
  }
@@ -1102,6 +1111,9 @@
1102
1111
  .w-\[3px\] {
1103
1112
  width: 3px;
1104
1113
  }
1114
+ .w-\[5\.5rem\] {
1115
+ width: 5.5rem;
1116
+ }
1105
1117
  .w-\[17px\] {
1106
1118
  width: 17px;
1107
1119
  }
@@ -1135,6 +1147,9 @@
1135
1147
  .w-\[420px\] {
1136
1148
  width: 420px;
1137
1149
  }
1150
+ .w-\[440px\] {
1151
+ width: 440px;
1152
+ }
1138
1153
  .w-\[480px\] {
1139
1154
  width: 480px;
1140
1155
  }
@@ -4124,6 +4139,9 @@
4124
4139
  .hover\:bg-red-700:hover {
4125
4140
  background-color: var(--color-red-700);
4126
4141
  }
4142
+ .hover\:bg-surface-container:hover {
4143
+ background-color: var(--color-surface-container);
4144
+ }
4127
4145
  .hover\:bg-surface-container-high:hover {
4128
4146
  background-color: var(--color-surface-container-high);
4129
4147
  }
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import { CSSProperties } from 'react';
3
- import { RichFileTreeGitEntry, RichFileTreeGitStatus } from '@tangle-network/ui/files';
3
+ import { RichFileTreeGitStatus, RichFileTreeGitEntry } from '@tangle-network/ui/files';
4
4
  import { E as ExposedPort } from './ports-list-CDIvTNnX.js';
5
5
 
6
6
  /** Which surface the artifact pane is currently showing. */
@@ -67,6 +67,47 @@ interface SandboxArtifactPaneProps {
67
67
  className?: string;
68
68
  style?: CSSProperties;
69
69
  }
70
+ /**
71
+ * One entry of the sandbox's git working tree, as the Changes pane lists it.
72
+ * `additions` / `deletions` are the numstat counts the sidecar's
73
+ * `GET /git/diff` reports per file.
74
+ *
75
+ * `baseline` / `current` are the file contents the diff is drawn from and are
76
+ * absent until the consumer resolves them for the selected file. Which of the
77
+ * two counts as "resolved" follows the status: an added or untracked file has
78
+ * no baseline and diffs against the empty string once `current` is present; a
79
+ * deleted file has no working copy and diffs against the empty string once
80
+ * `baseline` is present; every other status waits for both.
81
+ */
82
+ interface ChangedFile {
83
+ path: string;
84
+ status: RichFileTreeGitStatus;
85
+ additions: number;
86
+ deletions: number;
87
+ /** Present once the consumer has resolved contents for the selected file. */
88
+ baseline?: string;
89
+ current?: string;
90
+ }
91
+ interface ChangesPaneProps {
92
+ /** Current branch; `null` when the tree has no commits yet or is not a repository. */
93
+ branch: string | null;
94
+ ahead?: number;
95
+ behind?: number;
96
+ files: ChangedFile[];
97
+ selectedPath?: string;
98
+ onSelectFile?: (path: string) => void;
99
+ loading?: boolean;
100
+ error?: string | null;
101
+ onRefresh?: () => void;
102
+ /** Stage everything and commit. Resolve on success; reject with an Error the pane shows inline. */
103
+ onCommit?: (message: string) => Promise<void>;
104
+ onPush?: () => Promise<void>;
105
+ /** Which action is in flight; the pane disables the other and shows a spinner on this one. */
106
+ busy?: "commit" | "push" | null;
107
+ /** Open the file as a workbench artifact (the product wires this to its artifact opener). */
108
+ onOpenFile?: (path: string) => void;
109
+ className?: string;
110
+ }
70
111
 
71
112
  /**
72
113
  * Blueprint-grade artifact workbench. Composes the sandbox-ui substrate
@@ -78,6 +119,21 @@ interface SandboxArtifactPaneProps {
78
119
  */
79
120
  declare function SandboxArtifactPane({ artifact, paths, gitStatus, showTree, ports, previewUrl, terminal, activeView, defaultView, onViewChange, onFileSelect, onExposePort, onRemovePort, className, style, }: SandboxArtifactPaneProps): React.JSX.Element;
80
121
 
122
+ /**
123
+ * The sandbox's git working tree as a review surface: every changed file with
124
+ * its line counts, the selected file's word-level diff beneath, and a commit
125
+ * box in the footer. Data in, callbacks out — the pane never fetches; the
126
+ * consumer feeds it the sidecar's `/git/status` + `/git/diff` and resolves
127
+ * `baseline` / `current` for the file it is told was selected.
128
+ *
129
+ * The list is capped at 40% of the pane and scrolls, so the diff keeps its
130
+ * room however many files changed. The row's own chrome is compact: the
131
+ * workspace shell already gives its right pane the 56px `WorkspacePaneHeader`,
132
+ * so this header nests beneath it at `PanelHeader` rhythm rather than adding a
133
+ * second tall bar.
134
+ */
135
+ declare function ChangesPane({ branch, ahead, behind, files, selectedPath, onSelectFile, loading, error, onRefresh, onCommit, onPush, busy, onOpenFile, className, }: ChangesPaneProps): React.JSX.Element;
136
+
81
137
  interface PillTabItem<T extends string> {
82
138
  value: T;
83
139
  label: string;
@@ -124,6 +180,11 @@ declare function buildUnifiedPatch(filename: string, baseline: string, current:
124
180
  declare function GitStatusBadge({ status }: {
125
181
  status: RichFileTreeGitStatus;
126
182
  }): React.JSX.Element;
183
+ /** `+added −removed` line counts in mono; the same pair the Changes list prints per file. */
184
+ declare function DiffStatsBadge({ stats, className }: {
185
+ stats: DiffStats;
186
+ className?: string;
187
+ }): React.JSX.Element;
127
188
  interface PanelHeaderProps {
128
189
  filename: string;
129
190
  gitStatus?: RichFileTreeGitStatus;
@@ -182,6 +243,12 @@ interface DiffViewProps {
182
243
  filename: string;
183
244
  baseline: string;
184
245
  current: string;
246
+ /**
247
+ * The renderer's own sticky file header (name + counts). On by default; a
248
+ * host whose chrome already names the file, such as the Changes pane under
249
+ * its `PanelHeader`, turns it off so the name is not printed twice.
250
+ */
251
+ showFileHeader?: boolean;
185
252
  className?: string;
186
253
  }
187
254
  /**
@@ -189,7 +256,7 @@ interface DiffViewProps {
189
256
  * with jsdiff's `createTwoFilesPatch` (baseline → current); the renderer owns
190
257
  * hunking, intra-line word diffing, gutters, and syntax highlighting.
191
258
  */
192
- declare function DiffView({ filename, baseline, current, className }: DiffViewProps): React.JSX.Element;
259
+ declare function DiffView({ filename, baseline, current, showFileHeader, className }: DiffViewProps): React.JSX.Element;
193
260
 
194
261
  interface PreviewViewProps {
195
262
  /** Origin to load in the preview iframe. */
@@ -223,4 +290,4 @@ interface CodeSurfaceProps {
223
290
  */
224
291
  declare function CodeSurface({ code, filename, language, showLineNumbers, className, }: CodeSurfaceProps): React.JSX.Element;
225
292
 
226
- export { type ArtifactView, BRAND_PRISM_THEME, CodeSurface, type CodeSurfaceProps, CodeView, type CodeViewProps, type DiffStats, DiffView, type DiffViewProps, FileBreadcrumb, type FileBreadcrumbProps, GitStatusBadge, PanelHeader, type PanelHeaderProps, type PillTabItem, PillTabs, type PillTabsProps, PreviewView, type PreviewViewProps, type SandboxArtifact, SandboxArtifactPane, type SandboxArtifactPaneProps, type SandboxArtifactTerminal, buildUnifiedPatch, computeDiffStats, resolveLanguage };
293
+ export { type ArtifactView, BRAND_PRISM_THEME, type ChangedFile, ChangesPane, type ChangesPaneProps, CodeSurface, type CodeSurfaceProps, CodeView, type CodeViewProps, type DiffStats, DiffStatsBadge, DiffView, type DiffViewProps, FileBreadcrumb, type FileBreadcrumbProps, GitStatusBadge, PanelHeader, type PanelHeaderProps, type PillTabItem, PillTabs, type PillTabsProps, PreviewView, type PreviewViewProps, type SandboxArtifact, SandboxArtifactPane, type SandboxArtifactPaneProps, type SandboxArtifactTerminal, buildUnifiedPatch, computeDiffStats, resolveLanguage };
package/dist/workbench.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import {
2
2
  BRAND_PRISM_THEME,
3
+ ChangesPane,
3
4
  CodeSurface,
4
5
  CodeView,
6
+ DiffStatsBadge,
5
7
  DiffView,
6
8
  FileBreadcrumb,
7
9
  GitStatusBadge,
@@ -12,7 +14,7 @@ import {
12
14
  buildUnifiedPatch,
13
15
  computeDiffStats,
14
16
  resolveLanguage
15
- } from "./chunk-5M67IFVI.js";
17
+ } from "./chunk-ISTJTUHJ.js";
16
18
  import "./chunk-PZVUIUUW.js";
17
19
  import "./chunk-K4Q4E7D7.js";
18
20
  import "./chunk-OMUIPJQZ.js";
@@ -25,8 +27,10 @@ import "./chunk-EI44GEQ5.js";
25
27
  import "./chunk-CZ7CSFO4.js";
26
28
  export {
27
29
  BRAND_PRISM_THEME,
30
+ ChangesPane,
28
31
  CodeSurface,
29
32
  CodeView,
33
+ DiffStatsBadge,
30
34
  DiffView,
31
35
  FileBreadcrumb,
32
36
  GitStatusBadge,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/sandbox-ui",
3
- "version": "0.111.2",
3
+ "version": "0.112.0",
4
4
  "description": "Unified UI component library for Tangle Sandbox — primitives, chat, dashboard, terminal, editor, and workspace components",
5
5
  "repository": {
6
6
  "type": "git",