@tangle-network/sandbox-ui 0.13.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/{chunk-ESVYBDGA.js → chunk-AG7QDC2Q.js} +182 -2
- package/dist/globals.css +3 -0
- package/dist/hooks.d.ts +1 -1
- package/dist/hooks.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/styles.css +3 -0
- package/dist/terminal.js +39 -2
- package/dist/{use-sandbox-metrics-B64diPqN.d.ts → use-sandbox-metrics-DWc0k9Xm.d.ts} +18 -6
- package/package.json +5 -1
|
@@ -3,6 +3,38 @@ import { useState, useEffect, useRef, useCallback } from "react";
|
|
|
3
3
|
function createEmptyBatch() {
|
|
4
4
|
return { data: "", waiters: [] };
|
|
5
5
|
}
|
|
6
|
+
var WS_OPEN_TIMEOUT_MS = 1500;
|
|
7
|
+
function toWsUrl(apiUrl, sessionId) {
|
|
8
|
+
try {
|
|
9
|
+
const url = new URL(`${apiUrl}/terminals/${sessionId}/ws`);
|
|
10
|
+
if (url.protocol === "https:") url.protocol = "wss:";
|
|
11
|
+
else if (url.protocol === "http:") url.protocol = "ws:";
|
|
12
|
+
else return null;
|
|
13
|
+
return url.toString();
|
|
14
|
+
} catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
var BEARER_SUBPROTOCOL_PREFIX = "bearer.";
|
|
19
|
+
function toBearerSubprotocol(token) {
|
|
20
|
+
if (typeof btoa === "undefined") return null;
|
|
21
|
+
let encoded;
|
|
22
|
+
try {
|
|
23
|
+
encoded = btoa(token);
|
|
24
|
+
} catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
return `${BEARER_SUBPROTOCOL_PREFIX}${encoded.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "")}`;
|
|
28
|
+
}
|
|
29
|
+
var stdinEncoder = typeof TextEncoder !== "undefined" ? new TextEncoder() : null;
|
|
30
|
+
function encodeStdin(text) {
|
|
31
|
+
if (!stdinEncoder) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
"TextEncoder is unavailable; WebSocket transport cannot encode stdin"
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
return stdinEncoder.encode(text);
|
|
37
|
+
}
|
|
6
38
|
function usePtySession({ apiUrl, token, onData }) {
|
|
7
39
|
const [isConnected, setIsConnected] = useState(false);
|
|
8
40
|
const [error, setError] = useState(null);
|
|
@@ -13,6 +45,8 @@ function usePtySession({ apiUrl, token, onData }) {
|
|
|
13
45
|
const mountedRef = useRef(true);
|
|
14
46
|
const onDataRef = useRef(onData);
|
|
15
47
|
const connectStreamRef = useRef(null);
|
|
48
|
+
const wsRef = useRef(null);
|
|
49
|
+
const pendingWsRef = useRef(null);
|
|
16
50
|
const pendingBatchRef = useRef(createEmptyBatch());
|
|
17
51
|
const drainPromiseRef = useRef(null);
|
|
18
52
|
const inputAbortRef = useRef(null);
|
|
@@ -37,8 +71,27 @@ function usePtySession({ apiUrl, token, onData }) {
|
|
|
37
71
|
abortRef.current = null;
|
|
38
72
|
}
|
|
39
73
|
}, []);
|
|
74
|
+
const closeWs = useCallback(() => {
|
|
75
|
+
const active = wsRef.current;
|
|
76
|
+
if (active) {
|
|
77
|
+
wsRef.current = null;
|
|
78
|
+
try {
|
|
79
|
+
active.close();
|
|
80
|
+
} catch {
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const pending = pendingWsRef.current;
|
|
84
|
+
if (pending) {
|
|
85
|
+
pendingWsRef.current = null;
|
|
86
|
+
try {
|
|
87
|
+
pending.close();
|
|
88
|
+
} catch {
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}, []);
|
|
40
92
|
const cleanup = useCallback(() => {
|
|
41
93
|
abortStream();
|
|
94
|
+
closeWs();
|
|
42
95
|
if (inputAbortRef.current) {
|
|
43
96
|
inputAbortRef.current.abort();
|
|
44
97
|
inputAbortRef.current = null;
|
|
@@ -55,7 +108,110 @@ function usePtySession({ apiUrl, token, onData }) {
|
|
|
55
108
|
}
|
|
56
109
|
rejectPendingInput("Terminal session is not connected");
|
|
57
110
|
setIsConnected(false);
|
|
58
|
-
}, [apiUrl, token, abortStream, rejectPendingInput]);
|
|
111
|
+
}, [apiUrl, token, abortStream, closeWs, rejectPendingInput]);
|
|
112
|
+
const connectWs = useCallback((sessionId) => {
|
|
113
|
+
return new Promise((resolve) => {
|
|
114
|
+
if (typeof WebSocket === "undefined" || stdinEncoder === null) {
|
|
115
|
+
resolve(false);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const wsUrl = toWsUrl(apiUrl, sessionId);
|
|
119
|
+
if (!wsUrl) {
|
|
120
|
+
resolve(false);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const subprotocol = toBearerSubprotocol(token);
|
|
124
|
+
if (!subprotocol) {
|
|
125
|
+
resolve(false);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
let ws;
|
|
129
|
+
try {
|
|
130
|
+
ws = new WebSocket(wsUrl, [subprotocol]);
|
|
131
|
+
} catch {
|
|
132
|
+
resolve(false);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
ws.binaryType = "arraybuffer";
|
|
136
|
+
pendingWsRef.current = ws;
|
|
137
|
+
let opened = false;
|
|
138
|
+
let settled = false;
|
|
139
|
+
const settle = (ok) => {
|
|
140
|
+
if (settled) return;
|
|
141
|
+
settled = true;
|
|
142
|
+
clearTimeout(handshakeTimer);
|
|
143
|
+
resolve(ok);
|
|
144
|
+
};
|
|
145
|
+
const handshakeTimer = setTimeout(() => {
|
|
146
|
+
if (opened) return;
|
|
147
|
+
try {
|
|
148
|
+
ws.close();
|
|
149
|
+
} catch {
|
|
150
|
+
}
|
|
151
|
+
settle(false);
|
|
152
|
+
}, WS_OPEN_TIMEOUT_MS);
|
|
153
|
+
ws.onopen = () => {
|
|
154
|
+
opened = true;
|
|
155
|
+
if (pendingWsRef.current !== ws || !mountedRef.current) {
|
|
156
|
+
try {
|
|
157
|
+
ws.close();
|
|
158
|
+
} catch {
|
|
159
|
+
}
|
|
160
|
+
settle(false);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
pendingWsRef.current = null;
|
|
164
|
+
wsRef.current = ws;
|
|
165
|
+
setIsConnected(true);
|
|
166
|
+
setError(null);
|
|
167
|
+
retryCountRef.current = 0;
|
|
168
|
+
settle(true);
|
|
169
|
+
};
|
|
170
|
+
ws.onmessage = (ev) => {
|
|
171
|
+
if (!mountedRef.current) return;
|
|
172
|
+
const data = ev.data;
|
|
173
|
+
let text;
|
|
174
|
+
if (typeof data === "string") {
|
|
175
|
+
text = data;
|
|
176
|
+
} else if (data instanceof ArrayBuffer) {
|
|
177
|
+
text = new TextDecoder().decode(data);
|
|
178
|
+
} else if (ArrayBuffer.isView(data)) {
|
|
179
|
+
text = new TextDecoder().decode(data);
|
|
180
|
+
} else {
|
|
181
|
+
data.text().then((t) => {
|
|
182
|
+
if (mountedRef.current) onDataRef.current(t);
|
|
183
|
+
}).catch(() => {
|
|
184
|
+
});
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
if (text) onDataRef.current(text);
|
|
188
|
+
};
|
|
189
|
+
ws.onerror = () => {
|
|
190
|
+
};
|
|
191
|
+
ws.onclose = () => {
|
|
192
|
+
if (pendingWsRef.current === ws) {
|
|
193
|
+
pendingWsRef.current = null;
|
|
194
|
+
}
|
|
195
|
+
const wasActive = wsRef.current === ws;
|
|
196
|
+
if (wasActive) {
|
|
197
|
+
wsRef.current = null;
|
|
198
|
+
}
|
|
199
|
+
if (!opened) {
|
|
200
|
+
settle(false);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
if (!wasActive || !mountedRef.current) return;
|
|
204
|
+
setIsConnected(false);
|
|
205
|
+
if (sessionIdRef.current) {
|
|
206
|
+
retryTimerRef.current = setTimeout(() => {
|
|
207
|
+
if (mountedRef.current && sessionIdRef.current) {
|
|
208
|
+
connectStreamRef.current?.(sessionIdRef.current);
|
|
209
|
+
}
|
|
210
|
+
}, 1e3);
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
});
|
|
214
|
+
}, [apiUrl, token]);
|
|
59
215
|
const connectStream = useCallback(async (sessionId) => {
|
|
60
216
|
abortStream();
|
|
61
217
|
setError(null);
|
|
@@ -162,7 +318,12 @@ function usePtySession({ apiUrl, token, onData }) {
|
|
|
162
318
|
if (!mountedRef.current) return;
|
|
163
319
|
sessionIdRef.current = sessionId;
|
|
164
320
|
inputAbortRef.current = new AbortController();
|
|
321
|
+
const wsOk = await connectWs(sessionId);
|
|
322
|
+
if (!mountedRef.current || sessionIdRef.current !== sessionId) return;
|
|
165
323
|
ensureDrainRunningRef.current?.();
|
|
324
|
+
if (wsOk) {
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
166
327
|
await connectStream(sessionId);
|
|
167
328
|
} catch (err) {
|
|
168
329
|
if (err.name === "AbortError") return;
|
|
@@ -172,10 +333,19 @@ function usePtySession({ apiUrl, token, onData }) {
|
|
|
172
333
|
setIsConnected(false);
|
|
173
334
|
}
|
|
174
335
|
}
|
|
175
|
-
}, [apiUrl, token, cleanup, connectStream]);
|
|
336
|
+
}, [apiUrl, token, cleanup, connectWs, connectStream]);
|
|
176
337
|
const resizeTerminal = useCallback(async (cols, rows) => {
|
|
177
338
|
const sid = sessionIdRef.current;
|
|
178
339
|
if (!sid || cols <= 0 || rows <= 0) return;
|
|
340
|
+
const ws = wsRef.current;
|
|
341
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
342
|
+
try {
|
|
343
|
+
ws.send(JSON.stringify({ type: "resize", cols, rows }));
|
|
344
|
+
return;
|
|
345
|
+
} catch (err) {
|
|
346
|
+
console.warn("Terminal resize over WS failed; falling back to HTTP", err);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
179
349
|
try {
|
|
180
350
|
const res = await fetch(`${apiUrl}/terminals/${sid}`, {
|
|
181
351
|
method: "PATCH",
|
|
@@ -201,6 +371,16 @@ function usePtySession({ apiUrl, token, onData }) {
|
|
|
201
371
|
}
|
|
202
372
|
const batch = pendingBatchRef.current;
|
|
203
373
|
pendingBatchRef.current = createEmptyBatch();
|
|
374
|
+
const ws = wsRef.current;
|
|
375
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
376
|
+
try {
|
|
377
|
+
ws.send(encodeStdin(batch.data));
|
|
378
|
+
for (const w of batch.waiters) w.resolve();
|
|
379
|
+
} catch (err) {
|
|
380
|
+
for (const w of batch.waiters) w.reject(err);
|
|
381
|
+
}
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
204
384
|
try {
|
|
205
385
|
const res = await fetch(`${apiUrl}/terminals/${sid}/input`, {
|
|
206
386
|
method: "POST",
|
package/dist/globals.css
CHANGED
package/dist/hooks.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { A as AuthUser, S as SandboxMetrics, a as SidecarMetricsPayload, U as UseAuthOptions, b as UseAuthResult, c as UsePtySessionOptions, d as UsePtySessionReturn, e as UseSandboxMetricsOptions, f as UseSandboxMetricsResult, g as createAuthFetcher, u as useApiKey, h as useAuth, i as useLiveTime, j as usePtySession, k as useSandboxMetrics } from './use-sandbox-metrics-
|
|
1
|
+
export { A as AuthUser, S as SandboxMetrics, a as SidecarMetricsPayload, U as UseAuthOptions, b as UseAuthResult, c as UsePtySessionOptions, d as UsePtySessionReturn, e as UseSandboxMetricsOptions, f as UseSandboxMetricsResult, g as createAuthFetcher, u as useApiKey, h as useAuth, i as useLiveTime, j as usePtySession, k as useSandboxMetrics } from './use-sandbox-metrics-DWc0k9Xm.js';
|
|
2
2
|
export { AgentStreamEvent, AppendUserMessageOptions, ApplySdkEventOptions, AutomationStreamEvent, BeginAssistantMessageOptions, BotStreamEvent, CompleteAssistantMessageOptions, ConnectionState, RealtimeSessionOptions, RealtimeSessionRegistry, RealtimeSessionRegistryProps, RealtimeSessionState, RealtimeSessionTarget, SSEEvent, SdkSessionAttachment, SdkSessionEvent, SdkSessionSeed, SessionInfo, SidecarAuth, TaskStreamEvent, TerminalStreamEvent, UseRunGroupsOptions, UseSSEStreamOptions, UseSSEStreamResult, UseSdkSessionOptions, UseSdkSessionReturn, UseSessionStreamOptions, UseSessionStreamResult, UseSidecarAuthOptions, UseToolCallStreamReturn, useAutoScroll, useDropdownMenu, useRealtimeSession, useRunCollapseState, useRunGroups, useSSEStream, useSdkSession, useSessionStream, useSidecarAuth, useToolCallStream } from './sdk-hooks.js';
|
|
3
3
|
import * as _tanstack_react_query from '@tanstack/react-query';
|
|
4
4
|
import { S as Session } from './sidecar-CFU2W9j1.js';
|
package/dist/hooks.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ export { c as BillingDashboard, d as BillingDashboardProps, e as PricingCards, f
|
|
|
20
20
|
export { AuthHeader, GitHubLoginButton, LoginLayout, LoginLayoutProps, UserMenu } from './auth.js';
|
|
21
21
|
export { CodeBlock, CodeBlock as CodeBlockDisplay, CopyButton, Markdown, MarkdownProps } from './markdown.js';
|
|
22
22
|
export { AppendUserMessageOptions, ApplySdkEventOptions, BeginAssistantMessageOptions, CompleteAssistantMessageOptions, RealtimeSessionOptions, RealtimeSessionRegistry, RealtimeSessionRegistryProps, RealtimeSessionState, RealtimeSessionTarget, SdkSessionAttachment, SdkSessionEvent, SdkSessionSeed, UseSdkSessionOptions, UseSdkSessionReturn, UseToolCallStreamReturn, useAutoScroll, useDropdownMenu, useRealtimeSession, useRunCollapseState, useRunGroups, useSSEStream, useSdkSession, useSessionStream, useSidecarAuth, useToolCallStream } from './sdk-hooks.js';
|
|
23
|
-
export { S as SandboxMetrics, a as SidecarMetricsPayload, e as UseSandboxMetricsOptions, f as UseSandboxMetricsResult, g as createAuthFetcher, u as useApiKey, h as useAuth, i as useLiveTime, j as usePtySession, k as useSandboxMetrics } from './use-sandbox-metrics-
|
|
23
|
+
export { S as SandboxMetrics, a as SidecarMetricsPayload, e as UseSandboxMetricsOptions, f as UseSandboxMetricsResult, g as createAuthFetcher, u as useApiKey, h as useAuth, i as useLiveTime, j as usePtySession, k as useSandboxMetrics } from './use-sandbox-metrics-DWc0k9Xm.js';
|
|
24
24
|
export { G as GroupedMessage, R as Run, b as RunStats, T as ToolCategory } from './run-CtFZ6s-D.js';
|
|
25
25
|
export { S as Session } from './sidecar-CFU2W9j1.js';
|
|
26
26
|
export { C as CustomToolRenderer, D as DisplayVariant, T as ToolDisplayMetadata } from './tool-display-Ct9nFAzJ.js';
|
package/dist/index.js
CHANGED
package/dist/styles.css
CHANGED
package/dist/terminal.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
usePtySession
|
|
3
|
-
} from "./chunk-
|
|
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
|
-
|
|
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
|
|
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
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
63
|
+
* Transport:
|
|
64
|
+
* 1. POST /terminals creates the session.
|
|
65
|
+
* 2. The hook tries WebSocket: GET /terminals/:id/ws (Upgrade).
|
|
66
|
+
* - Server → client: TEXT frames carrying raw PTY output.
|
|
67
|
+
* - Client → server: 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.
|
|
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",
|
|
@@ -119,6 +119,9 @@
|
|
|
119
119
|
"@xterm/addon-web-links": {
|
|
120
120
|
"optional": true
|
|
121
121
|
},
|
|
122
|
+
"@xterm/addon-webgl": {
|
|
123
|
+
"optional": true
|
|
124
|
+
},
|
|
122
125
|
"@tanstack/react-query": {
|
|
123
126
|
"optional": true
|
|
124
127
|
},
|
|
@@ -206,6 +209,7 @@
|
|
|
206
209
|
"@types/turndown": "^5.0.6",
|
|
207
210
|
"@xterm/addon-fit": "^0.11.0",
|
|
208
211
|
"@xterm/addon-web-links": "^0.12.0",
|
|
212
|
+
"@xterm/addon-webgl": "^0.19.0",
|
|
209
213
|
"@xterm/xterm": "^6.0.0",
|
|
210
214
|
"jsdom": "^29.0.2",
|
|
211
215
|
"postcss": "^8.5.8",
|