whipdesk 0.0.8 → 0.0.9
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/agent.cjs +8220 -6587
- package/dist/mobile-web/assets/index-C1gDHBib.css +1 -0
- package/dist/mobile-web/assets/index-YSgNjLfG.js +2 -0
- package/dist/mobile-web/assets/index.esm-B3ZTr43m.js +1 -0
- package/dist/mobile-web/assets/index.esm-BgjPHsdM.js +1 -0
- package/dist/mobile-web/assets/index.esm-CmSagqpw.js +1 -0
- package/dist/mobile-web/assets/index.esm-Di8jSGaG.js +4 -0
- package/dist/mobile-web/assets/index.esm-mXKu2C3o.js +1 -0
- package/dist/mobile-web/assets/index.esm-wogdVWd2.js +30 -0
- package/dist/mobile-web/index.html +2 -2
- package/package.json +9 -9
- package/dist/mobile-web/assets/index-7VFGQkgi.css +0 -1
- package/dist/mobile-web/assets/index-ChOsBFZF.js +0 -2
- package/dist/mobile-web/assets/index.esm-B3nnhxdY.js +0 -1144
- package/dist/mobile-web/assets/index.esm-C6Dwpirr.js +0 -16
- package/dist/mobile-web/assets/index.esm-DOt1fDqh.js +0 -1636
- package/dist/mobile-web/assets/index.esm-DYB-Ijb5.js +0 -2287
- package/dist/mobile-web/assets/index.esm-ES8aBvqn.js +0 -599
- package/dist/mobile-web/assets/index.esm2017-B0pGzpCg.js +0 -481
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
<meta name="apple-mobile-web-app-title" content="WhipDesk" />
|
|
19
19
|
<meta name="mobile-web-app-capable" content="yes" />
|
|
20
20
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
|
21
|
-
<script type="module" crossorigin src="./assets/index-
|
|
22
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
21
|
+
<script type="module" crossorigin src="./assets/index-YSgNjLfG.js"></script>
|
|
22
|
+
<link rel="stylesheet" crossorigin href="./assets/index-C1gDHBib.css">
|
|
23
23
|
</head>
|
|
24
24
|
<body>
|
|
25
25
|
<div id="app"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "whipdesk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "WhipDesk desktop host: screen capture, input injection, notification hub, and the HTTP/WebSocket server that serves the mobile client.",
|
|
6
6
|
"license": "AGPL-3.0",
|
|
@@ -28,14 +28,14 @@
|
|
|
28
28
|
"start": "tsx src/index.ts",
|
|
29
29
|
"dev": "tsx watch src/index.ts",
|
|
30
30
|
"typecheck": "tsc --noEmit",
|
|
31
|
-
"test": "tsx --test src/security/pin.test.ts src/monitor/state.test.ts",
|
|
31
|
+
"test": "tsx --test src/security/pin.test.ts src/monitor/state.test.ts src/transport/session.test.ts",
|
|
32
32
|
"build:bundle": "node scripts/build-bundle.mjs",
|
|
33
33
|
"build:sea": "node scripts/build-sea.mjs",
|
|
34
34
|
"prepublishOnly": "node scripts/build-bundle.mjs"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@whipdesk/protocol": "*",
|
|
38
|
-
"express": "^
|
|
38
|
+
"express": "^5.2.1",
|
|
39
39
|
"ffmpeg-static": "^5.3.0",
|
|
40
40
|
"qrcode-terminal": "^0.12.0",
|
|
41
41
|
"screenshot-desktop": "^1.15.1",
|
|
@@ -43,17 +43,17 @@
|
|
|
43
43
|
},
|
|
44
44
|
"optionalDependencies": {
|
|
45
45
|
"@nut-tree-fork/nut-js": "^4.2.0",
|
|
46
|
-
"sharp": "^0.
|
|
47
|
-
"werift": "^0.
|
|
46
|
+
"sharp": "^0.35.3",
|
|
47
|
+
"werift": "^0.23.0"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@types/express": "^
|
|
51
|
-
"@types/node": "^
|
|
50
|
+
"@types/express": "^5.0.6",
|
|
51
|
+
"@types/node": "^24.0.0",
|
|
52
52
|
"@types/qrcode-terminal": "^0.12.2",
|
|
53
53
|
"@types/ws": "^8.5.12",
|
|
54
|
-
"esbuild": "^0.
|
|
54
|
+
"esbuild": "^0.28.1",
|
|
55
55
|
"postject": "^1.0.0-alpha.6",
|
|
56
56
|
"tsx": "^4.19.2",
|
|
57
|
-
"typescript": "^5.
|
|
57
|
+
"typescript": "^5.9.3"
|
|
58
58
|
}
|
|
59
59
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
:root{--bg: #f4f6f9;--panel: #ffffff;--panel-2: #eef1f5;--border: #d6dce4;--text: #1a2330;--muted: #5f6b7a;--accent: #2f6fed;--accent-2: #1aa66b;--warn: #c77700;--error: #d83b3b;--shadow: rgba(20, 30, 50, .16);--screen-bg: #1b2027}@media (prefers-color-scheme: dark){:root{--bg: #0b0e14;--panel: #151a23;--panel-2: #1d2430;--border: #2a3342;--text: #e6edf3;--muted: #8b98a9;--accent: #4ea1ff;--accent-2: #36d399;--warn: #f5a623;--error: #ff5c5c;--shadow: rgba(0, 0, 0, .5);--screen-bg: #000000}}*{box-sizing:border-box;-webkit-tap-highlight-color:transparent}html,body{margin:0;height:100%;overflow:hidden;background:var(--bg);color:var(--text);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,system-ui,sans-serif;overscroll-behavior:none}#app{position:fixed;inset:0;user-select:none;-webkit-user-select:none}input,textarea{user-select:text;-webkit-user-select:text}.wd-screen{position:absolute;inset:0;width:100%;height:100%;display:block;background:var(--screen-bg);touch-action:none}.wd-statusbar{position:absolute;top:calc(env(safe-area-inset-top,0px) + 8px);left:8px;max-width:calc(100% - 64px);display:flex;align-items:center;gap:8px;padding:6px 10px;min-height:38px;border-radius:10px;background:var(--panel);border:1px solid var(--border);box-shadow:0 2px 10px var(--shadow);font-size:12px;z-index:5;cursor:pointer;transition:max-width .25s ease,padding .25s ease}.wd-statusbar.collapsed{max-width:124px;padding:6px 8px;overflow:hidden}.wd-statusbar.collapsed .wd-status-text,.wd-statusbar.collapsed .wd-watchers{display:none}.wd-dot{width:9px;height:9px;border-radius:50%;background:var(--muted);flex:none}.wd-dot[data-status=connected]{background:var(--accent-2)}.wd-dot[data-status=connecting]{background:var(--warn)}.wd-dot[data-status=disconnected]{background:var(--error)}.wd-status-text{color:var(--text);font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1 1 auto;min-width:0}.wd-watchers{color:var(--warn);font-weight:700;flex:none;white-space:nowrap}.wd-transport{flex:none;font-size:11px;font-weight:800;letter-spacing:.4px;padding:2px 6px;border-radius:6px;color:#fff;background:var(--accent-2);text-transform:uppercase}.wd-transport.hidden{display:none}.wd-transport[data-kind=turn]{background:#e0a800;color:#111}.wd-transport[data-kind=stun],.wd-transport[data-kind=direct]{background:var(--accent-2)}.wd-transport[data-kind=lan]{background:#3b82f6}.wd-bell{position:absolute;top:calc(env(safe-area-inset-top,0px) + 8px);right:8px;z-index:6;display:grid;place-items:center;min-width:38px;min-height:38px;padding:6px;border-radius:10px;border:1px solid var(--border);background:var(--panel);color:var(--text);box-shadow:0 2px 10px var(--shadow);cursor:pointer}.wd-badge{position:absolute;top:-6px;right:-6px;min-width:18px;height:18px;padding:0 5px;border-radius:9px;background:var(--accent);color:#fff;font-size:11px;font-weight:700;line-height:18px;text-align:center;box-shadow:0 1px 4px var(--shadow)}.wd-ribbon{position:absolute;left:0;right:0;bottom:0;z-index:6;padding-bottom:env(safe-area-inset-bottom,0px);background:var(--panel);border-top:1px solid var(--border);box-shadow:0 -4px 16px var(--shadow)}.wd-panel{display:flex;flex-direction:column}.wd-options{padding:6px;border-bottom:1px solid var(--border)}.wd-pane{display:flex;gap:5px;align-items:center;flex-wrap:wrap;justify-content:center}.wd-pane-col{flex-direction:column;align-items:stretch;gap:8px}.wd-pane-single-row{flex-wrap:nowrap;overflow-x:auto;justify-content:center;align-items:stretch;gap:4px;scrollbar-width:none}.wd-pane-single-row::-webkit-scrollbar{display:none}.wd-pane-single-row .wd-group{flex:0 1 auto;min-width:0;padding:3px 4px;gap:2px}.wd-pane-single-row .wd-group-items{flex-wrap:nowrap;gap:3px}.wd-pane-single-row .wd-btn{min-width:0}.wd-pane-single-row .wd-btn.wd-icon-only{min-width:30px;padding:4px}.wd-pane-single-row .wd-btn.wd-icon-only svg{width:18px;height:18px}.wd-pane-single-row .wd-btn.wd-go{padding:6px 9px;font-size:12px;gap:4px}@media (min-width: 390px){.wd-pane-single-row{gap:5px}.wd-pane-single-row .wd-group{padding:3px 6px}.wd-pane-single-row .wd-btn.wd-icon-only{min-width:36px;padding:5px}.wd-pane-single-row .wd-btn.wd-icon-only svg{width:20px;height:20px}.wd-pane-single-row .wd-btn.wd-go{padding:7px 12px;font-size:13px}}@media (min-width: 560px){.wd-pane-single-row .wd-btn.wd-icon-only{min-width:42px;padding:6px}.wd-pane-single-row .wd-btn.wd-go{padding:8px 16px}}@media (min-width: 820px){.wd-ribbon{left:50%;right:auto;transform:translate(-50%);width:min(880px,94vw);bottom:16px;border:1px solid var(--border);border-radius:16px;box-shadow:0 10px 34px var(--shadow);padding-bottom:0}.wd-options{padding:5px 6px}.wd-btn{min-height:32px;font-size:12px}.wd-pane-single-row .wd-btn.wd-icon-only{min-width:32px;padding:4px}.wd-pane-single-row .wd-btn.wd-icon-only svg{width:17px;height:17px}.wd-pane-single-row .wd-btn.wd-go{padding:6px 12px;font-size:12px}.wd-group{padding:3px 6px}.wd-bell{min-width:32px;min-height:32px}.wd-statusbar{min-height:32px}}.wd-group{display:inline-flex;flex-direction:column;align-items:stretch;gap:3px;padding:4px 6px;border:1px solid var(--border);border-radius:12px;background:var(--panel-2)}.wd-group-label{font-size:9px;font-weight:700;letter-spacing:.03em;text-transform:uppercase;color:var(--muted);white-space:nowrap;text-align:center}.wd-group-head{display:flex;align-items:center;justify-content:space-between;gap:4px}.wd-mode-toggle{display:inline-flex;border:1px solid var(--border);border-radius:999px;overflow:hidden;background:var(--panel)}.wd-mode-btn{border:0;background:transparent;color:var(--muted);font-size:10px;font-weight:700;line-height:1;padding:4px 7px;cursor:pointer}.wd-mode-btn.on{background:var(--accent);color:#fff}.wd-group-items{display:flex;gap:3px;flex-wrap:wrap;justify-content:center;align-items:center}.wd-wrap{display:flex;gap:6px;flex-wrap:wrap;justify-content:center}.wd-btn-label{line-height:1}.wd-row{display:flex;gap:6px;align-items:center;flex-wrap:wrap;justify-content:center}.wd-tabs{display:flex;gap:4px;padding:5px 6px;align-items:center}.wd-tab{flex:1;display:flex;flex-direction:column;align-items:center;gap:2px;min-height:48px;padding:6px 4px;border-radius:10px;border:1px solid transparent;background:transparent;color:var(--muted);font-size:11px;font-weight:600;cursor:pointer;touch-action:manipulation}.wd-tab .wd-tab-label{line-height:1}.wd-tab.on{background:var(--panel-2);color:var(--accent);border-color:var(--border)}.wd-collapse{flex:none;min-width:44px;min-height:48px;display:grid;place-items:center;border-radius:10px;border:1px solid var(--border);background:var(--panel);color:var(--text);cursor:pointer}.wd-btn{display:inline-flex;align-items:center;justify-content:center;gap:7px;min-width:40px;min-height:40px;padding:6px 10px;border-radius:10px;border:1px solid var(--border);background:var(--panel);color:var(--text);font-size:13px;font-weight:600;line-height:1;cursor:pointer;user-select:none;-webkit-user-select:none;touch-action:manipulation;white-space:nowrap}.wd-btn svg{flex:none}.wd-btn.wd-icon-only{padding:6px;min-width:40px}.wd-btn:active{background:var(--panel-2);transform:translateY(1px)}.wd-btn.on{border-color:var(--accent);color:var(--accent);box-shadow:inset 0 0 0 1px var(--accent)}.wd-btn.wd-primary{background:var(--accent);border-color:var(--accent);color:#fff}.wd-btn.wd-go{background:var(--accent-2);border-color:var(--accent-2);color:#fff;padding:8px 14px}.wd-btn.wd-go:active{filter:brightness(.92);transform:translateY(1px)}.wd-seg{display:inline-flex;border:1px solid var(--border);border-radius:10px;overflow:hidden}.wd-seg-btn{display:inline-flex;align-items:center;gap:6px;min-height:44px;padding:8px 18px;border:none;background:var(--panel);color:var(--muted);font-size:15px;font-weight:600;cursor:pointer;touch-action:manipulation}.wd-seg-btn.on{background:var(--accent);color:#fff}.wd-zoom{min-width:52px;text-align:center;font-variant-numeric:tabular-nums;color:var(--text);font-weight:600}.wd-hint{flex-basis:100%;text-align:center;color:var(--muted);font-size:12px}.wd-type-input{width:100%;resize:vertical;min-height:46px;padding:10px;border-radius:10px;border:1px solid var(--border);background:var(--panel-2);color:var(--text);font-size:16px;font-family:inherit}.wd-keys-row{display:flex;gap:6px;flex-wrap:wrap;justify-content:center}.wd-type-actions{display:flex;gap:6px}.wd-type-actions .wd-btn{flex:1}.wd-monitor-list{display:flex;gap:6px;flex-wrap:wrap;justify-content:center}.wd-toasts{position:absolute;top:calc(env(safe-area-inset-top,0px) + 36px);left:8px;right:8px;display:flex;flex-direction:column;gap:8px;z-index:9;pointer-events:none}.wd-toast{background:var(--panel);border:1px solid var(--border);border-left-width:4px;border-radius:10px;padding:10px 12px;box-shadow:0 6px 20px #0006;display:flex;flex-direction:column;gap:2px;transition:opacity .3s ease,transform .3s ease}.wd-toast strong{font-size:14px}.wd-toast span{font-size:13px;color:var(--muted)}.wd-toast.wd-success{border-left-color:var(--accent-2)}.wd-toast.wd-warning{border-left-color:var(--warn)}.wd-toast.wd-error{border-left-color:var(--error)}.wd-toast.wd-info{border-left-color:var(--accent)}.wd-toast.wd-hide{opacity:0;transform:translateY(-8px)}.hidden{display:none!important}.wd-pin-overlay{position:absolute;inset:0;z-index:20;display:grid;place-items:center;background:#06090eeb;backdrop-filter:blur(6px);padding:20px}.wd-pin-card{width:100%;max-width:320px;background:var(--panel);border:1px solid var(--border);border-radius:14px;padding:24px;text-align:center}.wd-pin-whip{width:64px;height:64px;object-fit:contain;display:block;margin:0 auto 12px}.wd-pin-card h2{margin:0 0 8px;font-size:20px}.wd-pin-msg{margin:0 0 16px;color:var(--muted);font-size:14px}.wd-pin-msg.err{color:var(--error)}.wd-pin-input{width:100%;padding:14px;border-radius:10px;border:1px solid var(--border);background:var(--panel-2);color:var(--text);font-size:22px;text-align:center;letter-spacing:6px;margin-bottom:14px}.wd-pin-submit{width:100%;min-height:50px;border:none;border-radius:12px;background:var(--accent);color:#fff;font-size:17px;font-weight:700;cursor:pointer;touch-action:manipulation}.wd-pin-submit:active{filter:brightness(.92);transform:translateY(1px)}.wd-pin-back{display:block;margin:14px auto 0;padding:6px 10px;background:none;border:none;color:var(--muted);font-size:13px;font-weight:600;cursor:pointer}.wd-pin-back:active{color:var(--text)}.wd-connecting{position:absolute;inset:0;z-index:18;display:grid;place-items:center;background:#06090eeb;backdrop-filter:blur(6px);padding:20px}.wd-connecting-card{width:100%;max-width:320px;text-align:center}.wd-connecting-whip{width:56px;height:56px;object-fit:contain;display:block;margin:0 auto 18px;transform-origin:left bottom;animation:wd-whip-crack 1.4s ease-in-out infinite}.wd-connecting-spinner{width:34px;height:34px;margin:0 auto 16px;border:3px solid rgba(255,255,255,.22);border-top-color:#4ea1ff;border-radius:50%;animation:wd-spin .8s linear infinite}.wd-connecting-msg{margin:0;color:#eef2f7;font-size:15px;font-weight:600;text-shadow:0 1px 2px rgba(0,0,0,.5)}@keyframes wd-spin{to{transform:rotate(360deg)}}@keyframes wd-whip-crack{0%,to{transform:rotate(-16deg)}40%{transform:rotate(14deg)}55%{transform:rotate(5deg)}70%{transform:rotate(11deg)}}.wd-dialog-overlay{position:absolute;inset:0;z-index:18;display:grid;place-items:center;background:#06090e99;backdrop-filter:blur(4px);padding:16px}.wd-dialog{width:100%;max-width:380px;background:var(--panel);border:1px solid var(--border);border-radius:14px;padding:16px}.wd-dialog-head{display:flex;align-items:center;justify-content:space-between}.wd-dialog-head h2{margin:0;font-size:18px}.wd-dialog-x{display:grid;place-items:center;width:36px;height:36px;border-radius:8px;border:1px solid var(--border);background:var(--panel-2);color:var(--text);cursor:pointer}.wd-dialog-help{color:var(--muted);font-size:13px;margin:8px 0 12px}.wd-help-intro{margin:0 0 6px}.wd-help-list{margin:0 0 8px;padding-left:18px;display:flex;flex-direction:column;gap:5px}.wd-help-list strong{color:var(--text)}.wd-help-note{margin:0;font-style:italic}.wd-watch-list{display:flex;flex-direction:column;gap:8px;margin-bottom:12px}.wd-watch-row{display:flex;align-items:center;gap:8px;padding:8px 10px;border:1px solid var(--border);border-radius:10px;background:var(--panel-2)}.wd-watch-name{flex:1;font-weight:600;text-align:left;border:none;background:transparent;color:var(--text);font-size:14px;font-family:inherit;padding:4px 0;cursor:pointer}.wd-watch-name:active{color:var(--accent)}.wd-timer-info{flex:1;display:flex;flex-direction:column;gap:2px;min-width:0}.wd-timer-name{display:flex;align-items:center;gap:6px;font-weight:600;font-size:14px;color:var(--text)}.wd-timer-name svg{flex:none;color:var(--accent)}.wd-timer-remain{font-size:12px;color:var(--muted);font-variant-numeric:tabular-nums}.wd-timer-tag{flex:none;font-size:10px;font-weight:800;letter-spacing:.5px;text-transform:uppercase;color:#fff;background:var(--accent);padding:2px 6px;border-radius:6px}.wd-dialog-actions{display:flex;gap:8px;margin-top:4px}.wd-dialog-actions .wd-btn{flex:1;justify-content:center}.wd-dialog-actions.wd-actions-stack{flex-direction:column}.wd-actions-stack .wd-btn{width:100%}.wd-mon-state{flex:none;font-size:10px;font-weight:800;letter-spacing:.4px;text-transform:uppercase;color:#fff;background:var(--muted);padding:2px 6px;border-radius:6px}.wd-mon-state[data-state=working]{background:var(--accent-2)}.wd-mon-state[data-state=blocked]{background:var(--warn)}.wd-mon-state[data-state=finished]{background:var(--accent)}.wd-mon-state[data-state=crashed]{background:var(--error)}.wd-mon-pick-head{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:6px}.wd-mon-pick{display:flex;flex-direction:column;gap:6px;max-height:40vh;overflow-y:auto;margin-bottom:12px}.wd-mon-item{display:flex;align-items:center;justify-content:space-between;gap:10px;width:100%;padding:9px 11px;border-radius:10px;border:1px solid var(--border);background:var(--panel);color:var(--text);cursor:pointer;text-align:left}.wd-mon-item.on{border-color:var(--accent);box-shadow:inset 0 0 0 1px var(--accent)}.wd-mon-item-main{display:flex;align-items:center;gap:8px;min-width:0}.wd-mon-item-main svg{flex:none;color:var(--accent)}.wd-mon-item-title{font-weight:600;font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.wd-mon-always{display:flex;flex-direction:column;gap:8px}.wd-check{display:flex;align-items:flex-start;gap:8px;font-size:13px;color:var(--text);cursor:pointer}.wd-check input{width:16px;height:16px;margin-top:1px;flex:none;accent-color:var(--accent)}.wd-check-text{display:flex;flex-direction:column;gap:2px}.wd-check-sub{font-size:11px;color:var(--muted)}.wd-form-row{display:flex;flex-direction:column;gap:5px;margin-bottom:12px}.wd-form-label{font-size:12px;font-weight:700;color:var(--muted)}.wd-input{width:100%;padding:10px 12px;border-radius:10px;border:1px solid var(--border);background:var(--panel-2);color:var(--text);font-size:15px;font-family:inherit}.wd-input-area{min-height:64px;resize:vertical}.wd-form-duration{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.wd-input-num{width:56px;text-align:center}.wd-form-unit{font-size:14px;color:var(--muted);font-weight:600}.wd-preset-row{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:10px}.wd-preset{flex:1 1 auto;min-width:48px;padding:9px 10px;border-radius:999px;border:1px solid var(--border);background:var(--panel-2);color:var(--text);font-size:13px;font-weight:700;font-family:inherit;cursor:pointer}.wd-preset:active{background:var(--accent);color:#fff;border-color:transparent}.wd-stepper{display:flex;align-items:center;gap:4px}.wd-step-btn{width:38px;height:38px;flex:none;border-radius:10px;border:1px solid var(--border);background:var(--panel-2);color:var(--text);font-size:22px;font-weight:700;line-height:1;font-family:inherit;cursor:pointer;touch-action:manipulation}.wd-step-btn:active{background:var(--accent);color:#fff;border-color:transparent}.wd-form-target{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.wd-form-target-status{font-size:13px;color:var(--muted)}.wd-form-target-status.set{color:var(--accent-2);font-weight:700}.wd-pick-banner{position:absolute;top:calc(env(safe-area-inset-top,0px) + 56px);left:50%;transform:translate(-50%);z-index:30;background:var(--accent);color:#fff;font-weight:700;font-size:13px;padding:10px 16px;border-radius:999px;box-shadow:0 6px 20px var(--shadow);pointer-events:none;text-align:center;max-width:90%}.wd-conn-row{display:flex;align-items:flex-start;gap:10px;padding:10px 0;border-bottom:1px solid var(--border)}.wd-conn-label{flex:none;width:92px;font-size:13px;font-weight:700;color:var(--muted);padding-top:2px}.wd-conn-value{flex:1;font-size:15px;font-weight:600;color:var(--text);word-break:break-word}.wd-conn-route{flex:1;display:flex;flex-direction:column;gap:4px}.wd-conn-route .wd-transport{align-self:flex-start;position:static}.wd-conn-desc{font-size:12.5px;color:var(--muted)}.wd-conn-status{flex:1;display:flex;align-items:center;gap:8px}.wd-conn-error{margin-top:12px;padding:9px 11px;border-radius:10px;background:#dc35451f;border:1px solid var(--error);color:var(--error);font-size:12.5px;font-weight:600;word-break:break-word}.wd-disconnect{width:100%;justify-content:center;margin-top:14px;background:var(--error);color:#fff;border-color:transparent;min-height:46px;font-size:15px;font-weight:700}.wd-support-link{display:inline-flex;align-items:center;gap:6px;align-self:flex-start;margin-top:8px;padding:5px 12px;background:transparent;border:1px solid var(--border);border-radius:999px;color:var(--muted);font-size:12.5px;font-weight:600;cursor:pointer;transition:color .15s ease,border-color .15s ease}.wd-support-link:hover,.wd-support-link:active{color:var(--accent);border-color:var(--accent)}.wd-support-link svg{width:14px;height:14px;flex:none}.wd-conn-feedback{margin-top:14px;padding-top:12px;border-top:1px solid var(--border);text-align:center}.wd-conn-feedback-text{margin:0;font-size:12.5px;color:var(--muted)}.wd-conn-feedback-links{display:flex;justify-content:center;gap:10px;margin-top:9px}.wd-conn-feedback-link{display:inline-flex;align-items:center;gap:7px;padding:7px 16px;background:transparent;border:1px solid var(--border);border-radius:999px;color:var(--text);font-size:13px;font-weight:600;text-decoration:none;transition:color .15s ease,border-color .15s ease}.wd-conn-feedback-link:hover,.wd-conn-feedback-link:active{color:var(--accent);border-color:var(--accent)}.wd-conn-feedback-link svg{width:16px;height:16px;flex:none}.wd-pin-support{margin-top:16px}.wd-placing .wd-ribbon,.wd-placing .wd-statusbar,.wd-placing .wd-bell{display:none}.wd-place-layer{position:absolute;inset:0;z-index:40;touch-action:none;background:transparent}.wd-place-marker{position:absolute;z-index:41;width:46px;height:46px;transform:translate(-50%,-50%);border:2px solid #4ea1ff;border-radius:50%;box-shadow:0 0 0 2px #00000080,0 0 14px #4ea1ffb3;pointer-events:none}.wd-place-marker:before,.wd-place-marker:after{content:"";position:absolute;background:#4ea1ff}.wd-place-marker:before{left:50%;top:-8px;bottom:-8px;width:2px;transform:translate(-50%)}.wd-place-marker:after{top:50%;left:-8px;right:-8px;height:2px;transform:translateY(-50%)}.wd-place-bar{position:absolute;left:0;right:0;bottom:0;z-index:42;padding:12px 14px calc(env(safe-area-inset-bottom,0px) + 12px);background:#0a0e14f0;backdrop-filter:blur(6px);border-top:1px solid var(--border);display:flex;flex-direction:column;gap:10px}.wd-place-hint{margin:0;color:#eef2f7;font-size:13px;font-weight:600;text-align:center}.wd-place-text{width:100%}.wd-place-buttons{display:flex;gap:8px}.wd-place-buttons .wd-btn{flex:1;justify-content:center}.wd-perm-row{display:flex;align-items:center;gap:8px;padding:8px 10px;margin-bottom:12px;border:1px solid var(--border);border-radius:10px;background:var(--panel-2)}.wd-perm-dot{width:9px;height:9px;border-radius:50%;background:var(--muted);flex:none}.wd-perm-dot[data-state=on]{background:var(--accent-2)}.wd-perm-dot[data-state=warn]{background:var(--warn)}.wd-perm-dot[data-state=off]{background:var(--error)}.wd-perm-text{flex:1;font-size:12px;color:var(--muted)}.wd-perm-enable{flex:none;min-height:32px;min-width:auto;padding:4px 12px;font-size:12px}.wd-dialog .wd-go{width:100%;justify-content:center}.wd-selector{position:absolute;z-index:19;border:2px solid var(--accent);background:#2f6fed24;border-radius:6px;box-shadow:0 0 0 9999px #06090e59;touch-action:none}.wd-selector-move{position:absolute;top:-1px;left:-1px;display:grid;place-items:center;width:38px;height:38px;border-radius:8px 0;background:var(--accent);color:#fff;cursor:move;touch-action:none}.wd-selector-handle{position:absolute;right:-12px;bottom:-12px;width:28px;height:28px;border-radius:50%;background:var(--accent);border:3px solid var(--panel);cursor:nwse-resize;touch-action:none}.wd-selector-bar{position:absolute;left:50%;bottom:-56px;transform:translate(-50%);display:flex;gap:8px;white-space:nowrap}.wd-selector-info{position:absolute;top:calc(env(safe-area-inset-top,0px) + 52px);left:12px;right:12px;z-index:20;margin:0 auto;max-width:430px;padding:8px 12px;border-radius:10px;background:var(--panel);border:1px solid var(--border);border-left:4px solid var(--accent);box-shadow:0 6px 20px var(--shadow);color:var(--text);font-size:12.5px;line-height:1.35;text-align:center;pointer-events:none}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./index.esm-C6Dwpirr.js","./index.esm2017-B0pGzpCg.js","./index.esm-DOt1fDqh.js","./index.esm-DYB-Ijb5.js","./index.esm-ES8aBvqn.js","./index.esm-B3nnhxdY.js"])))=>i.map(i=>d[i]);
|
|
2
|
-
(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))n(i);new MutationObserver(i=>{for(const s of i)if(s.type==="childList")for(const o of s.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&n(o)}).observe(document,{childList:!0,subtree:!0});function e(i){const s={};return i.integrity&&(s.integrity=i.integrity),i.referrerPolicy&&(s.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?s.credentials="include":i.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function n(i){if(i.ep)return;i.ep=!0;const s=e(i);fetch(i.href,s)}})();const Mt=new Uint32Array([1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298]);function At(a){return new TextEncoder().encode(a)}function Lt(a){const t=new Uint32Array([1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225]),e=a.length*8,n=new Uint8Array((a.length+8>>6)+1<<6);n.set(a),n[a.length]=128;const i=new DataView(n.buffer);i.setUint32(n.length-4,e>>>0,!1),i.setUint32(n.length-8,Math.floor(e/4294967296),!1);const s=new Uint32Array(64);for(let r=0;r<n.length;r+=64){for(let x=0;x<16;x++)s[x]=i.getUint32(r+x*4,!1);for(let x=16;x<64;x++){const b=s[x-15],T=s[x-2],d=(b>>>7|b<<25)^(b>>>18|b<<14)^b>>>3,u=(T>>>17|T<<15)^(T>>>19|T<<13)^T>>>10;s[x]=s[x-16]+d+s[x-7]+u>>>0}let[c,h,w,g,v,y,p,C]=t;for(let x=0;x<64;x++){const b=(v>>>6|v<<26)^(v>>>11|v<<21)^(v>>>25|v<<7),T=v&y^~v&p,d=C+b+T+Mt[x]+s[x]>>>0,u=(c>>>2|c<<30)^(c>>>13|c<<19)^(c>>>22|c<<10),A=c&h^c&w^h&w,E=u+A>>>0;C=p,p=y,y=v,v=g+d>>>0,g=w,w=h,h=c,c=d+E>>>0}t[0]=t[0]+c>>>0,t[1]=t[1]+h>>>0,t[2]=t[2]+w>>>0,t[3]=t[3]+g>>>0,t[4]=t[4]+v>>>0,t[5]=t[5]+y>>>0,t[6]=t[6]+p>>>0,t[7]=t[7]+C>>>0}const o=new Uint8Array(32);new DataView(o.buffer).setUint32(0,t[0],!1);for(let r=0;r<8;r++)new DataView(o.buffer).setUint32(r*4,t[r],!1);return o}const ct="0123456789abcdef";function Et(a){let t="";for(const e of a)t+=ct[e>>4]+ct[e&15];return t}function ot(a){return Et(Lt(At(a)))}function Rt(a,t,e){let n=ot(`${t}:${a}`);for(let i=1;i<e;i++)n=ot(n);return n}function Pt(a,t){return ot(`${a}:${t}`)}const nt=1;class gt{constructor(t){this.token=t}handlers={status:new Set,welcome:new Set,screenMeta:new Set,screenRegion:new Set,videoTrack:new Set,overviewTrack:new Set,transport:new Set,notification:new Set,presence:new Set,pinRequired:new Set,watchers:new Set,timers:new Set,monitors:new Set,monitorAlways:new Set,monitorSessions:new Set,netStats:new Set,versionMismatch:new Set,error:new Set};sender=()=>{};challenge=null;wrongPin=!1;rememberedPin=null;on(t,e){this.handlers[t].add(e)}emit(t,e){for(const n of this.handlers[t])n(e)}setSender(t){this.sender=t}send(t){try{this.sender(JSON.stringify(t))}catch{}}sendHello(){this.send({type:"hello",protocol:nt,token:this.token,role:"controller",client:{userAgent:navigator.userAgent}})}submitPin(t){if(this.rememberedPin=t,!this.challenge)return;const{salt:e,iterations:n,nonce:i}=this.challenge,s=Rt(t,e,n);this.send({type:"auth",response:Pt(s,i)})}setVisible(t){this.send({type:"visibility",visible:t})}handleText(t){let e;try{e=JSON.parse(t)}catch{return}switch(e.type){case"welcome":this.challenge=null,e.protocol!==nt&&this.emit("versionMismatch",{agentProtocol:e.protocol,clientProtocol:nt,agentVersion:e.agent?.version}),this.emit("welcome",e),this.emit("timers",e.timers??[]),this.emit("monitors",e.monitors??[]),this.emit("monitorAlways",e.alwaysAgents??[]);break;case"auth-required":this.challenge={salt:e.salt,iterations:e.iterations,nonce:e.nonce},this.rememberedPin&&!this.wrongPin?this.submitPin(this.rememberedPin):this.emit("pinRequired",{attemptsLeft:e.attemptsLeft,retry:this.wrongPin}),this.wrongPin=!1;break;case"screen-meta":this.emit("screenMeta",{screen:e.screen,activeDisplay:e.activeDisplay});break;case"screen-region":this.emit("screenRegion",{x:e.x,y:e.y,w:e.w,h:e.h,active:e.active});break;case"notification":this.emit("notification",e);break;case"presence":this.emit("presence",e.watchers);break;case"watchers":this.emit("watchers",e.regions);break;case"timers":this.emit("timers",e.timers);break;case"monitors":this.emit("monitors",e.monitors);break;case"monitor-always-agents":this.emit("monitorAlways",e.agents);break;case"monitor-sessions":this.emit("monitorSessions",e.sessions);break;case"error":e.code==="pin"?(this.wrongPin=!0,this.rememberedPin=null):this.emit("error",e.message);break}}}class Dt{constructor(t,e){this.url=t,this.core=new gt(e),this.core.setSender(n=>{if(this.dc&&this.dc.readyState==="open")try{this.dc.send(n)}catch{}})}core;ws=null;pc=null;dc=null;mainXcv=null;overviewXcv=null;reconnectTimer=0;statsTimer=0;closedByUser=!1;on(t,e){this.core.on(t,e)}send(t){this.core.send(t)}submitPin(t){this.core.submitPin(t)}setVisible(t){this.core.setVisible(t)}connect(){this.closedByUser=!1,this.open()}close(){this.closedByUser=!0,window.clearTimeout(this.reconnectTimer),this.teardown()}isHealthy(){return!this.closedByUser&&this.dc?.readyState==="open"&&this.pc?.connectionState==="connected"}wake(){this.closedByUser||this.isHealthy()||(window.clearTimeout(this.reconnectTimer),this.reconnectTimer=0,this.open())}teardown(){if(window.clearInterval(this.statsTimer),this.statsTimer=0,this.core.emit("videoTrack",null),this.core.emit("overviewTrack",null),this.dc){this.dc.onopen=this.dc.onclose=this.dc.onmessage=null;try{this.dc.close()}catch{}this.dc=null}if(this.pc){this.pc.onconnectionstatechange=null,this.pc.ontrack=null,this.pc.onicecandidate=null;try{this.pc.close()}catch{}this.pc=null}if(this.ws){this.ws.onopen=this.ws.onclose=this.ws.onmessage=this.ws.onerror=null;try{this.ws.close()}catch{}this.ws=null}this.mainXcv=null,this.overviewXcv=null}scheduleReconnect(){this.closedByUser||(window.clearTimeout(this.reconnectTimer),this.reconnectTimer=window.setTimeout(()=>this.open(),1500))}open(){this.core.emit("status","connecting"),this.teardown();const t=new WebSocket(this.url);this.ws=t;const e=o=>{if(t.readyState===WebSocket.OPEN)try{t.send(JSON.stringify(o))}catch{}},n=new RTCPeerConnection({iceServers:[]});this.pc=n;const i=n.createDataChannel("whipdesk");i.binaryType="arraybuffer",this.dc=i,i.onopen=()=>{this.core.sendHello(),this.core.emit("status","connected"),this.core.emit("transport","LAN"),this.startStatsPoll(n)},i.onclose=()=>this.core.emit("status","disconnected"),i.onmessage=o=>{typeof o.data=="string"&&this.core.handleText(o.data)};try{this.mainXcv=n.addTransceiver("video",{direction:"recvonly"}),this.overviewXcv=n.addTransceiver("video",{direction:"recvonly"})}catch{}n.ontrack=o=>{const r=o.streams[0]??new MediaStream([o.track]);this.overviewXcv&&o.transceiver===this.overviewXcv?this.core.emit("overviewTrack",r):this.core.emit("videoTrack",r)},n.onicecandidate=o=>{o.candidate&&e({kind:"candidate",candidate:o.candidate.toJSON()})},n.onconnectionstatechange=()=>{const o=n.connectionState;(o==="failed"||o==="disconnected"||o==="closed")&&(this.core.emit("status","disconnected"),this.scheduleReconnect())};let s=!1;t.onopen=async()=>{try{const o=await n.createOffer();await n.setLocalDescription(o),e({kind:"offer",sdp:n.localDescription?.sdp??""})}catch{}},t.onmessage=o=>{let r;try{r=JSON.parse(typeof o.data=="string"?o.data:"")}catch{return}if(r.kind==="answer"&&r.sdp&&!s)s=!0,n.setRemoteDescription({type:"answer",sdp:r.sdp});else if(r.kind==="candidate"&&r.candidate)try{n.addIceCandidate(r.candidate)}catch{}else r.kind==="error"&&this.core.emit("error",String(r.message??"host error"))},t.onclose=()=>{this.core.emit("status","disconnected"),this.scheduleReconnect()},t.onerror=()=>{}}startStatsPoll(t){window.clearInterval(this.statsTimer),this.statsTimer=window.setInterval(async()=>{let e=null,n=0;try{(await t.getStats()).forEach(s=>{s.type==="inbound-rtp"&&s.kind==="video"?n=Math.max(n,Number(s.framesPerSecond??0)):s.type==="candidate-pair"&&s.nominated&&typeof s.currentRoundTripTime=="number"&&(e=s.currentRoundTripTime)})}catch{return}this.core.emit("netStats",{fps:Math.round(n),rtt:e!=null?Math.round(e*1e3):null})},1e3)}}const vt=""+new URL("whip-CTqIatiK.png",import.meta.url).href,G=["Rounding up your AI agents…","Connecting to your agent farm…","Uncoiling the whip…","Negotiating the fastest route…","Tightening the leash…","Almost in the saddle…"];class Nt{overlay;msg;rotateTimer=0;msgIndex=0;constructor(t){this.overlay=document.createElement("div"),this.overlay.className="wd-connecting hidden";const e=document.createElement("div");e.className="wd-connecting-card";const n=document.createElement("img");n.className="wd-connecting-whip",n.src=vt,n.alt="WhipDesk",n.decoding="async";const i=document.createElement("div");i.className="wd-connecting-spinner",this.msg=document.createElement("p"),this.msg.className="wd-connecting-msg",this.msg.textContent=G[0],e.append(n,i,this.msg),this.overlay.appendChild(e),t.appendChild(this.overlay)}show(t){this.msg.textContent=t??G[this.msgIndex],this.overlay.classList.contains("hidden")&&(this.overlay.classList.remove("hidden"),!t&&(this.rotateTimer=window.setInterval(()=>{this.msgIndex=(this.msgIndex+1)%G.length,this.msg.textContent=G[this.msgIndex]},2200)))}hide(){this.overlay.classList.add("hidden"),window.clearInterval(this.rotateTimer),this.rotateTimer=0}}const It={eye:'<path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7Z"/><circle cx="12" cy="12" r="3"/>',mouse:'<rect x="6" y="3" width="12" height="18" rx="6"/><path d="M12 7v4"/>',keyboard:'<rect x="2" y="6" width="20" height="12" rx="2"/><path d="M6 10h.01M10 10h.01M14 10h.01M18 10h.01M7 14h10"/>',monitor:'<rect x="3" y="4" width="18" height="12" rx="2"/><path d="M8 20h8M12 16v4"/>',hand:'<path d="M7 11V6a1.5 1.5 0 0 1 3 0v4m0-1V4.5a1.5 1.5 0 0 1 3 0V10m0-1.5a1.5 1.5 0 0 1 3 0V12c0 4-2.5 8-6.5 8S6 17 5 15l-1.5-3a1.4 1.4 0 0 1 2.3-1.5L7 12"/>',bell:'<path d="M6 9a6 6 0 0 1 12 0c0 5 2 6 2 6H4s2-1 2-6Z"/><path d="M10 19a2 2 0 0 0 4 0"/>',plus:'<path d="M12 5v14M5 12h14"/>',minus:'<path d="M5 12h14"/>',"chevron-down":'<path d="m6 9 6 6 6-6"/>',"chevron-up":'<path d="m6 15 6-6 6 6"/>',pointer:'<path d="m4 4 6 16 2.5-6.5L19 11Z"/>',"scroll-up":'<path d="m6 14 6-6 6 6"/><path d="M12 8v11"/>',"scroll-down":'<path d="m6 10 6 6 6-6"/><path d="M12 16V5"/>',"mouse-left":'<rect x="6" y="3" width="12" height="18" rx="6"/><path d="M12 3v8H6V8"/>',"mouse-right":'<rect x="6" y="3" width="12" height="18" rx="6"/><path d="M12 3v8h6V8"/>',"double-click":'<path d="m4 4 6 16 2.5-6.5L19 11Z"/><path d="M18 4v3M21 6h-3"/>',drag:'<path d="M12 2v20M2 12h20" /><path d="m8 6 4-4 4 4M8 18l4 4 4-4M6 8l-4 4 4 4M18 8l4 4-4 4"/>',send:'<path d="M22 2 11 13M22 2l-7 20-4-9-9-4Z"/>',insert:'<path d="M12 5v14M5 12h7"/><rect x="16" y="4" width="4" height="16" rx="1"/>',lock:'<rect x="5" y="11" width="14" height="10" rx="2"/><path d="M8 11V7a4 4 0 0 1 8 0v4"/>',clock:'<circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/>',power:'<path d="M12 3v9"/><path d="M6.4 7.4a8 8 0 1 0 11.2 0"/>',activity:'<path d="M3 12h4l2-7 4 14 2-7h6"/>',x:'<path d="M6 6 18 18M18 6 6 18"/>',heart:'<path d="M12 20s-7-4.4-9.3-8.5a4.5 4.5 0 0 1 8.1-3.9l1.2 1.6 1.2-1.6a4.5 4.5 0 0 1 8.1 3.9C19 15.6 12 20 12 20Z"/>',trash:'<path d="M3 6h18"/><path d="M8 6V4h8v2"/><path d="M19 6l-1 14H6L5 6"/><path d="M10 11v6M14 11v6"/>',github:'<path fill="currentColor" stroke="none" d="M12 .5C5.37.5 0 5.87 0 12.5c0 5.3 3.44 9.8 8.21 11.39.6.11.82-.26.82-.58v-2.03c-3.34.73-4.04-1.61-4.04-1.61-.55-1.39-1.34-1.76-1.34-1.76-1.09-.74.08-.73.08-.73 1.2.09 1.84 1.24 1.84 1.24 1.07 1.83 2.81 1.3 3.5.99.11-.78.42-1.3.76-1.6-2.67-.3-5.47-1.33-5.47-5.93 0-1.31.47-2.38 1.24-3.22-.13-.3-.54-1.52.12-3.18 0 0 1.01-.32 3.3 1.23a11.5 11.5 0 0 1 6 0c2.29-1.55 3.3-1.23 3.3-1.23.66 1.66.25 2.88.12 3.18.77.84 1.23 1.91 1.23 3.22 0 4.61-2.81 5.62-5.49 5.92.43.37.81 1.1.81 2.22v3.29c0 .32.22.7.83.58A12 12 0 0 0 24 12.5C24 5.87 18.63.5 12 .5Z"/>',reddit:'<path fill="currentColor" stroke="none" d="M24 11.78a2.6 2.6 0 0 0-4.4-1.86 12.74 12.74 0 0 0-6.86-2.16l1.17-3.68 3.16.74a1.83 1.83 0 1 0 .2-1.18l-3.6-.85a.6.6 0 0 0-.72.43l-1.3 4.08a12.8 12.8 0 0 0-7 2.18 2.6 2.6 0 1 0-2.86 4.28 5.1 5.1 0 0 0-.06.79c0 4 4.66 7.24 10.42 7.24S20.42 17.83 20.42 12.95c0-.26-.02-.52-.06-.78A2.6 2.6 0 0 0 24 11.78ZM6.13 13.5a1.83 1.83 0 1 1 3.66 0 1.83 1.83 0 0 1-3.66 0Zm10.2 4.83c-1.25 1.25-3.64 1.34-4.33 1.34-.7 0-3.09-.09-4.33-1.34a.47.47 0 0 1 .67-.67c.79.79 2.47.99 3.66.99 1.2 0 2.88-.2 3.67-.99a.47.47 0 1 1 .66.67h.03Zm-.27-3a1.83 1.83 0 1 1 0-3.66 1.83 1.83 0 0 1 0 3.66Z"/>'},$t="http://www.w3.org/2000/svg";function P(a,t=20){const e=document.createElementNS($t,"svg");return e.setAttribute("viewBox","0 0 24 24"),e.setAttribute("width",String(t)),e.setAttribute("height",String(t)),e.setAttribute("fill","none"),e.setAttribute("stroke","currentColor"),e.setAttribute("stroke-width","2"),e.setAttribute("stroke-linecap","round"),e.setAttribute("stroke-linejoin","round"),e.setAttribute("aria-hidden","true"),e.innerHTML=It[a],e}const dt=["en"],Ot="en";function yt(){try{const t=localStorage.getItem("wd-locale");if(t&&dt.includes(t))return t}catch{}const a=navigator.languages?.length?navigator.languages:[navigator.language];for(const t of a){const e=(t||"").toLowerCase().split("-")[0]??"";if(dt.includes(e))return e}return Ot}function bt(){return location.hostname.endsWith("whipdesk.com")?"":"https://whipdesk.com"}function xt(){return`${bt()}/${yt()}/dashboard/`}function Vt(a){const t=a?`?next=${encodeURIComponent(a)}`:"";return`${bt()}/${yt()}/sign-in/${t}`}const Ct="https://donate.stripe.com/6oU5kE19N5v35652n88so01",_t="https://github.com/BinaryBananaLLC/WhipDesk/",Bt="https://www.reddit.com/r/WhipDesk/",Wt=[["Esc","Escape"],["Tab","Tab"],["⌫","Backspace"],["⏎","Enter"],["←","ArrowLeft"],["↑","ArrowUp"],["↓","ArrowDown"],["→","ArrowRight"]];function m(a,t,e){const n=document.createElement(a);return t&&(n.className=t),e!==void 0&&(n.textContent=e),n}function Y(a,...t){const e=m("div","wd-group");e.appendChild(m("span","wd-group-label",a));const n=m("div","wd-group-items");return n.append(...t),e.appendChild(n),e}function U(a,t){let e=0,n=0;const i=()=>{window.clearTimeout(e),window.clearInterval(n)};a.addEventListener("pointerdown",s=>{s.preventDefault(),t(),e=window.setTimeout(()=>{n=window.setInterval(t,80)},350)});for(const s of["pointerup","pointercancel","pointerleave"])a.addEventListener(s,i);return a}function H(a,t="wd-btn"){return m("button",t,a)}function D(a,t="",e="wd-btn"){const n=m("button",e);if(n.appendChild(P(a)),t){const i=m("span","wd-btn-label",t);n.appendChild(i)}else n.classList.add("wd-icon-only"),n.setAttribute("aria-label",a);return n}function lt(a,t,e){const n=m("a","wd-conn-feedback-link");return n.href=e,n.target="_blank",n.rel="noopener noreferrer",n.append(P(a,16),m("span",void 0,t)),n}function Ft(a){switch(a.toUpperCase()){case"LAN":return"Direct on your local network — fastest, no relay.";case"STUN":return"Direct peer-to-peer across networks.";case"TURN":return"Your network blocked a direct P2P connection, so we're bouncing your stream through our encrypted relay. Yes, this costs us actual money to run. Consider supporting us.";default:return""}}function ht(a){return a.toUpperCase()==="TURN"?`$ ${a}`:a}class Ht{constructor(t,e){this.root=t,this.deps=e,this.build()}statusDot=m("span","wd-dot");statusText=m("span","wd-status-text","Connecting…");transportBadge=m("span","wd-transport hidden");watchersText=m("span","wd-watchers","");alertBadge=m("span","wd-badge hidden");statusbar;statusCollapseTimer=0;connectionOverlay;connName;connRoute;connPresence;connSpeed;connStatusDot;connStatusText;connError;lastError="";netFps=0;netRtt=null;panel;optionsArea;tabButtons=new Map;tabPanes=new Map;collapseBtn;interactHost;monitorList;promptInput;activeTab=null;interactMode="mouse";collapsed=!1;deviceName="";transport="";presenceCount=1;status="connecting";displays=[];activeDisplay=0;setStatus(t){this.status=t,this.statusDot.dataset.status=t,t==="connected"&&(this.lastError=""),this.renderStatusText(),this.updateStatusCollapse(),this.connectionOverlay&&!this.connectionOverlay.classList.contains("hidden")&&this.renderConnection()}setAlertCount(t){this.alertBadge.textContent=t>0?String(t):"",this.alertBadge.classList.toggle("hidden",t<=0)}setTransport(t){this.transport=t,this.transportBadge.textContent=ht(t),this.transportBadge.dataset.kind=t.toLowerCase(),this.transportBadge.classList.toggle("hidden",!t),this.connectionOverlay&&!this.connectionOverlay.classList.contains("hidden")&&this.renderConnection(),this.peekStatus()}updateStatusCollapse(){window.clearTimeout(this.statusCollapseTimer),this.status==="connected"?this.scheduleStatusCollapse():this.statusbar?.classList.remove("collapsed")}scheduleStatusCollapse(){window.clearTimeout(this.statusCollapseTimer),this.statusCollapseTimer=window.setTimeout(()=>{this.status==="connected"&&this.statusbar?.classList.add("collapsed")},4e3)}peekStatus(){this.statusbar?.classList.remove("collapsed"),this.scheduleStatusCollapse()}renderStatusText(){this.statusText.textContent=this.status==="connected"?this.deviceName?`Connected to ${this.deviceName}`:"Connected":this.status==="connecting"?"Connecting…":"Disconnected"}setWelcome(t){this.deviceName=t.agent.hostname,this.renderStatusText(),this.displays=t.displays??[],this.activeDisplay=t.activeDisplay??0,this.renderMonitors(),t.capabilities.mouse||this.deps.notifications.show({type:"notification",id:`cap-${Date.now()}`,title:"View-only",body:"Host mouse/keyboard unavailable — grant Accessibility on the host.",level:"warning",source:"client",t:Date.now()})}setActiveDisplay(t){this.activeDisplay=t,this.renderMonitors()}setPresence(t){this.presenceCount=t,this.watchersText.textContent=t>1?`● ${t} watching`:"",this.connectionOverlay&&!this.connectionOverlay.classList.contains("hidden")&&this.renderConnection()}buildConnectionDialog(){const t=m("div","wd-dialog-overlay hidden");t.addEventListener("pointerdown",p=>{p.target===t&&t.classList.add("hidden")});const e=m("div","wd-dialog"),n=m("div","wd-dialog-head");n.append(m("h2","","Connection"));const i=m("button","wd-dialog-x");i.appendChild(P("x")),i.onclick=()=>t.classList.add("hidden"),n.appendChild(i);const s=m("div","wd-conn-row");s.append(m("span","wd-conn-label","Status"));const o=m("div","wd-conn-status");this.connStatusDot=m("span","wd-dot"),this.connStatusText=m("span","wd-conn-value","—"),o.append(this.connStatusDot,this.connStatusText),s.appendChild(o);const r=m("div","wd-conn-row");r.append(m("span","wd-conn-label","Machine")),this.connName=m("span","wd-conn-value","—"),r.appendChild(this.connName);const c=m("div","wd-conn-row");c.append(m("span","wd-conn-label","Connection")),this.connRoute=m("div","wd-conn-route"),c.appendChild(this.connRoute);const h=m("div","wd-conn-row");h.append(m("span","wd-conn-label","Viewers")),this.connPresence=m("span","wd-conn-value","1"),h.appendChild(this.connPresence);const w=m("div","wd-conn-row");w.append(m("span","wd-conn-label","Speed (FPS/latency)")),this.connSpeed=m("span","wd-conn-value","—"),w.appendChild(this.connSpeed),this.connError=m("div","wd-conn-error hidden");const g=m("button","wd-btn wd-disconnect");g.append(P("power"),m("span","wd-btn-label","Disconnect")),g.onclick=()=>this.disconnect();const v=m("div","wd-conn-feedback");v.append(m("p","wd-conn-feedback-text","Noticed an issue or have an idea? Reach out:"));const y=m("div","wd-conn-feedback-links");y.append(lt("reddit","Reddit",Bt),lt("github","GitHub",_t)),v.appendChild(y),e.append(n,s,c,w,r,h,this.connError,g,v),t.appendChild(e),this.root.appendChild(t),this.connectionOverlay=t}renderConnection(){if(this.connStatusDot.dataset.status=this.status,this.connStatusText.textContent=this.status==="connected"?"Connected":this.status==="connecting"?"Connecting…":"Disconnected",this.lastError?(this.connError.textContent=this.lastError,this.connError.classList.remove("hidden")):this.connError.classList.add("hidden"),this.connName.textContent=this.deviceName||"Connected device",this.connPresence.textContent=String(Math.max(1,this.presenceCount)),this.connRoute.replaceChildren(),this.transport){const t=m("span","wd-transport");if(t.textContent=ht(this.transport),t.dataset.kind=this.transport.toLowerCase(),this.connRoute.append(t,m("span","wd-conn-desc",Ft(this.transport))),this.transport.toLowerCase()==="turn"){const e=m("button","wd-support-link");e.append(P("heart",14),m("span",void 0,"Support WhipDesk")),e.onclick=()=>window.open(Ct,"_blank","noopener"),this.connRoute.append(e)}}else this.connRoute.append(m("span","wd-conn-desc",this.status==="connected"?"Detecting route…":"Connecting…"));this.renderSpeed()}renderSpeed(){const t=`${this.netFps} FPS`;this.connSpeed.textContent=this.netRtt!=null?`${t} / ${this.netRtt} ms`:t}setNetStats(t,e){this.netFps=t,this.netRtt=e,this.connectionOverlay&&!this.connectionOverlay.classList.contains("hidden")&&this.renderSpeed()}openConnection(){this.renderConnection(),this.connectionOverlay.classList.remove("hidden")}disconnect(){this.deps.conn.close(),window.location.href=xt()}flashError(t){this.lastError=t,this.connectionOverlay&&!this.connectionOverlay.classList.contains("hidden")&&this.renderConnection(),this.deps.notifications.show({type:"notification",id:`err-${Date.now()}`,title:"WhipDesk",body:t,level:"warning",source:"client",t:Date.now()})}build(){const t=m("div","wd-statusbar");t.append(this.statusDot,this.statusText,this.transportBadge,this.watchersText),t.onclick=()=>this.openConnection(),this.statusbar=t,this.buildConnectionDialog();const e=D("bell","","wd-bell");e.setAttribute("aria-label","Auto-Whips"),e.title="Auto-Whips",e.appendChild(this.alertBadge),e.onclick=()=>this.deps.watchers.open(),this.root.append(t,e);const n=m("div","wd-ribbon");this.panel=m("div","wd-panel"),this.optionsArea=m("div","wd-options"),this.tabPanes.set("viewer",this.buildViewerPane()),this.tabPanes.set("interact",this.buildInteractPane()),this.tabPanes.set("type",this.buildTypePane()),this.tabPanes.set("monitor",this.buildMonitorPane());for(const o of this.tabPanes.values())this.optionsArea.appendChild(o);const i=m("div","wd-tabs"),s=(o,r,c)=>{const h=m("button","wd-tab");h.appendChild(P(r,18)),h.appendChild(m("span","wd-tab-label",c)),h.onclick=()=>this.selectTab(o),this.tabButtons.set(o,h),i.appendChild(h)};s("viewer","eye","Viewer"),s("interact","mouse","Interact"),s("type","keyboard","Type"),s("monitor","monitor","Monitor"),this.collapseBtn=D("chevron-down","","wd-collapse"),this.collapseBtn.onclick=()=>this.setCollapsed(!this.collapsed),i.appendChild(this.collapseBtn),this.panel.append(this.optionsArea,i),n.appendChild(this.panel),this.root.appendChild(n),this.selectTab("viewer")}buildViewerPane(){const{view:t,input:e}=this.deps,n=m("div","wd-pane wd-pane-single-row"),i=U(D("minus","","wd-btn wd-icon-only"),()=>t.zoomBy(.9)),s=U(D("plus","","wd-btn wd-icon-only"),()=>t.zoomBy(1.11)),o=U(D("scroll-up","","wd-btn wd-icon-only"),()=>e.scrollStep(-6)),r=U(D("scroll-down","","wd-btn wd-icon-only"),()=>e.scrollStep(6)),c=D("hand","","wd-btn wd-icon-only");c.setAttribute("aria-label","Drag to scroll"),c.title="Drag to scroll";const h=D("drag","","wd-btn wd-icon-only");h.setAttribute("aria-label","Pan the zoomed screen with one finger"),h.title="Pan the zoomed screen with one finger",c.onclick=()=>{const g=!e.getDragScroll();e.setDragScroll(g),c.classList.toggle("on",g),h.classList.toggle("on",e.getPan())},h.onclick=()=>{const g=!e.getPan();e.setPan(g),h.classList.toggle("on",g),c.classList.toggle("on",e.getDragScroll())};const w=D("pointer","Click","wd-btn wd-go");return w.onclick=()=>e.click("left"),n.append(Y("Zoom",i,s),Y("Pan",h),Y("Scroll",o,r,c),Y("Pointer",w)),n}buildInteractPane(){const t=m("div","wd-pane");return this.interactHost=m("div","wd-pane"),t.appendChild(this.interactHost),this.renderInteract(),t}renderInteract(){const{input:t}=this.deps;this.interactHost.replaceChildren();const e=m("div","wd-group"),n=m("div","wd-group-head"),i=m("span","wd-group-label","Mode"),s=m("div","wd-mode-toggle"),o=m("button","wd-mode-btn","Mouse"),r=m("button","wd-mode-btn","Touch");o.classList.toggle("on",this.interactMode==="mouse"),r.classList.toggle("on",this.interactMode==="touch"),o.onclick=()=>{this.interactMode="mouse",this.activeTab==="interact"&&this.deps.input.setInteraction("mouse"),this.renderInteract()},r.onclick=()=>{this.interactMode="touch",this.activeTab==="interact"&&this.deps.input.setInteraction("touch"),this.renderInteract()},s.append(o,r),n.append(i,s);const c=m("div","wd-group-items");if(this.interactMode==="mouse"){const h=D("mouse-left","Left");h.onclick=()=>t.click("left");const w=D("mouse-right","Right");w.onclick=()=>t.click("right");const g=D("double-click","Double");g.onclick=()=>t.multiClick(2);const v=D("drag","Drag");v.onclick=()=>{const y=!t.getDragLock();t.setDragLock(y),v.classList.toggle("on",y)},c.append(h,w,g,v)}else{const h=D("pointer","Tap");h.onclick=()=>t.click("left");const w=D("hand","Hold");w.onclick=()=>t.longPress();const g=U(D("scroll-up","Up","wd-btn"),()=>t.swipe(0,-.25)),v=U(D("scroll-down","Down","wd-btn"),()=>t.swipe(0,.25)),y=H("←");y.onclick=()=>t.swipe(-.25,0);const p=H("→");p.onclick=()=>t.swipe(.25,0);const C=H("2 fingers");C.onclick=()=>t.click("right"),c.append(h,w,g,v,y,p,C)}e.append(n,c),this.interactHost.append(e)}buildTypePane(){const{conn:t,input:e}=this.deps,n=m("div","wd-pane wd-pane-col");this.promptInput=m("textarea","wd-type-input"),this.promptInput.placeholder="Type to send to the focused app (URL, command, message…)",this.promptInput.rows=2;const i=m("div","wd-wrap");for(const[h,w]of Wt){const g=H(h);g.onclick=()=>t.send({type:"key",key:w}),i.appendChild(g)}const s=H("Double-click");s.onclick=()=>e.multiClick(2);const o=H("Triple-click");o.onclick=()=>e.multiClick(3);const r=D("insert","Insert");r.onclick=()=>this.sendText(!1);const c=D("send","Send","wd-btn wd-go");return c.onclick=()=>this.sendText(!0),i.append(s,o,r,c),n.append(this.promptInput,i),n}buildMonitorPane(){const t=m("div","wd-pane");return this.monitorList=m("div","wd-monitor-list"),t.appendChild(this.monitorList),this.renderMonitors(),t}renderMonitors(){if(this.monitorList){if(this.monitorList.replaceChildren(),this.displays.length===0){this.monitorList.appendChild(m("span","wd-hint","Single display"));return}this.displays.forEach((t,e)=>{const n=`${e+1}. ${t.name}${t.primary?" ★":""}`,i=H(n);i.classList.toggle("on",t.id===this.activeDisplay),i.onclick=()=>{this.deps.conn.send({type:"select-display",id:t.id}),this.activeDisplay=t.id,this.renderMonitors()},this.monitorList.appendChild(i)})}}selectTab(t){if(this.activeTab!==null&&t===this.activeTab){this.setCollapsed(!this.collapsed);return}this.activeTab=t,this.collapsed&&this.setCollapsed(!1);for(const[e,n]of this.tabButtons)n.classList.toggle("on",e===t);for(const[e,n]of this.tabPanes)n.classList.toggle("hidden",e!==t);this.deps.input.setInteraction(t==="interact"?this.interactMode:"viewer"),t==="type"&&window.setTimeout(()=>this.promptInput.focus(),50)}setCollapsed(t){this.collapsed=t,this.optionsArea.classList.toggle("hidden",t),this.collapseBtn.replaceChildren(P(t?"chevron-up":"chevron-down"))}sendText(t){const e=this.promptInput.value;e&&(this.deps.conn.send({type:"type",text:e,submit:t}),this.promptInput.value="")}}function z(a){return a<0?0:a>1?1:a}const j=2.5,pt=250,Ut=500,zt=8;class qt{constructor(t,e,n,i={}){this.canvas=t,this.view=e,this.conn=n,this.cb=i,t.style.touchAction="none",t.addEventListener("pointerdown",s=>this.onDown(s)),t.addEventListener("pointermove",s=>this.onMove(s)),t.addEventListener("pointerup",s=>this.onUp(s)),t.addEventListener("pointercancel",s=>this.onUp(s)),t.addEventListener("contextmenu",s=>s.preventDefault()),this.view.setCursor(this.cursor.nx,this.cursor.ny)}interaction="viewer";dragLock=!1;dragScroll=!1;pan=!1;holdingLeft=!1;cursor={nx:.5,ny:.5};pointers=new Map;longPressTimer=0;twoFinger=null;suppressTap=!1;setInteraction(t){this.interaction=t,t==="viewer"&&(this.dragLock=!1)}getInteraction(){return this.interaction}setCallbacks(t){this.cb=t}setDragLock(t){this.dragLock=t}getDragLock(){return this.dragLock}setDragScroll(t){this.dragScroll=t,t&&(this.pan=!1)}getDragScroll(){return this.dragScroll}setPan(t){this.pan=t,t&&(this.dragScroll=!1)}getPan(){return this.pan}isPanning(){return this.pan&&this.interaction==="viewer"}click(t,e=!1){this.send({type:"pointer",action:"click",button:t,double:e,x:this.cursor.nx,y:this.cursor.ny}),navigator.vibrate?.(15)}multiClick(t){for(let e=0;e<t;e++)this.send({type:"pointer",action:"click",button:"left",x:this.cursor.nx,y:this.cursor.ny});navigator.vibrate?.(15)}longPress(t=650){this.send({type:"pointer",action:"down",button:"left",x:this.cursor.nx,y:this.cursor.ny}),navigator.vibrate?.(25),window.setTimeout(()=>this.send({type:"pointer",action:"up",button:"left"}),t)}swipe(t,e){const n=z(this.cursor.nx),i=z(this.cursor.ny),s=z(this.cursor.nx+t),o=z(this.cursor.ny+e);this.send({type:"pointer",action:"down",button:"left",x:n,y:i});const r=6;for(let c=1;c<=r;c++){const h=c/r;window.setTimeout(()=>{this.send({type:"pointer",action:"move",x:n+(s-n)*h,y:i+(o-i)*h}),c===r&&(this.moveCursor(s,o),this.send({type:"pointer",action:"up",button:"left"}))},c*16)}navigator.vibrate?.(15)}scrollStep(t){this.send({type:"scroll",dx:0,dy:t})}send(t){this.conn.send(t)}positionOf(t){const e=this.canvas.getBoundingClientRect();return{x:t.clientX-e.left,y:t.clientY-e.top}}moveCursor(t,e){this.cursor.nx=z(t),this.cursor.ny=z(e),this.view.setCursor(this.cursor.nx,this.cursor.ny),this.cb.onCursor?.(this.cursor.nx,this.cursor.ny)}onDown(t){this.canvas.setPointerCapture?.(t.pointerId);const e=this.positionOf(t);if(this.pointers.set(t.pointerId,{x:e.x,y:e.y,startX:e.x,startY:e.y,startT:performance.now(),moved:!1,consumed:!1}),this.pointers.size===2){window.clearTimeout(this.longPressTimer),this.beginTwoFinger();return}if(this.pointers.size===1&&(this.view.beginViewGesture(),window.clearTimeout(this.longPressTimer),this.interaction==="mouse"&&!this.dragScroll&&(this.longPressTimer=window.setTimeout(()=>this.onLongPress(),Ut)),!this.dragScroll&&!this.isPanning()&&(this.interaction==="mouse"||this.interaction==="viewer"))){const n=this.view.canvasToNorm(e.x,e.y);this.moveCursor(n.nx,n.ny)}}onLongPress(){const t=[...this.pointers.values()][0];!t||t.moved||t.consumed||(t.consumed=!0,this.send({type:"pointer",action:"click",button:"right",x:this.cursor.nx,y:this.cursor.ny}),navigator.vibrate?.(20))}beginTwoFinger(){const t=[...this.pointers.values()];if(t.length<2)return;const e=t[0],n=t[1];this.twoFinger={dist:Math.hypot(e.x-n.x,e.y-n.y),mx:(e.x+n.x)/2,my:(e.y+n.y)/2,start:performance.now(),moved:!1}}onMove(t){const e=this.pointers.get(t.pointerId);if(!e)return;const n=this.positionOf(t),i=e.x,s=e.y;if(e.x=n.x,e.y=n.y,Math.hypot(n.x-e.startX,n.y-e.startY)>zt&&(e.moved=!0),this.pointers.size>=2&&this.twoFinger){this.onTwoFingerMove();return}if(this.pointers.size!==1||e.consumed||!e.moved)return;window.clearTimeout(this.longPressTimer);const o=n.x-i,r=n.y-s;if(this.isPanning()){this.view.panByCanvasPixels(o,r);return}if(this.interaction==="touch"||this.dragScroll){this.send({type:"scroll",dx:Math.round(-o/j),dy:Math.round(-r/j)});return}const c=this.view.canvasToNorm(n.x,n.y);this.moveCursor(c.nx,c.ny),this.interaction==="mouse"&&this.dragLock&&!this.holdingLeft&&(this.holdingLeft=!0,this.send({type:"pointer",action:"down",button:"left",x:this.cursor.nx,y:this.cursor.ny})),this.send({type:"pointer",action:"move",x:this.cursor.nx,y:this.cursor.ny})}onTwoFingerMove(){const t=[...this.pointers.values()];if(t.length<2||!this.twoFinger)return;const e=t[0],n=t[1],i=Math.hypot(e.x-n.x,e.y-n.y),s=(e.x+n.x)/2,o=(e.y+n.y)/2,r=i-this.twoFinger.dist,c=s-this.twoFinger.mx,h=o-this.twoFinger.my;if(Math.abs(r)>6){this.twoFinger.moved=!0,this.view.zoomAround(1+r/200,s,o),this.twoFinger.dist=i;return}(Math.abs(h)>2||Math.abs(c)>2)&&(this.twoFinger.moved=!0,this.view.getZoom()>1?this.view.panByCanvasPixels(c,h):this.send({type:"scroll",dx:Math.round(-c/j),dy:Math.round(-h/j)}),this.twoFinger.mx=s,this.twoFinger.my=o)}onUp(t){const e=this.pointers.size,n=this.pointers.get(t.pointerId);if(this.pointers.delete(t.pointerId),window.clearTimeout(this.longPressTimer),this.pointers.size===0&&this.view.endViewGesture(),e===2){const o=this.twoFinger;o&&!o.moved&&this.interaction==="mouse"&&performance.now()-o.start<pt&&(this.send({type:"pointer",action:"click",button:"right",x:this.cursor.nx,y:this.cursor.ny}),navigator.vibrate?.(20)),this.twoFinger=null,this.suppressTap=!0;for(const r of this.pointers.values())r.consumed=!0;return}if(this.holdingLeft&&this.pointers.size===0&&(this.holdingLeft=!1,this.send({type:"pointer",action:"up",button:"left"})),this.pointers.size<2&&(this.twoFinger=null),!n||n.consumed)return;if(this.suppressTap){this.pointers.size===0&&(this.suppressTap=!1);return}const i=performance.now()-n.startT;if(!(!(!n.moved&&i<pt&&this.pointers.size===0)||this.dragScroll)&&(this.interaction==="mouse"||this.interaction==="touch")){const o=this.view.canvasToNorm(n.startX,n.startY);this.moveCursor(o.nx,o.ny),this.send({type:"pointer",action:"click",button:"left",x:o.nx,y:o.ny}),navigator.vibrate?.(12)}}}class Xt{constructor(t){this.container=t}async requestPermission(){try{"Notification"in window&&Notification.permission==="default"&&await Notification.requestPermission()}catch{}}get permission(){return"Notification"in window?Notification.permission:"unsupported"}flash(t,e,n="info"){this.toast({type:"notification",id:`flash-${Date.now()}`,title:t,body:e,level:n,source:"client",t:Date.now()})}show(t){this.toast(t);try{"Notification"in window&&Notification.permission==="granted"&&new Notification(t.title,{body:t.body,tag:t.id})}catch{}navigator.vibrate?.(t.level==="error"?[60,40,60]:40)}toast(t){const e=document.createElement("div");e.className=`wd-toast wd-${t.level}`;const n=document.createElement("strong");if(n.textContent=t.title,e.appendChild(n),t.body){const i=document.createElement("span");i.textContent=t.body,e.appendChild(i)}this.container.appendChild(e),window.setTimeout(()=>{e.classList.add("wd-hide"),window.setTimeout(()=>e.remove(),300)},5e3)}}class Zt{overlay;input;message;onSubmit=null;constructor(t){this.overlay=document.createElement("div"),this.overlay.className="wd-pin-overlay hidden";const e=document.createElement("div");e.className="wd-pin-card";const n=document.createElement("img");n.className="wd-pin-whip",n.src=vt,n.alt="WhipDesk",n.decoding="async";const i=document.createElement("h2");i.textContent="Enter device PIN",this.message=document.createElement("p"),this.message.className="wd-pin-msg",this.message.textContent="You're connected. Enter the PIN to unlock this device and start whipping.",this.input=document.createElement("input"),this.input.className="wd-pin-input",this.input.type="password",this.input.inputMode="numeric",this.input.autocomplete="one-time-code",this.input.name="wd-device-pin",this.input.setAttribute("data-1p-ignore","true"),this.input.setAttribute("data-lpignore","true"),this.input.setAttribute("aria-label","Device PIN"),this.input.addEventListener("keydown",h=>{h.key==="Enter"&&this.submit()});const s=document.createElement("button");s.className="wd-pin-submit",s.textContent="Unlock",s.onclick=()=>this.submit();const o=document.createElement("button");o.type="button",o.className="wd-support-link wd-pin-support",o.append(P("heart",14));const r=document.createElement("span");r.textContent="Support WhipDesk",o.append(r),o.onclick=()=>window.open(Ct,"_blank","noopener");const c=document.createElement("button");c.className="wd-pin-back",c.textContent="← Back to dashboard",c.onclick=()=>{window.location.href=xt()},e.append(n,i,this.message,this.input,s,o,c),this.overlay.appendChild(e),t.appendChild(this.overlay)}show(t,e){this.onSubmit=e,this.input.value="",t.retry?(this.message.textContent=t.attemptsLeft>0?`Wrong PIN — ${t.attemptsLeft} attempt(s) left.`:"Wrong PIN. Try again.",this.message.classList.add("err"),navigator.vibrate?.([40,40,40])):(this.message.textContent="You're connected. Enter the PIN to unlock this device and start whipping.",this.message.classList.remove("err")),this.overlay.classList.remove("hidden"),window.setTimeout(()=>this.input.focus(),50)}hide(){this.overlay.classList.add("hidden")}submit(){const t=this.input.value.trim();if(t.length<4){this.message.textContent="PIN is at least 4 characters.",this.message.classList.add("err");return}this.onSubmit?.(t),this.message.textContent="Checking…",this.message.classList.remove("err")}}const Kt="modulepreload",Gt=function(a,t){return new URL(a,t).href},ut={},W=function(t,e,n){let i=Promise.resolve();if(e&&e.length>0){const o=document.getElementsByTagName("link"),r=document.querySelector("meta[property=csp-nonce]"),c=r?.nonce||r?.getAttribute("nonce");i=Promise.allSettled(e.map(h=>{if(h=Gt(h,n),h in ut)return;ut[h]=!0;const w=h.endsWith(".css"),g=w?'[rel="stylesheet"]':"";if(!!n)for(let p=o.length-1;p>=0;p--){const C=o[p];if(C.href===h&&(!w||C.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${h}"]${g}`))return;const y=document.createElement("link");if(y.rel=w?"stylesheet":Kt,w||(y.as="script"),y.crossOrigin="",y.href=h,c&&y.setAttribute("nonce",c),document.head.appendChild(y),w)return new Promise((p,C)=>{y.addEventListener("load",p),y.addEventListener("error",()=>C(new Error(`Unable to preload CSS for ${h}`)))})}))}function s(o){const r=new Event("vite:preloadError",{cancelable:!0});if(r.payload=o,window.dispatchEvent(r),!r.defaultPrevented)throw o}return i.then(o=>{for(const r of o||[])r.status==="rejected"&&s(r.reason);return t().catch(s)})};async function mt(a,t){if(a.vapidKey&&!(!("serviceWorker"in navigator)||!("Notification"in window)))try{const{initializeApp:e,getApps:n}=await W(async()=>{const{initializeApp:d,getApps:u}=await import("./index.esm-C6Dwpirr.js");return{initializeApp:d,getApps:u}},__vite__mapDeps([0,1]),import.meta.url),{getAuth:i}=await W(async()=>{const{getAuth:d}=await import("./index.esm-DOt1fDqh.js");return{getAuth:d}},__vite__mapDeps([2,1]),import.meta.url),{getFirestore:s,doc:o,setDoc:r,serverTimestamp:c}=await W(async()=>{const{getFirestore:d,doc:u,setDoc:A,serverTimestamp:E}=await import("./index.esm-DYB-Ijb5.js");return{getFirestore:d,doc:u,setDoc:A,serverTimestamp:E}},__vite__mapDeps([3,1]),import.meta.url),{getMessaging:h,getToken:w,onMessage:g,isSupported:v}=await W(async()=>{const{getMessaging:d,getToken:u,onMessage:A,isSupported:E}=await import("./index.esm-ES8aBvqn.js");return{getMessaging:d,getToken:u,onMessage:A,isSupported:E}},__vite__mapDeps([4,1]),import.meta.url);if(!await v().catch(()=>!1))return;const y=n()[0]??e(a),p=i(y).currentUser;if(!p||Notification.permission==="default"&&await Notification.requestPermission()!=="granted"||Notification.permission!=="granted")return;const C=await navigator.serviceWorker.register("./firebase-messaging-sw.js"),x=h(y),b=await w(x,{vapidKey:a.vapidKey,serviceWorkerRegistration:C});if(!b)return;const T=s(y);await r(o(T,"users",p.uid,"fcmTokens",b),{token:b,ua:navigator.userAgent,updatedAt:c()},{merge:!0}),g(x,d=>{const u=d.notification;t.show({type:"notification",id:`push-${Date.now()}`,title:u?.title??"WhipDesk",body:u?.body,level:"info",source:"push",t:Date.now()})})}catch{}}const Yt=[{urls:"stun:turn-us1.whipdesk.com:3478"}];async function jt(a){try{const t=await a.getStats();let e="",n="";t.forEach(s=>{s.type==="transport"&&s.selectedCandidatePairId&&(e=s.selectedCandidatePairId)}),t.forEach(s=>{s.type==="candidate-pair"&&!n&&(s.id===e||s.nominated&&s.state==="succeeded")&&(n=s.localCandidateId)});let i="";if(t.forEach(s=>{s.id===n&&s.type==="local-candidate"&&(i=s.candidateType)}),i==="relay")return"TURN";if(i==="srflx"||i==="prflx")return"STUN";if(i==="host")return"LAN"}catch{}return null}class Jt{constructor(t,e,n){this.deviceId=t,this.config=n,this.core=new gt(e),this.core.setSender(i=>{if(this.dc&&this.dc.readyState==="open")try{this.dc.send(i)}catch{}})}core;pc=null;dc=null;mainXcv=null;overviewXcv=null;cleanupSignal=null;deleteSessionDoc=null;pushCandidate=null;appliedAnswer=!1;appliedCandidates=new Set;closed=!1;reconnectTimer=0;statsTimer=0;on(t,e){this.core.on(t,e)}send(t){this.core.send(t)}submitPin(t){this.core.submitPin(t)}setVisible(t){this.core.setVisible(t)}connect(){this.closed=!1,this.start()}close(){this.closed=!0,window.clearTimeout(this.reconnectTimer),this.reconnectTimer=0,this.teardown()}isHealthy(){return!this.closed&&this.pc?.connectionState==="connected"&&this.dc?.readyState==="open"}wake(){this.closed||this.isHealthy()||(window.clearTimeout(this.reconnectTimer),this.reconnectTimer=0,this.teardown(),this.start())}start(){this.open().catch(t=>{this.core.emit("error",`Remote connect failed: ${t.message}`),this.core.emit("status","disconnected"),this.scheduleReconnect()})}teardown(){if(window.clearInterval(this.statsTimer),this.statsTimer=0,this.cleanupSignal?.(),this.cleanupSignal=null,this.deleteSessionDoc?.(),this.deleteSessionDoc=null,this.pushCandidate=null,this.appliedAnswer=!1,this.appliedCandidates.clear(),this.core.emit("videoTrack",null),this.core.emit("overviewTrack",null),this.dc){this.dc.onopen=this.dc.onclose=this.dc.onmessage=null;try{this.dc.close()}catch{}this.dc=null}if(this.pc){this.pc.onconnectionstatechange=null,this.pc.ontrack=null;try{this.pc.close()}catch{}this.pc=null}this.mainXcv=null,this.overviewXcv=null}scheduleReconnect(){this.closed||this.reconnectTimer||(this.reconnectTimer=window.setTimeout(()=>{this.reconnectTimer=0,!this.closed&&(this.teardown(),this.start())},2e3))}async open(){this.core.emit("status","connecting");const{initializeApp:t,getApps:e}=await W(async()=>{const{initializeApp:S,getApps:k}=await import("./index.esm-C6Dwpirr.js");return{initializeApp:S,getApps:k}},__vite__mapDeps([0,1]),import.meta.url),{getAuth:n}=await W(async()=>{const{getAuth:S}=await import("./index.esm-DOt1fDqh.js");return{getAuth:S}},__vite__mapDeps([2,1]),import.meta.url),{getDatabase:i,ref:s,child:o,push:r,set:c,onValue:h,remove:w,onDisconnect:g}=await W(async()=>{const{getDatabase:S,ref:k,child:f,push:M,set:L,onValue:R,remove:V,onDisconnect:$}=await import("./index.esm-B3nnhxdY.js");return{getDatabase:S,ref:k,child:f,push:M,set:L,onValue:R,remove:V,onDisconnect:$}},__vite__mapDeps([5,1]),import.meta.url),v=e()[0]??t(this.config),y=n(v);if(await new Promise(S=>{if(y.currentUser)return S();const k=y.onAuthStateChanged(()=>{k(),S()})}),!y.currentUser){window.location.assign(Vt(`/app/${window.location.hash}`));return}const p=y.currentUser.uid,C=i(v),x=await this.fetchIceServers(y.currentUser),b=new RTCPeerConnection({iceServers:x,iceTransportPolicy:"all"});this.pc=b;const T=b.createDataChannel("whipdesk");T.binaryType="arraybuffer",this.dc=T,this.wireDataChannel(T);try{this.mainXcv=b.addTransceiver("video",{direction:"recvonly"}),this.overviewXcv=b.addTransceiver("video",{direction:"recvonly"})}catch{}b.ontrack=S=>{const k=S.streams[0]??new MediaStream([S.track]);this.overviewXcv&&S.transceiver===this.overviewXcv?this.core.emit("overviewTrack",k):this.core.emit("videoTrack",k)};let d=!1;const u=async()=>{if(d)return;const S=await jt(b);if(!S)return;d=!0,this.core.emit("transport",S);const k=S==="TURN"?"turn":S==="STUN"?"stun":"lan";try{const{getFirestore:f,doc:M,setDoc:L,increment:R}=await W(async()=>{const{getFirestore:V,doc:$,setDoc:O,increment:_}=await import("./index.esm-DYB-Ijb5.js");return{getFirestore:V,doc:$,setDoc:O,increment:_}},__vite__mapDeps([3,1]),import.meta.url);L(M(f(v),"users",p),{stats:{[k]:R(1)}},{merge:!0}).catch(()=>{})}catch{}};b.onconnectionstatechange=()=>{const S=b.connectionState;S==="connected"?(this.core.emit("status","connected"),u(),this.startStatsPoll(b)):(S==="failed"||S==="disconnected"||S==="closed")&&(this.core.emit("status","disconnected"),this.scheduleReconnect())};const A=r(s(C,`signaling/${p}/${this.deviceId}`));this.pushCandidate=S=>{try{c(r(o(A,"offerCandidates")),S)}catch{}},b.onicecandidate=S=>{S.candidate&&this.pushCandidate?.(S.candidate.toJSON())};const E=await b.createOffer();await b.setLocalDescription(E),await c(A,{offer:{sdp:b.localDescription?.sdp??""},controllerUid:p,createdAtMs:Date.now()}),g(A).remove(),this.deleteSessionDoc=()=>void w(A).catch(()=>{}),this.cleanupSignal=h(A,S=>{const k=S.val();if(!(!k||this.closed)&&(k.answer?.sdp&&!this.appliedAnswer&&(this.appliedAnswer=!0,b.setRemoteDescription({type:"answer",sdp:k.answer.sdp})),k.answerCandidates&&this.appliedAnswer)){for(const[f,M]of Object.entries(k.answerCandidates))if(!this.appliedCandidates.has(f)){this.appliedCandidates.add(f);try{b.addIceCandidate(M)}catch{}}}})}startStatsPoll(t){window.clearInterval(this.statsTimer),this.statsTimer=window.setInterval(()=>void this.pollStats(t),1e3)}async pollStats(t){let e=null,n=0;try{(await t.getStats()).forEach(s=>{s.type==="inbound-rtp"&&s.kind==="video"?n=Math.max(n,Number(s.framesPerSecond??0)):s.type==="candidate-pair"&&s.nominated&&typeof s.currentRoundTripTime=="number"&&(e=s.currentRoundTripTime)})}catch{return}this.core.emit("netStats",{fps:Math.round(n),rtt:e!=null?Math.round(e*1e3):null})}wireDataChannel(t){t.onopen=()=>this.core.sendHello(),t.onclose=()=>{this.core.emit("status","disconnected"),this.scheduleReconnect()},t.onmessage=e=>{typeof e.data=="string"&&this.core.handleText(e.data)}}async fetchIceServers(t){const e=[...Yt];try{const n=sessionStorage.getItem("wd-ice");if(n){const{servers:i,exp:s}=JSON.parse(n);if(Array.isArray(i)&&i.length&&s>Date.now())return i}}catch{}try{const n=this.config.iceUrl||`https://us-central1-${this.config.projectId}.cloudfunctions.net/iceServers`,i=await t.getIdToken(),s=await fetch(n,{headers:{authorization:`Bearer ${i}`}});if(s.ok){const o=await s.json();if(Array.isArray(o.iceServers)&&o.iceServers.length){try{sessionStorage.setItem("wd-ice",JSON.stringify({servers:o.iceServers,exp:Date.now()+8*6e4}))}catch{}return o.iceServers}}}catch{}return e}}function N(a,t,e){return a<t?t:a>e?e:a}const Qt=480,te=600,q=-.6,X=1.6,ee=1500,ne=120,ie=3e3;function Q(a){return!a||a.x<=.001&&a.y<=.001&&a.w>=.999&&a.h>=.999}function it(a,t){if(Q(a)&&Q(t))return!0;if(!a||!t)return!1;const e=.005;return Math.abs(a.x-t.x)<e&&Math.abs(a.y-t.y)<e&&Math.abs(a.w-t.w)<e&&Math.abs(a.h-t.h)<e}class se{constructor(t){this.canvas=t;const e=t.getContext("2d");if(!e)throw new Error("2D canvas context unavailable");this.ctx=e,this.createOverlay(),this.startOverlayLoop(),this.resize(),new ResizeObserver(()=>this.resize()).observe(t),window.addEventListener("orientationchange",()=>window.setTimeout(()=>this.resize(),200))}ctx;mainEl=null;region=null;shownRegion=null;regionBridge=0;regionActiveHold=0;screen={width:0,height:0};viewMode="fit";zoom=1;center={nx:.5,ny:.5};cursor=null;onZoomCb;onViewCb;gestureActive=!1;viewDirty=!1;videoActive=!1;videoRaf=0;cssW=1;cssH=1;dpr=1;layout={S:1,tx:0,ty:0};rafPending=!1;overlayBox=null;overlayView=null;overlayCanvas=null;overlayCtx=null;lastOverlayKey="";overlayCanvasKey="";minimapActiveAt=0;minimapShown=!0;overview=null;overviewCtx=null;overviewReady=!1;overviewDirty=!1;lastSnapAt=0;overviewEl=null;createOverlay(){const t=document.createElement("div");t.style.cssText="position:fixed;top:calc(env(safe-area-inset-top, 0px) + 56px);right:10px;box-sizing:border-box;border:1.5px solid rgba(255,255,255,0.8);border-radius:6px;background:rgba(0,0,0,0.55);z-index:2147483600;pointer-events:none;display:none;box-shadow:0 1px 8px rgba(0,0,0,0.6);";const e=document.createElement("canvas");e.style.cssText="position:absolute;inset:0;width:100%;height:100%;border-radius:5px;display:block;";const n=document.createElement("div");n.style.cssText="position:absolute;box-sizing:border-box;border:2px solid #4ea1ff;background:rgba(78,161,255,0.18);border-radius:2px;",t.appendChild(e),t.appendChild(n),document.body.appendChild(t),this.overlayBox=t,this.overlayView=n,this.overlayCanvas=e,this.overlayCtx=e.getContext("2d")}startOverlayLoop(){const t=()=>{this.renderOverlay(),requestAnimationFrame(t)};requestAnimationFrame(t)}renderOverlay(){const t=this.overlayBox,e=this.overlayView;if(!t||!e)return;const n=this.getVisibleRegion(),i=this.region,s=this.dispW(),o=this.dispH();if(!(!!i||n.w<.985||n.h<.985)){t.style.display!=="none"&&(t.style.display="none"),this.lastOverlayKey="",this.overlayCanvasKey="";return}t.style.display==="none"&&(t.style.display="block",this.minimapActiveAt=performance.now(),this.minimapShown=!1);const c=Math.round(Math.min(132,Math.max(76,this.cssW*.32))),h=Math.round(c*(o/s||.625)),w=`${c}x${h}`;(w!==this.overlayCanvasKey||this.overviewDirty)&&(this.overlayCanvasKey=w,this.overviewDirty=!1,t.style.width=`${c}px`,t.style.height=`${h}px`,this.paintMinimap(c,h));const g=n,v=performance.now(),y=`${g.x.toFixed(3)},${g.y.toFixed(3)},${g.w.toFixed(3)},${g.h.toFixed(3)}`;y!==this.lastOverlayKey&&(this.lastOverlayKey=y,this.minimapActiveAt=v,e.style.left=`${(N(g.x,0,1)*100).toFixed(2)}%`,e.style.top=`${(N(g.y,0,1)*100).toFixed(2)}%`,e.style.width=`${(N(Math.max(.04,g.w),0,1)*100).toFixed(2)}%`,e.style.height=`${(N(Math.max(.04,g.h),0,1)*100).toFixed(2)}%`);const p=v-this.minimapActiveAt<ie;p!==this.minimapShown&&(this.minimapShown=p,t.style.transition=`opacity ${p?.18:.5}s ease`,t.style.opacity=p?"1":"0")}paintMinimap(t,e){const n=this.overlayCanvas,i=this.overlayCtx;if(!n||!i)return;const s=Math.max(1,Math.round(t*this.dpr)),o=Math.max(1,Math.round(e*this.dpr));n.width!==s&&(n.width=s),n.height!==o&&(n.height=o),i.clearRect(0,0,s,o);const r=this.overviewImg();r&&(i.imageSmoothingEnabled=!0,i.imageSmoothingQuality="low",i.drawImage(r,0,0,s,o))}overviewImg(){return this.overviewReady?this.overview:null}maybeSnapshot(t,e,n){const i=performance.now();if(i-this.lastSnapAt<te)return;this.lastSnapAt=i,this.overview||(this.overview=document.createElement("canvas"),this.overviewCtx=this.overview.getContext("2d"));const s=this.overview,o=this.overviewCtx;if(!s||!o)return;const r=Qt,c=Math.max(1,Math.round(r*n/e));s.width!==r&&(s.width=r),s.height!==c&&(s.height=c),o.imageSmoothingEnabled=!0,o.imageSmoothingQuality="medium",o.drawImage(t,0,0,e,n,0,0,r,c),this.overviewReady=!0,this.overviewDirty=!0}getCanvas(){return this.canvas}resize(){const t=this.canvas.getBoundingClientRect();this.cssW=Math.max(1,t.width),this.cssH=Math.max(1,t.height),this.dpr=Math.min(window.devicePixelRatio||1,2),this.canvas.width=Math.round(this.cssW*this.dpr),this.canvas.height=Math.round(this.cssH*this.dpr),this.requestDraw(),this.emitView()}setScreen(t){const e=t.width!==this.screen.width||t.height!==this.screen.height;this.screen=t,e&&(this.overviewReady=!1,this.lastSnapAt=0,this.overviewDirty=!0),this.requestDraw()}getScreen(){return this.screen}dispW(){return this.screen.width||this.mainEl?.videoWidth||1}dispH(){return this.screen.height||this.mainEl?.videoHeight||1}setFrameRegion(t){const e=Q(t)?null:t;this.region=e,window.clearTimeout(this.regionActiveHold),window.clearTimeout(this.regionBridge),this.regionBridge=window.setTimeout(()=>{this.shownRegion=e,this.videoActive||this.requestDraw()},ee),this.videoActive||this.requestDraw()}setFrameRegionActive(t){const e=Q(t)?null:t;it(e,this.region)&&(it(e,this.shownRegion)||(window.clearTimeout(this.regionActiveHold),this.regionActiveHold=window.setTimeout(()=>{it(e,this.region)&&(this.shownRegion=e,window.clearTimeout(this.regionBridge),this.videoActive||this.requestDraw())},ne)))}setVideoSource(t){this.mainEl=t,this.videoActive=!!t,t?this.startVideoLoop():this.stopVideoLoop()}isVideoActive(){return this.videoActive}setOverviewSource(t){this.overviewEl=t}startVideoLoop(){if(this.videoRaf)return;const t=()=>{if(!this.videoActive){this.videoRaf=0;return}this.draw(),this.videoRaf=requestAnimationFrame(t)};this.videoRaf=requestAnimationFrame(t)}stopVideoLoop(){this.videoRaf&&cancelAnimationFrame(this.videoRaf),this.videoRaf=0,this.requestDraw()}setViewMode(t){this.viewMode=t,t==="fit"?(this.zoom=1,this.center={nx:.5,ny:.5}):this.zoom===1&&(this.zoom=2),this.emitView(),this.requestDraw()}getViewMode(){return this.viewMode}toggleViewMode(){return this.setViewMode(this.viewMode==="fit"?"magnify":"fit"),this.viewMode}setZoom(t){this.zoom=N(t,1,8),this.viewMode=this.zoom===1?"fit":"magnify",this.zoom===1&&(this.center={nx:.5,ny:.5}),this.onZoomCb?.(this.zoom),this.emitView(),this.requestDraw()}zoomBy(t){this.setZoom(this.zoom*t)}getZoom(){return this.zoom}setOnZoom(t){this.onZoomCb=t}setOnView(t){this.onViewCb=t}beginViewGesture(){this.gestureActive=!0}endViewGesture(){this.gestureActive&&(this.gestureActive=!1,this.viewDirty&&(this.viewDirty=!1,this.onViewCb?.(this.getVisibleRegion())))}emitView(){if(this.gestureActive){this.viewDirty=!0;return}this.onViewCb?.(this.getVisibleRegion())}zoomAround(t,e,n){const i=this.canvasToNorm(e,n),s=N(this.zoom*t,1,8);if(s===1){this.setZoom(1);return}this.zoom=s,this.viewMode="magnify";const o=this.dispW(),r=this.dispH(),h=Math.min(this.cssW/o,this.cssH/r)*this.zoom;this.center.nx=N(i.nx-(e-this.cssW/2)/(o*h||1),q,X),this.center.ny=N(i.ny-(n-this.cssH/2)/(r*h||1),q,X),this.onZoomCb?.(this.zoom),this.emitView(),this.requestDraw()}panByNorm(t,e){this.center.nx=N(this.center.nx+t,q,X),this.center.ny=N(this.center.ny+e,q,X),this.emitView(),this.requestDraw()}panByCanvasPixels(t,e){const{S:n}=this.computeLayout(),i=this.dispW(),s=this.dispH();!i||!s||!n||(this.center.nx=N(this.center.nx-t/(i*n),q,X),this.center.ny=N(this.center.ny-e/(s*n),q,X),this.emitView(),this.requestDraw())}setCursor(t,e=0){this.cursor=t===null?null:{nx:t,ny:e},this.videoActive||this.requestDraw()}computeLayout(){const{cssW:t,cssH:e}=this,n=this.dispW(),i=this.dispH();if(!n||!i)return this.layout={S:1,tx:0,ty:0},this.layout;const o=Math.min(t/n,e/i)*this.zoom;let r,c;return this.viewMode==="fit"||this.zoom===1?(r=(t-n*o)/2,c=(e-i*o)/2):(r=t/2-this.center.nx*n*o,c=e/2-this.center.ny*i*o),this.layout={S:o,tx:r,ty:c},this.layout}canvasToNorm(t,e){const{S:n,tx:i,ty:s}=this.computeLayout();return{nx:N((t-i)/(this.dispW()*n||1),0,1),ny:N((e-s)/(this.dispH()*n||1),0,1)}}normToCanvas(t,e){const{S:n,tx:i,ty:s}=this.computeLayout();return{cx:i+t*this.dispW()*n,cy:s+e*this.dispH()*n}}getVisibleRegion(){const{S:t,tx:e,ty:n}=this.computeLayout(),i=this.dispW()*t,s=this.dispH()*t;if(!i||!s)return{x:0,y:0,w:1,h:1};const o=N(-e/i,0,1),r=N(-n/s,0,1),c=N((this.cssW-e)/i,0,1),h=N((this.cssH-n)/s,0,1);return{x:o,y:r,w:Math.max(.05,c-o),h:Math.max(.05,h-r)}}requestDraw(){this.videoActive||this.rafPending||(this.rafPending=!0,requestAnimationFrame(()=>{this.rafPending=!1,this.draw()}))}draw(){const t=this.ctx;t.setTransform(this.dpr,0,0,this.dpr,0,0),t.clearRect(0,0,this.cssW,this.cssH);const e=this.mainEl&&this.mainEl.videoWidth>0?this.mainEl:null;if(!e)return;const{S:n,tx:i,ty:s}=this.computeLayout(),o=this.dispW(),r=this.dispH(),c=o*n,h=r*n,w=e.videoWidth,g=e.videoHeight,v=w/g,y=f=>f?f.w*o/(f.h*r):o/r;if(this.region!==this.shownRegion){const f=Math.abs(v/y(this.region)-1)<.06,M=Math.abs(v/y(this.shownRegion)-1)<.06;f&&!M&&(this.shownRegion=this.region,window.clearTimeout(this.regionBridge))}const p=this.shownRegion,C=!!p&&Math.abs(v/y(p)-1)<.08,x=C?i+p.x*c:i,b=C?s+p.y*h:s,T=C?p.w*c:c,d=C?p.h*h:h,u=this.overviewImg();u&&(this.region||this.shownRegion)&&(t.imageSmoothingEnabled=!0,t.imageSmoothingQuality="low",t.drawImage(u,i,s,c,h));const A=Math.min(T/w,d/g),E=w*A,S=g*A;if(t.imageSmoothingEnabled=!0,t.imageSmoothingQuality="high",t.drawImage(e,0,0,w,g,x+(T-E)/2,b+(d-S)/2,E,S),this.cursor){const{cx:f,cy:M}=this.normToCanvas(this.cursor.nx,this.cursor.ny);this.drawCursor(f,M)}const k=this.overviewEl;if(this.region&&k&&k.videoWidth>0)this.maybeSnapshot(k,k.videoWidth,k.videoHeight);else if(!this.region&&!this.shownRegion&&r>0){const f=o/r;f>0&&Math.abs(v/f-1)<.06&&this.maybeSnapshot(e,w,g)}}drawCursor(t,e){const n=this.ctx;n.save(),n.beginPath(),n.arc(t,e,9,0,Math.PI*2),n.strokeStyle="rgba(78,161,255,0.95)",n.lineWidth=2,n.stroke(),n.beginPath(),n.arc(t,e,2.5,0,Math.PI*2),n.fillStyle="rgba(78,161,255,0.95)",n.fill(),n.restore()}}function oe(a,t,e,n){const i=document.createElement("div");i.className="wd-place-layer";const s=document.createElement("div");s.className="wd-place-marker";const o=document.createElement("div");o.className="wd-place-bar";const r=document.createElement("p");r.className="wd-place-hint",r.textContent=e.hint,o.appendChild(r);let c=null;e.withText&&(c=document.createElement("textarea"),c.className="wd-input wd-input-area wd-place-text",c.placeholder="Type the prompt to send…",o.appendChild(c));const h=document.createElement("div");h.className="wd-place-buttons";const w=document.createElement("button");w.className="wd-btn",w.textContent="Cancel";const g=document.createElement("button");g.className="wd-btn wd-go",g.textContent=e.confirmLabel,h.append(w,g),o.appendChild(h),t.append(i,s,o),t.classList.add("wd-placing");let v={nx:.5,ny:.5},y=0;const p=()=>{const{cx:k,cy:f}=a.normToCanvas(v.nx,v.ny);s.style.left=`${k}px`,s.style.top=`${f}px`,y=requestAnimationFrame(p)};y=requestAnimationFrame(p);const C=new Map;let x="idle",b=null,T={x:0,y:0},d=!1;const u=k=>{const f=i.getBoundingClientRect();return{x:k.clientX-f.left,y:k.clientY-f.top}},A=k=>{const{cx:f,cy:M}=a.normToCanvas(v.nx,v.ny);return Math.hypot(k.x-f,k.y-M)<38};i.addEventListener("pointerdown",k=>{i.setPointerCapture?.(k.pointerId);const f=u(k);if(C.set(k.pointerId,f),C.size===2){const M=[...C.values()],L=M[0],R=M[1];b={dist:Math.hypot(L.x-R.x,L.y-R.y),mx:(L.x+R.x)/2,my:(L.y+R.y)/2},x="idle"}else C.size===1&&(T=f,d=!1,x=A(f)?"marker":"pan",x==="marker"&&(v=a.canvasToNorm(f.x,f.y)))}),i.addEventListener("pointermove",k=>{const f=C.get(k.pointerId);if(!f)return;const M=u(k);if(C.set(k.pointerId,M),C.size>=2&&b){const V=[...C.values()],$=V[0],O=V[1],_=Math.hypot($.x-O.x,$.y-O.y),Z=($.x+O.x)/2,et=($.y+O.y)/2;Math.abs(_-b.dist)>4&&(a.zoomAround(1+(_-b.dist)/200,Z,et),b.dist=_),a.panByCanvasPixels(Z-b.mx,et-b.my),b.mx=Z,b.my=et;return}Math.hypot(M.x-T.x,M.y-T.y)>6&&(d=!0);const L=M.x-f.x,R=M.y-f.y;x==="marker"?v=a.canvasToNorm(M.x,M.y):x==="pan"&&a.panByCanvasPixels(L,R)});const E=k=>{const f=C.get(k.pointerId);C.delete(k.pointerId),f&&x==="pan"&&!d&&(v=a.canvasToNorm(f.x,f.y)),C.size<2&&(b=null),C.size===0&&(x="idle")};i.addEventListener("pointerup",E),i.addEventListener("pointercancel",E);const S=k=>{cancelAnimationFrame(y),t.classList.remove("wd-placing"),i.remove(),s.remove(),o.remove(),n(k)};w.onclick=()=>S(null),g.onclick=()=>{const k=c?.value.trim()??"";if(e.withText&&!k){c?.focus();return}S({nx:v.nx,ny:v.ny,text:e.withText?k:void 0})}}let re=0;function st(){return`w${Date.now().toString(36)}${(re++).toString(36)}`}function ae(){return new Date().toLocaleString(void 0,{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})}function ce(a){const t=Math.max(0,Math.round((a-Date.now())/1e3)),e=Math.floor(t/3600),n=Math.floor(t%3600/60),i=t%60;return e>0?`${e}h ${n}m`:n>0?`${n}m ${String(i).padStart(2,"0")}s`:`${i}s`}function l(a,t,e){const n=document.createElement(a);return t&&(n.className=t),e!==void 0&&(n.textContent=e),n}const de={claude:"Claude Code",codex:"Codex CLI",gemini:"Gemini CLI",aider:"Aider",copilot:"Copilot CLI",opencode:"opencode",cursor:"Cursor Agent",amp:"Amp",unknown:"AI agent"};function J(a){return de[a]??"AI agent"}function wt(a){switch(a){case"working":return"working";case"blocked":return"needs you";case"idle":return"idle";case"finished":return"finished";case"crashed":return"crashed";default:return"…"}}class le{constructor(t,e,n,i,s=()=>{}){this.root=t,this.conn=e,this.view=n,this.notifications=i,this.requestNotifications=s,this.overlay=l("div","wd-dialog-overlay hidden"),this.overlay.addEventListener("pointerdown",u=>{u.target===this.overlay&&this.close()});const o=l("div","wd-dialog"),r=l("div","wd-dialog-head");r.append(l("h2","","Auto-Whips"));const c=l("button","wd-dialog-x");c.appendChild(P("x")),c.onclick=()=>this.close(),r.appendChild(c);const h=l("div","wd-dialog-help"),w=l("p","wd-help-intro","Auto-Whips are a set of features that can whip and monitor agents for you automatically:"),g=l("ul","wd-help-list"),v=l("li");v.append(l("strong",void 0,"Alerts"),document.createTextNode(" — watch part of the screen and ping you when it changes (e.g. your agent finishes)."));const y=l("li");y.append(l("strong",void 0,"Timers"),document.createTextNode(" — ping you after a set time, and can auto-click or send a prompt when they fire."));const p=l("li");p.append(l("strong",void 0,"Session Monitoring"),document.createTextNode(" — pick a running AI session and get pinged the moment the agent stops working (it's waiting on you or has gone idle).")),g.append(v,y,p);const C=l("p","wd-help-note","Enable browser notifications to be reminded even when the browser is closed.");h.append(w,g,C),this.permissionRow=l("div","wd-perm-row"),this.list=l("div","wd-watch-list");const x=l("button","wd-btn wd-go");x.append(P("plus"),l("span","wd-btn-label","Add alert")),x.onclick=()=>this.beginSelection();const b=l("button","wd-btn wd-go");b.append(P("clock"),l("span","wd-btn-label","Add timer")),b.onclick=()=>this.beginTimer();const T=l("button","wd-btn wd-go");T.append(P("activity"),l("span","wd-btn-label","Add Session Monitoring")),T.onclick=()=>this.beginMonitor();const d=l("div","wd-dialog-actions wd-actions-stack");d.append(x,b,T),o.append(r,h,this.permissionRow,this.list,d),this.overlay.appendChild(o),this.root.appendChild(this.overlay),this.renderPermission(),this.conn.on("monitorSessions",u=>{this.monitorSessions=u,this.renderPicker?.()})}regions=[];timers=[];monitors=[];monitorSessions=[];alwaysAgents=new Set;renderPicker=null;renderAlways=null;countdownTimer=0;overlay;list;permissionRow;selector=null;setRegions(t){this.regions=t,this.renderList()}setTimers(t){this.timers=t,this.renderList()}setMonitors(t){this.monitors=t,this.renderList()}setAlwaysAgents(t){this.alwaysAgents=new Set(t),this.renderAlways?.()}open(){this.renderPermission(),this.renderList(),this.overlay.classList.remove("hidden"),window.clearInterval(this.countdownTimer),this.countdownTimer=window.setInterval(()=>{this.timers.length&&this.renderList()},1e3)}close(){this.overlay.classList.add("hidden"),window.clearInterval(this.countdownTimer),this.countdownTimer=0}renderPermission(){this.permissionRow.replaceChildren();const t=l("span","wd-perm-dot"),e=l("span","wd-perm-text"),n=this.notifications.permission;if(n==="granted")t.dataset.state="on",e.textContent="Browser notifications are on.",this.permissionRow.append(t,e);else if(n==="denied")t.dataset.state="off",e.textContent="Notifications are blocked — enable them in your browser settings.",this.permissionRow.append(t,e);else if(n==="unsupported")t.dataset.state="off",e.textContent="This browser can't show notifications. Keep this page open for in-app alerts.",this.permissionRow.append(t,e);else{t.dataset.state="warn",e.textContent="Allow notifications to be alerted while this page is in the background.";const i=l("button","wd-btn wd-perm-enable");i.append(l("span","wd-btn-label","Enable")),i.onclick=async()=>{await this.requestNotifications(),this.renderPermission()},this.permissionRow.append(t,e,i)}}renderList(){if(this.list.replaceChildren(),this.regions.length===0&&this.timers.length===0&&this.monitors.length===0){this.list.appendChild(l("p","wd-dialog-help","No alerts, timers, or session monitors yet."));return}for(const t of this.regions){const e=l("div","wd-watch-row"),n=l("button","wd-watch-name",t.label);n.title="Edit this alert",n.onclick=()=>this.beginSelection(t),e.appendChild(n);const i=l("button","wd-btn wd-icon-only");i.appendChild(P("trash")),i.setAttribute("aria-label",`Remove ${t.label}`),i.onclick=()=>{this.conn.send({type:"watch-remove",id:t.id}),this.regions=this.regions.filter(s=>s.id!==t.id),this.renderList()},e.appendChild(i),this.list.appendChild(e)}for(const t of this.timers){const e=l("div","wd-watch-row"),n=l("div","wd-timer-info"),i=l("span","wd-timer-name");i.append(P("clock",15),document.createTextNode(t.label));const s=l("span","wd-timer-remain",ce(t.fireAtMs));n.append(i,s),e.appendChild(n),t.hasAction&&e.appendChild(l("span","wd-timer-tag","auto"));const o=l("button","wd-btn wd-icon-only");o.appendChild(P("trash")),o.setAttribute("aria-label",`Cancel ${t.label}`),o.onclick=()=>{this.conn.send({type:"timer-remove",id:t.id}),this.timers=this.timers.filter(r=>r.id!==t.id),this.renderList()},e.appendChild(o),this.list.appendChild(e)}for(const t of this.monitors){const e=l("div","wd-watch-row"),n=l("div","wd-timer-info"),i=l("span","wd-timer-name");i.append(P("activity",15),document.createTextNode(`${J(t.agent)} · ${t.label}`));const s=l("span","wd-mon-state");s.dataset.state=t.live?t.state:"finished",s.textContent=t.live?wt(t.state):"ended",n.append(i,s),e.appendChild(n);const o=l("button","wd-btn wd-icon-only");o.appendChild(P("trash")),o.setAttribute("aria-label",`Stop monitoring ${t.label}`),o.onclick=()=>{this.conn.send({type:"monitor-remove",id:t.id}),this.monitors=this.monitors.filter(r=>r.id!==t.id),this.renderList()},e.appendChild(o),this.list.appendChild(e)}}beginSelection(t){this.close(),this.selector&&this.selector.remove();const e=this.root.getBoundingClientRect(),n=t?this.boxFromRegion(t,e):{x:e.width*.3,y:e.height*.3,w:e.width*.4,h:e.height*.25},i=l("div","wd-selector-info",t?"Move or resize the area to watch, then Save — you'll be alerted when its pixels change.":"You'll get a browser notification when the pixels inside this box change. The agent watches this part of the screen."),s=l("div","wd-selector"),o=l("div","wd-selector-move");o.appendChild(P("drag"));const r=l("div","wd-selector-handle"),c=l("div","wd-selector-bar"),h=l("button","wd-btn wd-go");h.append(l("span","wd-btn-label",t?"Save":"Create"));const w=l("button","wd-btn");w.append(l("span","wd-btn-label","Cancel")),c.append(w,h),s.append(o,r,c),this.root.append(i,s),this.selector=s;const g=()=>{s.remove(),i.remove(),this.selector=null,this.open()},v=()=>{s.style.left=`${n.x}px`,s.style.top=`${n.y}px`,s.style.width=`${n.w}px`,s.style.height=`${n.h}px`};v();const y=(p,C)=>{p.addEventListener("pointerdown",x=>{x.preventDefault(),x.stopPropagation(),p.setPointerCapture(x.pointerId);let b=x.clientX,T=x.clientY;const d=A=>{C(A.clientX-b,A.clientY-T),b=A.clientX,T=A.clientY,v()},u=()=>{p.removeEventListener("pointermove",d),p.removeEventListener("pointerup",u)};p.addEventListener("pointermove",d),p.addEventListener("pointerup",u)})};y(o,(p,C)=>{n.x=Math.max(0,Math.min(e.width-n.w,n.x+p)),n.y=Math.max(0,Math.min(e.height-n.h,n.y+C))}),y(r,(p,C)=>{n.w=Math.max(40,Math.min(e.width-n.x,n.w+p)),n.h=Math.max(40,Math.min(e.height-n.y,n.h+C))}),w.onclick=g,h.onclick=()=>{const p=this.toScreenRegion(n,t);p&&(this.conn.send({type:"watch-add",region:p}),this.regions=t?this.regions.map(C=>C.id===p.id?p:C):[...this.regions,p],this.notifications.permission==="default"&&this.requestNotifications(),this.notifications.flash(t?"Alert updated":"Alert created",t?`"${p.label}" updated.`:`Monitoring "${p.label}". We'll notify you when it changes.`,"success")),g()}}boxFromRegion(t,e){const n=this.view.normToCanvas(t.x,t.y),i=this.view.normToCanvas(t.x+t.w,t.y+t.h),s=Math.max(40,i.cx-n.cx),o=Math.max(40,i.cy-n.cy);return{x:Math.max(0,Math.min(e.width-s,n.cx)),y:Math.max(0,Math.min(e.height-o,n.cy)),w:s,h:o}}toScreenRegion(t,e){const n=this.view.canvasToNorm(t.x,t.y),i=this.view.canvasToNorm(t.x+t.w,t.y+t.h),s=Math.min(n.nx,i.nx),o=Math.min(n.ny,i.ny),r=Math.abs(i.nx-n.nx),c=Math.abs(i.ny-n.ny);return r<.005||c<.005?null:{id:e?.id??st(),label:e?.label??ae(),x:s,y:o,w:r,h:c}}beginTimer(){this.close();const t=l("div","wd-dialog-overlay"),e=()=>{t.remove(),this.open()};t.addEventListener("pointerdown",f=>{f.target===t&&e()});const n=l("div","wd-dialog"),i=l("div","wd-dialog-head");i.append(l("h2","","Set timer"));const s=l("button","wd-dialog-x");s.appendChild(P("x")),s.onclick=e,i.appendChild(s);const o=l("p","wd-dialog-help","Waiting for a session limit to reset? Get notified when the time's up. You can also schedule an action for the moment it hits zero — click Retry, or enter a new prompt."),r=l("div","wd-form-row");r.appendChild(l("label","wd-form-label","Remind me in"));const c=l("input","wd-input wd-input-num");c.type="number",c.min="0",c.max="168",c.value="0",c.inputMode="numeric";const h=l("input","wd-input wd-input-num");h.type="number",h.min="0",h.max="59",h.value="30",h.inputMode="numeric";const w=l("div","wd-preset-row");for(const[f,M,L]of[["15m",0,15],["30m",0,30],["1h",1,0],["2h",2,0],["5h",5,0]]){const R=l("button","wd-preset",f);R.type="button",R.onclick=()=>{c.value=String(M),h.value=String(L)},w.appendChild(R)}const g=(f,M,L,R)=>{const V=l("div","wd-stepper"),$=l("button","wd-step-btn","−");$.type="button";const O=l("button","wd-step-btn","+");O.type="button";const _=Z=>Math.max(0,Math.min(L,Z));return $.onclick=()=>f.value=String(_((Number(f.value)||0)-M)),O.onclick=()=>f.value=String(_((Number(f.value)||0)+M)),V.append($,f,l("span","wd-form-unit",R),O),V},v=l("div","wd-form-duration");v.append(g(c,1,168,"h"),g(h,5,59,"m")),r.append(w,v);const y=l("div","wd-form-row");y.appendChild(l("label","wd-form-label","Label"));const p=l("input","wd-input");p.placeholder="e.g. Claude is back",y.appendChild(p);const C=l("div","wd-form-row");C.appendChild(l("label","wd-form-label","When it fires"));const x=l("select","wd-input");for(const[f,M]of[["none","Just remind me"],["click","Click a button"],["key","Click & press Enter"],["text","Click, type & send a prompt"]]){const L=document.createElement("option");L.value=f,L.textContent=M,x.appendChild(L)}C.appendChild(x);const b=l("div","wd-dialog-actions"),T=l("button","wd-btn");T.append(l("span","wd-btn-label","Cancel")),T.onclick=e;const d=l("button","wd-btn wd-go"),u=l("span","wd-btn-label","Start timer");d.append(P("clock"),u);const A=()=>{u.textContent=x.value==="none"?"Start timer":"Next: place target"};x.onchange=A,A();const E=()=>{const f=Math.max(0,Math.min(168,Math.round(Number(c.value)||0))),M=Math.max(0,Math.min(59,Math.round(Number(h.value)||0))),L=(f*60+M)*6e4;return L>=6e4?L:null},S=()=>{const f=Math.max(0,Math.round(Number(c.value)||0)),M=Math.max(0,Math.round(Number(h.value)||0));return`${f?`${f}h `:""}${M}m`},k=f=>{const M=st(),L=E(),R=p.value.trim()||`Timer (${S()})`;this.conn.send({type:"timer-add",id:M,fireInMs:L,label:R,action:f}),this.timers=[...this.timers,{id:M,label:R,fireAtMs:Date.now()+L,hasAction:!!f}],this.notifications.permission==="default"&&this.requestNotifications(),this.notifications.flash("Timer started",`"${R}" — I'll ping you in ${S()}.`,"success"),e()};d.onclick=()=>{if(E()==null){this.notifications.flash("Set a time","Choose at least 1 minute.","warning");return}const f=x.value;if(f==="none"){k(void 0);return}t.style.display="none";const M=f==="text"?"Drag the crosshair onto the target input and type your prompt. The timer will click it to focus, insert the text and press Enter when time is up.":f==="key"?"Drag the crosshair onto the field. The timer will click it to focus and press Enter when time is up.":"Drag the crosshair onto the button or UI. The timer will click it when time is up.";oe(this.view,this.root,{withText:f==="text",hint:M,confirmLabel:"Confirm target"},L=>{if(!L){t.style.display="";return}let R;f==="click"?R={kind:"click",x:L.nx,y:L.ny,button:"left"}:f==="key"?R={kind:"key",key:"Enter",x:L.nx,y:L.ny}:R={kind:"text",text:L.text??"",x:L.nx,y:L.ny},k(R)})},b.append(T,d),n.append(i,o,r,y,C,b),t.appendChild(n),this.root.appendChild(t)}beginMonitor(){this.close();const t=l("div","wd-dialog-overlay"),e=()=>{this.renderPicker=null,this.renderAlways=null,t.remove(),this.open()};t.addEventListener("pointerdown",d=>{d.target===t&&e()});const n=l("div","wd-dialog"),i=l("div","wd-dialog-head");i.append(l("h2","","Monitor a session"));const s=l("button","wd-dialog-x");s.appendChild(P("x")),s.onclick=e,i.appendChild(s);const o=l("p","wd-dialog-help","Pick a running AI session to watch. WhipDesk finds them automatically — no setup, no wrappers. You'll get one ping the moment the agent stops working (it's waiting on you or has gone idle)."),r=l("div","wd-mon-pick-head"),c=l("button","wd-btn");c.append(l("span","wd-btn-label","Rescan")),c.onclick=()=>this.conn.send({type:"monitor-scan"}),r.append(l("span","wd-form-label","Running sessions"),c);const h=l("div","wd-mon-pick");let w="";const g=()=>{if(h.replaceChildren(),this.monitorSessions.length===0){w="",T(),p(),h.appendChild(l("p","wd-dialog-help","No AI sessions detected yet. Start Claude Code, Codex, Gemini, or Aider, then Rescan."));return}(!w||!this.monitorSessions.some(d=>d.key===w))&&(w=this.monitorSessions[0].key);for(const d of this.monitorSessions){const u=l("button","wd-mon-item");u.type="button",d.key===w&&u.classList.add("on");const A=l("div","wd-mon-item-main");A.append(P("activity",15),l("span","wd-mon-item-title",`${J(d.agent)} · ${d.title}`));const E=l("span","wd-mon-state");E.dataset.state=d.state,E.textContent=wt(d.state),u.append(A,E),u.onclick=()=>{w=d.key,g()},h.appendChild(u)}T(),p()};this.renderPicker=g;const v=l("div","wd-form-row");v.appendChild(l("label","wd-form-label","Always alert me"));const y=l("div","wd-mon-always");v.appendChild(y);const p=()=>{y.replaceChildren();const d=this.monitorSessions.find(k=>k.key===w);if(!d){y.appendChild(l("p","wd-dialog-help","Pick a session above to always alert for its agent."));return}const u=d.agent,A=l("label","wd-check"),E=l("input");E.type="checkbox",E.checked=this.alwaysAgents.has(u),E.onchange=()=>{E.checked?this.alwaysAgents.add(u):this.alwaysAgents.delete(u),this.conn.send({type:"monitor-always",agent:u,enabled:E.checked}),E.checked&&this.notifications.permission==="default"&&this.requestNotifications(),p()};const S=l("span","wd-check-text");S.append(document.createTextNode(`Always monitor every ${J(u)} session`),l("span","wd-check-sub","Keeps alerting across agent and WhipDesk restarts — no need to re-add it.")),A.append(E,S),y.appendChild(A)};this.renderAlways=p;const C=l("div","wd-dialog-actions"),x=l("button","wd-btn");x.append(l("span","wd-btn-label","Cancel")),x.onclick=e;const b=l("button","wd-btn wd-go");b.append(P("activity"),l("span","wd-btn-label","Add monitor"));const T=()=>b.disabled=!w;T(),b.onclick=()=>{const d=this.monitorSessions.find(E=>E.key===w);if(!d)return;const u=st(),A=d.title;this.conn.send({type:"monitor-add",id:u,key:d.key,agent:d.agent,label:A}),this.monitors=[...this.monitors,{id:u,key:d.key,agent:d.agent,label:A,state:d.state,live:!0}],this.notifications.permission==="default"&&this.requestNotifications(),this.notifications.flash("Monitoring started",`Watching ${J(d.agent)} · ${A}.`,"success"),e()},C.append(x,b),n.append(i,o,r,h,v,C),t.appendChild(n),this.root.appendChild(t),g(),this.conn.send({type:"monitor-scan"})}}function he(){const a=new URLSearchParams(location.hash.replace(/^#/,""));return{token:a.get("t")??a.get("token")??"",remote:a.get("remote")==="1"||a.has("device"),device:a.get("device")??a.get("d")??""}}function pe(){return`${location.protocol==="https:"?"wss":"ws"}://${location.host}/ws`}async function ue(){const a=window.__WHIPDESK_FB__;if(a?.apiKey)return a;try{const t=await fetch("firebase.json",{cache:"no-store"});if(t.ok)return await t.json()}catch{}return null}const F=document.getElementById("app");if(!F)throw new Error("#app not found");const tt=Tt("canvas","wd-screen");tt.id="wd-screen";const kt=Tt("div","wd-toasts");F.append(tt,kt);const{token:rt,remote:St,device:at}=he(),I=new se(tt),B=new Xt(kt),ft=new Zt(F),K=new Nt(F);async function me(a){if(St&&at){if(a)return new Jt(at,rt,a);B.show({type:"notification",id:"no-fb",title:"Remote unavailable",body:"Firebase config not found for remote mode.",level:"error",source:"client",t:Date.now()})}return new Dt(pe(),rt)}async function we(){const a=St&&at?await ue():null,t=await me(a),e=new qt(tt,I,t),n=()=>a?mt(a,B):B.requestPermission(),i=new le(F,t,I,B,n),s=new Ht(F,{conn:t,view:I,input:e,notifications:B,watchers:i}),o=document.createElement("video");o.muted=!0,o.autoplay=!0,o.playsInline=!0,o.setAttribute("playsinline",""),o.style.display="none",F.append(o);const r=document.createElement("video");r.muted=!0,r.autoplay=!0,r.playsInline=!0,r.setAttribute("playsinline",""),r.style.display="none",F.append(r);let c=!1,h=!1,w="",g=0;const v=d=>d.w>=.999&&d.h>=.999,y=d=>{const u=d.w>=.92&&d.h>=.92?{x:0,y:0,w:1,h:1}:d,A=`${u.x.toFixed(2)},${u.y.toFixed(2)},${u.w.toFixed(2)},${u.h.toFixed(2)}`;A!==w&&(w=A,t.send({type:"set-viewport",x:u.x,y:u.y,w:u.w,h:u.h}))};I.setOnView(d=>{const u=I.getZoom()>1.01;window.clearTimeout(g),g=window.setTimeout(()=>y(u?d:{x:0,y:0,w:1,h:1}),u?120:0)}),t.on("status",d=>{s.setStatus(d),d==="connected"?(h=!1,K.hide()):(!c||h)&&K.show(c?"Reconnecting…":void 0),d==="disconnected"&&(w="")}),t.on("transport",d=>s.setTransport(d)),t.on("welcome",d=>{const u=!c;c=!0,K.hide(),ft.hide(),I.setScreen(d.screen),s.setWelcome(d),u&&(w="0.000,0.000,1.000,1.000",I.setZoom(1),t.send({type:"set-viewport",x:0,y:0,w:1,h:1}))}),t.on("versionMismatch",d=>fe(d.agentVersion)),t.on("pinRequired",d=>{K.hide(),ft.show(d,u=>t.submitPin(u))}),t.on("presence",d=>s.setPresence(d));let p=0,C=0,x=0;const b=()=>s.setAlertCount(p+C+x);if(t.on("watchers",d=>{p=d.length,i.setRegions(d),b()}),t.on("timers",d=>{C=d.length,i.setTimers(d),b()}),t.on("monitors",d=>{x=d.length,i.setMonitors(d),b()}),t.on("monitorAlways",d=>{i.setAlwaysAgents(d)}),t.on("screenRegion",d=>{const u=v(d)?null:d;d.active?I.setFrameRegionActive(u):I.setFrameRegion(u)}),t.on("videoTrack",d=>{if(!d){o.srcObject=null,I.setVideoSource(null);return}o.srcObject=d,o.play().catch(()=>{}),I.setVideoSource(o)}),t.on("overviewTrack",d=>{if(!d){r.srcObject=null,I.setOverviewSource(null);return}r.srcObject=d,r.play().catch(()=>{}),I.setOverviewSource(r)}),t.on("netStats",({fps:d,rtt:u})=>s.setNetStats(d,u)),t.on("screenMeta",({screen:d,activeDisplay:u})=>{I.setScreen(d),u!==void 0&&s.setActiveDisplay(u)}),t.on("notification",d=>B.show(d)),t.on("error",d=>s.flashError(d)),a){let d=!1;t.on("welcome",()=>{d||B.permission!=="granted"||(d=!0,mt(a,B))})}const T=()=>{document.hidden||t.isHealthy()||(h=!0,K.show("Reconnecting…"),t.wake())};document.addEventListener("visibilitychange",()=>{t.setVisible(!document.hidden),document.hidden||T()}),window.addEventListener("pageshow",T),rt||s.flashError("No pairing token in the URL (#t=…). Re-open the link/QR from the agent."),t.connect()}we();function Tt(a,t){const e=document.createElement(a);return e.className=t,e}function fe(a){const t="wd-outdated-banner";if(document.getElementById(t))return;const e=document.createElement("div");e.id=t,e.style.cssText="position:fixed;top:0;left:0;right:0;z-index:9999;background:#7a2e00;color:#fff;font:14px/1.4 system-ui,sans-serif;padding:10px 40px 10px 14px;text-align:center;box-shadow:0 2px 8px rgba(0,0,0,.35)";const n=a?` (agent ${a})`:"";e.innerHTML=`⚠️ This WhipDesk agent${n} is out of date. Update from <a style="color:#ffd9a6" href="https://github.com/BinaryBananaLLC/WhipDesk/releases/latest" target="_blank" rel="noreferrer noopener">the latest release</a> or run <code>npm i -g whipdesk@latest</code>.`;const i=document.createElement("button");i.textContent="✕",i.setAttribute("aria-label","Dismiss"),i.style.cssText="position:absolute;right:8px;top:6px;background:none;border:none;color:#fff;font-size:16px;cursor:pointer",i.onclick=()=>e.remove(),e.appendChild(i),document.body.appendChild(e)}
|