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:
+100
-2
@@ -829,7 +829,7 @@
|
||||
<div style="color:#777; margin-bottom:14px; font-size:13px; line-height:1.9;">
|
||||
<code style="color:#8cc88c;">/bibleapp/feedback/state/tab_active/<n></code> — <code>1</code> while the <strong>n-th tab on the row</strong> is showing (1–16).<br>
|
||||
<code style="color:#8cc88c;">/bibleapp/feedback/state/tab_kind/<kind></code> — <code>1</code> while a tab of that <strong>kind</strong> is showing:<br>
|
||||
<span style="margin-left:18px;">bible · sync · whiteboard · notes · media · alpha · announcements · graphics · maps · prayer · ask</span>
|
||||
<span style="margin-left:18px;">bible · sync · whiteboard · notes · media · alpha · announcements · graphics · maps · browser · prayer · ask</span>
|
||||
</div>
|
||||
<label style="display:flex; align-items:center; gap:8px; margin:0;">
|
||||
<input type="checkbox" id="oscFeedbackTabStates"> Send per-tab on/off states for Companion button colours
|
||||
@@ -943,6 +943,57 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Browser Tab Settings -->
|
||||
<div class="section">
|
||||
<h2>Browser Tab</h2>
|
||||
<p style="color: #999; margin-bottom: 18px;">
|
||||
The Browser tab shows one web page, full screen in the tab: no address bar, bookmarks or scrollbars.
|
||||
It loads when the app starts and keeps running while other tabs are showing, so it is ready the moment
|
||||
you open it. Scroll it with a finger; tap a text box on the page and the app's own keyboard comes up to
|
||||
type into it; tap a scripture reference on the page to open it in a new Bible tab. The booth follower
|
||||
screen shows the same page, clean.
|
||||
</p>
|
||||
<div class="form-group">
|
||||
<label for="browserTabUrl">Start page</label>
|
||||
<input type="url" id="browserTabUrl" placeholder="https://it-is-written-waters-church.vercel.app/" onchange="saveBrowserTabSettings()"
|
||||
style="width: 100%; max-width: 560px; padding: 10px; background: #1f1f1f; color: #e0e0e0; border: 1px solid #444; border-radius: 6px;">
|
||||
<div class="help-text">The page the tab opens. Leave blank for It Is Written. Changing it takes the tab to the new page.</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="browserTabZoom">Page size</label>
|
||||
<select id="browserTabZoom" onchange="saveBrowserTabSettings()"
|
||||
style="width: 100%; max-width: 360px; padding: 10px; background: #1f1f1f; color: #e0e0e0; border: 1px solid #444; border-radius: 6px;">
|
||||
<option value="auto">Auto — fit the page to the screen</option>
|
||||
<option value="75">75%</option>
|
||||
<option value="100">100%</option>
|
||||
<option value="125">125%</option>
|
||||
<option value="150">150%</option>
|
||||
<option value="175">175%</option>
|
||||
<option value="200">200%</option>
|
||||
<option value="250">250%</option>
|
||||
<option value="300">300%</option>
|
||||
</select>
|
||||
<div class="help-text">Auto lays the page out like a tablet and scales it to fill the width — on the portrait TV that is
|
||||
about 200%. Pick a size if a site looks too small or too big.</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label style="display:flex; align-items:center; gap:8px;">
|
||||
<input type="checkbox" id="browserTabScriptureTaps" onchange="saveBrowserTabSettings()"> Tap a scripture reference on the page to open it in a new Bible tab
|
||||
</label>
|
||||
<div class="help-text">Opens in the translation printed beside the reference (e.g. NIV) when that translation is switched on
|
||||
here, otherwise in the default. Taps anywhere else work on the page as normal.</div>
|
||||
</div>
|
||||
<div style="display:flex; gap:10px; flex-wrap:wrap; align-items:center;">
|
||||
<span style="color:#8cc88c; font-size:13px;">✓ Saves automatically</span>
|
||||
<button type="button" class="btn btn-secondary" onclick="browserTabBackToStart()">Return to the start page</button>
|
||||
</div>
|
||||
<div class="help-text" style="margin-top:8px;">
|
||||
The tab remembers where it was (a search, how far down) while the app is running, so a trip to Settings does
|
||||
not lose it. <strong>Return to the start page</strong> clears that, as does the refresh button on the TV.
|
||||
From the booth: OSC <code>/bibleapp/browser/home</code> and <code>/bibleapp/browser/reload</code>.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Ask Tab (AI) Settings -->
|
||||
<div class="section">
|
||||
<h2>Ask Tab (AI Assistant)</h2>
|
||||
@@ -1002,6 +1053,14 @@
|
||||
<div><strong>/bibleapp/close</strong> - Close active tab</div>
|
||||
<div><strong>/bibleapp/tab/left</strong> - Switch to previous tab</div>
|
||||
<div><strong>/bibleapp/tab/right</strong> - Switch to next tab</div>
|
||||
<div><strong>/bibleapp/maps</strong> - Show the Maps & Timelines tab</div>
|
||||
<div><strong>/bibleapp/browser</strong> - Show the Browser tab</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 10px;"><strong style="color: #4a9eff;">Browser tab:</strong></div>
|
||||
<div style="margin-left: 10px; margin-bottom: 10px;">
|
||||
<div><strong>/bibleapp/browser/reload</strong> - Reload the page (if it hung or looks wrong)</div>
|
||||
<div><strong>/bibleapp/browser/home</strong> - Take the page back to its start page (Settings › Browser Tab)</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 10px;"><strong style="color: #4a9eff;">Tools:</strong></div>
|
||||
@@ -2027,6 +2086,7 @@
|
||||
{ mode: 'announcements', label: 'Announcements Tab', description: 'Image-based announcements display' },
|
||||
{ mode: 'graphics', label: 'Graphics Tab', description: 'Image-based graphics display' },
|
||||
{ mode: 'maps', label: 'Maps & Timelines Tab', description: 'Image gallery for maps and timelines' },
|
||||
{ mode: 'browser', label: 'Browser Tab', description: 'A live web page, full screen in the tab with no browser bars (set it up under Browser Tab below)' },
|
||||
{ mode: 'whiteboard', label: 'Whiteboard Tab', description: 'Blank whiteboard for drawing' },
|
||||
{ mode: 'notes', label: 'Notes Tab', description: 'PDF notes with annotations' },
|
||||
{ mode: 'prayer', label: 'Prayer Tab', description: 'Text editor for prayers and notes' },
|
||||
@@ -2791,6 +2851,43 @@
|
||||
}).join('<br>');
|
||||
}
|
||||
try { if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', loadSplashTriggers); else loadSplashTriggers(); } catch (_) {}
|
||||
|
||||
// ---- Browser Tab (browser-tab.js reads these from the same-origin localStorage) ----
|
||||
const BROWSER_TAB_DEFAULT_URL = 'https://it-is-written-waters-church.vercel.app/';
|
||||
function loadBrowserTabSettings() {
|
||||
try {
|
||||
document.getElementById('browserTabUrl').value = localStorage.getItem('browserTabUrl') || '';
|
||||
const z = localStorage.getItem('browserTabZoom') || 'auto';
|
||||
const sel = document.getElementById('browserTabZoom');
|
||||
sel.value = Array.from(sel.options).some(o => o.value === z) ? z : 'auto';
|
||||
document.getElementById('browserTabScriptureTaps').checked = localStorage.getItem('browserTabScriptureTaps') !== '0';
|
||||
} catch (_) {}
|
||||
}
|
||||
function saveBrowserTabSettings() {
|
||||
try {
|
||||
const urlEl = document.getElementById('browserTabUrl');
|
||||
let url = String(urlEl.value || '').trim();
|
||||
// "example.org/page" is what people type; the tab needs a full web address.
|
||||
if (url && !/^https?:\/\//i.test(url)) url = 'https://' + url.replace(/^\/+/, '');
|
||||
if (url) {
|
||||
try { new URL(url); } catch (_) { showStatus('That start page is not a web address.', 'error'); return; }
|
||||
}
|
||||
const before = localStorage.getItem('browserTabUrl') || '';
|
||||
if (url) localStorage.setItem('browserTabUrl', url); else localStorage.removeItem('browserTabUrl');
|
||||
urlEl.value = url;
|
||||
// A new start page means the tab should go there, not back to where it was on the old one.
|
||||
if (url !== before) { try { sessionStorage.removeItem('browserTabSession'); } catch (_) {} }
|
||||
localStorage.setItem('browserTabZoom', document.getElementById('browserTabZoom').value || 'auto');
|
||||
localStorage.setItem('browserTabScriptureTaps', document.getElementById('browserTabScriptureTaps').checked ? '1' : '0');
|
||||
showStatus('Browser tab saved — ' + (url || BROWSER_TAB_DEFAULT_URL), 'success');
|
||||
} catch (e) { showStatus('Could not save Browser tab settings: ' + e.message, 'error'); }
|
||||
}
|
||||
// Settings replaces the app in this same window, so the session the tab keeps is visible from here.
|
||||
function browserTabBackToStart() {
|
||||
try { sessionStorage.removeItem('browserTabSession'); } catch (_) {}
|
||||
showStatus('The Browser tab will open its start page when you go back to the app.', 'success');
|
||||
}
|
||||
try { if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', loadBrowserTabSettings); else loadBrowserTabSettings(); } catch (_) {}
|
||||
window.addEventListener('storage', (ev) => { try { if (ev && ev.key === 'splashHistory') renderSplashHistory(); } catch (_) {} });
|
||||
|
||||
// ---- Follower screen (booth mirror → ProPresenter / ATEM) ----
|
||||
@@ -3058,7 +3155,8 @@
|
||||
'redLetterEnabled', 'redLetterOverrides', 'popupVisibility',
|
||||
'translationVisibility', 'wordPopupsEnabled', 'spotlightHalfPx',
|
||||
'userLaserColor', 'toolWheelEnabled', 'toolWheelStyle', 'toolSnapBackSec',
|
||||
'idleSplashSeconds', 'fiveFingerSplash', 'ppsyncSeparateAnnotations'].forEach(function (k) {
|
||||
'idleSplashSeconds', 'fiveFingerSplash', 'ppsyncSeparateAnnotations',
|
||||
'browserTabUrl', 'browserTabZoom', 'browserTabScriptureTaps'].forEach(function (k) {
|
||||
var v = localStorage.getItem(k);
|
||||
if (v !== null) settings[k] = v;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user