@limrun/ui 0.1.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/index.js ADDED
@@ -0,0 +1,735 @@
1
+ import { jsxs as ne, jsx as q } from "react/jsx-runtime";
2
+ import { forwardRef as De, useRef as S, useState as Ce, useMemo as Oe, useEffect as Ke, useImperativeHandle as le } from "react";
3
+ import './index.css';function re(E) {
4
+ var a, D, i = "";
5
+ if (typeof E == "string" || typeof E == "number") i += E;
6
+ else if (typeof E == "object") if (Array.isArray(E)) {
7
+ var l = E.length;
8
+ for (a = 0; a < l; a++) E[a] && (D = re(E[a])) && (i && (i += " "), i += D);
9
+ } else for (D in E) E[D] && (i && (i += " "), i += D);
10
+ return i;
11
+ }
12
+ function Ye() {
13
+ for (var E, a, D = 0, i = "", l = arguments.length; D < l; D++) (E = arguments[D]) && (a = re(E)) && (i && (i += " "), i += a);
14
+ return i;
15
+ }
16
+ const Q = {
17
+ INJECT_KEYCODE: 0,
18
+ INJECT_TOUCH_EVENT: 2,
19
+ SET_CLIPBOARD: 9
20
+ }, w = {
21
+ ACTION_DOWN: 0,
22
+ ACTION_UP: 1,
23
+ ACTION_MOVE: 2,
24
+ ACTION_CANCEL: 3,
25
+ BUTTON_PRIMARY: 1
26
+ }, e = {
27
+ // Actions
28
+ ACTION_DOWN: 0,
29
+ ACTION_UP: 1,
30
+ // Meta state flags (from android.view.KeyEvent)
31
+ META_NONE: 0,
32
+ META_SHIFT_ON: 1,
33
+ META_ALT_ON: 2,
34
+ META_CTRL_ON: 4096,
35
+ // META_CTRL_LEFT_ON | META_CTRL_RIGHT_ON
36
+ META_META_ON: 65536,
37
+ KEYCODE_0: 7,
38
+ KEYCODE_1: 8,
39
+ KEYCODE_2: 9,
40
+ KEYCODE_3: 10,
41
+ KEYCODE_4: 11,
42
+ KEYCODE_5: 12,
43
+ KEYCODE_6: 13,
44
+ KEYCODE_7: 14,
45
+ KEYCODE_8: 15,
46
+ KEYCODE_9: 16,
47
+ DPAD_UP: 19,
48
+ // KEYCODE_DPAD_UP
49
+ DPAD_DOWN: 20,
50
+ // KEYCODE_DPAD_DOWN
51
+ DPAD_LEFT: 21,
52
+ // KEYCODE_DPAD_LEFT
53
+ DPAD_RIGHT: 22,
54
+ KEYCODE_A: 29,
55
+ KEYCODE_B: 30,
56
+ KEYCODE_C: 31,
57
+ KEYCODE_D: 32,
58
+ KEYCODE_E: 33,
59
+ KEYCODE_F: 34,
60
+ KEYCODE_G: 35,
61
+ KEYCODE_H: 36,
62
+ KEYCODE_I: 37,
63
+ KEYCODE_J: 38,
64
+ KEYCODE_K: 39,
65
+ KEYCODE_L: 40,
66
+ KEYCODE_M: 41,
67
+ KEYCODE_N: 42,
68
+ KEYCODE_O: 43,
69
+ KEYCODE_P: 44,
70
+ KEYCODE_Q: 45,
71
+ KEYCODE_R: 46,
72
+ KEYCODE_S: 47,
73
+ KEYCODE_T: 48,
74
+ KEYCODE_U: 49,
75
+ KEYCODE_V: 50,
76
+ KEYCODE_W: 51,
77
+ KEYCODE_X: 52,
78
+ KEYCODE_Y: 53,
79
+ KEYCODE_Z: 54,
80
+ KEYCODE_COMMA: 55,
81
+ KEYCODE_PERIOD: 56,
82
+ KEYCODE_ALT_LEFT: 57,
83
+ KEYCODE_ALT_RIGHT: 58,
84
+ KEYCODE_SHIFT_LEFT: 59,
85
+ KEYCODE_SHIFT_RIGHT: 60,
86
+ KEYCODE_TAB: 61,
87
+ KEYCODE_SPACE: 62,
88
+ ENTER: 66,
89
+ // KEYCODE_ENTER
90
+ DEL: 67,
91
+ // KEYCODE_DEL (Backspace)
92
+ KEYCODE_GRAVE: 68,
93
+ // `
94
+ KEYCODE_MINUS: 69,
95
+ // -
96
+ KEYCODE_EQUALS: 70,
97
+ // =
98
+ KEYCODE_LEFT_BRACKET: 71,
99
+ // [
100
+ KEYCODE_RIGHT_BRACKET: 72,
101
+ // ]
102
+ KEYCODE_BACKSLASH: 73,
103
+ // \
104
+ KEYCODE_SEMICOLON: 74,
105
+ // ;
106
+ KEYCODE_APOSTROPHE: 75,
107
+ // '
108
+ KEYCODE_SLASH: 76,
109
+ MENU: 82,
110
+ KEYCODE_PAGE_UP: 92,
111
+ KEYCODE_PAGE_DOWN: 93,
112
+ KEYCODE_ESCAPE: 111,
113
+ FORWARD_DEL: 112,
114
+ // KEYCODE_FORWARD_DEL (Delete key)
115
+ KEYCODE_CTRL_LEFT: 113,
116
+ KEYCODE_CTRL_RIGHT: 114,
117
+ KEYCODE_META_LEFT: 117,
118
+ KEYCODE_META_RIGHT: 118,
119
+ KEYCODE_MOVE_HOME: 122,
120
+ KEYCODE_MOVE_END: 123,
121
+ KEYCODE_INSERT: 124,
122
+ KEYCODE_F1: 131,
123
+ KEYCODE_F2: 132,
124
+ KEYCODE_F3: 133,
125
+ KEYCODE_F4: 134,
126
+ KEYCODE_F5: 135,
127
+ KEYCODE_F6: 136,
128
+ KEYCODE_F7: 137,
129
+ KEYCODE_F8: 138,
130
+ KEYCODE_F9: 139,
131
+ KEYCODE_F10: 140,
132
+ KEYCODE_F11: 141,
133
+ KEYCODE_F12: 142,
134
+ KEYCODE_NUMPAD_0: 144,
135
+ KEYCODE_NUMPAD_1: 145,
136
+ KEYCODE_NUMPAD_2: 146,
137
+ KEYCODE_NUMPAD_3: 147,
138
+ KEYCODE_NUMPAD_4: 148,
139
+ KEYCODE_NUMPAD_5: 149,
140
+ KEYCODE_NUMPAD_6: 150,
141
+ KEYCODE_NUMPAD_7: 151,
142
+ KEYCODE_NUMPAD_8: 152,
143
+ KEYCODE_NUMPAD_9: 153,
144
+ KEYCODE_NUMPAD_DIVIDE: 154,
145
+ KEYCODE_NUMPAD_MULTIPLY: 155,
146
+ KEYCODE_NUMPAD_SUBTRACT: 156,
147
+ KEYCODE_NUMPAD_ADD: 157,
148
+ KEYCODE_NUMPAD_DOT: 158,
149
+ KEYCODE_NUMPAD_COMMA: 159,
150
+ KEYCODE_NUMPAD_ENTER: 160,
151
+ KEYCODE_NUMPAD_EQUALS: 161
152
+ }, oe = {
153
+ KeyA: e.KEYCODE_A,
154
+ KeyB: e.KEYCODE_B,
155
+ KeyC: e.KEYCODE_C,
156
+ KeyD: e.KEYCODE_D,
157
+ KeyE: e.KEYCODE_E,
158
+ KeyF: e.KEYCODE_F,
159
+ KeyG: e.KEYCODE_G,
160
+ KeyH: e.KEYCODE_H,
161
+ KeyI: e.KEYCODE_I,
162
+ KeyJ: e.KEYCODE_J,
163
+ KeyK: e.KEYCODE_K,
164
+ KeyL: e.KEYCODE_L,
165
+ KeyM: e.KEYCODE_M,
166
+ KeyN: e.KEYCODE_N,
167
+ KeyO: e.KEYCODE_O,
168
+ KeyP: e.KEYCODE_P,
169
+ KeyQ: e.KEYCODE_Q,
170
+ KeyR: e.KEYCODE_R,
171
+ KeyS: e.KEYCODE_S,
172
+ KeyT: e.KEYCODE_T,
173
+ KeyU: e.KEYCODE_U,
174
+ KeyV: e.KEYCODE_V,
175
+ KeyW: e.KEYCODE_W,
176
+ KeyX: e.KEYCODE_X,
177
+ KeyY: e.KEYCODE_Y,
178
+ KeyZ: e.KEYCODE_Z,
179
+ Digit0: e.KEYCODE_0,
180
+ Digit1: e.KEYCODE_1,
181
+ Digit2: e.KEYCODE_2,
182
+ Digit3: e.KEYCODE_3,
183
+ Digit4: e.KEYCODE_4,
184
+ Digit5: e.KEYCODE_5,
185
+ Digit6: e.KEYCODE_6,
186
+ Digit7: e.KEYCODE_7,
187
+ Digit8: e.KEYCODE_8,
188
+ Digit9: e.KEYCODE_9,
189
+ Backquote: e.KEYCODE_GRAVE,
190
+ Minus: e.KEYCODE_MINUS,
191
+ Equal: e.KEYCODE_EQUALS,
192
+ BracketLeft: e.KEYCODE_LEFT_BRACKET,
193
+ BracketRight: e.KEYCODE_RIGHT_BRACKET,
194
+ Backslash: e.KEYCODE_BACKSLASH,
195
+ Semicolon: e.KEYCODE_SEMICOLON,
196
+ Quote: e.KEYCODE_APOSTROPHE,
197
+ Comma: e.KEYCODE_COMMA,
198
+ Period: e.KEYCODE_PERIOD,
199
+ Slash: e.KEYCODE_SLASH,
200
+ Space: e.KEYCODE_SPACE,
201
+ Tab: e.KEYCODE_TAB,
202
+ Escape: e.KEYCODE_ESCAPE,
203
+ ArrowUp: e.DPAD_UP,
204
+ ArrowDown: e.DPAD_DOWN,
205
+ ArrowLeft: e.DPAD_LEFT,
206
+ ArrowRight: e.DPAD_RIGHT,
207
+ Enter: e.ENTER,
208
+ Backspace: e.DEL,
209
+ Delete: e.FORWARD_DEL,
210
+ Home: e.KEYCODE_MOVE_HOME,
211
+ End: e.KEYCODE_MOVE_END,
212
+ PageUp: e.KEYCODE_PAGE_UP,
213
+ PageDown: e.KEYCODE_PAGE_DOWN,
214
+ Insert: e.KEYCODE_INSERT,
215
+ F1: e.KEYCODE_F1,
216
+ F2: e.KEYCODE_F2,
217
+ F3: e.KEYCODE_F3,
218
+ F4: e.KEYCODE_F4,
219
+ F5: e.KEYCODE_F5,
220
+ F6: e.KEYCODE_F6,
221
+ F7: e.KEYCODE_F7,
222
+ F8: e.KEYCODE_F8,
223
+ F9: e.KEYCODE_F9,
224
+ F10: e.KEYCODE_F10,
225
+ F11: e.KEYCODE_F11,
226
+ F12: e.KEYCODE_F12,
227
+ ShiftLeft: e.KEYCODE_SHIFT_LEFT,
228
+ ShiftRight: e.KEYCODE_SHIFT_RIGHT,
229
+ ControlLeft: e.KEYCODE_CTRL_LEFT,
230
+ ControlRight: e.KEYCODE_CTRL_RIGHT,
231
+ AltLeft: e.KEYCODE_ALT_LEFT,
232
+ AltRight: e.KEYCODE_ALT_RIGHT,
233
+ MetaLeft: e.KEYCODE_META_LEFT,
234
+ MetaRight: e.KEYCODE_META_RIGHT,
235
+ // Windows/Command key
236
+ ContextMenu: e.MENU,
237
+ // Often the Menu key
238
+ // Numpad mappings
239
+ Numpad0: e.KEYCODE_NUMPAD_0,
240
+ Numpad1: e.KEYCODE_NUMPAD_1,
241
+ Numpad2: e.KEYCODE_NUMPAD_2,
242
+ Numpad3: e.KEYCODE_NUMPAD_3,
243
+ Numpad4: e.KEYCODE_NUMPAD_4,
244
+ Numpad5: e.KEYCODE_NUMPAD_5,
245
+ Numpad6: e.KEYCODE_NUMPAD_6,
246
+ Numpad7: e.KEYCODE_NUMPAD_7,
247
+ Numpad8: e.KEYCODE_NUMPAD_8,
248
+ Numpad9: e.KEYCODE_NUMPAD_9,
249
+ NumpadDivide: e.KEYCODE_NUMPAD_DIVIDE,
250
+ NumpadMultiply: e.KEYCODE_NUMPAD_MULTIPLY,
251
+ NumpadSubtract: e.KEYCODE_NUMPAD_SUBTRACT,
252
+ NumpadAdd: e.KEYCODE_NUMPAD_ADD,
253
+ NumpadDecimal: e.KEYCODE_NUMPAD_DOT,
254
+ NumpadComma: e.KEYCODE_NUMPAD_COMMA,
255
+ // Some numpads have comma
256
+ NumpadEnter: e.KEYCODE_NUMPAD_ENTER,
257
+ NumpadEqual: e.KEYCODE_NUMPAD_EQUALS
258
+ };
259
+ function fe(E, a, D, i, l, f, o = 1, c = 0, s = 0) {
260
+ const u = new ArrayBuffer(32), y = new DataView(u);
261
+ let d = 0;
262
+ return y.setUint8(d, Q.INJECT_TOUCH_EVENT), d += 1, y.setUint8(d, E), d += 1, y.setBigInt64(d, BigInt(a)), d += 8, y.setInt32(d, Math.round(l), !0), d += 4, y.setInt32(d, Math.round(f), !0), d += 4, y.setUint16(d, D, !0), d += 2, y.setUint16(d, i, !0), d += 2, y.setInt16(d, Math.round(o * 65535), !0), d += 2, y.setInt32(d, c, !0), d += 4, y.setInt32(d, s, !0), u;
263
+ }
264
+ function Ae(E, a = !0) {
265
+ const i = new TextEncoder().encode(E), l = new ArrayBuffer(14 + i.length), f = new DataView(l);
266
+ let o = 0;
267
+ return f.setUint8(o, Q.SET_CLIPBOARD), o += 1, f.setBigInt64(o, BigInt(0), !1), o += 8, f.setUint8(o, a ? 1 : 0), o += 1, f.setUint32(o, i.length, !1), o += 4, new Uint8Array(l, o).set(i), l;
268
+ }
269
+ function B(E, a, D = 0, i = 0) {
270
+ const l = new ArrayBuffer(14), f = new DataView(l);
271
+ let o = 0;
272
+ return f.setUint8(o, Q.INJECT_KEYCODE), o += 1, f.setUint8(o, E), o += 1, f.setInt32(o, a, !0), o += 4, f.setInt32(o, D, !0), o += 4, f.setInt32(o, i, !0), l;
273
+ }
274
+ const N = (...E) => {
275
+ window.debugRemoteControl && console.log(...E);
276
+ }, Y = (...E) => {
277
+ window.debugRemoteControl && console.warn(...E);
278
+ };
279
+ function ye(E) {
280
+ const a = E.code, D = oe[a];
281
+ if (!D)
282
+ return Y(`Unknown event.code: ${a}, key: ${E.key}`), null;
283
+ let i = e.META_NONE;
284
+ const l = a >= "KeyA" && a <= "KeyZ", f = E.getModifierState("CapsLock"), o = E.shiftKey;
285
+ let c = o;
286
+ return l && (c = o !== f), c && (i |= e.META_SHIFT_ON), E.ctrlKey && (i |= e.META_CTRL_ON), E.altKey && (i |= e.META_ALT_ON), E.metaKey && (i |= e.META_META_ON), { keycode: D, metaState: i };
287
+ }
288
+ const Te = De(
289
+ ({ className: E, url: a, token: D, sessionId: i, openUrl: l }, f) => {
290
+ const o = S(null), c = S(null), s = S(null), u = S(null), [y, d] = Ce(!1), P = S(void 0), R = S(/* @__PURE__ */ new Map()), I = S(/* @__PURE__ */ new Map()), p = S(/* @__PURE__ */ new Map()), g = Oe(
291
+ () => i || Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15),
292
+ [i]
293
+ ), C = (r) => {
294
+ N(r);
295
+ }, U = (r) => {
296
+ !u.current || u.current.readyState !== "open" || u.current.send(r);
297
+ }, m = (r) => {
298
+ if (r.preventDefault(), r.stopPropagation(), !u.current || u.current.readyState !== "open" || !o.current)
299
+ return;
300
+ const O = o.current, t = O.getBoundingClientRect(), n = O.videoWidth, _ = O.videoHeight;
301
+ if (!n || !_) return;
302
+ const T = (A, K, k, M) => {
303
+ const b = t.width, F = t.height, W = n / _, ae = b / F;
304
+ let v = b, H = F;
305
+ W > ae ? H = b / W : v = F * W;
306
+ const de = (b - v) / 2, ue = (F - H) / 2, $ = K - t.left - de, V = k - t.top - ue, G = $ >= 0 && $ <= v && V >= 0 && V <= H;
307
+ let J = 0, x = 0;
308
+ G && (J = Math.max(0, Math.min(n, $ / v * n)), x = Math.max(0, Math.min(_, V / H * _)));
309
+ let L = null, h = null, _e = 1;
310
+ const ee = w.BUTTON_PRIMARY;
311
+ switch (M) {
312
+ case "down":
313
+ G ? (L = w.ACTION_DOWN, h = { x: J, y: x }, p.current.set(A, h), A === -1 && o.current?.focus()) : p.current.delete(A);
314
+ break;
315
+ case "move":
316
+ p.current.has(A) && G && (L = w.ACTION_MOVE, h = { x: J, y: x }, p.current.set(A, h));
317
+ break;
318
+ case "up":
319
+ case "cancel":
320
+ p.current.has(A) && (L = M === "cancel" ? w.ACTION_CANCEL : w.ACTION_UP, h = p.current.get(A), p.current.delete(A));
321
+ break;
322
+ }
323
+ if (L !== null && h !== null) {
324
+ const te = fe(
325
+ L,
326
+ A,
327
+ n,
328
+ _,
329
+ h.x,
330
+ h.y,
331
+ _e,
332
+ ee,
333
+ ee
334
+ );
335
+ te && U(te);
336
+ } else (M === "up" || M === "cancel") && p.current.delete(A);
337
+ };
338
+ if ("touches" in r) {
339
+ const A = r.changedTouches;
340
+ let K;
341
+ switch (r.type) {
342
+ case "touchstart":
343
+ K = "down";
344
+ break;
345
+ case "touchmove":
346
+ K = "move";
347
+ break;
348
+ case "touchend":
349
+ K = "up";
350
+ break;
351
+ case "touchcancel":
352
+ K = "cancel";
353
+ break;
354
+ default:
355
+ return;
356
+ }
357
+ for (let k = 0; k < A.length; k++) {
358
+ const M = A[k];
359
+ T(M.identifier, M.clientX, M.clientY, K);
360
+ }
361
+ } else {
362
+ let K = null;
363
+ switch (r.type) {
364
+ case "mousedown":
365
+ r.button === 0 && (K = "down");
366
+ break;
367
+ case "mousemove":
368
+ p.current.has(-1) && (K = "move");
369
+ break;
370
+ case "mouseup":
371
+ r.button === 0 && (K = "up");
372
+ break;
373
+ case "mouseleave":
374
+ p.current.has(-1) && (K = "up");
375
+ break;
376
+ }
377
+ K && T(-1, r.clientX, r.clientY, K);
378
+ }
379
+ }, X = (r) => {
380
+ if (r.preventDefault(), r.stopPropagation(), N("Keyboard event:", {
381
+ type: r.type,
382
+ key: r.key,
383
+ keyCode: r.keyCode,
384
+ code: r.code,
385
+ target: r.target.tagName,
386
+ focused: document.activeElement === o.current
387
+ }), document.activeElement !== o.current) {
388
+ Y("Video element not focused, skipping keyboard event");
389
+ return;
390
+ }
391
+ if (!u.current || u.current.readyState !== "open") {
392
+ Y("Data channel not ready for keyboard event:", u.current?.readyState);
393
+ return;
394
+ }
395
+ if (r.type === "keydown") {
396
+ if (r.key.toLowerCase() === "v" && (r.metaKey || r.ctrlKey)) {
397
+ N("Paste shortcut detected"), navigator.clipboard.readText().then((t) => {
398
+ if (t) {
399
+ N(
400
+ "Pasting text via SET_CLIPBOARD:",
401
+ t.substring(0, 20) + (t.length > 20 ? "..." : "")
402
+ );
403
+ const n = Ae(t, !0);
404
+ U(n);
405
+ }
406
+ }).catch((t) => {
407
+ console.error("Failed to read clipboard contents: ", t);
408
+ });
409
+ return;
410
+ }
411
+ if (r.key.toLowerCase() === "m" && (r.metaKey || r.ctrlKey)) {
412
+ N("Menu shortcut detected");
413
+ const t = B(
414
+ e.ACTION_DOWN,
415
+ e.MENU,
416
+ 0,
417
+ e.META_NONE
418
+ // Modifiers are handled by the shortcut check, not passed down
419
+ );
420
+ U(t);
421
+ const n = B(
422
+ e.ACTION_UP,
423
+ e.MENU,
424
+ 0,
425
+ e.META_NONE
426
+ );
427
+ U(n);
428
+ return;
429
+ }
430
+ }
431
+ const O = ye(r);
432
+ if (O) {
433
+ const { keycode: t, metaState: n } = O, _ = r.type === "keydown" ? e.ACTION_DOWN : e.ACTION_UP;
434
+ N(`Sending Keycode: key=${r.key}, code=${t}, action=${_}, meta=${n}`);
435
+ const T = B(
436
+ _,
437
+ t,
438
+ 0,
439
+ // repeat count, typically 0 for single presses
440
+ n
441
+ );
442
+ U(T);
443
+ } else
444
+ N(`Ignoring unhandled key event: type=${r.type}, key=${r.key}`);
445
+ }, ce = () => {
446
+ c.current && c.current.readyState === WebSocket.OPEN && c.current.send(
447
+ JSON.stringify({
448
+ type: "keepAlive",
449
+ sessionId: g
450
+ })
451
+ );
452
+ }, j = () => {
453
+ P.current && window.clearInterval(P.current), P.current = window.setInterval(ce, 1e4);
454
+ }, Z = () => {
455
+ P.current && (window.clearInterval(P.current), P.current = void 0);
456
+ }, z = () => {
457
+ document.hidden ? Z() : j();
458
+ }, Ee = async () => {
459
+ try {
460
+ c.current = new WebSocket(`${a}?token=${D}`), c.current.onerror = (t) => {
461
+ C("WebSocket error: " + t);
462
+ }, c.current.onclose = () => {
463
+ C("WebSocket closed");
464
+ }, await new Promise((t, n) => {
465
+ c.current && (c.current.onopen = t, setTimeout(() => n(new Error("WebSocket connection timeout")), 5e3));
466
+ });
467
+ const O = await new Promise((t, n) => {
468
+ const _ = setTimeout(() => n(new Error("RTCConfiguration timeout")), 5e3), T = (A) => {
469
+ try {
470
+ const K = JSON.parse(A.data);
471
+ K.type === "rtcConfiguration" && (clearTimeout(_), c.current?.removeEventListener("message", T), t(K.rtcConfiguration));
472
+ } catch (K) {
473
+ console.error("Error handling RTC configuration:", K), n(K);
474
+ }
475
+ };
476
+ c.current?.addEventListener("message", T), c.current?.send(
477
+ JSON.stringify({
478
+ type: "requestRtcConfiguration",
479
+ sessionId: g
480
+ })
481
+ );
482
+ });
483
+ if (s.current = new RTCPeerConnection(O), s.current.addTransceiver("audio", { direction: "recvonly" }), s.current.addTransceiver("video", { direction: "recvonly" }), u.current = s.current.createDataChannel("control", {
484
+ ordered: !0,
485
+ negotiated: !0,
486
+ id: 1
487
+ }), u.current.onopen = () => {
488
+ if (C("Control channel opened"), c.current && (c.current.send(JSON.stringify({ type: "requestFrame", sessionId: g })), l))
489
+ try {
490
+ const t = decodeURIComponent(l);
491
+ C("Opening URL"), c.current.send(
492
+ JSON.stringify({
493
+ type: "openUrl",
494
+ url: t,
495
+ sessionId: g
496
+ })
497
+ );
498
+ } catch (t) {
499
+ console.error({ error: t }, "Error decoding URL, falling back to the original URL"), c.current.send(
500
+ JSON.stringify({
501
+ type: "openUrl",
502
+ url: l,
503
+ sessionId: g
504
+ })
505
+ );
506
+ }
507
+ }, u.current.onclose = () => {
508
+ C("Control channel closed");
509
+ }, u.current.onerror = (t) => {
510
+ console.error("Control channel error:", t), C("Control channel error: " + t);
511
+ }, s.current.onconnectionstatechange = () => {
512
+ C("Connection state: " + s.current?.connectionState), d(s.current?.connectionState === "connected");
513
+ }, s.current.oniceconnectionstatechange = () => {
514
+ C("ICE state: " + s.current?.iceConnectionState);
515
+ }, s.current.ontrack = (t) => {
516
+ C("Received remote track: " + t.track.kind), t.track.kind === "video" && o.current && (N(`[${(/* @__PURE__ */ new Date()).toISOString()}] Video track received:`, t.track), o.current.srcObject = t.streams[0]);
517
+ }, s.current.onicecandidate = (t) => {
518
+ if (t.candidate && c.current) {
519
+ const n = {
520
+ type: "candidate",
521
+ candidate: t.candidate.candidate,
522
+ sdpMid: t.candidate.sdpMid,
523
+ sdpMLineIndex: t.candidate.sdpMLineIndex,
524
+ sessionId: g
525
+ };
526
+ c.current.send(JSON.stringify(n)), C("Sent ICE candidate");
527
+ } else
528
+ C("ICE candidate gathering completed");
529
+ }, c.current.onmessage = async (t) => {
530
+ let n;
531
+ try {
532
+ n = JSON.parse(t.data);
533
+ } catch (_) {
534
+ Y("Error parsing message:", _);
535
+ return;
536
+ }
537
+ switch (C("Received: " + n.type), n.type) {
538
+ case "answer":
539
+ if (!s.current) {
540
+ C("No peer connection, skipping answer");
541
+ break;
542
+ }
543
+ await s.current.setRemoteDescription(
544
+ new RTCSessionDescription({
545
+ type: "answer",
546
+ sdp: n.sdp
547
+ })
548
+ ), C("Set remote description");
549
+ break;
550
+ case "candidate":
551
+ if (!s.current) {
552
+ C("No peer connection, skipping candidate");
553
+ break;
554
+ }
555
+ await s.current.addIceCandidate(
556
+ new RTCIceCandidate({
557
+ candidate: n.candidate,
558
+ sdpMid: n.sdpMid,
559
+ sdpMLineIndex: n.sdpMLineIndex
560
+ })
561
+ ), C("Added ICE candidate");
562
+ break;
563
+ case "screenshot":
564
+ if (typeof n.id != "string" || typeof n.dataUri != "string") {
565
+ Y("Received invalid screenshot success message:", n);
566
+ break;
567
+ }
568
+ const _ = R.current.get(n.id);
569
+ if (!_) {
570
+ Y(`Received screenshot data for unknown or handled id: ${n.id}`);
571
+ break;
572
+ }
573
+ N(`Received screenshot data for id ${n.id}`), _({ dataUri: n.dataUri }), R.current.delete(n.id), I.current.delete(n.id);
574
+ break;
575
+ case "screenshotError":
576
+ if (typeof n.id != "string" || typeof n.message != "string") {
577
+ Y("Received invalid screenshot error message:", n);
578
+ break;
579
+ }
580
+ const T = I.current.get(n.id);
581
+ if (!T) {
582
+ Y(`Received screenshot error for unknown or handled id: ${n.id}`);
583
+ break;
584
+ }
585
+ Y(`Received screenshot error for id ${n.id}: ${n.message}`), T(new Error(n.message)), R.current.delete(n.id), I.current.delete(n.id);
586
+ break;
587
+ default:
588
+ Y(`Received unhandled message type: ${n.type}`, n);
589
+ break;
590
+ }
591
+ }, s.current) {
592
+ const t = await s.current.createOffer({
593
+ offerToReceiveVideo: !0,
594
+ offerToReceiveAudio: !1
595
+ });
596
+ await s.current.setLocalDescription(t), c.current && c.current.send(
597
+ JSON.stringify({
598
+ type: "offer",
599
+ sdp: t.sdp,
600
+ sessionId: g
601
+ })
602
+ ), C("Sent offer");
603
+ }
604
+ } catch (r) {
605
+ C("Error: " + r);
606
+ }
607
+ }, ie = () => {
608
+ c.current && (c.current.close(), c.current = null), s.current && (s.current.close(), s.current = null), o.current && (o.current.srcObject = null), u.current && (u.current.close(), u.current = null), d(!1), C("Stopped");
609
+ };
610
+ Ke(() => (Ee(), document.hidden || j(), document.addEventListener("visibilitychange", z), () => {
611
+ Z(), ie(), document.removeEventListener("visibilitychange", z);
612
+ }), [a, D, i]);
613
+ const se = () => {
614
+ o.current && o.current.focus();
615
+ };
616
+ return le(f, () => ({
617
+ openUrl: (r) => {
618
+ if (!c.current || c.current.readyState !== WebSocket.OPEN) {
619
+ Y("WebSocket not open, cannot send open_url command via ref.");
620
+ return;
621
+ }
622
+ try {
623
+ const O = decodeURIComponent(r);
624
+ C("Opening URL"), c.current.send(
625
+ JSON.stringify({
626
+ type: "openUrl",
627
+ url: O,
628
+ sessionId: g
629
+ })
630
+ );
631
+ } catch (O) {
632
+ Y("Error decoding or sending URL via ref:", { error: O, url: r }), c.current.send(
633
+ JSON.stringify({
634
+ type: "openUrl",
635
+ url: r,
636
+ sessionId: g
637
+ })
638
+ );
639
+ }
640
+ },
641
+ sendKeyEvent: (r) => {
642
+ if (!u.current || u.current.readyState !== "open") {
643
+ Y("Data channel not ready for imperative key command:", u.current?.readyState);
644
+ return;
645
+ }
646
+ const O = oe[r.code];
647
+ if (!O) {
648
+ Y(`Unknown event.code for imperative command: ${r.code}`);
649
+ return;
650
+ }
651
+ let t = e.META_NONE;
652
+ r.shiftKey && (t |= e.META_SHIFT_ON), r.altKey && (t |= e.META_ALT_ON), r.ctrlKey && (t |= e.META_CTRL_ON), r.metaKey && (t |= e.META_META_ON);
653
+ const n = r.type === "keydown" ? e.ACTION_DOWN : e.ACTION_UP;
654
+ N(
655
+ `Sending Imperative Key Command: code=${r.code}, keycode=${O}, action=${n}, meta=${t}`
656
+ );
657
+ const _ = B(
658
+ n,
659
+ O,
660
+ 0,
661
+ // repeat count, typically 0 for single presses
662
+ t
663
+ );
664
+ _ && U(_);
665
+ },
666
+ screenshot: () => new Promise((r, O) => {
667
+ if (!c.current || c.current.readyState !== WebSocket.OPEN)
668
+ return Y("WebSocket not open, cannot send screenshot command."), O(new Error("WebSocket is not connected or connection is not open."));
669
+ const t = `ui-ss-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`, n = {
670
+ type: "screenshot",
671
+ // Matches the type expected by instance API
672
+ id: t
673
+ };
674
+ R.current.set(t, r), I.current.set(t, O), N("Sending screenshot request:", n);
675
+ try {
676
+ c.current.send(JSON.stringify(n));
677
+ } catch (_) {
678
+ Y("Failed to send screenshot request immediately:", _), R.current.delete(t), I.current.delete(t), O(_);
679
+ return;
680
+ }
681
+ setTimeout(() => {
682
+ R.current.has(t) && (Y(`Screenshot request timed out for id ${t}`), I.current.get(t)?.(new Error("Screenshot request timed out")), R.current.delete(t), I.current.delete(t));
683
+ }, 3e4);
684
+ })
685
+ })), /* @__PURE__ */ ne(
686
+ "div",
687
+ {
688
+ className: Ye(
689
+ "rc-container",
690
+ // Use custom CSS class instead of Tailwind
691
+ E
692
+ ),
693
+ style: { touchAction: "none" },
694
+ onMouseDown: m,
695
+ onMouseMove: m,
696
+ onMouseUp: m,
697
+ onMouseLeave: m,
698
+ onTouchStart: m,
699
+ onTouchMove: m,
700
+ onTouchEnd: m,
701
+ onTouchCancel: m,
702
+ children: [
703
+ /* @__PURE__ */ q(
704
+ "video",
705
+ {
706
+ ref: o,
707
+ className: "rc-video",
708
+ autoPlay: !0,
709
+ playsInline: !0,
710
+ muted: !0,
711
+ tabIndex: 0,
712
+ style: { outline: "none", pointerEvents: "none" },
713
+ onKeyDown: X,
714
+ onKeyUp: X,
715
+ onClick: se,
716
+ onFocus: () => {
717
+ o.current && (o.current.style.outline = "none");
718
+ },
719
+ onBlur: () => {
720
+ o.current && (o.current.style.outline = "none");
721
+ }
722
+ }
723
+ ),
724
+ !y && /* @__PURE__ */ ne("div", { className: "rc-placeholder-wrapper", children: [
725
+ /* @__PURE__ */ q("div", { className: "rc-spinner" }),
726
+ /* @__PURE__ */ q("p", { className: "rc-placeholder-content", children: "Connecting..." })
727
+ ] })
728
+ ]
729
+ }
730
+ );
731
+ }
732
+ );
733
+ export {
734
+ Te as RemoteControl
735
+ };