From 72e6e86f39f64651295ff1a4d4dac096b6b012f3 Mon Sep 17 00:00:00 2001 From: SleepinDevil Date: Sat, 7 Mar 2026 16:36:33 +1100 Subject: [PATCH 1/3] Update protocol.ts Added header and packet packer into protocol --- src/protocol.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/protocol.ts b/src/protocol.ts index 84a7b64..8644822 100644 --- a/src/protocol.ts +++ b/src/protocol.ts @@ -16,6 +16,8 @@ // Keepalive message: // [type u8=5][ver u8=1] // +// Current URL packet: +// [type u8][ver u8][len u32][url utf8 bytes...] export const PROTOCOL_VERSION = 1 as const; @@ -26,6 +28,7 @@ export enum MsgType { FrameStats = 3, OpenURL = 4, Keepalive = 5, + CurrentURL = 6, // <-- Current URL packet } export enum Encoding { @@ -75,9 +78,21 @@ export const TILE_HEADER_BYTES = 2 + 2 + 2 + 2 + 4; // 12 export const TOUCH_BYTES = 1 + 1 + 1 + 1 + 2 + 2; // 8 export const FRAME_STATS_BYTES = 1 + 1 + 4 + 4; // 10 export const OPENURL_HEADER_BYTES = 1 + 1 + 2 + 4; // 8 +export const CURRENTURL_HEADER_BYTES = 1 + 1 + 4; // 6 bytes: [type u8][ver u8][len u32] - Current URL header const clampU16 = (v: number) => (v < 0 ? 0 : v > 0xffff ? 0xffff : v|0); +// Current URL packet +export function buildCurrentURLPacket(url: string): Buffer { + const urlBuf = Buffer.from(url, "utf8"); + const buf = Buffer.alloc(CURRENTURL_HEADER_BYTES + urlBuf.length); + buf.writeUInt8(MsgType.CurrentURL, 0); + buf.writeUInt8(PROTOCOL_VERSION, 1); + buf.writeUInt32LE(urlBuf.length, 2); + urlBuf.copy(buf, CURRENTURL_HEADER_BYTES); + return buf; +} + export function buildTouchPacket(kind: TouchKind, x: number, y: number, pointerId = 0): Buffer { const buf = Buffer.alloc(TOUCH_BYTES); buf.writeUInt8(MsgType.Touch, 0); From 55173e30a573b5dd6876056a752138fff9e4ccb3 Mon Sep 17 00:00:00 2001 From: SleepinDevil Date: Sat, 7 Mar 2026 16:39:52 +1100 Subject: [PATCH 2/3] Update broadcaster.ts --- src/broadcaster.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/broadcaster.ts b/src/broadcaster.ts index 32a4f43..a24c526 100644 --- a/src/broadcaster.ts +++ b/src/broadcaster.ts @@ -1,5 +1,5 @@ import { WebSocket } from "ws"; -import { buildFrameStatsPacket, buildFramePackets } from "./protocol.js"; +import { buildFrameStatsPacket, buildFramePackets, buildCurrentURLPacket } from "./protocol.js"; import type { FrameOut } from "./frameProcessor.js"; type OutFrame = { frameId: number; packets: Buffer[] }; @@ -62,6 +62,20 @@ export class DeviceBroadcaster { this._drainAsync(id).catch(() => {}); } + // Send packet with current URL info to connected client: + public sendCurrentURL(id: string, url: string): void { + const peers = this._clients.get(id); + if (!peers || peers.size === 0) return; + + // We use the URL packet packer from protocol.js here + const packet = buildCurrentURLPacket(url); + const st = this._ensureState(id); + + // We use frameId: 0 since this is a control packet, not an image frame + st.queue.push({ frameId: 0, packets: [packet] }); + this._drainAsync(id).catch(() => {}); + } + private _ensureState(id: string): BroadcasterState { let st = this._state.get(id); if (!st) { From beb9f4c650a26bec03866e17a9faa25b0ccc985c Mon Sep 17 00:00:00 2001 From: SleepinDevil Date: Sat, 7 Mar 2026 16:42:14 +1100 Subject: [PATCH 3/3] Update deviceManager.ts --- src/deviceManager.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/deviceManager.ts b/src/deviceManager.ts index cf7eef3..2e5fa95 100644 --- a/src/deviceManager.ts +++ b/src/deviceManager.ts @@ -203,6 +203,25 @@ export async function ensureDeviceAsync(id: string, cfg: DeviceConfig): Promise< } }); + // Function to deal with URL changing via either full page refresh or single page # follow + const handleNavigation = (url: string) => { + if (newDevice.url !== url) { + newDevice.url = url; + broadcaster.sendCurrentURL(newDevice.deviceId, url); + console.log(`[device] URL changed to: ${url}`); + } + }; + // Triggered on full page loads + session.on('Page.frameNavigated', (evt: any) => { + if (!evt.frame.parentId) { // Only track the main frame, ignore iframes + handleNavigation(evt.frame.url); + } + }); + // Triggered on Single Page App (SPA) hash or history API changes + session.on('Page.navigatedWithinDocument', (evt: any) => { + handleNavigation(evt.url); + }); + return newDevice; }