@mindstudio-ai/local-model-tunnel 0.5.36 → 0.5.38

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.
@@ -1638,13 +1638,15 @@ var ClientRegistry = class {
1638
1638
  id,
1639
1639
  ws,
1640
1640
  mode: hello.mode,
1641
+ mirrorSource: !!hello.mirror,
1642
+ mirrorReady: false,
1641
1643
  url: hello.url,
1642
1644
  viewport: hello.viewport,
1643
1645
  connectedAt: Date.now(),
1644
1646
  alive: true,
1645
1647
  activeCommandId: null
1646
1648
  });
1647
- log.info("proxy", "Browser client connected", { clientId: id, mode: hello.mode, url: hello.url });
1649
+ log.info("proxy", "Browser client connected", { clientId: id, mode: hello.mode, mirror: !!hello.mirror, url: hello.url });
1648
1650
  return id;
1649
1651
  }
1650
1652
  remove(id) {
@@ -1673,10 +1675,24 @@ var ClientRegistry = class {
1673
1675
  }
1674
1676
  return fallback;
1675
1677
  }
1676
- /** Get all connected mirror-mode clients (for relaying mirror events). */
1678
+ /** Get all connected mirror viewer clients (for relaying mirror events). */
1677
1679
  getMirrorClients() {
1678
1680
  return [...this.clients.values()].filter((c) => c.mode === "mirror");
1679
1681
  }
1682
+ /** Check if a mirror recording source (phone) is connected. */
1683
+ hasMirrorSource() {
1684
+ for (const client of this.clients.values()) {
1685
+ if (client.mirrorSource) return true;
1686
+ }
1687
+ return false;
1688
+ }
1689
+ /** Get the mirror source client if connected. */
1690
+ getMirrorSource() {
1691
+ for (const client of this.clients.values()) {
1692
+ if (client.mirrorSource) return client;
1693
+ }
1694
+ return void 0;
1695
+ }
1680
1696
  getAll() {
1681
1697
  return [...this.clients.values()];
1682
1698
  }
@@ -1737,6 +1753,8 @@ var DevProxy = class _DevProxy {
1737
1753
  clients = new ClientRegistry();
1738
1754
  pendingResults = /* @__PURE__ */ new Map();
1739
1755
  commandQueue = [];
1756
+ /** Last mirror snapshot — sent to new mirror viewers so they don't wait for the next checkout. */
1757
+ lastMirrorSnapshot = null;
1740
1758
  /** Upstream dev server health tracking. */
1741
1759
  upstreamUp = true;
1742
1760
  healthCheckTimer = null;
@@ -1966,9 +1984,16 @@ var DevProxy = class _DevProxy {
1966
1984
  clientId = this.clients.add(ws, {
1967
1985
  mode,
1968
1986
  url: String(msg.url || ""),
1969
- viewport
1987
+ viewport,
1988
+ mirror: !!msg.mirror
1970
1989
  });
1971
1990
  ws.send(JSON.stringify({ type: "ack", clientId }));
1991
+ if (mode === "mirror" && this.lastMirrorSnapshot) {
1992
+ try {
1993
+ ws.send(this.lastMirrorSnapshot);
1994
+ } catch {
1995
+ }
1996
+ }
1972
1997
  return;
1973
1998
  }
1974
1999
  switch (msg.type) {
@@ -1980,9 +2005,38 @@ var DevProxy = class _DevProxy {
1980
2005
  appendBrowserLogEntries(msg.entries);
1981
2006
  }
1982
2007
  break;
1983
- case "mirror":
2008
+ case "mirror": {
2009
+ const events = msg.events;
2010
+ if (clientId && Array.isArray(events)) {
2011
+ let meta = null;
2012
+ let snapshot = null;
2013
+ for (const evt of events) {
2014
+ if (evt.type === 4) meta = evt;
2015
+ if (evt.type === 2) snapshot = evt;
2016
+ if (evt.type === 4 && evt.data?.width && evt.data?.height) {
2017
+ const client = this.clients.get(clientId);
2018
+ if (client) {
2019
+ client.viewport = {
2020
+ w: evt.data.width,
2021
+ h: evt.data.height
2022
+ };
2023
+ client.mirrorReady = true;
2024
+ }
2025
+ }
2026
+ }
2027
+ if (snapshot) {
2028
+ const snapshotEvents = [];
2029
+ if (meta) snapshotEvents.push(meta);
2030
+ snapshotEvents.push(snapshot);
2031
+ this.lastMirrorSnapshot = JSON.stringify({
2032
+ type: "mirror",
2033
+ events: snapshotEvents
2034
+ });
2035
+ }
2036
+ }
1984
2037
  this.relayMirrorEvents(data.toString());
1985
2038
  break;
2039
+ }
1986
2040
  }
1987
2041
  });
1988
2042
  ws.on("pong", () => {
@@ -1993,9 +2047,13 @@ var DevProxy = class _DevProxy {
1993
2047
  if (clientId) {
1994
2048
  const client = this.clients.remove(clientId);
1995
2049
  if (client?.activeCommandId) {
1996
- log.debug("proxy", "Browser disconnected with active command, keeping pending", {
1997
- commandId: client.activeCommandId
1998
- });
2050
+ log.debug(
2051
+ "proxy",
2052
+ "Browser disconnected with active command, keeping pending",
2053
+ {
2054
+ commandId: client.activeCommandId
2055
+ }
2056
+ );
1999
2057
  }
2000
2058
  }
2001
2059
  });
@@ -2137,6 +2195,21 @@ var DevProxy = class _DevProxy {
2137
2195
  this.serveMirrorPage(clientRes);
2138
2196
  return;
2139
2197
  }
2198
+ if (clientReq.url === "/__mindstudio_dev__/mirror-status" && clientReq.method === "GET") {
2199
+ const source = this.clients.getMirrorSource();
2200
+ const ready = source?.mirrorReady ?? false;
2201
+ const body = JSON.stringify({
2202
+ active: ready,
2203
+ viewport: ready ? source.viewport : null
2204
+ });
2205
+ clientRes.writeHead(200, {
2206
+ "Content-Type": "application/json",
2207
+ "Cache-Control": "no-store",
2208
+ ...this.corsHeaders(clientReq)
2209
+ });
2210
+ clientRes.end(body);
2211
+ return;
2212
+ }
2140
2213
  }
2141
2214
  if (clientReq.method === "OPTIONS" && clientReq.headers.origin) {
2142
2215
  clientRes.writeHead(204, {
@@ -2228,7 +2301,9 @@ var DevProxy = class _DevProxy {
2228
2301
  upstreamRes.on("data", (chunk) => chunks.push(chunk));
2229
2302
  upstreamRes.on("end", async () => {
2230
2303
  let html = Buffer.concat(chunks).toString("utf-8");
2231
- const authCookie = _DevProxy.parseAuthCookie(clientReq.headers.cookie);
2304
+ const authCookie = _DevProxy.parseAuthCookie(
2305
+ clientReq.headers.cookie
2306
+ );
2232
2307
  let contextOverride;
2233
2308
  if (authCookie) {
2234
2309
  const resolved = await this.resolveAuthCookie(authCookie);
@@ -2313,64 +2388,105 @@ var DevProxy = class _DevProxy {
2313
2388
  <meta charset="utf-8">
2314
2389
  <meta name="viewport" content="width=device-width, initial-scale=1">
2315
2390
  <title>Mobile Mirror</title>
2391
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@rrweb/replay@latest/dist/style.css">
2316
2392
  <style>
2317
2393
  * { margin: 0; padding: 0; box-sizing: border-box; }
2318
- html, body { height: 100%; background: #111; overflow: hidden; }
2319
- #player { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; }
2320
- .replayer-wrapper { box-shadow: 0 0 40px rgba(0,0,0,0.5); border-radius: 4px; overflow: hidden; }
2321
- #status { position: fixed; bottom: 16px; left: 50%; transform: translateX(-50%); color: #666; font-family: -apple-system, system-ui, sans-serif; font-size: 13px; }
2394
+ html, body { height: 100%; background: #eaeaea; overflow: hidden; }
2395
+ #player { width: 100%; height: 100%; }
2396
+ .replayer-wrapper { overflow: hidden; transform-origin: center center; visibility: hidden; }
2397
+ .replayer-wrapper iframe { border: none; outline: none; }
2398
+ .replayer-mouse.touch-device {
2399
+ width: 44px; height: 44px; margin-left: -22px; margin-top: -22px;
2400
+ border-width: 2px; border-color: rgba(221, 37, 144, 0);
2401
+ background: rgba(221, 37, 144, 0.06);
2402
+ }
2403
+ .replayer-mouse.touch-device.touch-active {
2404
+ border-color: rgba(221, 37, 144, 0.8);
2405
+ background: rgba(221, 37, 144, 0.12);
2406
+ }
2407
+ .replayer-mouse.touch-device::after,
2408
+ .replayer-mouse.touch-device.active::after { display: none !important; }
2409
+ .replayer-mouse:not(.touch-device) { display: none !important; }
2322
2410
  </style>
2411
+ <script type="importmap">
2412
+ { "imports": { "@rrweb/replay": "https://cdn.jsdelivr.net/npm/@rrweb/replay@latest/+esm" } }
2413
+ </script>
2323
2414
  </head>
2324
2415
  <body>
2325
2416
  <div id="player"></div>
2326
- <div id="status">Waiting for mobile device...</div>
2327
- <script src="https://cdn.jsdelivr.net/npm/rrweb@2.0.0-alpha.13/dist/rrweb.umd.cjs.js"></script>
2328
- <script>
2329
- (function() {
2330
- var proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
2331
- var ws = new WebSocket(proto + '//' + location.host + '/__mindstudio_dev__/ws');
2332
- var replayer = null;
2333
- var status = document.getElementById('status');
2417
+ <script type="module">
2418
+ import { Replayer } from '@rrweb/replay';
2334
2419
 
2335
- ws.onopen = function() {
2336
- ws.send(JSON.stringify({
2337
- type: 'hello',
2338
- mode: 'mirror',
2339
- url: location.href,
2340
- viewport: { w: window.innerWidth, h: window.innerHeight }
2341
- }));
2342
- };
2420
+ const BUFFER_MS = 50;
2421
+ const playerRoot = document.getElementById('player');
2422
+
2423
+ let replayer = null;
2424
+ let lastMeta = null;
2425
+ let notifiedViewport = false;
2426
+
2427
+ function showWrapper() {
2428
+ const wrapper = document.querySelector('.replayer-wrapper');
2429
+ if (wrapper) wrapper.style.visibility = 'visible';
2430
+ }
2431
+
2432
+ function buildReplayer(snapshotEvent) {
2433
+ if (replayer) {
2434
+ try { replayer.destroy(); } catch(e) {}
2435
+ playerRoot.innerHTML = '';
2436
+ }
2437
+ const initEvents = [];
2438
+ if (lastMeta) initEvents.push(lastMeta);
2439
+ initEvents.push(snapshotEvent);
2440
+
2441
+ replayer = new Replayer(initEvents, {
2442
+ root: playerRoot,
2443
+ liveMode: true,
2444
+ pauseAnimation: false,
2445
+ mouseTail: false,
2446
+ });
2447
+ replayer.startLive(snapshotEvent.timestamp - BUFFER_MS);
2448
+ requestAnimationFrame(showWrapper);
2449
+ }
2343
2450
 
2344
- ws.onmessage = function(e) {
2345
- var msg;
2346
- try { msg = JSON.parse(e.data); } catch(e) { return; }
2451
+ const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
2452
+ const ws = new WebSocket(proto + '//' + location.host + '/__mindstudio_dev__/ws');
2347
2453
 
2348
- if (msg.type !== 'mirror' || !Array.isArray(msg.events)) return;
2454
+ ws.onopen = () => {
2455
+ ws.send(JSON.stringify({
2456
+ type: 'hello', mode: 'mirror', url: location.href,
2457
+ viewport: { w: window.innerWidth, h: window.innerHeight },
2458
+ }));
2459
+ };
2349
2460
 
2350
- for (var i = 0; i < msg.events.length; i++) {
2351
- var event = msg.events[i];
2352
- if (!replayer) {
2353
- replayer = new rrweb.Replayer([], {
2354
- root: document.getElementById('player'),
2355
- liveMode: true,
2356
- insertStyleRules: [
2357
- '.replayer-wrapper { position: relative !important; }',
2358
- ],
2359
- });
2360
- replayer.startLive(Date.now() - 500);
2361
- status.textContent = 'Connected';
2362
- setTimeout(function() { status.style.opacity = '0'; }, 2000);
2461
+ ws.onmessage = (e) => {
2462
+ let msg;
2463
+ try { msg = JSON.parse(e.data); } catch { return; }
2464
+ if (msg.type !== 'mirror' || !Array.isArray(msg.events)) return;
2465
+
2466
+ for (const event of msg.events) {
2467
+ if (event.type === 4) {
2468
+ lastMeta = event;
2469
+ if (!notifiedViewport && event.data && event.data.width && window.parent !== window) {
2470
+ notifiedViewport = true;
2471
+ window.parent.postMessage({
2472
+ channel: 'mindstudio-mirror',
2473
+ command: 'viewport',
2474
+ width: event.data.width,
2475
+ height: event.data.height,
2476
+ }, '*');
2363
2477
  }
2364
- replayer.addEvent(event);
2365
2478
  }
2366
- };
2479
+ if (event.type === 2 && !replayer) {
2480
+ buildReplayer(event);
2481
+ continue;
2482
+ }
2483
+ if (replayer) replayer.addEvent(event);
2484
+ }
2485
+ };
2367
2486
 
2368
- ws.onclose = function() {
2369
- status.style.opacity = '1';
2370
- status.textContent = 'Disconnected \u2014 reconnecting...';
2371
- setTimeout(function() { location.reload(); }, 2000);
2372
- };
2373
- })();
2487
+ ws.onclose = () => {
2488
+ setTimeout(() => location.reload(), 2000);
2489
+ };
2374
2490
  </script>
2375
2491
  </body>
2376
2492
  </html>`;
@@ -2463,7 +2579,11 @@ var DevProxy = class _DevProxy {
2463
2579
  try {
2464
2580
  const body = JSON.parse(Buffer.concat(chunks).toString("utf-8"));
2465
2581
  if (body.user && body.token) {
2466
- resolve2({ user: body.user, token: body.token, methods: body.methods ?? {} });
2582
+ resolve2({
2583
+ user: body.user,
2584
+ token: body.token,
2585
+ methods: body.methods ?? {}
2586
+ });
2467
2587
  } else {
2468
2588
  resolve2(null);
2469
2589
  }
@@ -2782,4 +2902,4 @@ export {
2782
2902
  watchTableFiles,
2783
2903
  watchConfigFile
2784
2904
  };
2785
- //# sourceMappingURL=chunk-HWO7KKLN.js.map
2905
+ //# sourceMappingURL=chunk-7E5LTDSO.js.map