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>
214 lines
15 KiB
HTML
214 lines
15 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Bible App</title>
|
||
<link rel="stylesheet" href="styles.css?v=__BUILD__">
|
||
<!-- Removed remote Google Fonts to ensure consistent offline rendering -->
|
||
<!-- Font Awesome 6.0.0 (free) vendored locally so toolbar icons render with zero internet dependency -->
|
||
<link rel="stylesheet" href="vendor/fontawesome/css/all.min.css?v=__BUILD__">
|
||
<script>
|
||
// Apply the saved Bible reading font size before first paint (set in Settings > Bible Reading).
|
||
// Defaults to the built-in 60px when unset, so existing behavior is unchanged.
|
||
try {
|
||
var __bfs = parseInt(localStorage.getItem('bibleFontSize'), 10);
|
||
if (__bfs && !isNaN(__bfs)) {
|
||
__bfs = Math.max(40, Math.min(120, __bfs));
|
||
document.documentElement.style.setProperty('--bible-font-size', __bfs + 'px');
|
||
}
|
||
} catch (e) {}
|
||
</script>
|
||
<script>
|
||
/*
|
||
* Follower marker — this window is the BOOTH screen (?follower=1), not the stage TV.
|
||
*
|
||
* Set before every other script for the same reason as the admin marker below: remote-state.js
|
||
* and follower-publish.js decide on their very first tick whether they are the stage renderer,
|
||
* and a follower that published its own state would fight the real TV for the feed.
|
||
*
|
||
* The follower renders the identical page so live pen strokes land on the identical words. It
|
||
* is driven entirely by follower-view.js from the state the stage TV publishes.
|
||
*/
|
||
try {
|
||
if (/(^|[?&])follower=1(&|$)/.test(window.location.search)) {
|
||
window.__followerMode = true;
|
||
document.documentElement.style.background = '#000';
|
||
}
|
||
} catch (e) {}
|
||
</script>
|
||
<script>
|
||
/*
|
||
* Offstage (admin) copy marker.
|
||
*
|
||
* `?admin=1` means this window is the admin console's PRIVATE copy of the app, running in
|
||
* the admin's own browser — not the renderer on the TV. Set before any other script so
|
||
* remote-state.js sees it on its first tick and never publishes this window's state as the
|
||
* stage state.
|
||
*
|
||
* The badge is not decoration: this copy looks pixel-identical to the live stage, and an
|
||
* admin who forgets which one they are driving will make changes believing the congregation
|
||
* can see them (or worse, believing they cannot).
|
||
*/
|
||
try {
|
||
if (/(^|[?&])admin=1(&|$)/.test(window.location.search)) {
|
||
window.__adminOffstage = true;
|
||
document.addEventListener('DOMContentLoaded', function () {
|
||
try {
|
||
var b = document.createElement('div');
|
||
b.id = 'offstageBadge';
|
||
b.textContent = 'ADMIN COPY — NOT ON TV';
|
||
b.setAttribute('role', 'status');
|
||
b.style.cssText = [
|
||
'position:fixed', 'top:0', 'left:0', 'right:0', 'z-index:2147483647',
|
||
'background:#b26a00', 'color:#fff', 'font:600 12px/1.6 system-ui,sans-serif',
|
||
'letter-spacing:.08em', 'text-align:center', 'padding:3px 8px',
|
||
'pointer-events:none', 'text-transform:uppercase'
|
||
].join(';');
|
||
document.body.appendChild(b);
|
||
} catch (_) {}
|
||
});
|
||
}
|
||
} catch (e) {}
|
||
</script>
|
||
<link rel="icon" type="image/x-icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAABILAAASCwAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAA==">
|
||
</head>
|
||
<body>
|
||
<div id="app">
|
||
<!-- Upper Toolbar -->
|
||
<div class="toolbar">
|
||
<div class="toolbar-left">
|
||
<div class="browser-tabs">
|
||
<button class="reset-app-btn" id="resetAppBtn" title="Refresh app (Ctrl+R)"><i class="fas fa-rotate-right"></i></button>
|
||
<div class="tab-bar">
|
||
<!-- Tabs will be created dynamically by JavaScript -->
|
||
</div>
|
||
<button class="new-tab-btn" id="newTabBtn">+</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="toolbar-right">
|
||
<div class="tool-indicator" id="toolIndicator" aria-live="polite" title="Current tool"><i class="fas"></i></div>
|
||
<div class="tool-rail-edge" id="toolRailEdge" aria-hidden="true"></div>
|
||
<div class="tool-rail" id="toolRail">
|
||
<div class="tool-slider" id="toolSlider">
|
||
<div class="qd-rail-grip" id="railGrip" title="Drag to float the toolbar"><i class="fas fa-grip-vertical"></i></div>
|
||
<div class="tool-wrapper">
|
||
<button class="tool-icon selected" data-tool="pen" id="penToolBtn"><i class="fas fa-pen"></i></button>
|
||
<div class="pen-color-dropdown" id="penColorDropdown">
|
||
<button class="pen-color-option" style="--pen-color: #fefefe;" data-color="#fefefe" title="White"> </button>
|
||
<button class="pen-color-option" style="--pen-color: #ff3333;" data-color="#ff3333" title="Red"> </button>
|
||
<button class="pen-color-option" style="--pen-color: #0090ff;" data-color="#0090ff" title="Blue"> </button>
|
||
<button class="pen-color-option" style="--pen-color: #00ff00;" data-color="#00ff00" title="Green"> </button>
|
||
<button class="pen-color-option" style="--pen-color: #ffff00;" data-color="#ffff00" title="Yellow"> </button>
|
||
</div>
|
||
<div class="size-slider-popup" id="penSizeSliderPopup">
|
||
<div class="size-slider-wrap">
|
||
<div class="size-slider-ticks" id="penSizeTicks" aria-hidden="true"></div>
|
||
<input type="range" min="0" max="6" step="1" value="1" class="size-slider" id="penSizeSlider" aria-label="Pen size">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="tool-wrapper">
|
||
<button class="tool-icon" data-tool="highlight" id="highlightToolBtn"><i class="fas fa-highlighter"></i></button>
|
||
<div class="highlighter-color-dropdown" id="highlighterColorDropdown">
|
||
<button class="highlighter-color-option" style="--highlighter-color: #fefefe;" data-color="#fefefe" title="White"> </button>
|
||
<button class="highlighter-color-option" style="--highlighter-color: #ff3333;" data-color="#ff3333" title="Red"> </button>
|
||
<button class="highlighter-color-option" style="--highlighter-color: #0090ff;" data-color="#0090ff" title="Blue"> </button>
|
||
<button class="highlighter-color-option" style="--highlighter-color: #00ff00;" data-color="#00ff00" title="Green"> </button>
|
||
<button class="highlighter-color-option" style="--highlighter-color: #ffff00;" data-color="#ffff00" title="Yellow"> </button>
|
||
</div>
|
||
</div>
|
||
<button class="tool-icon" data-tool="erase"><i class="fas fa-eraser"></i></button>
|
||
<div class="tool-wrapper">
|
||
<button class="tool-icon" data-tool="laser" id="laserToolBtn" title="Laser pointer" style="--laser-color: #ffff00;"><i class="fas fa-circle-dot"></i></button>
|
||
<div class="laser-color-dropdown" id="laserColorDropdown">
|
||
<button class="laser-color-option" style="background: #fefefe;" data-color="#fefefe" title="White"> </button>
|
||
<button class="laser-color-option" style="background: #ff3333;" data-color="#ff3333" title="Red"> </button>
|
||
<button class="laser-color-option" style="background: #0090ff;" data-color="#0090ff" title="Blue"> </button>
|
||
<button class="laser-color-option" style="background: #00ff00;" data-color="#00ff00" title="Green"> </button>
|
||
<button class="laser-color-option selected" style="background: #ffff00;" data-color="#ffff00" title="Yellow"> </button>
|
||
</div>
|
||
</div>
|
||
<button class="tool-icon" data-tool="spotlight" id="spotlightToolBtn" title="Spotlight"><i class="fas fa-bullseye"></i></button>
|
||
<button class="tool-icon" id="touchMenuBtn" data-tool="menu" title="Menu (Settings & Content Manager)"><i class="fas fa-bars"></i></button>
|
||
</div>
|
||
<button class="toolbar-btn" id="undoBtn" tabindex="-1">
|
||
<i class="fas fa-undo"></i>
|
||
</button>
|
||
<button class="toolbar-btn" id="redoBtn" tabindex="-1">
|
||
<i class="fas fa-redo"></i>
|
||
</button>
|
||
<button class="toolbar-btn" id="resetBtn" title="Clear current tab (tap) or all annotations (hold 1.5s)" tabindex="-1">
|
||
<i class="fas fa-trash"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Tab Content Panels -->
|
||
<div class="tab-content">
|
||
<!-- Tab panels will be created dynamically by JavaScript -->
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Touch Menu Overlay -->
|
||
<div id="touchMenuOverlay" class="touch-menu-overlay hidden">
|
||
<div class="touch-menu-content">
|
||
<button class="touch-menu-close" aria-label="Close menu">
|
||
<i class="fas fa-times"></i>
|
||
</button>
|
||
<div class="touch-menu-items">
|
||
<button class="touch-menu-item" id="touchMenuSettings">
|
||
<i class="fas fa-cog"></i>
|
||
<span>Settings</span>
|
||
</button>
|
||
<button class="touch-menu-item" id="touchMenuContentManager">
|
||
<i class="fas fa-folder-open"></i>
|
||
<span>Content Manager</span>
|
||
</button>
|
||
<button class="touch-menu-item" id="touchMenuAbout">
|
||
<i class="fas fa-info-circle"></i>
|
||
<span>About</span>
|
||
</button>
|
||
<button class="touch-menu-item" id="touchMenuExit">
|
||
<i class="fas fa-sign-out-alt"></i>
|
||
<span>Exit</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Strong's dictionaries are served via /api/dict endpoints; avoid loading Node-formatted JS directly in browser -->
|
||
<!-- Removed direct script tags to prevent "module is not defined" errors in browser -->
|
||
<script type="module" src="init.js?v=__BUILD__"></script>
|
||
<script type="module" src="wordnet-init.js?v=__BUILD__"></script>
|
||
<!-- Presentation overlays (laser / spotlight) — isolated, additive -->
|
||
<script src="presentation-tools.js?v=__BUILD__"></script>
|
||
<!-- Optional draggable quick-tool dock (toggled in Settings) — isolated, additive -->
|
||
<script src="quick-dock.js?v=__BUILD__"></script>
|
||
<!-- Two-finger tap → radial tool selector under the fingers (Settings › Presentation) — isolated, additive -->
|
||
<script src="tool-wheel.js?v=__BUILD__"></script>
|
||
<!-- Red-letter (words of Christ) — isolated, additive -->
|
||
<script src="red-letter.js?v=__BUILD__"></script>
|
||
<!-- Watchdog: re-render a Bible tab that came up blank — isolated, additive -->
|
||
<script src="text-watchdog.js?v=__BUILD__"></script>
|
||
<!-- Draw on the word-definition popup cards — isolated, additive -->
|
||
<script src="popup-draw.js?v=__BUILD__"></script>
|
||
<!-- Live UI-state feed for the phone remote — isolated, additive -->
|
||
<script src="remote-state.js?v=__BUILD__"></script>
|
||
<!-- Fast frame source for the phone remote's mirror (renderer self-capture) — isolated, additive -->
|
||
<script src="mirror-stream.js?v=__BUILD__"></script>
|
||
<!-- An X may only close: stops a closing overlay's tap falling through to what was behind it -->
|
||
<script src="overlay-close-guard.js?v=__BUILD__"></script>
|
||
<!-- Book/abbreviation reference resolver, shared with the phone remote -->
|
||
<script src="book-aliases.js?v=__BUILD__"></script>
|
||
<!-- Follower link: the stage TV publishes its state so the booth screen can re-render this exact
|
||
picture for ProPresenter / the ATEM without a video cable — isolated, additive -->
|
||
<script src="follower-publish.js?v=__BUILD__"></script>
|
||
<script src="follower-view.js?v=__BUILD__"></script>
|
||
<!-- ProPresenter Sync tab: slide-reference scanner + the live link client — isolated, additive -->
|
||
<script src="scripture-scan.js?v=__BUILD__"></script>
|
||
<script src="propresenter-sync.js?v=__BUILD__"></script>
|
||
</body>
|
||
</html> |