@8bitbish/screenshot-service 1.0.0 → 1.0.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.
@@ -0,0 +1,188 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.pairAndroidWireless = pairAndroidWireless;
4
+ exports.getLastAndroidEndpoint = getLastAndroidEndpoint;
5
+ const child_process_1 = require("child_process");
6
+ const util_1 = require("util");
7
+ const fs_1 = require("fs");
8
+ const os_1 = require("os");
9
+ const path_1 = require("path");
10
+ const detect_1 = require("./detect");
11
+ const execFileAsync = (0, util_1.promisify)(child_process_1.execFile);
12
+ const ANDROID_IP_FILE = (0, path_1.join)((0, os_1.homedir)(), ".screenshotAssistent-android-ip");
13
+ /**
14
+ * After Wireless Debugging pairing, the phone exposes its data port via mDNS as
15
+ * `_adb-tls-connect._tcp`. Pairing port and connect port are DIFFERENT — the
16
+ * connect port is dynamic and shown on the main Wireless Debugging screen.
17
+ * Polls `adb mdns services` for up to `timeoutMs` waiting for the entry to appear.
18
+ */
19
+ async function discoverConnectPort(adb, ip, timeoutMs = 8000) {
20
+ const portRegex = new RegExp(ip.replace(/\./g, "\\.") + ":(\\d+)");
21
+ const deadline = Date.now() + timeoutMs;
22
+ while (Date.now() < deadline) {
23
+ try {
24
+ const { stdout } = await execFileAsync(adb, ["mdns", "services"], { timeout: 3000 });
25
+ for (const line of stdout.split("\n")) {
26
+ if (line.includes("_adb-tls-connect") && line.includes(ip)) {
27
+ const m = line.match(portRegex);
28
+ if (m)
29
+ return m[1];
30
+ }
31
+ }
32
+ }
33
+ catch { }
34
+ await new Promise(r => setTimeout(r, 500));
35
+ }
36
+ return null;
37
+ }
38
+ /** Returns the port if `<ip>` already appears as a connected device, else null. */
39
+ async function findExistingConnection(adb, ip) {
40
+ try {
41
+ const { stdout } = await execFileAsync(adb, ["devices"], { timeout: 3000, encoding: "utf8" });
42
+ const m = stdout.match(new RegExp("^" + ip.replace(/\./g, "\\.") + ":(\\d+)\\s+device", "m"));
43
+ if (m)
44
+ return m[1];
45
+ }
46
+ catch { }
47
+ return null;
48
+ }
49
+ function emitStep(cb, patch) {
50
+ const p = { ...patch };
51
+ cb?.(p);
52
+ return p;
53
+ }
54
+ /**
55
+ * Pair and connect an Android device over wireless ADB.
56
+ *
57
+ * Phone setup (user-driven, before calling this):
58
+ * Settings → Developer Options → Wireless Debugging ON → Pair device with pairing code
59
+ * The phone shows: IP, pairing port, 6-digit code.
60
+ *
61
+ * What this does:
62
+ * 1. `adb pair ip:port` and feeds the code via stdin (no terminal prompt).
63
+ * 2. `adb connect ip:5555` (or custom connectPort) to establish the data connection.
64
+ * 3. Saves the IP for auto-reconnect on subsequent screenshots.
65
+ */
66
+ async function pairAndroidWireless(opts, onProgress) {
67
+ const { ip, port, code, connectPort: userConnectPort } = opts;
68
+ if (!/^\d+\.\d+\.\d+\.\d+$/.test(ip))
69
+ throw new Error(`Invalid IP address: ${ip}`);
70
+ if (!/^\d+$/.test(port))
71
+ throw new Error(`Invalid pairing port: ${port}`);
72
+ if (!/^\d{6}$/.test(code))
73
+ throw new Error(`Pairing code must be 6 digits: got "${code}"`);
74
+ const { adb } = await (0, detect_1.checkAndroidSetup)();
75
+ if (!adb) {
76
+ throw new Error("adb not found. Run installAndroidDeps() first.");
77
+ }
78
+ const TOTAL = 2;
79
+ // ── Step 1: pair ───────────────────────────────────────────────────────────
80
+ let pair = emitStep(onProgress, {
81
+ id: "pair", label: `Pairing with ${ip}:${port}`, status: "running", index: 1, total: TOTAL,
82
+ });
83
+ await new Promise((resolve, reject) => {
84
+ const child = (0, child_process_1.spawn)(adb, ["pair", `${ip}:${port}`], {
85
+ stdio: ["pipe", "pipe", "pipe"],
86
+ });
87
+ const lines = [];
88
+ const onOut = (chunk) => {
89
+ const text = chunk.toString("utf8");
90
+ lines.push(text);
91
+ for (const line of text.split("\n")) {
92
+ if (!line.trim())
93
+ continue;
94
+ onProgress?.({ ...pair, log: line });
95
+ }
96
+ };
97
+ child.stdout.on("data", onOut);
98
+ child.stderr.on("data", onOut);
99
+ // adb pair prompts "Enter pairing code: " on stdin — feed the code immediately
100
+ child.stdin.write(`${code}\n`);
101
+ child.stdin.end();
102
+ child.on("error", reject);
103
+ child.on("close", (exitCode) => {
104
+ const combined = lines.join("");
105
+ if (exitCode === 0 && /successfully paired/i.test(combined)) {
106
+ resolve();
107
+ }
108
+ else {
109
+ reject(new Error("Pairing failed. Check the IP/port/code are correct and that the phone's pairing screen is still open.\n" +
110
+ combined.trim()));
111
+ }
112
+ });
113
+ });
114
+ emitStep(onProgress, { ...pair, status: "done" });
115
+ // ── Step 2: connect ───────────────────────────────────────────────────────
116
+ // The pairing port is dead now. The persistent connect port is different
117
+ // (shown on the main Wireless Debugging screen) and is also broadcast via
118
+ // mDNS as `_adb-tls-connect._tcp`. Discover it automatically.
119
+ let connect = emitStep(onProgress, {
120
+ id: "connect", label: "Detecting connection port…", status: "running", index: 2, total: TOTAL,
121
+ });
122
+ let connectPort = userConnectPort ?? null;
123
+ // 1. Did mDNS auto-connect us already? (Android 11+ often does this immediately after pairing.)
124
+ if (!connectPort) {
125
+ const existing = await findExistingConnection(adb, ip);
126
+ if (existing) {
127
+ (0, fs_1.writeFileSync)(ANDROID_IP_FILE, `${ip}:${existing}`, "utf8");
128
+ emitStep(onProgress, { ...connect, status: "done", label: `Connected on ${ip}:${existing}` });
129
+ return;
130
+ }
131
+ }
132
+ // 2. Discover via mDNS.
133
+ if (!connectPort) {
134
+ onProgress?.({ ...connect, log: "Looking for the device on the network (mDNS)…" });
135
+ connectPort = await discoverConnectPort(adb, ip);
136
+ }
137
+ // 3. If discovery fails, surface a clear error pointing the user at the right place.
138
+ if (!connectPort) {
139
+ throw new Error("Couldn't auto-detect the connection port for " + ip + ".\n" +
140
+ "Open Settings → Developer Options → Wireless Debugging on your phone (NOT the pairing sub-screen) and " +
141
+ "note the port shown next to the IP at the top — that's the connect port. Then retry.");
142
+ }
143
+ emitStep(onProgress, { ...connect, label: `Connecting to ${ip}:${connectPort}` });
144
+ await new Promise((resolve, reject) => {
145
+ const child = (0, child_process_1.spawn)(adb, ["connect", `${ip}:${connectPort}`], { stdio: ["ignore", "pipe", "pipe"] });
146
+ const lines = [];
147
+ const onOut = (chunk) => {
148
+ const text = chunk.toString("utf8");
149
+ lines.push(text);
150
+ for (const line of text.split("\n")) {
151
+ if (!line.trim())
152
+ continue;
153
+ onProgress?.({ ...connect, log: line });
154
+ }
155
+ };
156
+ child.stdout.on("data", onOut);
157
+ child.stderr.on("data", onOut);
158
+ child.on("error", reject);
159
+ child.on("close", (exitCode) => {
160
+ const combined = lines.join("");
161
+ // adb connect returns 0 even on failure — check the output text
162
+ if (exitCode === 0 && /connected to/i.test(combined) && !/failed to connect/i.test(combined)) {
163
+ resolve();
164
+ }
165
+ else {
166
+ reject(new Error("Connect failed. Make sure your Mac and phone are on the same WiFi and Wireless Debugging is on.\n" +
167
+ combined.trim()));
168
+ }
169
+ });
170
+ });
171
+ // Save IP for auto-reconnect
172
+ try {
173
+ (0, fs_1.writeFileSync)(ANDROID_IP_FILE, `${ip}:${connectPort}`, "utf8");
174
+ }
175
+ catch { }
176
+ emitStep(onProgress, { ...connect, status: "done" });
177
+ }
178
+ /** Returns the last saved wireless ADB endpoint (e.g. "192.168.1.50:5555"), or null. */
179
+ function getLastAndroidEndpoint() {
180
+ try {
181
+ const v = (0, fs_1.readFileSync)(ANDROID_IP_FILE, "utf8").trim();
182
+ return v || null;
183
+ }
184
+ catch {
185
+ return null;
186
+ }
187
+ }
188
+ //# sourceMappingURL=pair.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pair.js","sourceRoot":"","sources":["../../setup/pair.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Returns the path to the first python binary that has pymobiledevice3 installed,
3
+ * or null if none found. Never throws — use {@link findPythonOrThrow} when an
4
+ * exception is the right behaviour.
5
+ */
6
+ export declare function findPython(): string | null;
7
+ export declare function findPythonOrThrow(): string;
8
+ /** Returns the path to a python3 binary (with or without pymobiledevice3), or null. */
9
+ export declare function findAnyPython3(): string | null;
10
+ //# sourceMappingURL=python.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"python.d.ts","sourceRoot":"","sources":["../../setup/python.ts"],"names":[],"mappings":"AAsBA;;;;GAIG;AACH,wBAAgB,UAAU,IAAI,MAAM,GAAG,IAAI,CAQ1C;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAO1C;AAED,uFAAuF;AACvF,wBAAgB,cAAc,IAAI,MAAM,GAAG,IAAI,CAQ9C"}
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.findPython = findPython;
4
+ exports.findPythonOrThrow = findPythonOrThrow;
5
+ exports.findAnyPython3 = findAnyPython3;
6
+ const child_process_1 = require("child_process");
7
+ // Candidate python binaries to try, in preference order. Newer versions first so
8
+ // users on 3.14+ get picked up automatically when those land.
9
+ const PYTHON_CANDIDATES = [
10
+ "/opt/homebrew/bin/python3.14",
11
+ "/usr/local/bin/python3.14",
12
+ "python3.14",
13
+ "/opt/homebrew/bin/python3.13",
14
+ "/usr/local/bin/python3.13",
15
+ "python3.13",
16
+ "/opt/homebrew/bin/python3.12",
17
+ "/usr/local/bin/python3.12",
18
+ "python3.12",
19
+ "/opt/homebrew/bin/python3.11",
20
+ "/usr/local/bin/python3.11",
21
+ "python3.11",
22
+ "/opt/homebrew/bin/python3",
23
+ "/usr/local/bin/python3",
24
+ "python3",
25
+ ];
26
+ /**
27
+ * Returns the path to the first python binary that has pymobiledevice3 installed,
28
+ * or null if none found. Never throws — use {@link findPythonOrThrow} when an
29
+ * exception is the right behaviour.
30
+ */
31
+ function findPython() {
32
+ for (const p of PYTHON_CANDIDATES) {
33
+ try {
34
+ (0, child_process_1.execFileSync)(p, ["-c", "import pymobiledevice3"], { stdio: "ignore" });
35
+ return p;
36
+ }
37
+ catch { }
38
+ }
39
+ return null;
40
+ }
41
+ function findPythonOrThrow() {
42
+ const p = findPython();
43
+ if (p)
44
+ return p;
45
+ throw new Error("Python with pymobiledevice3 not found. Run the iOS setup wizard or:\n" +
46
+ " npx @8bitbish/screenshot-service setup-ios");
47
+ }
48
+ /** Returns the path to a python3 binary (with or without pymobiledevice3), or null. */
49
+ function findAnyPython3() {
50
+ for (const p of PYTHON_CANDIDATES) {
51
+ try {
52
+ (0, child_process_1.execFileSync)(p, ["--version"], { stdio: "ignore" });
53
+ return p;
54
+ }
55
+ catch { }
56
+ }
57
+ return null;
58
+ }
59
+ //# sourceMappingURL=python.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"python.js","sourceRoot":"","sources":["../../setup/python.ts"],"names":[],"mappings":";;AA2BA,gCAQC;AAED,8CAOC;AAGD,wCAQC;AAvDD,iDAA6C;AAE7C,iFAAiF;AACjF,8DAA8D;AAC9D,MAAM,iBAAiB,GAAG;IACxB,8BAA8B;IAC9B,2BAA2B;IAC3B,YAAY;IACZ,8BAA8B;IAC9B,2BAA2B;IAC3B,YAAY;IACZ,8BAA8B;IAC9B,2BAA2B;IAC3B,YAAY;IACZ,8BAA8B;IAC9B,2BAA2B;IAC3B,YAAY;IACZ,2BAA2B;IAC3B,wBAAwB;IACxB,SAAS;CACV,CAAC;AAEF;;;;GAIG;AACH,SAAgB,UAAU;IACxB,KAAK,MAAM,CAAC,IAAI,iBAAiB,EAAE,CAAC;QAClC,IAAI,CAAC;YACH,IAAA,4BAAY,EAAC,CAAC,EAAE,CAAC,IAAI,EAAE,wBAAwB,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;YACvE,OAAO,CAAC,CAAC;QACX,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACZ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAgB,iBAAiB;IAC/B,MAAM,CAAC,GAAG,UAAU,EAAE,CAAC;IACvB,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IAChB,MAAM,IAAI,KAAK,CACb,uEAAuE;QACvE,8CAA8C,CAC/C,CAAC;AACJ,CAAC;AAED,uFAAuF;AACvF,SAAgB,cAAc;IAC5B,KAAK,MAAM,CAAC,IAAI,iBAAiB,EAAE,CAAC;QAClC,IAAI,CAAC;YACH,IAAA,4BAAY,EAAC,CAAC,EAAE,CAAC,WAAW,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;YACpD,OAAO,CAAC,CAAC;QACX,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACZ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,37 @@
1
+ export interface IosSetupStatus {
2
+ brew: boolean;
3
+ python: string | null;
4
+ pymobiledevice3: boolean;
5
+ sudoers: boolean;
6
+ deviceVisible: boolean;
7
+ ok: boolean;
8
+ }
9
+ export interface AndroidSetupStatus {
10
+ adb: string | null;
11
+ deviceConnected: boolean;
12
+ ok: boolean;
13
+ }
14
+ export type SetupStepStatus = "running" | "done" | "skipped" | "error";
15
+ export interface SetupProgress {
16
+ /** Stable id for the step — e.g. "brew", "python", "pymobiledevice3", "sudoers". */
17
+ id: string;
18
+ /** Human-readable label — e.g. "Installing Python 3.13". */
19
+ label: string;
20
+ /** Optional sub-detail — e.g. the package name being installed. */
21
+ detail?: string;
22
+ /** Raw log line from the underlying subprocess, when available. */
23
+ log?: string;
24
+ status: SetupStepStatus;
25
+ /** Step index / total, for UI progress bars. */
26
+ index: number;
27
+ total: number;
28
+ }
29
+ export type SetupProgressCallback = (p: SetupProgress) => void;
30
+ export interface AndroidPairOptions {
31
+ ip: string;
32
+ port: string;
33
+ code: string;
34
+ /** Optional override for the connect port. Defaults to 5555. */
35
+ connectPort?: string;
36
+ }
37
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../setup/types.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,eAAe,EAAE,OAAO,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,OAAO,CAAC;IACvB,EAAE,EAAE,OAAO,CAAC;CACb;AAED,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,eAAe,EAAE,OAAO,CAAC;IACzB,EAAE,EAAE,OAAO,CAAC;CACb;AAED,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;AAEvE,MAAM,WAAW,aAAa;IAC5B,oFAAoF;IACpF,EAAE,EAAE,MAAM,CAAC;IACX,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,eAAe,CAAC;IACxB,gDAAgD;IAChD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,EAAE,aAAa,KAAK,IAAI,CAAC;AAE/D,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB"}
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ // Shared types for the setup/wizard subsystem.
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../setup/types.ts"],"names":[],"mappings":";AAAA,+CAA+C"}
@@ -0,0 +1,18 @@
1
+ export interface SetupWindowOptions {
2
+ /** Which platform's wizard to show. */
3
+ platform: "ios" | "android";
4
+ /** Optional parent BrowserWindow — wizard becomes modal to it. */
5
+ parent?: import("electron").BrowserWindow;
6
+ /** Override the window title. Default: "Setup". */
7
+ title?: string;
8
+ }
9
+ /**
10
+ * Open the setup wizard as a native window. Resolves when the user closes the
11
+ * window (whether they finished setup or bailed out).
12
+ *
13
+ * The window contains an HTML wizard that calls back into the main process
14
+ * over IPC to drive {@link installIosDeps}, {@link installAndroidDeps}, and
15
+ * {@link pairAndroidWireless}. The user never sees a terminal.
16
+ */
17
+ export declare function createSetupWindow(opts: SetupWindowOptions): Promise<void>;
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../wizard/index.ts"],"names":[],"mappings":"AA8BA,MAAM,WAAW,kBAAkB;IACjC,uCAAuC;IACvC,QAAQ,EAAE,KAAK,GAAG,SAAS,CAAC;IAC5B,kEAAkE;IAClE,MAAM,CAAC,EAAE,OAAO,UAAU,EAAE,aAAa,CAAC;IAC1C,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiF/E"}
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createSetupWindow = createSetupWindow;
4
+ const path_1 = require("path");
5
+ const setup_1 = require("../setup");
6
+ let cachedElectron = null;
7
+ function loadElectron() {
8
+ if (cachedElectron)
9
+ return cachedElectron;
10
+ try {
11
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
12
+ cachedElectron = require("electron");
13
+ return cachedElectron;
14
+ }
15
+ catch (err) {
16
+ throw new Error("electron is required to open the setup wizard window. Install it in your host app:\n" +
17
+ " npm install electron");
18
+ }
19
+ }
20
+ /**
21
+ * Open the setup wizard as a native window. Resolves when the user closes the
22
+ * window (whether they finished setup or bailed out).
23
+ *
24
+ * The window contains an HTML wizard that calls back into the main process
25
+ * over IPC to drive {@link installIosDeps}, {@link installAndroidDeps}, and
26
+ * {@link pairAndroidWireless}. The user never sees a terminal.
27
+ */
28
+ async function createSetupWindow(opts) {
29
+ const electron = loadElectron();
30
+ const { BrowserWindow, ipcMain } = electron;
31
+ const win = new BrowserWindow({
32
+ width: 640,
33
+ height: 720,
34
+ minWidth: 560,
35
+ minHeight: 560,
36
+ title: opts.title ?? (opts.platform === "ios" ? "Setup iPhone capture" : "Setup Android capture"),
37
+ parent: opts.parent,
38
+ modal: !!opts.parent,
39
+ resizable: true,
40
+ minimizable: false,
41
+ maximizable: false,
42
+ fullscreenable: false,
43
+ autoHideMenuBar: true,
44
+ backgroundColor: "#0e0f12",
45
+ show: false,
46
+ webPreferences: {
47
+ preload: (0, path_1.join)(__dirname, "preload.js"),
48
+ contextIsolation: true,
49
+ nodeIntegration: false,
50
+ sandbox: false,
51
+ },
52
+ });
53
+ win.once("ready-to-show", () => win.show());
54
+ win.loadFile((0, path_1.join)(__dirname, "renderer", "index.html"), {
55
+ query: { platform: opts.platform },
56
+ });
57
+ // ── IPC handlers (scoped to this window) ────────────────────────────────────
58
+ // We register handlers per-window to keep them isolated and ensure they are
59
+ // cleaned up when the window closes.
60
+ const channels = {
61
+ checkIos: `wizard:${win.id}:check-ios`,
62
+ installIos: `wizard:${win.id}:install-ios`,
63
+ checkAndroid: `wizard:${win.id}:check-android`,
64
+ installAndroid: `wizard:${win.id}:install-android`,
65
+ pairAndroid: `wizard:${win.id}:pair-android`,
66
+ close: `wizard:${win.id}:close`,
67
+ };
68
+ // Expose channel names to renderer via initial-load query (read by preload).
69
+ win.webContents.on("did-finish-load", () => {
70
+ win.webContents.send("wizard:init", { platform: opts.platform, channels });
71
+ });
72
+ ipcMain.handle(channels.checkIos, async () => (0, setup_1.checkIosSetup)());
73
+ ipcMain.handle(channels.checkAndroid, async () => (0, setup_1.checkAndroidSetup)());
74
+ ipcMain.handle(channels.installIos, async () => {
75
+ await (0, setup_1.installIosDeps)((p) => {
76
+ if (!win.isDestroyed())
77
+ win.webContents.send(`${channels.installIos}:progress`, p);
78
+ });
79
+ });
80
+ ipcMain.handle(channels.installAndroid, async () => {
81
+ await (0, setup_1.installAndroidDeps)((p) => {
82
+ if (!win.isDestroyed())
83
+ win.webContents.send(`${channels.installAndroid}:progress`, p);
84
+ });
85
+ });
86
+ ipcMain.handle(channels.pairAndroid, async (_e, opts) => {
87
+ await (0, setup_1.pairAndroidWireless)(opts, (p) => {
88
+ if (!win.isDestroyed())
89
+ win.webContents.send(`${channels.pairAndroid}:progress`, p);
90
+ });
91
+ });
92
+ ipcMain.handle(channels.close, async () => {
93
+ if (!win.isDestroyed())
94
+ win.close();
95
+ });
96
+ // Resolve when the window closes
97
+ return new Promise((resolve) => {
98
+ win.on("closed", () => {
99
+ for (const ch of Object.values(channels)) {
100
+ try {
101
+ ipcMain.removeHandler(ch);
102
+ }
103
+ catch { }
104
+ }
105
+ resolve();
106
+ });
107
+ });
108
+ }
109
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../wizard/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ 'use strict';
2
+
3
+ // Preload script for the wizard BrowserWindow. Bridges renderer-side JS to the
4
+ // main process's setup IPC handlers without exposing Node APIs.
5
+ //
6
+ // Plain JavaScript (rather than TypeScript) so the package can build without
7
+ // electron's type definitions installed — electron is an optional peer dep.
8
+
9
+ const { contextBridge, ipcRenderer } = require('electron');
10
+
11
+ let initPayload = null;
12
+ const initListeners = [];
13
+
14
+ ipcRenderer.on('wizard:init', (_e, payload) => {
15
+ initPayload = payload;
16
+ while (initListeners.length) initListeners.shift()(payload);
17
+ });
18
+
19
+ function waitForInit() {
20
+ if (initPayload) return Promise.resolve(initPayload);
21
+ return new Promise((resolve) => initListeners.push(resolve));
22
+ }
23
+
24
+ async function invoke(channelKey, ...args) {
25
+ const { channels } = await waitForInit();
26
+ const channel = channels[channelKey];
27
+ if (!channel) throw new Error(`Unknown wizard channel: ${channelKey}`);
28
+ return ipcRenderer.invoke(channel, ...args);
29
+ }
30
+
31
+ function onProgress(channelKey, cb) {
32
+ let cleanup = () => {};
33
+ waitForInit().then(({ channels }) => {
34
+ const channel = channels[channelKey] + ':progress';
35
+ const handler = (_e, p) => cb(p);
36
+ ipcRenderer.on(channel, handler);
37
+ cleanup = () => ipcRenderer.removeListener(channel, handler);
38
+ });
39
+ return () => cleanup();
40
+ }
41
+
42
+ contextBridge.exposeInMainWorld('wizard', {
43
+ init: waitForInit,
44
+ checkIos: () => invoke('checkIos'),
45
+ installIos: () => invoke('installIos'),
46
+ onInstallIosProgress: (cb) => onProgress('installIos', cb),
47
+ checkAndroid: () => invoke('checkAndroid'),
48
+ installAndroid: () => invoke('installAndroid'),
49
+ onInstallAndroidProgress: (cb) => onProgress('installAndroid', cb),
50
+ pairAndroid: (opts) => invoke('pairAndroid', opts),
51
+ onPairAndroidProgress: (cb) => onProgress('pairAndroid', cb),
52
+ close: () => invoke('close'),
53
+ });