@tangle-network/sandbox-ui 0.3.12 → 0.3.13

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.
Files changed (37) hide show
  1. package/README.md +7 -1
  2. package/dist/auth.js +2 -3
  3. package/dist/{chunk-QMKWQF6F.js → chunk-DJEZKF5A.js} +3 -2
  4. package/dist/chunk-DLCFZDGX.js +182 -0
  5. package/dist/{chunk-FOQTE67I.js → chunk-FJLS7PNT.js} +9 -4
  6. package/dist/chunk-HXEA7L2T.js +1401 -0
  7. package/dist/{chunk-IAIJUFM6.js → chunk-HYLTXGOI.js} +1 -1
  8. package/dist/{chunk-MCGKDCOR.js → chunk-IW2JZCOC.js} +55 -14
  9. package/dist/{chunk-PCTEG6HR.js → chunk-OHMO7NUX.js} +2 -4
  10. package/dist/{chunk-DMYYQXPN.js → chunk-SMBF6HB5.js} +646 -465
  11. package/dist/dashboard.d.ts +1 -1
  12. package/dist/dashboard.js +40 -6
  13. package/dist/{document-editor-pane-AVKKXSLG.js → document-editor-pane-5TN2VWGZ.js} +1 -1
  14. package/dist/{document-editor-pane-Xnl8SmA7.d.ts → document-editor-pane-A70-EhdQ.d.ts} +1 -1
  15. package/dist/editor.d.ts +2 -2
  16. package/dist/editor.js +1 -1
  17. package/dist/files.d.ts +1 -1
  18. package/dist/files.js +1 -1
  19. package/dist/hooks.d.ts +1 -1
  20. package/dist/hooks.js +2 -2
  21. package/dist/index-D7_ZDkwB.d.ts +375 -0
  22. package/dist/index.d.ts +5 -3
  23. package/dist/index.js +74 -16
  24. package/dist/pages.d.ts +12 -2
  25. package/dist/pages.js +60 -5
  26. package/dist/primitives.js +4 -6
  27. package/dist/sdk-hooks.js +1 -1
  28. package/dist/terminal.d.ts +2 -2
  29. package/dist/terminal.js +9 -39
  30. package/dist/{use-pty-session-DeZSxOCN.d.ts → use-pty-session-COzVkhtc.d.ts} +1 -1
  31. package/dist/workspace.d.ts +3 -1
  32. package/dist/workspace.js +2 -2
  33. package/package.json +1 -1
  34. package/dist/chunk-4HT5J6CE.js +0 -11001
  35. package/dist/chunk-B26TQ7SA.js +0 -47
  36. package/dist/chunk-GRYHFH5O.js +0 -110
  37. package/dist/index-BJIPTCKk.d.ts +0 -264
package/README.md CHANGED
@@ -222,6 +222,12 @@ For that, compose directly from:
222
222
 
223
223
  Retheming is absolutely supported, but the documentation was thinner than it should be. The token layer is strong; the higher-level surfaces are themeable, but more opinionated. For a radically different product look, prefer keeping the token contract and wrapping the higher-level workbench/chat surfaces rather than fighting every internal class.
224
224
 
225
+ ## Docs
226
+
227
+ | Guide | Description |
228
+ |-------|-------------|
229
+ | [Sidebar](./docs/sidebar.md) | Composable Rail + Panel sidebar system (architecture, components, full API) |
230
+
225
231
  ## Subpath Exports
226
232
 
227
233
  | Subpath | Description |
@@ -232,7 +238,7 @@ Retheming is absolutely supported, but the documentation was thinner than it sho
232
238
  | `/workspace` | SandboxWorkbench, WorkspaceLayout, DirectoryPane, RuntimePane, StatusBar |
233
239
  | `/openui` | OpenUIArtifactRenderer and schema types for structured artifact rendering |
234
240
  | `/files` | FileTree, FilePreview, FileTabs, FileArtifactPane |
235
- | `/dashboard` | DashboardLayout, BillingDashboard, UsageChart, ProfileSelector |
241
+ | `/dashboard` | [Sidebar](./docs/sidebar.md), DashboardLayout, BillingDashboard, UsageChart, ProfileSelector |
236
242
  | `/editor` | TipTap collaborative editor (requires optional peers) |
237
243
  | `/terminal` | xterm.js terminal view (requires optional peers) |
238
244
  | `/markdown` | Markdown renderer with GFM, code blocks, copy button |
package/dist/auth.js CHANGED
@@ -3,9 +3,8 @@ import {
3
3
  GitHubLoginButton,
4
4
  LoginLayout,
5
5
  UserMenu
6
- } from "./chunk-PCTEG6HR.js";
7
- import "./chunk-B26TQ7SA.js";
8
- import "./chunk-MCGKDCOR.js";
6
+ } from "./chunk-OHMO7NUX.js";
7
+ import "./chunk-IW2JZCOC.js";
9
8
  import "./chunk-HWLX5NME.js";
10
9
  import "./chunk-RQHJBTEU.js";
11
10
  export {
@@ -18,7 +18,7 @@ import {
18
18
  FileArtifactPane,
19
19
  FileTree,
20
20
  filterFileTree
21
- } from "./chunk-IAIJUFM6.js";
21
+ } from "./chunk-HYLTXGOI.js";
22
22
  import {
23
23
  ArtifactPane
24
24
  } from "./chunk-W4LM3QYZ.js";
@@ -712,6 +712,7 @@ function RuntimePane({
712
712
  subtitle = "Session state, execution output, and inspection surfaces",
713
713
  statusBanner,
714
714
  statusBar,
715
+ content,
715
716
  terminal,
716
717
  audit,
717
718
  inspector,
@@ -733,7 +734,7 @@ function RuntimePane({
733
734
  ] }),
734
735
  statusBanner && /* @__PURE__ */ jsx6(StatusBanner, { ...statusBanner }),
735
736
  statusBar && /* @__PURE__ */ jsx6(StatusBar, { ...statusBar }),
736
- /* @__PURE__ */ jsxs6("div", { className: "grid min-h-0 flex-1 gap-px bg-[var(--border-subtle)] lg:grid-cols-[minmax(0,1.35fr)_minmax(20rem,0.9fr)]", children: [
737
+ content ? /* @__PURE__ */ jsx6("div", { className: "min-h-0 flex-1 overflow-hidden", children: content }) : /* @__PURE__ */ jsxs6("div", { className: "grid min-h-0 flex-1 gap-px bg-[var(--border-subtle)] lg:grid-cols-[minmax(0,1.35fr)_minmax(20rem,0.9fr)]", children: [
737
738
  /* @__PURE__ */ jsx6("div", { className: "min-h-0 overflow-auto bg-[var(--bg-card)]", children: terminal ? /* @__PURE__ */ jsx6(
738
739
  TerminalPanel,
739
740
  {
@@ -0,0 +1,182 @@
1
+ // src/hooks/use-pty-session.ts
2
+ import { useState, useEffect, useRef, useCallback } from "react";
3
+ function usePtySession({ apiUrl, token, onData }) {
4
+ const [isConnected, setIsConnected] = useState(false);
5
+ const [error, setError] = useState(null);
6
+ const sessionIdRef = useRef(null);
7
+ const abortRef = useRef(null);
8
+ const retryTimerRef = useRef(void 0);
9
+ const retryCountRef = useRef(0);
10
+ const mountedRef = useRef(true);
11
+ const onDataRef = useRef(onData);
12
+ onDataRef.current = onData;
13
+ const connectStreamRef = useRef(null);
14
+ const abortStream = useCallback(() => {
15
+ if (retryTimerRef.current) {
16
+ clearTimeout(retryTimerRef.current);
17
+ retryTimerRef.current = void 0;
18
+ }
19
+ if (abortRef.current) {
20
+ abortRef.current.abort();
21
+ abortRef.current = null;
22
+ }
23
+ }, []);
24
+ const cleanup = useCallback(() => {
25
+ abortStream();
26
+ if (sessionIdRef.current) {
27
+ const sid = sessionIdRef.current;
28
+ sessionIdRef.current = null;
29
+ fetch(`${apiUrl}/terminals/${sid}`, {
30
+ method: "DELETE",
31
+ headers: { Authorization: `Bearer ${token}` },
32
+ credentials: "include"
33
+ }).catch(() => {
34
+ });
35
+ }
36
+ setIsConnected(false);
37
+ }, [apiUrl, token, abortStream]);
38
+ const connectStream = useCallback(async (sessionId) => {
39
+ abortStream();
40
+ setError(null);
41
+ try {
42
+ const controller = new AbortController();
43
+ abortRef.current = controller;
44
+ const streamRes = await fetch(`${apiUrl}/terminals/${sessionId}/stream`, {
45
+ headers: { Authorization: `Bearer ${token}` },
46
+ credentials: "include",
47
+ signal: controller.signal
48
+ });
49
+ if (!streamRes.ok || !streamRes.body) {
50
+ const err = new Error(`SSE stream failed: ${streamRes.status}`);
51
+ err.httpStatus = streamRes.status;
52
+ throw err;
53
+ }
54
+ if (mountedRef.current) {
55
+ setIsConnected(true);
56
+ setError(null);
57
+ retryCountRef.current = 0;
58
+ }
59
+ const reader = streamRes.body.getReader();
60
+ const decoder = new TextDecoder();
61
+ let buffer = "";
62
+ while (true) {
63
+ const { done, value } = await reader.read();
64
+ if (done) break;
65
+ buffer += decoder.decode(value, { stream: true });
66
+ const frames = buffer.split("\n\n");
67
+ buffer = frames.pop() ?? "";
68
+ for (const frame of frames) {
69
+ if (!frame.trim()) continue;
70
+ for (const line of frame.split("\n")) {
71
+ if (line.startsWith("data:")) {
72
+ const raw = line.slice(5).trim();
73
+ if (!raw) continue;
74
+ try {
75
+ const event = JSON.parse(raw);
76
+ if (event.type === "data.stdout" || event.type === "data.stderr") {
77
+ const text = event.properties?.text ?? "";
78
+ if (text && mountedRef.current) {
79
+ onDataRef.current(text);
80
+ }
81
+ }
82
+ } catch {
83
+ if (mountedRef.current) {
84
+ onDataRef.current(raw);
85
+ }
86
+ }
87
+ }
88
+ }
89
+ }
90
+ }
91
+ if (mountedRef.current) {
92
+ setIsConnected(false);
93
+ retryTimerRef.current = setTimeout(() => {
94
+ if (mountedRef.current && sessionIdRef.current) {
95
+ connectStreamRef.current?.(sessionIdRef.current);
96
+ }
97
+ }, 1e3);
98
+ }
99
+ } catch (err) {
100
+ if (err.name === "AbortError") return;
101
+ if (mountedRef.current) {
102
+ const message = err instanceof Error ? err.message : "Stream connection failed";
103
+ setError(message);
104
+ setIsConnected(false);
105
+ const httpStatus = err.httpStatus;
106
+ const is4xx = httpStatus !== void 0 && httpStatus >= 400 && httpStatus < 500;
107
+ const MAX_RETRIES = 8;
108
+ if (!is4xx && retryCountRef.current < MAX_RETRIES) {
109
+ const delay = Math.min(3e3 * Math.pow(2, retryCountRef.current), 3e4);
110
+ retryCountRef.current++;
111
+ retryTimerRef.current = setTimeout(() => {
112
+ if (mountedRef.current && sessionIdRef.current) {
113
+ connectStreamRef.current?.(sessionIdRef.current);
114
+ }
115
+ }, delay);
116
+ }
117
+ }
118
+ }
119
+ }, [apiUrl, token, abortStream]);
120
+ connectStreamRef.current = connectStream;
121
+ const connect = useCallback(async () => {
122
+ cleanup();
123
+ retryCountRef.current = 0;
124
+ setError(null);
125
+ try {
126
+ const res = await fetch(`${apiUrl}/terminals`, {
127
+ method: "POST",
128
+ headers: {
129
+ Authorization: `Bearer ${token}`,
130
+ "Content-Type": "application/json"
131
+ },
132
+ credentials: "include"
133
+ });
134
+ if (!res.ok) {
135
+ throw new Error(`Failed to create terminal: ${res.status}`);
136
+ }
137
+ const body = await res.json();
138
+ const sessionId = body.data?.sessionId ?? body.sessionId;
139
+ if (!sessionId) throw new Error("No sessionId in response");
140
+ if (!mountedRef.current) return;
141
+ sessionIdRef.current = sessionId;
142
+ await connectStream(sessionId);
143
+ } catch (err) {
144
+ if (err.name === "AbortError") return;
145
+ if (mountedRef.current) {
146
+ const message = err instanceof Error ? err.message : "Terminal connection failed";
147
+ setError(message);
148
+ setIsConnected(false);
149
+ }
150
+ }
151
+ }, [apiUrl, token, cleanup, connectStream]);
152
+ const sendCommand = useCallback(async (command) => {
153
+ const sid = sessionIdRef.current;
154
+ if (!sid) return;
155
+ const res = await fetch(`${apiUrl}/terminals/${sid}/input`, {
156
+ method: "POST",
157
+ headers: {
158
+ Authorization: `Bearer ${token}`,
159
+ "Content-Type": "application/json"
160
+ },
161
+ credentials: "include",
162
+ body: JSON.stringify({ data: command })
163
+ });
164
+ if (!res.ok) {
165
+ const text = await res.text();
166
+ throw new Error(text || `Input failed: ${res.status}`);
167
+ }
168
+ }, [apiUrl, token]);
169
+ useEffect(() => {
170
+ mountedRef.current = true;
171
+ connect();
172
+ return () => {
173
+ mountedRef.current = false;
174
+ cleanup();
175
+ };
176
+ }, [connect, cleanup]);
177
+ return { isConnected, error, sendCommand, reconnect: connect };
178
+ }
179
+
180
+ export {
181
+ usePtySession
182
+ };
@@ -1040,7 +1040,7 @@ function mapApiMessage(msg) {
1040
1040
  async function fetchJson(url, token, init) {
1041
1041
  const headers = { Authorization: `Bearer ${token}` };
1042
1042
  if (init?.body) headers["Content-Type"] = "application/json";
1043
- const res = await fetch(url, { ...init, headers: { ...headers, ...init?.headers } });
1043
+ const res = await fetch(url, { ...init, headers: { ...headers, ...init?.headers }, credentials: "include" });
1044
1044
  if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`);
1045
1045
  return res.json();
1046
1046
  }
@@ -1057,6 +1057,7 @@ function useSessionStream({
1057
1057
  const [connected, setConnected] = useState5(false);
1058
1058
  const abortRef = useRef5(null);
1059
1059
  const streamingMsgIdRef = useRef5(null);
1060
+ const handleSSEEventRef = useRef5(null);
1060
1061
  const refetch = useCallback4(async () => {
1061
1062
  if (!token || !sessionId || !apiUrl) return;
1062
1063
  try {
@@ -1087,7 +1088,8 @@ function useSessionStream({
1087
1088
  const url = `${apiUrl}/session/events?sessionId=${encodeURIComponent(sessionId)}`;
1088
1089
  const res = await fetch(url, {
1089
1090
  headers: { Authorization: `Bearer ${token}` },
1090
- signal: controller.signal
1091
+ signal: controller.signal,
1092
+ credentials: "include"
1091
1093
  });
1092
1094
  if (!res.ok) throw new Error(`SSE connection failed: ${res.status}`);
1093
1095
  setConnected(true);
@@ -1120,7 +1122,7 @@ function useSessionStream({
1120
1122
  } catch {
1121
1123
  continue;
1122
1124
  }
1123
- handleSSEEvent(eventType, parsed);
1125
+ handleSSEEventRef.current?.(eventType, parsed);
1124
1126
  }
1125
1127
  }
1126
1128
  } catch (err) {
@@ -1133,7 +1135,9 @@ function useSessionStream({
1133
1135
  }
1134
1136
  }
1135
1137
  }, [apiUrl, token, sessionId, enabled]);
1136
- const handleSSEEvent = useCallback4((type, props) => {
1138
+ const handleSSEEvent = useCallback4((type, raw) => {
1139
+ const envelope = raw?.properties;
1140
+ const props = envelope?.info ?? envelope?.part ?? envelope ?? raw;
1137
1141
  if (type === "message.updated") {
1138
1142
  const id = props.id ?? props.messageId ?? "";
1139
1143
  const role = props.role ?? "assistant";
@@ -1219,6 +1223,7 @@ function useSessionStream({
1219
1223
  refetch();
1220
1224
  }
1221
1225
  }, [refetch]);
1226
+ handleSSEEventRef.current = handleSSEEvent;
1222
1227
  const send = useCallback4(async (text) => {
1223
1228
  if (!token || !sessionId || !apiUrl) return;
1224
1229
  try {