@tangle-network/sandbox-ui 0.134.11 → 0.135.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/terminal.js CHANGED
@@ -1,2 +1,2 @@
1
- import { n as TerminalView, t as DEFAULT_TERMINAL_THEME } from "./terminal-DmSUfGha.js";
1
+ import { n as TerminalView, t as DEFAULT_TERMINAL_THEME } from "./terminal-X6r5pjgv.js";
2
2
  export { DEFAULT_TERMINAL_THEME, TerminalView };
@@ -17,13 +17,18 @@ function createEmptyBatch() {
17
17
  */
18
18
  const WS_READY_TIMEOUT_MS = 1e4;
19
19
  /** Convert an http(s) base URL into the matching ws(s) URL. */
20
- function toWsUrl(apiUrl, sessionId) {
20
+ function toWsUrl(apiUrl, sessionId, defaultShell) {
21
21
  try {
22
22
  const base = typeof window !== "undefined" ? window.location.href : void 0;
23
23
  const url = new URL(`${apiUrl}/terminals/${sessionId}/ws`, base);
24
24
  if (url.protocol === "https:") url.protocol = "wss:";
25
25
  else if (url.protocol === "http:") url.protocol = "ws:";
26
26
  else return null;
27
+ if (defaultShell) {
28
+ url.searchParams.set("cols", String(defaultShell.cols));
29
+ url.searchParams.set("rows", String(defaultShell.rows));
30
+ url.searchParams.set("defaultShell", "1");
31
+ }
27
32
  return url.toString();
28
33
  } catch {
29
34
  return null;
@@ -215,7 +220,10 @@ function usePtySession({ enabled = true, apiUrl, token, onData, connectionId: pr
215
220
  resolve(false);
216
221
  return;
217
222
  }
218
- const wsUrl = toWsUrl(apiUrl, sessionId);
223
+ const wsUrl = toWsUrl(apiUrl, sessionId, options.initOnOpen && !exactAttachRequiredRef.current ? {
224
+ cols: colsRef.current,
225
+ rows: rowsRef.current
226
+ } : void 0);
219
227
  if (!wsUrl) {
220
228
  resolve(false);
221
229
  return;
@@ -1,6 +1,6 @@
1
1
  import { t as cn$1 } from "./utils-DzWbktWG.js";
2
2
  import { a as PortsList } from "./dashboard-C1ALa6lw.js";
3
- import { n as TerminalView } from "./terminal-DmSUfGha.js";
3
+ import { n as TerminalView } from "./terminal-X6r5pjgv.js";
4
4
  import { RichFileTree } from "./files.js";
5
5
  import { n as PreviewView } from "./embedded-app-view-CYb9hevq.js";
6
6
  import { Button, Skeleton, Textarea } from "@tangle-network/ui/primitives";
package/dist/workbench.js CHANGED
@@ -1,3 +1,3 @@
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-QHNLHsoL.js";
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-BzXAYUId.js";
2
2
  import { n as PreviewView, t as EmbeddedAppView } from "./embedded-app-view-CYb9hevq.js";
3
3
  export { BRAND_PRISM_THEME, ChangesPane, CodeSurface, CodeView, DiffStatsBadge, DiffView, EmbeddedAppView, FileBreadcrumb, GitStatusBadge, PanelHeader, PillTabs, PreviewView, SandboxArtifactPane, buildUnifiedPatch, computeDiffStats, resolveLanguage };
@@ -24,7 +24,11 @@ type WfNodeTone = "trigger" | "structural" | "action";
24
24
  * working when in fact it is blocked on THEM — so it gets its own colour, its own
25
25
  * label, and a progress bar that does not move.
26
26
  */
27
- type WfNodeStatus = "queued" | "running" | "waiting" | "succeeded" | "failed";
27
+ type WfNodeStatus = "queued" | "running" | "waiting" | "succeeded" | "failed" |
28
+ /** The run went another way: a guard or `when` left this step out. Drawn
29
+ * dimmed and dashed, so the path the run took stands out from the one it
30
+ * did not, and never confused with a step that is still to come. */
31
+ "skipped";
28
32
  /**
29
33
  * Live RUN state for one node, supplied by the host (keyed by node id) and
30
34
  * merged onto {@link WfNodeData.state} at render time. Absent ⇒ the graph shows
@@ -256,6 +260,15 @@ interface WfGraph {
256
260
  edges: WfEdge[];
257
261
  /** Set when the YAML couldn't be parsed into a renderable graph. */
258
262
  error: string | null;
263
+ /** The separators this layout pitched its layers and lanes at, so a second
264
+ * layouter (see `elk-layout.ts`) keeps the same density. Absent on an error. */
265
+ spacing?: WfSpacing;
266
+ }
267
+ /** Gaps between layers (`rank`, along the flow) and between nodes stacked in
268
+ * one layer (`cross`). */
269
+ interface WfSpacing {
270
+ rank: number;
271
+ cross: number;
259
272
  }
260
273
  /** Main-axis flow direction: "LR" (left-to-right, default) suits the wide/short
261
274
  * run-detail panel; "TB" (top-to-bottom) suits a narrow column. */
@@ -322,8 +335,9 @@ interface WorkflowGraphProps {
322
335
  yaml: string;
323
336
  /** "full" = interactive (pan/zoom/drag + controls); "preview" = static fit. */
324
337
  variant?: "full" | "preview";
325
- /** Flow direction: "LR" (default) reads left-to-right — best for the wide/short
326
- * run-detail panel; "TB" is a vertical column. */
338
+ /** Flow direction: "LR" reads left-to-right — best for the wide/short
339
+ * run-detail panel; "TB" is a vertical column. Unset, it follows the
340
+ * canvas: "LR" in a panel, "TB" in a portrait canvas such as a phone's. */
327
341
  direction?: WfDirection;
328
342
  /**
329
343
  * Start collapsed (compact icon tiles) rather than expanded.
package/dist/workflows.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { i as modelBrandFor, t as ModelBrandStack } from "./model-brand-BSr9on8C.js";
2
- import { A as triggerNodeIndex, D as buildWorkflowGraph, E as branchNodeId, F as parseActionPath, I as shortModel, N as actionPathLabel, P as humanizeIdentifier, T as actionNodeId, a as NodeMark, d as STATUS_LABEL, f as STATUS_PILL, h as StatusPill, i as KIND_ICON, k as triggerNodeId, n as classifyOutput, p as STATUS_TONE, r as condenseText, t as NodeOutputBody, u as STATUS_COLOR, w as TRIGGER_NODE_ID } from "./node-output-CHlf3tni.js";
2
+ import { A as triggerNodeIndex, D as buildWorkflowGraph, E as branchNodeId, F as parseActionPath, I as shortModel, N as actionPathLabel, P as humanizeIdentifier, T as actionNodeId, a as NodeMark, d as STATUS_LABEL, f as STATUS_PILL, h as StatusPill, i as KIND_ICON, k as triggerNodeId, n as classifyOutput, p as STATUS_TONE, r as condenseText, t as NodeOutputBody, u as STATUS_COLOR, w as TRIGGER_NODE_ID } from "./node-output-Bs51H68g.js";
3
3
  import { jsx, jsxs } from "react/jsx-runtime";
4
4
  import { Component, Suspense, lazy, useMemo, useState } from "react";
5
5
  //#region src/workflows/WorkflowGraphLazy.tsx
@@ -30,7 +30,7 @@ function retryImport(factory, retries = 2, delayMs = 350) {
30
30
  });
31
31
  }
32
32
  function lazyWorkflowGraph() {
33
- return lazy(() => retryImport(() => import("./WorkflowGraph-Dz6aA2Mp.js").then((m) => ({ default: m.WorkflowGraph }))));
33
+ return lazy(() => retryImport(() => import("./WorkflowGraph-WgonmxDh.js").then((m) => ({ default: m.WorkflowGraph }))));
34
34
  }
35
35
  var GraphErrorBoundary = class extends Component {
36
36
  state = { hasError: false };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/sandbox-ui",
3
- "version": "0.134.11",
3
+ "version": "0.135.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": {
@@ -146,6 +146,7 @@
146
146
  "@xterm/addon-webgl": "^0.19.0",
147
147
  "@xterm/xterm": "^6.0.0",
148
148
  "@xyflow/react": "^12.0.0",
149
+ "elkjs": "^0.12.0",
149
150
  "pdfjs-dist": ">=6.0.0 <7.0.0",
150
151
  "react": "^18 || ^19",
151
152
  "react-dom": "^18 || ^19",
@@ -191,6 +192,9 @@
191
192
  "@xyflow/react": {
192
193
  "optional": true
193
194
  },
195
+ "elkjs": {
196
+ "optional": true
197
+ },
194
198
  "pdfjs-dist": {
195
199
  "optional": true
196
200
  },
@@ -239,10 +243,12 @@
239
243
  "@types/react": "^19.3.0",
240
244
  "@types/react-dom": "^19.3.0",
241
245
  "@xterm/addon-fit": "^0.11.0",
246
+ "@xterm/addon-unicode-graphemes": "^0.4.0",
242
247
  "@xterm/addon-web-links": "^0.12.0",
243
248
  "@xterm/addon-webgl": "^0.19.0",
244
249
  "@xterm/xterm": "^6.0.0",
245
250
  "@xyflow/react": "^12.12.0",
251
+ "elkjs": "0.12.0",
246
252
  "jsdom": "^30.1.1",
247
253
  "pdfjs-dist": "^6.4.299",
248
254
  "playwright": "^1.63.0",
@@ -1,203 +0,0 @@
1
- import { t as usePtySession } from "./use-pty-session-Bf4hBLXJ.js";
2
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
- import { useCallback, useEffect, useMemo, useRef } from "react";
4
- import "@xterm/xterm/css/xterm.css";
5
- import { Terminal } from "@xterm/xterm";
6
- import { FitAddon } from "@xterm/addon-fit";
7
- import { WebLinksAddon } from "@xterm/addon-web-links";
8
- //#region src/terminal/terminal-view.tsx
9
- const DEFAULT_TERMINAL_THEME = {
10
- background: "#0c0c0e",
11
- foreground: "#d4d4d8",
12
- cursor: "#34d399",
13
- cursorAccent: "#0c0c0e",
14
- selectionBackground: "#7c3aed33",
15
- selectionForeground: "#d4d4d8",
16
- black: "#18181b",
17
- red: "#ef4444",
18
- green: "#34d399",
19
- yellow: "#fbbf24",
20
- blue: "#60a5fa",
21
- magenta: "#a78bfa",
22
- cyan: "#22d3ee",
23
- white: "#d4d4d8",
24
- brightBlack: "#52525b",
25
- brightRed: "#f87171",
26
- brightGreen: "#6ee7b7",
27
- brightYellow: "#fde68a",
28
- brightBlue: "#93c5fd",
29
- brightMagenta: "#c4b5fd",
30
- brightCyan: "#67e8f9",
31
- brightWhite: "#fafafa"
32
- };
33
- function TerminalView({ session, apiUrl, token, theme, title = "Terminal", subtitle = "Connected to PTY session", isActive = true, connectionId, incarnationId, control, fontSize = 13 }) {
34
- const resolvedTheme = useMemo(() => ({
35
- ...DEFAULT_TERMINAL_THEME,
36
- ...theme
37
- }), [theme]);
38
- const resolvedFontSize = Number.isFinite(fontSize) && fontSize > 0 ? fontSize : 13;
39
- const fontSizeRef = useRef(resolvedFontSize);
40
- fontSizeRef.current = resolvedFontSize;
41
- const containerRef = useRef(null);
42
- const termRef = useRef(null);
43
- const fitAddonRef = useRef(null);
44
- const pendingWritesRef = useRef([]);
45
- const writeRafRef = useRef(null);
46
- const onData = useCallback((data) => {
47
- if (!data) return;
48
- pendingWritesRef.current.push(data);
49
- if (writeRafRef.current !== null) return;
50
- writeRafRef.current = requestAnimationFrame(() => {
51
- writeRafRef.current = null;
52
- const chunks = pendingWritesRef.current;
53
- if (chunks.length === 0) return;
54
- pendingWritesRef.current = [];
55
- termRef.current?.write(chunks.length === 1 ? chunks[0] : chunks.join(""));
56
- });
57
- }, []);
58
- const usesCallerSession = session !== void 0;
59
- const shellSession = usePtySession({
60
- enabled: !usesCallerSession,
61
- apiUrl,
62
- token,
63
- onData,
64
- connectionId,
65
- incarnationId,
66
- control
67
- });
68
- const { isConnected, error, sendCommand, resizeTerminal, reconnect } = session ?? shellSession;
69
- const subscribeData = session?.subscribeData;
70
- useEffect(() => subscribeData?.(onData), [subscribeData, onData]);
71
- useEffect(() => {
72
- if (!containerRef.current) return;
73
- const term = new Terminal({
74
- theme: resolvedTheme,
75
- fontFamily: "\"JetBrains Mono\", \"Fira Code\", \"Cascadia Code\", Menlo, monospace",
76
- fontSize: fontSizeRef.current,
77
- lineHeight: 1.4,
78
- cursorBlink: true,
79
- cursorStyle: "bar",
80
- scrollback: 5e3,
81
- convertEol: true,
82
- allowProposedApi: true
83
- });
84
- const fitAddon = new FitAddon();
85
- const webLinksAddon = new WebLinksAddon();
86
- term.loadAddon(fitAddon);
87
- term.loadAddon(webLinksAddon);
88
- term.open(containerRef.current);
89
- let webglAddon = null;
90
- let webglCancelled = false;
91
- (async () => {
92
- try {
93
- const mod = await import("@xterm/addon-webgl");
94
- if (webglCancelled) return;
95
- try {
96
- const addon = new mod.WebglAddon();
97
- addon.onContextLoss(() => {
98
- webglAddon?.dispose();
99
- webglAddon = null;
100
- });
101
- webglAddon = addon;
102
- term.loadAddon(addon);
103
- } catch {}
104
- } catch {}
105
- })();
106
- requestAnimationFrame(() => {
107
- fitAddon.fit();
108
- });
109
- termRef.current = term;
110
- fitAddonRef.current = fitAddon;
111
- term.onData((data) => {
112
- if (!usesCallerSession && data === "\f") {
113
- termRef.current?.clear();
114
- sendCommand("\r").catch(console.error);
115
- return;
116
- }
117
- sendCommand(data).catch((err) => {
118
- termRef.current?.writeln(`\r\n\x1b[31m${err instanceof Error ? err.message : "Send failed"}\x1b[0m`);
119
- });
120
- });
121
- term.onResize(({ cols, rows }) => {
122
- resizeTerminal(cols, rows).catch(console.error);
123
- });
124
- const ro = new ResizeObserver(() => {
125
- requestAnimationFrame(() => {
126
- fitAddon.fit();
127
- });
128
- });
129
- ro.observe(containerRef.current);
130
- return () => {
131
- webglCancelled = true;
132
- ro.disconnect();
133
- if (writeRafRef.current !== null) {
134
- cancelAnimationFrame(writeRafRef.current);
135
- writeRafRef.current = null;
136
- }
137
- pendingWritesRef.current = [];
138
- webglAddon?.dispose();
139
- term.dispose();
140
- termRef.current = null;
141
- fitAddonRef.current = null;
142
- };
143
- }, [
144
- sendCommand,
145
- resizeTerminal,
146
- title,
147
- subtitle,
148
- usesCallerSession
149
- ]);
150
- useEffect(() => {
151
- if (termRef.current) termRef.current.options.theme = resolvedTheme;
152
- }, [resolvedTheme]);
153
- useEffect(() => {
154
- const term = termRef.current;
155
- if (!term || term.options.fontSize === resolvedFontSize) return;
156
- term.options.fontSize = resolvedFontSize;
157
- const rafId = requestAnimationFrame(() => fitAddonRef.current?.fit());
158
- return () => cancelAnimationFrame(rafId);
159
- }, [resolvedFontSize]);
160
- useEffect(() => {
161
- if (isConnected && termRef.current) {
162
- resizeTerminal(termRef.current.cols, termRef.current.rows).catch(console.error);
163
- if (isActive) termRef.current.focus();
164
- }
165
- }, [
166
- isConnected,
167
- resizeTerminal,
168
- isActive
169
- ]);
170
- useEffect(() => {
171
- if (isActive && termRef.current && fitAddonRef.current) setTimeout(() => {
172
- fitAddonRef.current?.fit();
173
- termRef.current?.focus();
174
- }, 50);
175
- }, [isActive]);
176
- return /* @__PURE__ */ jsxs("div", {
177
- className: "relative h-full w-full group cursor-text",
178
- onClick: () => termRef.current?.focus(),
179
- children: [/* @__PURE__ */ jsx("div", {
180
- ref: containerRef,
181
- className: "h-full w-full overflow-hidden relative z-0",
182
- style: { backgroundColor: resolvedTheme.background }
183
- }), (!isConnected || error) && /* @__PURE__ */ jsx("div", {
184
- className: "absolute inset-0 flex items-center justify-center bg-surface-container-lowest",
185
- children: /* @__PURE__ */ jsx("div", {
186
- className: "text-center",
187
- children: error ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("p", {
188
- className: "text-sm text-[var(--surface-danger-text)] mb-3",
189
- children: error
190
- }), /* @__PURE__ */ jsx("button", {
191
- onClick: reconnect,
192
- className: "text-sm text-[var(--surface-success-text)] hover:opacity-80 underline cursor-pointer",
193
- children: "Retry connection"
194
- })] }) : /* @__PURE__ */ jsx("p", {
195
- className: "text-[13px] text-muted-foreground",
196
- children: "Connecting to terminal..."
197
- })
198
- })
199
- })]
200
- });
201
- }
202
- //#endregion
203
- export { TerminalView as n, DEFAULT_TERMINAL_THEME as t };