phone-lens 1.1.0 → 1.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/lib/client.js +38 -13
- package/lib/dev.js +428 -73
- package/lib/index.js +486 -80
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -268,6 +268,7 @@ window.__ModuleLoader__.load({
|
|
|
268
268
|
const [rename, setRename] = useState(null); // {id, name} — self-drawn rename dialog
|
|
269
269
|
const [appQr, setAppQr] = useState(null); // app-download QR dialog payload (or {loading}/{fallback:true})
|
|
270
270
|
const [previewOff, setPreviewOff] = useState(false); // host says the phone's stream stopped (preview off on the phone)
|
|
271
|
+
const [previewOffline, setPreviewOffline] = useState(false); // host flagged the LINK itself dead (wifi drop, no frames AND no keepalive) — not a preview toggle
|
|
271
272
|
const [settingsOpen, setSettingsOpen] = useState(false); // collapsible settings section under the main card
|
|
272
273
|
const [appSt, setAppSt] = useState(null); // {saveMode, saveDir, effectiveSaveDir} | null (null = host too old)
|
|
273
274
|
const [dirDraft, setDirDraft] = useState("");
|
|
@@ -276,6 +277,11 @@ window.__ModuleLoader__.load({
|
|
|
276
277
|
const wsRef = useRef(null);
|
|
277
278
|
const framesRef = useRef(0);
|
|
278
279
|
const rotRef = useRef(0);
|
|
280
|
+
// last devices-broadcast length — the onmessage closure never re-runs
|
|
281
|
+
// (the WS effect depends only on wsUrl), so comparing against the
|
|
282
|
+
// `devices` STATE there read a stale first-render value and fired the
|
|
283
|
+
// QR-rotation flash on EVERY broadcast instead of only on growth
|
|
284
|
+
const lastDeviceCount = useRef(0);
|
|
279
285
|
|
|
280
286
|
const baseUrl = `http://127.0.0.1:${port}`;
|
|
281
287
|
const wsUrl = `ws://127.0.0.1:${port}/ws/view`;
|
|
@@ -341,9 +347,13 @@ window.__ModuleLoader__.load({
|
|
|
341
347
|
} else if (m.type === "frame_meta") {
|
|
342
348
|
if (m.rotation !== undefined) rotRef.current = m.rotation;
|
|
343
349
|
} else if (m.type === "preview_state") {
|
|
344
|
-
// host-derived: the ACTIVE phone's stream stopped
|
|
345
|
-
// preview off in the app) or
|
|
350
|
+
// host-derived: the ACTIVE phone's stream stopped — either the
|
|
351
|
+
// user turned preview off in the app (keepalive still alive) or
|
|
352
|
+
// the link itself died (no frames AND no keepalive for 25s →
|
|
353
|
+
// reason:"offline"). Old hosts never send `reason` → legacy
|
|
354
|
+
// behavior is kept bit-for-bit.
|
|
346
355
|
setPreviewOff(!m.on);
|
|
356
|
+
setPreviewOffline(!m.on && m.reason === "offline");
|
|
347
357
|
} else if (m.type === "upload_saved") {
|
|
348
358
|
// folder-only mode: the photo bypassed the composer; local hint
|
|
349
359
|
setFlashOk(`已保存至 ${m.dir || "指定文件夹"}`);
|
|
@@ -363,8 +373,10 @@ window.__ModuleLoader__.load({
|
|
|
363
373
|
.catch((e) => setFlashErr("自动放入失败: " + String(e && e.message || e).slice(0, 90)));
|
|
364
374
|
} else if (m.type === "devices") {
|
|
365
375
|
// side effects OUT of the updater (updaters must stay pure —
|
|
366
|
-
// StrictMode double-invokes them); compare
|
|
367
|
-
|
|
376
|
+
// StrictMode double-invokes them); compare against the ref,
|
|
377
|
+
// NOT the `devices` state (stale-closure trap), then setState
|
|
378
|
+
const grew = (m.devices || []).length > lastDeviceCount.current;
|
|
379
|
+
lastDeviceCount.current = (m.devices || []).length;
|
|
368
380
|
setDevices(m.devices || []);
|
|
369
381
|
// a NEW device came online → the one-time pairing code was
|
|
370
382
|
// consumed; rotate the QR so the next scan sees a fresh code
|
|
@@ -380,6 +392,12 @@ window.__ModuleLoader__.load({
|
|
|
380
392
|
if (prev === "disconnected") return "connecting";
|
|
381
393
|
return prev;
|
|
382
394
|
});
|
|
395
|
+
} else if (m.type === "paired") {
|
|
396
|
+
// a phone completed /pair — the one-time pairing code burned
|
|
397
|
+
// with it. Rotate the QR right now; the devices-list growth
|
|
398
|
+
// path also rotates, but only once the uplink actually lands.
|
|
399
|
+
setFlashOk(`设备已配对${m.name ? ": " + m.name : ""},二维码已更换`);
|
|
400
|
+
void refreshQr(true);
|
|
383
401
|
} else if (m.type === "error") {
|
|
384
402
|
setFlashErr(`${m.code}${m.message ? ":" + m.message : ""}`);
|
|
385
403
|
}
|
|
@@ -387,6 +405,7 @@ window.__ModuleLoader__.load({
|
|
|
387
405
|
ws.onclose = () => {
|
|
388
406
|
setCamOn(false);
|
|
389
407
|
setPreviewOff(false);
|
|
408
|
+
setPreviewOffline(false);
|
|
390
409
|
if (!stopped) timer = setTimeout(connect, 2000);
|
|
391
410
|
};
|
|
392
411
|
};
|
|
@@ -577,11 +596,13 @@ window.__ModuleLoader__.load({
|
|
|
577
596
|
{ className: "lm-body" },
|
|
578
597
|
// only show the viewfinder canvas while a camera is actually
|
|
579
598
|
// streaming; when the phone turned preview off, clear the last
|
|
580
|
-
// frame and say so instead of freezing on a stale picture
|
|
599
|
+
// frame and say so instead of freezing on a stale picture.
|
|
600
|
+
// previewOffline = the host proved the LINK died (no frames AND
|
|
601
|
+
// no keepalive 25s): say "connection lost", never "preview off"
|
|
581
602
|
camOn && !previewOff
|
|
582
603
|
? h("canvas", { ref: canvasRef, className: "lm-canvas", width: 360, height: 640 })
|
|
583
604
|
: camOn && previewOff
|
|
584
|
-
? h("div", { className: "lm-preview-off" }, "您在手机端已关闭预览功能")
|
|
605
|
+
? h("div", { className: "lm-preview-off" }, previewOffline ? "与手机的连接已中断(设备离线)" : "您在手机端已关闭预览功能")
|
|
585
606
|
: null,
|
|
586
607
|
h(
|
|
587
608
|
"div",
|
|
@@ -589,12 +610,14 @@ window.__ModuleLoader__.load({
|
|
|
589
610
|
h("span", null, camOn && !previewOff && fps > 0 ? `${fps} fps` : "—"),
|
|
590
611
|
h(
|
|
591
612
|
"span",
|
|
592
|
-
{ className: !camOn ? "warn-big" : previewOff ? "warn" : "" },
|
|
613
|
+
{ className: !camOn || previewOffline ? "warn-big" : previewOff ? "warn" : "" },
|
|
593
614
|
!camOn
|
|
594
615
|
? "手机未连接!相同局域网下扫码添加配对设备;若已配对过的手机,请在设置内点击本电脑端,激活为活动设备"
|
|
595
|
-
:
|
|
596
|
-
? "
|
|
597
|
-
:
|
|
616
|
+
: previewOffline
|
|
617
|
+
? "连接已断开(手机离线),请检查手机网络;网络恢复后将自动重连"
|
|
618
|
+
: previewOff
|
|
619
|
+
? "预览已关闭,在手机端重新开启后恢复"
|
|
620
|
+
: "预览中",
|
|
598
621
|
),
|
|
599
622
|
),
|
|
600
623
|
// ── version hints (plain colored lines, never dialogs) ──
|
|
@@ -918,9 +941,11 @@ window.__ModuleLoader__.load({
|
|
|
918
941
|
{ className: "lm-setsec-rows" },
|
|
919
942
|
// tool names in the open (user-requested): what the
|
|
920
943
|
// model will actually see in its tool list
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
944
|
+
infoRow("拍摄照片(phone_take_photo)", "手机立即拍照,照片直接进入模型视野——不放入输入框、不保存到电脑文件夹,与拍照注入模式设置无关。", "w1"),
|
|
945
|
+
infoRow("关闭手机摄像头(phone_camera_pause)", "让手机相机提前进入空闲省电状态。", "w2"),
|
|
946
|
+
infoRow("恢复手机摄像头(phone_camera_resume)", "立即打开相机并恢复预览拍摄。", "w3"),
|
|
947
|
+
infoRow("调整变焦(phone_camera_zoom)", "按指定倍数(或步进)拉近/拉远手机镜头,取景画面与照片同步生效,超出设备范围自动收拢。", "w4"),
|
|
948
|
+
infoRow("查询变焦(phone_camera_zoom_info)", "读取手机当前变焦倍数与设备支持的变焦范围。", "w5"),
|
|
924
949
|
),
|
|
925
950
|
),
|
|
926
951
|
flash ? h("div", { className: "lm-setsec-note", key: "f" }, flash) : null,
|