@linkshell/host 0.4.15 → 0.4.17
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/host/src/drivers/claude/activity.d.ts +5 -0
- package/dist/host/src/drivers/claude/activity.js +80 -25
- package/dist/host/src/drivers/claude/activity.js.map +1 -1
- package/dist/host/src/drivers/claude/children.d.ts +18 -0
- package/dist/host/src/drivers/claude/children.js +60 -0
- package/dist/host/src/drivers/claude/children.js.map +1 -0
- package/dist/host/src/drivers/claude/driver.js +7 -2
- package/dist/host/src/drivers/claude/driver.js.map +1 -1
- package/dist/host/src/drivers/claude/transcript.d.ts +7 -3
- package/dist/host/src/drivers/claude/transcript.js +27 -13
- package/dist/host/src/drivers/claude/transcript.js.map +1 -1
- package/dist/host/src/hub.js +9 -3
- package/dist/host/src/hub.js.map +1 -1
- package/dist/host/src/screen-viewer.js +64 -13
- package/dist/host/src/screen-viewer.js.map +1 -1
- package/dist/host/tsconfig.tsbuildinfo +1 -1
- package/dist/wire/src/model.d.ts +3 -0
- package/dist/wire/src/model.js +1 -0
- package/dist/wire/src/model.js.map +1 -1
- package/dist/wire/src/rpc.d.ts +55 -0
- package/dist/wire/src/updates.d.ts +32 -0
- package/dist/wire/src/updates.js +2 -0
- package/dist/wire/src/updates.js.map +1 -1
- package/package.json +2 -2
|
@@ -24,7 +24,6 @@ const ICONS = {
|
|
|
24
24
|
touch: icon('<circle cx="12" cy="12" r="3" fill="currentColor" stroke="none"/><circle cx="12" cy="12" r="7.6"/>'),
|
|
25
25
|
keyboard: icon('<rect x="2.5" y="6" width="19" height="12" rx="2.6"/><path d="M6.6 10h.01M10.2 10h.01M13.8 10h.01M17.4 10h.01M6.6 14h.01M17.4 14h.01M10 14h4"/>'),
|
|
26
26
|
fit: icon('<circle cx="10.5" cy="10.5" r="6.2"/><path d="m15.2 15.2 5 5M8 10.5h5"/>'),
|
|
27
|
-
rotate: icon('<rect x="3.5" y="10" width="17" height="9.5" rx="2.2"/><path d="M7 6.2a8.4 8.4 0 0 1 10 0M17 6.2V3.4M17 6.2h-2.8"/>'),
|
|
28
27
|
expand: icon('<path d="M4 9V4h5M20 9V4h-5M4 15v5h5M20 15v5h-5"/>'),
|
|
29
28
|
shrink: icon('<path d="M9 4v5H4M15 4v5h5M9 20v-5H4M15 20v-5h5"/>'),
|
|
30
29
|
down: icon('<path d="m6 9.5 6 6 6-6"/>'),
|
|
@@ -37,6 +36,8 @@ const STYLE = String.raw `
|
|
|
37
36
|
* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; }
|
|
38
37
|
html, body { margin: 0; height: 100%; -webkit-text-size-adjust: 100%; text-size-adjust: 100%; background: #000; overflow: hidden; overscroll-behavior: none; }
|
|
39
38
|
body { position: fixed; inset: 0; color: #fff; font: 15px/1.45 -apple-system, system-ui, "PingFang SC", "Noto Sans CJK SC", sans-serif; }
|
|
39
|
+
html.native-picture, html.native-picture body { background: transparent; }
|
|
40
|
+
.native-picture canvas, .native-picture video { visibility: hidden; }
|
|
40
41
|
#stage { position: absolute; inset: 0; overflow: hidden; touch-action: none; }
|
|
41
42
|
#picture { position: absolute; inset: 0; transform-origin: 0 0; will-change: transform; }
|
|
42
43
|
#stage.presenting #pointer { opacity: 0 !important; }
|
|
@@ -434,6 +435,12 @@ let mode = MODES.includes(query.get("mode")) ? query.get("mode") : MODES.include
|
|
|
434
435
|
// What the app around the page has done to the phone's screen. A page in a plain browser has none of it.
|
|
435
436
|
// "clear" is the side of a phone lying down that has no camera in it.
|
|
436
437
|
const chrome = Object.assign({ fullscreen: false, landscape: false, canRotate: false, clear: null, insets: { top: 0, right: 0, bottom: 0, left: 0 } }, window.__linkshellChrome || {});
|
|
438
|
+
// The same controls and gestures sit over the iOS Metal surface. Only small input and
|
|
439
|
+
// layout messages cross WebKit's native bridge; video frames never enter this page.
|
|
440
|
+
const nativePicture = chrome.nativePicture === true;
|
|
441
|
+
document.documentElement.classList.toggle("native-picture", nativePicture);
|
|
442
|
+
const nativePost = (message) => window.webkit?.messageHandlers?.linkshellScreen?.postMessage(message);
|
|
443
|
+
let nativeStats = null;
|
|
437
444
|
// Whether this viewer may move the computer's pointer: asked for with the first controlling mode.
|
|
438
445
|
const control = { asked: false, known: false, available: false, trusted: false, reason: "", app: "", prompted: false };
|
|
439
446
|
const content = { w: 0, h: 0 };
|
|
@@ -457,11 +464,11 @@ function sps(unit) {
|
|
|
457
464
|
// full-screen player for it. The app says so in the address as well (?video=1): on Android what it tells the
|
|
458
465
|
// page as it loads can arrive after this script has run. ?video=0 keeps to the socket, to try that way out.
|
|
459
466
|
const relayed = query.get("q") === "low";
|
|
460
|
-
const wantVideo = "RTCPeerConnection" in window && (!app || chrome.video === true || query.get("video") === "1") && query.get("video") !== "0";
|
|
467
|
+
const wantVideo = !nativePicture && "RTCPeerConnection" in window && (!app || chrome.video === true || query.get("video") === "1") && query.get("video") !== "0";
|
|
461
468
|
// The iOS app owns the final transport fallback, so it can bypass a failed direct data channel.
|
|
462
|
-
const relayFallback = !!app && (chrome.relayFallback === true || query.get("fallback") === "relay") && query.get("video") !== "0";
|
|
469
|
+
const relayFallback = !nativePicture && !!app && (chrome.relayFallback === true || query.get("fallback") === "relay") && query.get("video") !== "0";
|
|
463
470
|
const unseeable = () => say("这个系统版本的浏览器内核不支持视频解码,请升级系统后再试。");
|
|
464
|
-
if (!wantVideo && !("VideoDecoder" in window)) unseeable();
|
|
471
|
+
if (!nativePicture && !wantVideo && !("VideoDecoder" in window)) unseeable();
|
|
465
472
|
let decoder, skipping = false, skipped = 0, arrived = 0, keyAsked = -Infinity;
|
|
466
473
|
// Only a keyframe gets a decoder that lost its place going again, and the host makes them when asked rather than by the clock.
|
|
467
474
|
const askKey = () => { const now = performance.now(); if (now - keyAsked > 500) { keyAsked = now; send({ t: "keyframe" }); } };
|
|
@@ -491,7 +498,9 @@ asked.set("width", width);
|
|
|
491
498
|
const framed = document.URL === "about:srcdoc";
|
|
492
499
|
const reloadVia = framed && typeof window.__linkshellReload === "function" ? window.__linkshellReload : null;
|
|
493
500
|
const canChooseWidth = !framed || !!reloadVia;
|
|
494
|
-
const ws =
|
|
501
|
+
const ws = nativePicture
|
|
502
|
+
? { readyState: 1, send: (data) => nativePost({ kind: "signal", message: JSON.parse(data) }) }
|
|
503
|
+
: new WebSocket((location.protocol === "https:" ? "wss://" : "ws://") + location.host + "/stream?" + asked);
|
|
495
504
|
ws.binaryType = "arraybuffer";
|
|
496
505
|
const send = (message) => { if (ws.readyState === 1) ws.send(JSON.stringify(message)); };
|
|
497
506
|
ws.onopen = () => { if (mode !== "view") askControl(); if (document.hidden) send({ t: "hidden" }); };
|
|
@@ -697,7 +706,7 @@ function fallBack() {
|
|
|
697
706
|
if (tried) hint(live ? "直连断开了:已改用兼容方式传画面,延迟会高一些" : "没能和电脑直连:已改用兼容方式传画面,延迟会高一些", 5000);
|
|
698
707
|
}
|
|
699
708
|
|
|
700
|
-
if (!wantVideo) requestRelay();
|
|
709
|
+
if (!nativePicture && !wantVideo) requestRelay();
|
|
701
710
|
|
|
702
711
|
function play() {
|
|
703
712
|
const started = video.play();
|
|
@@ -781,7 +790,7 @@ function echo(x, y) {
|
|
|
781
790
|
else if (gesture) return;
|
|
782
791
|
cursor.x = x;
|
|
783
792
|
cursor.y = y;
|
|
784
|
-
if (rtc && !pointed) point();
|
|
793
|
+
if ((rtc || nativePicture) && !pointed) point();
|
|
785
794
|
else place();
|
|
786
795
|
}
|
|
787
796
|
|
|
@@ -837,6 +846,7 @@ function finishPresentation() {
|
|
|
837
846
|
presentation.time = null;
|
|
838
847
|
presentation.active = false;
|
|
839
848
|
stage.classList.remove("presenting");
|
|
849
|
+
if (nativePicture && content.w) nativePost({ kind: "frame", rect: shown });
|
|
840
850
|
armControlsIdle();
|
|
841
851
|
}
|
|
842
852
|
function drawPresentation() {
|
|
@@ -850,6 +860,7 @@ function drawPresentation() {
|
|
|
850
860
|
const flip = presentationFlip(presentation.current, shown, reducedMotion.matches);
|
|
851
861
|
if (!flip) return finishPresentation();
|
|
852
862
|
picture.style.transform = "translate(" + flip.x + "px," + flip.y + "px) scale(" + flip.sx + "," + flip.sy + ")";
|
|
863
|
+
if (nativePicture) nativePost({ kind: "frame", rect: visiblePicture() });
|
|
853
864
|
}
|
|
854
865
|
function stepPresentation(time) {
|
|
855
866
|
presentation.frame = 0;
|
|
@@ -1273,6 +1284,7 @@ function layout() {
|
|
|
1273
1284
|
toast.style.top = area.y + 14 + "px";
|
|
1274
1285
|
place();
|
|
1275
1286
|
animatePresentation();
|
|
1287
|
+
if (nativePicture && content.w && !presentation.active) nativePost({ kind: "frame", rect: shown });
|
|
1276
1288
|
}
|
|
1277
1289
|
|
|
1278
1290
|
function place() {
|
|
@@ -1335,6 +1347,7 @@ let wired = false, held = 0, sure = true, sent = 0, rest = 0, movedAt = -Infinit
|
|
|
1335
1347
|
const trail = [];
|
|
1336
1348
|
|
|
1337
1349
|
function lanes() {
|
|
1350
|
+
if (nativePicture) { wired = true; return; }
|
|
1338
1351
|
const channels = rtc && rtc.channels;
|
|
1339
1352
|
const open = !!channels && !!channels.input && channels.input.readyState === "open" && !!channels.pointer && channels.pointer.readyState === "open";
|
|
1340
1353
|
// Not taken up with a button down: its release goes the way it went down.
|
|
@@ -1342,6 +1355,7 @@ function lanes() {
|
|
|
1342
1355
|
}
|
|
1343
1356
|
|
|
1344
1357
|
function post(message, lane) {
|
|
1358
|
+
if (nativePicture) return nativePost({ kind: "input", message, replaceable: lane === "pointer" });
|
|
1345
1359
|
if (!wired) return send(message);
|
|
1346
1360
|
message.i = ++sent;
|
|
1347
1361
|
rtc.channels[lane].send(JSON.stringify(message));
|
|
@@ -1353,7 +1367,7 @@ function tell(placed) {
|
|
|
1353
1367
|
lanes();
|
|
1354
1368
|
moveWaiting = false;
|
|
1355
1369
|
const now = performance.now(), lane = laneOf("move", placed, held > 0, false);
|
|
1356
|
-
if (rtc) {
|
|
1370
|
+
if (rtc || nativePicture) {
|
|
1357
1371
|
// What was sent, to know it by when it comes back (see echo).
|
|
1358
1372
|
trail.push({ x: cursor.x, y: cursor.y, t: now });
|
|
1359
1373
|
while (now - trail[0].t > 1000) trail.shift();
|
|
@@ -1916,8 +1930,6 @@ function refresh() {
|
|
|
1916
1930
|
$("keyboard").classList.toggle("gone", mode === "view");
|
|
1917
1931
|
$("quick").classList.toggle("gone", mode === "view");
|
|
1918
1932
|
$("fit").classList.toggle("gone", zoom < 1.05);
|
|
1919
|
-
$("rotate").classList.toggle("gone", !app || !chrome.canRotate);
|
|
1920
|
-
$("rotate").classList.toggle("on", chrome.landscape);
|
|
1921
1933
|
const full = app ? chrome.fullscreen : !!document.fullscreenElement;
|
|
1922
1934
|
$("full").classList.toggle("gone", !app && !document.fullscreenEnabled);
|
|
1923
1935
|
$("full").innerHTML = full ? ICON.shrink : ICON.expand;
|
|
@@ -1983,6 +1995,14 @@ async function status() {
|
|
|
1983
1995
|
layout();
|
|
1984
1996
|
}
|
|
1985
1997
|
const way = !content.w ? "正在连接…" : live ? "直连 · 视频" : (relayed ? "中继" : "直连") + " · 兼容\n" + content.w + "×" + content.h;
|
|
1998
|
+
if (nativePicture) {
|
|
1999
|
+
nativePost({ kind: "stats" });
|
|
2000
|
+
const detail = [content.w ? content.w + "×" + content.h : null,
|
|
2001
|
+
nativeStats?.decodedFps == null ? null : Math.round(nativeStats.decodedFps) + " 帧/秒",
|
|
2002
|
+
nativeStats?.rttMs == null ? null : "往返 " + Math.round(nativeStats.rttMs) + " 毫秒"].filter(Boolean).join(" · ");
|
|
2003
|
+
put(way + (detail ? "\n" + detail : ""));
|
|
2004
|
+
return;
|
|
2005
|
+
}
|
|
1986
2006
|
const pc = live && rtc && rtc.pc;
|
|
1987
2007
|
if (!pc || !line.textContent.startsWith(way)) put(way);
|
|
1988
2008
|
const now = pc && (await vitals(pc, statusWas).catch(() => null));
|
|
@@ -2017,7 +2037,6 @@ tappable($("closeinfo"), closeMenu);
|
|
|
2017
2037
|
pressable($("keyboard"), () => (coarse ? raiseKeys() : toggleKeys()));
|
|
2018
2038
|
pressable($("quick"), openSheet);
|
|
2019
2039
|
pressable($("fit"), () => zoomTo(1, { x: area.x + area.w / 2, y: area.y + area.h / 2 }));
|
|
2020
|
-
pressable($("rotate"), () => tellApp({ type: "landscape", on: !chrome.landscape }));
|
|
2021
2040
|
for (const tile of $("widths").querySelectorAll(".tile")) {
|
|
2022
2041
|
tile.classList.toggle("on", tile.dataset.width === width);
|
|
2023
2042
|
tile.setAttribute("aria-pressed", String(tile.dataset.width === width));
|
|
@@ -2026,6 +2045,7 @@ for (const tile of $("widths").querySelectorAll(".tile")) {
|
|
|
2026
2045
|
if (chosen === width) return;
|
|
2027
2046
|
try { localStorage.setItem("linkshell.screen.width", chosen); } catch {}
|
|
2028
2047
|
tellApp({ type: "width", width: chosen });
|
|
2048
|
+
if (nativePicture) return;
|
|
2029
2049
|
if (reloadVia) return reloadVia({ width: chosen, mode });
|
|
2030
2050
|
const next = new URLSearchParams(location.search);
|
|
2031
2051
|
next.set("width", chosen);
|
|
@@ -2341,7 +2361,7 @@ window.linkshellChrome = (next) => {
|
|
|
2341
2361
|
// was seen, whichever way the picture comes. The page's clock is taken to be the computer's (a browser on
|
|
2342
2362
|
// it, a simulator); with ?measure=sync the host is asked the time, for a device with a clock of its own.
|
|
2343
2363
|
let meter = null;
|
|
2344
|
-
if (query.get("measure")) meter = (() => {
|
|
2364
|
+
if (!nativePicture && query.get("measure")) meter = (() => {
|
|
2345
2365
|
const pageTime = () => performance.timeOrigin + performance.now();
|
|
2346
2366
|
const sync = query.get("measure") === "sync", pings = new Map();
|
|
2347
2367
|
let clock = { offset: 0, trip: Infinity }, pinged = 0;
|
|
@@ -2488,6 +2508,38 @@ if (query.get("measure")) meter = (() => {
|
|
|
2488
2508
|
resetTyping();
|
|
2489
2509
|
refresh();
|
|
2490
2510
|
wake();
|
|
2511
|
+
if (nativePicture) {
|
|
2512
|
+
window.linkshellNative = {
|
|
2513
|
+
picture: (size) => {
|
|
2514
|
+
content.w = size.width; content.h = size.height;
|
|
2515
|
+
if (!live) show(true);
|
|
2516
|
+
say("");
|
|
2517
|
+
layout();
|
|
2518
|
+
},
|
|
2519
|
+
message: (message) => {
|
|
2520
|
+
if (message.t === "cursor" || message.t === "shape") told(message);
|
|
2521
|
+
else heard(message);
|
|
2522
|
+
},
|
|
2523
|
+
stats: (stats) => { nativeStats = stats; },
|
|
2524
|
+
connecting: () => {
|
|
2525
|
+
nativeStats = null;
|
|
2526
|
+
Object.assign(control, { asked: false, known: false, trusted: false });
|
|
2527
|
+
say("正在连接电脑屏幕…");
|
|
2528
|
+
if (mode !== "view") askControl();
|
|
2529
|
+
},
|
|
2530
|
+
};
|
|
2531
|
+
// React Native mounts the WebView as a child of the native renderer. Its WebKit
|
|
2532
|
+
// handler can arrive just after this page, so wait for that attachment once.
|
|
2533
|
+
const waitingSince = performance.now();
|
|
2534
|
+
const attachNative = () => {
|
|
2535
|
+
if (window.webkit?.messageHandlers?.linkshellScreen) {
|
|
2536
|
+
nativePost({ kind: "ready", version: 1 });
|
|
2537
|
+
ws.onopen();
|
|
2538
|
+
} else if (performance.now() - waitingSince < 5000) setTimeout(attachNative, 50);
|
|
2539
|
+
else tellApp({ type: "nativeUnavailable" });
|
|
2540
|
+
};
|
|
2541
|
+
attachNative();
|
|
2542
|
+
}
|
|
2491
2543
|
tellApp({ type: "ready", mode });
|
|
2492
2544
|
`;
|
|
2493
2545
|
const SCRIPT = `(() => {${VIEWER_LOGIC}${BODY}})();`;
|
|
@@ -2525,7 +2577,6 @@ export function viewerPage() {
|
|
|
2525
2577
|
<div class="tool" id="keyboard" role="button" aria-label="键盘">${ICONS.keyboard}</div>
|
|
2526
2578
|
<div class="tool" id="quick" role="button" aria-label="快捷操作">${ICONS.quick}</div>
|
|
2527
2579
|
<div class="tool" id="fit" role="button" aria-label="还原缩放">${ICONS.fit}</div>
|
|
2528
|
-
<div class="tool" id="rotate" role="button" aria-label="横屏">${ICONS.rotate}</div>
|
|
2529
2580
|
<div class="tool" id="info" role="button" aria-label="连接信息">${ICONS.info}</div>
|
|
2530
2581
|
<div class="tool" id="full" role="button" aria-label="全屏"></div>
|
|
2531
2582
|
</div>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"screen-viewer.js","sourceRoot":"","sources":["../../../src/screen-viewer.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,2EAA2E;AAC3E,6EAA6E;AAC7E,mEAAmE;AACnE,EAAE;AACF,4DAA4D;AAC5D,yEAAyE;AACzE,2CAA2C;AAC3C,6EAA6E;AAC7E,+EAA+E;AAC/E,2CAA2C;AAC3C,qGAAqG;AACrG,+EAA+E;AAC/E,6FAA6F;AAC7F,EAAE;AACF,oGAAoG;AACpG,mGAAmG;AACnG,+FAA+F;AAC/F,8FAA8F;AAE9F,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,EAAE,CAC5B,uJAAuJ,IAAI,QAAQ,CAAC;AAEtK,MAAM,KAAK,GAAG;IACZ,IAAI,EAAE,IAAI,CAAC,qHAAqH,CAAC;IACjI,QAAQ,EAAE,IAAI,CAAC,6DAA6D,CAAC;IAC7E,KAAK,EAAE,IAAI,CAAC,oGAAoG,CAAC;IACjH,QAAQ,EAAE,IAAI,CAAC,iJAAiJ,CAAC;IACjK,GAAG,EAAE,IAAI,CAAC,0EAA0E,CAAC;IACrF,MAAM,EAAE,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"screen-viewer.js","sourceRoot":"","sources":["../../../src/screen-viewer.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,2EAA2E;AAC3E,6EAA6E;AAC7E,mEAAmE;AACnE,EAAE;AACF,4DAA4D;AAC5D,yEAAyE;AACzE,2CAA2C;AAC3C,6EAA6E;AAC7E,+EAA+E;AAC/E,2CAA2C;AAC3C,qGAAqG;AACrG,+EAA+E;AAC/E,6FAA6F;AAC7F,EAAE;AACF,oGAAoG;AACpG,mGAAmG;AACnG,+FAA+F;AAC/F,8FAA8F;AAE9F,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,EAAE,CAC5B,uJAAuJ,IAAI,QAAQ,CAAC;AAEtK,MAAM,KAAK,GAAG;IACZ,IAAI,EAAE,IAAI,CAAC,qHAAqH,CAAC;IACjI,QAAQ,EAAE,IAAI,CAAC,6DAA6D,CAAC;IAC7E,KAAK,EAAE,IAAI,CAAC,oGAAoG,CAAC;IACjH,QAAQ,EAAE,IAAI,CAAC,iJAAiJ,CAAC;IACjK,GAAG,EAAE,IAAI,CAAC,0EAA0E,CAAC;IACrF,MAAM,EAAE,IAAI,CAAC,oDAAoD,CAAC;IAClE,MAAM,EAAE,IAAI,CAAC,oDAAoD,CAAC;IAClE,IAAI,EAAE,IAAI,CAAC,4BAA4B,CAAC;IACxC,KAAK,EAAE,IAAI,CAAC,qDAAqD,CAAC;IAClE,OAAO,EAAE,IAAI,CAAC,sEAAsE,CAAC;IACrF,IAAI,EAAE,IAAI,CAAC,iEAAiE,CAAC;IAC7E,KAAK,EAAE,IAAI,CAAC,4CAA4C,CAAC;CAC1D,CAAC;AAEF,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkHvB,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkQrC,CAAC;AAEF,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmlEtB,CAAC;AAEF,MAAM,MAAM,GAAG,WAAW,YAAY,GAAG,IAAI,OAAO,CAAC;AAErD,MAAM,UAAU,UAAU;IACxB,OAAO;;;;SAIA,KAAK;;;;;;;;;;;iHAWmG,KAAK,CAAC,KAAK;;;;;;;;;;;;;;;kEAe1D,KAAK,CAAC,QAAQ;iEACf,KAAK,CAAC,KAAK;+DACb,KAAK,CAAC,GAAG;gEACR,KAAK,CAAC,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6CAmD7B,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;UACxD,MAAM;eACD,CAAC;AAChB,CAAC"}
|