@anvia/react-ui 1.3.0 → 1.3.1
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-DsKTPumt.js +89 -0
- package/dist/attachment-DsKTPumt.js.map +1 -0
- package/dist/chat/index.d.ts +3 -152
- package/dist/chat/index.js +3 -25
- package/dist/chat-CAnvUEAv.js +1210 -0
- package/dist/chat-CAnvUEAv.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-D_7vaQCR.js +302 -0
- package/dist/image-D_7vaQCR.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-Dn3wW6yy.js +783 -0
- package/dist/message-Dn3wW6yy.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/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,216 +1,225 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
resolveTicket,
|
|
5
|
-
ticketSubprotocolPrefix
|
|
6
|
-
} from "../chunk-5FZKTDZR.js";
|
|
7
|
-
|
|
8
|
-
// src/sandbox-terminal/terminal.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-terminal/connection.ts
|
|
18
|
-
|
|
19
|
-
|
|
5
|
+
/** Subprotocol the hub's `handleTerminalUpgrade` requires. */
|
|
6
|
+
const terminalSubprotocol = "anvia.terminal.v1";
|
|
7
|
+
/** Errors worth another attempt: the sandbox may simply not be reachable yet. */
|
|
8
|
+
const retryableCodes = /* @__PURE__ */ new Set(["sandbox_unavailable", "timeout"]);
|
|
9
|
+
/**
|
|
10
|
+
* One browser terminal link: speaks the hub's terminal relay protocol over a {@link
|
|
11
|
+
* RelayConnection}. It knows nothing about xterm, so it is testable on its own.
|
|
12
|
+
*/
|
|
20
13
|
var TerminalConnection = 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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
14
|
+
relay;
|
|
15
|
+
constructor(options) {
|
|
16
|
+
let ready = false;
|
|
17
|
+
const protocol = {
|
|
18
|
+
subprotocol: terminalSubprotocol,
|
|
19
|
+
label: "terminal",
|
|
20
|
+
open: (socket) => {
|
|
21
|
+
ready = false;
|
|
22
|
+
const { cols, rows } = options.size();
|
|
23
|
+
socket.send(JSON.stringify({
|
|
24
|
+
type: "open",
|
|
25
|
+
cols,
|
|
26
|
+
rows
|
|
27
|
+
}));
|
|
28
|
+
},
|
|
29
|
+
message: (event, session) => {
|
|
30
|
+
if (typeof event.data !== "string") {
|
|
31
|
+
if (ready) options.onOutput(new Uint8Array(event.data));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const message = parseMessage(event.data);
|
|
35
|
+
if (message === void 0) return;
|
|
36
|
+
if (message.type === "ready") {
|
|
37
|
+
ready = true;
|
|
38
|
+
options.onOpen(session.ready());
|
|
39
|
+
} else if (message.type === "exit") session.ended({
|
|
40
|
+
message: message.reason ?? "exited",
|
|
41
|
+
exitCode: message.exitCode
|
|
42
|
+
});
|
|
43
|
+
else if (!retryableCodes.has(message.code ?? "")) session.fatal(message.message ?? "The terminal could not be opened.");
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
this.relay = new RelayConnection({
|
|
47
|
+
url: options.url,
|
|
48
|
+
sessionName: options.sessionName,
|
|
49
|
+
getTicket: options.getTicket,
|
|
50
|
+
onStatus: options.onStatus,
|
|
51
|
+
reconnect: options.reconnect,
|
|
52
|
+
protocol
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
start() {
|
|
56
|
+
this.relay.start();
|
|
57
|
+
}
|
|
58
|
+
/** Try again now, for a link that gave up. */
|
|
59
|
+
retry() {
|
|
60
|
+
this.relay.retry();
|
|
61
|
+
}
|
|
62
|
+
sendInput(data) {
|
|
63
|
+
this.relay.sendJson({
|
|
64
|
+
type: "input",
|
|
65
|
+
data
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
sendResize(cols, rows) {
|
|
69
|
+
this.relay.sendJson({
|
|
70
|
+
type: "resize",
|
|
71
|
+
cols,
|
|
72
|
+
rows
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
dispose() {
|
|
76
|
+
this.relay.dispose();
|
|
77
|
+
}
|
|
74
78
|
};
|
|
75
79
|
function parseMessage(text) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
try {
|
|
81
|
+
const value = JSON.parse(text);
|
|
82
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
83
|
+
const type = value.type;
|
|
84
|
+
return type === "ready" || type === "exit" || type === "error" ? value : void 0;
|
|
85
|
+
} catch {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
84
88
|
}
|
|
85
|
-
|
|
86
89
|
// src/sandbox-terminal/terminal.tsx
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
function SandboxTerminal2({
|
|
92
|
-
url,
|
|
93
|
-
sessionName,
|
|
94
|
-
ticket,
|
|
95
|
-
readOnly = false,
|
|
96
|
-
reconnect,
|
|
97
|
-
terminalOptions,
|
|
98
|
-
onStatusChange,
|
|
99
|
-
renderStatus,
|
|
100
|
-
style,
|
|
101
|
-
...rest
|
|
102
|
-
}, ref) {
|
|
103
|
-
const screen = useRef(null);
|
|
104
|
-
const connection = useRef(void 0);
|
|
105
|
-
const terminal = useRef(void 0);
|
|
106
|
-
const [status, setStatus] = useState({ state: "loading" });
|
|
107
|
-
const latest = useRef({ ticket, reconnect, terminalOptions, onStatusChange });
|
|
108
|
-
latest.current = { ticket, reconnect, terminalOptions, onStatusChange };
|
|
109
|
-
useImperativeHandle(
|
|
110
|
-
ref,
|
|
111
|
-
() => ({
|
|
112
|
-
focus: () => terminal.current?.focus(),
|
|
113
|
-
reconnect: () => connection.current?.retry()
|
|
114
|
-
}),
|
|
115
|
-
[]
|
|
116
|
-
);
|
|
117
|
-
useEffect(() => {
|
|
118
|
-
const container = screen.current;
|
|
119
|
-
if (container === null) return void 0;
|
|
120
|
-
let disposed = false;
|
|
121
|
-
let teardown;
|
|
122
|
-
const publish = (next) => {
|
|
123
|
-
if (disposed) return;
|
|
124
|
-
setStatus(next);
|
|
125
|
-
latest.current.onStatusChange?.(next);
|
|
126
|
-
};
|
|
127
|
-
publish({ state: "loading" });
|
|
128
|
-
void (async () => {
|
|
129
|
-
const [{ Terminal: XTerm }, { FitAddon }] = await Promise.all([
|
|
130
|
-
import("@xterm/xterm"),
|
|
131
|
-
import("@xterm/addon-fit")
|
|
132
|
-
]);
|
|
133
|
-
if (disposed) return;
|
|
134
|
-
const xterm = new XTerm({
|
|
135
|
-
cursorBlink: !readOnly,
|
|
136
|
-
disableStdin: readOnly,
|
|
137
|
-
scrollback: 5e3,
|
|
138
|
-
...latest.current.terminalOptions
|
|
139
|
-
});
|
|
140
|
-
const fit = new FitAddon();
|
|
141
|
-
xterm.loadAddon(fit);
|
|
142
|
-
xterm.open(container);
|
|
143
|
-
fit.fit();
|
|
144
|
-
terminal.current = xterm;
|
|
145
|
-
const link = new TerminalConnection({
|
|
146
|
-
url,
|
|
147
|
-
sessionName,
|
|
148
|
-
getTicket: resolveTicket(() => latest.current.ticket),
|
|
149
|
-
size: () => ({ cols: xterm.cols, rows: xterm.rows }),
|
|
150
|
-
onOutput: (bytes) => xterm.write(bytes),
|
|
151
|
-
// The hub redraws the session on every attach, so start from a clean screen.
|
|
152
|
-
onOpen: (reconnected) => {
|
|
153
|
-
if (reconnected) xterm.reset();
|
|
154
|
-
if (!readOnly) xterm.focus();
|
|
155
|
-
},
|
|
156
|
-
onStatus: publish,
|
|
157
|
-
reconnect: latest.current.reconnect
|
|
158
|
-
});
|
|
159
|
-
connection.current = link;
|
|
160
|
-
const input = xterm.onData((data) => {
|
|
161
|
-
if (!readOnly) link.sendInput(data);
|
|
162
|
-
});
|
|
163
|
-
const resized = xterm.onResize(({ cols, rows }) => {
|
|
164
|
-
if (!readOnly) link.sendResize(cols, rows);
|
|
165
|
-
});
|
|
166
|
-
let frame = 0;
|
|
167
|
-
const observer = new ResizeObserver(() => {
|
|
168
|
-
cancelAnimationFrame(frame);
|
|
169
|
-
frame = requestAnimationFrame(() => fit.fit());
|
|
170
|
-
});
|
|
171
|
-
observer.observe(container);
|
|
172
|
-
link.start();
|
|
173
|
-
teardown = () => {
|
|
174
|
-
cancelAnimationFrame(frame);
|
|
175
|
-
observer.disconnect();
|
|
176
|
-
input.dispose();
|
|
177
|
-
resized.dispose();
|
|
178
|
-
link.dispose();
|
|
179
|
-
xterm.dispose();
|
|
180
|
-
connection.current = void 0;
|
|
181
|
-
terminal.current = void 0;
|
|
182
|
-
};
|
|
183
|
-
})().catch((error) => {
|
|
184
|
-
publish({
|
|
185
|
-
state: "failed",
|
|
186
|
-
message: error instanceof Error ? error.message : "Could not load the terminal."
|
|
187
|
-
});
|
|
188
|
-
});
|
|
189
|
-
return () => {
|
|
190
|
-
disposed = true;
|
|
191
|
-
teardown?.();
|
|
192
|
-
};
|
|
193
|
-
}, [url, sessionName, readOnly]);
|
|
194
|
-
const retry = () => connection.current?.retry();
|
|
195
|
-
const statusNode = renderStatus === void 0 ? /* @__PURE__ */ jsx(DefaultStatus, { status, retry }) : renderStatus(status, retry);
|
|
196
|
-
return /* @__PURE__ */ jsxs(
|
|
197
|
-
"div",
|
|
198
|
-
{
|
|
199
|
-
...rest,
|
|
200
|
-
"data-state": status.state,
|
|
201
|
-
"data-readonly": readOnly ? "" : void 0,
|
|
202
|
-
style: { ...rootStyle, ...style },
|
|
203
|
-
children: [
|
|
204
|
-
/* @__PURE__ */ jsx("div", { ref: screen, style: screenStyle }),
|
|
205
|
-
statusNode
|
|
206
|
-
]
|
|
207
|
-
}
|
|
208
|
-
);
|
|
209
|
-
}
|
|
210
|
-
);
|
|
211
|
-
export {
|
|
212
|
-
SandboxTerminal,
|
|
213
|
-
terminalSubprotocol,
|
|
214
|
-
ticketSubprotocolPrefix
|
|
90
|
+
const rootStyle = {
|
|
91
|
+
position: "relative",
|
|
92
|
+
width: "100%",
|
|
93
|
+
height: "100%"
|
|
215
94
|
};
|
|
95
|
+
const screenStyle = {
|
|
96
|
+
width: "100%",
|
|
97
|
+
height: "100%"
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* A terminal for a remote sandbox session, drawn with xterm.js. xterm is loaded on demand, so it
|
|
101
|
+
* costs nothing until a terminal mounts. Import `@xterm/xterm/css/xterm.css` once in your app.
|
|
102
|
+
*/
|
|
103
|
+
const SandboxTerminal = forwardRef(function SandboxTerminal({ url, sessionName, ticket, readOnly = false, reconnect, terminalOptions, onStatusChange, renderStatus, style, ...rest }, ref) {
|
|
104
|
+
const screen = useRef(null);
|
|
105
|
+
const connection = useRef(void 0);
|
|
106
|
+
const terminal = useRef(void 0);
|
|
107
|
+
const [status, setStatus] = useState({ state: "loading" });
|
|
108
|
+
const latest = useRef({
|
|
109
|
+
ticket,
|
|
110
|
+
reconnect,
|
|
111
|
+
terminalOptions,
|
|
112
|
+
onStatusChange
|
|
113
|
+
});
|
|
114
|
+
latest.current = {
|
|
115
|
+
ticket,
|
|
116
|
+
reconnect,
|
|
117
|
+
terminalOptions,
|
|
118
|
+
onStatusChange
|
|
119
|
+
};
|
|
120
|
+
useImperativeHandle(ref, () => ({
|
|
121
|
+
focus: () => terminal.current?.focus(),
|
|
122
|
+
reconnect: () => connection.current?.retry()
|
|
123
|
+
}), []);
|
|
124
|
+
useEffect(() => {
|
|
125
|
+
const container = screen.current;
|
|
126
|
+
if (container === null) return void 0;
|
|
127
|
+
let disposed = false;
|
|
128
|
+
let teardown;
|
|
129
|
+
const publish = (next) => {
|
|
130
|
+
if (disposed) return;
|
|
131
|
+
setStatus(next);
|
|
132
|
+
latest.current.onStatusChange?.(next);
|
|
133
|
+
};
|
|
134
|
+
publish({ state: "loading" });
|
|
135
|
+
(async () => {
|
|
136
|
+
const [{ Terminal: XTerm }, { FitAddon }] = await Promise.all([import("@xterm/xterm"), import("@xterm/addon-fit")]);
|
|
137
|
+
if (disposed) return;
|
|
138
|
+
const xterm = new XTerm({
|
|
139
|
+
cursorBlink: !readOnly,
|
|
140
|
+
disableStdin: readOnly,
|
|
141
|
+
scrollback: 5e3,
|
|
142
|
+
...latest.current.terminalOptions
|
|
143
|
+
});
|
|
144
|
+
const fit = new FitAddon();
|
|
145
|
+
xterm.loadAddon(fit);
|
|
146
|
+
xterm.open(container);
|
|
147
|
+
fit.fit();
|
|
148
|
+
terminal.current = xterm;
|
|
149
|
+
const link = new TerminalConnection({
|
|
150
|
+
url,
|
|
151
|
+
sessionName,
|
|
152
|
+
getTicket: resolveTicket(() => latest.current.ticket),
|
|
153
|
+
size: () => ({
|
|
154
|
+
cols: xterm.cols,
|
|
155
|
+
rows: xterm.rows
|
|
156
|
+
}),
|
|
157
|
+
onOutput: (bytes) => xterm.write(bytes),
|
|
158
|
+
onOpen: (reconnected) => {
|
|
159
|
+
if (reconnected) xterm.reset();
|
|
160
|
+
if (!readOnly) xterm.focus();
|
|
161
|
+
},
|
|
162
|
+
onStatus: publish,
|
|
163
|
+
reconnect: latest.current.reconnect
|
|
164
|
+
});
|
|
165
|
+
connection.current = link;
|
|
166
|
+
const input = xterm.onData((data) => {
|
|
167
|
+
if (!readOnly) link.sendInput(data);
|
|
168
|
+
});
|
|
169
|
+
const resized = xterm.onResize(({ cols, rows }) => {
|
|
170
|
+
if (!readOnly) link.sendResize(cols, rows);
|
|
171
|
+
});
|
|
172
|
+
let frame = 0;
|
|
173
|
+
const observer = new ResizeObserver(() => {
|
|
174
|
+
cancelAnimationFrame(frame);
|
|
175
|
+
frame = requestAnimationFrame(() => fit.fit());
|
|
176
|
+
});
|
|
177
|
+
observer.observe(container);
|
|
178
|
+
link.start();
|
|
179
|
+
teardown = () => {
|
|
180
|
+
cancelAnimationFrame(frame);
|
|
181
|
+
observer.disconnect();
|
|
182
|
+
input.dispose();
|
|
183
|
+
resized.dispose();
|
|
184
|
+
link.dispose();
|
|
185
|
+
xterm.dispose();
|
|
186
|
+
connection.current = void 0;
|
|
187
|
+
terminal.current = void 0;
|
|
188
|
+
};
|
|
189
|
+
})().catch((error) => {
|
|
190
|
+
publish({
|
|
191
|
+
state: "failed",
|
|
192
|
+
message: error instanceof Error ? error.message : "Could not load the terminal."
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
return () => {
|
|
196
|
+
disposed = true;
|
|
197
|
+
teardown?.();
|
|
198
|
+
};
|
|
199
|
+
}, [
|
|
200
|
+
url,
|
|
201
|
+
sessionName,
|
|
202
|
+
readOnly
|
|
203
|
+
]);
|
|
204
|
+
const retry = () => connection.current?.retry();
|
|
205
|
+
const statusNode = renderStatus === void 0 ? /* @__PURE__ */ jsx(DefaultStatus, {
|
|
206
|
+
status,
|
|
207
|
+
retry
|
|
208
|
+
}) : renderStatus(status, retry);
|
|
209
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
210
|
+
...rest,
|
|
211
|
+
"data-state": status.state,
|
|
212
|
+
"data-readonly": readOnly ? "" : void 0,
|
|
213
|
+
style: {
|
|
214
|
+
...rootStyle,
|
|
215
|
+
...style
|
|
216
|
+
},
|
|
217
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
218
|
+
ref: screen,
|
|
219
|
+
style: screenStyle
|
|
220
|
+
}), statusNode]
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
export { SandboxTerminal, terminalSubprotocol, ticketSubprotocolPrefix };
|
|
224
|
+
|
|
216
225
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/sandbox-terminal/terminal.tsx","../../src/sandbox-terminal/connection.ts"],"sourcesContent":["import type { ITerminalOptions, Terminal } from \"@xterm/xterm\";\nimport {\n forwardRef,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n type CSSProperties,\n type ReactNode,\n} from \"react\";\n\nimport { type PrimitiveProps } from \"../primitives\";\nimport { DefaultStatus } from \"../relay/status\";\nimport { type TicketSource, resolveTicket } from \"../relay/ticket\";\nimport {\n type SandboxTerminalReconnectOptions,\n type SandboxTerminalStatus,\n TerminalConnection,\n} from \"./connection\";\n\nexport interface SandboxTerminalHandle {\n focus(): void;\n /** Reconnect now, after the terminal gave up. */\n reconnect(): void;\n}\n\nexport type SandboxTerminalProps = Omit<PrimitiveProps<\"div\">, \"children\" | \"onError\"> & {\n /**\n * The hub's terminal endpoint (`hub.handleTerminalUpgrade`): `ws:`/`wss:`, `http:`/`https:`, or a\n * path relative to this page.\n */\n url: string;\n /** Sent as `?session=` so your `authorizeTerminal` can choose the session. The server decides. */\n sessionName?: string;\n /**\n * A one-time ticket, or a function returning one. Pass a function: tickets are single use, so\n * each reconnect needs a fresh one. It travels as a WebSocket subprotocol, not in the URL.\n */\n ticket?: TicketSource;\n /** Show output only. The server enforces it too; this also stops the terminal taking input. */\n readOnly?: boolean;\n reconnect?: SandboxTerminalReconnectOptions;\n /** xterm options, such as `fontSize` or `theme`. Applied when the terminal is created. */\n terminalOptions?: ITerminalOptions;\n onStatusChange?: (status: SandboxTerminalStatus) => void;\n /** Replace the default status line. Return null to show nothing. */\n renderStatus?: (status: SandboxTerminalStatus, retry: () => void) => ReactNode;\n};\n\nconst rootStyle: CSSProperties = { position: \"relative\", width: \"100%\", height: \"100%\" };\nconst screenStyle: CSSProperties = { width: \"100%\", height: \"100%\" };\n\n/**\n * A terminal for a remote sandbox session, drawn with xterm.js. xterm is loaded on demand, so it\n * costs nothing until a terminal mounts. Import `@xterm/xterm/css/xterm.css` once in your app.\n */\nexport const SandboxTerminal = forwardRef<SandboxTerminalHandle, SandboxTerminalProps>(\n function SandboxTerminal(\n {\n url,\n sessionName,\n ticket,\n readOnly = false,\n reconnect,\n terminalOptions,\n onStatusChange,\n renderStatus,\n style,\n ...rest\n },\n ref,\n ) {\n const screen = useRef<HTMLDivElement>(null);\n const connection = useRef<TerminalConnection | undefined>(undefined);\n const terminal = useRef<Terminal | undefined>(undefined);\n const [status, setStatus] = useState<SandboxTerminalStatus>({ state: \"loading\" });\n\n // The latest props, read by long-lived callbacks without restarting the terminal.\n const latest = useRef({ ticket, reconnect, terminalOptions, onStatusChange });\n latest.current = { ticket, reconnect, terminalOptions, onStatusChange };\n\n useImperativeHandle(\n ref,\n () => ({\n focus: () => terminal.current?.focus(),\n reconnect: () => connection.current?.retry(),\n }),\n [],\n );\n\n useEffect(() => {\n const container = screen.current;\n if (container === null) return undefined;\n let disposed = false;\n let teardown: (() => void) | undefined;\n\n const publish = (next: SandboxTerminalStatus) => {\n if (disposed) return;\n setStatus(next);\n latest.current.onStatusChange?.(next);\n };\n publish({ state: \"loading\" });\n\n void (async () => {\n const [{ Terminal: XTerm }, { FitAddon }] = await Promise.all([\n import(\"@xterm/xterm\"),\n import(\"@xterm/addon-fit\"),\n ]);\n if (disposed) return;\n\n const xterm = new XTerm({\n cursorBlink: !readOnly,\n disableStdin: readOnly,\n scrollback: 5_000,\n ...latest.current.terminalOptions,\n });\n const fit = new FitAddon();\n xterm.loadAddon(fit);\n xterm.open(container);\n fit.fit();\n terminal.current = xterm;\n\n const link = new TerminalConnection({\n url,\n sessionName,\n getTicket: resolveTicket(() => latest.current.ticket),\n size: () => ({ cols: xterm.cols, rows: xterm.rows }),\n onOutput: (bytes) => xterm.write(bytes),\n // The hub redraws the session on every attach, so start from a clean screen.\n onOpen: (reconnected) => {\n if (reconnected) xterm.reset();\n if (!readOnly) xterm.focus();\n },\n onStatus: publish,\n reconnect: latest.current.reconnect,\n });\n connection.current = link;\n\n const input = xterm.onData((data) => {\n if (!readOnly) link.sendInput(data);\n });\n const resized = xterm.onResize(({ cols, rows }) => {\n if (!readOnly) link.sendResize(cols, rows);\n });\n let frame = 0;\n const observer = new ResizeObserver(() => {\n cancelAnimationFrame(frame);\n frame = requestAnimationFrame(() => fit.fit());\n });\n observer.observe(container);\n\n link.start();\n teardown = () => {\n cancelAnimationFrame(frame);\n observer.disconnect();\n input.dispose();\n resized.dispose();\n link.dispose();\n xterm.dispose();\n connection.current = undefined;\n terminal.current = undefined;\n };\n })().catch((error: unknown) => {\n publish({\n state: \"failed\",\n message: error instanceof Error ? error.message : \"Could not load the terminal.\",\n });\n });\n\n return () => {\n disposed = true;\n teardown?.();\n };\n }, [url, sessionName, readOnly]);\n\n const retry = () => connection.current?.retry();\n const statusNode =\n renderStatus === undefined ? (\n <DefaultStatus status={status} retry={retry} />\n ) : (\n renderStatus(status, retry)\n );\n\n return (\n <div\n {...rest}\n data-state={status.state}\n data-readonly={readOnly ? \"\" : undefined}\n style={{ ...rootStyle, ...style }}\n >\n <div ref={screen} style={screenStyle} />\n {statusNode}\n </div>\n );\n },\n);\n","import {\n type RelayProtocol,\n type RelayReconnectOptions,\n type RelaySession,\n type RelayState,\n type RelayStatus,\n RelayConnection,\n} from \"../relay/connection\";\n\nexport { ticketSubprotocolPrefix } from \"../relay/connection\";\n\n/** Subprotocol the hub's `handleTerminalUpgrade` requires. */\nexport const terminalSubprotocol = \"anvia.terminal.v1\";\n\nexport type SandboxTerminalState = RelayState;\nexport type SandboxTerminalStatus = RelayStatus;\nexport type SandboxTerminalReconnectOptions = RelayReconnectOptions;\n\nexport interface TerminalConnectionOptions {\n /** `ws:`, `wss:`, `http:`, `https:`, or a path relative to the page. */\n url: string;\n /** Appended as `?session=` so the server can pick the session. The server stays in charge. */\n sessionName?: string | undefined;\n /** Called for every attempt, because tickets are single use. */\n getTicket?: (() => string | Promise<string>) | undefined;\n size: () => { cols: number; rows: number };\n onOutput: (bytes: Uint8Array) => void;\n /** The terminal is attached. `reconnected` is true when an earlier attachment existed. */\n onOpen: (reconnected: boolean) => void;\n onStatus: (status: SandboxTerminalStatus) => void;\n reconnect?: SandboxTerminalReconnectOptions | undefined;\n}\n\n/** Errors worth another attempt: the sandbox may simply not be reachable yet. */\nconst retryableCodes = new Set([\"sandbox_unavailable\", \"timeout\"]);\n\ntype ServerMessage =\n | { type: \"ready\"; readOnly?: boolean }\n | { type: \"exit\"; reason?: string; exitCode?: number }\n | { type: \"error\"; code?: string; message?: string };\n\n/**\n * One browser terminal link: speaks the hub's terminal relay protocol over a {@link\n * RelayConnection}. It knows nothing about xterm, so it is testable on its own.\n */\nexport class TerminalConnection {\n private readonly relay: RelayConnection;\n\n constructor(options: TerminalConnectionOptions) {\n // The relay only sends output once it has opened the terminal, which starts with our size.\n let ready = false;\n const protocol: RelayProtocol = {\n subprotocol: terminalSubprotocol,\n label: \"terminal\",\n open: (socket) => {\n ready = false;\n const { cols, rows } = options.size();\n socket.send(JSON.stringify({ type: \"open\", cols, rows }));\n },\n message: (event, session: RelaySession) => {\n if (typeof event.data !== \"string\") {\n if (ready) options.onOutput(new Uint8Array(event.data as ArrayBuffer));\n return;\n }\n const message = parseMessage(event.data);\n if (message === undefined) return;\n if (message.type === \"ready\") {\n ready = true;\n options.onOpen(session.ready());\n } else if (message.type === \"exit\") {\n session.ended({ message: message.reason ?? \"exited\", exitCode: message.exitCode });\n } else if (!retryableCodes.has(message.code ?? \"\")) {\n session.fatal(message.message ?? \"The terminal could not be opened.\");\n }\n },\n };\n this.relay = new RelayConnection({\n url: options.url,\n sessionName: options.sessionName,\n getTicket: options.getTicket,\n onStatus: options.onStatus,\n reconnect: options.reconnect,\n protocol,\n });\n }\n\n start(): void {\n this.relay.start();\n }\n\n /** Try again now, for a link that gave up. */\n retry(): void {\n this.relay.retry();\n }\n\n sendInput(data: string): void {\n this.relay.sendJson({ type: \"input\", data });\n }\n\n sendResize(cols: number, rows: number): void {\n this.relay.sendJson({ type: \"resize\", cols, rows });\n }\n\n dispose(): void {\n this.relay.dispose();\n }\n}\n\nfunction parseMessage(text: string): ServerMessage | undefined {\n try {\n const value: unknown = JSON.parse(text);\n if (typeof value !== \"object\" || value === null) return undefined;\n const type = (value as { type?: unknown }).type;\n return type === \"ready\" || type === \"exit\" || type === \"error\"\n ? (value as ServerMessage)\n : undefined;\n } catch {\n return undefined;\n }\n}\n"],"mappings":"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","names":["SandboxTerminal"]}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/sandbox-terminal/connection.ts","../../src/sandbox-terminal/terminal.tsx"],"sourcesContent":["import {\n type RelayProtocol,\n type RelayReconnectOptions,\n type RelaySession,\n type RelayState,\n type RelayStatus,\n RelayConnection,\n} from \"../relay/connection\";\n\nexport { ticketSubprotocolPrefix } from \"../relay/connection\";\n\n/** Subprotocol the hub's `handleTerminalUpgrade` requires. */\nexport const terminalSubprotocol = \"anvia.terminal.v1\";\n\nexport type SandboxTerminalState = RelayState;\nexport type SandboxTerminalStatus = RelayStatus;\nexport type SandboxTerminalReconnectOptions = RelayReconnectOptions;\n\nexport interface TerminalConnectionOptions {\n /** `ws:`, `wss:`, `http:`, `https:`, or a path relative to the page. */\n url: string;\n /** Appended as `?session=` so the server can pick the session. The server stays in charge. */\n sessionName?: string | undefined;\n /** Called for every attempt, because tickets are single use. */\n getTicket?: (() => string | Promise<string>) | undefined;\n size: () => { cols: number; rows: number };\n onOutput: (bytes: Uint8Array) => void;\n /** The terminal is attached. `reconnected` is true when an earlier attachment existed. */\n onOpen: (reconnected: boolean) => void;\n onStatus: (status: SandboxTerminalStatus) => void;\n reconnect?: SandboxTerminalReconnectOptions | undefined;\n}\n\n/** Errors worth another attempt: the sandbox may simply not be reachable yet. */\nconst retryableCodes = new Set([\"sandbox_unavailable\", \"timeout\"]);\n\ntype ServerMessage =\n | { type: \"ready\"; readOnly?: boolean }\n | { type: \"exit\"; reason?: string; exitCode?: number }\n | { type: \"error\"; code?: string; message?: string };\n\n/**\n * One browser terminal link: speaks the hub's terminal relay protocol over a {@link\n * RelayConnection}. It knows nothing about xterm, so it is testable on its own.\n */\nexport class TerminalConnection {\n private readonly relay: RelayConnection;\n\n constructor(options: TerminalConnectionOptions) {\n // The relay only sends output once it has opened the terminal, which starts with our size.\n let ready = false;\n const protocol: RelayProtocol = {\n subprotocol: terminalSubprotocol,\n label: \"terminal\",\n open: (socket) => {\n ready = false;\n const { cols, rows } = options.size();\n socket.send(JSON.stringify({ type: \"open\", cols, rows }));\n },\n message: (event, session: RelaySession) => {\n if (typeof event.data !== \"string\") {\n if (ready) options.onOutput(new Uint8Array(event.data as ArrayBuffer));\n return;\n }\n const message = parseMessage(event.data);\n if (message === undefined) return;\n if (message.type === \"ready\") {\n ready = true;\n options.onOpen(session.ready());\n } else if (message.type === \"exit\") {\n session.ended({ message: message.reason ?? \"exited\", exitCode: message.exitCode });\n } else if (!retryableCodes.has(message.code ?? \"\")) {\n session.fatal(message.message ?? \"The terminal could not be opened.\");\n }\n },\n };\n this.relay = new RelayConnection({\n url: options.url,\n sessionName: options.sessionName,\n getTicket: options.getTicket,\n onStatus: options.onStatus,\n reconnect: options.reconnect,\n protocol,\n });\n }\n\n start(): void {\n this.relay.start();\n }\n\n /** Try again now, for a link that gave up. */\n retry(): void {\n this.relay.retry();\n }\n\n sendInput(data: string): void {\n this.relay.sendJson({ type: \"input\", data });\n }\n\n sendResize(cols: number, rows: number): void {\n this.relay.sendJson({ type: \"resize\", cols, rows });\n }\n\n dispose(): void {\n this.relay.dispose();\n }\n}\n\nfunction parseMessage(text: string): ServerMessage | undefined {\n try {\n const value: unknown = JSON.parse(text);\n if (typeof value !== \"object\" || value === null) return undefined;\n const type = (value as { type?: unknown }).type;\n return type === \"ready\" || type === \"exit\" || type === \"error\"\n ? (value as ServerMessage)\n : undefined;\n } catch {\n return undefined;\n }\n}\n","import type { ITerminalOptions, Terminal } from \"@xterm/xterm\";\nimport {\n forwardRef,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n type CSSProperties,\n type ReactNode,\n} from \"react\";\n\nimport { type PrimitiveProps } from \"../primitives\";\nimport { DefaultStatus } from \"../relay/status\";\nimport { type TicketSource, resolveTicket } from \"../relay/ticket\";\nimport {\n type SandboxTerminalReconnectOptions,\n type SandboxTerminalStatus,\n TerminalConnection,\n} from \"./connection\";\n\nexport interface SandboxTerminalHandle {\n focus(): void;\n /** Reconnect now, after the terminal gave up. */\n reconnect(): void;\n}\n\nexport type SandboxTerminalProps = Omit<PrimitiveProps<\"div\">, \"children\" | \"onError\"> & {\n /**\n * The hub's terminal endpoint (`hub.handleTerminalUpgrade`): `ws:`/`wss:`, `http:`/`https:`, or a\n * path relative to this page.\n */\n url: string;\n /** Sent as `?session=` so your `authorizeTerminal` can choose the session. The server decides. */\n sessionName?: string;\n /**\n * A one-time ticket, or a function returning one. Pass a function: tickets are single use, so\n * each reconnect needs a fresh one. It travels as a WebSocket subprotocol, not in the URL.\n */\n ticket?: TicketSource;\n /** Show output only. The server enforces it too; this also stops the terminal taking input. */\n readOnly?: boolean;\n reconnect?: SandboxTerminalReconnectOptions;\n /** xterm options, such as `fontSize` or `theme`. Applied when the terminal is created. */\n terminalOptions?: ITerminalOptions;\n onStatusChange?: (status: SandboxTerminalStatus) => void;\n /** Replace the default status line. Return null to show nothing. */\n renderStatus?: (status: SandboxTerminalStatus, retry: () => void) => ReactNode;\n};\n\nconst rootStyle: CSSProperties = { position: \"relative\", width: \"100%\", height: \"100%\" };\nconst screenStyle: CSSProperties = { width: \"100%\", height: \"100%\" };\n\n/**\n * A terminal for a remote sandbox session, drawn with xterm.js. xterm is loaded on demand, so it\n * costs nothing until a terminal mounts. Import `@xterm/xterm/css/xterm.css` once in your app.\n */\nexport const SandboxTerminal = forwardRef<SandboxTerminalHandle, SandboxTerminalProps>(\n function SandboxTerminal(\n {\n url,\n sessionName,\n ticket,\n readOnly = false,\n reconnect,\n terminalOptions,\n onStatusChange,\n renderStatus,\n style,\n ...rest\n },\n ref,\n ) {\n const screen = useRef<HTMLDivElement>(null);\n const connection = useRef<TerminalConnection | undefined>(undefined);\n const terminal = useRef<Terminal | undefined>(undefined);\n const [status, setStatus] = useState<SandboxTerminalStatus>({ state: \"loading\" });\n\n // The latest props, read by long-lived callbacks without restarting the terminal.\n const latest = useRef({ ticket, reconnect, terminalOptions, onStatusChange });\n latest.current = { ticket, reconnect, terminalOptions, onStatusChange };\n\n useImperativeHandle(\n ref,\n () => ({\n focus: () => terminal.current?.focus(),\n reconnect: () => connection.current?.retry(),\n }),\n [],\n );\n\n useEffect(() => {\n const container = screen.current;\n if (container === null) return undefined;\n let disposed = false;\n let teardown: (() => void) | undefined;\n\n const publish = (next: SandboxTerminalStatus) => {\n if (disposed) return;\n setStatus(next);\n latest.current.onStatusChange?.(next);\n };\n publish({ state: \"loading\" });\n\n void (async () => {\n const [{ Terminal: XTerm }, { FitAddon }] = await Promise.all([\n import(\"@xterm/xterm\"),\n import(\"@xterm/addon-fit\"),\n ]);\n if (disposed) return;\n\n const xterm = new XTerm({\n cursorBlink: !readOnly,\n disableStdin: readOnly,\n scrollback: 5_000,\n ...latest.current.terminalOptions,\n });\n const fit = new FitAddon();\n xterm.loadAddon(fit);\n xterm.open(container);\n fit.fit();\n terminal.current = xterm;\n\n const link = new TerminalConnection({\n url,\n sessionName,\n getTicket: resolveTicket(() => latest.current.ticket),\n size: () => ({ cols: xterm.cols, rows: xterm.rows }),\n onOutput: (bytes) => xterm.write(bytes),\n // The hub redraws the session on every attach, so start from a clean screen.\n onOpen: (reconnected) => {\n if (reconnected) xterm.reset();\n if (!readOnly) xterm.focus();\n },\n onStatus: publish,\n reconnect: latest.current.reconnect,\n });\n connection.current = link;\n\n const input = xterm.onData((data) => {\n if (!readOnly) link.sendInput(data);\n });\n const resized = xterm.onResize(({ cols, rows }) => {\n if (!readOnly) link.sendResize(cols, rows);\n });\n let frame = 0;\n const observer = new ResizeObserver(() => {\n cancelAnimationFrame(frame);\n frame = requestAnimationFrame(() => fit.fit());\n });\n observer.observe(container);\n\n link.start();\n teardown = () => {\n cancelAnimationFrame(frame);\n observer.disconnect();\n input.dispose();\n resized.dispose();\n link.dispose();\n xterm.dispose();\n connection.current = undefined;\n terminal.current = undefined;\n };\n })().catch((error: unknown) => {\n publish({\n state: \"failed\",\n message: error instanceof Error ? error.message : \"Could not load the terminal.\",\n });\n });\n\n return () => {\n disposed = true;\n teardown?.();\n };\n }, [url, sessionName, readOnly]);\n\n const retry = () => connection.current?.retry();\n const statusNode =\n renderStatus === undefined ? (\n <DefaultStatus status={status} retry={retry} />\n ) : (\n renderStatus(status, retry)\n );\n\n return (\n <div\n {...rest}\n data-state={status.state}\n data-readonly={readOnly ? \"\" : undefined}\n style={{ ...rootStyle, ...style }}\n >\n <div ref={screen} style={screenStyle} />\n {statusNode}\n </div>\n );\n },\n);\n"],"mappings":"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"}
|
|
@@ -1,21 +1,3 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
export {
|
|
4
|
-
import { P as PrimitiveProps } from '../render-CRKWam2v.js';
|
|
5
|
-
|
|
6
|
-
declare const SelectionToolbarPrimitive: {
|
|
7
|
-
readonly Root: react.ForwardRefExoticComponent<Omit<PrimitiveProps<"div">, "children"> & {
|
|
8
|
-
children?: react.ReactNode;
|
|
9
|
-
container?: Element | DocumentFragment;
|
|
10
|
-
onQuote?: (selection: SelectionToolbarSelection) => void;
|
|
11
|
-
onSelectionChange?: (selection: SelectionToolbarSelection | undefined) => void;
|
|
12
|
-
} & react.RefAttributes<HTMLDivElement>>;
|
|
13
|
-
readonly Quote: react.ForwardRefExoticComponent<Omit<Omit<react.DetailedHTMLProps<react.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref">, "asChild"> & {
|
|
14
|
-
asChild?: boolean;
|
|
15
|
-
} & react.RefAttributes<HTMLButtonElement>>;
|
|
16
|
-
readonly Copy: react.ForwardRefExoticComponent<Omit<Omit<react.DetailedHTMLProps<react.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref">, "asChild"> & {
|
|
17
|
-
asChild?: boolean;
|
|
18
|
-
} & react.RefAttributes<HTMLButtonElement>>;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export { SelectionToolbarPrimitive, SelectionToolbarSelection };
|
|
1
|
+
import { d as SelectionToolbarSelection, f as useSelectionToolbar, u as SelectionToolbarContextValue } from "../index-Mw3ltwSG.js";
|
|
2
|
+
import { i as SelectionToolbarPrimitive } from "../index-BCBVcl0D.js";
|
|
3
|
+
export { type SelectionToolbarContextValue, SelectionToolbarPrimitive, type SelectionToolbarSelection, useSelectionToolbar };
|
|
@@ -1,13 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import "../chunk-6XOJFLX4.js";
|
|
5
|
-
import "../chunk-T3ZFZ3NP.js";
|
|
6
|
-
import {
|
|
7
|
-
useSelectionToolbar
|
|
8
|
-
} from "../chunk-E73S3SOF.js";
|
|
9
|
-
export {
|
|
10
|
-
SelectionToolbarPrimitive,
|
|
11
|
-
useSelectionToolbar
|
|
12
|
-
};
|
|
13
|
-
//# sourceMappingURL=index.js.map
|
|
1
|
+
import { u as useSelectionToolbar } from "../contexts-B4ijG24z.js";
|
|
2
|
+
import { t as SelectionToolbarPrimitive } from "../selection-toolbar-CIkOoM-7.js";
|
|
3
|
+
export { SelectionToolbarPrimitive, useSelectionToolbar };
|