@tangle-network/sandbox-ui 0.12.0 → 0.14.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,6 +1,6 @@
1
1
  import {
2
2
  usePtySession
3
- } from "./chunk-ESVYBDGA.js";
3
+ } from "./chunk-AG7QDC2Q.js";
4
4
 
5
5
  // src/terminal/terminal-view.tsx
6
6
  import "@xterm/xterm/css/xterm.css";
@@ -48,8 +48,19 @@ function TerminalView({
48
48
  const containerRef = useRef(null);
49
49
  const termRef = useRef(null);
50
50
  const fitAddonRef = useRef(null);
51
+ const pendingWritesRef = useRef([]);
52
+ const writeRafRef = useRef(null);
51
53
  const onData = useCallback((data) => {
52
- termRef.current?.write(data);
54
+ if (!data) return;
55
+ pendingWritesRef.current.push(data);
56
+ if (writeRafRef.current !== null) return;
57
+ writeRafRef.current = requestAnimationFrame(() => {
58
+ writeRafRef.current = null;
59
+ const chunks = pendingWritesRef.current;
60
+ if (chunks.length === 0) return;
61
+ pendingWritesRef.current = [];
62
+ termRef.current?.write(chunks.length === 1 ? chunks[0] : chunks.join(""));
63
+ });
53
64
  }, []);
54
65
  const { isConnected, error, sendCommand, resizeTerminal, reconnect } = usePtySession({
55
66
  apiUrl,
@@ -74,6 +85,25 @@ function TerminalView({
74
85
  term.loadAddon(fitAddon);
75
86
  term.loadAddon(webLinksAddon);
76
87
  term.open(containerRef.current);
88
+ let webglAddon = null;
89
+ let webglCancelled = false;
90
+ void (async () => {
91
+ try {
92
+ const mod = await import("@xterm/addon-webgl");
93
+ if (webglCancelled) return;
94
+ try {
95
+ const addon = new mod.WebglAddon();
96
+ addon.onContextLoss(() => {
97
+ webglAddon?.dispose();
98
+ webglAddon = null;
99
+ });
100
+ webglAddon = addon;
101
+ term.loadAddon(addon);
102
+ } catch {
103
+ }
104
+ } catch {
105
+ }
106
+ })();
77
107
  requestAnimationFrame(() => {
78
108
  fitAddon.fit();
79
109
  });
@@ -102,7 +132,14 @@ function TerminalView({
102
132
  });
103
133
  ro.observe(containerRef.current);
104
134
  return () => {
135
+ webglCancelled = true;
105
136
  ro.disconnect();
137
+ if (writeRafRef.current !== null) {
138
+ cancelAnimationFrame(writeRafRef.current);
139
+ writeRafRef.current = null;
140
+ }
141
+ pendingWritesRef.current = [];
142
+ webglAddon?.dispose();
106
143
  term.dispose();
107
144
  termRef.current = null;
108
145
  fitAddonRef.current = null;
@@ -46,7 +46,7 @@ interface UsePtySessionOptions {
46
46
  onData: (data: string) => void;
47
47
  }
48
48
  interface UsePtySessionReturn {
49
- /** Whether the SSE stream is connected and receiving data. */
49
+ /** Whether the underlying transport is connected and receiving data. */
50
50
  isConnected: boolean;
51
51
  /** Connection or API error, if any. */
52
52
  error: string | null;
@@ -60,11 +60,23 @@ interface UsePtySessionReturn {
60
60
  /**
61
61
  * Manages a PTY session against the sidecar terminal API.
62
62
  *
63
- * Protocol:
64
- * - POST /terminals create session → { data: { sessionId } }
65
- * - GET /terminals/{id}/stream → SSE output (raw PTY with ANSI codes)
66
- * - POST /terminals/{id}/input send input { data: "..." }
67
- * - DELETE /terminals/{id} close session
63
+ * Transport:
64
+ * 1. POST /terminals creates the session.
65
+ * 2. The hook tries WebSocket: GET /terminals/:id/ws (Upgrade).
66
+ * - Serverclient: TEXT frames carrying raw PTY output.
67
+ * - Clientserver: BINARY frames carrying stdin (UTF-8); TEXT
68
+ * frames carrying JSON control messages (`{"type":"resize",...}`).
69
+ * 3. If the WS does not reach OPEN within WS_OPEN_TIMEOUT_MS, or it
70
+ * errors before opening, the hook falls back to SSE+POST:
71
+ * - GET /terminals/:id/stream (SSE for output)
72
+ * - POST /terminals/:id/input (one batched POST at a time)
73
+ * - PATCH /terminals/:id (resize)
74
+ * 4. DELETE /terminals/:id closes the session (both transports).
75
+ *
76
+ * The WS path eliminates the per-keystroke HTTP round-trip that
77
+ * dominates typing latency through edge proxies; the HTTP+SSE path is
78
+ * preserved as a fallback so the hook keeps working against
79
+ * deployments that have not yet shipped the WS endpoint.
68
80
  */
69
81
  declare function usePtySession({ apiUrl, token, onData }: UsePtySessionOptions): UsePtySessionReturn;
70
82
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/sandbox-ui",
3
- "version": "0.12.0",
3
+ "version": "0.14.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",
@@ -105,6 +105,7 @@
105
105
  "test:watch": "vitest"
106
106
  },
107
107
  "peerDependencies": {
108
+ "@tangle-network/brand": "^0.2.0",
108
109
  "react": "^18 || ^19",
109
110
  "react-dom": "^18 || ^19"
110
111
  },
@@ -118,6 +119,9 @@
118
119
  "@xterm/addon-web-links": {
119
120
  "optional": true
120
121
  },
122
+ "@xterm/addon-webgl": {
123
+ "optional": true
124
+ },
121
125
  "@tanstack/react-query": {
122
126
  "optional": true
123
127
  },
@@ -189,6 +193,7 @@
189
193
  "@storybook/react-vite": "^8.6.18",
190
194
  "@tailwindcss/postcss": "^4.2.2",
191
195
  "@tailwindcss/vite": "^4.2.2",
196
+ "@tangle-network/brand": "^0.2.0",
192
197
  "@tanstack/react-query": "^5.91.0",
193
198
  "@testing-library/dom": "^10.4.1",
194
199
  "@testing-library/jest-dom": "^6.9.1",
@@ -204,9 +209,11 @@
204
209
  "@types/turndown": "^5.0.6",
205
210
  "@xterm/addon-fit": "^0.11.0",
206
211
  "@xterm/addon-web-links": "^0.12.0",
212
+ "@xterm/addon-webgl": "^0.19.0",
207
213
  "@xterm/xterm": "^6.0.0",
208
214
  "jsdom": "^29.0.2",
209
215
  "postcss": "^8.5.8",
216
+ "postcss-import": "^16.1.1",
210
217
  "react": "^19.1.0",
211
218
  "react-dom": "^19.1.0",
212
219
  "storybook": "^8.6.18",