@anvia/react-ui 1.3.0 → 1.3.2
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/attachment/index.d.ts +3 -21
- package/dist/attachment/index.js +3 -12
- package/dist/attachment-By2pged3.js +91 -0
- package/dist/attachment-By2pged3.js.map +1 -0
- package/dist/chat/index.d.ts +3 -152
- package/dist/chat/index.js +3 -25
- package/dist/chat-Dgz8hsVT.js +1210 -0
- package/dist/chat-Dgz8hsVT.js.map +1 -0
- package/dist/completion/index.d.ts +3 -29
- package/dist/completion/index.js +3 -16
- package/dist/completion-DDTKoZRJ.js +124 -0
- package/dist/completion-DDTKoZRJ.js.map +1 -0
- package/dist/connection-DeNq8wHS.js +203 -0
- package/dist/connection-DeNq8wHS.js.map +1 -0
- package/dist/contexts-B4ijG24z.js +212 -0
- package/dist/contexts-B4ijG24z.js.map +1 -0
- package/dist/elements-D4vmkkF8.js +19 -0
- package/dist/elements-D4vmkkF8.js.map +1 -0
- package/dist/graph-explorer/index.d.ts +54 -73
- package/dist/graph-explorer/index.d.ts.map +1 -0
- package/dist/graph-explorer/index.js +181 -248
- package/dist/graph-explorer/index.js.map +1 -1
- package/dist/human-input/index.d.ts +3 -78
- package/dist/human-input/index.js +3 -21
- package/dist/human-input-CsCbZomZ.js +314 -0
- package/dist/human-input-CsCbZomZ.js.map +1 -0
- package/dist/image/index.d.ts +3 -40
- package/dist/image/index.js +3 -12
- package/dist/image-aCWm6hFe.js +303 -0
- package/dist/image-aCWm6hFe.js.map +1 -0
- package/dist/index-B1Qs2Cjv.d.ts +145 -0
- package/dist/index-B1Qs2Cjv.d.ts.map +1 -0
- package/dist/index-BCBVcl0D.d.ts +277 -0
- package/dist/index-BCBVcl0D.d.ts.map +1 -0
- package/dist/index-CE22tsua.d.ts +20 -0
- package/dist/index-CE22tsua.d.ts.map +1 -0
- package/dist/index-CGqtpc6b.d.ts +25 -0
- package/dist/index-CGqtpc6b.d.ts.map +1 -0
- package/dist/index-DqdyM0Jm.d.ts +7 -0
- package/dist/index-DqdyM0Jm.d.ts.map +1 -0
- package/dist/index-Mw3ltwSG.d.ts +318 -0
- package/dist/index-Mw3ltwSG.d.ts.map +1 -0
- package/dist/index.d.ts +7 -40
- package/dist/index.js +34 -118
- package/dist/index.js.map +1 -1
- package/dist/message/index.d.ts +3 -105
- package/dist/message/index.js +3 -20
- package/dist/message-CElFEKgL.js +783 -0
- package/dist/message-CElFEKgL.js.map +1 -0
- package/dist/primitives-xfE7zfwJ.js +15 -0
- package/dist/primitives-xfE7zfwJ.js.map +1 -0
- package/dist/refs-Djj9rSvB.js +10 -0
- package/dist/refs-Djj9rSvB.js.map +1 -0
- package/dist/sandbox-desktop/index.d.ts +100 -104
- package/dist/sandbox-desktop/index.d.ts.map +1 -0
- package/dist/sandbox-desktop/index.js +344 -342
- package/dist/sandbox-desktop/index.js.map +1 -1
- package/dist/sandbox-terminal/index.d.ts +52 -56
- package/dist/sandbox-terminal/index.d.ts.map +1 -0
- package/dist/sandbox-terminal/index.js +217 -208
- package/dist/sandbox-terminal/index.js.map +1 -1
- package/dist/selection-toolbar/index.d.ts +3 -21
- package/dist/selection-toolbar/index.js +3 -13
- package/dist/selection-toolbar-CIkOoM-7.js +171 -0
- package/dist/selection-toolbar-CIkOoM-7.js.map +1 -0
- package/dist/shared-BSYElFU2.d.ts +2 -0
- package/dist/shared.d.ts +4 -14
- package/dist/shared.js +1 -43
- package/dist/stream/index.d.ts +2 -22
- package/dist/stream/index.js +1 -8
- package/dist/stream-CAO0x8_1.js +298 -0
- package/dist/stream-CAO0x8_1.js.map +1 -0
- package/dist/thread-list/index.d.ts +3 -47
- package/dist/thread-list/index.js +3 -18
- package/dist/thread-list-xJbGXFrE.js +228 -0
- package/dist/thread-list-xJbGXFrE.js.map +1 -0
- package/dist/ticket-CoJHBpdF.d.ts +32 -0
- package/dist/ticket-CoJHBpdF.d.ts.map +1 -0
- package/dist/url-CiYZkDks.js +52 -0
- package/dist/url-CiYZkDks.js.map +1 -0
- package/dist/value-BGGrx72L.js +13 -0
- package/dist/value-BGGrx72L.js.map +1 -0
- package/package.json +22 -22
- package/dist/attachment/index.js.map +0 -1
- package/dist/attachment-mbjsroli.d.ts +0 -9
- package/dist/chat/index.js.map +0 -1
- package/dist/chat-PSj1Fo4W.d.ts +0 -88
- package/dist/chunk-22EZJUPM.js +0 -120
- package/dist/chunk-22EZJUPM.js.map +0 -1
- package/dist/chunk-5FZKTDZR.js +0 -201
- package/dist/chunk-5FZKTDZR.js.map +0 -1
- package/dist/chunk-6XOJFLX4.js +0 -22
- package/dist/chunk-6XOJFLX4.js.map +0 -1
- package/dist/chunk-CKA4CE26.js +0 -25
- package/dist/chunk-CKA4CE26.js.map +0 -1
- package/dist/chunk-DLBNFBXQ.js +0 -425
- package/dist/chunk-DLBNFBXQ.js.map +0 -1
- package/dist/chunk-E73S3SOF.js +0 -404
- package/dist/chunk-E73S3SOF.js.map +0 -1
- package/dist/chunk-F6AKBUO3.js +0 -380
- package/dist/chunk-F6AKBUO3.js.map +0 -1
- package/dist/chunk-HGOPGZNQ.js +0 -195
- package/dist/chunk-HGOPGZNQ.js.map +0 -1
- package/dist/chunk-KHB5CHAU.js +0 -220
- package/dist/chunk-KHB5CHAU.js.map +0 -1
- package/dist/chunk-LHABOAOP.js +0 -316
- package/dist/chunk-LHABOAOP.js.map +0 -1
- package/dist/chunk-N5XQU36W.js +0 -410
- package/dist/chunk-N5XQU36W.js.map +0 -1
- package/dist/chunk-T3ZFZ3NP.js +0 -30
- package/dist/chunk-T3ZFZ3NP.js.map +0 -1
- package/dist/chunk-T4IYUEPH.js +0 -1612
- package/dist/chunk-T4IYUEPH.js.map +0 -1
- package/dist/chunk-W2AHKMRS.js +0 -1092
- package/dist/chunk-W2AHKMRS.js.map +0 -1
- package/dist/completion/index.js.map +0 -1
- package/dist/completion-input-DHlwrhxB.d.ts +0 -24
- package/dist/composer-6iCSIq6L.d.ts +0 -111
- package/dist/human-input/index.js.map +0 -1
- package/dist/human-input-CkLrZ8IC.d.ts +0 -27
- package/dist/image/index.js.map +0 -1
- package/dist/image-VZv6Ddsn.d.ts +0 -14
- package/dist/message/index.js.map +0 -1
- package/dist/message-CNRnZQwU.d.ts +0 -14
- package/dist/render-CRKWam2v.d.ts +0 -8
- package/dist/selection-toolbar/index.js.map +0 -1
- package/dist/selection-toolbar-Cwjbuv5S.d.ts +0 -14
- package/dist/shared.js.map +0 -1
- package/dist/stream/index.js.map +0 -1
- package/dist/thread-Bz6-1zaJ.d.ts +0 -12
- package/dist/thread-list/index.js.map +0 -1
- package/dist/thread-list-On8db4XL.d.ts +0 -34
- package/dist/ticket-pexI1zIa.d.ts +0 -33
|
@@ -1,356 +1,358 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
resolveTicket,
|
|
5
|
-
ticketSubprotocolPrefix
|
|
6
|
-
} from "../chunk-5FZKTDZR.js";
|
|
7
|
-
|
|
8
|
-
// src/sandbox-desktop/desktop.tsx
|
|
9
|
-
import {
|
|
10
|
-
forwardRef,
|
|
11
|
-
useEffect,
|
|
12
|
-
useImperativeHandle,
|
|
13
|
-
useRef,
|
|
14
|
-
useState
|
|
15
|
-
} from "react";
|
|
16
|
-
|
|
1
|
+
import { i as DefaultStatus, n as ticketSubprotocolPrefix, r as resolveTicket, t as RelayConnection } from "../connection-DeNq8wHS.js";
|
|
2
|
+
import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from "react";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
17
4
|
// src/sandbox-desktop/vnc-channel.ts
|
|
18
|
-
|
|
19
|
-
|
|
5
|
+
const OPEN = 1;
|
|
6
|
+
const CLOSED = 3;
|
|
7
|
+
/**
|
|
8
|
+
* A WebSocket-shaped channel for noVNC to attach to. The hub's desktop relay mixes binary VNC frames
|
|
9
|
+
* with JSON control frames on one socket, and noVNC understands only the former, so the link
|
|
10
|
+
* hands this channel just the binary ones.
|
|
11
|
+
*
|
|
12
|
+
* The VNC server speaks first, so frames can arrive before noVNC has attached. They wait here
|
|
13
|
+
* until it installs its `onmessage`.
|
|
14
|
+
*
|
|
15
|
+
* noVNC checks a channel for these property names (`send`, `close`, `binaryType`, `onerror`,
|
|
16
|
+
* `onmessage`, `onopen`, `protocol`, `readyState`), as it does for a WebSocket or an
|
|
17
|
+
* RTCDataChannel, so they must all be present.
|
|
18
|
+
*/
|
|
20
19
|
var VncChannel = class {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
20
|
+
link;
|
|
21
|
+
binaryType = "arraybuffer";
|
|
22
|
+
protocol;
|
|
23
|
+
onerror = null;
|
|
24
|
+
onopen = null;
|
|
25
|
+
onclose = null;
|
|
26
|
+
handler = null;
|
|
27
|
+
backlog = [];
|
|
28
|
+
state = OPEN;
|
|
29
|
+
constructor(link, protocol = "") {
|
|
30
|
+
this.link = link;
|
|
31
|
+
this.protocol = protocol;
|
|
32
|
+
}
|
|
33
|
+
get readyState() {
|
|
34
|
+
return this.state;
|
|
35
|
+
}
|
|
36
|
+
get onmessage() {
|
|
37
|
+
return this.handler;
|
|
38
|
+
}
|
|
39
|
+
set onmessage(handler) {
|
|
40
|
+
this.handler = handler;
|
|
41
|
+
if (handler === null) return;
|
|
42
|
+
for (const data of this.backlog.splice(0)) handler({ data });
|
|
43
|
+
}
|
|
44
|
+
send(data) {
|
|
45
|
+
if (this.state === OPEN) this.link.send(data);
|
|
46
|
+
}
|
|
47
|
+
/** noVNC is done with the connection. */
|
|
48
|
+
close() {
|
|
49
|
+
if (this.state !== OPEN) return;
|
|
50
|
+
this.state = CLOSED;
|
|
51
|
+
this.link.close();
|
|
52
|
+
}
|
|
53
|
+
/** A VNC frame arrived from the relay. */
|
|
54
|
+
receive(data) {
|
|
55
|
+
if (this.handler === null) this.backlog.push(data);
|
|
56
|
+
else this.handler({ data });
|
|
57
|
+
}
|
|
58
|
+
/** The relay closed the socket. */
|
|
59
|
+
closed(event) {
|
|
60
|
+
if (this.state === CLOSED) return;
|
|
61
|
+
this.state = CLOSED;
|
|
62
|
+
this.backlog = [];
|
|
63
|
+
this.onclose?.(event);
|
|
64
|
+
}
|
|
66
65
|
};
|
|
67
|
-
|
|
68
66
|
// src/sandbox-desktop/connection.ts
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
67
|
+
/** Subprotocol the hub's `handleDesktopUpgrade` selects. */
|
|
68
|
+
const desktopSubprotocol = "anvia.desktop.v1";
|
|
69
|
+
/** Errors worth another attempt: the sandbox may simply not be reachable yet. */
|
|
70
|
+
const retryableCodes = /* @__PURE__ */ new Set(["sandbox_unavailable", "timeout"]);
|
|
71
|
+
/** The hub closes a socket it could not set up with `<error code>: <message>`. */
|
|
72
|
+
const closeReason = /^([a-z_]+): (.*)$/s;
|
|
73
|
+
/** The relay closes with this code when the viewer sent something it could not account for. */
|
|
74
|
+
const policyViolation = 1008;
|
|
75
|
+
/**
|
|
76
|
+
* One browser link to the hub's desktop relay, over a {@link RelayConnection}. It hands each
|
|
77
|
+
* accepted attachment to the owner as a channel for a VNC client, and speaks the relay's small
|
|
78
|
+
* JSON control protocol (`takeover`, `release`, and the `control` updates that come back).
|
|
79
|
+
*/
|
|
73
80
|
var DesktopConnection = class {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
this.relay.sendJson({ type: "release" });
|
|
138
|
-
}
|
|
139
|
-
dispose() {
|
|
140
|
-
this.relay.dispose();
|
|
141
|
-
}
|
|
81
|
+
relay;
|
|
82
|
+
constructor(options) {
|
|
83
|
+
let channel;
|
|
84
|
+
let accepted = false;
|
|
85
|
+
const protocol = {
|
|
86
|
+
subprotocol: desktopSubprotocol,
|
|
87
|
+
label: "desktop",
|
|
88
|
+
open: (socket) => {
|
|
89
|
+
accepted = false;
|
|
90
|
+
channel = new VncChannel({
|
|
91
|
+
send: (data) => socket.send(data),
|
|
92
|
+
close: () => socket.close(1e3)
|
|
93
|
+
}, socket.protocol);
|
|
94
|
+
},
|
|
95
|
+
message: (event, session) => {
|
|
96
|
+
if (!accepted && channel !== void 0) {
|
|
97
|
+
accepted = true;
|
|
98
|
+
options.onChannel(channel, session.ready());
|
|
99
|
+
}
|
|
100
|
+
if (typeof event.data !== "string") {
|
|
101
|
+
channel?.receive(event.data);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const message = parseText(event.data);
|
|
105
|
+
if (message?.type === "control") {
|
|
106
|
+
const { type: _type, ...control } = message;
|
|
107
|
+
options.onControl(control);
|
|
108
|
+
} else if (message?.type === "error") options.onNotice?.({
|
|
109
|
+
code: message.code ?? "protocol",
|
|
110
|
+
message: message.message ?? "The request failed."
|
|
111
|
+
});
|
|
112
|
+
},
|
|
113
|
+
close: (event, session) => {
|
|
114
|
+
channel?.closed(event);
|
|
115
|
+
const match = closeReason.exec(event.reason);
|
|
116
|
+
if (match !== null && !retryableCodes.has(match[1] ?? "")) session.fatal(match[2] ?? "The desktop could not be opened.");
|
|
117
|
+
else if (event.code === policyViolation) session.fatal(event.reason || "The desktop relay refused what the viewer sent.");
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
this.relay = new RelayConnection({
|
|
121
|
+
url: options.url,
|
|
122
|
+
getTicket: options.getTicket,
|
|
123
|
+
onStatus: options.onStatus,
|
|
124
|
+
reconnect: options.reconnect,
|
|
125
|
+
protocol
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
start() {
|
|
129
|
+
this.relay.start();
|
|
130
|
+
}
|
|
131
|
+
retry() {
|
|
132
|
+
this.relay.retry();
|
|
133
|
+
}
|
|
134
|
+
/** Ask for control, which pauses the agent's inputs. The answer arrives as a `control` update. */
|
|
135
|
+
takeOver() {
|
|
136
|
+
this.relay.sendJson({ type: "takeover" });
|
|
137
|
+
}
|
|
138
|
+
release() {
|
|
139
|
+
this.relay.sendJson({ type: "release" });
|
|
140
|
+
}
|
|
141
|
+
dispose() {
|
|
142
|
+
this.relay.dispose();
|
|
143
|
+
}
|
|
142
144
|
};
|
|
143
145
|
function parseText(text) {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
146
|
+
try {
|
|
147
|
+
const value = JSON.parse(text);
|
|
148
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
149
|
+
const type = value.type;
|
|
150
|
+
return type === "control" || type === "error" ? value : void 0;
|
|
151
|
+
} catch {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
152
154
|
}
|
|
153
|
-
|
|
154
155
|
// src/sandbox-desktop/desktop.tsx
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
156
|
+
const defaultQualityLevel = 6;
|
|
157
|
+
const defaultCompressionLevel = 2;
|
|
158
|
+
/** A level noVNC accepts: a whole number from 0 to 9. Anything else falls back to the default. */
|
|
158
159
|
function level(value, fallback) {
|
|
159
|
-
|
|
160
|
-
|
|
160
|
+
if (value === void 0 || !Number.isFinite(value)) return fallback;
|
|
161
|
+
return Math.min(9, Math.max(0, Math.round(value)));
|
|
161
162
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
url,
|
|
167
|
-
ticket,
|
|
168
|
-
readOnly = false,
|
|
169
|
-
scaleToFit = true,
|
|
170
|
-
qualityLevel,
|
|
171
|
-
compressionLevel,
|
|
172
|
-
reconnect,
|
|
173
|
-
onStatusChange,
|
|
174
|
-
onControlChange,
|
|
175
|
-
renderStatus,
|
|
176
|
-
renderControls,
|
|
177
|
-
style,
|
|
178
|
-
...rest
|
|
179
|
-
}, ref) {
|
|
180
|
-
const screen = useRef(null);
|
|
181
|
-
const connection = useRef(void 0);
|
|
182
|
-
const viewer = useRef(void 0);
|
|
183
|
-
const [status, setStatus] = useState({ state: "loading" });
|
|
184
|
-
const [control, setControl] = useState(void 0);
|
|
185
|
-
const [notice, setNotice] = useState(void 0);
|
|
186
|
-
const viewOnly = readOnly || control?.readOnly === true || control?.you !== true;
|
|
187
|
-
const latest = useRef({
|
|
188
|
-
ticket,
|
|
189
|
-
reconnect,
|
|
190
|
-
onStatusChange,
|
|
191
|
-
onControlChange,
|
|
192
|
-
scaleToFit,
|
|
193
|
-
viewOnly,
|
|
194
|
-
quality: level(qualityLevel, defaultQualityLevel),
|
|
195
|
-
compression: level(compressionLevel, defaultCompressionLevel)
|
|
196
|
-
});
|
|
197
|
-
latest.current = {
|
|
198
|
-
ticket,
|
|
199
|
-
reconnect,
|
|
200
|
-
onStatusChange,
|
|
201
|
-
onControlChange,
|
|
202
|
-
scaleToFit,
|
|
203
|
-
viewOnly,
|
|
204
|
-
quality: level(qualityLevel, defaultQualityLevel),
|
|
205
|
-
compression: level(compressionLevel, defaultCompressionLevel)
|
|
206
|
-
};
|
|
207
|
-
useImperativeHandle(
|
|
208
|
-
ref,
|
|
209
|
-
() => ({
|
|
210
|
-
focus: () => viewer.current?.focus(),
|
|
211
|
-
reconnect: () => connection.current?.retry(),
|
|
212
|
-
takeOver: () => connection.current?.takeOver(),
|
|
213
|
-
release: () => connection.current?.release()
|
|
214
|
-
}),
|
|
215
|
-
[]
|
|
216
|
-
);
|
|
217
|
-
useEffect(() => {
|
|
218
|
-
if (viewer.current !== void 0) viewer.current.viewOnly = viewOnly;
|
|
219
|
-
}, [viewOnly]);
|
|
220
|
-
useEffect(() => {
|
|
221
|
-
if (viewer.current !== void 0) viewer.current.scaleViewport = scaleToFit;
|
|
222
|
-
}, [scaleToFit]);
|
|
223
|
-
useEffect(() => {
|
|
224
|
-
if (viewer.current !== void 0) viewer.current.qualityLevel = latest.current.quality;
|
|
225
|
-
}, [qualityLevel]);
|
|
226
|
-
useEffect(() => {
|
|
227
|
-
if (viewer.current !== void 0) {
|
|
228
|
-
viewer.current.compressionLevel = latest.current.compression;
|
|
229
|
-
}
|
|
230
|
-
}, [compressionLevel]);
|
|
231
|
-
useEffect(() => {
|
|
232
|
-
const container = screen.current;
|
|
233
|
-
if (container === null) return void 0;
|
|
234
|
-
let disposed = false;
|
|
235
|
-
let teardown;
|
|
236
|
-
const publish = (next) => {
|
|
237
|
-
if (disposed) return;
|
|
238
|
-
setStatus(next);
|
|
239
|
-
if (next.state !== "open") setControl(void 0);
|
|
240
|
-
latest.current.onStatusChange?.(next);
|
|
241
|
-
};
|
|
242
|
-
publish({ state: "loading" });
|
|
243
|
-
void (async () => {
|
|
244
|
-
const { default: Rfb } = await import("@novnc/novnc");
|
|
245
|
-
if (disposed) return;
|
|
246
|
-
const link = new DesktopConnection({
|
|
247
|
-
url,
|
|
248
|
-
getTicket: resolveTicket(() => latest.current.ticket),
|
|
249
|
-
reconnect: latest.current.reconnect,
|
|
250
|
-
onStatus: publish,
|
|
251
|
-
onChannel: (channel) => {
|
|
252
|
-
viewer.current?.disconnect();
|
|
253
|
-
const rfb = new Rfb(container, channel);
|
|
254
|
-
rfb.scaleViewport = latest.current.scaleToFit;
|
|
255
|
-
rfb.clipViewport = false;
|
|
256
|
-
rfb.resizeSession = false;
|
|
257
|
-
rfb.viewOnly = latest.current.viewOnly;
|
|
258
|
-
rfb.qualityLevel = latest.current.quality;
|
|
259
|
-
rfb.compressionLevel = latest.current.compression;
|
|
260
|
-
viewer.current = rfb;
|
|
261
|
-
},
|
|
262
|
-
onControl: (next) => {
|
|
263
|
-
if (disposed) return;
|
|
264
|
-
setControl(next);
|
|
265
|
-
setNotice(void 0);
|
|
266
|
-
latest.current.onControlChange?.(next);
|
|
267
|
-
},
|
|
268
|
-
onNotice: (next) => {
|
|
269
|
-
if (!disposed) setNotice(next);
|
|
270
|
-
}
|
|
271
|
-
});
|
|
272
|
-
connection.current = link;
|
|
273
|
-
link.start();
|
|
274
|
-
teardown = () => {
|
|
275
|
-
link.dispose();
|
|
276
|
-
viewer.current?.disconnect();
|
|
277
|
-
viewer.current = void 0;
|
|
278
|
-
connection.current = void 0;
|
|
279
|
-
};
|
|
280
|
-
})().catch((error) => {
|
|
281
|
-
publish({
|
|
282
|
-
state: "failed",
|
|
283
|
-
message: error instanceof Error ? error.message : "Could not load the desktop viewer."
|
|
284
|
-
});
|
|
285
|
-
});
|
|
286
|
-
return () => {
|
|
287
|
-
disposed = true;
|
|
288
|
-
teardown?.();
|
|
289
|
-
};
|
|
290
|
-
}, [url]);
|
|
291
|
-
const retry = () => connection.current?.retry();
|
|
292
|
-
const takeOver = () => connection.current?.takeOver();
|
|
293
|
-
const release = () => connection.current?.release();
|
|
294
|
-
const statusNode = renderStatus === void 0 ? /* @__PURE__ */ jsx(DefaultStatus, { status, retry }) : renderStatus(status, retry);
|
|
295
|
-
const controlsState = {
|
|
296
|
-
status,
|
|
297
|
-
control,
|
|
298
|
-
readOnly: readOnly || control?.readOnly === true,
|
|
299
|
-
notice,
|
|
300
|
-
takeOver,
|
|
301
|
-
release
|
|
302
|
-
};
|
|
303
|
-
const controlsNode = renderControls === void 0 ? /* @__PURE__ */ jsx(DefaultControls, { ...controlsState }) : renderControls(controlsState);
|
|
304
|
-
return /* @__PURE__ */ jsxs(
|
|
305
|
-
"div",
|
|
306
|
-
{
|
|
307
|
-
...rest,
|
|
308
|
-
"data-state": status.state,
|
|
309
|
-
"data-readonly": viewOnly ? "" : void 0,
|
|
310
|
-
"data-control": control?.you === true ? "you" : control?.held === true ? "other" : "none",
|
|
311
|
-
style: { ...rootStyle, ...style },
|
|
312
|
-
children: [
|
|
313
|
-
/* @__PURE__ */ jsx("div", { ref: screen, style: screenStyle }),
|
|
314
|
-
controlsNode,
|
|
315
|
-
statusNode
|
|
316
|
-
]
|
|
317
|
-
}
|
|
318
|
-
);
|
|
319
|
-
}
|
|
320
|
-
);
|
|
321
|
-
var controlsStyle = {
|
|
322
|
-
position: "absolute",
|
|
323
|
-
top: 8,
|
|
324
|
-
left: 8,
|
|
325
|
-
zIndex: 1,
|
|
326
|
-
display: "flex",
|
|
327
|
-
gap: 8,
|
|
328
|
-
alignItems: "center",
|
|
329
|
-
padding: "2px 8px",
|
|
330
|
-
borderRadius: 4,
|
|
331
|
-
fontSize: 12,
|
|
332
|
-
background: "rgba(0, 0, 0, 0.7)",
|
|
333
|
-
color: "#fff"
|
|
163
|
+
const rootStyle = {
|
|
164
|
+
position: "relative",
|
|
165
|
+
width: "100%",
|
|
166
|
+
height: "100%"
|
|
334
167
|
};
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
168
|
+
const screenStyle = {
|
|
169
|
+
width: "100%",
|
|
170
|
+
height: "100%"
|
|
171
|
+
};
|
|
172
|
+
/**
|
|
173
|
+
* A viewer for a remote sandbox's desktop, drawn with noVNC. noVNC is loaded on demand, so it costs
|
|
174
|
+
* nothing until a desktop mounts. It connects to the hub's desktop relay, reconnects with a fresh
|
|
175
|
+
* ticket when the link drops, and lets a person take over control from the agent.
|
|
176
|
+
*/
|
|
177
|
+
const SandboxDesktop = forwardRef(function SandboxDesktop({ url, ticket, readOnly = false, scaleToFit = true, qualityLevel, compressionLevel, reconnect, onStatusChange, onControlChange, renderStatus, renderControls, style, ...rest }, ref) {
|
|
178
|
+
const screen = useRef(null);
|
|
179
|
+
const connection = useRef(void 0);
|
|
180
|
+
const viewer = useRef(void 0);
|
|
181
|
+
const [status, setStatus] = useState({ state: "loading" });
|
|
182
|
+
const [control, setControl] = useState(void 0);
|
|
183
|
+
const [notice, setNotice] = useState(void 0);
|
|
184
|
+
const viewOnly = readOnly || control?.readOnly === true || control?.you !== true;
|
|
185
|
+
const latest = useRef({
|
|
186
|
+
ticket,
|
|
187
|
+
reconnect,
|
|
188
|
+
onStatusChange,
|
|
189
|
+
onControlChange,
|
|
190
|
+
scaleToFit,
|
|
191
|
+
viewOnly,
|
|
192
|
+
quality: level(qualityLevel, defaultQualityLevel),
|
|
193
|
+
compression: level(compressionLevel, defaultCompressionLevel)
|
|
194
|
+
});
|
|
195
|
+
latest.current = {
|
|
196
|
+
ticket,
|
|
197
|
+
reconnect,
|
|
198
|
+
onStatusChange,
|
|
199
|
+
onControlChange,
|
|
200
|
+
scaleToFit,
|
|
201
|
+
viewOnly,
|
|
202
|
+
quality: level(qualityLevel, defaultQualityLevel),
|
|
203
|
+
compression: level(compressionLevel, defaultCompressionLevel)
|
|
204
|
+
};
|
|
205
|
+
useImperativeHandle(ref, () => ({
|
|
206
|
+
focus: () => viewer.current?.focus(),
|
|
207
|
+
reconnect: () => connection.current?.retry(),
|
|
208
|
+
takeOver: () => connection.current?.takeOver(),
|
|
209
|
+
release: () => connection.current?.release()
|
|
210
|
+
}), []);
|
|
211
|
+
useEffect(() => {
|
|
212
|
+
if (viewer.current !== void 0) viewer.current.viewOnly = viewOnly;
|
|
213
|
+
}, [viewOnly]);
|
|
214
|
+
useEffect(() => {
|
|
215
|
+
if (viewer.current !== void 0) viewer.current.scaleViewport = scaleToFit;
|
|
216
|
+
}, [scaleToFit]);
|
|
217
|
+
useEffect(() => {
|
|
218
|
+
if (viewer.current !== void 0) viewer.current.qualityLevel = latest.current.quality;
|
|
219
|
+
}, [qualityLevel]);
|
|
220
|
+
useEffect(() => {
|
|
221
|
+
if (viewer.current !== void 0) viewer.current.compressionLevel = latest.current.compression;
|
|
222
|
+
}, [compressionLevel]);
|
|
223
|
+
useEffect(() => {
|
|
224
|
+
const container = screen.current;
|
|
225
|
+
if (container === null) return void 0;
|
|
226
|
+
let disposed = false;
|
|
227
|
+
let teardown;
|
|
228
|
+
const publish = (next) => {
|
|
229
|
+
if (disposed) return;
|
|
230
|
+
setStatus(next);
|
|
231
|
+
if (next.state !== "open") setControl(void 0);
|
|
232
|
+
latest.current.onStatusChange?.(next);
|
|
233
|
+
};
|
|
234
|
+
publish({ state: "loading" });
|
|
235
|
+
(async () => {
|
|
236
|
+
const { default: Rfb } = await import("@novnc/novnc");
|
|
237
|
+
if (disposed) return;
|
|
238
|
+
const link = new DesktopConnection({
|
|
239
|
+
url,
|
|
240
|
+
getTicket: resolveTicket(() => latest.current.ticket),
|
|
241
|
+
reconnect: latest.current.reconnect,
|
|
242
|
+
onStatus: publish,
|
|
243
|
+
onChannel: (channel) => {
|
|
244
|
+
viewer.current?.disconnect();
|
|
245
|
+
const rfb = new Rfb(container, channel);
|
|
246
|
+
rfb.scaleViewport = latest.current.scaleToFit;
|
|
247
|
+
rfb.clipViewport = false;
|
|
248
|
+
rfb.resizeSession = false;
|
|
249
|
+
rfb.viewOnly = latest.current.viewOnly;
|
|
250
|
+
rfb.qualityLevel = latest.current.quality;
|
|
251
|
+
rfb.compressionLevel = latest.current.compression;
|
|
252
|
+
viewer.current = rfb;
|
|
253
|
+
},
|
|
254
|
+
onControl: (next) => {
|
|
255
|
+
if (disposed) return;
|
|
256
|
+
setControl(next);
|
|
257
|
+
setNotice(void 0);
|
|
258
|
+
latest.current.onControlChange?.(next);
|
|
259
|
+
},
|
|
260
|
+
onNotice: (next) => {
|
|
261
|
+
if (!disposed) setNotice(next);
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
connection.current = link;
|
|
265
|
+
link.start();
|
|
266
|
+
teardown = () => {
|
|
267
|
+
link.dispose();
|
|
268
|
+
viewer.current?.disconnect();
|
|
269
|
+
viewer.current = void 0;
|
|
270
|
+
connection.current = void 0;
|
|
271
|
+
};
|
|
272
|
+
})().catch((error) => {
|
|
273
|
+
publish({
|
|
274
|
+
state: "failed",
|
|
275
|
+
message: error instanceof Error ? error.message : "Could not load the desktop viewer."
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
return () => {
|
|
279
|
+
disposed = true;
|
|
280
|
+
teardown?.();
|
|
281
|
+
};
|
|
282
|
+
}, [url]);
|
|
283
|
+
const retry = () => connection.current?.retry();
|
|
284
|
+
const takeOver = () => connection.current?.takeOver();
|
|
285
|
+
const release = () => connection.current?.release();
|
|
286
|
+
const statusNode = renderStatus === void 0 ? /* @__PURE__ */ jsx(DefaultStatus, {
|
|
287
|
+
status,
|
|
288
|
+
retry
|
|
289
|
+
}) : renderStatus(status, retry);
|
|
290
|
+
const controlsState = {
|
|
291
|
+
status,
|
|
292
|
+
control,
|
|
293
|
+
readOnly: readOnly || control?.readOnly === true,
|
|
294
|
+
notice,
|
|
295
|
+
takeOver,
|
|
296
|
+
release
|
|
297
|
+
};
|
|
298
|
+
const controlsNode = renderControls === void 0 ? /* @__PURE__ */ jsx(DefaultControls, { ...controlsState }) : renderControls(controlsState);
|
|
299
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
300
|
+
...rest,
|
|
301
|
+
"data-state": status.state,
|
|
302
|
+
"data-readonly": viewOnly ? "" : void 0,
|
|
303
|
+
"data-control": control?.you === true ? "you" : control?.held === true ? "other" : "none",
|
|
304
|
+
style: {
|
|
305
|
+
...rootStyle,
|
|
306
|
+
...style
|
|
307
|
+
},
|
|
308
|
+
children: [
|
|
309
|
+
/* @__PURE__ */ jsx("div", {
|
|
310
|
+
ref: screen,
|
|
311
|
+
style: screenStyle
|
|
312
|
+
}),
|
|
313
|
+
controlsNode,
|
|
314
|
+
statusNode
|
|
315
|
+
]
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
const controlsStyle = {
|
|
319
|
+
position: "absolute",
|
|
320
|
+
top: 8,
|
|
321
|
+
left: 8,
|
|
322
|
+
zIndex: 1,
|
|
323
|
+
display: "flex",
|
|
324
|
+
gap: 8,
|
|
325
|
+
alignItems: "center",
|
|
326
|
+
padding: "2px 8px",
|
|
327
|
+
borderRadius: 4,
|
|
328
|
+
fontSize: 12,
|
|
329
|
+
background: "rgba(0, 0, 0, 0.7)",
|
|
330
|
+
color: "#fff"
|
|
355
331
|
};
|
|
332
|
+
function DefaultControls({ status, control, readOnly, notice, takeOver, release }) {
|
|
333
|
+
if (status.state !== "open" || control === void 0) return null;
|
|
334
|
+
const who = control.you ? "You have control" : control.held ? `${control.holder ?? "Someone"} has control` : readOnly ? "View only" : "The agent has control";
|
|
335
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
336
|
+
"data-sandbox-desktop-controls": "",
|
|
337
|
+
style: controlsStyle,
|
|
338
|
+
children: [
|
|
339
|
+
/* @__PURE__ */ jsx("span", { children: who }),
|
|
340
|
+
control.you ? /* @__PURE__ */ jsx("button", {
|
|
341
|
+
type: "button",
|
|
342
|
+
onClick: release,
|
|
343
|
+
children: "Release"
|
|
344
|
+
}) : !readOnly && !control.held ? /* @__PURE__ */ jsx("button", {
|
|
345
|
+
type: "button",
|
|
346
|
+
onClick: takeOver,
|
|
347
|
+
children: "Take over"
|
|
348
|
+
}) : null,
|
|
349
|
+
notice === void 0 ? null : /* @__PURE__ */ jsx("span", {
|
|
350
|
+
role: "alert",
|
|
351
|
+
children: notice.message
|
|
352
|
+
})
|
|
353
|
+
]
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
export { SandboxDesktop, desktopSubprotocol, ticketSubprotocolPrefix };
|
|
357
|
+
|
|
356
358
|
//# sourceMappingURL=index.js.map
|