whipdesk 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent.cjs +200 -27
- package/dist/mobile-web/assets/index-C_O8Rc7r.css +1 -0
- package/dist/mobile-web/assets/index-_PAhuG6R.js +2 -0
- package/dist/mobile-web/assets/loading-whip-anim-DmlnYIJ-.gif +0 -0
- package/dist/mobile-web/firebase-messaging-sw.js +34 -26
- package/dist/mobile-web/index.html +2 -2
- package/package.json +2 -2
- package/dist/mobile-web/assets/index-C1gDHBib.css +0 -1
- package/dist/mobile-web/assets/index-YSgNjLfG.js +0 -2
package/dist/agent.cjs
CHANGED
|
@@ -18909,7 +18909,7 @@ var require_view = __commonJS({
|
|
|
18909
18909
|
var dirname3 = path.dirname;
|
|
18910
18910
|
var basename2 = path.basename;
|
|
18911
18911
|
var extname = path.extname;
|
|
18912
|
-
var
|
|
18912
|
+
var join11 = path.join;
|
|
18913
18913
|
var resolve = path.resolve;
|
|
18914
18914
|
module2.exports = View;
|
|
18915
18915
|
function View(name, options) {
|
|
@@ -18971,12 +18971,12 @@ var require_view = __commonJS({
|
|
|
18971
18971
|
};
|
|
18972
18972
|
View.prototype.resolve = function resolve2(dir, file) {
|
|
18973
18973
|
var ext = this.ext;
|
|
18974
|
-
var path2 =
|
|
18974
|
+
var path2 = join11(dir, file);
|
|
18975
18975
|
var stat2 = tryStat(path2);
|
|
18976
18976
|
if (stat2 && stat2.isFile()) {
|
|
18977
18977
|
return path2;
|
|
18978
18978
|
}
|
|
18979
|
-
path2 =
|
|
18979
|
+
path2 = join11(dir, basename2(file, ext), "index" + ext);
|
|
18980
18980
|
stat2 = tryStat(path2);
|
|
18981
18981
|
if (stat2 && stat2.isFile()) {
|
|
18982
18982
|
return path2;
|
|
@@ -22802,7 +22802,7 @@ var require_send = __commonJS({
|
|
|
22802
22802
|
var Stream = require("stream");
|
|
22803
22803
|
var util = require("util");
|
|
22804
22804
|
var extname = path.extname;
|
|
22805
|
-
var
|
|
22805
|
+
var join11 = path.join;
|
|
22806
22806
|
var normalize = path.normalize;
|
|
22807
22807
|
var resolve = path.resolve;
|
|
22808
22808
|
var sep2 = path.sep;
|
|
@@ -22974,7 +22974,7 @@ var require_send = __commonJS({
|
|
|
22974
22974
|
return res;
|
|
22975
22975
|
}
|
|
22976
22976
|
parts = path2.split(sep2);
|
|
22977
|
-
path2 = normalize(
|
|
22977
|
+
path2 = normalize(join11(root, path2));
|
|
22978
22978
|
} else {
|
|
22979
22979
|
if (UP_PATH_REGEXP.test(path2)) {
|
|
22980
22980
|
debug('malicious path "%s"', path2);
|
|
@@ -23107,7 +23107,7 @@ var require_send = __commonJS({
|
|
|
23107
23107
|
if (err) return self.onStatError(err);
|
|
23108
23108
|
return self.error(404);
|
|
23109
23109
|
}
|
|
23110
|
-
var p =
|
|
23110
|
+
var p = join11(path2, self._index[i]);
|
|
23111
23111
|
debug('stat "%s"', p);
|
|
23112
23112
|
fs.stat(p, function(err2, stat2) {
|
|
23113
23113
|
if (err2) return next(err2);
|
|
@@ -27693,7 +27693,7 @@ function isPackaged() {
|
|
|
27693
27693
|
}
|
|
27694
27694
|
|
|
27695
27695
|
// src/version.ts
|
|
27696
|
-
var AGENT_VERSION = "0.1.
|
|
27696
|
+
var AGENT_VERSION = "0.1.2";
|
|
27697
27697
|
|
|
27698
27698
|
// src/config.ts
|
|
27699
27699
|
var here = (0, import_node_path2.dirname)((0, import_node_url2.fileURLToPath)(__whipdesk_meta_url));
|
|
@@ -28650,6 +28650,13 @@ var ScreenCaptureSession = class _ScreenCaptureSession {
|
|
|
28650
28650
|
spawnAt = 0;
|
|
28651
28651
|
lastPacketAt = 0;
|
|
28652
28652
|
everGotPacket = false;
|
|
28653
|
+
/** Whether the CURRENT spawn has delivered any packet — distinguishes a capture that "went
|
|
28654
|
+
* silent" from one that never started (the avfoundation zero-frame deadlock) in stall logs. */
|
|
28655
|
+
spawnGotPacket = false;
|
|
28656
|
+
/** Consecutive stall-restarts whose spawn never produced a frame (resets on any packet). */
|
|
28657
|
+
framelessRestarts = 0;
|
|
28658
|
+
/** Resolves the restart loop's bounded wait as soon as the current spawn's first packet lands. */
|
|
28659
|
+
firstPacketWaiter = null;
|
|
28653
28660
|
deadRestarts = 0;
|
|
28654
28661
|
erroredOut = false;
|
|
28655
28662
|
stopped = false;
|
|
@@ -28674,7 +28681,7 @@ var ScreenCaptureSession = class _ScreenCaptureSession {
|
|
|
28674
28681
|
this.activeListener = cb;
|
|
28675
28682
|
}
|
|
28676
28683
|
async start() {
|
|
28677
|
-
await this.
|
|
28684
|
+
await this.restart();
|
|
28678
28685
|
}
|
|
28679
28686
|
/** Change the zoom crop or target display. A no-op when nothing changed, so a redundant viewport
|
|
28680
28687
|
* echo never restarts ffmpeg. If a restart is already running (the controller settled a new zoom
|
|
@@ -28702,16 +28709,28 @@ var ScreenCaptureSession = class _ScreenCaptureSession {
|
|
|
28702
28709
|
return false;
|
|
28703
28710
|
}
|
|
28704
28711
|
const ov = !isFull(cfg.crop) && cfg.overview ? cfg.overview : null;
|
|
28705
|
-
let sock;
|
|
28712
|
+
let sock = null;
|
|
28706
28713
|
let sockOv = null;
|
|
28707
28714
|
try {
|
|
28708
28715
|
sock = await bindUdp();
|
|
28709
|
-
if (ov)
|
|
28716
|
+
if (ov) {
|
|
28717
|
+
sockOv = await bindUdp();
|
|
28718
|
+
for (let tries = 0; tries < 4; tries++) {
|
|
28719
|
+
const p = sock.address().port;
|
|
28720
|
+
const q = sockOv.address().port;
|
|
28721
|
+
if (Math.abs(p - q) > 1) break;
|
|
28722
|
+
const again = await bindUdp();
|
|
28723
|
+
closeQuietly(sockOv);
|
|
28724
|
+
sockOv = again;
|
|
28725
|
+
}
|
|
28726
|
+
}
|
|
28710
28727
|
} catch {
|
|
28728
|
+
if (sock) closeQuietly(sock);
|
|
28711
28729
|
if (sockOv) closeQuietly(sockOv);
|
|
28712
28730
|
this.fail();
|
|
28713
28731
|
return false;
|
|
28714
28732
|
}
|
|
28733
|
+
if (!sock) return false;
|
|
28715
28734
|
if (this.stopped) {
|
|
28716
28735
|
closeQuietly(sock);
|
|
28717
28736
|
if (sockOv) closeQuietly(sockOv);
|
|
@@ -28731,6 +28750,12 @@ var ScreenCaptureSession = class _ScreenCaptureSession {
|
|
|
28731
28750
|
"yuv420p",
|
|
28732
28751
|
"-g",
|
|
28733
28752
|
String(gop),
|
|
28753
|
+
// Also key on WALL-CLOCK time, not just frame count: with VFR a static screen yields few output
|
|
28754
|
+
// frames, so a frames-based GOP can leave a lost IDR unrepaired "until something moves". Forcing
|
|
28755
|
+
// a keyframe ~1s (by PTS, which advances via -use_wallclock_as_timestamps) guarantees the client
|
|
28756
|
+
// can always re-sync within ~1s even with no PLI and a frozen desktop.
|
|
28757
|
+
"-force_key_frames",
|
|
28758
|
+
"expr:gte(t,n_forced*1)",
|
|
28734
28759
|
"-b:v",
|
|
28735
28760
|
kbpsArg(kbps),
|
|
28736
28761
|
"-maxrate",
|
|
@@ -28774,14 +28799,18 @@ var ScreenCaptureSession = class _ScreenCaptureSession {
|
|
|
28774
28799
|
this.appliedCfg = cfg;
|
|
28775
28800
|
this.spawnAt = Date.now();
|
|
28776
28801
|
this.lastPacketAt = Date.now();
|
|
28802
|
+
this.spawnGotPacket = false;
|
|
28777
28803
|
const spawnCrop = cfg.crop;
|
|
28778
28804
|
let firstPacket = true;
|
|
28779
28805
|
sock.on("message", (msg) => {
|
|
28780
28806
|
this.lastPacketAt = Date.now();
|
|
28781
28807
|
this.everGotPacket = true;
|
|
28808
|
+
this.spawnGotPacket = true;
|
|
28782
28809
|
this.deadRestarts = 0;
|
|
28783
28810
|
if (firstPacket) {
|
|
28784
28811
|
firstPacket = false;
|
|
28812
|
+
this.framelessRestarts = 0;
|
|
28813
|
+
this.firstPacketWaiter?.();
|
|
28785
28814
|
this.activeListener?.(spawnCrop);
|
|
28786
28815
|
}
|
|
28787
28816
|
this.listener?.(msg);
|
|
@@ -28801,9 +28830,13 @@ var ScreenCaptureSession = class _ScreenCaptureSession {
|
|
|
28801
28830
|
}
|
|
28802
28831
|
});
|
|
28803
28832
|
proc.on("error", (e) => log.warn("video encoder process error:", e.message));
|
|
28833
|
+
proc.on("exit", (code, sig) => {
|
|
28834
|
+
if (this.proc === proc && !this.stopped) log.debug(`ffmpeg exited unexpectedly (${code ?? sig ?? "?"})`);
|
|
28835
|
+
});
|
|
28804
28836
|
this.startHealthMonitor();
|
|
28837
|
+
const cropDesc = isFull(cfg.crop) ? "full desktop" : `zoomed ${cfg.crop.x.toFixed(2)},${cfg.crop.y.toFixed(2)} ${cfg.crop.w.toFixed(2)}x${cfg.crop.h.toFixed(2)}`;
|
|
28805
28838
|
log.debug(
|
|
28806
|
-
`video: H.264 capture started (${enc}, ${kbpsArg(cfg.kbps)}@${cfg.fps}fps, ${
|
|
28839
|
+
`video: H.264 capture started (${enc}, ${kbpsArg(cfg.kbps)}@${cfg.fps}fps, ${cropDesc}${ov ? " + overview" : ""})`
|
|
28807
28840
|
);
|
|
28808
28841
|
return true;
|
|
28809
28842
|
}
|
|
@@ -28817,8 +28850,12 @@ var ScreenCaptureSession = class _ScreenCaptureSession {
|
|
|
28817
28850
|
if (this.stopped || this.restarting || !this.proc) return;
|
|
28818
28851
|
const now = Date.now();
|
|
28819
28852
|
if (this.everGotPacket) {
|
|
28820
|
-
|
|
28821
|
-
|
|
28853
|
+
const stallMs = this.spawnGotPacket ? _ScreenCaptureSession.STALL_MS : Math.min(4e3 + this.framelessRestarts * 1500, 1e4);
|
|
28854
|
+
if (now - this.lastPacketAt > stallMs) {
|
|
28855
|
+
if (!this.spawnGotPacket) this.framelessRestarts += 1;
|
|
28856
|
+
log.debug(
|
|
28857
|
+
`screen capture stalled \u2014 restarting (${this.spawnGotPacket ? "went silent" : `spawn never produced a frame after ${Math.round(stallMs / 1e3)}s`})`
|
|
28858
|
+
);
|
|
28822
28859
|
void this.restart();
|
|
28823
28860
|
}
|
|
28824
28861
|
return;
|
|
@@ -28838,13 +28875,58 @@ var ScreenCaptureSession = class _ScreenCaptureSession {
|
|
|
28838
28875
|
this.restarting = true;
|
|
28839
28876
|
try {
|
|
28840
28877
|
do {
|
|
28841
|
-
this.
|
|
28878
|
+
await this.killAndWait();
|
|
28842
28879
|
if (!await this.spawn()) break;
|
|
28880
|
+
await this.waitForFirstPacket(3e3);
|
|
28843
28881
|
} while (!this.stopped && this.appliedCfg !== null && !sameConfig(this.cfg, this.appliedCfg));
|
|
28844
28882
|
} finally {
|
|
28845
28883
|
this.restarting = false;
|
|
28846
28884
|
}
|
|
28847
28885
|
}
|
|
28886
|
+
/**
|
|
28887
|
+
* Kill the current ffmpeg and WAIT until it has actually EXITED — plus a short macOS beat for
|
|
28888
|
+
* WindowServer to release its screen-capture session — before the caller spawns the next one.
|
|
28889
|
+
*
|
|
28890
|
+
* kill() alone is fire-and-forget: signal delivery and the OS-side capture teardown are both
|
|
28891
|
+
* asynchronous, so under re-crop churn (pan swipe after swipe) the next ffmpeg starts while the
|
|
28892
|
+
* old capture session is still registered — the documented avfoundation deadlock where BOTH
|
|
28893
|
+
* captures yield zero frames. Worse, the health monitor's recovery restart repeats the same
|
|
28894
|
+
* unserialized kill→spawn every 5s, re-triggering the overlap each time: the observed
|
|
28895
|
+
* "stalled — restarting" loop with no "crop live" ever following, dead for tens of seconds.
|
|
28896
|
+
* Serializing exit→settle→spawn removes the overlap entirely, at ~150ms per re-crop.
|
|
28897
|
+
*/
|
|
28898
|
+
async killAndWait() {
|
|
28899
|
+
const proc = this.proc;
|
|
28900
|
+
const exited = proc && proc.exitCode === null && proc.signalCode === null ? new Promise((resolve) => {
|
|
28901
|
+
const cap = setTimeout(resolve, 2500);
|
|
28902
|
+
cap.unref?.();
|
|
28903
|
+
proc.once("exit", () => {
|
|
28904
|
+
clearTimeout(cap);
|
|
28905
|
+
resolve();
|
|
28906
|
+
});
|
|
28907
|
+
}) : null;
|
|
28908
|
+
this.kill();
|
|
28909
|
+
if (exited) {
|
|
28910
|
+
await exited;
|
|
28911
|
+
if (process.platform === "darwin") await new Promise((r) => setTimeout(r, 150));
|
|
28912
|
+
}
|
|
28913
|
+
}
|
|
28914
|
+
/** Bounded wait for the current spawn's first RTP packet (resolves immediately if it already
|
|
28915
|
+
* produced, on stop, or at the cap). See the restart loop for why replacing a capture that
|
|
28916
|
+
* hasn't produced yet is dangerous on macOS. */
|
|
28917
|
+
waitForFirstPacket(capMs) {
|
|
28918
|
+
if (this.spawnGotPacket || this.stopped) return Promise.resolve();
|
|
28919
|
+
return new Promise((resolve) => {
|
|
28920
|
+
const done = () => {
|
|
28921
|
+
clearTimeout(cap);
|
|
28922
|
+
if (this.firstPacketWaiter === done) this.firstPacketWaiter = null;
|
|
28923
|
+
resolve();
|
|
28924
|
+
};
|
|
28925
|
+
const cap = setTimeout(done, capMs);
|
|
28926
|
+
cap.unref?.();
|
|
28927
|
+
this.firstPacketWaiter = done;
|
|
28928
|
+
});
|
|
28929
|
+
}
|
|
28848
28930
|
fail() {
|
|
28849
28931
|
if (this.erroredOut) return;
|
|
28850
28932
|
this.erroredOut = true;
|
|
@@ -28858,11 +28940,22 @@ var ScreenCaptureSession = class _ScreenCaptureSession {
|
|
|
28858
28940
|
}
|
|
28859
28941
|
const proc = this.proc;
|
|
28860
28942
|
this.proc = null;
|
|
28861
|
-
if (proc) {
|
|
28943
|
+
if (proc && proc.exitCode === null && proc.signalCode === null) {
|
|
28862
28944
|
try {
|
|
28863
|
-
proc.kill("
|
|
28945
|
+
proc.kill("SIGTERM");
|
|
28864
28946
|
} catch {
|
|
28865
28947
|
}
|
|
28948
|
+
const hardKill = setTimeout(
|
|
28949
|
+
() => {
|
|
28950
|
+
try {
|
|
28951
|
+
proc.kill("SIGKILL");
|
|
28952
|
+
} catch {
|
|
28953
|
+
}
|
|
28954
|
+
},
|
|
28955
|
+
this.spawnGotPacket ? 1500 : 700
|
|
28956
|
+
);
|
|
28957
|
+
hardKill.unref?.();
|
|
28958
|
+
proc.once("exit", () => clearTimeout(hardKill));
|
|
28866
28959
|
}
|
|
28867
28960
|
if (this.sock) {
|
|
28868
28961
|
closeQuietly(this.sock);
|
|
@@ -28989,7 +29082,15 @@ var VideoHub = class {
|
|
|
28989
29082
|
if (this.opts.onCropActive) session.onActive((crop) => this.opts.onCropActive(crop));
|
|
28990
29083
|
await session.start();
|
|
28991
29084
|
if (this.paused || this.sinks.size === 0 && this.overviewSinks.size === 0) session.stop();
|
|
28992
|
-
else
|
|
29085
|
+
else {
|
|
29086
|
+
this.session = session;
|
|
29087
|
+
void session.reconfigure({
|
|
29088
|
+
displayIndex: this.opts.displayIndex(),
|
|
29089
|
+
crop: this.crop,
|
|
29090
|
+
kbps: this.kbps,
|
|
29091
|
+
fps: this.fps
|
|
29092
|
+
});
|
|
29093
|
+
}
|
|
28993
29094
|
})().finally(() => this.starting = null);
|
|
28994
29095
|
return this.starting;
|
|
28995
29096
|
}
|
|
@@ -29125,6 +29226,9 @@ async function answerWebRtcOffer(ctx, offerSdp, onClosed, opts = {}) {
|
|
|
29125
29226
|
pc.connectionStateChange.subscribe((s) => {
|
|
29126
29227
|
if (s === "failed" || s === "closed" || s === "disconnected") teardown();
|
|
29127
29228
|
});
|
|
29229
|
+
pc.iceConnectionStateChange.subscribe((s) => {
|
|
29230
|
+
if (s === "failed" || s === "closed" || s === "disconnected") teardown();
|
|
29231
|
+
});
|
|
29128
29232
|
await pc.setRemoteDescription({ type: "offer", sdp: offerSdp });
|
|
29129
29233
|
if (offerVideoCount > 0 && canSendVideo && ctx.video) {
|
|
29130
29234
|
for (let i = 0; i < offerVideoCount; i++) {
|
|
@@ -29345,8 +29449,8 @@ function printSetupReminder() {
|
|
|
29345
29449
|
|
|
29346
29450
|
// src/server.ts
|
|
29347
29451
|
var import_node_http = require("node:http");
|
|
29348
|
-
var
|
|
29349
|
-
var
|
|
29452
|
+
var import_node_fs10 = require("node:fs");
|
|
29453
|
+
var import_node_path12 = require("node:path");
|
|
29350
29454
|
var import_node_url3 = require("node:url");
|
|
29351
29455
|
var import_express = __toESM(require_express2(), 1);
|
|
29352
29456
|
|
|
@@ -30073,9 +30177,9 @@ var PinGuard = class _PinGuard {
|
|
|
30073
30177
|
get salt() {
|
|
30074
30178
|
return this.record?.salt ?? "";
|
|
30075
30179
|
}
|
|
30076
|
-
/** Persist a new PIN (>=
|
|
30180
|
+
/** Persist a new PIN (>= 6 chars). Stores only the stretched key. */
|
|
30077
30181
|
setPin(pin) {
|
|
30078
|
-
if (pin.length <
|
|
30182
|
+
if (pin.length < 6) throw new Error("PIN must be at least 6 characters");
|
|
30079
30183
|
const salt = (0, import_node_crypto5.randomBytes)(16).toString("hex");
|
|
30080
30184
|
const key = stretch(pin, salt, ITERATIONS);
|
|
30081
30185
|
this.record = { salt, iterations: ITERATIONS, key };
|
|
@@ -31036,6 +31140,30 @@ function saveAlwaysAgents(stateDir2, agents) {
|
|
|
31036
31140
|
}
|
|
31037
31141
|
}
|
|
31038
31142
|
|
|
31143
|
+
// src/timer-store.ts
|
|
31144
|
+
var import_node_fs9 = require("node:fs");
|
|
31145
|
+
var import_node_path11 = require("node:path");
|
|
31146
|
+
var FILE2 = "timers.json";
|
|
31147
|
+
function loadTimers(stateDir2) {
|
|
31148
|
+
try {
|
|
31149
|
+
const raw = (0, import_node_fs9.readFileSync)((0, import_node_path11.join)(stateDir2, FILE2), "utf8");
|
|
31150
|
+
const parsed = JSON.parse(raw);
|
|
31151
|
+
if (!Array.isArray(parsed.timers)) return [];
|
|
31152
|
+
return parsed.timers.filter(
|
|
31153
|
+
(t) => !!t && typeof t.id === "string" && typeof t.fireAtMs === "number"
|
|
31154
|
+
);
|
|
31155
|
+
} catch {
|
|
31156
|
+
return [];
|
|
31157
|
+
}
|
|
31158
|
+
}
|
|
31159
|
+
function saveTimers(stateDir2, timers) {
|
|
31160
|
+
try {
|
|
31161
|
+
(0, import_node_fs9.mkdirSync)(stateDir2, { recursive: true });
|
|
31162
|
+
(0, import_node_fs9.writeFileSync)((0, import_node_path11.join)(stateDir2, FILE2), JSON.stringify({ timers }), { mode: 384 });
|
|
31163
|
+
} catch {
|
|
31164
|
+
}
|
|
31165
|
+
}
|
|
31166
|
+
|
|
31039
31167
|
// src/util/update-check.ts
|
|
31040
31168
|
async function checkForUpdate(current) {
|
|
31041
31169
|
try {
|
|
@@ -31078,8 +31206,8 @@ function announceUpdate(latest, current, notify) {
|
|
|
31078
31206
|
}
|
|
31079
31207
|
|
|
31080
31208
|
// src/server.ts
|
|
31081
|
-
var here2 = (0,
|
|
31082
|
-
var webDist = isPackaged() ? (0,
|
|
31209
|
+
var here2 = (0, import_node_path12.dirname)((0, import_node_url3.fileURLToPath)(__whipdesk_meta_url));
|
|
31210
|
+
var webDist = isPackaged() ? (0, import_node_path12.join)(here2, "mobile-web") : (0, import_node_path12.join)(here2, "..", "..", "mobile-web", "dist");
|
|
31083
31211
|
async function startAgent() {
|
|
31084
31212
|
const config = loadConfig();
|
|
31085
31213
|
const capturer = new ScreenCapturer({ quality: config.quality, maxWidth: config.maxWidth });
|
|
@@ -31169,6 +31297,7 @@ async function startAgent() {
|
|
|
31169
31297
|
let captureFailing = false;
|
|
31170
31298
|
let viewport = { x: 0, y: 0, w: 1, h: 1 };
|
|
31171
31299
|
const timers = /* @__PURE__ */ new Map();
|
|
31300
|
+
const persistTimers = () => saveTimers(config.stateDir, [...timers.values()]);
|
|
31172
31301
|
const listTimers = () => [...timers.values()].map((t) => ({ id: t.id, label: t.label, fireAtMs: t.fireAtMs, hasAction: !!t.action }));
|
|
31173
31302
|
const broadcastTimers = () => {
|
|
31174
31303
|
const msg = { type: "timers", timers: listTimers() };
|
|
@@ -31190,13 +31319,38 @@ async function startAgent() {
|
|
|
31190
31319
|
const t = timers.get(id);
|
|
31191
31320
|
if (!t) return;
|
|
31192
31321
|
timers.delete(id);
|
|
31322
|
+
persistTimers();
|
|
31193
31323
|
broadcastTimers();
|
|
31194
31324
|
const a = t.action;
|
|
31195
31325
|
if (a) {
|
|
31196
31326
|
try {
|
|
31197
31327
|
log.info(`timer "${t.label || id}" firing${a.kind ? ` (${a.kind})` : ""}`);
|
|
31198
31328
|
if (typeof a.x === "number" && typeof a.y === "number") {
|
|
31199
|
-
|
|
31329
|
+
const pinnedId = t.displayId;
|
|
31330
|
+
let restoreInput = null;
|
|
31331
|
+
if (pinnedId !== void 0 && pinnedId !== activeDisplay) {
|
|
31332
|
+
const pinned = displays.find((d) => d.id === pinnedId);
|
|
31333
|
+
if (!pinned || pinned.width <= 0 || pinned.height <= 0) {
|
|
31334
|
+
throw new Error(`the display it was scheduled on ([${pinnedId}]) is no longer connected`);
|
|
31335
|
+
}
|
|
31336
|
+
input.setActiveDisplay({
|
|
31337
|
+
originX: pinned.originX,
|
|
31338
|
+
originY: pinned.originY,
|
|
31339
|
+
width: pinned.width,
|
|
31340
|
+
height: pinned.height
|
|
31341
|
+
});
|
|
31342
|
+
restoreInput = () => {
|
|
31343
|
+
const cur = displays.find((d) => d.id === activeDisplay);
|
|
31344
|
+
input.setActiveDisplay(
|
|
31345
|
+
cur && cur.width > 0 && cur.height > 0 ? { originX: cur.originX, originY: cur.originY, width: cur.width, height: cur.height } : null
|
|
31346
|
+
);
|
|
31347
|
+
};
|
|
31348
|
+
}
|
|
31349
|
+
try {
|
|
31350
|
+
await input.click(a.button ?? "left", false, a.x, a.y);
|
|
31351
|
+
} finally {
|
|
31352
|
+
restoreInput?.();
|
|
31353
|
+
}
|
|
31200
31354
|
if (a.kind === "key" || a.kind === "text") await delay(250);
|
|
31201
31355
|
}
|
|
31202
31356
|
if (a.kind === "key" && a.key) await input.keyTap(a.key);
|
|
@@ -31220,6 +31374,23 @@ async function startAgent() {
|
|
|
31220
31374
|
source: "timer"
|
|
31221
31375
|
});
|
|
31222
31376
|
};
|
|
31377
|
+
for (const t of loadTimers(config.stateDir)) {
|
|
31378
|
+
if (t.fireAtMs <= Date.now()) {
|
|
31379
|
+
hub.emit({
|
|
31380
|
+
title: t.label || "WhipDesk timer",
|
|
31381
|
+
body: "This timer came due while WhipDesk was not running \u2014 its action was NOT performed.",
|
|
31382
|
+
level: "error",
|
|
31383
|
+
source: "timer"
|
|
31384
|
+
});
|
|
31385
|
+
} else {
|
|
31386
|
+
timers.set(t.id, t);
|
|
31387
|
+
}
|
|
31388
|
+
}
|
|
31389
|
+
persistTimers();
|
|
31390
|
+
if (timers.size > 0) {
|
|
31391
|
+
ensureTimerTicker();
|
|
31392
|
+
log.info(`restored ${timers.size} pending timer${timers.size === 1 ? "" : "s"} from disk`);
|
|
31393
|
+
}
|
|
31223
31394
|
const captureOnce = async () => {
|
|
31224
31395
|
if (regionWatcher.count === 0) return "idle";
|
|
31225
31396
|
try {
|
|
@@ -31384,13 +31555,15 @@ async function startAgent() {
|
|
|
31384
31555
|
addTimer(msg) {
|
|
31385
31556
|
const fireInMs = Math.max(1e3, Math.min(msg.fireInMs, 7 * 24 * 36e5));
|
|
31386
31557
|
const fireAtMs = Date.now() + fireInMs;
|
|
31387
|
-
timers.set(msg.id, { id: msg.id, label: msg.label, fireAtMs, action: msg.action });
|
|
31558
|
+
timers.set(msg.id, { id: msg.id, label: msg.label, fireAtMs, action: msg.action, displayId: activeDisplay });
|
|
31559
|
+
persistTimers();
|
|
31388
31560
|
ensureTimerTicker();
|
|
31389
31561
|
broadcastTimers();
|
|
31390
31562
|
log.info(`timer "${msg.label}" in ${Math.round(fireInMs / 1e3)}s${msg.action ? ` (+${msg.action.kind})` : ""}`);
|
|
31391
31563
|
},
|
|
31392
31564
|
removeTimer(id) {
|
|
31393
31565
|
if (!timers.delete(id)) return;
|
|
31566
|
+
persistTimers();
|
|
31394
31567
|
broadcastTimers();
|
|
31395
31568
|
},
|
|
31396
31569
|
listTimers() {
|
|
@@ -31474,9 +31647,9 @@ async function startAgent() {
|
|
|
31474
31647
|
}
|
|
31475
31648
|
res.json({ ok: true });
|
|
31476
31649
|
});
|
|
31477
|
-
if ((0,
|
|
31650
|
+
if ((0, import_node_fs10.existsSync)(webDist)) {
|
|
31478
31651
|
app.use(import_express.default.static(webDist));
|
|
31479
|
-
app.get("/*splat", (_req, res) => res.sendFile((0,
|
|
31652
|
+
app.get("/*splat", (_req, res) => res.sendFile((0, import_node_path12.join)(webDist, "index.html")));
|
|
31480
31653
|
} else {
|
|
31481
31654
|
log.warn(`mobile-web build not found at ${webDist} \u2014 run "npm run build:web"`);
|
|
31482
31655
|
app.get(
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{--bg:#f4f6f9;--panel:#fff;--panel-2:#eef1f5;--border:#d6dce4;--text:#1a2330;--muted:#5f6b7a;--accent:#2f6fed;--accent-2:#1aa66b;--warn:#c77700;--error:#d83b3b;--shadow:#141e3229;--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:#00000080;--screen-bg:#000}}*{box-sizing:border-box;-webkit-tap-highlight-color:transparent}html,body{background:var(--bg);height:100%;color:var(--text);overscroll-behavior:none;margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,system-ui,sans-serif;overflow:hidden}#app{-webkit-user-select:none;user-select:none;position:fixed;inset:0}input,textarea{-webkit-user-select:text;user-select:text}.wd-screen{background:var(--screen-bg);touch-action:none;width:100%;height:100%;display:block;position:absolute;inset:0}.wd-statusbar{top:calc(env(safe-area-inset-top,0px) + 8px);background:var(--panel);border:1px solid var(--border);max-width:calc(100% - 64px);min-height:38px;box-shadow:0 2px 10px var(--shadow);z-index:5;cursor:pointer;border-radius:10px;align-items:center;gap:8px;padding:6px 10px;font-size:12px;transition:max-width .25s,padding .25s;display:flex;position:absolute;left:8px}.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{background:var(--muted);border-radius:50%;flex:none;width:9px;height:9px}.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);white-space:nowrap;text-overflow:ellipsis;flex:auto;min-width:0;font-weight:600;overflow:hidden}.wd-watchers{color:var(--warn);white-space:nowrap;flex:none;font-weight:700}.wd-transport{letter-spacing:.4px;color:#fff;background:var(--accent-2);text-transform:uppercase;border-radius:6px;flex:none;padding:2px 6px;font-size:11px;font-weight:800}.wd-transport.hidden{display:none}.wd-transport[data-kind=turn]{color:#111;background:#e0a800}.wd-transport[data-kind=stun],.wd-transport[data-kind=direct]{background:var(--accent-2)}.wd-transport[data-kind=lan]{background:#3b82f6}.wd-bell{top:calc(env(safe-area-inset-top,0px) + 8px);z-index:6;border:1px solid var(--border);background:var(--panel);min-width:38px;min-height:38px;color:var(--text);box-shadow:0 2px 10px var(--shadow);cursor:pointer;border-radius:10px;place-items:center;padding:6px;display:grid;position:absolute;right:8px}.wd-badge{background:var(--accent);color:#fff;text-align:center;min-width:18px;height:18px;box-shadow:0 1px 4px var(--shadow);border-radius:9px;padding:0 5px;font-size:11px;font-weight:700;line-height:18px;position:absolute;top:-6px;right:-6px}.wd-ribbon{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);position:absolute;bottom:0;left:0;right:0}.wd-panel{flex-direction:column;display:flex}.wd-options{border-bottom:1px solid var(--border);padding:6px}.wd-pane{flex-wrap:wrap;justify-content:center;align-items:center;gap:5px;display:flex}.wd-pane-col{flex-direction:column;align-items:stretch;gap:8px}.wd-pane-single-row{scrollbar-width:none;flex-wrap:nowrap;justify-content:center;align-items:stretch;gap:4px;overflow-x:auto}.wd-pane-single-row::-webkit-scrollbar{display:none}.wd-pane-single-row .wd-group{flex:0 auto;gap:2px;min-width:0;padding:3px 4px}.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{gap:4px;padding:6px 9px;font-size:12px}@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{border:1px solid var(--border);width:min(880px,94vw);box-shadow:0 10px 34px var(--shadow);border-radius:16px;padding-bottom:0;bottom:16px;left:50%;right:auto;transform:translate(-50%)}.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{border:1px solid var(--border);background:var(--panel-2);border-radius:12px;flex-direction:column;align-items:stretch;gap:3px;padding:4px 6px;display:inline-flex}.wd-group-label{letter-spacing:.03em;text-transform:uppercase;color:var(--muted);white-space:nowrap;text-align:center;font-size:9px;font-weight:700}.wd-group-head{justify-content:space-between;align-items:center;gap:4px;display:flex}.wd-mode-toggle{border:1px solid var(--border);background:var(--panel);border-radius:999px;display:inline-flex;overflow:hidden}.wd-mode-btn{color:var(--muted);cursor:pointer;background:0 0;border:0;padding:4px 7px;font-size:10px;font-weight:700;line-height:1}.wd-mode-btn.on{background:var(--accent);color:#fff}.wd-group-items{flex-wrap:wrap;justify-content:center;align-items:center;gap:3px;display:flex}.wd-wrap{flex-wrap:wrap;justify-content:center;gap:6px;display:flex}.wd-btn-label{line-height:1}.wd-row{flex-wrap:wrap;justify-content:center;align-items:center;gap:6px;display:flex}.wd-tabs{align-items:center;gap:4px;padding:5px 6px;display:flex}.wd-tab{min-height:48px;color:var(--muted);cursor:pointer;touch-action:manipulation;background:0 0;border:1px solid #0000;border-radius:10px;flex-direction:column;flex:1;align-items:center;gap:2px;padding:6px 4px;font-size:11px;font-weight:600;display:flex}.wd-tab .wd-tab-label{line-height:1}.wd-tab.on{background:var(--panel-2);color:var(--accent);border-color:var(--border)}.wd-collapse{border:1px solid var(--border);background:var(--panel);min-width:44px;min-height:48px;color:var(--text);cursor:pointer;border-radius:10px;flex:none;place-items:center;display:grid}.wd-btn{border:1px solid var(--border);background:var(--panel);min-width:40px;min-height:40px;color:var(--text);cursor:pointer;-webkit-user-select:none;user-select:none;touch-action:manipulation;white-space:nowrap;border-radius:10px;justify-content:center;align-items:center;gap:7px;padding:6px 10px;font-size:13px;font-weight:600;line-height:1;display:inline-flex}.wd-btn svg{flex:none}.wd-btn.wd-icon-only{min-width:40px;padding:6px}.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{border:1px solid var(--border);border-radius:10px;display:inline-flex;overflow:hidden}.wd-seg-btn{background:var(--panel);min-height:44px;color:var(--muted);cursor:pointer;touch-action:manipulation;border:none;align-items:center;gap:6px;padding:8px 18px;font-size:15px;font-weight:600;display:inline-flex}.wd-seg-btn.on{background:var(--accent);color:#fff}.wd-zoom{text-align:center;font-variant-numeric:tabular-nums;min-width:52px;color:var(--text);font-weight:600}.wd-hint{text-align:center;color:var(--muted);flex-basis:100%;font-size:12px}.wd-type-input{resize:vertical;border:1px solid var(--border);background:var(--panel-2);width:100%;min-height:46px;color:var(--text);border-radius:10px;padding:10px;font-family:inherit;font-size:16px}.wd-keys-row{flex-wrap:wrap;justify-content:center;gap:6px;display:flex}.wd-type-actions{gap:6px;display:flex}.wd-type-actions .wd-btn{flex:1}.wd-monitor-list{flex-wrap:wrap;justify-content:center;gap:6px;display:flex}.wd-toasts{top:calc(env(safe-area-inset-top,0px) + 36px);z-index:9;pointer-events:none;flex-direction:column;gap:8px;display:flex;position:absolute;left:8px;right:8px}.wd-toast{background:var(--panel);border:1px solid var(--border);border-left-width:4px;border-radius:10px;flex-direction:column;gap:2px;padding:10px 12px;transition:opacity .3s,transform .3s;display:flex;box-shadow:0 6px 20px #0006}.wd-toast strong{font-size:14px}.wd-toast span{color:var(--muted);font-size:13px}.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-sharpen{pointer-events:none;z-index:15;border:2.5px solid #ebf0f840;border-top-color:#ebf0f8e6;border-radius:50%;width:22px;height:22px;margin:-11px 0 0 -11px;animation:.9s linear infinite wd-sharpen-spin;position:fixed;top:50%;left:50%;box-shadow:0 0 0 4px #06090e59}@keyframes wd-sharpen-spin{to{transform:rotate(360deg)}}.wd-pin-overlay{z-index:20;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);background:#06090eeb;place-items:center;padding:20px;display:grid;position:absolute;inset:0}.wd-pin-card{background:var(--panel);border:1px solid var(--border);text-align:center;border-radius:14px;width:100%;max-width:320px;padding:24px}.wd-pin-whip{object-fit:contain;width:64px;height:64px;margin:0 auto 12px;display:block}.wd-pin-card h2{margin:0 0 8px;font-size:20px}.wd-pin-msg{color:var(--muted);margin:0 0 16px;font-size:14px}.wd-pin-msg.err{color:var(--error)}.wd-pin-input{border:1px solid var(--border);background:var(--panel-2);width:100%;color:var(--text);text-align:center;letter-spacing:6px;border-radius:10px;margin-bottom:14px;padding:14px;font-size:22px}.wd-pin-submit{background:var(--accent);color:#fff;cursor:pointer;touch-action:manipulation;border:none;border-radius:12px;width:100%;min-height:50px;font-size:17px;font-weight:700}.wd-pin-submit:active{filter:brightness(.92);transform:translateY(1px)}.wd-pin-back{color:var(--muted);cursor:pointer;background:0 0;border:none;margin:14px auto 0;padding:6px 10px;font-size:13px;font-weight:600;display:block}.wd-pin-back:active{color:var(--text)}.wd-connecting{z-index:18;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);background:#000000f5;place-items:center;padding:20px;display:grid;position:absolute;inset:0}.wd-connecting-card{text-align:center;width:100%;max-width:320px}.wd-connecting-anim{background:#000;border-radius:10px;width:180px;height:auto;margin:0 auto 16px;display:block}.wd-connecting-msg{color:#eef2f7;text-shadow:0 1px 2px #00000080;margin:0;font-size:15px;font-weight:600}.wd-dialog-overlay{z-index:18;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);background:#06090e99;place-items:center;padding:16px;display:grid;position:absolute;inset:0}.wd-dialog{background:var(--panel);border:1px solid var(--border);border-radius:14px;width:100%;max-width:380px;padding:16px}.wd-dialog-head{justify-content:space-between;align-items:center;display:flex}.wd-dialog-head h2{margin:0;font-size:18px}.wd-dialog-x{border:1px solid var(--border);background:var(--panel-2);width:36px;height:36px;color:var(--text);cursor:pointer;border-radius:8px;place-items:center;display:grid}.wd-dialog-help{color:var(--muted);margin:8px 0 12px;font-size:13px}.wd-help-intro{margin:0 0 6px}.wd-help-list{flex-direction:column;gap:5px;margin:0 0 8px;padding-left:18px;display:flex}.wd-help-list strong{color:var(--text)}.wd-help-note{margin:0;font-style:italic}.wd-watch-list{flex-direction:column;gap:8px;margin-bottom:12px;display:flex}.wd-watch-row{border:1px solid var(--border);background:var(--panel-2);border-radius:10px;align-items:center;gap:8px;padding:8px 10px;display:flex}.wd-watch-name{text-align:left;color:var(--text);cursor:pointer;background:0 0;border:none;flex:1;padding:4px 0;font-family:inherit;font-size:14px;font-weight:600}.wd-watch-name:active{color:var(--accent)}.wd-timer-info{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.wd-timer-name{color:var(--text);align-items:center;gap:6px;font-size:14px;font-weight:600;display:flex}.wd-timer-name svg{color:var(--accent);flex:none}.wd-timer-remain{color:var(--muted);font-variant-numeric:tabular-nums;font-size:12px}.wd-timer-tag{letter-spacing:.5px;text-transform:uppercase;color:#fff;background:var(--accent);border-radius:6px;flex:none;padding:2px 6px;font-size:10px;font-weight:800}.wd-dialog-actions{gap:8px;margin-top:4px;display:flex}.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{letter-spacing:.4px;text-transform:uppercase;color:#fff;background:var(--muted);border-radius:6px;flex:none;padding:2px 6px;font-size:10px;font-weight:800}.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{justify-content:space-between;align-items:center;gap:8px;margin-bottom:6px;display:flex}.wd-mon-pick{flex-direction:column;gap:6px;max-height:40vh;margin-bottom:12px;display:flex;overflow-y:auto}.wd-mon-item{border:1px solid var(--border);background:var(--panel);width:100%;color:var(--text);cursor:pointer;text-align:left;border-radius:10px;justify-content:space-between;align-items:center;gap:10px;padding:9px 11px;display:flex}.wd-mon-item.on{border-color:var(--accent);box-shadow:inset 0 0 0 1px var(--accent)}.wd-mon-item-main{align-items:center;gap:8px;min-width:0;display:flex}.wd-mon-item-main svg{color:var(--accent);flex:none}.wd-mon-item-title{white-space:nowrap;text-overflow:ellipsis;font-size:13px;font-weight:600;overflow:hidden}.wd-mon-always{flex-direction:column;gap:8px;display:flex}.wd-check{color:var(--text);cursor:pointer;align-items:flex-start;gap:8px;font-size:13px;display:flex}.wd-check input{width:16px;height:16px;accent-color:var(--accent);flex:none;margin-top:1px}.wd-check-text{flex-direction:column;gap:2px;display:flex}.wd-check-sub{color:var(--muted);font-size:11px}.wd-form-row{flex-direction:column;gap:5px;margin-bottom:12px;display:flex}.wd-form-label{color:var(--muted);font-size:12px;font-weight:700}.wd-input{border:1px solid var(--border);background:var(--panel-2);width:100%;color:var(--text);border-radius:10px;padding:10px 12px;font-family:inherit;font-size:15px}.wd-input-area{resize:vertical;min-height:64px}.wd-form-duration{flex-wrap:wrap;align-items:center;gap:12px;display:flex}.wd-input-num{text-align:center;width:56px}.wd-form-unit{color:var(--muted);font-size:14px;font-weight:600}.wd-preset-row{flex-wrap:wrap;gap:6px;margin-bottom:10px;display:flex}.wd-preset{border:1px solid var(--border);background:var(--panel-2);min-width:48px;color:var(--text);cursor:pointer;border-radius:999px;flex:auto;padding:9px 10px;font-family:inherit;font-size:13px;font-weight:700}.wd-preset:active{background:var(--accent);color:#fff;border-color:#0000}.wd-stepper{align-items:center;gap:4px;display:flex}.wd-step-btn{border:1px solid var(--border);background:var(--panel-2);width:38px;height:38px;color:var(--text);cursor:pointer;touch-action:manipulation;border-radius:10px;flex:none;font-family:inherit;font-size:22px;font-weight:700;line-height:1}.wd-step-btn:active{background:var(--accent);color:#fff;border-color:#0000}.wd-form-target{flex-wrap:wrap;align-items:center;gap:10px;display:flex}.wd-form-target-status{color:var(--muted);font-size:13px}.wd-form-target-status.set{color:var(--accent-2);font-weight:700}.wd-pick-banner{top:calc(env(safe-area-inset-top,0px) + 56px);z-index:30;background:var(--accent);color:#fff;box-shadow:0 6px 20px var(--shadow);pointer-events:none;text-align:center;border-radius:999px;max-width:90%;padding:10px 16px;font-size:13px;font-weight:700;position:absolute;left:50%;transform:translate(-50%)}.wd-conn-row{border-bottom:1px solid var(--border);align-items:flex-start;gap:10px;padding:10px 0;display:flex}.wd-conn-label{width:92px;color:var(--muted);flex:none;padding-top:2px;font-size:13px;font-weight:700}.wd-conn-value{color:var(--text);word-break:break-word;flex:1;font-size:15px;font-weight:600}.wd-conn-route{flex-direction:column;flex:1;gap:4px;display:flex}.wd-conn-route .wd-transport{align-self:flex-start;position:static}.wd-conn-desc{color:var(--muted);font-size:12.5px}.wd-conn-status{flex:1;align-items:center;gap:8px;display:flex}.wd-conn-error{border:1px solid var(--error);color:var(--error);word-break:break-word;background:#dc35451f;border-radius:10px;margin-top:12px;padding:9px 11px;font-size:12.5px;font-weight:600}.wd-disconnect{background:var(--error);color:#fff;border-color:#0000;justify-content:center;width:100%;min-height:46px;margin-top:14px;font-size:15px;font-weight:700}.wd-support-link{border:1px solid var(--border);color:var(--muted);cursor:pointer;background:0 0;border-radius:999px;align-self:flex-start;align-items:center;gap:6px;margin-top:8px;padding:5px 12px;font-size:12.5px;font-weight:600;transition:color .15s,border-color .15s;display:inline-flex}.wd-support-link:hover,.wd-support-link:active{color:var(--accent);border-color:var(--accent)}.wd-support-link svg{flex:none;width:14px;height:14px}.wd-conn-feedback{border-top:1px solid var(--border);text-align:center;margin-top:14px;padding-top:12px}.wd-conn-feedback-text{color:var(--muted);margin:0;font-size:12.5px}.wd-conn-feedback-links{justify-content:center;gap:10px;margin-top:9px;display:flex}.wd-conn-feedback-link{border:1px solid var(--border);color:var(--text);background:0 0;border-radius:999px;align-items:center;gap:7px;padding:7px 16px;font-size:13px;font-weight:600;text-decoration:none;transition:color .15s,border-color .15s;display:inline-flex}.wd-conn-feedback-link:hover,.wd-conn-feedback-link:active{color:var(--accent);border-color:var(--accent)}.wd-conn-feedback-link svg{flex:none;width:16px;height:16px}.wd-pin-support{margin-top:16px}.wd-placing .wd-ribbon,.wd-placing .wd-statusbar,.wd-placing .wd-bell{display:none}.wd-place-layer{z-index:40;touch-action:none;background:0 0;position:absolute;inset:0}.wd-place-marker{z-index:41;pointer-events:none;border:2px solid #4ea1ff;border-radius:50%;width:46px;height:46px;position:absolute;transform:translate(-50%,-50%);box-shadow:0 0 0 2px #00000080,0 0 14px #4ea1ffb3}.wd-place-marker:before,.wd-place-marker:after{content:"";background:#4ea1ff;position:absolute}.wd-place-marker:before{width:2px;top:-8px;bottom:-8px;left:50%;transform:translate(-50%)}.wd-place-marker:after{height:2px;top:50%;left:-8px;right:-8px;transform:translateY(-50%)}.wd-place-bar{z-index:42;padding:12px 14px calc(env(safe-area-inset-bottom,0px) + 12px);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);border-top:1px solid var(--border);background:#0a0e14f0;flex-direction:column;gap:10px;display:flex;position:absolute;bottom:0;left:0;right:0}.wd-place-hint{color:#eef2f7;text-align:center;margin:0;font-size:13px;font-weight:600}.wd-place-text{width:100%}.wd-place-buttons{gap:8px;display:flex}.wd-place-buttons .wd-btn{flex:1;justify-content:center}.wd-perm-row{border:1px solid var(--border);background:var(--panel-2);border-radius:10px;align-items:center;gap:8px;margin-bottom:12px;padding:8px 10px;display:flex}.wd-perm-dot{background:var(--muted);border-radius:50%;flex:none;width:9px;height:9px}.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{color:var(--muted);flex:1;font-size:12px}.wd-perm-enable{flex:none;min-width:auto;min-height:32px;padding:4px 12px;font-size:12px}.wd-dialog .wd-go{justify-content:center;width:100%}.wd-selector{z-index:19;border:2px solid var(--accent);touch-action:none;background:#2f6fed24;border-radius:6px;position:absolute;box-shadow:0 0 0 9999px #06090e59}.wd-selector-move{background:var(--accent);color:#fff;cursor:move;touch-action:none;border-radius:8px 0;place-items:center;width:38px;height:38px;display:grid;position:absolute;top:-1px;left:-1px}.wd-selector-handle{background:var(--accent);border:3px solid var(--panel);cursor:nwse-resize;touch-action:none;border-radius:50%;width:28px;height:28px;position:absolute;bottom:-12px;right:-12px}.wd-selector-bar{white-space:nowrap;gap:8px;display:flex;position:absolute;bottom:-56px;left:50%;transform:translate(-50%)}.wd-selector-info{top:calc(env(safe-area-inset-top,0px) + 52px);z-index:20;background:var(--panel);border:1px solid var(--border);border-left:4px solid var(--accent);max-width:430px;box-shadow:0 6px 20px var(--shadow);color:var(--text);text-align:center;pointer-events:none;border-radius:10px;margin:0 auto;padding:8px 12px;font-size:12.5px;line-height:1.35;position:absolute;left:12px;right:12px}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./index.esm-mXKu2C3o.js","./index.esm-B3ZTr43m.js","./index.esm-CmSagqpw.js","./index.esm-wogdVWd2.js","./index.esm-BgjPHsdM.js","./index.esm-Di8jSGaG.js"])))=>i.map(i=>d[i]);
|
|
2
|
+
(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();function e(e){return typeof e==`object`&&!!e&&typeof e.type==`string`}var t=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 n(e){return new TextEncoder().encode(e)}function r(e){let n=new Uint32Array([1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225]),r=e.length*8,i=new Uint8Array((e.length+8>>6)+1<<6);i.set(e),i[e.length]=128;let a=new DataView(i.buffer);a.setUint32(i.length-4,r>>>0,!1),a.setUint32(i.length-8,Math.floor(r/4294967296),!1);let o=new Uint32Array(64);for(let e=0;e<i.length;e+=64){for(let t=0;t<16;t++)o[t]=a.getUint32(e+t*4,!1);for(let e=16;e<64;e++){let t=o[e-15],n=o[e-2],r=(t>>>7|t<<25)^(t>>>18|t<<14)^t>>>3,i=(n>>>17|n<<15)^(n>>>19|n<<13)^n>>>10;o[e]=o[e-16]+r+o[e-7]+i>>>0}let[r,i,s,c,l,u,d,f]=n;for(let e=0;e<64;e++){let n=(l>>>6|l<<26)^(l>>>11|l<<21)^(l>>>25|l<<7),a=l&u^~l&d,p=f+n+a+t[e]+o[e]>>>0,m=((r>>>2|r<<30)^(r>>>13|r<<19)^(r>>>22|r<<10))+(r&i^r&s^i&s)>>>0;f=d,d=u,u=l,l=c+p>>>0,c=s,s=i,i=r,r=p+m>>>0}n[0]=n[0]+r>>>0,n[1]=n[1]+i>>>0,n[2]=n[2]+s>>>0,n[3]=n[3]+c>>>0,n[4]=n[4]+l>>>0,n[5]=n[5]+u>>>0,n[6]=n[6]+d>>>0,n[7]=n[7]+f>>>0}let s=new Uint8Array(32);new DataView(s.buffer).setUint32(0,n[0],!1);for(let e=0;e<8;e++)new DataView(s.buffer).setUint32(e*4,n[e],!1);return s}var i=`0123456789abcdef`;function a(e){let t=``;for(let n of e)t+=i[n>>4]+i[n&15];return t}function o(e){return a(r(n(e)))}function s(e,t,n){let r=o(`${t}:${e}`);for(let e=1;e<n;e++)r=o(r);return r}function c(e,t){return o(`${e}:${t}`)}var l=class{token;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;constructor(e){this.token=e}on(e,t){this.handlers[e].add(t)}emit(e,t){for(let n of this.handlers[e])n(t)}setSender(e){this.sender=e}send(e){try{this.sender(JSON.stringify(e))}catch{}}sendHello(){this.send({type:`hello`,protocol:1,token:this.token,role:`controller`,client:{userAgent:navigator.userAgent}})}submitPin(e){if(this.rememberedPin=e,!this.challenge)return;let{salt:t,iterations:n,nonce:r}=this.challenge,i=s(e,t,n);this.send({type:`auth`,response:c(i,r)})}setVisible(e){this.send({type:`visibility`,visible:e})}handleText(t){let n;try{n=JSON.parse(t)}catch{return}if(!e(n))return;let r=n;switch(r.type){case`welcome`:this.challenge=null,r.protocol!==1&&this.emit(`versionMismatch`,{agentProtocol:r.protocol,clientProtocol:1,agentVersion:r.agent?.version}),this.emit(`welcome`,r),this.emit(`timers`,r.timers??[]),this.emit(`monitors`,r.monitors??[]),this.emit(`monitorAlways`,r.alwaysAgents??[]);break;case`auth-required`:this.challenge={salt:r.salt,iterations:r.iterations,nonce:r.nonce},this.rememberedPin&&!this.wrongPin?this.submitPin(this.rememberedPin):this.emit(`pinRequired`,{attemptsLeft:r.attemptsLeft,retry:this.wrongPin}),this.wrongPin=!1;break;case`screen-meta`:this.emit(`screenMeta`,{screen:r.screen,activeDisplay:r.activeDisplay});break;case`screen-region`:this.emit(`screenRegion`,{x:r.x,y:r.y,w:r.w,h:r.h,active:r.active});break;case`notification`:this.emit(`notification`,r);break;case`presence`:this.emit(`presence`,r.watchers);break;case`watchers`:this.emit(`watchers`,r.regions);break;case`timers`:this.emit(`timers`,r.timers);break;case`monitors`:this.emit(`monitors`,r.monitors);break;case`monitor-always-agents`:this.emit(`monitorAlways`,r.agents);break;case`monitor-sessions`:this.emit(`monitorSessions`,r.sessions);break;case`error`:r.code===`pin`?(this.wrongPin=!0,this.rememberedPin=null):this.emit(`error`,r.message);break;case`pong`:break}}},u=class{url;core;ws=null;pc=null;dc=null;mainXcv=null;overviewXcv=null;reconnectTimer=0;statsTimer=0;closedByUser=!1;constructor(e,t){this.url=e,this.core=new l(t),this.core.setSender(e=>{if(this.dc&&this.dc.readyState===`open`)try{this.dc.send(e)}catch{}})}on(e,t){this.core.on(e,t)}send(e){this.core.send(e)}submitPin(e){this.core.submitPin(e)}setVisible(e){this.core.setVisible(e)}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();let e=new WebSocket(this.url);this.ws=e;let t=t=>{if(e.readyState===WebSocket.OPEN)try{e.send(JSON.stringify(t))}catch{}},n=new RTCPeerConnection({iceServers:[]});this.pc=n;let r=n.createDataChannel(`whipdesk`);r.binaryType=`arraybuffer`,this.dc=r,r.onopen=()=>{this.core.sendHello(),this.core.emit(`status`,`connected`),this.core.emit(`transport`,`LAN`),this.startStatsPoll(n)},r.onclose=()=>this.core.emit(`status`,`disconnected`),r.onmessage=e=>{typeof e.data==`string`&&this.core.handleText(e.data)};try{this.mainXcv=n.addTransceiver(`video`,{direction:`recvonly`}),this.overviewXcv=n.addTransceiver(`video`,{direction:`recvonly`})}catch{}n.ontrack=e=>{let t=e.streams[0]??new MediaStream([e.track]);this.overviewXcv&&e.transceiver===this.overviewXcv?this.core.emit(`overviewTrack`,t):this.core.emit(`videoTrack`,t)},n.onicecandidate=e=>{e.candidate&&t({kind:`candidate`,candidate:e.candidate.toJSON()})},n.onconnectionstatechange=()=>{let e=n.connectionState;(e===`failed`||e===`disconnected`||e===`closed`)&&(this.core.emit(`status`,`disconnected`),this.scheduleReconnect())};let i=!1;e.onopen=async()=>{try{let e=await n.createOffer();await n.setLocalDescription(e),t({kind:`offer`,sdp:n.localDescription?.sdp??``})}catch{}},e.onmessage=e=>{let t;try{t=JSON.parse(typeof e.data==`string`?e.data:``)}catch{return}if(t.kind===`answer`&&t.sdp&&!i)i=!0,n.setRemoteDescription({type:`answer`,sdp:t.sdp});else if(t.kind===`candidate`&&t.candidate)try{n.addIceCandidate(t.candidate)}catch{}else t.kind===`error`&&this.core.emit(`error`,String(t.message??`host error`))},e.onclose=()=>{this.core.emit(`status`,`disconnected`),this.scheduleReconnect()},e.onerror=()=>{}}startStatsPoll(e){window.clearInterval(this.statsTimer),this.statsTimer=window.setInterval(async()=>{let t=null,n=0;try{(await e.getStats()).forEach(e=>{e.type===`inbound-rtp`&&e.kind===`video`?n=Math.max(n,Number(e.framesPerSecond??0)):e.type===`candidate-pair`&&e.nominated&&typeof e.currentRoundTripTime==`number`&&(t=e.currentRoundTripTime)})}catch{return}this.core.emit(`netStats`,{fps:Math.round(n),rtt:t==null?null:Math.round(t*1e3)})},1e3)}},d=``+new URL(`loading-whip-anim-DmlnYIJ-.gif`,import.meta.url).href,f=[`Rounding up your AI agents…`,`Connecting to your agent farm…`,`Uncoiling the whip…`,`Negotiating the fastest route…`,`Tightening the leash…`,`Almost in the saddle…`],p=class{overlay;msg;rotateTimer=0;msgIndex=0;constructor(e){this.overlay=document.createElement(`div`),this.overlay.className=`wd-connecting hidden`;let t=document.createElement(`div`);t.className=`wd-connecting-card`;let n=document.createElement(`img`);n.className=`wd-connecting-anim`,n.src=d,n.alt=`WhipDesk`,n.decoding=`async`,this.msg=document.createElement(`p`),this.msg.className=`wd-connecting-msg`,this.msg.textContent=f[0],t.append(n,this.msg),this.overlay.appendChild(t),e.appendChild(this.overlay)}show(e){this.msg.textContent=e??f[this.msgIndex],this.overlay.classList.contains(`hidden`)&&(this.overlay.classList.remove(`hidden`),!e&&(this.rotateTimer=window.setInterval(()=>{this.msgIndex=(this.msgIndex+1)%f.length,this.msg.textContent=f[this.msgIndex]},2200)))}hide(){this.overlay.classList.add(`hidden`),window.clearInterval(this.rotateTimer),this.rotateTimer=0}},m={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"/>`},h=`http://www.w3.org/2000/svg`;function g(e,t=20){let n=document.createElementNS(h,`svg`);return n.setAttribute(`viewBox`,`0 0 24 24`),n.setAttribute(`width`,String(t)),n.setAttribute(`height`,String(t)),n.setAttribute(`fill`,`none`),n.setAttribute(`stroke`,`currentColor`),n.setAttribute(`stroke-width`,`2`),n.setAttribute(`stroke-linecap`,`round`),n.setAttribute(`stroke-linejoin`,`round`),n.setAttribute(`aria-hidden`,`true`),n.innerHTML=m[e],n}var _=[`en`],v=`en`;function y(){try{let e=localStorage.getItem(`wd-locale`);if(e&&_.includes(e))return e}catch{}let e=navigator.languages?.length?navigator.languages:[navigator.language];for(let t of e){let e=(t||``).toLowerCase().split(`-`)[0]??``;if(_.includes(e))return e}return v}function b(){return location.hostname.endsWith(`whipdesk.com`)?``:`https://whipdesk.com`}function x(){return`${b()}/${y()}/dashboard/`}function S(e){let t=e?`?next=${encodeURIComponent(e)}`:``;return`${b()}/${y()}/sign-in/${t}`}var C=`https://donate.stripe.com/6oU5kE19N5v35652n88so01`,w=`https://github.com/BinaryBananaLLC/WhipDesk/`,T=`https://www.reddit.com/r/WhipDesk/`,E=[[`Esc`,`Escape`],[`Tab`,`Tab`],[`⌫`,`Backspace`],[`⏎`,`Enter`],[`←`,`ArrowLeft`],[`↑`,`ArrowUp`],[`↓`,`ArrowDown`],[`→`,`ArrowRight`]];function D(e,t,n){let r=document.createElement(e);return t&&(r.className=t),n!==void 0&&(r.textContent=n),r}function O(e,...t){let n=D(`div`,`wd-group`);n.appendChild(D(`span`,`wd-group-label`,e));let r=D(`div`,`wd-group-items`);return r.append(...t),n.appendChild(r),n}function k(e,t){let n=0,r=0,i=()=>{window.clearTimeout(n),window.clearInterval(r)};e.addEventListener(`pointerdown`,e=>{e.preventDefault(),t(),n=window.setTimeout(()=>{r=window.setInterval(t,80)},350)});for(let t of[`pointerup`,`pointercancel`,`pointerleave`])e.addEventListener(t,i);return e}function A(e,t=`wd-btn`){return D(`button`,t,e)}function j(e,t=``,n=`wd-btn`){let r=D(`button`,n);if(r.appendChild(g(e)),t){let e=D(`span`,`wd-btn-label`,t);r.appendChild(e)}else r.classList.add(`wd-icon-only`),r.setAttribute(`aria-label`,e);return r}function ee(e,t,n){let r=D(`a`,`wd-conn-feedback-link`);return r.href=n,r.target=`_blank`,r.rel=`noopener noreferrer`,r.append(g(e,16),D(`span`,void 0,t)),r}function te(e){switch(e.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 M(e){return e.toUpperCase()===`TURN`?`$ ${e}`:e}var ne=class{root;deps;statusDot=D(`span`,`wd-dot`);statusText=D(`span`,`wd-status-text`,`Connecting…`);transportBadge=D(`span`,`wd-transport hidden`);watchersText=D(`span`,`wd-watchers`,``);alertBadge=D(`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;constructor(e,t){this.root=e,this.deps=t,this.build()}setStatus(e){this.status=e,this.statusDot.dataset.status=e,e===`connected`&&(this.lastError=``),this.renderStatusText(),this.updateStatusCollapse(),this.connectionOverlay&&!this.connectionOverlay.classList.contains(`hidden`)&&this.renderConnection()}setAlertCount(e){this.alertBadge.textContent=e>0?String(e):``,this.alertBadge.classList.toggle(`hidden`,e<=0)}setTransport(e){this.transport=e,this.transportBadge.textContent=M(e),this.transportBadge.dataset.kind=e.toLowerCase(),this.transportBadge.classList.toggle(`hidden`,!e),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(e){this.deviceName=e.agent.hostname,this.renderStatusText(),this.displays=e.displays??[],this.activeDisplay=e.activeDisplay??0,this.renderMonitors(),e.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(e){this.activeDisplay=e,this.renderMonitors()}setPresence(e){this.presenceCount=e,this.watchersText.textContent=e>1?`● ${e} watching`:``,this.connectionOverlay&&!this.connectionOverlay.classList.contains(`hidden`)&&this.renderConnection()}buildConnectionDialog(){let e=D(`div`,`wd-dialog-overlay hidden`);e.addEventListener(`pointerdown`,t=>{t.target===e&&e.classList.add(`hidden`)});let t=D(`div`,`wd-dialog`),n=D(`div`,`wd-dialog-head`);n.append(D(`h2`,``,`Connection`));let r=D(`button`,`wd-dialog-x`);r.appendChild(g(`x`)),r.onclick=()=>e.classList.add(`hidden`),n.appendChild(r);let i=D(`div`,`wd-conn-row`);i.append(D(`span`,`wd-conn-label`,`Status`));let a=D(`div`,`wd-conn-status`);this.connStatusDot=D(`span`,`wd-dot`),this.connStatusText=D(`span`,`wd-conn-value`,`—`),a.append(this.connStatusDot,this.connStatusText),i.appendChild(a);let o=D(`div`,`wd-conn-row`);o.append(D(`span`,`wd-conn-label`,`Machine`)),this.connName=D(`span`,`wd-conn-value`,`—`),o.appendChild(this.connName);let s=D(`div`,`wd-conn-row`);s.append(D(`span`,`wd-conn-label`,`Connection`)),this.connRoute=D(`div`,`wd-conn-route`),s.appendChild(this.connRoute);let c=D(`div`,`wd-conn-row`);c.append(D(`span`,`wd-conn-label`,`Viewers`)),this.connPresence=D(`span`,`wd-conn-value`,`1`),c.appendChild(this.connPresence);let l=D(`div`,`wd-conn-row`);l.append(D(`span`,`wd-conn-label`,`Speed (FPS/latency)`)),this.connSpeed=D(`span`,`wd-conn-value`,`—`),l.appendChild(this.connSpeed),this.connError=D(`div`,`wd-conn-error hidden`);let u=D(`button`,`wd-btn wd-disconnect`);u.append(g(`power`),D(`span`,`wd-btn-label`,`Disconnect`)),u.onclick=()=>this.disconnect();let d=D(`div`,`wd-conn-feedback`);d.append(D(`p`,`wd-conn-feedback-text`,`Noticed an issue or have an idea? Reach out:`));let f=D(`div`,`wd-conn-feedback-links`);f.append(ee(`reddit`,`Reddit`,T),ee(`github`,`GitHub`,w)),d.appendChild(f),t.append(n,i,s,l,o,c,this.connError,u,d),e.appendChild(t),this.root.appendChild(e),this.connectionOverlay=e}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){let e=D(`span`,`wd-transport`);if(e.textContent=M(this.transport),e.dataset.kind=this.transport.toLowerCase(),this.connRoute.append(e,D(`span`,`wd-conn-desc`,te(this.transport))),this.transport.toLowerCase()===`turn`){let e=D(`button`,`wd-support-link`);e.append(g(`heart`,14),D(`span`,void 0,`Support WhipDesk`)),e.onclick=()=>window.open(C,`_blank`,`noopener`),this.connRoute.append(e)}}else this.connRoute.append(D(`span`,`wd-conn-desc`,this.status===`connected`?`Detecting route…`:`Connecting…`));this.renderSpeed()}renderSpeed(){let e=`${this.netFps} FPS`;this.connSpeed.textContent=this.netRtt==null?e:`${e} / ${this.netRtt} ms`}setNetStats(e,t){this.netFps=e,this.netRtt=t,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=x()}flashError(e){this.lastError=e,this.connectionOverlay&&!this.connectionOverlay.classList.contains(`hidden`)&&this.renderConnection(),this.deps.notifications.show({type:`notification`,id:`err-${Date.now()}`,title:`WhipDesk`,body:e,level:`warning`,source:`client`,t:Date.now()})}build(){let e=D(`div`,`wd-statusbar`);e.append(this.statusDot,this.statusText,this.transportBadge,this.watchersText),e.onclick=()=>this.openConnection(),this.statusbar=e,this.buildConnectionDialog();let t=j(`bell`,``,`wd-bell`);t.setAttribute(`aria-label`,`Auto-Whips`),t.title=`Auto-Whips`,t.appendChild(this.alertBadge),t.onclick=()=>this.deps.watchers.open(),this.root.append(e,t);let n=D(`div`,`wd-ribbon`);this.panel=D(`div`,`wd-panel`),this.optionsArea=D(`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(let e of this.tabPanes.values())this.optionsArea.appendChild(e);let r=D(`div`,`wd-tabs`),i=(e,t,n)=>{let i=D(`button`,`wd-tab`);i.appendChild(g(t,18)),i.appendChild(D(`span`,`wd-tab-label`,n)),i.onclick=()=>this.selectTab(e),this.tabButtons.set(e,i),r.appendChild(i)};i(`viewer`,`eye`,`Viewer`),i(`interact`,`mouse`,`Interact`),i(`type`,`keyboard`,`Type`),i(`monitor`,`monitor`,`Monitor`),this.collapseBtn=j(`chevron-down`,``,`wd-collapse`),this.collapseBtn.onclick=()=>this.setCollapsed(!this.collapsed),r.appendChild(this.collapseBtn),this.panel.append(this.optionsArea,r),n.appendChild(this.panel),this.root.appendChild(n),this.selectTab(`viewer`)}buildViewerPane(){let{view:e,input:t}=this.deps,n=D(`div`,`wd-pane wd-pane-single-row`),r=k(j(`minus`,``,`wd-btn wd-icon-only`),()=>e.zoomBy(.9)),i=k(j(`plus`,``,`wd-btn wd-icon-only`),()=>e.zoomBy(1.11)),a=k(j(`scroll-up`,``,`wd-btn wd-icon-only`),()=>t.scrollStep(-6)),o=k(j(`scroll-down`,``,`wd-btn wd-icon-only`),()=>t.scrollStep(6)),s=j(`hand`,``,`wd-btn wd-icon-only`);s.setAttribute(`aria-label`,`Drag to scroll`),s.title=`Drag to scroll`;let c=j(`drag`,``,`wd-btn wd-icon-only`);c.setAttribute(`aria-label`,`Pan the zoomed screen with one finger`),c.title=`Pan the zoomed screen with one finger`,s.onclick=()=>{let e=!t.getDragScroll();t.setDragScroll(e),s.classList.toggle(`on`,e),c.classList.toggle(`on`,t.getPan())},c.onclick=()=>{let e=!t.getPan();t.setPan(e),c.classList.toggle(`on`,e),s.classList.toggle(`on`,t.getDragScroll())};let l=j(`pointer`,`Click`,`wd-btn wd-go`);return l.onclick=()=>t.click(`left`),n.append(O(`Zoom`,r,i),O(`Pan`,c),O(`Scroll`,a,o,s),O(`Pointer`,l)),n}buildInteractPane(){let e=D(`div`,`wd-pane`);return this.interactHost=D(`div`,`wd-pane`),e.appendChild(this.interactHost),this.renderInteract(),e}renderInteract(){let{input:e}=this.deps;this.interactHost.replaceChildren();let t=D(`div`,`wd-group`),n=D(`div`,`wd-group-head`),r=D(`span`,`wd-group-label`,`Mode`),i=D(`div`,`wd-mode-toggle`),a=D(`button`,`wd-mode-btn`,`Mouse`),o=D(`button`,`wd-mode-btn`,`Touch`);a.classList.toggle(`on`,this.interactMode===`mouse`),o.classList.toggle(`on`,this.interactMode===`touch`),a.onclick=()=>{this.interactMode=`mouse`,this.activeTab===`interact`&&this.deps.input.setInteraction(`mouse`),this.renderInteract()},o.onclick=()=>{this.interactMode=`touch`,this.activeTab===`interact`&&this.deps.input.setInteraction(`touch`),this.renderInteract()},i.append(a,o),n.append(r,i);let s=D(`div`,`wd-group-items`);if(this.interactMode===`mouse`){let t=j(`mouse-left`,`Left`);t.onclick=()=>e.click(`left`);let n=j(`mouse-right`,`Right`);n.onclick=()=>e.click(`right`);let r=j(`double-click`,`Double`);r.onclick=()=>e.multiClick(2);let i=j(`drag`,`Drag`);i.onclick=()=>{let t=!e.getDragLock();e.setDragLock(t),i.classList.toggle(`on`,t)},s.append(t,n,r,i)}else{let t=j(`pointer`,`Tap`);t.onclick=()=>e.click(`left`);let n=j(`hand`,`Hold`);n.onclick=()=>e.longPress();let r=k(j(`scroll-up`,`Up`,`wd-btn`),()=>e.swipe(0,-.25)),i=k(j(`scroll-down`,`Down`,`wd-btn`),()=>e.swipe(0,.25)),a=A(`←`);a.onclick=()=>e.swipe(-.25,0);let o=A(`→`);o.onclick=()=>e.swipe(.25,0);let c=A(`2 fingers`);c.onclick=()=>e.click(`right`),s.append(t,n,r,i,a,o,c)}t.append(n,s),this.interactHost.append(t)}buildTypePane(){let{conn:e,input:t}=this.deps,n=D(`div`,`wd-pane wd-pane-col`);this.promptInput=D(`textarea`,`wd-type-input`),this.promptInput.placeholder=`Type to send to the focused app (URL, command, message…)`,this.promptInput.rows=2;let r=D(`div`,`wd-wrap`);for(let[t,n]of E){let i=A(t);i.onclick=()=>e.send({type:`key`,key:n}),r.appendChild(i)}let i=j(`pointer`,`1x click`);i.onclick=()=>t.click(`left`);let a=j(`double-click`,`2x click`);a.onclick=()=>t.multiClick(2);let o=j(`double-click`,`3x click`);o.onclick=()=>t.multiClick(3);let s=j(`insert`,`Insert`);s.onclick=()=>this.sendText(!1);let c=j(`send`,`Send`,`wd-btn wd-go`);return c.onclick=()=>this.sendText(!0),r.append(i,a,o,s,c),n.append(this.promptInput,r),n}buildMonitorPane(){let e=D(`div`,`wd-pane`);return this.monitorList=D(`div`,`wd-monitor-list`),e.appendChild(this.monitorList),this.renderMonitors(),e}renderMonitors(){if(this.monitorList){if(this.monitorList.replaceChildren(),this.displays.length===0){this.monitorList.appendChild(D(`span`,`wd-hint`,`Single display`));return}this.displays.forEach((e,t)=>{let n=A(`${t+1}. ${e.name}${e.primary?` ★`:``}`);n.classList.toggle(`on`,e.id===this.activeDisplay),n.onclick=()=>{this.deps.conn.send({type:`select-display`,id:e.id}),this.activeDisplay=e.id,this.renderMonitors()},this.monitorList.appendChild(n)})}}selectTab(e){if(this.activeTab!==null&&e===this.activeTab){this.setCollapsed(!this.collapsed);return}this.activeTab=e,this.collapsed&&this.setCollapsed(!1);for(let[t,n]of this.tabButtons)n.classList.toggle(`on`,t===e);for(let[t,n]of this.tabPanes)n.classList.toggle(`hidden`,t!==e);this.deps.input.setInteraction(e===`interact`?this.interactMode:`viewer`),e===`type`&&window.setTimeout(()=>this.promptInput.focus(),50)}setCollapsed(e){this.collapsed=e,this.optionsArea.classList.toggle(`hidden`,e),this.collapseBtn.replaceChildren(g(e?`chevron-up`:`chevron-down`))}sendText(e){let t=this.promptInput.value;t&&(this.deps.conn.send({type:`type`,text:t,submit:e}),this.promptInput.value=``)}};function N(e){return e<0?0:e>1?1:e}var P=2.5,F=250,re=500,ie=8,ae=class{canvas;view;conn;cb;interaction=`viewer`;dragLock=!1;dragScroll=!1;pan=!1;holdingLeft=!1;cursor={nx:.5,ny:.5};pointers=new Map;longPressTimer=0;twoFinger=null;suppressTap=!1;constructor(e,t,n,r={}){this.canvas=e,this.view=t,this.conn=n,this.cb=r,e.style.touchAction=`none`,e.addEventListener(`pointerdown`,e=>this.onDown(e)),e.addEventListener(`pointermove`,e=>this.onMove(e)),e.addEventListener(`pointerup`,e=>this.onUp(e)),e.addEventListener(`pointercancel`,e=>this.onUp(e)),e.addEventListener(`contextmenu`,e=>e.preventDefault()),this.view.setCursor(this.cursor.nx,this.cursor.ny)}setInteraction(e){this.interaction=e,e===`viewer`&&(this.dragLock=!1)}getInteraction(){return this.interaction}setCallbacks(e){this.cb=e}setDragLock(e){this.dragLock=e}getDragLock(){return this.dragLock}setDragScroll(e){this.dragScroll=e,e&&(this.pan=!1)}getDragScroll(){return this.dragScroll}setPan(e){this.pan=e,e&&(this.dragScroll=!1)}getPan(){return this.pan}isPanning(){return this.pan&&this.interaction===`viewer`}click(e,t=!1){this.send({type:`pointer`,action:`click`,button:e,double:t,x:this.cursor.nx,y:this.cursor.ny}),navigator.vibrate?.(15)}multiClick(e){for(let t=0;t<e;t++)this.send({type:`pointer`,action:`click`,button:`left`,x:this.cursor.nx,y:this.cursor.ny});navigator.vibrate?.(15)}longPress(e=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`}),e)}swipe(e,t){let n=N(this.cursor.nx),r=N(this.cursor.ny),i=N(this.cursor.nx+e),a=N(this.cursor.ny+t);this.send({type:`pointer`,action:`down`,button:`left`,x:n,y:r});for(let e=1;e<=6;e++){let t=e/6;window.setTimeout(()=>{this.send({type:`pointer`,action:`move`,x:n+(i-n)*t,y:r+(a-r)*t}),e===6&&(this.moveCursor(i,a),this.send({type:`pointer`,action:`up`,button:`left`}))},e*16)}navigator.vibrate?.(15)}scrollStep(e){this.send({type:`scroll`,dx:0,dy:e})}send(e){this.conn.send(e)}positionOf(e){let t=this.canvas.getBoundingClientRect();return{x:e.clientX-t.left,y:e.clientY-t.top}}moveCursor(e,t){this.cursor.nx=N(e),this.cursor.ny=N(t),this.view.setCursor(this.cursor.nx,this.cursor.ny),this.cb.onCursor?.(this.cursor.nx,this.cursor.ny)}onDown(e){this.canvas.setPointerCapture?.(e.pointerId);let t=this.positionOf(e);if(this.pointers.set(e.pointerId,{x:t.x,y:t.y,startX:t.x,startY:t.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(),re)),!this.dragScroll&&!this.isPanning()&&(this.interaction===`mouse`||this.interaction===`viewer`))){let e=this.view.canvasToNorm(t.x,t.y);this.moveCursor(e.nx,e.ny)}}onLongPress(){let e=[...this.pointers.values()][0];!e||e.moved||e.consumed||(e.consumed=!0,this.send({type:`pointer`,action:`click`,button:`right`,x:this.cursor.nx,y:this.cursor.ny}),navigator.vibrate?.(20))}beginTwoFinger(){let e=[...this.pointers.values()];if(e.length<2)return;let t=e[0],n=e[1];this.twoFinger={dist:Math.hypot(t.x-n.x,t.y-n.y),mx:(t.x+n.x)/2,my:(t.y+n.y)/2,start:performance.now(),moved:!1}}onMove(e){let t=this.pointers.get(e.pointerId);if(!t)return;let n=this.positionOf(e),r=t.x,i=t.y;if(t.x=n.x,t.y=n.y,Math.hypot(n.x-t.startX,n.y-t.startY)>ie&&(t.moved=!0),this.pointers.size>=2&&this.twoFinger){this.onTwoFingerMove();return}if(this.pointers.size!==1||t.consumed||!t.moved)return;window.clearTimeout(this.longPressTimer);let a=n.x-r,o=n.y-i;if(this.isPanning()){this.view.panByCanvasPixels(a,o);return}if(this.interaction===`touch`||this.dragScroll){this.send({type:`scroll`,dx:Math.round(-a/P),dy:Math.round(-o/P)});return}let s=this.view.canvasToNorm(n.x,n.y);this.moveCursor(s.nx,s.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(){let e=[...this.pointers.values()];if(e.length<2||!this.twoFinger)return;let t=e[0],n=e[1],r=Math.hypot(t.x-n.x,t.y-n.y),i=(t.x+n.x)/2,a=(t.y+n.y)/2,o=r-this.twoFinger.dist,s=i-this.twoFinger.mx,c=a-this.twoFinger.my;if(Math.abs(o)>6){this.twoFinger.moved=!0,this.view.zoomAround(1+o/200,i,a),this.twoFinger.dist=r;return}(Math.abs(c)>2||Math.abs(s)>2)&&(this.twoFinger.moved=!0,this.view.getZoom()>1?this.view.panByCanvasPixels(s,c):this.send({type:`scroll`,dx:Math.round(-s/P),dy:Math.round(-c/P)}),this.twoFinger.mx=i,this.twoFinger.my=a)}onUp(e){let t=this.pointers.size,n=this.pointers.get(e.pointerId);if(this.pointers.delete(e.pointerId),window.clearTimeout(this.longPressTimer),this.pointers.size===0&&this.view.endViewGesture(),t===2){let e=this.twoFinger;e&&!e.moved&&this.interaction===`mouse`&&performance.now()-e.start<F&&(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(let e of this.pointers.values())e.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}let r=performance.now()-n.startT;if(!(!(!n.moved&&r<F&&this.pointers.size===0)||this.dragScroll)&&(this.interaction===`mouse`||this.interaction===`touch`)){let e=this.view.canvasToNorm(n.startX,n.startY);this.moveCursor(e.nx,e.ny),this.send({type:`pointer`,action:`click`,button:`left`,x:e.nx,y:e.ny}),navigator.vibrate?.(12)}}},oe=class{container;constructor(e){this.container=e}async requestPermission(){try{`Notification`in window&&Notification.permission==="default"&&await Notification.requestPermission()}catch{}}get permission(){return`Notification`in window?Notification.permission:`unsupported`}flash(e,t,n=`info`){this.toast({type:`notification`,id:`flash-${Date.now()}`,title:e,body:t,level:n,source:`client`,t:Date.now()})}show(e){this.toast(e);try{`Notification`in window&&Notification.permission===`granted`&&new Notification(e.title,{body:e.body,tag:e.id})}catch{}navigator.vibrate?.(e.level===`error`?[60,40,60]:40)}toast(e){let t=document.createElement(`div`);t.className=`wd-toast wd-${e.level}`;let n=document.createElement(`strong`);if(n.textContent=e.title,t.appendChild(n),e.body){let n=document.createElement(`span`);n.textContent=e.body,t.appendChild(n)}this.container.appendChild(t),window.setTimeout(()=>{t.classList.add(`wd-hide`),window.setTimeout(()=>t.remove(),300)},5e3)}},se=``+new URL(`whip-CTqIatiK.png`,import.meta.url).href,ce=class{overlay;input;message;onSubmit=null;constructor(e){this.overlay=document.createElement(`div`),this.overlay.className=`wd-pin-overlay hidden`;let t=document.createElement(`div`);t.className=`wd-pin-card`;let n=document.createElement(`img`);n.className=`wd-pin-whip`,n.src=se,n.alt=`WhipDesk`,n.decoding=`async`;let r=document.createElement(`h2`);r.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`,e=>{e.key===`Enter`&&this.submit()});let i=document.createElement(`button`);i.className=`wd-pin-submit`,i.textContent=`Unlock`,i.onclick=()=>this.submit();let a=document.createElement(`button`);a.type=`button`,a.className=`wd-support-link wd-pin-support`,a.append(g(`heart`,14));let o=document.createElement(`span`);o.textContent=`Support WhipDesk`,a.append(o),a.onclick=()=>window.open(C,`_blank`,`noopener`);let s=document.createElement(`button`);s.className=`wd-pin-back`,s.textContent=`← Back to dashboard`,s.onclick=()=>{window.location.href=x()},t.append(n,r,this.message,this.input,i,a,s),this.overlay.appendChild(t),e.appendChild(this.overlay)}show(e,t){this.onSubmit=t,this.input.value=``,e.retry?(this.message.textContent=e.attemptsLeft>0?`Wrong PIN — ${e.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(){let e=this.input.value.trim();if(e.length<4){this.message.textContent=`PIN is at least 4 characters.`,this.message.classList.add(`err`);return}this.onSubmit?.(e),this.message.textContent=`Checking…`,this.message.classList.remove(`err`)}},le=`modulepreload`,ue=function(e,t){return new URL(e,t).href},de={},I=function(e,t,n){let r=Promise.resolve();if(t&&t.length>0){let e=document.getElementsByTagName(`link`),i=document.querySelector(`meta[property=csp-nonce]`),a=i?.nonce||i?.getAttribute(`nonce`);function o(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}function s(e){return import.meta.resolve?import.meta.resolve(e):new URL(e,new URL(`../../../src/node/plugins/importAnalysisBuild.ts`,import.meta.url)).href}r=o(t.map(t=>{if(t=ue(t,n),t=s(t),t in de)return;de[t]=!0;let r=t.endsWith(`.css`);for(let n=e.length-1;n>=0;n--){let i=e[n];if(i.href===t&&(!r||i.rel===`stylesheet`))return}let i=document.createElement(`link`);if(i.rel=r?`stylesheet`:le,r||(i.as=`script`),i.crossOrigin=``,i.href=t,a&&i.setAttribute(`nonce`,a),document.head.appendChild(i),r)return new Promise((e,n)=>{i.addEventListener(`load`,e),i.addEventListener(`error`,()=>n(Error(`Unable to preload CSS for ${t}`)))})}))}function i(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(t=>{for(let e of t||[])e.status===`rejected`&&i(e.reason);return e().catch(i)})};async function fe(e,t){if(e.vapidKey&&!(!(`serviceWorker`in navigator)||!(`Notification`in window)))try{let{initializeApp:n,getApps:r}=await I(async()=>{let{initializeApp:e,getApps:t}=await import(`./index.esm-mXKu2C3o.js`);return{initializeApp:e,getApps:t}},__vite__mapDeps([0,1]),import.meta.url),{getAuth:i}=await I(async()=>{let{getAuth:e}=await import(`./index.esm-CmSagqpw.js`);return{getAuth:e}},__vite__mapDeps([2,1]),import.meta.url),{getFirestore:a,doc:o,setDoc:s,serverTimestamp:c}=await I(async()=>{let{getFirestore:e,doc:t,setDoc:n,serverTimestamp:r}=await import(`./index.esm-wogdVWd2.js`);return{getFirestore:e,doc:t,setDoc:n,serverTimestamp:r}},__vite__mapDeps([3,1]),import.meta.url),{getMessaging:l,getToken:u,onMessage:d,isSupported:f}=await I(async()=>{let{getMessaging:e,getToken:t,onMessage:n,isSupported:r}=await import(`./index.esm-BgjPHsdM.js`);return{getMessaging:e,getToken:t,onMessage:n,isSupported:r}},__vite__mapDeps([4,1]),import.meta.url);if(!await f().catch(()=>!1))return;let p=r()[0]??n(e),m=i(p).currentUser;if(!m||Notification.permission==="default"&&await Notification.requestPermission()!==`granted`||Notification.permission!==`granted`)return;let h=await navigator.serviceWorker.register(`./firebase-messaging-sw.js`),g=l(p),_=await u(g,{vapidKey:e.vapidKey,serviceWorkerRegistration:h});if(!_)return;await s(o(a(p),`users`,m.uid,`fcmTokens`,_),{token:_,ua:navigator.userAgent,updatedAt:c()},{merge:!0}),d(g,e=>{let n=e.notification;t.show({type:`notification`,id:`push-${Date.now()}`,title:n?.title??`WhipDesk`,body:n?.body,level:`info`,source:`push`,t:Date.now()})})}catch{}}var pe=[{urls:`stun:turn-us1.whipdesk.com:3478`}];async function me(e){try{let t=await e.getStats(),n=``,r=``;t.forEach(e=>{e.type===`transport`&&e.selectedCandidatePairId&&(n=e.selectedCandidatePairId)}),t.forEach(e=>{e.type===`candidate-pair`&&!r&&(e.id===n||e.nominated&&e.state===`succeeded`)&&(r=e.localCandidateId)});let i=``;if(t.forEach(e=>{e.id===r&&e.type===`local-candidate`&&(i=e.candidateType)}),i===`relay`)return`TURN`;if(i===`srflx`||i===`prflx`)return`STUN`;if(i===`host`)return`LAN`}catch{}return null}var he=class{deviceId;config;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;constructor(e,t,n){this.deviceId=e,this.config=n,this.core=new l(t),this.core.setSender(e=>{if(this.dc&&this.dc.readyState===`open`)try{this.dc.send(e)}catch{}})}on(e,t){this.core.on(e,t)}send(e){this.core.send(e)}submitPin(e){this.core.submitPin(e)}setVisible(e){this.core.setVisible(e)}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(e=>{this.core.emit(`error`,`Remote connect failed: ${e.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`);let{initializeApp:e,getApps:t}=await I(async()=>{let{initializeApp:e,getApps:t}=await import(`./index.esm-mXKu2C3o.js`);return{initializeApp:e,getApps:t}},__vite__mapDeps([0,1]),import.meta.url),{getAuth:n}=await I(async()=>{let{getAuth:e}=await import(`./index.esm-CmSagqpw.js`);return{getAuth:e}},__vite__mapDeps([2,1]),import.meta.url),{getDatabase:r,ref:i,child:a,push:o,set:s,onValue:c,remove:l,onDisconnect:u}=await I(async()=>{let{getDatabase:e,ref:t,child:n,push:r,set:i,onValue:a,remove:o,onDisconnect:s}=await import(`./index.esm-Di8jSGaG.js`);return{getDatabase:e,ref:t,child:n,push:r,set:i,onValue:a,remove:o,onDisconnect:s}},__vite__mapDeps([5,1]),import.meta.url),d=t()[0]??e(this.config),f=n(d);if(await new Promise(e=>{if(f.currentUser)return e();let t=f.onAuthStateChanged(()=>{t(),e()})}),!f.currentUser){window.location.assign(S(`/app/${window.location.hash}`));return}let p=f.currentUser.uid,m=r(d),h=await this.fetchIceServers(f.currentUser),g=new RTCPeerConnection({iceServers:h,iceTransportPolicy:`all`});this.pc=g;let _=g.createDataChannel(`whipdesk`);_.binaryType=`arraybuffer`,this.dc=_,this.wireDataChannel(_);try{this.mainXcv=g.addTransceiver(`video`,{direction:`recvonly`}),this.overviewXcv=g.addTransceiver(`video`,{direction:`recvonly`})}catch{}g.ontrack=e=>{let t=e.streams[0]??new MediaStream([e.track]);this.overviewXcv&&e.transceiver===this.overviewXcv?this.core.emit(`overviewTrack`,t):this.core.emit(`videoTrack`,t)};let v=!1,y=async()=>{if(v)return;let e=await me(g);if(!e)return;v=!0,this.core.emit(`transport`,e);let t=e===`TURN`?`turn`:e===`STUN`?`stun`:`lan`;try{let{getFirestore:e,doc:n,setDoc:r,increment:i}=await I(async()=>{let{getFirestore:e,doc:t,setDoc:n,increment:r}=await import(`./index.esm-wogdVWd2.js`);return{getFirestore:e,doc:t,setDoc:n,increment:r}},__vite__mapDeps([3,1]),import.meta.url);r(n(e(d),`users`,p),{stats:{[t]:i(1)}},{merge:!0}).catch(()=>{})}catch{}};g.onconnectionstatechange=()=>{let e=g.connectionState;e===`connected`?(this.core.emit(`status`,`connected`),y(),this.startStatsPoll(g)):(e===`failed`||e===`disconnected`||e===`closed`)&&(this.core.emit(`status`,`disconnected`),this.scheduleReconnect())};let b=o(i(m,`signaling/${p}/${this.deviceId}`));this.pushCandidate=e=>{try{s(o(a(b,`offerCandidates`)),e)}catch{}},g.onicecandidate=e=>{e.candidate&&this.pushCandidate?.(e.candidate.toJSON())};let x=await g.createOffer();await g.setLocalDescription(x),await s(b,{offer:{sdp:g.localDescription?.sdp??``},controllerUid:p,createdAtMs:Date.now()}),u(b).remove(),this.deleteSessionDoc=()=>void l(b).catch(()=>{}),this.cleanupSignal=c(b,e=>{let t=e.val();if(!(!t||this.closed)&&(t.answer?.sdp&&!this.appliedAnswer&&(this.appliedAnswer=!0,g.setRemoteDescription({type:`answer`,sdp:t.answer.sdp})),t.answerCandidates&&this.appliedAnswer)){for(let[e,n]of Object.entries(t.answerCandidates))if(!this.appliedCandidates.has(e)){this.appliedCandidates.add(e);try{g.addIceCandidate(n)}catch{}}}})}startStatsPoll(e){window.clearInterval(this.statsTimer),this.lossBase=null,this.statsPolls=0,this.statsTimer=window.setInterval(()=>void this.pollStats(e),1e3)}lossBase=null;statsPolls=0;async pollStats(e){let t=null,n=0,r=0,i=0;try{(await e.getStats()).forEach(e=>{e.type===`inbound-rtp`&&e.kind===`video`?(n=Math.max(n,Number(e.framesPerSecond??0)),r+=Number(e.packetsLost??0),i+=Number(e.packetsReceived??0)):e.type===`candidate-pair`&&e.nominated&&typeof e.currentRoundTripTime==`number`&&(t=e.currentRoundTripTime)})}catch{return}if(this.core.emit(`netStats`,{fps:Math.round(n),rtt:t==null?null:Math.round(t*1e3)}),this.statsPolls+=1,!this.lossBase)this.lossBase={lost:r,received:i};else if(this.statsPolls%5==0){let e=Math.max(0,r-this.lossBase.lost),n=Math.max(0,i-this.lossBase.received);this.lossBase={lost:r,received:i};let a=e+n;if(a>0){let n=e/a*100;this.core.send({type:`video-stats`,lossPct:n,rttMs:t==null?void 0:Math.round(t*1e3)})}}}wireDataChannel(e){e.onopen=()=>this.core.sendHello(),e.onclose=()=>{this.core.emit(`status`,`disconnected`),this.scheduleReconnect()},e.onmessage=e=>{typeof e.data==`string`&&this.core.handleText(e.data)}}async fetchIceServers(e){let t=[...pe];try{let e=sessionStorage.getItem(`wd-ice`);if(e){let{servers:t,exp:n}=JSON.parse(e);if(Array.isArray(t)&&t.length&&n>Date.now())return t}}catch{}try{let t=this.config.iceUrl||`https://us-central1-${this.config.projectId}.cloudfunctions.net/iceServers`,n=await e.getIdToken(),r=await fetch(t,{headers:{authorization:`Bearer ${n}`}});if(r.ok){let e=await r.json();if(Array.isArray(e.iceServers)&&e.iceServers.length){try{sessionStorage.setItem(`wd-ice`,JSON.stringify({servers:e.iceServers,exp:Date.now()+8*6e4}))}catch{}return e.iceServers}}}catch{}return t}};function L(e,t,n){return e<t?t:e>n?n:e}var ge=480,_e=600,R=-.6,z=1.6,ve=1500,ye=120,be=3e3;function B(e){return!e||e.x<=.001&&e.y<=.001&&e.w>=.999&&e.h>=.999}function V(e,t){if(B(e)&&B(t))return!0;if(!e||!t)return!1;let n=.005;return Math.abs(e.x-t.x)<n&&Math.abs(e.y-t.y)<n&&Math.abs(e.w-t.w)<n&&Math.abs(e.h-t.h)<n}var xe=class{canvas;ctx;mainEl=null;region=null;shownRegion=null;regionBridge=0;regionActiveHold=0;hasActiveEcho=!1;screen={width:0,height:0};viewMode=`fit`;zoom=1;center={nx:.5,ny:.5};cursor=null;onZoomCb;onViewCb;onGestureCb;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;constructor(e){this.canvas=e;let t=e.getContext(`2d`);if(!t)throw Error(`2D canvas context unavailable`);this.ctx=t,this.createOverlay(),this.startOverlayLoop(),this.resize(),new ResizeObserver(()=>this.resize()).observe(e),window.addEventListener(`orientationchange`,()=>window.setTimeout(()=>this.resize(),200))}createOverlay(){let e=document.createElement(`div`);e.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);`;let t=document.createElement(`canvas`);t.style.cssText=`position:absolute;inset:0;width:100%;height:100%;border-radius:5px;display:block;`;let 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;`,e.appendChild(t),e.appendChild(n),document.body.appendChild(e),this.overlayBox=e,this.overlayView=n,this.overlayCanvas=t,this.overlayCtx=t.getContext(`2d`)}startOverlayLoop(){let e=()=>{this.renderOverlay(),requestAnimationFrame(e)};requestAnimationFrame(e)}renderOverlay(){let e=this.overlayBox,t=this.overlayView;if(!e||!t)return;let n=this.getVisibleRegion(),r=this.region,i=this.dispW(),a=this.dispH();if(!(r||n.w<.985||n.h<.985)){e.style.display!==`none`&&(e.style.display=`none`),this.lastOverlayKey=``,this.overlayCanvasKey=``;return}e.style.display===`none`&&(e.style.display=`block`,this.minimapActiveAt=performance.now(),this.minimapShown=!1);let o=Math.round(Math.min(132,Math.max(76,this.cssW*.32))),s=Math.round(o*(a/i||.625)),c=`${o}x${s}`;(c!==this.overlayCanvasKey||this.overviewDirty)&&(this.overlayCanvasKey=c,this.overviewDirty=!1,e.style.width=`${o}px`,e.style.height=`${s}px`,this.paintMinimap(o,s));let l=n,u=performance.now(),d=`${l.x.toFixed(3)},${l.y.toFixed(3)},${l.w.toFixed(3)},${l.h.toFixed(3)}`;d!==this.lastOverlayKey&&(this.lastOverlayKey=d,this.minimapActiveAt=u,t.style.left=`${(L(l.x,0,1)*100).toFixed(2)}%`,t.style.top=`${(L(l.y,0,1)*100).toFixed(2)}%`,t.style.width=`${(L(Math.max(.04,l.w),0,1)*100).toFixed(2)}%`,t.style.height=`${(L(Math.max(.04,l.h),0,1)*100).toFixed(2)}%`);let f=u-this.minimapActiveAt<be;f!==this.minimapShown&&(this.minimapShown=f,e.style.transition=`opacity ${f?.18:.5}s ease`,e.style.opacity=f?`1`:`0`)}paintMinimap(e,t){let n=this.overlayCanvas,r=this.overlayCtx;if(!n||!r)return;let i=Math.max(1,Math.round(e*this.dpr)),a=Math.max(1,Math.round(t*this.dpr));n.width!==i&&(n.width=i),n.height!==a&&(n.height=a),r.clearRect(0,0,i,a);let o=this.overviewImg();o&&(r.imageSmoothingEnabled=!0,r.imageSmoothingQuality=`low`,r.drawImage(o,0,0,i,a))}overviewImg(){return this.overviewReady?this.overview:null}maybeSnapshot(e,t,n){let r=performance.now();if(r-this.lastSnapAt<_e)return;this.lastSnapAt=r,this.overview||(this.overview=document.createElement(`canvas`),this.overviewCtx=this.overview.getContext(`2d`));let i=this.overview,a=this.overviewCtx;if(!i||!a)return;let o=ge,s=Math.max(1,Math.round(o*n/t));i.width!==o&&(i.width=o),i.height!==s&&(i.height=s),a.imageSmoothingEnabled=!0,a.imageSmoothingQuality=`medium`,a.drawImage(e,0,0,t,n,0,0,o,s),this.overviewReady=!0,this.overviewDirty=!0}getCanvas(){return this.canvas}resize(){let e=this.canvas.getBoundingClientRect();this.cssW=Math.max(1,e.width),this.cssH=Math.max(1,e.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(e){let t=e.width!==this.screen.width||e.height!==this.screen.height;this.screen=e,t&&(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(e){let t=B(e)?null:e;this.region=t,window.clearTimeout(this.regionActiveHold),window.clearTimeout(this.regionBridge),this.hasActiveEcho||(this.regionBridge=window.setTimeout(()=>{this.shownRegion=t,this.videoActive||this.requestDraw()},ve)),this.videoActive||this.requestDraw()}setFrameRegionActive(e){this.hasActiveEcho=!0;let t=B(e)?null:e;if(!V(t,this.region)||V(t,this.shownRegion))return;window.clearTimeout(this.regionActiveHold);let n=()=>{V(t,this.region)&&(this.shownRegion=t,window.clearTimeout(this.regionBridge),this.videoActive||this.requestDraw())};this.regionActiveHold=window.setTimeout(()=>{if(!V(t,this.region))return;let e=this.mainEl;e&&typeof e.requestVideoFrameCallback==`function`?e.requestVideoFrameCallback(n):n()},ye)}setVideoSource(e){this.mainEl=e,this.videoActive=!!e,e?this.startVideoLoop():this.stopVideoLoop()}isVideoActive(){return this.videoActive}setOverviewSource(e){this.overviewEl=e}startVideoLoop(){if(this.videoRaf)return;let e=()=>{if(!this.videoActive){this.videoRaf=0;return}this.draw(),this.videoRaf=requestAnimationFrame(e)};this.videoRaf=requestAnimationFrame(e)}stopVideoLoop(){this.videoRaf&&cancelAnimationFrame(this.videoRaf),this.videoRaf=0,this.requestDraw()}setViewMode(e){this.viewMode=e,e===`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(e){this.zoom=L(e,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(e){this.setZoom(this.zoom*e)}getZoom(){return this.zoom}setOnZoom(e){this.onZoomCb=e}setOnView(e){this.onViewCb=e}setOnGesture(e){this.onGestureCb=e}beginViewGesture(){this.gestureActive=!0,this.onGestureCb?.(!0)}endViewGesture(){this.gestureActive&&(this.gestureActive=!1,this.viewDirty&&(this.viewDirty=!1,this.onViewCb?.(this.getVisibleRegion())),this.onGestureCb?.(!1))}emitView(){if(this.gestureActive){this.viewDirty=!0;return}this.onViewCb?.(this.getVisibleRegion())}zoomAround(e,t,n){let r=this.canvasToNorm(t,n),i=L(this.zoom*e,1,8);if(i===1){this.setZoom(1);return}this.zoom=i,this.viewMode=`magnify`;let a=this.dispW(),o=this.dispH(),s=Math.min(this.cssW/a,this.cssH/o)*this.zoom;this.center.nx=L(r.nx-(t-this.cssW/2)/(a*s||1),R,z),this.center.ny=L(r.ny-(n-this.cssH/2)/(o*s||1),R,z),this.onZoomCb?.(this.zoom),this.emitView(),this.requestDraw()}panByNorm(e,t){this.center.nx=L(this.center.nx+e,R,z),this.center.ny=L(this.center.ny+t,R,z),this.emitView(),this.requestDraw()}panByCanvasPixels(e,t){let{S:n}=this.computeLayout(),r=this.dispW(),i=this.dispH();!r||!i||!n||(this.center.nx=L(this.center.nx-e/(r*n),R,z),this.center.ny=L(this.center.ny-t/(i*n),R,z),this.emitView(),this.requestDraw())}setCursor(e,t=0){this.cursor=e===null?null:{nx:e,ny:t},this.videoActive||this.requestDraw()}computeLayout(){let{cssW:e,cssH:t}=this,n=this.dispW(),r=this.dispH();if(!n||!r)return this.layout={S:1,tx:0,ty:0},this.layout;let i=Math.min(e/n,t/r)*this.zoom,a,o;return this.viewMode===`fit`||this.zoom===1?(a=(e-n*i)/2,o=(t-r*i)/2):(a=e/2-this.center.nx*n*i,o=t/2-this.center.ny*r*i),this.layout={S:i,tx:a,ty:o},this.layout}canvasToNorm(e,t){let{S:n,tx:r,ty:i}=this.computeLayout();return{nx:L((e-r)/(this.dispW()*n||1),0,1),ny:L((t-i)/(this.dispH()*n||1),0,1)}}normToCanvas(e,t){let{S:n,tx:r,ty:i}=this.computeLayout();return{cx:r+e*this.dispW()*n,cy:i+t*this.dispH()*n}}getVisibleRegion(){let{S:e,tx:t,ty:n}=this.computeLayout(),r=this.dispW()*e,i=this.dispH()*e;if(!r||!i)return{x:0,y:0,w:1,h:1};let a=L(-t/r,0,1),o=L(-n/i,0,1),s=L((this.cssW-t)/r,0,1),c=L((this.cssH-n)/i,0,1);return{x:a,y:o,w:Math.max(.05,s-a),h:Math.max(.05,c-o)}}requestDraw(){this.videoActive||this.rafPending||(this.rafPending=!0,requestAnimationFrame(()=>{this.rafPending=!1,this.draw()}))}draw(){let e=this.ctx;e.setTransform(this.dpr,0,0,this.dpr,0,0),e.clearRect(0,0,this.cssW,this.cssH);let t=this.mainEl&&this.mainEl.videoWidth>0?this.mainEl:null;if(!t)return;let{S:n,tx:r,ty:i}=this.computeLayout(),a=this.dispW(),o=this.dispH(),s=a*n,c=o*n,l=t.videoWidth,u=t.videoHeight,d=l/u,f=e=>e?e.w*a/(e.h*o):a/o;if(this.region!==this.shownRegion){let e=Math.abs(d/f(this.region)-1)<.06,t=Math.abs(d/f(this.shownRegion)-1)<.06;e&&!t&&(this.shownRegion=this.region,window.clearTimeout(this.regionBridge))}let p=this.shownRegion,m=!!p&&Math.abs(d/f(p)-1)<.08,h=m?r+p.x*s:r,g=m?i+p.y*c:i,_=m?p.w*s:s,v=m?p.h*c:c,y=this.overviewImg();y&&(this.region||this.shownRegion)&&(e.imageSmoothingEnabled=!0,e.imageSmoothingQuality=`low`,e.drawImage(y,r,i,s,c));let b=Math.min(_/l,v/u),x=l*b,S=u*b;if(e.imageSmoothingEnabled=!0,e.imageSmoothingQuality=`high`,e.drawImage(t,0,0,l,u,h+(_-x)/2,g+(v-S)/2,x,S),this.cursor){let{cx:e,cy:t}=this.normToCanvas(this.cursor.nx,this.cursor.ny);this.drawCursor(e,t)}let C=this.overviewEl;if(this.region&&C&&C.videoWidth>0)this.maybeSnapshot(C,C.videoWidth,C.videoHeight);else if(!this.region&&!this.shownRegion&&o>0){let e=a/o;e>0&&Math.abs(d/e-1)<.06&&this.maybeSnapshot(t,l,u)}}drawCursor(e,t){let n=this.ctx;n.save(),n.beginPath(),n.arc(e,t,9,0,Math.PI*2),n.strokeStyle=`rgba(78,161,255,0.95)`,n.lineWidth=2,n.stroke(),n.beginPath(),n.arc(e,t,2.5,0,Math.PI*2),n.fillStyle=`rgba(78,161,255,0.95)`,n.fill(),n.restore()}};function Se(e,t,n,r){let i=document.createElement(`div`);i.className=`wd-place-layer`;let a=document.createElement(`div`);a.className=`wd-place-marker`;let o=document.createElement(`div`);o.className=`wd-place-bar`;let s=document.createElement(`p`);s.className=`wd-place-hint`,s.textContent=n.hint,o.appendChild(s);let c=null;n.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));let l=document.createElement(`div`);l.className=`wd-place-buttons`;let u=document.createElement(`button`);u.className=`wd-btn`,u.textContent=`Cancel`;let d=document.createElement(`button`);d.className=`wd-btn wd-go`,d.textContent=n.confirmLabel,l.append(u,d),o.appendChild(l),t.append(i,a,o),t.classList.add(`wd-placing`);let f={nx:.5,ny:.5},p=0,m=()=>{let{cx:t,cy:n}=e.normToCanvas(f.nx,f.ny);a.style.left=`${t}px`,a.style.top=`${n}px`,p=requestAnimationFrame(m)};p=requestAnimationFrame(m);let h=new Map,g=`idle`,_=null,v={x:0,y:0},y=!1,b=e=>{let t=i.getBoundingClientRect();return{x:e.clientX-t.left,y:e.clientY-t.top}},x=t=>{let{cx:n,cy:r}=e.normToCanvas(f.nx,f.ny);return Math.hypot(t.x-n,t.y-r)<38};i.addEventListener(`pointerdown`,t=>{i.setPointerCapture?.(t.pointerId);let n=b(t);if(h.set(t.pointerId,n),h.size===2){let e=[...h.values()],t=e[0],n=e[1];_={dist:Math.hypot(t.x-n.x,t.y-n.y),mx:(t.x+n.x)/2,my:(t.y+n.y)/2},g=`idle`}else h.size===1&&(v=n,y=!1,g=x(n)?`marker`:`pan`,g===`marker`&&(f=e.canvasToNorm(n.x,n.y)))}),i.addEventListener(`pointermove`,t=>{let n=h.get(t.pointerId);if(!n)return;let r=b(t);if(h.set(t.pointerId,r),h.size>=2&&_){let t=[...h.values()],n=t[0],r=t[1],i=Math.hypot(n.x-r.x,n.y-r.y),a=(n.x+r.x)/2,o=(n.y+r.y)/2;Math.abs(i-_.dist)>4&&(e.zoomAround(1+(i-_.dist)/200,a,o),_.dist=i),e.panByCanvasPixels(a-_.mx,o-_.my),_.mx=a,_.my=o;return}Math.hypot(r.x-v.x,r.y-v.y)>6&&(y=!0);let i=r.x-n.x,a=r.y-n.y;g===`marker`?f=e.canvasToNorm(r.x,r.y):g===`pan`&&e.panByCanvasPixels(i,a)});let S=t=>{let n=h.get(t.pointerId);h.delete(t.pointerId),n&&g===`pan`&&!y&&(f=e.canvasToNorm(n.x,n.y)),h.size<2&&(_=null),h.size===0&&(g=`idle`)};i.addEventListener(`pointerup`,S),i.addEventListener(`pointercancel`,S);let C=e=>{cancelAnimationFrame(p),t.classList.remove(`wd-placing`),i.remove(),a.remove(),o.remove(),r(e)};u.onclick=()=>C(null),d.onclick=()=>{let e=c?.value.trim()??``;if(n.withText&&!e){c?.focus();return}C({nx:f.nx,ny:f.ny,text:n.withText?e:void 0})}}var Ce=0;function H(){return`w${Date.now().toString(36)}${(Ce++).toString(36)}`}function we(){return new Date().toLocaleString(void 0,{month:`short`,day:`numeric`,hour:`2-digit`,minute:`2-digit`})}function Te(e){let t=Math.max(0,Math.round((e-Date.now())/1e3)),n=Math.floor(t/3600),r=Math.floor(t%3600/60),i=t%60;return n>0?`${n}h ${r}m`:r>0?`${r}m ${String(i).padStart(2,`0`)}s`:`${i}s`}function U(e,t,n){let r=document.createElement(e);return t&&(r.className=t),n!==void 0&&(r.textContent=n),r}var Ee={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 W(e){return Ee[e]??`AI agent`}function De(e){switch(e){case`working`:return`working`;case`blocked`:return`needs you`;case`idle`:return`idle`;case`finished`:return`finished`;case`crashed`:return`crashed`;default:return`…`}}var Oe=class{root;conn;view;notifications;requestNotifications;regions=[];timers=[];monitors=[];monitorSessions=[];alwaysAgents=new Set;renderPicker=null;renderAlways=null;countdownTimer=0;overlay;list;permissionRow;selector=null;constructor(e,t,n,r,i=()=>{}){this.root=e,this.conn=t,this.view=n,this.notifications=r,this.requestNotifications=i,this.overlay=U(`div`,`wd-dialog-overlay hidden`),this.overlay.addEventListener(`pointerdown`,e=>{e.target===this.overlay&&this.close()});let a=U(`div`,`wd-dialog`),o=U(`div`,`wd-dialog-head`);o.append(U(`h2`,``,`Auto-Whips`));let s=U(`button`,`wd-dialog-x`);s.appendChild(g(`x`)),s.onclick=()=>this.close(),o.appendChild(s);let c=U(`div`,`wd-dialog-help`),l=U(`p`,`wd-help-intro`,`Auto-Whips are a set of features that can whip and monitor agents for you automatically:`),u=U(`ul`,`wd-help-list`),d=U(`li`);d.append(U(`strong`,void 0,`Alerts`),document.createTextNode(` — watch part of the screen and ping you when it changes (e.g. your agent finishes).`));let f=U(`li`);f.append(U(`strong`,void 0,`Timers`),document.createTextNode(` — ping you after a set time, and can auto-click or send a prompt when they fire.`));let p=U(`li`);p.append(U(`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).`)),u.append(d,f,p);let m=U(`p`,`wd-help-note`,`Enable browser notifications to be reminded even when the browser is closed.`);c.append(l,u,m),this.permissionRow=U(`div`,`wd-perm-row`),this.list=U(`div`,`wd-watch-list`);let h=U(`button`,`wd-btn wd-go`);h.append(g(`plus`),U(`span`,`wd-btn-label`,`Add alert`)),h.onclick=()=>this.beginSelection();let _=U(`button`,`wd-btn wd-go`);_.append(g(`clock`),U(`span`,`wd-btn-label`,`Add timer`)),_.onclick=()=>this.beginTimer();let v=U(`button`,`wd-btn wd-go`);v.append(g(`activity`),U(`span`,`wd-btn-label`,`Add Session Monitoring`)),v.onclick=()=>this.beginMonitor();let y=U(`div`,`wd-dialog-actions wd-actions-stack`);y.append(h,_,v),a.append(o,c,this.permissionRow,this.list,y),this.overlay.appendChild(a),this.root.appendChild(this.overlay),this.renderPermission(),this.conn.on(`monitorSessions`,e=>{this.monitorSessions=e,this.renderPicker?.()})}setRegions(e){this.regions=e,this.renderList()}setTimers(e){this.timers=e,this.renderList()}setMonitors(e){this.monitors=e,this.renderList()}setAlwaysAgents(e){this.alwaysAgents=new Set(e),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();let e=U(`span`,`wd-perm-dot`),t=U(`span`,`wd-perm-text`),n=this.notifications.permission;if(n===`granted`)e.dataset.state=`on`,t.textContent=`Browser notifications are on.`,this.permissionRow.append(e,t);else if(n===`denied`)e.dataset.state=`off`,t.textContent=`Notifications are blocked — enable them in your browser settings.`,this.permissionRow.append(e,t);else if(n===`unsupported`)e.dataset.state=`off`,t.textContent=`This browser can't show notifications. Keep this page open for in-app alerts.`,this.permissionRow.append(e,t);else{e.dataset.state=`warn`,t.textContent=`Allow notifications to be alerted while this page is in the background.`;let n=U(`button`,`wd-btn wd-perm-enable`);n.append(U(`span`,`wd-btn-label`,`Enable`)),n.onclick=async()=>{await this.requestNotifications(),this.renderPermission()},this.permissionRow.append(e,t,n)}}renderList(){if(this.list.replaceChildren(),this.regions.length===0&&this.timers.length===0&&this.monitors.length===0){this.list.appendChild(U(`p`,`wd-dialog-help`,`No alerts, timers, or session monitors yet.`));return}for(let e of this.regions){let t=U(`div`,`wd-watch-row`),n=U(`button`,`wd-watch-name`,e.label);n.title=`Edit this alert`,n.onclick=()=>this.beginSelection(e),t.appendChild(n);let r=U(`button`,`wd-btn wd-icon-only`);r.appendChild(g(`trash`)),r.setAttribute(`aria-label`,`Remove ${e.label}`),r.onclick=()=>{this.conn.send({type:`watch-remove`,id:e.id}),this.regions=this.regions.filter(t=>t.id!==e.id),this.renderList()},t.appendChild(r),this.list.appendChild(t)}for(let e of this.timers){let t=U(`div`,`wd-watch-row`),n=U(`div`,`wd-timer-info`),r=U(`span`,`wd-timer-name`);r.append(g(`clock`,15),document.createTextNode(e.label));let i=U(`span`,`wd-timer-remain`,Te(e.fireAtMs));n.append(r,i),t.appendChild(n),e.hasAction&&t.appendChild(U(`span`,`wd-timer-tag`,`auto`));let a=U(`button`,`wd-btn wd-icon-only`);a.appendChild(g(`trash`)),a.setAttribute(`aria-label`,`Cancel ${e.label}`),a.onclick=()=>{this.conn.send({type:`timer-remove`,id:e.id}),this.timers=this.timers.filter(t=>t.id!==e.id),this.renderList()},t.appendChild(a),this.list.appendChild(t)}for(let e of this.monitors){let t=U(`div`,`wd-watch-row`),n=U(`div`,`wd-timer-info`),r=U(`span`,`wd-timer-name`);r.append(g(`activity`,15),document.createTextNode(`${W(e.agent)} · ${e.label}`));let i=U(`span`,`wd-mon-state`);i.dataset.state=e.live?e.state:`finished`,i.textContent=e.live?De(e.state):`ended`,n.append(r,i),t.appendChild(n);let a=U(`button`,`wd-btn wd-icon-only`);a.appendChild(g(`trash`)),a.setAttribute(`aria-label`,`Stop monitoring ${e.label}`),a.onclick=()=>{this.conn.send({type:`monitor-remove`,id:e.id}),this.monitors=this.monitors.filter(t=>t.id!==e.id),this.renderList()},t.appendChild(a),this.list.appendChild(t)}}beginSelection(e){this.close(),this.selector&&this.selector.remove();let t=this.root.getBoundingClientRect(),n=e?this.boxFromRegion(e,t):{x:t.width*.3,y:t.height*.3,w:t.width*.4,h:t.height*.25},r=U(`div`,`wd-selector-info`,e?`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.`),i=U(`div`,`wd-selector`),a=U(`div`,`wd-selector-move`);a.appendChild(g(`drag`));let o=U(`div`,`wd-selector-handle`),s=U(`div`,`wd-selector-bar`),c=U(`button`,`wd-btn wd-go`);c.append(U(`span`,`wd-btn-label`,e?`Save`:`Create`));let l=U(`button`,`wd-btn`);l.append(U(`span`,`wd-btn-label`,`Cancel`)),s.append(l,c),i.append(a,o,s),this.root.append(r,i),this.selector=i;let u=()=>{i.remove(),r.remove(),this.selector=null,this.open()},d=()=>{i.style.left=`${n.x}px`,i.style.top=`${n.y}px`,i.style.width=`${n.w}px`,i.style.height=`${n.h}px`};d();let f=(e,t)=>{e.addEventListener(`pointerdown`,n=>{n.preventDefault(),n.stopPropagation(),e.setPointerCapture(n.pointerId);let r=n.clientX,i=n.clientY,a=e=>{t(e.clientX-r,e.clientY-i),r=e.clientX,i=e.clientY,d()},o=()=>{e.removeEventListener(`pointermove`,a),e.removeEventListener(`pointerup`,o)};e.addEventListener(`pointermove`,a),e.addEventListener(`pointerup`,o)})};f(a,(e,r)=>{n.x=Math.max(0,Math.min(t.width-n.w,n.x+e)),n.y=Math.max(0,Math.min(t.height-n.h,n.y+r))}),f(o,(e,r)=>{n.w=Math.max(40,Math.min(t.width-n.x,n.w+e)),n.h=Math.max(40,Math.min(t.height-n.y,n.h+r))}),l.onclick=u,c.onclick=()=>{let t=this.toScreenRegion(n,e);t&&(this.conn.send({type:`watch-add`,region:t}),this.regions=e?this.regions.map(e=>e.id===t.id?t:e):[...this.regions,t],this.notifications.permission==="default"&&this.requestNotifications(),this.notifications.flash(e?`Alert updated`:`Alert created`,e?`"${t.label}" updated.`:`Monitoring "${t.label}". We'll notify you when it changes.`,`success`)),u()}}boxFromRegion(e,t){let n=this.view.normToCanvas(e.x,e.y),r=this.view.normToCanvas(e.x+e.w,e.y+e.h),i=Math.max(40,r.cx-n.cx),a=Math.max(40,r.cy-n.cy);return{x:Math.max(0,Math.min(t.width-i,n.cx)),y:Math.max(0,Math.min(t.height-a,n.cy)),w:i,h:a}}toScreenRegion(e,t){let n=this.view.canvasToNorm(e.x,e.y),r=this.view.canvasToNorm(e.x+e.w,e.y+e.h),i=Math.min(n.nx,r.nx),a=Math.min(n.ny,r.ny),o=Math.abs(r.nx-n.nx),s=Math.abs(r.ny-n.ny);return o<.005||s<.005?null:{id:t?.id??H(),label:t?.label??we(),x:i,y:a,w:o,h:s}}beginTimer(){this.close();let e=U(`div`,`wd-dialog-overlay`),t=()=>{e.remove(),this.open()};e.addEventListener(`pointerdown`,n=>{n.target===e&&t()});let n=U(`div`,`wd-dialog`),r=U(`div`,`wd-dialog-head`);r.append(U(`h2`,``,`Set timer`));let i=U(`button`,`wd-dialog-x`);i.appendChild(g(`x`)),i.onclick=t,r.appendChild(i);let a=U(`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.`),o=U(`div`,`wd-form-row`);o.appendChild(U(`label`,`wd-form-label`,`Remind me in`));let s=U(`input`,`wd-input wd-input-num`);s.type=`number`,s.min=`0`,s.max=`168`,s.value=`0`,s.inputMode=`numeric`;let c=U(`input`,`wd-input wd-input-num`);c.type=`number`,c.min=`0`,c.max=`59`,c.value=`30`,c.inputMode=`numeric`;let l=U(`div`,`wd-preset-row`);for(let[e,t,n]of[[`15m`,0,15],[`30m`,0,30],[`1h`,1,0],[`2h`,2,0],[`5h`,5,0]]){let r=U(`button`,`wd-preset`,e);r.type=`button`,r.onclick=()=>{s.value=String(t),c.value=String(n)},l.appendChild(r)}let u=(e,t,n,r)=>{let i=U(`div`,`wd-stepper`),a=U(`button`,`wd-step-btn`,`−`);a.type=`button`;let o=U(`button`,`wd-step-btn`,`+`);o.type=`button`;let s=e=>Math.max(0,Math.min(n,e));return a.onclick=()=>e.value=String(s((Number(e.value)||0)-t)),o.onclick=()=>e.value=String(s((Number(e.value)||0)+t)),i.append(a,e,U(`span`,`wd-form-unit`,r),o),i},d=U(`div`,`wd-form-duration`);d.append(u(s,1,168,`h`),u(c,5,59,`m`)),o.append(l,d);let f=U(`div`,`wd-form-row`);f.appendChild(U(`label`,`wd-form-label`,`Label`));let p=U(`input`,`wd-input`);p.placeholder=`e.g. Claude is back`,f.appendChild(p);let m=U(`div`,`wd-form-row`);m.appendChild(U(`label`,`wd-form-label`,`When it fires`));let h=U(`select`,`wd-input`);for(let[e,t]of[[`none`,`Just remind me`],[`click`,`Click a button`],[`key`,`Click & press Enter`],[`text`,`Click, type & send a prompt`]]){let n=document.createElement(`option`);n.value=e,n.textContent=t,h.appendChild(n)}m.appendChild(h);let _=U(`div`,`wd-dialog-actions`),v=U(`button`,`wd-btn`);v.append(U(`span`,`wd-btn-label`,`Cancel`)),v.onclick=t;let y=U(`button`,`wd-btn wd-go`),b=U(`span`,`wd-btn-label`,`Start timer`);y.append(g(`clock`),b);let x=()=>{b.textContent=h.value===`none`?`Start timer`:`Next: place target`};h.onchange=x,x();let S=()=>{let e=Math.max(0,Math.min(168,Math.round(Number(s.value)||0))),t=Math.max(0,Math.min(59,Math.round(Number(c.value)||0))),n=(e*60+t)*6e4;return n>=6e4?n:null},C=()=>{let e=Math.max(0,Math.round(Number(s.value)||0)),t=Math.max(0,Math.round(Number(c.value)||0));return`${e?`${e}h `:``}${t}m`},w=e=>{let n=H(),r=S(),i=p.value.trim()||`Timer (${C()})`;this.conn.send({type:`timer-add`,id:n,fireInMs:r,label:i,action:e}),this.timers=[...this.timers,{id:n,label:i,fireAtMs:Date.now()+r,hasAction:!!e}],this.notifications.permission==="default"&&this.requestNotifications(),this.notifications.flash(`Timer started`,`"${i}" — I'll ping you in ${C()}.`,`success`),t()};y.onclick=()=>{if(S()==null){this.notifications.flash(`Set a time`,`Choose at least 1 minute.`,`warning`);return}let t=h.value;if(t===`none`){w(void 0);return}e.style.display=`none`;let n=t===`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.`:t===`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.`;Se(this.view,this.root,{withText:t===`text`,hint:n,confirmLabel:`Confirm target`},n=>{if(!n){e.style.display=``;return}let r;r=t===`click`?{kind:`click`,x:n.nx,y:n.ny,button:`left`}:t===`key`?{kind:`key`,key:`Enter`,x:n.nx,y:n.ny}:{kind:`text`,text:n.text??``,x:n.nx,y:n.ny},w(r)})},_.append(v,y),n.append(r,a,o,f,m,_),e.appendChild(n),this.root.appendChild(e)}beginMonitor(){this.close();let e=U(`div`,`wd-dialog-overlay`),t=()=>{this.renderPicker=null,this.renderAlways=null,e.remove(),this.open()};e.addEventListener(`pointerdown`,n=>{n.target===e&&t()});let n=U(`div`,`wd-dialog`),r=U(`div`,`wd-dialog-head`);r.append(U(`h2`,``,`Monitor a session`));let i=U(`button`,`wd-dialog-x`);i.appendChild(g(`x`)),i.onclick=t,r.appendChild(i);let a=U(`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).`),o=U(`div`,`wd-mon-pick-head`),s=U(`button`,`wd-btn`);s.append(U(`span`,`wd-btn-label`,`Rescan`)),s.onclick=()=>this.conn.send({type:`monitor-scan`}),o.append(U(`span`,`wd-form-label`,`Running sessions`),s);let c=U(`div`,`wd-mon-pick`),l=``,u=()=>{if(c.replaceChildren(),this.monitorSessions.length===0){l=``,v(),p(),c.appendChild(U(`p`,`wd-dialog-help`,`No AI sessions detected yet. Start Claude Code, Codex, Gemini, or Aider, then Rescan.`));return}(!l||!this.monitorSessions.some(e=>e.key===l))&&(l=this.monitorSessions[0].key);for(let e of this.monitorSessions){let t=U(`button`,`wd-mon-item`);t.type=`button`,e.key===l&&t.classList.add(`on`);let n=U(`div`,`wd-mon-item-main`);n.append(g(`activity`,15),U(`span`,`wd-mon-item-title`,`${W(e.agent)} · ${e.title}`));let r=U(`span`,`wd-mon-state`);r.dataset.state=e.state,r.textContent=De(e.state),t.append(n,r),t.onclick=()=>{l=e.key,u()},c.appendChild(t)}v(),p()};this.renderPicker=u;let d=U(`div`,`wd-form-row`);d.appendChild(U(`label`,`wd-form-label`,`Always alert me`));let f=U(`div`,`wd-mon-always`);d.appendChild(f);let p=()=>{f.replaceChildren();let e=this.monitorSessions.find(e=>e.key===l);if(!e){f.appendChild(U(`p`,`wd-dialog-help`,`Pick a session above to always alert for its agent.`));return}let t=e.agent,n=U(`label`,`wd-check`),r=U(`input`);r.type=`checkbox`,r.checked=this.alwaysAgents.has(t),r.onchange=()=>{r.checked?this.alwaysAgents.add(t):this.alwaysAgents.delete(t),this.conn.send({type:`monitor-always`,agent:t,enabled:r.checked}),r.checked&&this.notifications.permission==="default"&&this.requestNotifications(),p()};let i=U(`span`,`wd-check-text`);i.append(document.createTextNode(`Always monitor every ${W(t)} session`),U(`span`,`wd-check-sub`,`Keeps alerting across agent and WhipDesk restarts — no need to re-add it.`)),n.append(r,i),f.appendChild(n)};this.renderAlways=p;let m=U(`div`,`wd-dialog-actions`),h=U(`button`,`wd-btn`);h.append(U(`span`,`wd-btn-label`,`Cancel`)),h.onclick=t;let _=U(`button`,`wd-btn wd-go`);_.append(g(`activity`),U(`span`,`wd-btn-label`,`Add monitor`));let v=()=>_.disabled=!l;v(),_.onclick=()=>{let e=this.monitorSessions.find(e=>e.key===l);if(!e)return;let n=H(),r=e.title;this.conn.send({type:`monitor-add`,id:n,key:e.key,agent:e.agent,label:r}),this.monitors=[...this.monitors,{id:n,key:e.key,agent:e.agent,label:r,state:e.state,live:!0}],this.notifications.permission==="default"&&this.requestNotifications(),this.notifications.flash(`Monitoring started`,`Watching ${W(e.agent)} · ${r}.`,`success`),t()},m.append(h,_),n.append(r,a,o,c,d,m),e.appendChild(n),this.root.appendChild(e),u(),this.conn.send({type:`monitor-scan`})}};function ke(){let e=new URLSearchParams(location.hash.replace(/^#/,``));return{token:e.get(`t`)??e.get(`token`)??``,remote:e.get(`remote`)===`1`||e.has(`device`),device:e.get(`device`)??e.get(`d`)??``}}function Ae(){return`${location.protocol===`https:`?`wss`:`ws`}://${location.host}/ws`}async function je(){let e=window.__WHIPDESK_FB__;if(e?.apiKey)return e;try{let e=await fetch(`firebase.json`,{cache:`no-store`});if(e.ok)return await e.json()}catch{}return null}var G=document.getElementById(`app`);if(!G)throw Error(`#app not found`);var K=$(`canvas`,`wd-screen`);K.id=`wd-screen`;var Me=$(`div`,`wd-toasts`);G.append(K,Me);var{token:q,remote:J,device:Y}=ke(),X=new xe(K),Z=new oe(Me),Ne=new ce(G),Q=new p(G);async function Pe(e){if(J&&Y){if(e)return new he(Y,q,e);Z.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 u(Ae(),q)}async function Fe(){let e=J&&Y?await je():null,t=await Pe(e),n=new ae(K,X,t),r=new Oe(G,t,X,Z,()=>e?fe(e,Z):Z.requestPermission()),i=new ne(G,{conn:t,view:X,input:n,notifications:Z,watchers:r}),a=document.createElement(`video`);a.muted=!0,a.autoplay=!0,a.playsInline=!0,a.setAttribute(`playsinline`,``),a.style.display=`none`,G.append(a);let o=document.createElement(`video`);o.muted=!0,o.autoplay=!0,o.playsInline=!0,o.setAttribute(`playsinline`,``),o.style.display=`none`,G.append(o);let s=!1,c=!1,l=null,u=0,d=e=>e.w>=.999&&e.h>=.999,f=null,p=0,m=0,h=!1,g=0,_=document.createElement(`div`);_.className=`wd-sharpen hidden`,_.setAttribute(`aria-label`,`updating view`),G.append(_);let v=()=>{h=!1,_.classList.add(`hidden`)},y=e=>{let n=e.w>=.92&&e.h>=.92?{x:0,y:0,w:1,h:1}:e,r=d(n);if(l){if(r&&d(l))return;if(!r){let e=.08;if(Math.abs(n.x-l.x)<n.w*e&&Math.abs(n.y-l.y)<n.h*e&&Math.abs(n.w-l.w)<n.w*e&&Math.abs(n.h-l.h)<n.h*e)return}}l=n,p=Date.now(),m=0,h=!f||!b(n,f),h&&(g=Date.now()),t.send({type:`set-viewport`,x:n.x,y:n.y,w:n.w,h:n.h})},b=(e,t)=>Math.abs(e.x-t.x)<.005&&Math.abs(e.y-t.y)<.005&&Math.abs(e.w-t.w)<.005&&Math.abs(e.h-t.h)<.005;window.setInterval(()=>{let e=Date.now()-g;if(_.classList.toggle(`hidden`,!(h&&e>1200&&e<3e4)),!(!l||!f)){if(b(f,l)){m=0;return}Date.now()-p<1200||m>=3||(m+=1,p=Date.now(),t.send({type:`set-viewport`,x:l.x,y:l.y,w:l.w,h:l.h}))}},1e3);let x=!1,S=!1,C=()=>{let e=X.getZoom()>1.01;window.clearTimeout(u),u=window.setTimeout(()=>{u=0,y(X.getZoom()>1.01?X.getVisibleRegion():{x:0,y:0,w:1,h:1})},e?400:0)};X.setOnView(()=>{x=!0,C()}),X.setOnGesture(e=>{e?(S=u!==0,S&&(window.clearTimeout(u),u=0),x=!1):!x&&S&&(S=!1,C())}),t.on(`status`,e=>{i.setStatus(e),e===`connected`?(c=!1,Q.hide()):(!s||c)&&Q.show(s?`Reconnecting…`:void 0),e===`disconnected`&&(l=null,f=null,v())}),t.on(`transport`,e=>i.setTransport(e)),t.on(`welcome`,e=>{let t=!s;s=!0,Q.hide(),Ne.hide(),X.setScreen(e.screen),i.setWelcome(e),t?(X.setZoom(1),l=null,y({x:0,y:0,w:1,h:1})):(l=null,window.clearTimeout(u),u=0,y(X.getZoom()>1.01?X.getVisibleRegion():{x:0,y:0,w:1,h:1}))}),t.on(`versionMismatch`,e=>Ie(e.agentVersion)),t.on(`pinRequired`,e=>{Q.hide(),Ne.show(e,e=>t.submitPin(e))}),t.on(`presence`,e=>i.setPresence(e));let w=0,T=0,E=0,D=()=>i.setAlertCount(w+T+E);if(t.on(`watchers`,e=>{w=e.length,r.setRegions(e),D()}),t.on(`timers`,e=>{T=e.length,r.setTimers(e),D()}),t.on(`monitors`,e=>{E=e.length,r.setMonitors(e),D()}),t.on(`monitorAlways`,e=>{r.setAlwaysAgents(e)}),t.on(`screenRegion`,e=>{f={x:e.x,y:e.y,w:e.w,h:e.h},e.active&&v();let t=d(e)?null:e;e.active?X.setFrameRegionActive(t):X.setFrameRegion(t)}),t.on(`videoTrack`,e=>{if(!e){a.srcObject=null,X.setVideoSource(null);return}a.srcObject=e,a.play().catch(()=>{}),X.setVideoSource(a)}),t.on(`overviewTrack`,e=>{if(!e){o.srcObject=null,X.setOverviewSource(null);return}o.srcObject=e,o.play().catch(()=>{}),X.setOverviewSource(o)}),t.on(`netStats`,({fps:e,rtt:t})=>i.setNetStats(e,t)),t.on(`screenMeta`,({screen:e,activeDisplay:t})=>{X.setScreen(e),t!==void 0&&i.setActiveDisplay(t)}),t.on(`notification`,e=>Z.show(e)),t.on(`error`,e=>i.flashError(e)),e){let n=!1;t.on(`welcome`,()=>{n||Z.permission!==`granted`||(n=!0,fe(e,Z))})}let O=()=>{document.hidden||t.isHealthy()||(c=!0,Q.show(`Reconnecting…`),t.wake())};document.addEventListener(`visibilitychange`,()=>{t.setVisible(!document.hidden),document.hidden||O()}),window.addEventListener(`pageshow`,O),window.addEventListener(`pagehide`,e=>{e.persisted||t.close()}),q||i.flashError(`No pairing token in the URL (#t=…). Re-open the link/QR from the agent.`),t.connect()}Fe();function $(e,t){let n=document.createElement(e);return n.className=t,n}function Ie(e){let t=`wd-outdated-banner`;if(document.getElementById(t))return;let n=document.createElement(`div`);n.id=t,n.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)`,n.innerHTML=`⚠️ This WhipDesk agent${e?` (agent ${e})`:``} 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>.`;let r=document.createElement(`button`);r.textContent=`✕`,r.setAttribute(`aria-label`,`Dismiss`),r.style.cssText=`position:absolute;right:8px;top:6px;background:none;border:none;color:#fff;font-size:16px;cursor:pointer`,r.onclick=()=>n.remove(),n.appendChild(r),document.body.appendChild(n)}
|
|
Binary file
|
|
@@ -2,13 +2,41 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* WhipDesk FCM background handler (service worker).
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* Shows a notification for every incoming web push even when the controller PWA is closed.
|
|
6
|
+
*
|
|
7
|
+
* IMPORTANT: the `push` listener is registered SYNCHRONOUSLY at script evaluation and displays
|
|
8
|
+
* the notification itself — no Firebase SDK. The previous version initialized the FCM compat SDK
|
|
9
|
+
* asynchronously (after fetching ./firebase.json), but a push that WAKES a cold service worker
|
|
10
|
+
* dispatches before any late-registered handler exists, so nothing called showNotification and
|
|
11
|
+
* Chrome displayed its generic "This site has been updated in the background" fallback instead
|
|
12
|
+
* of the real alert. Token registration lives page-side (src/push.ts); the worker only needs to
|
|
13
|
+
* parse the FCM webpush payload ({ notification: { title, body, ... }, data: {...} }) and render
|
|
14
|
+
* it, which needs no SDK at all.
|
|
9
15
|
*/
|
|
10
|
-
|
|
11
|
-
|
|
16
|
+
self.addEventListener("push", (event) => {
|
|
17
|
+
let payload = {};
|
|
18
|
+
try {
|
|
19
|
+
payload = event.data ? event.data.json() : {};
|
|
20
|
+
} catch (e) {
|
|
21
|
+
/* non-JSON push — fall through to the generic title below */
|
|
22
|
+
}
|
|
23
|
+
const n = payload.notification || {};
|
|
24
|
+
const d = payload.data || {};
|
|
25
|
+
const title = n.title || d.title || "WhipDesk";
|
|
26
|
+
const body = n.body || d.body || "";
|
|
27
|
+
event.waitUntil(
|
|
28
|
+
self.clients.matchAll({ type: "window", includeUncontrolled: true }).then((wins) => {
|
|
29
|
+
// App open and visible => its own in-app alerts cover this; skip the OS notification
|
|
30
|
+
// (same policy the FCM SDK applied).
|
|
31
|
+
if (wins.some((w) => w.visibilityState === "visible")) return;
|
|
32
|
+
return self.registration.showNotification(title, {
|
|
33
|
+
body,
|
|
34
|
+
tag: n.tag || d.tag || "whipdesk-alert",
|
|
35
|
+
renotify: true,
|
|
36
|
+
});
|
|
37
|
+
}),
|
|
38
|
+
);
|
|
39
|
+
});
|
|
12
40
|
|
|
13
41
|
self.addEventListener("notificationclick", (event) => {
|
|
14
42
|
event.notification.close();
|
|
@@ -21,23 +49,3 @@ self.addEventListener("notificationclick", (event) => {
|
|
|
21
49
|
}),
|
|
22
50
|
);
|
|
23
51
|
});
|
|
24
|
-
|
|
25
|
-
(async () => {
|
|
26
|
-
try {
|
|
27
|
-
const res = await fetch("./firebase.json", { cache: "no-store" });
|
|
28
|
-
if (!res.ok) return;
|
|
29
|
-
const config = await res.json();
|
|
30
|
-
firebase.initializeApp(config);
|
|
31
|
-
const messaging = firebase.messaging();
|
|
32
|
-
messaging.onBackgroundMessage((payload) => {
|
|
33
|
-
const n = (payload && payload.notification) || {};
|
|
34
|
-
self.registration.showNotification(n.title || "WhipDesk", {
|
|
35
|
-
body: n.body || "",
|
|
36
|
-
tag: (payload && payload.data && payload.data.tag) || "whipdesk-alert",
|
|
37
|
-
renotify: true,
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
} catch (e) {
|
|
41
|
-
/* no config / unsupported browser — background push stays disabled */
|
|
42
|
-
}
|
|
43
|
-
})();
|
|
@@ -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-_PAhuG6R.js"></script>
|
|
22
|
+
<link rel="stylesheet" crossorigin href="./assets/index-C_O8Rc7r.css">
|
|
23
23
|
</head>
|
|
24
24
|
<body>
|
|
25
25
|
<div id="app"></div>
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "whipdesk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
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",
|
|
7
|
-
"homepage": "https://
|
|
7
|
+
"homepage": "https://whipdesk.com",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
10
|
"url": "git+https://github.com/BinaryBananaLLC/WhipDesk.git",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
:root{--bg:#f4f6f9;--panel:#fff;--panel-2:#eef1f5;--border:#d6dce4;--text:#1a2330;--muted:#5f6b7a;--accent:#2f6fed;--accent-2:#1aa66b;--warn:#c77700;--error:#d83b3b;--shadow:#141e3229;--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:#00000080;--screen-bg:#000}}*{box-sizing:border-box;-webkit-tap-highlight-color:transparent}html,body{background:var(--bg);height:100%;color:var(--text);overscroll-behavior:none;margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,system-ui,sans-serif;overflow:hidden}#app{-webkit-user-select:none;user-select:none;position:fixed;inset:0}input,textarea{-webkit-user-select:text;user-select:text}.wd-screen{background:var(--screen-bg);touch-action:none;width:100%;height:100%;display:block;position:absolute;inset:0}.wd-statusbar{top:calc(env(safe-area-inset-top,0px) + 8px);background:var(--panel);border:1px solid var(--border);max-width:calc(100% - 64px);min-height:38px;box-shadow:0 2px 10px var(--shadow);z-index:5;cursor:pointer;border-radius:10px;align-items:center;gap:8px;padding:6px 10px;font-size:12px;transition:max-width .25s,padding .25s;display:flex;position:absolute;left:8px}.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{background:var(--muted);border-radius:50%;flex:none;width:9px;height:9px}.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);white-space:nowrap;text-overflow:ellipsis;flex:auto;min-width:0;font-weight:600;overflow:hidden}.wd-watchers{color:var(--warn);white-space:nowrap;flex:none;font-weight:700}.wd-transport{letter-spacing:.4px;color:#fff;background:var(--accent-2);text-transform:uppercase;border-radius:6px;flex:none;padding:2px 6px;font-size:11px;font-weight:800}.wd-transport.hidden{display:none}.wd-transport[data-kind=turn]{color:#111;background:#e0a800}.wd-transport[data-kind=stun],.wd-transport[data-kind=direct]{background:var(--accent-2)}.wd-transport[data-kind=lan]{background:#3b82f6}.wd-bell{top:calc(env(safe-area-inset-top,0px) + 8px);z-index:6;border:1px solid var(--border);background:var(--panel);min-width:38px;min-height:38px;color:var(--text);box-shadow:0 2px 10px var(--shadow);cursor:pointer;border-radius:10px;place-items:center;padding:6px;display:grid;position:absolute;right:8px}.wd-badge{background:var(--accent);color:#fff;text-align:center;min-width:18px;height:18px;box-shadow:0 1px 4px var(--shadow);border-radius:9px;padding:0 5px;font-size:11px;font-weight:700;line-height:18px;position:absolute;top:-6px;right:-6px}.wd-ribbon{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);position:absolute;bottom:0;left:0;right:0}.wd-panel{flex-direction:column;display:flex}.wd-options{border-bottom:1px solid var(--border);padding:6px}.wd-pane{flex-wrap:wrap;justify-content:center;align-items:center;gap:5px;display:flex}.wd-pane-col{flex-direction:column;align-items:stretch;gap:8px}.wd-pane-single-row{scrollbar-width:none;flex-wrap:nowrap;justify-content:center;align-items:stretch;gap:4px;overflow-x:auto}.wd-pane-single-row::-webkit-scrollbar{display:none}.wd-pane-single-row .wd-group{flex:0 auto;gap:2px;min-width:0;padding:3px 4px}.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{gap:4px;padding:6px 9px;font-size:12px}@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{border:1px solid var(--border);width:min(880px,94vw);box-shadow:0 10px 34px var(--shadow);border-radius:16px;padding-bottom:0;bottom:16px;left:50%;right:auto;transform:translate(-50%)}.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{border:1px solid var(--border);background:var(--panel-2);border-radius:12px;flex-direction:column;align-items:stretch;gap:3px;padding:4px 6px;display:inline-flex}.wd-group-label{letter-spacing:.03em;text-transform:uppercase;color:var(--muted);white-space:nowrap;text-align:center;font-size:9px;font-weight:700}.wd-group-head{justify-content:space-between;align-items:center;gap:4px;display:flex}.wd-mode-toggle{border:1px solid var(--border);background:var(--panel);border-radius:999px;display:inline-flex;overflow:hidden}.wd-mode-btn{color:var(--muted);cursor:pointer;background:0 0;border:0;padding:4px 7px;font-size:10px;font-weight:700;line-height:1}.wd-mode-btn.on{background:var(--accent);color:#fff}.wd-group-items{flex-wrap:wrap;justify-content:center;align-items:center;gap:3px;display:flex}.wd-wrap{flex-wrap:wrap;justify-content:center;gap:6px;display:flex}.wd-btn-label{line-height:1}.wd-row{flex-wrap:wrap;justify-content:center;align-items:center;gap:6px;display:flex}.wd-tabs{align-items:center;gap:4px;padding:5px 6px;display:flex}.wd-tab{min-height:48px;color:var(--muted);cursor:pointer;touch-action:manipulation;background:0 0;border:1px solid #0000;border-radius:10px;flex-direction:column;flex:1;align-items:center;gap:2px;padding:6px 4px;font-size:11px;font-weight:600;display:flex}.wd-tab .wd-tab-label{line-height:1}.wd-tab.on{background:var(--panel-2);color:var(--accent);border-color:var(--border)}.wd-collapse{border:1px solid var(--border);background:var(--panel);min-width:44px;min-height:48px;color:var(--text);cursor:pointer;border-radius:10px;flex:none;place-items:center;display:grid}.wd-btn{border:1px solid var(--border);background:var(--panel);min-width:40px;min-height:40px;color:var(--text);cursor:pointer;-webkit-user-select:none;user-select:none;touch-action:manipulation;white-space:nowrap;border-radius:10px;justify-content:center;align-items:center;gap:7px;padding:6px 10px;font-size:13px;font-weight:600;line-height:1;display:inline-flex}.wd-btn svg{flex:none}.wd-btn.wd-icon-only{min-width:40px;padding:6px}.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{border:1px solid var(--border);border-radius:10px;display:inline-flex;overflow:hidden}.wd-seg-btn{background:var(--panel);min-height:44px;color:var(--muted);cursor:pointer;touch-action:manipulation;border:none;align-items:center;gap:6px;padding:8px 18px;font-size:15px;font-weight:600;display:inline-flex}.wd-seg-btn.on{background:var(--accent);color:#fff}.wd-zoom{text-align:center;font-variant-numeric:tabular-nums;min-width:52px;color:var(--text);font-weight:600}.wd-hint{text-align:center;color:var(--muted);flex-basis:100%;font-size:12px}.wd-type-input{resize:vertical;border:1px solid var(--border);background:var(--panel-2);width:100%;min-height:46px;color:var(--text);border-radius:10px;padding:10px;font-family:inherit;font-size:16px}.wd-keys-row{flex-wrap:wrap;justify-content:center;gap:6px;display:flex}.wd-type-actions{gap:6px;display:flex}.wd-type-actions .wd-btn{flex:1}.wd-monitor-list{flex-wrap:wrap;justify-content:center;gap:6px;display:flex}.wd-toasts{top:calc(env(safe-area-inset-top,0px) + 36px);z-index:9;pointer-events:none;flex-direction:column;gap:8px;display:flex;position:absolute;left:8px;right:8px}.wd-toast{background:var(--panel);border:1px solid var(--border);border-left-width:4px;border-radius:10px;flex-direction:column;gap:2px;padding:10px 12px;transition:opacity .3s,transform .3s;display:flex;box-shadow:0 6px 20px #0006}.wd-toast strong{font-size:14px}.wd-toast span{color:var(--muted);font-size:13px}.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{z-index:20;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);background:#06090eeb;place-items:center;padding:20px;display:grid;position:absolute;inset:0}.wd-pin-card{background:var(--panel);border:1px solid var(--border);text-align:center;border-radius:14px;width:100%;max-width:320px;padding:24px}.wd-pin-whip{object-fit:contain;width:64px;height:64px;margin:0 auto 12px;display:block}.wd-pin-card h2{margin:0 0 8px;font-size:20px}.wd-pin-msg{color:var(--muted);margin:0 0 16px;font-size:14px}.wd-pin-msg.err{color:var(--error)}.wd-pin-input{border:1px solid var(--border);background:var(--panel-2);width:100%;color:var(--text);text-align:center;letter-spacing:6px;border-radius:10px;margin-bottom:14px;padding:14px;font-size:22px}.wd-pin-submit{background:var(--accent);color:#fff;cursor:pointer;touch-action:manipulation;border:none;border-radius:12px;width:100%;min-height:50px;font-size:17px;font-weight:700}.wd-pin-submit:active{filter:brightness(.92);transform:translateY(1px)}.wd-pin-back{color:var(--muted);cursor:pointer;background:0 0;border:none;margin:14px auto 0;padding:6px 10px;font-size:13px;font-weight:600;display:block}.wd-pin-back:active{color:var(--text)}.wd-connecting{z-index:18;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);background:#06090eeb;place-items:center;padding:20px;display:grid;position:absolute;inset:0}.wd-connecting-card{text-align:center;width:100%;max-width:320px}.wd-connecting-whip{object-fit:contain;transform-origin:0 100%;width:56px;height:56px;margin:0 auto 18px;animation:1.4s ease-in-out infinite wd-whip-crack;display:block}.wd-connecting-spinner{border:3px solid #ffffff38;border-top-color:#4ea1ff;border-radius:50%;width:34px;height:34px;margin:0 auto 16px;animation:.8s linear infinite wd-spin}.wd-connecting-msg{color:#eef2f7;text-shadow:0 1px 2px #00000080;margin:0;font-size:15px;font-weight:600}@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{z-index:18;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);background:#06090e99;place-items:center;padding:16px;display:grid;position:absolute;inset:0}.wd-dialog{background:var(--panel);border:1px solid var(--border);border-radius:14px;width:100%;max-width:380px;padding:16px}.wd-dialog-head{justify-content:space-between;align-items:center;display:flex}.wd-dialog-head h2{margin:0;font-size:18px}.wd-dialog-x{border:1px solid var(--border);background:var(--panel-2);width:36px;height:36px;color:var(--text);cursor:pointer;border-radius:8px;place-items:center;display:grid}.wd-dialog-help{color:var(--muted);margin:8px 0 12px;font-size:13px}.wd-help-intro{margin:0 0 6px}.wd-help-list{flex-direction:column;gap:5px;margin:0 0 8px;padding-left:18px;display:flex}.wd-help-list strong{color:var(--text)}.wd-help-note{margin:0;font-style:italic}.wd-watch-list{flex-direction:column;gap:8px;margin-bottom:12px;display:flex}.wd-watch-row{border:1px solid var(--border);background:var(--panel-2);border-radius:10px;align-items:center;gap:8px;padding:8px 10px;display:flex}.wd-watch-name{text-align:left;color:var(--text);cursor:pointer;background:0 0;border:none;flex:1;padding:4px 0;font-family:inherit;font-size:14px;font-weight:600}.wd-watch-name:active{color:var(--accent)}.wd-timer-info{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.wd-timer-name{color:var(--text);align-items:center;gap:6px;font-size:14px;font-weight:600;display:flex}.wd-timer-name svg{color:var(--accent);flex:none}.wd-timer-remain{color:var(--muted);font-variant-numeric:tabular-nums;font-size:12px}.wd-timer-tag{letter-spacing:.5px;text-transform:uppercase;color:#fff;background:var(--accent);border-radius:6px;flex:none;padding:2px 6px;font-size:10px;font-weight:800}.wd-dialog-actions{gap:8px;margin-top:4px;display:flex}.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{letter-spacing:.4px;text-transform:uppercase;color:#fff;background:var(--muted);border-radius:6px;flex:none;padding:2px 6px;font-size:10px;font-weight:800}.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{justify-content:space-between;align-items:center;gap:8px;margin-bottom:6px;display:flex}.wd-mon-pick{flex-direction:column;gap:6px;max-height:40vh;margin-bottom:12px;display:flex;overflow-y:auto}.wd-mon-item{border:1px solid var(--border);background:var(--panel);width:100%;color:var(--text);cursor:pointer;text-align:left;border-radius:10px;justify-content:space-between;align-items:center;gap:10px;padding:9px 11px;display:flex}.wd-mon-item.on{border-color:var(--accent);box-shadow:inset 0 0 0 1px var(--accent)}.wd-mon-item-main{align-items:center;gap:8px;min-width:0;display:flex}.wd-mon-item-main svg{color:var(--accent);flex:none}.wd-mon-item-title{white-space:nowrap;text-overflow:ellipsis;font-size:13px;font-weight:600;overflow:hidden}.wd-mon-always{flex-direction:column;gap:8px;display:flex}.wd-check{color:var(--text);cursor:pointer;align-items:flex-start;gap:8px;font-size:13px;display:flex}.wd-check input{width:16px;height:16px;accent-color:var(--accent);flex:none;margin-top:1px}.wd-check-text{flex-direction:column;gap:2px;display:flex}.wd-check-sub{color:var(--muted);font-size:11px}.wd-form-row{flex-direction:column;gap:5px;margin-bottom:12px;display:flex}.wd-form-label{color:var(--muted);font-size:12px;font-weight:700}.wd-input{border:1px solid var(--border);background:var(--panel-2);width:100%;color:var(--text);border-radius:10px;padding:10px 12px;font-family:inherit;font-size:15px}.wd-input-area{resize:vertical;min-height:64px}.wd-form-duration{flex-wrap:wrap;align-items:center;gap:12px;display:flex}.wd-input-num{text-align:center;width:56px}.wd-form-unit{color:var(--muted);font-size:14px;font-weight:600}.wd-preset-row{flex-wrap:wrap;gap:6px;margin-bottom:10px;display:flex}.wd-preset{border:1px solid var(--border);background:var(--panel-2);min-width:48px;color:var(--text);cursor:pointer;border-radius:999px;flex:auto;padding:9px 10px;font-family:inherit;font-size:13px;font-weight:700}.wd-preset:active{background:var(--accent);color:#fff;border-color:#0000}.wd-stepper{align-items:center;gap:4px;display:flex}.wd-step-btn{border:1px solid var(--border);background:var(--panel-2);width:38px;height:38px;color:var(--text);cursor:pointer;touch-action:manipulation;border-radius:10px;flex:none;font-family:inherit;font-size:22px;font-weight:700;line-height:1}.wd-step-btn:active{background:var(--accent);color:#fff;border-color:#0000}.wd-form-target{flex-wrap:wrap;align-items:center;gap:10px;display:flex}.wd-form-target-status{color:var(--muted);font-size:13px}.wd-form-target-status.set{color:var(--accent-2);font-weight:700}.wd-pick-banner{top:calc(env(safe-area-inset-top,0px) + 56px);z-index:30;background:var(--accent);color:#fff;box-shadow:0 6px 20px var(--shadow);pointer-events:none;text-align:center;border-radius:999px;max-width:90%;padding:10px 16px;font-size:13px;font-weight:700;position:absolute;left:50%;transform:translate(-50%)}.wd-conn-row{border-bottom:1px solid var(--border);align-items:flex-start;gap:10px;padding:10px 0;display:flex}.wd-conn-label{width:92px;color:var(--muted);flex:none;padding-top:2px;font-size:13px;font-weight:700}.wd-conn-value{color:var(--text);word-break:break-word;flex:1;font-size:15px;font-weight:600}.wd-conn-route{flex-direction:column;flex:1;gap:4px;display:flex}.wd-conn-route .wd-transport{align-self:flex-start;position:static}.wd-conn-desc{color:var(--muted);font-size:12.5px}.wd-conn-status{flex:1;align-items:center;gap:8px;display:flex}.wd-conn-error{border:1px solid var(--error);color:var(--error);word-break:break-word;background:#dc35451f;border-radius:10px;margin-top:12px;padding:9px 11px;font-size:12.5px;font-weight:600}.wd-disconnect{background:var(--error);color:#fff;border-color:#0000;justify-content:center;width:100%;min-height:46px;margin-top:14px;font-size:15px;font-weight:700}.wd-support-link{border:1px solid var(--border);color:var(--muted);cursor:pointer;background:0 0;border-radius:999px;align-self:flex-start;align-items:center;gap:6px;margin-top:8px;padding:5px 12px;font-size:12.5px;font-weight:600;transition:color .15s,border-color .15s;display:inline-flex}.wd-support-link:hover,.wd-support-link:active{color:var(--accent);border-color:var(--accent)}.wd-support-link svg{flex:none;width:14px;height:14px}.wd-conn-feedback{border-top:1px solid var(--border);text-align:center;margin-top:14px;padding-top:12px}.wd-conn-feedback-text{color:var(--muted);margin:0;font-size:12.5px}.wd-conn-feedback-links{justify-content:center;gap:10px;margin-top:9px;display:flex}.wd-conn-feedback-link{border:1px solid var(--border);color:var(--text);background:0 0;border-radius:999px;align-items:center;gap:7px;padding:7px 16px;font-size:13px;font-weight:600;text-decoration:none;transition:color .15s,border-color .15s;display:inline-flex}.wd-conn-feedback-link:hover,.wd-conn-feedback-link:active{color:var(--accent);border-color:var(--accent)}.wd-conn-feedback-link svg{flex:none;width:16px;height:16px}.wd-pin-support{margin-top:16px}.wd-placing .wd-ribbon,.wd-placing .wd-statusbar,.wd-placing .wd-bell{display:none}.wd-place-layer{z-index:40;touch-action:none;background:0 0;position:absolute;inset:0}.wd-place-marker{z-index:41;pointer-events:none;border:2px solid #4ea1ff;border-radius:50%;width:46px;height:46px;position:absolute;transform:translate(-50%,-50%);box-shadow:0 0 0 2px #00000080,0 0 14px #4ea1ffb3}.wd-place-marker:before,.wd-place-marker:after{content:"";background:#4ea1ff;position:absolute}.wd-place-marker:before{width:2px;top:-8px;bottom:-8px;left:50%;transform:translate(-50%)}.wd-place-marker:after{height:2px;top:50%;left:-8px;right:-8px;transform:translateY(-50%)}.wd-place-bar{z-index:42;padding:12px 14px calc(env(safe-area-inset-bottom,0px) + 12px);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);border-top:1px solid var(--border);background:#0a0e14f0;flex-direction:column;gap:10px;display:flex;position:absolute;bottom:0;left:0;right:0}.wd-place-hint{color:#eef2f7;text-align:center;margin:0;font-size:13px;font-weight:600}.wd-place-text{width:100%}.wd-place-buttons{gap:8px;display:flex}.wd-place-buttons .wd-btn{flex:1;justify-content:center}.wd-perm-row{border:1px solid var(--border);background:var(--panel-2);border-radius:10px;align-items:center;gap:8px;margin-bottom:12px;padding:8px 10px;display:flex}.wd-perm-dot{background:var(--muted);border-radius:50%;flex:none;width:9px;height:9px}.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{color:var(--muted);flex:1;font-size:12px}.wd-perm-enable{flex:none;min-width:auto;min-height:32px;padding:4px 12px;font-size:12px}.wd-dialog .wd-go{justify-content:center;width:100%}.wd-selector{z-index:19;border:2px solid var(--accent);touch-action:none;background:#2f6fed24;border-radius:6px;position:absolute;box-shadow:0 0 0 9999px #06090e59}.wd-selector-move{background:var(--accent);color:#fff;cursor:move;touch-action:none;border-radius:8px 0;place-items:center;width:38px;height:38px;display:grid;position:absolute;top:-1px;left:-1px}.wd-selector-handle{background:var(--accent);border:3px solid var(--panel);cursor:nwse-resize;touch-action:none;border-radius:50%;width:28px;height:28px;position:absolute;bottom:-12px;right:-12px}.wd-selector-bar{white-space:nowrap;gap:8px;display:flex;position:absolute;bottom:-56px;left:50%;transform:translate(-50%)}.wd-selector-info{top:calc(env(safe-area-inset-top,0px) + 52px);z-index:20;background:var(--panel);border:1px solid var(--border);border-left:4px solid var(--accent);max-width:430px;box-shadow:0 6px 20px var(--shadow);color:var(--text);text-align:center;pointer-events:none;border-radius:10px;margin:0 auto;padding:8px 12px;font-size:12.5px;line-height:1.35;position:absolute;left:12px;right:12px}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./index.esm-mXKu2C3o.js","./index.esm-B3ZTr43m.js","./index.esm-CmSagqpw.js","./index.esm-wogdVWd2.js","./index.esm-BgjPHsdM.js","./index.esm-Di8jSGaG.js"])))=>i.map(i=>d[i]);
|
|
2
|
-
(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();function e(e){return typeof e==`object`&&!!e&&typeof e.type==`string`}var t=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 n(e){return new TextEncoder().encode(e)}function r(e){let n=new Uint32Array([1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225]),r=e.length*8,i=new Uint8Array((e.length+8>>6)+1<<6);i.set(e),i[e.length]=128;let a=new DataView(i.buffer);a.setUint32(i.length-4,r>>>0,!1),a.setUint32(i.length-8,Math.floor(r/4294967296),!1);let o=new Uint32Array(64);for(let e=0;e<i.length;e+=64){for(let t=0;t<16;t++)o[t]=a.getUint32(e+t*4,!1);for(let e=16;e<64;e++){let t=o[e-15],n=o[e-2],r=(t>>>7|t<<25)^(t>>>18|t<<14)^t>>>3,i=(n>>>17|n<<15)^(n>>>19|n<<13)^n>>>10;o[e]=o[e-16]+r+o[e-7]+i>>>0}let[r,i,s,c,l,u,d,f]=n;for(let e=0;e<64;e++){let n=(l>>>6|l<<26)^(l>>>11|l<<21)^(l>>>25|l<<7),a=l&u^~l&d,p=f+n+a+t[e]+o[e]>>>0,m=((r>>>2|r<<30)^(r>>>13|r<<19)^(r>>>22|r<<10))+(r&i^r&s^i&s)>>>0;f=d,d=u,u=l,l=c+p>>>0,c=s,s=i,i=r,r=p+m>>>0}n[0]=n[0]+r>>>0,n[1]=n[1]+i>>>0,n[2]=n[2]+s>>>0,n[3]=n[3]+c>>>0,n[4]=n[4]+l>>>0,n[5]=n[5]+u>>>0,n[6]=n[6]+d>>>0,n[7]=n[7]+f>>>0}let s=new Uint8Array(32);new DataView(s.buffer).setUint32(0,n[0],!1);for(let e=0;e<8;e++)new DataView(s.buffer).setUint32(e*4,n[e],!1);return s}var i=`0123456789abcdef`;function a(e){let t=``;for(let n of e)t+=i[n>>4]+i[n&15];return t}function o(e){return a(r(n(e)))}function s(e,t,n){let r=o(`${t}:${e}`);for(let e=1;e<n;e++)r=o(r);return r}function c(e,t){return o(`${e}:${t}`)}var l=class{token;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;constructor(e){this.token=e}on(e,t){this.handlers[e].add(t)}emit(e,t){for(let n of this.handlers[e])n(t)}setSender(e){this.sender=e}send(e){try{this.sender(JSON.stringify(e))}catch{}}sendHello(){this.send({type:`hello`,protocol:1,token:this.token,role:`controller`,client:{userAgent:navigator.userAgent}})}submitPin(e){if(this.rememberedPin=e,!this.challenge)return;let{salt:t,iterations:n,nonce:r}=this.challenge,i=s(e,t,n);this.send({type:`auth`,response:c(i,r)})}setVisible(e){this.send({type:`visibility`,visible:e})}handleText(t){let n;try{n=JSON.parse(t)}catch{return}if(!e(n))return;let r=n;switch(r.type){case`welcome`:this.challenge=null,r.protocol!==1&&this.emit(`versionMismatch`,{agentProtocol:r.protocol,clientProtocol:1,agentVersion:r.agent?.version}),this.emit(`welcome`,r),this.emit(`timers`,r.timers??[]),this.emit(`monitors`,r.monitors??[]),this.emit(`monitorAlways`,r.alwaysAgents??[]);break;case`auth-required`:this.challenge={salt:r.salt,iterations:r.iterations,nonce:r.nonce},this.rememberedPin&&!this.wrongPin?this.submitPin(this.rememberedPin):this.emit(`pinRequired`,{attemptsLeft:r.attemptsLeft,retry:this.wrongPin}),this.wrongPin=!1;break;case`screen-meta`:this.emit(`screenMeta`,{screen:r.screen,activeDisplay:r.activeDisplay});break;case`screen-region`:this.emit(`screenRegion`,{x:r.x,y:r.y,w:r.w,h:r.h,active:r.active});break;case`notification`:this.emit(`notification`,r);break;case`presence`:this.emit(`presence`,r.watchers);break;case`watchers`:this.emit(`watchers`,r.regions);break;case`timers`:this.emit(`timers`,r.timers);break;case`monitors`:this.emit(`monitors`,r.monitors);break;case`monitor-always-agents`:this.emit(`monitorAlways`,r.agents);break;case`monitor-sessions`:this.emit(`monitorSessions`,r.sessions);break;case`error`:r.code===`pin`?(this.wrongPin=!0,this.rememberedPin=null):this.emit(`error`,r.message);break;case`pong`:break}}},u=class{url;core;ws=null;pc=null;dc=null;mainXcv=null;overviewXcv=null;reconnectTimer=0;statsTimer=0;closedByUser=!1;constructor(e,t){this.url=e,this.core=new l(t),this.core.setSender(e=>{if(this.dc&&this.dc.readyState===`open`)try{this.dc.send(e)}catch{}})}on(e,t){this.core.on(e,t)}send(e){this.core.send(e)}submitPin(e){this.core.submitPin(e)}setVisible(e){this.core.setVisible(e)}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();let e=new WebSocket(this.url);this.ws=e;let t=t=>{if(e.readyState===WebSocket.OPEN)try{e.send(JSON.stringify(t))}catch{}},n=new RTCPeerConnection({iceServers:[]});this.pc=n;let r=n.createDataChannel(`whipdesk`);r.binaryType=`arraybuffer`,this.dc=r,r.onopen=()=>{this.core.sendHello(),this.core.emit(`status`,`connected`),this.core.emit(`transport`,`LAN`),this.startStatsPoll(n)},r.onclose=()=>this.core.emit(`status`,`disconnected`),r.onmessage=e=>{typeof e.data==`string`&&this.core.handleText(e.data)};try{this.mainXcv=n.addTransceiver(`video`,{direction:`recvonly`}),this.overviewXcv=n.addTransceiver(`video`,{direction:`recvonly`})}catch{}n.ontrack=e=>{let t=e.streams[0]??new MediaStream([e.track]);this.overviewXcv&&e.transceiver===this.overviewXcv?this.core.emit(`overviewTrack`,t):this.core.emit(`videoTrack`,t)},n.onicecandidate=e=>{e.candidate&&t({kind:`candidate`,candidate:e.candidate.toJSON()})},n.onconnectionstatechange=()=>{let e=n.connectionState;(e===`failed`||e===`disconnected`||e===`closed`)&&(this.core.emit(`status`,`disconnected`),this.scheduleReconnect())};let i=!1;e.onopen=async()=>{try{let e=await n.createOffer();await n.setLocalDescription(e),t({kind:`offer`,sdp:n.localDescription?.sdp??``})}catch{}},e.onmessage=e=>{let t;try{t=JSON.parse(typeof e.data==`string`?e.data:``)}catch{return}if(t.kind===`answer`&&t.sdp&&!i)i=!0,n.setRemoteDescription({type:`answer`,sdp:t.sdp});else if(t.kind===`candidate`&&t.candidate)try{n.addIceCandidate(t.candidate)}catch{}else t.kind===`error`&&this.core.emit(`error`,String(t.message??`host error`))},e.onclose=()=>{this.core.emit(`status`,`disconnected`),this.scheduleReconnect()},e.onerror=()=>{}}startStatsPoll(e){window.clearInterval(this.statsTimer),this.statsTimer=window.setInterval(async()=>{let t=null,n=0;try{(await e.getStats()).forEach(e=>{e.type===`inbound-rtp`&&e.kind===`video`?n=Math.max(n,Number(e.framesPerSecond??0)):e.type===`candidate-pair`&&e.nominated&&typeof e.currentRoundTripTime==`number`&&(t=e.currentRoundTripTime)})}catch{return}this.core.emit(`netStats`,{fps:Math.round(n),rtt:t==null?null:Math.round(t*1e3)})},1e3)}},d=``+new URL(`whip-CTqIatiK.png`,import.meta.url).href,f=[`Rounding up your AI agents…`,`Connecting to your agent farm…`,`Uncoiling the whip…`,`Negotiating the fastest route…`,`Tightening the leash…`,`Almost in the saddle…`],p=class{overlay;msg;rotateTimer=0;msgIndex=0;constructor(e){this.overlay=document.createElement(`div`),this.overlay.className=`wd-connecting hidden`;let t=document.createElement(`div`);t.className=`wd-connecting-card`;let n=document.createElement(`img`);n.className=`wd-connecting-whip`,n.src=d,n.alt=`WhipDesk`,n.decoding=`async`;let r=document.createElement(`div`);r.className=`wd-connecting-spinner`,this.msg=document.createElement(`p`),this.msg.className=`wd-connecting-msg`,this.msg.textContent=f[0],t.append(n,r,this.msg),this.overlay.appendChild(t),e.appendChild(this.overlay)}show(e){this.msg.textContent=e??f[this.msgIndex],this.overlay.classList.contains(`hidden`)&&(this.overlay.classList.remove(`hidden`),!e&&(this.rotateTimer=window.setInterval(()=>{this.msgIndex=(this.msgIndex+1)%f.length,this.msg.textContent=f[this.msgIndex]},2200)))}hide(){this.overlay.classList.add(`hidden`),window.clearInterval(this.rotateTimer),this.rotateTimer=0}},m={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"/>`},h=`http://www.w3.org/2000/svg`;function g(e,t=20){let n=document.createElementNS(h,`svg`);return n.setAttribute(`viewBox`,`0 0 24 24`),n.setAttribute(`width`,String(t)),n.setAttribute(`height`,String(t)),n.setAttribute(`fill`,`none`),n.setAttribute(`stroke`,`currentColor`),n.setAttribute(`stroke-width`,`2`),n.setAttribute(`stroke-linecap`,`round`),n.setAttribute(`stroke-linejoin`,`round`),n.setAttribute(`aria-hidden`,`true`),n.innerHTML=m[e],n}var _=[`en`],v=`en`;function y(){try{let e=localStorage.getItem(`wd-locale`);if(e&&_.includes(e))return e}catch{}let e=navigator.languages?.length?navigator.languages:[navigator.language];for(let t of e){let e=(t||``).toLowerCase().split(`-`)[0]??``;if(_.includes(e))return e}return v}function b(){return location.hostname.endsWith(`whipdesk.com`)?``:`https://whipdesk.com`}function x(){return`${b()}/${y()}/dashboard/`}function S(e){let t=e?`?next=${encodeURIComponent(e)}`:``;return`${b()}/${y()}/sign-in/${t}`}var C=`https://donate.stripe.com/6oU5kE19N5v35652n88so01`,w=`https://github.com/BinaryBananaLLC/WhipDesk/`,ee=`https://www.reddit.com/r/WhipDesk/`,te=[[`Esc`,`Escape`],[`Tab`,`Tab`],[`⌫`,`Backspace`],[`⏎`,`Enter`],[`←`,`ArrowLeft`],[`↑`,`ArrowUp`],[`↓`,`ArrowDown`],[`→`,`ArrowRight`]];function T(e,t,n){let r=document.createElement(e);return t&&(r.className=t),n!==void 0&&(r.textContent=n),r}function E(e,...t){let n=T(`div`,`wd-group`);n.appendChild(T(`span`,`wd-group-label`,e));let r=T(`div`,`wd-group-items`);return r.append(...t),n.appendChild(r),n}function D(e,t){let n=0,r=0,i=()=>{window.clearTimeout(n),window.clearInterval(r)};e.addEventListener(`pointerdown`,e=>{e.preventDefault(),t(),n=window.setTimeout(()=>{r=window.setInterval(t,80)},350)});for(let t of[`pointerup`,`pointercancel`,`pointerleave`])e.addEventListener(t,i);return e}function O(e,t=`wd-btn`){return T(`button`,t,e)}function k(e,t=``,n=`wd-btn`){let r=T(`button`,n);if(r.appendChild(g(e)),t){let e=T(`span`,`wd-btn-label`,t);r.appendChild(e)}else r.classList.add(`wd-icon-only`),r.setAttribute(`aria-label`,e);return r}function A(e,t,n){let r=T(`a`,`wd-conn-feedback-link`);return r.href=n,r.target=`_blank`,r.rel=`noopener noreferrer`,r.append(g(e,16),T(`span`,void 0,t)),r}function ne(e){switch(e.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 j(e){return e.toUpperCase()===`TURN`?`$ ${e}`:e}var re=class{root;deps;statusDot=T(`span`,`wd-dot`);statusText=T(`span`,`wd-status-text`,`Connecting…`);transportBadge=T(`span`,`wd-transport hidden`);watchersText=T(`span`,`wd-watchers`,``);alertBadge=T(`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;constructor(e,t){this.root=e,this.deps=t,this.build()}setStatus(e){this.status=e,this.statusDot.dataset.status=e,e===`connected`&&(this.lastError=``),this.renderStatusText(),this.updateStatusCollapse(),this.connectionOverlay&&!this.connectionOverlay.classList.contains(`hidden`)&&this.renderConnection()}setAlertCount(e){this.alertBadge.textContent=e>0?String(e):``,this.alertBadge.classList.toggle(`hidden`,e<=0)}setTransport(e){this.transport=e,this.transportBadge.textContent=j(e),this.transportBadge.dataset.kind=e.toLowerCase(),this.transportBadge.classList.toggle(`hidden`,!e),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(e){this.deviceName=e.agent.hostname,this.renderStatusText(),this.displays=e.displays??[],this.activeDisplay=e.activeDisplay??0,this.renderMonitors(),e.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(e){this.activeDisplay=e,this.renderMonitors()}setPresence(e){this.presenceCount=e,this.watchersText.textContent=e>1?`● ${e} watching`:``,this.connectionOverlay&&!this.connectionOverlay.classList.contains(`hidden`)&&this.renderConnection()}buildConnectionDialog(){let e=T(`div`,`wd-dialog-overlay hidden`);e.addEventListener(`pointerdown`,t=>{t.target===e&&e.classList.add(`hidden`)});let t=T(`div`,`wd-dialog`),n=T(`div`,`wd-dialog-head`);n.append(T(`h2`,``,`Connection`));let r=T(`button`,`wd-dialog-x`);r.appendChild(g(`x`)),r.onclick=()=>e.classList.add(`hidden`),n.appendChild(r);let i=T(`div`,`wd-conn-row`);i.append(T(`span`,`wd-conn-label`,`Status`));let a=T(`div`,`wd-conn-status`);this.connStatusDot=T(`span`,`wd-dot`),this.connStatusText=T(`span`,`wd-conn-value`,`—`),a.append(this.connStatusDot,this.connStatusText),i.appendChild(a);let o=T(`div`,`wd-conn-row`);o.append(T(`span`,`wd-conn-label`,`Machine`)),this.connName=T(`span`,`wd-conn-value`,`—`),o.appendChild(this.connName);let s=T(`div`,`wd-conn-row`);s.append(T(`span`,`wd-conn-label`,`Connection`)),this.connRoute=T(`div`,`wd-conn-route`),s.appendChild(this.connRoute);let c=T(`div`,`wd-conn-row`);c.append(T(`span`,`wd-conn-label`,`Viewers`)),this.connPresence=T(`span`,`wd-conn-value`,`1`),c.appendChild(this.connPresence);let l=T(`div`,`wd-conn-row`);l.append(T(`span`,`wd-conn-label`,`Speed (FPS/latency)`)),this.connSpeed=T(`span`,`wd-conn-value`,`—`),l.appendChild(this.connSpeed),this.connError=T(`div`,`wd-conn-error hidden`);let u=T(`button`,`wd-btn wd-disconnect`);u.append(g(`power`),T(`span`,`wd-btn-label`,`Disconnect`)),u.onclick=()=>this.disconnect();let d=T(`div`,`wd-conn-feedback`);d.append(T(`p`,`wd-conn-feedback-text`,`Noticed an issue or have an idea? Reach out:`));let f=T(`div`,`wd-conn-feedback-links`);f.append(A(`reddit`,`Reddit`,ee),A(`github`,`GitHub`,w)),d.appendChild(f),t.append(n,i,s,l,o,c,this.connError,u,d),e.appendChild(t),this.root.appendChild(e),this.connectionOverlay=e}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){let e=T(`span`,`wd-transport`);if(e.textContent=j(this.transport),e.dataset.kind=this.transport.toLowerCase(),this.connRoute.append(e,T(`span`,`wd-conn-desc`,ne(this.transport))),this.transport.toLowerCase()===`turn`){let e=T(`button`,`wd-support-link`);e.append(g(`heart`,14),T(`span`,void 0,`Support WhipDesk`)),e.onclick=()=>window.open(C,`_blank`,`noopener`),this.connRoute.append(e)}}else this.connRoute.append(T(`span`,`wd-conn-desc`,this.status===`connected`?`Detecting route…`:`Connecting…`));this.renderSpeed()}renderSpeed(){let e=`${this.netFps} FPS`;this.connSpeed.textContent=this.netRtt==null?e:`${e} / ${this.netRtt} ms`}setNetStats(e,t){this.netFps=e,this.netRtt=t,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=x()}flashError(e){this.lastError=e,this.connectionOverlay&&!this.connectionOverlay.classList.contains(`hidden`)&&this.renderConnection(),this.deps.notifications.show({type:`notification`,id:`err-${Date.now()}`,title:`WhipDesk`,body:e,level:`warning`,source:`client`,t:Date.now()})}build(){let e=T(`div`,`wd-statusbar`);e.append(this.statusDot,this.statusText,this.transportBadge,this.watchersText),e.onclick=()=>this.openConnection(),this.statusbar=e,this.buildConnectionDialog();let t=k(`bell`,``,`wd-bell`);t.setAttribute(`aria-label`,`Auto-Whips`),t.title=`Auto-Whips`,t.appendChild(this.alertBadge),t.onclick=()=>this.deps.watchers.open(),this.root.append(e,t);let n=T(`div`,`wd-ribbon`);this.panel=T(`div`,`wd-panel`),this.optionsArea=T(`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(let e of this.tabPanes.values())this.optionsArea.appendChild(e);let r=T(`div`,`wd-tabs`),i=(e,t,n)=>{let i=T(`button`,`wd-tab`);i.appendChild(g(t,18)),i.appendChild(T(`span`,`wd-tab-label`,n)),i.onclick=()=>this.selectTab(e),this.tabButtons.set(e,i),r.appendChild(i)};i(`viewer`,`eye`,`Viewer`),i(`interact`,`mouse`,`Interact`),i(`type`,`keyboard`,`Type`),i(`monitor`,`monitor`,`Monitor`),this.collapseBtn=k(`chevron-down`,``,`wd-collapse`),this.collapseBtn.onclick=()=>this.setCollapsed(!this.collapsed),r.appendChild(this.collapseBtn),this.panel.append(this.optionsArea,r),n.appendChild(this.panel),this.root.appendChild(n),this.selectTab(`viewer`)}buildViewerPane(){let{view:e,input:t}=this.deps,n=T(`div`,`wd-pane wd-pane-single-row`),r=D(k(`minus`,``,`wd-btn wd-icon-only`),()=>e.zoomBy(.9)),i=D(k(`plus`,``,`wd-btn wd-icon-only`),()=>e.zoomBy(1.11)),a=D(k(`scroll-up`,``,`wd-btn wd-icon-only`),()=>t.scrollStep(-6)),o=D(k(`scroll-down`,``,`wd-btn wd-icon-only`),()=>t.scrollStep(6)),s=k(`hand`,``,`wd-btn wd-icon-only`);s.setAttribute(`aria-label`,`Drag to scroll`),s.title=`Drag to scroll`;let c=k(`drag`,``,`wd-btn wd-icon-only`);c.setAttribute(`aria-label`,`Pan the zoomed screen with one finger`),c.title=`Pan the zoomed screen with one finger`,s.onclick=()=>{let e=!t.getDragScroll();t.setDragScroll(e),s.classList.toggle(`on`,e),c.classList.toggle(`on`,t.getPan())},c.onclick=()=>{let e=!t.getPan();t.setPan(e),c.classList.toggle(`on`,e),s.classList.toggle(`on`,t.getDragScroll())};let l=k(`pointer`,`Click`,`wd-btn wd-go`);return l.onclick=()=>t.click(`left`),n.append(E(`Zoom`,r,i),E(`Pan`,c),E(`Scroll`,a,o,s),E(`Pointer`,l)),n}buildInteractPane(){let e=T(`div`,`wd-pane`);return this.interactHost=T(`div`,`wd-pane`),e.appendChild(this.interactHost),this.renderInteract(),e}renderInteract(){let{input:e}=this.deps;this.interactHost.replaceChildren();let t=T(`div`,`wd-group`),n=T(`div`,`wd-group-head`),r=T(`span`,`wd-group-label`,`Mode`),i=T(`div`,`wd-mode-toggle`),a=T(`button`,`wd-mode-btn`,`Mouse`),o=T(`button`,`wd-mode-btn`,`Touch`);a.classList.toggle(`on`,this.interactMode===`mouse`),o.classList.toggle(`on`,this.interactMode===`touch`),a.onclick=()=>{this.interactMode=`mouse`,this.activeTab===`interact`&&this.deps.input.setInteraction(`mouse`),this.renderInteract()},o.onclick=()=>{this.interactMode=`touch`,this.activeTab===`interact`&&this.deps.input.setInteraction(`touch`),this.renderInteract()},i.append(a,o),n.append(r,i);let s=T(`div`,`wd-group-items`);if(this.interactMode===`mouse`){let t=k(`mouse-left`,`Left`);t.onclick=()=>e.click(`left`);let n=k(`mouse-right`,`Right`);n.onclick=()=>e.click(`right`);let r=k(`double-click`,`Double`);r.onclick=()=>e.multiClick(2);let i=k(`drag`,`Drag`);i.onclick=()=>{let t=!e.getDragLock();e.setDragLock(t),i.classList.toggle(`on`,t)},s.append(t,n,r,i)}else{let t=k(`pointer`,`Tap`);t.onclick=()=>e.click(`left`);let n=k(`hand`,`Hold`);n.onclick=()=>e.longPress();let r=D(k(`scroll-up`,`Up`,`wd-btn`),()=>e.swipe(0,-.25)),i=D(k(`scroll-down`,`Down`,`wd-btn`),()=>e.swipe(0,.25)),a=O(`←`);a.onclick=()=>e.swipe(-.25,0);let o=O(`→`);o.onclick=()=>e.swipe(.25,0);let c=O(`2 fingers`);c.onclick=()=>e.click(`right`),s.append(t,n,r,i,a,o,c)}t.append(n,s),this.interactHost.append(t)}buildTypePane(){let{conn:e,input:t}=this.deps,n=T(`div`,`wd-pane wd-pane-col`);this.promptInput=T(`textarea`,`wd-type-input`),this.promptInput.placeholder=`Type to send to the focused app (URL, command, message…)`,this.promptInput.rows=2;let r=T(`div`,`wd-wrap`);for(let[t,n]of te){let i=O(t);i.onclick=()=>e.send({type:`key`,key:n}),r.appendChild(i)}let i=O(`Double-click`);i.onclick=()=>t.multiClick(2);let a=O(`Triple-click`);a.onclick=()=>t.multiClick(3);let o=k(`insert`,`Insert`);o.onclick=()=>this.sendText(!1);let s=k(`send`,`Send`,`wd-btn wd-go`);return s.onclick=()=>this.sendText(!0),r.append(i,a,o,s),n.append(this.promptInput,r),n}buildMonitorPane(){let e=T(`div`,`wd-pane`);return this.monitorList=T(`div`,`wd-monitor-list`),e.appendChild(this.monitorList),this.renderMonitors(),e}renderMonitors(){if(this.monitorList){if(this.monitorList.replaceChildren(),this.displays.length===0){this.monitorList.appendChild(T(`span`,`wd-hint`,`Single display`));return}this.displays.forEach((e,t)=>{let n=O(`${t+1}. ${e.name}${e.primary?` ★`:``}`);n.classList.toggle(`on`,e.id===this.activeDisplay),n.onclick=()=>{this.deps.conn.send({type:`select-display`,id:e.id}),this.activeDisplay=e.id,this.renderMonitors()},this.monitorList.appendChild(n)})}}selectTab(e){if(this.activeTab!==null&&e===this.activeTab){this.setCollapsed(!this.collapsed);return}this.activeTab=e,this.collapsed&&this.setCollapsed(!1);for(let[t,n]of this.tabButtons)n.classList.toggle(`on`,t===e);for(let[t,n]of this.tabPanes)n.classList.toggle(`hidden`,t!==e);this.deps.input.setInteraction(e===`interact`?this.interactMode:`viewer`),e===`type`&&window.setTimeout(()=>this.promptInput.focus(),50)}setCollapsed(e){this.collapsed=e,this.optionsArea.classList.toggle(`hidden`,e),this.collapseBtn.replaceChildren(g(e?`chevron-up`:`chevron-down`))}sendText(e){let t=this.promptInput.value;t&&(this.deps.conn.send({type:`type`,text:t,submit:e}),this.promptInput.value=``)}};function M(e){return e<0?0:e>1?1:e}var N=2.5,ie=250,ae=500,oe=8,se=class{canvas;view;conn;cb;interaction=`viewer`;dragLock=!1;dragScroll=!1;pan=!1;holdingLeft=!1;cursor={nx:.5,ny:.5};pointers=new Map;longPressTimer=0;twoFinger=null;suppressTap=!1;constructor(e,t,n,r={}){this.canvas=e,this.view=t,this.conn=n,this.cb=r,e.style.touchAction=`none`,e.addEventListener(`pointerdown`,e=>this.onDown(e)),e.addEventListener(`pointermove`,e=>this.onMove(e)),e.addEventListener(`pointerup`,e=>this.onUp(e)),e.addEventListener(`pointercancel`,e=>this.onUp(e)),e.addEventListener(`contextmenu`,e=>e.preventDefault()),this.view.setCursor(this.cursor.nx,this.cursor.ny)}setInteraction(e){this.interaction=e,e===`viewer`&&(this.dragLock=!1)}getInteraction(){return this.interaction}setCallbacks(e){this.cb=e}setDragLock(e){this.dragLock=e}getDragLock(){return this.dragLock}setDragScroll(e){this.dragScroll=e,e&&(this.pan=!1)}getDragScroll(){return this.dragScroll}setPan(e){this.pan=e,e&&(this.dragScroll=!1)}getPan(){return this.pan}isPanning(){return this.pan&&this.interaction===`viewer`}click(e,t=!1){this.send({type:`pointer`,action:`click`,button:e,double:t,x:this.cursor.nx,y:this.cursor.ny}),navigator.vibrate?.(15)}multiClick(e){for(let t=0;t<e;t++)this.send({type:`pointer`,action:`click`,button:`left`,x:this.cursor.nx,y:this.cursor.ny});navigator.vibrate?.(15)}longPress(e=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`}),e)}swipe(e,t){let n=M(this.cursor.nx),r=M(this.cursor.ny),i=M(this.cursor.nx+e),a=M(this.cursor.ny+t);this.send({type:`pointer`,action:`down`,button:`left`,x:n,y:r});for(let e=1;e<=6;e++){let t=e/6;window.setTimeout(()=>{this.send({type:`pointer`,action:`move`,x:n+(i-n)*t,y:r+(a-r)*t}),e===6&&(this.moveCursor(i,a),this.send({type:`pointer`,action:`up`,button:`left`}))},e*16)}navigator.vibrate?.(15)}scrollStep(e){this.send({type:`scroll`,dx:0,dy:e})}send(e){this.conn.send(e)}positionOf(e){let t=this.canvas.getBoundingClientRect();return{x:e.clientX-t.left,y:e.clientY-t.top}}moveCursor(e,t){this.cursor.nx=M(e),this.cursor.ny=M(t),this.view.setCursor(this.cursor.nx,this.cursor.ny),this.cb.onCursor?.(this.cursor.nx,this.cursor.ny)}onDown(e){this.canvas.setPointerCapture?.(e.pointerId);let t=this.positionOf(e);if(this.pointers.set(e.pointerId,{x:t.x,y:t.y,startX:t.x,startY:t.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(),ae)),!this.dragScroll&&!this.isPanning()&&(this.interaction===`mouse`||this.interaction===`viewer`))){let e=this.view.canvasToNorm(t.x,t.y);this.moveCursor(e.nx,e.ny)}}onLongPress(){let e=[...this.pointers.values()][0];!e||e.moved||e.consumed||(e.consumed=!0,this.send({type:`pointer`,action:`click`,button:`right`,x:this.cursor.nx,y:this.cursor.ny}),navigator.vibrate?.(20))}beginTwoFinger(){let e=[...this.pointers.values()];if(e.length<2)return;let t=e[0],n=e[1];this.twoFinger={dist:Math.hypot(t.x-n.x,t.y-n.y),mx:(t.x+n.x)/2,my:(t.y+n.y)/2,start:performance.now(),moved:!1}}onMove(e){let t=this.pointers.get(e.pointerId);if(!t)return;let n=this.positionOf(e),r=t.x,i=t.y;if(t.x=n.x,t.y=n.y,Math.hypot(n.x-t.startX,n.y-t.startY)>oe&&(t.moved=!0),this.pointers.size>=2&&this.twoFinger){this.onTwoFingerMove();return}if(this.pointers.size!==1||t.consumed||!t.moved)return;window.clearTimeout(this.longPressTimer);let a=n.x-r,o=n.y-i;if(this.isPanning()){this.view.panByCanvasPixels(a,o);return}if(this.interaction===`touch`||this.dragScroll){this.send({type:`scroll`,dx:Math.round(-a/N),dy:Math.round(-o/N)});return}let s=this.view.canvasToNorm(n.x,n.y);this.moveCursor(s.nx,s.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(){let e=[...this.pointers.values()];if(e.length<2||!this.twoFinger)return;let t=e[0],n=e[1],r=Math.hypot(t.x-n.x,t.y-n.y),i=(t.x+n.x)/2,a=(t.y+n.y)/2,o=r-this.twoFinger.dist,s=i-this.twoFinger.mx,c=a-this.twoFinger.my;if(Math.abs(o)>6){this.twoFinger.moved=!0,this.view.zoomAround(1+o/200,i,a),this.twoFinger.dist=r;return}(Math.abs(c)>2||Math.abs(s)>2)&&(this.twoFinger.moved=!0,this.view.getZoom()>1?this.view.panByCanvasPixels(s,c):this.send({type:`scroll`,dx:Math.round(-s/N),dy:Math.round(-c/N)}),this.twoFinger.mx=i,this.twoFinger.my=a)}onUp(e){let t=this.pointers.size,n=this.pointers.get(e.pointerId);if(this.pointers.delete(e.pointerId),window.clearTimeout(this.longPressTimer),this.pointers.size===0&&this.view.endViewGesture(),t===2){let e=this.twoFinger;e&&!e.moved&&this.interaction===`mouse`&&performance.now()-e.start<ie&&(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(let e of this.pointers.values())e.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}let r=performance.now()-n.startT;if(!(!(!n.moved&&r<ie&&this.pointers.size===0)||this.dragScroll)&&(this.interaction===`mouse`||this.interaction===`touch`)){let e=this.view.canvasToNorm(n.startX,n.startY);this.moveCursor(e.nx,e.ny),this.send({type:`pointer`,action:`click`,button:`left`,x:e.nx,y:e.ny}),navigator.vibrate?.(12)}}},ce=class{container;constructor(e){this.container=e}async requestPermission(){try{`Notification`in window&&Notification.permission==="default"&&await Notification.requestPermission()}catch{}}get permission(){return`Notification`in window?Notification.permission:`unsupported`}flash(e,t,n=`info`){this.toast({type:`notification`,id:`flash-${Date.now()}`,title:e,body:t,level:n,source:`client`,t:Date.now()})}show(e){this.toast(e);try{`Notification`in window&&Notification.permission===`granted`&&new Notification(e.title,{body:e.body,tag:e.id})}catch{}navigator.vibrate?.(e.level===`error`?[60,40,60]:40)}toast(e){let t=document.createElement(`div`);t.className=`wd-toast wd-${e.level}`;let n=document.createElement(`strong`);if(n.textContent=e.title,t.appendChild(n),e.body){let n=document.createElement(`span`);n.textContent=e.body,t.appendChild(n)}this.container.appendChild(t),window.setTimeout(()=>{t.classList.add(`wd-hide`),window.setTimeout(()=>t.remove(),300)},5e3)}},le=class{overlay;input;message;onSubmit=null;constructor(e){this.overlay=document.createElement(`div`),this.overlay.className=`wd-pin-overlay hidden`;let t=document.createElement(`div`);t.className=`wd-pin-card`;let n=document.createElement(`img`);n.className=`wd-pin-whip`,n.src=d,n.alt=`WhipDesk`,n.decoding=`async`;let r=document.createElement(`h2`);r.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`,e=>{e.key===`Enter`&&this.submit()});let i=document.createElement(`button`);i.className=`wd-pin-submit`,i.textContent=`Unlock`,i.onclick=()=>this.submit();let a=document.createElement(`button`);a.type=`button`,a.className=`wd-support-link wd-pin-support`,a.append(g(`heart`,14));let o=document.createElement(`span`);o.textContent=`Support WhipDesk`,a.append(o),a.onclick=()=>window.open(C,`_blank`,`noopener`);let s=document.createElement(`button`);s.className=`wd-pin-back`,s.textContent=`← Back to dashboard`,s.onclick=()=>{window.location.href=x()},t.append(n,r,this.message,this.input,i,a,s),this.overlay.appendChild(t),e.appendChild(this.overlay)}show(e,t){this.onSubmit=t,this.input.value=``,e.retry?(this.message.textContent=e.attemptsLeft>0?`Wrong PIN — ${e.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(){let e=this.input.value.trim();if(e.length<4){this.message.textContent=`PIN is at least 4 characters.`,this.message.classList.add(`err`);return}this.onSubmit?.(e),this.message.textContent=`Checking…`,this.message.classList.remove(`err`)}},ue=`modulepreload`,de=function(e,t){return new URL(e,t).href},P={},F=function(e,t,n){let r=Promise.resolve();if(t&&t.length>0){let e=document.getElementsByTagName(`link`),i=document.querySelector(`meta[property=csp-nonce]`),a=i?.nonce||i?.getAttribute(`nonce`);function o(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}function s(e){return import.meta.resolve?import.meta.resolve(e):new URL(e,new URL(`../../../src/node/plugins/importAnalysisBuild.ts`,import.meta.url)).href}r=o(t.map(t=>{if(t=de(t,n),t=s(t),t in P)return;P[t]=!0;let r=t.endsWith(`.css`);for(let n=e.length-1;n>=0;n--){let i=e[n];if(i.href===t&&(!r||i.rel===`stylesheet`))return}let i=document.createElement(`link`);if(i.rel=r?`stylesheet`:ue,r||(i.as=`script`),i.crossOrigin=``,i.href=t,a&&i.setAttribute(`nonce`,a),document.head.appendChild(i),r)return new Promise((e,n)=>{i.addEventListener(`load`,e),i.addEventListener(`error`,()=>n(Error(`Unable to preload CSS for ${t}`)))})}))}function i(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(t=>{for(let e of t||[])e.status===`rejected`&&i(e.reason);return e().catch(i)})};async function I(e,t){if(e.vapidKey&&!(!(`serviceWorker`in navigator)||!(`Notification`in window)))try{let{initializeApp:n,getApps:r}=await F(async()=>{let{initializeApp:e,getApps:t}=await import(`./index.esm-mXKu2C3o.js`);return{initializeApp:e,getApps:t}},__vite__mapDeps([0,1]),import.meta.url),{getAuth:i}=await F(async()=>{let{getAuth:e}=await import(`./index.esm-CmSagqpw.js`);return{getAuth:e}},__vite__mapDeps([2,1]),import.meta.url),{getFirestore:a,doc:o,setDoc:s,serverTimestamp:c}=await F(async()=>{let{getFirestore:e,doc:t,setDoc:n,serverTimestamp:r}=await import(`./index.esm-wogdVWd2.js`);return{getFirestore:e,doc:t,setDoc:n,serverTimestamp:r}},__vite__mapDeps([3,1]),import.meta.url),{getMessaging:l,getToken:u,onMessage:d,isSupported:f}=await F(async()=>{let{getMessaging:e,getToken:t,onMessage:n,isSupported:r}=await import(`./index.esm-BgjPHsdM.js`);return{getMessaging:e,getToken:t,onMessage:n,isSupported:r}},__vite__mapDeps([4,1]),import.meta.url);if(!await f().catch(()=>!1))return;let p=r()[0]??n(e),m=i(p).currentUser;if(!m||Notification.permission==="default"&&await Notification.requestPermission()!==`granted`||Notification.permission!==`granted`)return;let h=await navigator.serviceWorker.register(`./firebase-messaging-sw.js`),g=l(p),_=await u(g,{vapidKey:e.vapidKey,serviceWorkerRegistration:h});if(!_)return;await s(o(a(p),`users`,m.uid,`fcmTokens`,_),{token:_,ua:navigator.userAgent,updatedAt:c()},{merge:!0}),d(g,e=>{let n=e.notification;t.show({type:`notification`,id:`push-${Date.now()}`,title:n?.title??`WhipDesk`,body:n?.body,level:`info`,source:`push`,t:Date.now()})})}catch{}}var fe=[{urls:`stun:turn-us1.whipdesk.com:3478`}];async function pe(e){try{let t=await e.getStats(),n=``,r=``;t.forEach(e=>{e.type===`transport`&&e.selectedCandidatePairId&&(n=e.selectedCandidatePairId)}),t.forEach(e=>{e.type===`candidate-pair`&&!r&&(e.id===n||e.nominated&&e.state===`succeeded`)&&(r=e.localCandidateId)});let i=``;if(t.forEach(e=>{e.id===r&&e.type===`local-candidate`&&(i=e.candidateType)}),i===`relay`)return`TURN`;if(i===`srflx`||i===`prflx`)return`STUN`;if(i===`host`)return`LAN`}catch{}return null}var me=class{deviceId;config;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;constructor(e,t,n){this.deviceId=e,this.config=n,this.core=new l(t),this.core.setSender(e=>{if(this.dc&&this.dc.readyState===`open`)try{this.dc.send(e)}catch{}})}on(e,t){this.core.on(e,t)}send(e){this.core.send(e)}submitPin(e){this.core.submitPin(e)}setVisible(e){this.core.setVisible(e)}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(e=>{this.core.emit(`error`,`Remote connect failed: ${e.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`);let{initializeApp:e,getApps:t}=await F(async()=>{let{initializeApp:e,getApps:t}=await import(`./index.esm-mXKu2C3o.js`);return{initializeApp:e,getApps:t}},__vite__mapDeps([0,1]),import.meta.url),{getAuth:n}=await F(async()=>{let{getAuth:e}=await import(`./index.esm-CmSagqpw.js`);return{getAuth:e}},__vite__mapDeps([2,1]),import.meta.url),{getDatabase:r,ref:i,child:a,push:o,set:s,onValue:c,remove:l,onDisconnect:u}=await F(async()=>{let{getDatabase:e,ref:t,child:n,push:r,set:i,onValue:a,remove:o,onDisconnect:s}=await import(`./index.esm-Di8jSGaG.js`);return{getDatabase:e,ref:t,child:n,push:r,set:i,onValue:a,remove:o,onDisconnect:s}},__vite__mapDeps([5,1]),import.meta.url),d=t()[0]??e(this.config),f=n(d);if(await new Promise(e=>{if(f.currentUser)return e();let t=f.onAuthStateChanged(()=>{t(),e()})}),!f.currentUser){window.location.assign(S(`/app/${window.location.hash}`));return}let p=f.currentUser.uid,m=r(d),h=await this.fetchIceServers(f.currentUser),g=new RTCPeerConnection({iceServers:h,iceTransportPolicy:`all`});this.pc=g;let _=g.createDataChannel(`whipdesk`);_.binaryType=`arraybuffer`,this.dc=_,this.wireDataChannel(_);try{this.mainXcv=g.addTransceiver(`video`,{direction:`recvonly`}),this.overviewXcv=g.addTransceiver(`video`,{direction:`recvonly`})}catch{}g.ontrack=e=>{let t=e.streams[0]??new MediaStream([e.track]);this.overviewXcv&&e.transceiver===this.overviewXcv?this.core.emit(`overviewTrack`,t):this.core.emit(`videoTrack`,t)};let v=!1,y=async()=>{if(v)return;let e=await pe(g);if(!e)return;v=!0,this.core.emit(`transport`,e);let t=e===`TURN`?`turn`:e===`STUN`?`stun`:`lan`;try{let{getFirestore:e,doc:n,setDoc:r,increment:i}=await F(async()=>{let{getFirestore:e,doc:t,setDoc:n,increment:r}=await import(`./index.esm-wogdVWd2.js`);return{getFirestore:e,doc:t,setDoc:n,increment:r}},__vite__mapDeps([3,1]),import.meta.url);r(n(e(d),`users`,p),{stats:{[t]:i(1)}},{merge:!0}).catch(()=>{})}catch{}};g.onconnectionstatechange=()=>{let e=g.connectionState;e===`connected`?(this.core.emit(`status`,`connected`),y(),this.startStatsPoll(g)):(e===`failed`||e===`disconnected`||e===`closed`)&&(this.core.emit(`status`,`disconnected`),this.scheduleReconnect())};let b=o(i(m,`signaling/${p}/${this.deviceId}`));this.pushCandidate=e=>{try{s(o(a(b,`offerCandidates`)),e)}catch{}},g.onicecandidate=e=>{e.candidate&&this.pushCandidate?.(e.candidate.toJSON())};let x=await g.createOffer();await g.setLocalDescription(x),await s(b,{offer:{sdp:g.localDescription?.sdp??``},controllerUid:p,createdAtMs:Date.now()}),u(b).remove(),this.deleteSessionDoc=()=>void l(b).catch(()=>{}),this.cleanupSignal=c(b,e=>{let t=e.val();if(!(!t||this.closed)&&(t.answer?.sdp&&!this.appliedAnswer&&(this.appliedAnswer=!0,g.setRemoteDescription({type:`answer`,sdp:t.answer.sdp})),t.answerCandidates&&this.appliedAnswer)){for(let[e,n]of Object.entries(t.answerCandidates))if(!this.appliedCandidates.has(e)){this.appliedCandidates.add(e);try{g.addIceCandidate(n)}catch{}}}})}startStatsPoll(e){window.clearInterval(this.statsTimer),this.lossBase=null,this.statsPolls=0,this.statsTimer=window.setInterval(()=>void this.pollStats(e),1e3)}lossBase=null;statsPolls=0;async pollStats(e){let t=null,n=0,r=0,i=0;try{(await e.getStats()).forEach(e=>{e.type===`inbound-rtp`&&e.kind===`video`?(n=Math.max(n,Number(e.framesPerSecond??0)),r+=Number(e.packetsLost??0),i+=Number(e.packetsReceived??0)):e.type===`candidate-pair`&&e.nominated&&typeof e.currentRoundTripTime==`number`&&(t=e.currentRoundTripTime)})}catch{return}if(this.core.emit(`netStats`,{fps:Math.round(n),rtt:t==null?null:Math.round(t*1e3)}),this.statsPolls+=1,!this.lossBase)this.lossBase={lost:r,received:i};else if(this.statsPolls%5==0){let e=Math.max(0,r-this.lossBase.lost),n=Math.max(0,i-this.lossBase.received);this.lossBase={lost:r,received:i};let a=e+n;if(a>0){let n=e/a*100;this.core.send({type:`video-stats`,lossPct:n,rttMs:t==null?void 0:Math.round(t*1e3)})}}}wireDataChannel(e){e.onopen=()=>this.core.sendHello(),e.onclose=()=>{this.core.emit(`status`,`disconnected`),this.scheduleReconnect()},e.onmessage=e=>{typeof e.data==`string`&&this.core.handleText(e.data)}}async fetchIceServers(e){let t=[...fe];try{let e=sessionStorage.getItem(`wd-ice`);if(e){let{servers:t,exp:n}=JSON.parse(e);if(Array.isArray(t)&&t.length&&n>Date.now())return t}}catch{}try{let t=this.config.iceUrl||`https://us-central1-${this.config.projectId}.cloudfunctions.net/iceServers`,n=await e.getIdToken(),r=await fetch(t,{headers:{authorization:`Bearer ${n}`}});if(r.ok){let e=await r.json();if(Array.isArray(e.iceServers)&&e.iceServers.length){try{sessionStorage.setItem(`wd-ice`,JSON.stringify({servers:e.iceServers,exp:Date.now()+8*6e4}))}catch{}return e.iceServers}}}catch{}return t}};function L(e,t,n){return e<t?t:e>n?n:e}var he=480,ge=600,R=-.6,z=1.6,_e=1500,ve=120,ye=3e3;function B(e){return!e||e.x<=.001&&e.y<=.001&&e.w>=.999&&e.h>=.999}function V(e,t){if(B(e)&&B(t))return!0;if(!e||!t)return!1;let n=.005;return Math.abs(e.x-t.x)<n&&Math.abs(e.y-t.y)<n&&Math.abs(e.w-t.w)<n&&Math.abs(e.h-t.h)<n}var be=class{canvas;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;constructor(e){this.canvas=e;let t=e.getContext(`2d`);if(!t)throw Error(`2D canvas context unavailable`);this.ctx=t,this.createOverlay(),this.startOverlayLoop(),this.resize(),new ResizeObserver(()=>this.resize()).observe(e),window.addEventListener(`orientationchange`,()=>window.setTimeout(()=>this.resize(),200))}createOverlay(){let e=document.createElement(`div`);e.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);`;let t=document.createElement(`canvas`);t.style.cssText=`position:absolute;inset:0;width:100%;height:100%;border-radius:5px;display:block;`;let 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;`,e.appendChild(t),e.appendChild(n),document.body.appendChild(e),this.overlayBox=e,this.overlayView=n,this.overlayCanvas=t,this.overlayCtx=t.getContext(`2d`)}startOverlayLoop(){let e=()=>{this.renderOverlay(),requestAnimationFrame(e)};requestAnimationFrame(e)}renderOverlay(){let e=this.overlayBox,t=this.overlayView;if(!e||!t)return;let n=this.getVisibleRegion(),r=this.region,i=this.dispW(),a=this.dispH();if(!(r||n.w<.985||n.h<.985)){e.style.display!==`none`&&(e.style.display=`none`),this.lastOverlayKey=``,this.overlayCanvasKey=``;return}e.style.display===`none`&&(e.style.display=`block`,this.minimapActiveAt=performance.now(),this.minimapShown=!1);let o=Math.round(Math.min(132,Math.max(76,this.cssW*.32))),s=Math.round(o*(a/i||.625)),c=`${o}x${s}`;(c!==this.overlayCanvasKey||this.overviewDirty)&&(this.overlayCanvasKey=c,this.overviewDirty=!1,e.style.width=`${o}px`,e.style.height=`${s}px`,this.paintMinimap(o,s));let l=n,u=performance.now(),d=`${l.x.toFixed(3)},${l.y.toFixed(3)},${l.w.toFixed(3)},${l.h.toFixed(3)}`;d!==this.lastOverlayKey&&(this.lastOverlayKey=d,this.minimapActiveAt=u,t.style.left=`${(L(l.x,0,1)*100).toFixed(2)}%`,t.style.top=`${(L(l.y,0,1)*100).toFixed(2)}%`,t.style.width=`${(L(Math.max(.04,l.w),0,1)*100).toFixed(2)}%`,t.style.height=`${(L(Math.max(.04,l.h),0,1)*100).toFixed(2)}%`);let f=u-this.minimapActiveAt<ye;f!==this.minimapShown&&(this.minimapShown=f,e.style.transition=`opacity ${f?.18:.5}s ease`,e.style.opacity=f?`1`:`0`)}paintMinimap(e,t){let n=this.overlayCanvas,r=this.overlayCtx;if(!n||!r)return;let i=Math.max(1,Math.round(e*this.dpr)),a=Math.max(1,Math.round(t*this.dpr));n.width!==i&&(n.width=i),n.height!==a&&(n.height=a),r.clearRect(0,0,i,a);let o=this.overviewImg();o&&(r.imageSmoothingEnabled=!0,r.imageSmoothingQuality=`low`,r.drawImage(o,0,0,i,a))}overviewImg(){return this.overviewReady?this.overview:null}maybeSnapshot(e,t,n){let r=performance.now();if(r-this.lastSnapAt<ge)return;this.lastSnapAt=r,this.overview||(this.overview=document.createElement(`canvas`),this.overviewCtx=this.overview.getContext(`2d`));let i=this.overview,a=this.overviewCtx;if(!i||!a)return;let o=he,s=Math.max(1,Math.round(o*n/t));i.width!==o&&(i.width=o),i.height!==s&&(i.height=s),a.imageSmoothingEnabled=!0,a.imageSmoothingQuality=`medium`,a.drawImage(e,0,0,t,n,0,0,o,s),this.overviewReady=!0,this.overviewDirty=!0}getCanvas(){return this.canvas}resize(){let e=this.canvas.getBoundingClientRect();this.cssW=Math.max(1,e.width),this.cssH=Math.max(1,e.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(e){let t=e.width!==this.screen.width||e.height!==this.screen.height;this.screen=e,t&&(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(e){let t=B(e)?null:e;this.region=t,window.clearTimeout(this.regionActiveHold),window.clearTimeout(this.regionBridge),this.regionBridge=window.setTimeout(()=>{this.shownRegion=t,this.videoActive||this.requestDraw()},_e),this.videoActive||this.requestDraw()}setFrameRegionActive(e){let t=B(e)?null:e;V(t,this.region)&&(V(t,this.shownRegion)||(window.clearTimeout(this.regionActiveHold),this.regionActiveHold=window.setTimeout(()=>{V(t,this.region)&&(this.shownRegion=t,window.clearTimeout(this.regionBridge),this.videoActive||this.requestDraw())},ve)))}setVideoSource(e){this.mainEl=e,this.videoActive=!!e,e?this.startVideoLoop():this.stopVideoLoop()}isVideoActive(){return this.videoActive}setOverviewSource(e){this.overviewEl=e}startVideoLoop(){if(this.videoRaf)return;let e=()=>{if(!this.videoActive){this.videoRaf=0;return}this.draw(),this.videoRaf=requestAnimationFrame(e)};this.videoRaf=requestAnimationFrame(e)}stopVideoLoop(){this.videoRaf&&cancelAnimationFrame(this.videoRaf),this.videoRaf=0,this.requestDraw()}setViewMode(e){this.viewMode=e,e===`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(e){this.zoom=L(e,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(e){this.setZoom(this.zoom*e)}getZoom(){return this.zoom}setOnZoom(e){this.onZoomCb=e}setOnView(e){this.onViewCb=e}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(e,t,n){let r=this.canvasToNorm(t,n),i=L(this.zoom*e,1,8);if(i===1){this.setZoom(1);return}this.zoom=i,this.viewMode=`magnify`;let a=this.dispW(),o=this.dispH(),s=Math.min(this.cssW/a,this.cssH/o)*this.zoom;this.center.nx=L(r.nx-(t-this.cssW/2)/(a*s||1),R,z),this.center.ny=L(r.ny-(n-this.cssH/2)/(o*s||1),R,z),this.onZoomCb?.(this.zoom),this.emitView(),this.requestDraw()}panByNorm(e,t){this.center.nx=L(this.center.nx+e,R,z),this.center.ny=L(this.center.ny+t,R,z),this.emitView(),this.requestDraw()}panByCanvasPixels(e,t){let{S:n}=this.computeLayout(),r=this.dispW(),i=this.dispH();!r||!i||!n||(this.center.nx=L(this.center.nx-e/(r*n),R,z),this.center.ny=L(this.center.ny-t/(i*n),R,z),this.emitView(),this.requestDraw())}setCursor(e,t=0){this.cursor=e===null?null:{nx:e,ny:t},this.videoActive||this.requestDraw()}computeLayout(){let{cssW:e,cssH:t}=this,n=this.dispW(),r=this.dispH();if(!n||!r)return this.layout={S:1,tx:0,ty:0},this.layout;let i=Math.min(e/n,t/r)*this.zoom,a,o;return this.viewMode===`fit`||this.zoom===1?(a=(e-n*i)/2,o=(t-r*i)/2):(a=e/2-this.center.nx*n*i,o=t/2-this.center.ny*r*i),this.layout={S:i,tx:a,ty:o},this.layout}canvasToNorm(e,t){let{S:n,tx:r,ty:i}=this.computeLayout();return{nx:L((e-r)/(this.dispW()*n||1),0,1),ny:L((t-i)/(this.dispH()*n||1),0,1)}}normToCanvas(e,t){let{S:n,tx:r,ty:i}=this.computeLayout();return{cx:r+e*this.dispW()*n,cy:i+t*this.dispH()*n}}getVisibleRegion(){let{S:e,tx:t,ty:n}=this.computeLayout(),r=this.dispW()*e,i=this.dispH()*e;if(!r||!i)return{x:0,y:0,w:1,h:1};let a=L(-t/r,0,1),o=L(-n/i,0,1),s=L((this.cssW-t)/r,0,1),c=L((this.cssH-n)/i,0,1);return{x:a,y:o,w:Math.max(.05,s-a),h:Math.max(.05,c-o)}}requestDraw(){this.videoActive||this.rafPending||(this.rafPending=!0,requestAnimationFrame(()=>{this.rafPending=!1,this.draw()}))}draw(){let e=this.ctx;e.setTransform(this.dpr,0,0,this.dpr,0,0),e.clearRect(0,0,this.cssW,this.cssH);let t=this.mainEl&&this.mainEl.videoWidth>0?this.mainEl:null;if(!t)return;let{S:n,tx:r,ty:i}=this.computeLayout(),a=this.dispW(),o=this.dispH(),s=a*n,c=o*n,l=t.videoWidth,u=t.videoHeight,d=l/u,f=e=>e?e.w*a/(e.h*o):a/o;if(this.region!==this.shownRegion){let e=Math.abs(d/f(this.region)-1)<.06,t=Math.abs(d/f(this.shownRegion)-1)<.06;e&&!t&&(this.shownRegion=this.region,window.clearTimeout(this.regionBridge))}let p=this.shownRegion,m=!!p&&Math.abs(d/f(p)-1)<.08,h=m?r+p.x*s:r,g=m?i+p.y*c:i,_=m?p.w*s:s,v=m?p.h*c:c,y=this.overviewImg();y&&(this.region||this.shownRegion)&&(e.imageSmoothingEnabled=!0,e.imageSmoothingQuality=`low`,e.drawImage(y,r,i,s,c));let b=Math.min(_/l,v/u),x=l*b,S=u*b;if(e.imageSmoothingEnabled=!0,e.imageSmoothingQuality=`high`,e.drawImage(t,0,0,l,u,h+(_-x)/2,g+(v-S)/2,x,S),this.cursor){let{cx:e,cy:t}=this.normToCanvas(this.cursor.nx,this.cursor.ny);this.drawCursor(e,t)}let C=this.overviewEl;if(this.region&&C&&C.videoWidth>0)this.maybeSnapshot(C,C.videoWidth,C.videoHeight);else if(!this.region&&!this.shownRegion&&o>0){let e=a/o;e>0&&Math.abs(d/e-1)<.06&&this.maybeSnapshot(t,l,u)}}drawCursor(e,t){let n=this.ctx;n.save(),n.beginPath(),n.arc(e,t,9,0,Math.PI*2),n.strokeStyle=`rgba(78,161,255,0.95)`,n.lineWidth=2,n.stroke(),n.beginPath(),n.arc(e,t,2.5,0,Math.PI*2),n.fillStyle=`rgba(78,161,255,0.95)`,n.fill(),n.restore()}};function xe(e,t,n,r){let i=document.createElement(`div`);i.className=`wd-place-layer`;let a=document.createElement(`div`);a.className=`wd-place-marker`;let o=document.createElement(`div`);o.className=`wd-place-bar`;let s=document.createElement(`p`);s.className=`wd-place-hint`,s.textContent=n.hint,o.appendChild(s);let c=null;n.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));let l=document.createElement(`div`);l.className=`wd-place-buttons`;let u=document.createElement(`button`);u.className=`wd-btn`,u.textContent=`Cancel`;let d=document.createElement(`button`);d.className=`wd-btn wd-go`,d.textContent=n.confirmLabel,l.append(u,d),o.appendChild(l),t.append(i,a,o),t.classList.add(`wd-placing`);let f={nx:.5,ny:.5},p=0,m=()=>{let{cx:t,cy:n}=e.normToCanvas(f.nx,f.ny);a.style.left=`${t}px`,a.style.top=`${n}px`,p=requestAnimationFrame(m)};p=requestAnimationFrame(m);let h=new Map,g=`idle`,_=null,v={x:0,y:0},y=!1,b=e=>{let t=i.getBoundingClientRect();return{x:e.clientX-t.left,y:e.clientY-t.top}},x=t=>{let{cx:n,cy:r}=e.normToCanvas(f.nx,f.ny);return Math.hypot(t.x-n,t.y-r)<38};i.addEventListener(`pointerdown`,t=>{i.setPointerCapture?.(t.pointerId);let n=b(t);if(h.set(t.pointerId,n),h.size===2){let e=[...h.values()],t=e[0],n=e[1];_={dist:Math.hypot(t.x-n.x,t.y-n.y),mx:(t.x+n.x)/2,my:(t.y+n.y)/2},g=`idle`}else h.size===1&&(v=n,y=!1,g=x(n)?`marker`:`pan`,g===`marker`&&(f=e.canvasToNorm(n.x,n.y)))}),i.addEventListener(`pointermove`,t=>{let n=h.get(t.pointerId);if(!n)return;let r=b(t);if(h.set(t.pointerId,r),h.size>=2&&_){let t=[...h.values()],n=t[0],r=t[1],i=Math.hypot(n.x-r.x,n.y-r.y),a=(n.x+r.x)/2,o=(n.y+r.y)/2;Math.abs(i-_.dist)>4&&(e.zoomAround(1+(i-_.dist)/200,a,o),_.dist=i),e.panByCanvasPixels(a-_.mx,o-_.my),_.mx=a,_.my=o;return}Math.hypot(r.x-v.x,r.y-v.y)>6&&(y=!0);let i=r.x-n.x,a=r.y-n.y;g===`marker`?f=e.canvasToNorm(r.x,r.y):g===`pan`&&e.panByCanvasPixels(i,a)});let S=t=>{let n=h.get(t.pointerId);h.delete(t.pointerId),n&&g===`pan`&&!y&&(f=e.canvasToNorm(n.x,n.y)),h.size<2&&(_=null),h.size===0&&(g=`idle`)};i.addEventListener(`pointerup`,S),i.addEventListener(`pointercancel`,S);let C=e=>{cancelAnimationFrame(p),t.classList.remove(`wd-placing`),i.remove(),a.remove(),o.remove(),r(e)};u.onclick=()=>C(null),d.onclick=()=>{let e=c?.value.trim()??``;if(n.withText&&!e){c?.focus();return}C({nx:f.nx,ny:f.ny,text:n.withText?e:void 0})}}var Se=0;function H(){return`w${Date.now().toString(36)}${(Se++).toString(36)}`}function Ce(){return new Date().toLocaleString(void 0,{month:`short`,day:`numeric`,hour:`2-digit`,minute:`2-digit`})}function we(e){let t=Math.max(0,Math.round((e-Date.now())/1e3)),n=Math.floor(t/3600),r=Math.floor(t%3600/60),i=t%60;return n>0?`${n}h ${r}m`:r>0?`${r}m ${String(i).padStart(2,`0`)}s`:`${i}s`}function U(e,t,n){let r=document.createElement(e);return t&&(r.className=t),n!==void 0&&(r.textContent=n),r}var Te={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 W(e){return Te[e]??`AI agent`}function G(e){switch(e){case`working`:return`working`;case`blocked`:return`needs you`;case`idle`:return`idle`;case`finished`:return`finished`;case`crashed`:return`crashed`;default:return`…`}}var Ee=class{root;conn;view;notifications;requestNotifications;regions=[];timers=[];monitors=[];monitorSessions=[];alwaysAgents=new Set;renderPicker=null;renderAlways=null;countdownTimer=0;overlay;list;permissionRow;selector=null;constructor(e,t,n,r,i=()=>{}){this.root=e,this.conn=t,this.view=n,this.notifications=r,this.requestNotifications=i,this.overlay=U(`div`,`wd-dialog-overlay hidden`),this.overlay.addEventListener(`pointerdown`,e=>{e.target===this.overlay&&this.close()});let a=U(`div`,`wd-dialog`),o=U(`div`,`wd-dialog-head`);o.append(U(`h2`,``,`Auto-Whips`));let s=U(`button`,`wd-dialog-x`);s.appendChild(g(`x`)),s.onclick=()=>this.close(),o.appendChild(s);let c=U(`div`,`wd-dialog-help`),l=U(`p`,`wd-help-intro`,`Auto-Whips are a set of features that can whip and monitor agents for you automatically:`),u=U(`ul`,`wd-help-list`),d=U(`li`);d.append(U(`strong`,void 0,`Alerts`),document.createTextNode(` — watch part of the screen and ping you when it changes (e.g. your agent finishes).`));let f=U(`li`);f.append(U(`strong`,void 0,`Timers`),document.createTextNode(` — ping you after a set time, and can auto-click or send a prompt when they fire.`));let p=U(`li`);p.append(U(`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).`)),u.append(d,f,p);let m=U(`p`,`wd-help-note`,`Enable browser notifications to be reminded even when the browser is closed.`);c.append(l,u,m),this.permissionRow=U(`div`,`wd-perm-row`),this.list=U(`div`,`wd-watch-list`);let h=U(`button`,`wd-btn wd-go`);h.append(g(`plus`),U(`span`,`wd-btn-label`,`Add alert`)),h.onclick=()=>this.beginSelection();let _=U(`button`,`wd-btn wd-go`);_.append(g(`clock`),U(`span`,`wd-btn-label`,`Add timer`)),_.onclick=()=>this.beginTimer();let v=U(`button`,`wd-btn wd-go`);v.append(g(`activity`),U(`span`,`wd-btn-label`,`Add Session Monitoring`)),v.onclick=()=>this.beginMonitor();let y=U(`div`,`wd-dialog-actions wd-actions-stack`);y.append(h,_,v),a.append(o,c,this.permissionRow,this.list,y),this.overlay.appendChild(a),this.root.appendChild(this.overlay),this.renderPermission(),this.conn.on(`monitorSessions`,e=>{this.monitorSessions=e,this.renderPicker?.()})}setRegions(e){this.regions=e,this.renderList()}setTimers(e){this.timers=e,this.renderList()}setMonitors(e){this.monitors=e,this.renderList()}setAlwaysAgents(e){this.alwaysAgents=new Set(e),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();let e=U(`span`,`wd-perm-dot`),t=U(`span`,`wd-perm-text`),n=this.notifications.permission;if(n===`granted`)e.dataset.state=`on`,t.textContent=`Browser notifications are on.`,this.permissionRow.append(e,t);else if(n===`denied`)e.dataset.state=`off`,t.textContent=`Notifications are blocked — enable them in your browser settings.`,this.permissionRow.append(e,t);else if(n===`unsupported`)e.dataset.state=`off`,t.textContent=`This browser can't show notifications. Keep this page open for in-app alerts.`,this.permissionRow.append(e,t);else{e.dataset.state=`warn`,t.textContent=`Allow notifications to be alerted while this page is in the background.`;let n=U(`button`,`wd-btn wd-perm-enable`);n.append(U(`span`,`wd-btn-label`,`Enable`)),n.onclick=async()=>{await this.requestNotifications(),this.renderPermission()},this.permissionRow.append(e,t,n)}}renderList(){if(this.list.replaceChildren(),this.regions.length===0&&this.timers.length===0&&this.monitors.length===0){this.list.appendChild(U(`p`,`wd-dialog-help`,`No alerts, timers, or session monitors yet.`));return}for(let e of this.regions){let t=U(`div`,`wd-watch-row`),n=U(`button`,`wd-watch-name`,e.label);n.title=`Edit this alert`,n.onclick=()=>this.beginSelection(e),t.appendChild(n);let r=U(`button`,`wd-btn wd-icon-only`);r.appendChild(g(`trash`)),r.setAttribute(`aria-label`,`Remove ${e.label}`),r.onclick=()=>{this.conn.send({type:`watch-remove`,id:e.id}),this.regions=this.regions.filter(t=>t.id!==e.id),this.renderList()},t.appendChild(r),this.list.appendChild(t)}for(let e of this.timers){let t=U(`div`,`wd-watch-row`),n=U(`div`,`wd-timer-info`),r=U(`span`,`wd-timer-name`);r.append(g(`clock`,15),document.createTextNode(e.label));let i=U(`span`,`wd-timer-remain`,we(e.fireAtMs));n.append(r,i),t.appendChild(n),e.hasAction&&t.appendChild(U(`span`,`wd-timer-tag`,`auto`));let a=U(`button`,`wd-btn wd-icon-only`);a.appendChild(g(`trash`)),a.setAttribute(`aria-label`,`Cancel ${e.label}`),a.onclick=()=>{this.conn.send({type:`timer-remove`,id:e.id}),this.timers=this.timers.filter(t=>t.id!==e.id),this.renderList()},t.appendChild(a),this.list.appendChild(t)}for(let e of this.monitors){let t=U(`div`,`wd-watch-row`),n=U(`div`,`wd-timer-info`),r=U(`span`,`wd-timer-name`);r.append(g(`activity`,15),document.createTextNode(`${W(e.agent)} · ${e.label}`));let i=U(`span`,`wd-mon-state`);i.dataset.state=e.live?e.state:`finished`,i.textContent=e.live?G(e.state):`ended`,n.append(r,i),t.appendChild(n);let a=U(`button`,`wd-btn wd-icon-only`);a.appendChild(g(`trash`)),a.setAttribute(`aria-label`,`Stop monitoring ${e.label}`),a.onclick=()=>{this.conn.send({type:`monitor-remove`,id:e.id}),this.monitors=this.monitors.filter(t=>t.id!==e.id),this.renderList()},t.appendChild(a),this.list.appendChild(t)}}beginSelection(e){this.close(),this.selector&&this.selector.remove();let t=this.root.getBoundingClientRect(),n=e?this.boxFromRegion(e,t):{x:t.width*.3,y:t.height*.3,w:t.width*.4,h:t.height*.25},r=U(`div`,`wd-selector-info`,e?`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.`),i=U(`div`,`wd-selector`),a=U(`div`,`wd-selector-move`);a.appendChild(g(`drag`));let o=U(`div`,`wd-selector-handle`),s=U(`div`,`wd-selector-bar`),c=U(`button`,`wd-btn wd-go`);c.append(U(`span`,`wd-btn-label`,e?`Save`:`Create`));let l=U(`button`,`wd-btn`);l.append(U(`span`,`wd-btn-label`,`Cancel`)),s.append(l,c),i.append(a,o,s),this.root.append(r,i),this.selector=i;let u=()=>{i.remove(),r.remove(),this.selector=null,this.open()},d=()=>{i.style.left=`${n.x}px`,i.style.top=`${n.y}px`,i.style.width=`${n.w}px`,i.style.height=`${n.h}px`};d();let f=(e,t)=>{e.addEventListener(`pointerdown`,n=>{n.preventDefault(),n.stopPropagation(),e.setPointerCapture(n.pointerId);let r=n.clientX,i=n.clientY,a=e=>{t(e.clientX-r,e.clientY-i),r=e.clientX,i=e.clientY,d()},o=()=>{e.removeEventListener(`pointermove`,a),e.removeEventListener(`pointerup`,o)};e.addEventListener(`pointermove`,a),e.addEventListener(`pointerup`,o)})};f(a,(e,r)=>{n.x=Math.max(0,Math.min(t.width-n.w,n.x+e)),n.y=Math.max(0,Math.min(t.height-n.h,n.y+r))}),f(o,(e,r)=>{n.w=Math.max(40,Math.min(t.width-n.x,n.w+e)),n.h=Math.max(40,Math.min(t.height-n.y,n.h+r))}),l.onclick=u,c.onclick=()=>{let t=this.toScreenRegion(n,e);t&&(this.conn.send({type:`watch-add`,region:t}),this.regions=e?this.regions.map(e=>e.id===t.id?t:e):[...this.regions,t],this.notifications.permission==="default"&&this.requestNotifications(),this.notifications.flash(e?`Alert updated`:`Alert created`,e?`"${t.label}" updated.`:`Monitoring "${t.label}". We'll notify you when it changes.`,`success`)),u()}}boxFromRegion(e,t){let n=this.view.normToCanvas(e.x,e.y),r=this.view.normToCanvas(e.x+e.w,e.y+e.h),i=Math.max(40,r.cx-n.cx),a=Math.max(40,r.cy-n.cy);return{x:Math.max(0,Math.min(t.width-i,n.cx)),y:Math.max(0,Math.min(t.height-a,n.cy)),w:i,h:a}}toScreenRegion(e,t){let n=this.view.canvasToNorm(e.x,e.y),r=this.view.canvasToNorm(e.x+e.w,e.y+e.h),i=Math.min(n.nx,r.nx),a=Math.min(n.ny,r.ny),o=Math.abs(r.nx-n.nx),s=Math.abs(r.ny-n.ny);return o<.005||s<.005?null:{id:t?.id??H(),label:t?.label??Ce(),x:i,y:a,w:o,h:s}}beginTimer(){this.close();let e=U(`div`,`wd-dialog-overlay`),t=()=>{e.remove(),this.open()};e.addEventListener(`pointerdown`,n=>{n.target===e&&t()});let n=U(`div`,`wd-dialog`),r=U(`div`,`wd-dialog-head`);r.append(U(`h2`,``,`Set timer`));let i=U(`button`,`wd-dialog-x`);i.appendChild(g(`x`)),i.onclick=t,r.appendChild(i);let a=U(`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.`),o=U(`div`,`wd-form-row`);o.appendChild(U(`label`,`wd-form-label`,`Remind me in`));let s=U(`input`,`wd-input wd-input-num`);s.type=`number`,s.min=`0`,s.max=`168`,s.value=`0`,s.inputMode=`numeric`;let c=U(`input`,`wd-input wd-input-num`);c.type=`number`,c.min=`0`,c.max=`59`,c.value=`30`,c.inputMode=`numeric`;let l=U(`div`,`wd-preset-row`);for(let[e,t,n]of[[`15m`,0,15],[`30m`,0,30],[`1h`,1,0],[`2h`,2,0],[`5h`,5,0]]){let r=U(`button`,`wd-preset`,e);r.type=`button`,r.onclick=()=>{s.value=String(t),c.value=String(n)},l.appendChild(r)}let u=(e,t,n,r)=>{let i=U(`div`,`wd-stepper`),a=U(`button`,`wd-step-btn`,`−`);a.type=`button`;let o=U(`button`,`wd-step-btn`,`+`);o.type=`button`;let s=e=>Math.max(0,Math.min(n,e));return a.onclick=()=>e.value=String(s((Number(e.value)||0)-t)),o.onclick=()=>e.value=String(s((Number(e.value)||0)+t)),i.append(a,e,U(`span`,`wd-form-unit`,r),o),i},d=U(`div`,`wd-form-duration`);d.append(u(s,1,168,`h`),u(c,5,59,`m`)),o.append(l,d);let f=U(`div`,`wd-form-row`);f.appendChild(U(`label`,`wd-form-label`,`Label`));let p=U(`input`,`wd-input`);p.placeholder=`e.g. Claude is back`,f.appendChild(p);let m=U(`div`,`wd-form-row`);m.appendChild(U(`label`,`wd-form-label`,`When it fires`));let h=U(`select`,`wd-input`);for(let[e,t]of[[`none`,`Just remind me`],[`click`,`Click a button`],[`key`,`Click & press Enter`],[`text`,`Click, type & send a prompt`]]){let n=document.createElement(`option`);n.value=e,n.textContent=t,h.appendChild(n)}m.appendChild(h);let _=U(`div`,`wd-dialog-actions`),v=U(`button`,`wd-btn`);v.append(U(`span`,`wd-btn-label`,`Cancel`)),v.onclick=t;let y=U(`button`,`wd-btn wd-go`),b=U(`span`,`wd-btn-label`,`Start timer`);y.append(g(`clock`),b);let x=()=>{b.textContent=h.value===`none`?`Start timer`:`Next: place target`};h.onchange=x,x();let S=()=>{let e=Math.max(0,Math.min(168,Math.round(Number(s.value)||0))),t=Math.max(0,Math.min(59,Math.round(Number(c.value)||0))),n=(e*60+t)*6e4;return n>=6e4?n:null},C=()=>{let e=Math.max(0,Math.round(Number(s.value)||0)),t=Math.max(0,Math.round(Number(c.value)||0));return`${e?`${e}h `:``}${t}m`},w=e=>{let n=H(),r=S(),i=p.value.trim()||`Timer (${C()})`;this.conn.send({type:`timer-add`,id:n,fireInMs:r,label:i,action:e}),this.timers=[...this.timers,{id:n,label:i,fireAtMs:Date.now()+r,hasAction:!!e}],this.notifications.permission==="default"&&this.requestNotifications(),this.notifications.flash(`Timer started`,`"${i}" — I'll ping you in ${C()}.`,`success`),t()};y.onclick=()=>{if(S()==null){this.notifications.flash(`Set a time`,`Choose at least 1 minute.`,`warning`);return}let t=h.value;if(t===`none`){w(void 0);return}e.style.display=`none`;let n=t===`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.`:t===`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.`;xe(this.view,this.root,{withText:t===`text`,hint:n,confirmLabel:`Confirm target`},n=>{if(!n){e.style.display=``;return}let r;r=t===`click`?{kind:`click`,x:n.nx,y:n.ny,button:`left`}:t===`key`?{kind:`key`,key:`Enter`,x:n.nx,y:n.ny}:{kind:`text`,text:n.text??``,x:n.nx,y:n.ny},w(r)})},_.append(v,y),n.append(r,a,o,f,m,_),e.appendChild(n),this.root.appendChild(e)}beginMonitor(){this.close();let e=U(`div`,`wd-dialog-overlay`),t=()=>{this.renderPicker=null,this.renderAlways=null,e.remove(),this.open()};e.addEventListener(`pointerdown`,n=>{n.target===e&&t()});let n=U(`div`,`wd-dialog`),r=U(`div`,`wd-dialog-head`);r.append(U(`h2`,``,`Monitor a session`));let i=U(`button`,`wd-dialog-x`);i.appendChild(g(`x`)),i.onclick=t,r.appendChild(i);let a=U(`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).`),o=U(`div`,`wd-mon-pick-head`),s=U(`button`,`wd-btn`);s.append(U(`span`,`wd-btn-label`,`Rescan`)),s.onclick=()=>this.conn.send({type:`monitor-scan`}),o.append(U(`span`,`wd-form-label`,`Running sessions`),s);let c=U(`div`,`wd-mon-pick`),l=``,u=()=>{if(c.replaceChildren(),this.monitorSessions.length===0){l=``,v(),p(),c.appendChild(U(`p`,`wd-dialog-help`,`No AI sessions detected yet. Start Claude Code, Codex, Gemini, or Aider, then Rescan.`));return}(!l||!this.monitorSessions.some(e=>e.key===l))&&(l=this.monitorSessions[0].key);for(let e of this.monitorSessions){let t=U(`button`,`wd-mon-item`);t.type=`button`,e.key===l&&t.classList.add(`on`);let n=U(`div`,`wd-mon-item-main`);n.append(g(`activity`,15),U(`span`,`wd-mon-item-title`,`${W(e.agent)} · ${e.title}`));let r=U(`span`,`wd-mon-state`);r.dataset.state=e.state,r.textContent=G(e.state),t.append(n,r),t.onclick=()=>{l=e.key,u()},c.appendChild(t)}v(),p()};this.renderPicker=u;let d=U(`div`,`wd-form-row`);d.appendChild(U(`label`,`wd-form-label`,`Always alert me`));let f=U(`div`,`wd-mon-always`);d.appendChild(f);let p=()=>{f.replaceChildren();let e=this.monitorSessions.find(e=>e.key===l);if(!e){f.appendChild(U(`p`,`wd-dialog-help`,`Pick a session above to always alert for its agent.`));return}let t=e.agent,n=U(`label`,`wd-check`),r=U(`input`);r.type=`checkbox`,r.checked=this.alwaysAgents.has(t),r.onchange=()=>{r.checked?this.alwaysAgents.add(t):this.alwaysAgents.delete(t),this.conn.send({type:`monitor-always`,agent:t,enabled:r.checked}),r.checked&&this.notifications.permission==="default"&&this.requestNotifications(),p()};let i=U(`span`,`wd-check-text`);i.append(document.createTextNode(`Always monitor every ${W(t)} session`),U(`span`,`wd-check-sub`,`Keeps alerting across agent and WhipDesk restarts — no need to re-add it.`)),n.append(r,i),f.appendChild(n)};this.renderAlways=p;let m=U(`div`,`wd-dialog-actions`),h=U(`button`,`wd-btn`);h.append(U(`span`,`wd-btn-label`,`Cancel`)),h.onclick=t;let _=U(`button`,`wd-btn wd-go`);_.append(g(`activity`),U(`span`,`wd-btn-label`,`Add monitor`));let v=()=>_.disabled=!l;v(),_.onclick=()=>{let e=this.monitorSessions.find(e=>e.key===l);if(!e)return;let n=H(),r=e.title;this.conn.send({type:`monitor-add`,id:n,key:e.key,agent:e.agent,label:r}),this.monitors=[...this.monitors,{id:n,key:e.key,agent:e.agent,label:r,state:e.state,live:!0}],this.notifications.permission==="default"&&this.requestNotifications(),this.notifications.flash(`Monitoring started`,`Watching ${W(e.agent)} · ${r}.`,`success`),t()},m.append(h,_),n.append(r,a,o,c,d,m),e.appendChild(n),this.root.appendChild(e),u(),this.conn.send({type:`monitor-scan`})}};function De(){let e=new URLSearchParams(location.hash.replace(/^#/,``));return{token:e.get(`t`)??e.get(`token`)??``,remote:e.get(`remote`)===`1`||e.has(`device`),device:e.get(`device`)??e.get(`d`)??``}}function Oe(){return`${location.protocol===`https:`?`wss`:`ws`}://${location.host}/ws`}async function ke(){let e=window.__WHIPDESK_FB__;if(e?.apiKey)return e;try{let e=await fetch(`firebase.json`,{cache:`no-store`});if(e.ok)return await e.json()}catch{}return null}var K=document.getElementById(`app`);if(!K)throw Error(`#app not found`);var q=$(`canvas`,`wd-screen`);q.id=`wd-screen`;var Ae=$(`div`,`wd-toasts`);K.append(q,Ae);var{token:J,remote:je,device:Y}=De(),X=new be(q),Z=new ce(Ae),Me=new le(K),Q=new p(K);async function Ne(e){if(je&&Y){if(e)return new me(Y,J,e);Z.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 u(Oe(),J)}async function Pe(){let e=je&&Y?await ke():null,t=await Ne(e),n=new se(q,X,t),r=new Ee(K,t,X,Z,()=>e?I(e,Z):Z.requestPermission()),i=new re(K,{conn:t,view:X,input:n,notifications:Z,watchers:r}),a=document.createElement(`video`);a.muted=!0,a.autoplay=!0,a.playsInline=!0,a.setAttribute(`playsinline`,``),a.style.display=`none`,K.append(a);let o=document.createElement(`video`);o.muted=!0,o.autoplay=!0,o.playsInline=!0,o.setAttribute(`playsinline`,``),o.style.display=`none`,K.append(o);let s=!1,c=!1,l=``,u=0,d=e=>e.w>=.999&&e.h>=.999,f=e=>{let n=e.w>=.92&&e.h>=.92?{x:0,y:0,w:1,h:1}:e,r=`${n.x.toFixed(2)},${n.y.toFixed(2)},${n.w.toFixed(2)},${n.h.toFixed(2)}`;r!==l&&(l=r,t.send({type:`set-viewport`,x:n.x,y:n.y,w:n.w,h:n.h}))};X.setOnView(e=>{let t=X.getZoom()>1.01;window.clearTimeout(u),u=window.setTimeout(()=>f(t?e:{x:0,y:0,w:1,h:1}),t?120:0)}),t.on(`status`,e=>{i.setStatus(e),e===`connected`?(c=!1,Q.hide()):(!s||c)&&Q.show(s?`Reconnecting…`:void 0),e===`disconnected`&&(l=``)}),t.on(`transport`,e=>i.setTransport(e)),t.on(`welcome`,e=>{let n=!s;s=!0,Q.hide(),Me.hide(),X.setScreen(e.screen),i.setWelcome(e),n&&(l=`0.000,0.000,1.000,1.000`,X.setZoom(1),t.send({type:`set-viewport`,x:0,y:0,w:1,h:1}))}),t.on(`versionMismatch`,e=>Fe(e.agentVersion)),t.on(`pinRequired`,e=>{Q.hide(),Me.show(e,e=>t.submitPin(e))}),t.on(`presence`,e=>i.setPresence(e));let p=0,m=0,h=0,g=()=>i.setAlertCount(p+m+h);if(t.on(`watchers`,e=>{p=e.length,r.setRegions(e),g()}),t.on(`timers`,e=>{m=e.length,r.setTimers(e),g()}),t.on(`monitors`,e=>{h=e.length,r.setMonitors(e),g()}),t.on(`monitorAlways`,e=>{r.setAlwaysAgents(e)}),t.on(`screenRegion`,e=>{let t=d(e)?null:e;e.active?X.setFrameRegionActive(t):X.setFrameRegion(t)}),t.on(`videoTrack`,e=>{if(!e){a.srcObject=null,X.setVideoSource(null);return}a.srcObject=e,a.play().catch(()=>{}),X.setVideoSource(a)}),t.on(`overviewTrack`,e=>{if(!e){o.srcObject=null,X.setOverviewSource(null);return}o.srcObject=e,o.play().catch(()=>{}),X.setOverviewSource(o)}),t.on(`netStats`,({fps:e,rtt:t})=>i.setNetStats(e,t)),t.on(`screenMeta`,({screen:e,activeDisplay:t})=>{X.setScreen(e),t!==void 0&&i.setActiveDisplay(t)}),t.on(`notification`,e=>Z.show(e)),t.on(`error`,e=>i.flashError(e)),e){let n=!1;t.on(`welcome`,()=>{n||Z.permission!==`granted`||(n=!0,I(e,Z))})}let _=()=>{document.hidden||t.isHealthy()||(c=!0,Q.show(`Reconnecting…`),t.wake())};document.addEventListener(`visibilitychange`,()=>{t.setVisible(!document.hidden),document.hidden||_()}),window.addEventListener(`pageshow`,_),J||i.flashError(`No pairing token in the URL (#t=…). Re-open the link/QR from the agent.`),t.connect()}Pe();function $(e,t){let n=document.createElement(e);return n.className=t,n}function Fe(e){let t=`wd-outdated-banner`;if(document.getElementById(t))return;let n=document.createElement(`div`);n.id=t,n.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)`,n.innerHTML=`⚠️ This WhipDesk agent${e?` (agent ${e})`:``} 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>.`;let r=document.createElement(`button`);r.textContent=`✕`,r.setAttribute(`aria-label`,`Dismiss`),r.style.cssText=`position:absolute;right:8px;top:6px;background:none;border:none;color:#fff;font-size:16px;cursor:pointer`,r.onclick=()=>n.remove(),n.appendChild(r),document.body.appendChild(n)}
|