@tangle-network/sandbox-ui 0.111.2 → 0.113.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.
package/dist/styles.css CHANGED
@@ -368,6 +368,9 @@
368
368
  .top-full {
369
369
  top: 100%;
370
370
  }
371
+ .-right-0\.5 {
372
+ right: calc(var(--spacing) * -0.5);
373
+ }
371
374
  .-right-1 {
372
375
  right: calc(var(--spacing) * -1);
373
376
  }
@@ -389,6 +392,9 @@
389
392
  .right-4 {
390
393
  right: calc(var(--spacing) * 4);
391
394
  }
395
+ .-bottom-0\.5 {
396
+ bottom: calc(var(--spacing) * -0.5);
397
+ }
392
398
  .bottom-0 {
393
399
  bottom: 0px;
394
400
  }
@@ -847,6 +853,9 @@
847
853
  .h-\[640px\] {
848
854
  height: 640px;
849
855
  }
856
+ .h-\[720px\] {
857
+ height: 720px;
858
+ }
850
859
  .h-\[var\(--avatar-size\)\] {
851
860
  height: var(--avatar-size);
852
861
  }
@@ -898,6 +907,9 @@
898
907
  .max-h-96 {
899
908
  max-height: calc(var(--spacing) * 96);
900
909
  }
910
+ .max-h-\[40\%\] {
911
+ max-height: 40%;
912
+ }
901
913
  .max-h-\[70vh\] {
902
914
  max-height: 70vh;
903
915
  }
@@ -913,12 +925,18 @@
913
925
  .min-h-0 {
914
926
  min-height: 0px;
915
927
  }
928
+ .min-h-8 {
929
+ min-height: calc(var(--spacing) * 8);
930
+ }
916
931
  .min-h-24 {
917
932
  min-height: calc(var(--spacing) * 24);
918
933
  }
919
934
  .min-h-\[2px\] {
920
935
  min-height: 2px;
921
936
  }
937
+ .min-h-\[3\.5rem\] {
938
+ min-height: 3.5rem;
939
+ }
922
940
  .min-h-\[6rem\] {
923
941
  min-height: 6rem;
924
942
  }
@@ -1102,6 +1120,9 @@
1102
1120
  .w-\[3px\] {
1103
1121
  width: 3px;
1104
1122
  }
1123
+ .w-\[5\.5rem\] {
1124
+ width: 5.5rem;
1125
+ }
1105
1126
  .w-\[17px\] {
1106
1127
  width: 17px;
1107
1128
  }
@@ -1135,6 +1156,9 @@
1135
1156
  .w-\[420px\] {
1136
1157
  width: 420px;
1137
1158
  }
1159
+ .w-\[440px\] {
1160
+ width: 440px;
1161
+ }
1138
1162
  .w-\[480px\] {
1139
1163
  width: 480px;
1140
1164
  }
@@ -2691,6 +2715,9 @@
2691
2715
  .px-\[var\(--tool-card-px\)\] {
2692
2716
  padding-inline: var(--tool-card-px);
2693
2717
  }
2718
+ .py-0 {
2719
+ padding-block: 0px;
2720
+ }
2694
2721
  .py-0\.5 {
2695
2722
  padding-block: calc(var(--spacing) * 0.5);
2696
2723
  }
@@ -2796,6 +2823,9 @@
2796
2823
  .pr-2\.5 {
2797
2824
  padding-right: calc(var(--spacing) * 2.5);
2798
2825
  }
2826
+ .pr-3 {
2827
+ padding-right: calc(var(--spacing) * 3);
2828
+ }
2799
2829
  .pr-8 {
2800
2830
  padding-right: calc(var(--spacing) * 8);
2801
2831
  }
@@ -2965,6 +2995,10 @@
2965
2995
  --tw-leading: calc(var(--spacing) * 4);
2966
2996
  line-height: calc(var(--spacing) * 4);
2967
2997
  }
2998
+ .leading-5 {
2999
+ --tw-leading: calc(var(--spacing) * 5);
3000
+ line-height: calc(var(--spacing) * 5);
3001
+ }
2968
3002
  .leading-6 {
2969
3003
  --tw-leading: calc(var(--spacing) * 6);
2970
3004
  line-height: calc(var(--spacing) * 6);
@@ -3300,6 +3334,12 @@
3300
3334
  color: color-mix(in oklab, var(--color-foreground) 70%, transparent);
3301
3335
  }
3302
3336
  }
3337
+ .text-foreground\/75 {
3338
+ color: color-mix(in srgb, hsl(var(--foreground)) 75%, transparent);
3339
+ @supports (color: color-mix(in lab, red, red)) {
3340
+ color: color-mix(in oklab, var(--color-foreground) 75%, transparent);
3341
+ }
3342
+ }
3303
3343
  .text-foreground\/80 {
3304
3344
  color: color-mix(in srgb, hsl(var(--foreground)) 80%, transparent);
3305
3345
  @supports (color: color-mix(in lab, red, red)) {
@@ -3471,6 +3511,10 @@
3471
3511
  --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
3472
3512
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
3473
3513
  }
3514
+ .shadow-\[0_1px_2px_rgba\(0\,0\,0\,0\.2\)\] {
3515
+ --tw-shadow: 0 1px 2px var(--tw-shadow-color, rgba(0,0,0,0.2));
3516
+ box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
3517
+ }
3474
3518
  .shadow-\[0_1px_2px_rgba\(15\,23\,42\,0\.04\)\] {
3475
3519
  --tw-shadow: 0 1px 2px var(--tw-shadow-color, rgba(15,23,42,0.04));
3476
3520
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
@@ -3724,6 +3768,9 @@
3724
3768
  border-color: color-mix(in oklab, var(--color-primary) 40%, transparent);
3725
3769
  }
3726
3770
  }
3771
+ .group-hover\:text-foreground:is(:where(.group):hover *) {
3772
+ color: var(--color-foreground);
3773
+ }
3727
3774
  .group-hover\:text-foreground\/40:is(:where(.group):hover *) {
3728
3775
  color: color-mix(in srgb, hsl(var(--foreground)) 40%, transparent);
3729
3776
  }
@@ -4124,6 +4171,9 @@
4124
4171
  .hover\:bg-red-700:hover {
4125
4172
  background-color: var(--color-red-700);
4126
4173
  }
4174
+ .hover\:bg-surface-container:hover {
4175
+ background-color: var(--color-surface-container);
4176
+ }
4127
4177
  .hover\:bg-surface-container-high:hover {
4128
4178
  background-color: var(--color-surface-container-high);
4129
4179
  }
@@ -4327,6 +4377,9 @@
4327
4377
  background-color: color-mix(in oklab, var(--color-primary) 40%, transparent);
4328
4378
  }
4329
4379
  }
4380
+ .focus-visible\:bg-surface-container:focus-visible {
4381
+ background-color: var(--color-surface-container);
4382
+ }
4330
4383
  .focus-visible\:bg-transparent:focus-visible {
4331
4384
  background-color: transparent;
4332
4385
  }
@@ -4696,6 +4749,9 @@
4696
4749
  .lg\:items-center {
4697
4750
  align-items: center;
4698
4751
  }
4752
+ .lg\:p-0 {
4753
+ padding: 0px;
4754
+ }
4699
4755
  .lg\:p-4 {
4700
4756
  padding: calc(var(--spacing) * 4);
4701
4757
  }
@@ -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,
@@ -3,6 +3,7 @@ import { ReactNode, HTMLAttributes, Ref } from 'react';
3
3
  export { ArtifactPane, ArtifactPaneProps } from '@tangle-network/ui/primitives';
4
4
  import { FileNode, FileTreeVisibilityOptions, FileTabData } from '@tangle-network/ui/files';
5
5
  import { SessionProjectKey, ActiveSessionStatus, ActiveSessionRecord } from '@tangle-network/ui/stores';
6
+ import { LucideIcon } from 'lucide-react';
6
7
  import { ChatContainerProps } from '@tangle-network/ui/chat';
7
8
  import { OpenUIComponentNode, OpenUIAction } from '@tangle-network/ui/openui';
8
9
 
@@ -63,9 +64,42 @@ interface WorkspaceLayoutProps {
63
64
  rightLabel?: string;
64
65
  /** Accessible label for the bottom panel */
65
66
  bottomLabel?: string;
67
+ /**
68
+ * Controlled open state for the left pane. Omit to keep the pane
69
+ * uncontrolled (`defaultLeftOpen`, persisted under `persistenceKey`).
70
+ */
71
+ leftOpen?: boolean;
72
+ /** Called with the next state whenever the layout would open or close the left pane. */
73
+ onLeftOpenChange?: (open: boolean) => void;
74
+ /** Controlled open state for the right pane; see `leftOpen`. */
75
+ rightOpen?: boolean;
76
+ /** Called with the next state whenever the layout would open or close the right pane. */
77
+ onRightOpenChange?: (open: boolean) => void;
78
+ /**
79
+ * ⌘B / Ctrl+B toggles the left pane and ⌘E / Ctrl+E the right pane. A
80
+ * chord with Alt or Shift, or one fired while an input, textarea, select, or
81
+ * contentEditable has focus, is left to the page. Off by default.
82
+ */
83
+ keyboardShortcuts?: boolean;
84
+ /**
85
+ * Replaces the default "Open left panel" button at the top-left of the
86
+ * center pane while a left pane is provided but closed.
87
+ */
88
+ leftCollapsedControl?: ReactNode;
89
+ /** Extra classes for the left pane's scrolling content wrapper, e.g. `py-0` so a rail owns its own gutter. */
90
+ leftContentClassName?: string;
91
+ /** Extra classes for the right pane's scrolling content wrapper. */
92
+ rightContentClassName?: string;
93
+ /**
94
+ * `always` (default) keeps the center header row whenever a side or bottom
95
+ * pane exists, so pane headers line up across the shell even while the row
96
+ * is empty. `auto` renders the row only while it has content: a
97
+ * `centerHeader`, or an open-pane toggle for a closed pane.
98
+ */
99
+ centerHeaderVisibility?: "always" | "auto";
66
100
  className?: string;
67
101
  }
68
- declare function WorkspaceLayout({ left, leftHeader, center, centerHeader, centerFooter, right, rightHeader, bottom, bottomHeader, defaultLeftOpen, defaultRightOpen, defaultBottomOpen, defaultLeftWidth, defaultRightWidth, defaultBottomHeight, minLeftWidth, maxLeftWidth, minRightWidth, maxRightWidth, minBottomHeight, maxBottomHeight, persistenceKey, resizable, theme, density, leftLabel, rightLabel, bottomLabel, className, }: WorkspaceLayoutProps): React$1.JSX.Element;
102
+ declare function WorkspaceLayout({ left, leftHeader, center, centerHeader, centerFooter, right, rightHeader, bottom, bottomHeader, defaultLeftOpen, defaultRightOpen, defaultBottomOpen, defaultLeftWidth, defaultRightWidth, defaultBottomHeight, minLeftWidth, maxLeftWidth, minRightWidth, maxRightWidth, minBottomHeight, maxBottomHeight, persistenceKey, resizable, theme, density, leftLabel, rightLabel, bottomLabel, leftOpen: leftOpenProp, onLeftOpenChange, rightOpen: rightOpenProp, onRightOpenChange, keyboardShortcuts, leftCollapsedControl, leftContentClassName, rightContentClassName, centerHeaderVisibility, className, }: WorkspaceLayoutProps): React$1.JSX.Element;
69
103
 
70
104
  type WorkspacePaneHeaderProps = HTMLAttributes<HTMLDivElement>;
71
105
  /**
@@ -243,6 +277,17 @@ interface SessionSidebarItem {
243
277
  category?: string;
244
278
  isPinned?: boolean;
245
279
  badges?: SessionSidebarBadge[];
280
+ /**
281
+ * Glyph rendered in a 16×20 slot before the title — a harness mark, for
282
+ * example. While an icon is present the status dot becomes a 6px badge on
283
+ * the icon's corner instead of a leading dot.
284
+ */
285
+ icon?: ReactNode;
286
+ /**
287
+ * Muted 12px line under the title, e.g. `harness · age`. When omitted and
288
+ * `showUpdatedAt` is set, the row renders `updatedAt` as a relative age here.
289
+ */
290
+ meta?: ReactNode;
246
291
  }
247
292
  interface SessionSidebarBadge {
248
293
  id: string;
@@ -263,6 +308,8 @@ interface SessionSidebarFilter {
263
308
  isActive: boolean;
264
309
  }) => boolean;
265
310
  }
311
+ type SessionSidebarVariant = "default" | "quiet";
312
+ type SessionSidebarGroupBy = "none" | "status";
266
313
  interface SessionSidebarProps {
267
314
  title: string;
268
315
  subtitle?: string;
@@ -304,8 +351,45 @@ interface SessionSidebarProps {
304
351
  * handlers. Only populated when `optimistic` is enabled.
305
352
  */
306
353
  optimisticRef?: Ref<SessionOptimisticController | null>;
354
+ /**
355
+ * `default` is the branded panel: icon-in-a-box header, running-count chip,
356
+ * bordered search field, accent-filled selected row. `quiet` is the rail of a
357
+ * three-pane shell: the header is one row (a "new" text button plus the
358
+ * optional `onCollapse` button), rows are `rounded-lg` with a surface fill
359
+ * for hover and selection, only non-neutral badges render, and search is a
360
+ * borderless field.
361
+ */
362
+ variant?: SessionSidebarVariant;
363
+ /**
364
+ * `status` partitions the list into "Needs input" (`attention-needed`),
365
+ * "Failed" (`error`), "Working" (`running`), and "Done" (everything else),
366
+ * in that order. Group labels render only while more than one group has
367
+ * items. Within a group the sort is unchanged: pinned first, then activity.
368
+ * Status comes from the active-sessions store record when one exists for the
369
+ * id, else from `item.status`.
370
+ */
371
+ groupBy?: SessionSidebarGroupBy;
372
+ /** Render `updatedAt` as a relative age under the title for rows with no `meta`. */
373
+ showUpdatedAt?: boolean;
374
+ /** Renders a "Hide chats" button in the `quiet` header. */
375
+ onCollapse?: () => void;
376
+ /**
377
+ * Fill the parent instead of carrying the sidebar's own width: no inline
378
+ * width, `h-full w-full`. Use when a layout owns the pane size. Ignored while
379
+ * `resizable`, which needs the inline width it drags.
380
+ */
381
+ fill?: boolean;
307
382
  }
308
- declare function SessionSidebar({ title, subtitle, projectId, items, currentItemId, createLabel, onCreate, onSelectItem, onSelectLink, links, className, emptyMessage, searchPlaceholder, enableSearch, activityMonitor, filters, defaultFilterId, resizable, defaultWidth, minWidth, maxWidth, onWidthChange, renderItemActions, optimistic, optimisticRef, }: SessionSidebarProps): React$1.JSX.Element;
383
+ declare function SessionSidebar({ title, subtitle, projectId, items, currentItemId, createLabel, onCreate, onSelectItem, onSelectLink, links, className, emptyMessage, searchPlaceholder, enableSearch, activityMonitor, filters, defaultFilterId, resizable, defaultWidth, minWidth, maxWidth, onWidthChange, renderItemActions, optimistic, optimisticRef, variant, groupBy, showUpdatedAt, onCollapse, fill, }: SessionSidebarProps): React$1.JSX.Element;
384
+
385
+ /**
386
+ * Compact age for a session rail: `now`, `5m`, `3h`, `2d`, `3w`, `4mo`, `1y`.
387
+ * Unlike `timeAgo` from `@tangle-network/ui/utils` it accepts any date input,
388
+ * rolls past hours into days and beyond, and carries no "ago" suffix, so it
389
+ * fits a "codex · 2h" meta line. A future or unparseable date reads `now` /
390
+ * empty rather than a negative count. `now` is injectable for deterministic tests.
391
+ */
392
+ declare function formatRelativeAge(date: string | number | Date, now?: number): string;
309
393
 
310
394
  /**
311
395
  * Snappy in-app navigation: a shared `shouldRevalidate` rule for the agent-app
@@ -402,6 +486,10 @@ interface SandboxWorkbenchArtifactBase {
402
486
  headerActions?: ReactNode;
403
487
  toolbar?: ReactNode;
404
488
  footer?: ReactNode;
489
+ /** Tab glyph. Defaults per `kind`; a `custom` artifact usually names its own. */
490
+ icon?: LucideIcon;
491
+ /** A pinned artifact's tab sorts before the rest and carries no close button. */
492
+ pinned?: boolean;
405
493
  }
406
494
  interface SandboxWorkbenchFileArtifact extends SandboxWorkbenchArtifactBase {
407
495
  kind: "file";
@@ -447,7 +535,7 @@ interface SandboxWorkbenchSessionProps extends Omit<ChatContainerProps, "classNa
447
535
  renderUserMessageActions?: ChatContainerProps["renderUserMessageActions"];
448
536
  renderToolActions?: ChatContainerProps["renderToolActions"];
449
537
  }
450
- interface SandboxWorkbenchLayoutOptions extends Pick<WorkspaceLayoutProps, "bottomHeader" | "defaultBottomOpen" | "defaultLeftOpen" | "defaultLeftWidth" | "defaultRightOpen" | "defaultRightWidth" | "density" | "maxLeftWidth" | "maxRightWidth" | "minLeftWidth" | "minRightWidth" | "persistenceKey" | "resizable" | "theme"> {
538
+ interface SandboxWorkbenchLayoutOptions extends Pick<WorkspaceLayoutProps, "bottomHeader" | "defaultBottomOpen" | "defaultLeftOpen" | "defaultLeftWidth" | "defaultRightOpen" | "defaultRightWidth" | "density" | "keyboardShortcuts" | "leftCollapsedControl" | "leftOpen" | "maxLeftWidth" | "maxRightWidth" | "minLeftWidth" | "minRightWidth" | "onLeftOpenChange" | "onRightOpenChange" | "persistenceKey" | "resizable" | "rightOpen" | "theme"> {
451
539
  directoryPlacement?: SandboxWorkbenchPlacement;
452
540
  artifactPlacement?: SandboxWorkbenchPlacement;
453
541
  runtimePlacement?: SandboxWorkbenchPlacement;
@@ -456,6 +544,27 @@ interface SandboxWorkbenchProps {
456
544
  title?: ReactNode;
457
545
  subtitle?: ReactNode;
458
546
  status?: ReactNode;
547
+ /**
548
+ * Header above the transcript. `undefined` keeps the branded session card
549
+ * (`Tangle Sandbox` / `title` / `status`), a node replaces it, and `null`
550
+ * removes it: the transcript then renders directly on `bg-surface` with no
551
+ * eyebrow/title frame around it, and the shell's center header row appears
552
+ * only while it holds an open-pane toggle.
553
+ */
554
+ centerHeader?: ReactNode | null;
555
+ /**
556
+ * Composer rendered under the transcript on the panel background, inside the
557
+ * transcript's own `max-w-3xl` column. `session.composerControls`, when
558
+ * also given, sits above it as before.
559
+ */
560
+ composer?: ReactNode;
561
+ /**
562
+ * First section of the left region, ahead of the directory pane — a
563
+ * `SessionSidebar` for a chats rail. A rail that is the only left section
564
+ * fills the pane: no region header, no gutter. Give it `fill` and
565
+ * `className="border-r-0"` so the layout's own divider is the only one.
566
+ */
567
+ rail?: ReactNode;
459
568
  directory?: DirectoryPaneProps;
460
569
  session: SandboxWorkbenchSessionProps;
461
570
  artifacts?: SandboxWorkbenchArtifact[];
@@ -472,7 +581,7 @@ interface SandboxWorkbenchProps {
472
581
  * into a complete session surface: directory, agent timeline, artifacts, and
473
582
  * runtime in one reusable shell.
474
583
  */
475
- declare function SandboxWorkbench({ title, subtitle, status, directory, session, artifacts, activeArtifactId, onArtifactChange, onArtifactClose, runtime, layout, emptyArtifactState, className, }: SandboxWorkbenchProps): React$1.JSX.Element;
584
+ declare function SandboxWorkbench({ title, subtitle, status, centerHeader: centerHeaderProp, composer, rail, directory, session, artifacts: artifactsProp, activeArtifactId, onArtifactChange, onArtifactClose, runtime, layout, emptyArtifactState, className, }: SandboxWorkbenchProps): React$1.JSX.Element;
476
585
  declare function AgentWorkbench(props: SandboxWorkbenchProps): React$1.JSX.Element;
477
586
 
478
587
  /**
@@ -639,4 +748,4 @@ interface ApprovalQueueProps {
639
748
  */
640
749
  declare function ApprovalQueue({ items, className, onApprove, onReject, renderItemDetail, renderTypeBadge, renderStats, canResolve, header, emptyState, showResolved: controlledShowResolved, }: ApprovalQueueProps): React$1.JSX.Element;
641
750
 
642
- export { AgentWorkbench, type ApprovalConfidenceStat, type ApprovalItem, ApprovalQueue, type ApprovalQueueProps, type AuditCheck, AuditResults, type AuditResultsProps, type BannerType, type CalendarEvent, CalendarView, type CalendarViewProps, type ContextBadge, DirectoryPane, type DirectoryPaneProps, type FormAudit, RuntimePane, type RuntimePaneProps, SESSION_CREATED_EVENT, SandboxWorkbench, type SandboxWorkbenchArtifact, type SandboxWorkbenchCustomArtifact, type SandboxWorkbenchFileArtifact, type SandboxWorkbenchLayoutOptions, type SandboxWorkbenchMarkdownArtifact, type SandboxWorkbenchOpenUIArtifact, type SandboxWorkbenchProps, type SandboxWorkbenchSessionProps, SessionActivityMonitor, type SessionActivityMonitorProps, type SessionCreatedDetail, type SessionOptimisticController, SessionSidebar, type SessionSidebarBadge, type SessionSidebarFilter, type SessionSidebarItem, type SessionSidebarLink, type SessionSidebarProps, type ShellShouldRevalidateArgs, type ShellShouldRevalidateOptions, StatusBanner, type StatusBannerProps, StatusBar, type StatusBarProps, TaskBoard, type TaskBoardColumn, type TaskBoardItem, type TaskBoardProps, type TerminalLine, TerminalPanel, type TerminalProps, type UseOptimisticSessionItemsOptions, WorkspaceLayout, type WorkspaceLayoutProps, WorkspacePaneHeader, type WorkspacePaneHeaderProps, createShellShouldRevalidate, dispatchSessionCreated, shellShouldRevalidate, useOptimisticSessionItems };
751
+ export { AgentWorkbench, type ApprovalConfidenceStat, type ApprovalItem, ApprovalQueue, type ApprovalQueueProps, type AuditCheck, AuditResults, type AuditResultsProps, type BannerType, type CalendarEvent, CalendarView, type CalendarViewProps, type ContextBadge, DirectoryPane, type DirectoryPaneProps, type FormAudit, RuntimePane, type RuntimePaneProps, SESSION_CREATED_EVENT, SandboxWorkbench, type SandboxWorkbenchArtifact, type SandboxWorkbenchCustomArtifact, type SandboxWorkbenchFileArtifact, type SandboxWorkbenchLayoutOptions, type SandboxWorkbenchMarkdownArtifact, type SandboxWorkbenchOpenUIArtifact, type SandboxWorkbenchProps, type SandboxWorkbenchSessionProps, SessionActivityMonitor, type SessionActivityMonitorProps, type SessionCreatedDetail, type SessionOptimisticController, SessionSidebar, type SessionSidebarBadge, type SessionSidebarFilter, type SessionSidebarGroupBy, type SessionSidebarItem, type SessionSidebarLink, type SessionSidebarProps, type SessionSidebarVariant, type ShellShouldRevalidateArgs, type ShellShouldRevalidateOptions, StatusBanner, type StatusBannerProps, StatusBar, type StatusBarProps, TaskBoard, type TaskBoardColumn, type TaskBoardItem, type TaskBoardProps, type TerminalLine, TerminalPanel, type TerminalProps, type UseOptimisticSessionItemsOptions, WorkspaceLayout, type WorkspaceLayoutProps, WorkspacePaneHeader, type WorkspacePaneHeaderProps, createShellShouldRevalidate, dispatchSessionCreated, formatRelativeAge, shellShouldRevalidate, useOptimisticSessionItems };
package/dist/workspace.js CHANGED
@@ -18,9 +18,10 @@ import {
18
18
  WorkspacePaneHeader,
19
19
  createShellShouldRevalidate,
20
20
  dispatchSessionCreated,
21
+ formatRelativeAge,
21
22
  shellShouldRevalidate,
22
23
  useOptimisticSessionItems
23
- } from "./chunk-D5VXCWUG.js";
24
+ } from "./chunk-2Q3KDQX7.js";
24
25
  import "./chunk-BGVEHENC.js";
25
26
  import "./chunk-EI44GEQ5.js";
26
27
  import "./chunk-CZ7CSFO4.js";
@@ -44,6 +45,7 @@ export {
44
45
  WorkspacePaneHeader,
45
46
  createShellShouldRevalidate,
46
47
  dispatchSessionCreated,
48
+ formatRelativeAge,
47
49
  shellShouldRevalidate,
48
50
  useOptimisticSessionItems
49
51
  };
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.113.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",