178 lines
9.9 KiB
HTML
178 lines
9.9 KiB
HTML
<div class="section-head">
|
|
<span class="section-icon"><span class="mdi mdi-newspaper-variant-outline"></span></span>
|
|
<div class="section-title">
|
|
<h2>Magazines</h2>
|
|
<p>Listen to a magazine one article at a time — from its true beginning to its true end, without adverts, sidebars or a neighbouring piece spliced into the middle.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ── Library ──────────────────────────────────────────────────────── -->
|
|
<div id="mag-view-library">
|
|
<div class="card mag-new">
|
|
<h3>Add a magazine or paper</h3>
|
|
<div class="mag-new-grid">
|
|
<label for="mag-new-title">Title <span class="muted">(optional)</span>
|
|
<input id="mag-new-title" type="text" placeholder="Auto-filled from PDF filename if blank" autocomplete="off">
|
|
</label>
|
|
<label for="mag-new-issue">Issue <span class="muted">(optional)</span>
|
|
<input id="mag-new-issue" type="text" placeholder="September 2026" autocomplete="off">
|
|
</label>
|
|
</div>
|
|
<div class="reader-dropzone" id="mag-dropzone" style="cursor: pointer;">
|
|
<span class="mdi mdi-cloud-upload-outline reader-dropzone-icon" id="mag-dropzone-icon"></span>
|
|
<strong id="mag-dropzone-title">Drop a PDF or click to browse</strong>
|
|
<span class="note" id="mag-dropzone-note">Scanned issues work too — pages without selectable text are read with OCR.</span>
|
|
<label class="btn-secondary btn-sm reader-file-pick" title="Choose a PDF file" onclick="event.stopPropagation()">
|
|
<span class="mdi mdi-file-upload-outline"></span> Choose file
|
|
<input type="file" id="mag-new-file" accept="application/pdf" style="display:none">
|
|
</label>
|
|
</div>
|
|
<div class="mag-new-actions">
|
|
<button type="button" class="btn primary" id="mag-add-btn" onclick="magCreate()">
|
|
<span class="mdi mdi-plus"></span> Add magazine
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<h3>Your magazines</h3>
|
|
<div id="mag-library-list" class="mag-grid"></div>
|
|
</div>
|
|
|
|
<!-- ── Contents ─────────────────────────────────────────────────────── -->
|
|
<div id="mag-view-contents" hidden>
|
|
<div class="mag-bar">
|
|
<button type="button" class="btn-sm" onclick="magBackToLibrary()">
|
|
<span class="mdi mdi-arrow-left"></span> All magazines
|
|
</button>
|
|
<h3 id="mag-doc-title">Magazine</h3>
|
|
<div class="mag-bar-actions">
|
|
<button type="button" class="btn-sm primary" onclick="magPlayAll()">
|
|
<span class="mdi mdi-playlist-play"></span> Play all articles
|
|
</button>
|
|
<button type="button" class="btn-sm" onclick="magIngest()">
|
|
<span class="mdi mdi-refresh"></span> Re-run detection
|
|
</button>
|
|
<button type="button" class="btn-sm" onclick="magReocrMagazine()" title="Send every page through the configured OCR engine again, then re-detect articles">
|
|
<span class="mdi mdi-text-recognition"></span> OCR again (whole magazine)
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div id="mag-ingest-status" class="mag-status" hidden></div>
|
|
<div id="mag-ingest-preview" class="mag-ingest-preview" hidden>
|
|
<div class="mag-ingest-preview-side">
|
|
<span class="mag-ingest-preview-label">Page</span>
|
|
<img id="mag-ingest-preview-img" alt="Page currently being read">
|
|
</div>
|
|
<div class="mag-ingest-preview-resize" id="mag-ingest-preview-resize" role="separator" aria-orientation="vertical" aria-label="Resize page and recognised text panes" title="Drag to resize page and recognised text panes"></div>
|
|
<div class="mag-ingest-preview-side">
|
|
<span class="mag-ingest-preview-label">Recognised text</span>
|
|
<div id="mag-ingest-preview-text" class="mag-ingest-preview-text"></div>
|
|
</div>
|
|
</div>
|
|
<div id="mag-contents"></div>
|
|
</div>
|
|
|
|
<!-- ── Review (Stage D) ─────────────────────────────────────────────── -->
|
|
<div id="mag-view-review" hidden>
|
|
<div class="mag-bar">
|
|
<button type="button" class="btn-sm" onclick="magBackToContents()">
|
|
<span class="mdi mdi-arrow-left"></span> Contents
|
|
</button>
|
|
<h3>Review article boundaries</h3>
|
|
</div>
|
|
<div id="mag-review"></div>
|
|
</div>
|
|
|
|
<!-- ── Reading ──────────────────────────────────────────────────────── -->
|
|
<div id="mag-view-read" hidden>
|
|
<div class="mag-bar">
|
|
<button type="button" class="btn-sm" onclick="magBackToContents()">
|
|
<span class="mdi mdi-arrow-left"></span> Contents
|
|
</button>
|
|
<div class="mag-read-id">
|
|
<h3 id="mag-read-title">Article</h3>
|
|
<span id="mag-read-by" class="muted"></span>
|
|
<span id="mag-read-pages" class="muted"></span>
|
|
</div>
|
|
<button type="button" class="btn-sm" onclick="magToggleDriving()" title="Large controls for listening without looking at the screen">
|
|
<span class="mdi mdi-car-outline"></span> Driving mode
|
|
</button>
|
|
</div>
|
|
|
|
<div class="mag-controls">
|
|
<label for="mag-voice" class="mag-voice-pick">Narrator
|
|
<select id="mag-voice"></select>
|
|
</label>
|
|
<button type="button" class="btn primary" id="mag-play" onclick="magPlay()"
|
|
aria-label="Play narration">
|
|
<span class="mdi mdi-play"></span> Listen
|
|
</button>
|
|
<span id="mag-read-progress" class="muted" aria-live="polite"></span>
|
|
<span id="mag-queue-progress" class="muted" aria-live="polite"></span>
|
|
<label class="mag-karaoke-toggle" title="Highlight each word approximately as it is spoken">
|
|
<input type="checkbox" id="mag-karaoke-cb" checked onchange="TTVC.magazine.karaoke = this.checked">
|
|
Word highlighting
|
|
</label>
|
|
<div class="mag-pane-tabs" role="group" aria-label="Reading view">
|
|
<button type="button" class="btn-sm active" data-mag-pane="reflow"
|
|
aria-pressed="true" onclick="magSetPane('reflow')">Article</button>
|
|
<button type="button" class="btn-sm" data-mag-pane="pdf"
|
|
aria-pressed="false" onclick="magSetPane('pdf')">Original page</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="mag-pane-reflow" class="mag-pane">
|
|
<div id="mag-read-image" class="mag-read-image" hidden></div>
|
|
<div id="mag-article" class="mag-article"></div>
|
|
</div>
|
|
|
|
<div id="mag-pane-pdf" class="mag-pane" hidden>
|
|
<div class="mag-page-wrap">
|
|
<img id="mag-page-img" alt="">
|
|
<div id="mag-page-box" class="mag-page-box" hidden></div>
|
|
</div>
|
|
<p id="mag-page-label" class="muted"></p>
|
|
</div>
|
|
|
|
<!-- Podcast mode -->
|
|
<details class="card mag-podcast">
|
|
<summary><span class="mdi mdi-podcast"></span> Podcast mode — discuss this article</summary>
|
|
<p class="muted">Two or three hosts discuss the article for someone who has not read it. The script is written by the configured LLM from the article text; it will not invent facts, but it is a generated discussion, not the author’s words.</p>
|
|
<div class="mag-hosts">
|
|
<label for="mag-host-a">Host A<select id="mag-host-a"></select></label>
|
|
<label for="mag-host-b">Host B<select id="mag-host-b"></select></label>
|
|
<label for="mag-host-c">Host C <span class="muted">(optional)</span><select id="mag-host-c"></select></label>
|
|
</div>
|
|
<div class="mag-podcast-actions">
|
|
<button type="button" class="btn" id="mag-podcast-gen" onclick="magGeneratePodcast()">Generate script</button>
|
|
<button type="button" class="btn primary" id="mag-podcast-play" onclick="magPlayPodcast()" hidden>Play podcast</button>
|
|
</div>
|
|
<div id="mag-podcast-script"></div>
|
|
</details>
|
|
</div>
|
|
|
|
<!-- ── Driving mode overlay ─────────────────────────────────────────── -->
|
|
<!-- Large controls, minimal text, no interaction required beyond play/
|
|
pause/skip -- reuses whatever is already playing rather than its
|
|
own playback path. -->
|
|
<div id="mag-driving" class="mag-driving" hidden role="dialog" aria-label="Driving mode">
|
|
<button type="button" class="mag-driving-exit" onclick="magToggleDriving()" aria-label="Exit driving mode">
|
|
<span class="mdi mdi-close"></span>
|
|
</button>
|
|
<div class="mag-driving-body">
|
|
<h2 id="mag-driving-title">Article</h2>
|
|
<p id="mag-driving-by" class="muted"></p>
|
|
<div id="mag-driving-queue" class="mag-driving-queue" hidden>
|
|
<span id="mag-queue-progress-2" class="muted"></span>
|
|
</div>
|
|
<div class="mag-driving-controls">
|
|
<button type="button" class="mag-driving-btn" onclick="magQueueNext()" aria-label="Next article" title="Next article (only in Play all)">
|
|
<span class="mdi mdi-skip-next"></span>
|
|
</button>
|
|
<button type="button" class="mag-driving-btn mag-driving-play" id="mag-driving-play" onclick="magPlay()" aria-label="Play">
|
|
<span class="mdi mdi-play"></span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|