Add the Browser tab: a live web page with no browser chrome
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>
This commit is contained in:
@@ -82,8 +82,20 @@ export function attachFollowerLink(server, opts = {}) {
|
||||
if (msg && msg.passageKey && !Array.isArray(msg.strokes) && lastStrokes.has(msg.passageKey)) {
|
||||
msg.strokes = lastStrokes.get(msg.passageKey);
|
||||
}
|
||||
// The Browser tab's page (tens of KB of HTML) rides along only when it changed, exactly like a
|
||||
// stroke list — so the retained copy gets it back here, or a screen joining mid-service would show
|
||||
// the Browser tab blank until the page next changed.
|
||||
if (msg && msg.browser && typeof msg.browser.html !== 'string' && lastBrowser && lastBrowser.ver === msg.browser.domVer) {
|
||||
msg.browser.html = lastBrowser.html;
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
let lastBrowser = null; // { ver, html } — the newest Browser-tab page the stage published
|
||||
function rememberBrowser(msg) {
|
||||
if (msg && msg.browser && typeof msg.browser.html === 'string' && msg.browser.domVer) {
|
||||
lastBrowser = { ver: msg.browser.domVer, html: msg.browser.html };
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Only the machine this server runs on may publish. The stage renderer connects to its own server
|
||||
@@ -141,6 +153,7 @@ export function attachFollowerLink(server, opts = {}) {
|
||||
// A new stage session: nothing the previous one published (its ink, its passage) may be handed to
|
||||
// a screen that joins now. The new publisher's first snapshot arrives within a tick.
|
||||
lastStrokes.clear();
|
||||
lastBrowser = null;
|
||||
snapshot = null;
|
||||
log(`${marked ? 'stage renderer' : 'local publisher (unmarked)'} connected`);
|
||||
} else {
|
||||
@@ -177,6 +190,7 @@ export function attachFollowerLink(server, opts = {}) {
|
||||
if (msg && msg.type === 'snapshot') {
|
||||
msg.seq = ++seq;
|
||||
remember(msg);
|
||||
rememberBrowser(msg);
|
||||
// Live subscribers already hold the strokes; only the RETAINED copy is completed.
|
||||
fanOut(JSON.stringify(msg));
|
||||
snapshot = JSON.stringify(complete(msg));
|
||||
|
||||
Reference in New Issue
Block a user