Companion build 0.5.27 (versionCode 47) shims navigator.clipboard natively, so in-app copy/paste is fixed with zero web changes — but the contract must not be clobbered (no unconditional re-define, no Object.freeze). Still open web-side: main.ts's fake readText() makes SendBitcoinModal's Paste button render and silently no-op in plain-HTTP browsers; 30 writeText call sites across three inconsistent patterns, ~10 of which toast 'Copied!' regardless of success; scanner prewarm/torch/constraints/no-reinit. Also records three factual corrections to docs/qr-scanner-snappiness-handover.md (ZXing not ML Kit; FORMAT_QR_CODE + KEEP_ONLY_LATEST already in place; do NOT drop to 720p — 1080p is a deliberate 0.5.22 fix for dense bolt11 QRs). Routed at Phase 11: the signed-PSBT paste affordance and the scanner items are the same surface as WALLET-05. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
6.6 KiB
created, title, area, severity, files
| created | title | area | severity | files | |||||
|---|---|---|---|---|---|---|---|---|---|
| 2026-08-02T12:10:00.000Z | Companion 0.5.27 handover — node/web-side clipboard + QR scanner work | ui | major |
|
Problem
Handover from the companion-app workstream (build 0.5.27, versionCode 47), 2026-08-02.
All of it is node-repo / neode-ui/ side — none was implemented there, the companion was
the only thing changed. Captured verbatim-in-substance so it is not lost.
1. Companion now shims navigator.clipboard — do not clobber it
0.5.27 adds a native ArchipelagoClipboard bridge, injected on every page load, pointing
navigator.clipboard.readText/writeText at the Android clipboard. Fixes copy and paste
in-app with zero web changes, in both the kiosk WebView and the in-app browser (BTCPay, LND).
Contract to preserve:
- Shim runs at
onPageStartedandonPageFinished; setswindow.__archyClipboardPatched = true. - Defines
navigator.clipboardas configurable if absent, then assignsreadText/writeTextonto whatever object is there. - Reads return via
window.__archyClipboardResult(text). - Do not unconditionally re-define
navigator.clipboardafter page load. Do notObject.freezeit. Today'smain.tspolyfill is safe only because it is guarded byif (!navigator.clipboard).
2. Web-side clipboard bugs — still open, affect plain browsers
Native is fixed; the same code is broken in any plain-HTTP browser (LAN/mesh — non-secure
context, so navigator.clipboard is undefined).
neode-ui/src/main.ts:10-24— the polyfill definesasync readText() { return '' }. That makesSendBitcoinModal.vue:425'scanReadClipboardtrue, so "Paste invoice" renders, fires, gets'', and silently does nothing. Fix: drop the fakereadText(or define it only when a real source exists) so the button correctly hides.- 30
writeTextcall sites, three patterns:- ~8 duplicate their own
execCommandfallback —Server.vue:762,Apps.vue:708,Credentials.vue:392,settings/AccountInfoSection.vue,settings/TwoFactorSection.vue. - ~10 are bare
navigator.clipboard.writeText(x).catch(() => {})—ReceiveBitcoinModal.vue:149,SendBitcoinModal.vue:326/:574,OnboardingSeedVerify.vue:201,OnboardingDid.vue:216/:223,settings/BackupSection.vue:270,PeerFiles.vue:1110/:1415— these show "Copied!" whether or not anything reached the clipboard. views/web5/utils.ts:39safeClipboardWriteis the best existing base.
- ~8 duplicate their own
- The
execCommandfallbacks are fragile: nofocus(), noreadonly, nosetSelectionRange, and the return value is never checked, so failure is invisible.
Suggested shape: one src/utils/clipboard.ts exporting copyText() / readText() /
canPaste(), preferring native bridge → async Clipboard API → hardened execCommand, toasting
"Copied" only on real success. Repoint all 30 sites at it.
Paste affordances that don't exist yet (bare textareas today): WalletScanModal.vue paste
field, ecash token (views/web5/Web5SendReceiveModals.vue:160), signed PSBT, federation
invite code (views/federation/JoinModal.vue:16).
3. QR scanner — web-side items still open
Native items are done in 0.5.27. Remaining on the web side:
- Pre-warm the camera — start
getUserMediawhen the modal opens (action pane), not when the scan pane is reached; hide the preview until needed. - Torch toggle —
qr-scannerexposeshasFlash()/turnFlashOn(). - Constraints —
{ focusMode: 'continuous', width: { ideal: 1280 } }. - Don't stop/start between panes — amount → scan currently re-inits the scanner; keep the paused stream alive for the modal's lifetime.
- Already done upstream: 10 scans/sec where
BarcodeDetectorexists. - New optional hook:
window.ArchipelagoQr?.prewarm?.()— safe to call repeatedly, safe when absent. The companion also self-prewarms on every node page load, so this is a small extra win.
4. Corrections to docs/qr-scanner-snappiness-handover.md (fix the doc)
That doc's native section assumed ML Kit and is wrong on three points — leaving it uncorrected invites someone to "optimise" the scanner backwards:
- The native scanner uses ZXing (Apache-2.0, on-device, no telemetry), not ML Kit. ML Kit was rejected as a proprietary Google/Play-Services dependency, against project dependency policy. There is therefore no model cold-start to pay.
FORMAT_QR_CODE-only andSTRATEGY_KEEP_ONLY_LATESTwere already in place before this round.- Do not drop analysis resolution to 1280×720. 1920×1080 is a deliberate 0.5.22 fix: at 720p, dense bolt11 invoice QRs were undecodable on far-focusing lenses (e.g. Pixel 9a main) while sparse address QRs still read — that was the original "scanner doesn't pick up invoices" report.
What 0.5.27 changed natively, for the record: two-tier decode (cheap centre-70% pass ~18/s plus
the thorough full-frame TRY_HARDER + inverted-retry pass ~5/s, replacing a single expensive
pass capped ~7/s); camera/decoder prewarm; torch toggle in wallet-scan and pairing scanners;
tap-to-focus with 4s suppression of periodic centre autofocus; zoom hunt alternating 1×/1.5×
after ~3s with no decode; success haptic on first hit only (so animated QRs don't buzz per frame).
5. Not a web issue, noted for completeness
The companion regained "swipe away in recents = restart": the retained kiosk WebView (kept so remote ⇄ dashboard doesn't reload) is now released when the activity finishes, because the FIPS mesh service keeps the process alive and the static WebView was surviving the swipe. A Restart card was added to the hub menu as the manual path.
Solution
Route into Phase 11 (Wallet Experience & LND UI Parity) rather than a standalone pass —
§2's signed-PSBT paste affordance and §3's scanner items are the same surface as WALLET-05
(the PSBT air-gap round trip), and WalletScanModal.vue is named in both. The clipboard utility
(§2) is broader than Phase 11 and can land independently; the doc corrections (§4) are a
five-minute fix that should not wait for a phase.
Supersedes any overlapping assumptions in docs/qr-scanner-snappiness-handover.md — that
doc is now known-wrong on the three points in §4.