Every key on the app's keyboard takes focus from the page for a moment and
gives it back, so the site's focus ring (a 2px blue outline on the search
box, and a brighter border) flashed with each letter. Outline rings are
turned off with the page's clean-up CSS, and the guest preload strips
outline, border and box-shadow from the site's own :focus / :focus-within /
:focus-visible rules, keeping anything else those rules do.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The globe was missing from the rule that dims tab icons at rest, so it
always showed at full brightness and looked like the active tab.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Browser tab: the keyboard opens right under the search box, and Enter
searches and puts it away.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The stage TV is portrait, and the keyboard opened at the bottom of the screen — a
reach to the floor to type into a search box at the top.
- browser-guest-preload.cjs reports the tapped field's position (and which field it
is) with the tap, and again when focus moves to another field.
- browser-tab.js opens the keyboard directly under that field, centred on it and
kept on screen; if there is no room under it, just above it. Focus moving to a
different field (Tab, the next memorize blank) moves the keyboard with it.
Re-tapping the same field leaves a dragged keyboard where it was.
- The keyboard's Enter submits to the page and then hides the keyboard, so search
results are not covered. Tapping a field brings it back.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On the TV the update stopped at "Bible App cannot be closed", and uninstalling
from Windows Settings showed "Installer integrity check has failed". The cause is
the uninstaller, not anything running.
An update runs the PREVIOUS version's uninstaller first. Versions 1.4.35-1.4.37
were built on macOS, where electron-builder can't run NSIS to write the uninstaller
and rebuilds it in JavaScript (UninstallerReader) from the installer's stub — without
NSIS's icon patch. makensis computes the uninstaller's CRC over the stub WITH the
uninstaller icon in it, so whenever the two icons differ the result fails NSIS's
integrity check. With no app icon configured, NSIS uses its two different default
icons, so every Mac-built uninstaller was damaged. Verified with a reimplementation
of NSIS's loadHeaders() check: the shipped installers pass; the captured uninstaller
failed (0x4235c8f5 vs stored 0x90f7de2c) and passes with this fix (0xb37e533c).
- build/installerIcon.ico: NSIS's own default install icon, so electron-builder uses
ONE icon for installer and uninstaller (nothing looks different; the app's icon is
unchanged). tools/build-win.cjs refuses to build without it.
- build/installer.nsh: for installs of 1.4.35-1.4.37 only, clear the old
UninstallString so the damaged uninstaller is not run; the new files are copied over
the old ones and a fresh, valid uninstaller is written. Every other version keeps
the normal uninstall-then-install path.
- BUILD-AND-DEPLOY.md: why the icon file must stay.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Browser tab, with an installer that closes everything running from the app folder
before copying (the 1.4.36 install stopped at "cannot be closed").
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Updating 1.4.35 -> 1.4.36 on the TV stopped at "Bible App cannot be closed. Please
close it manually and click Retry" with the app plainly closed. That message also
comes from the installer's copy step (and from removing the old version) when any
file in the install folder is in use — not only when Bible App.exe is running.
The app runs programs from its own folder: the local AI server (llama-server.exe),
speech (whisper-cli.exe), the upscaler. electron-builder's stock check closes only
Bible App.exe, and when it has to force-close the app, the app's own shutdown (which
stops the AI server) never runs. The orphaned llama-server.exe then holds its files
open forever, so Retry can never succeed.
- build/installer.nsh: customCheckAppRunning replaces the stock check. It asks the
app to close, force-closes any Bible App.exe left, force-closes the helpers by name,
then, as a backstop, every process whose executable is inside the install folder
(via WMI, which works from the 32-bit installer on 64-bit Windows; the folder is
passed in an environment variable and a bare drive is refused). It runs before the
old version is removed and before any file is copied.
- main.cjs: the in-app updater stops the AI server before launching the installer.
The fix is in the INSTALLER, so the TV still on 1.4.35 gets it with this release.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Browser tab. Published to the Gitea package registry; 1.4.35 is kept there as the
rollback copy.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A new tab (globe, after Maps) shows one web page full-bleed in the content area —
no address bar, bookmarks or scrollbars — built for It Is Written but usable with
any page. It loads at startup and keeps running in the background.
Stage (browser-tab.js, browser-guest-preload.cjs):
- An Electron <webview> per Browser tab, in its own sandboxed process and persistent
session. The pages live in one persistent layer inside .tab-content: every tab
switch rebuilds .tab-content and moving a webview reloads it, so app.js now clears
around that layer (clearTabContentKeepingBrowser).
- Tapping a text field brings up the Prayer tab's keyboard (createPrayerKeyboard with
a sink) and types into the page with trusted input; Enter submits; the keyboard puts
itself away when the page moves focus on (e.g. after a search). The OS touch
keyboard is suppressed with inputmode="none" while a field is focused.
- Tapping a scripture reference opens it in a new Bible tab, in the translation
printed beside it when that one is switched on. References are found with the app's
own scanner: scripture-scan.js gains scanAll() (every match with its offsets),
translationAfter() and isTranslation(); scanText/parseSlide are unchanged.
- Input inside a webview never reaches the app, so the page reports activity (idle
splash) and five-finger touches (blank the screen) itself.
- main.cjs: webviewTag on; every webview is forced sandboxed, context-isolated, no Node,
http(s) only, with the guest preload. No new windows (opened in place), no permission
prompts, no downloads, no pinch-zoom, no alert/confirm/prompt boxes, and "Leave
site?" can never block Reload or Start page. The session reports plain Chrome.
- server.js CSP: frame-src allows http(s) (a webview is a frame to CSP); style-src and
font-src allow https (the follower's srcdoc copy inherits this policy).
Everywhere a tab kind is listed:
- app.js: create at startup and on Reset, icon, visibility defaults, + menu, fixed
mode, no ink layer, tool handling, OSC kind + information line (the page's search
term), phone remote scroll / tap / page buttons, OSC show / reload / home actions,
live setting changes, translation for tapped references.
- quick-dock, tool-wheel, presentation-tools: no drawing tools on the tab.
- Phone remote: tab label, one finger scrolls the page (taps still tap), hint text;
mirror-stream.js reports the page's scroll for the phone's local preview.
- Follower screen: the stage publishes a script-free copy of the page's DOM (only while
a follower is linked), its scroll and form fields; the follower shows it in
sandboxed, double-buffered frames; the relay keeps the page for late joiners.
- OSC: /bibleapp/browser, /bibleapp/browser/reload, /bibleapp/browser/home;
tab_kind/browser feedback. Admin and phone allow-lists updated.
- Settings: Tab Visibility, a Browser Tab section (start page, page size, scripture
taps, return to start page), Service Profiles carry its settings, OSC references.
- Admin console: show the tab, reload / start page, start page and page size.
- BROWSER_TAB.md documents it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Gitea returns an empty created_at for generic package files, so sorting by it
left every build equally old and the "keep one previous for rollback" rule
retained whichever the listing happened to return first — the OLDEST build,
with the newest deleted. Rank by the version in the filename instead, numeric
segment by segment so 1.4.34 outranks 1.4.9, and fall back to created_at only
when a name carries no version.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Version 1.4.35, published to the Gitea package registry and verified: the app
downloads it, checks size and sha256, and lands a byte-identical installer.
- tools/build-full-installer.cjs picked node_modules/7zip-bin/win/x64/7za.exe
unconditionally, so the one-file Full Setup could only be built on Windows.
7za only packs the payload — the SFX stub and its config are concatenated
byte-for-byte — so the host's own 7za yields the same Windows .exe. Also names
the three payload files instead of passing a "*" that each 7za build expands
its own way, and that would otherwise sweep the output archive into itself.
- BUILD-AND-DEPLOY.md / UPDATES.md rewritten for the laptop workflow and the
Gitea update source.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The dev machine is now a laptop, so the release path had to stop assuming Windows
and a reachable network share.
- tools/build-win.cjs: build the Windows x64 installer from macOS or Windows.
electron-builder fetches its own mac wine build, so the only real trap is
architecture: a plain `electron-builder --win` on Apple Silicon silently
produces an arm64 installer the stage PC cannot run. This pins --x64 and
strips the CSC_* signing vars (an EMPTY WIN_CSC_LINK aborts the build).
- tools/publish-update.cjs: an http(s) destination now uploads to a Gitea
generic package registry. Not a Gitea release: this server caps release
attachments at 100 MB and disallows .exe, while the package registry takes the
750 MB installer and serves it anonymously — which the updater requires, since
it sends no credentials. Installer first, manifest last, so a TV checking
mid-publish never sees a manifest naming a file that is not there yet.
- tools/release.cjs: bump, build and publish in one command.
- server.js: honour "check for updates automatically when the app starts". The
setting was stored but never acted on. It checks only — installing restarts
the app, which must stay a deliberate tap.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
First commit on the local Gitea server, made after moving the tree off the
Windows machine. Build outputs, node_modules and the AI models stay out
(see .gitignore); everything the installer build needs is tracked.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>