This commit is contained in:
parent
ab10516383
commit
5b0fc1cb65
10
CHANGELOG.md
10
CHANGELOG.md
@ -9,6 +9,13 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi
|
||||
|
||||
---
|
||||
|
||||
## [1.50.0] — 2026-09-22
|
||||
|
||||
### Added
|
||||
- **Magazine: an "All pages" view.** Articles are a poor unit for "have I seen everything" — one can span many pages, several can share one page, and neither the list nor thumbnail view (both article-grained) can show either case. This adds a third view, cycled by the same toggle button, showing every page 1..N with its own thumbnail and whichever article(s) claim it (a page shared by two pieces shows both; a page claimed by none is flagged and links straight into Review there via the page-coverage jump added earlier). Articles remain the unit for narration, Approve, and audio export, which are inherently article-scoped — this is a complete-picture view alongside them, not a replacement.
|
||||
|
||||
---
|
||||
|
||||
## [1.49.0] — 2026-09-22
|
||||
|
||||
### Added
|
||||
@ -4386,7 +4393,8 @@ Initial feature-complete release.
|
||||
|
||||
---
|
||||
|
||||
[Unreleased]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.49.0...HEAD
|
||||
[Unreleased]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.50.0...HEAD
|
||||
[1.50.0]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.49.0...v1.50.0
|
||||
[1.49.0]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.48.2...v1.49.0
|
||||
[1.48.2]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.48.1...v1.48.2
|
||||
[1.48.1]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.48.0...v1.48.1
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<meta name="theme-color" content="#2563EB">
|
||||
<meta name="app-version" content="1.49.0">
|
||||
<meta name="app-version" content="1.50.0">
|
||||
<link rel="manifest" href="/manifest.webmanifest">
|
||||
<link rel="icon" href="/static/icon.svg" type="image/svg+xml">
|
||||
<link rel="apple-touch-icon" href="/static/icon.svg">
|
||||
@ -27,12 +27,12 @@
|
||||
|
||||
<!-- ── Core styles (local — no CDN dependency for first paint) ────────── -->
|
||||
<link rel="stylesheet" href="/static/vendor/mdi/materialdesignicons.min.css">
|
||||
<link rel="stylesheet" href="/static/style.css?v=1.49.0">
|
||||
<link rel="stylesheet" href="/static/css/resource-diagnostics.css?v=1.49.0">
|
||||
<link rel="stylesheet" href="/static/css/background-jobs.css?v=1.49.0">
|
||||
<link rel="stylesheet" href="/static/css/voice-design.css?v=1.49.0">
|
||||
<link rel="stylesheet" href="/static/css/rehearser.css?v=1.49.0">
|
||||
<link rel="stylesheet" href="/static/css/audiobook.css?v=1.49.0">
|
||||
<link rel="stylesheet" href="/static/style.css?v=1.50.0">
|
||||
<link rel="stylesheet" href="/static/css/resource-diagnostics.css?v=1.50.0">
|
||||
<link rel="stylesheet" href="/static/css/background-jobs.css?v=1.50.0">
|
||||
<link rel="stylesheet" href="/static/css/voice-design.css?v=1.50.0">
|
||||
<link rel="stylesheet" href="/static/css/rehearser.css?v=1.50.0">
|
||||
<link rel="stylesheet" href="/static/css/audiobook.css?v=1.50.0">
|
||||
|
||||
|
||||
<!-- ── Flag icons — non-blocking (loaded async, icons appear after JS) ── -->
|
||||
@ -403,7 +403,7 @@ window.toggleNavTree = function(treeId, chevronId) {
|
||||
</script>
|
||||
|
||||
<!-- loader.js: fetches sections → loads JS modules → removes skeleton -->
|
||||
<script src="/static/loader.js?v=1.49.0"></script>
|
||||
<script src="/static/loader.js?v=1.50.0"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -695,10 +695,18 @@ function renderContents() {
|
||||
const grid = state.contentsView === 'grid';
|
||||
const toggleBtn = $('mag-contents-view-toggle');
|
||||
if (toggleBtn) {
|
||||
toggleBtn.innerHTML = grid
|
||||
? '<span class="mdi mdi-view-list-outline"></span> List'
|
||||
: '<span class="mdi mdi-view-grid-outline"></span> Thumbnails';
|
||||
toggleBtn.setAttribute('aria-pressed', String(grid));
|
||||
const nextLabel = { list: 'Thumbnails', grid: 'All pages', pages: 'List' }[state.contentsView];
|
||||
const nextIcon = { list: 'mdi-view-grid-outline', grid: 'mdi-view-module-outline', pages: 'mdi-view-list-outline' }[state.contentsView];
|
||||
toggleBtn.innerHTML = '<span class="mdi ' + nextIcon + '"></span> ' + nextLabel;
|
||||
toggleBtn.setAttribute('aria-pressed', String(state.contentsView !== 'list'));
|
||||
toggleBtn.title = 'Currently: ' + { list: 'list', grid: 'thumbnails (by article)', pages: 'all pages' }[state.contentsView] +
|
||||
'. Click to switch to ' + nextLabel.toLowerCase() + '.';
|
||||
}
|
||||
if (state.contentsView === 'pages') {
|
||||
renderPagesView(host);
|
||||
renderContentsBulkBar();
|
||||
magUpdateAudioZipLink();
|
||||
return;
|
||||
}
|
||||
|
||||
const actionsHtml = (a, done, progress) =>
|
||||
@ -774,8 +782,46 @@ function renderContents() {
|
||||
magUpdateAudioZipLink();
|
||||
}
|
||||
|
||||
const MAG_CONTENTS_VIEW_CYCLE = ['list', 'grid', 'pages'];
|
||||
|
||||
// Every page, 1..pageCount, regardless of whether an article claims it --
|
||||
// the article grid/list above can only ever show what detection already
|
||||
// grouped, which is exactly what left 36% of one real issue invisible (see
|
||||
// magUncoveredPageRanges). This is the complete picture: a page can belong
|
||||
// to zero articles (never reviewed), one, or several (a page shared by two
|
||||
// short pieces), and this shows all three cases rather than picking one.
|
||||
function renderPagesView(host) {
|
||||
const pageCount = state.doc?.pageCount || 0;
|
||||
if (!pageCount) { host.innerHTML = '<p class="muted">Page count unknown yet.</p>'; return; }
|
||||
const byPage = new Map();
|
||||
state.articles.forEach((a) => (a.pages || []).forEach((p) => {
|
||||
if (!byPage.has(p)) byPage.set(p, []);
|
||||
byPage.get(p).push(a);
|
||||
}));
|
||||
let html = '<div class="mag-grid mag-pages-grid">';
|
||||
for (let p = 1; p <= pageCount; p++) {
|
||||
const arts = byPage.get(p) || [];
|
||||
html += '<div class="mag-card-wrap"><div class="mag-card' + (arts.length ? '' : ' is-uncovered') + '">' +
|
||||
'<img class="mag-card-thumb" loading="lazy" alt="Page ' + p + '" src="/api/magazine/' + esc(state.doc.id) + '/pages/' + p + '">' +
|
||||
'<span class="mag-card-title">Page ' + p + '</span>' +
|
||||
(arts.length
|
||||
? '<span class="mag-card-sub">' + esc(arts[0].title || 'Untitled') +
|
||||
(arts.length > 1 ? ' <span class="muted">+' + (arts.length - 1) + ' more</span>' : '') + '</span>' +
|
||||
'<div class="mag-toc-actions">' +
|
||||
arts.map((a) => '<button type="button" class="btn-sm" onclick="magReview(\'' + a.id + '\')" title="' +
|
||||
esc(a.title || 'Untitled') + '">' + (arts.length > 1 ? esc((a.title || '?').slice(0, 14)) : 'Review') + '</button>').join('') +
|
||||
'</div>'
|
||||
: '<span class="mag-pill">no article</span>' +
|
||||
'<div class="mag-toc-actions"><button type="button" class="btn-sm" onclick="magReviewPage(' + p + ')">Review this page</button></div>') +
|
||||
'</div></div>';
|
||||
}
|
||||
html += '</div>';
|
||||
host.innerHTML = html;
|
||||
}
|
||||
|
||||
function magToggleContentsView() {
|
||||
state.contentsView = state.contentsView === 'grid' ? 'list' : 'grid';
|
||||
const i = MAG_CONTENTS_VIEW_CYCLE.indexOf(state.contentsView);
|
||||
state.contentsView = MAG_CONTENTS_VIEW_CYCLE[(i + 1) % MAG_CONTENTS_VIEW_CYCLE.length];
|
||||
try { localStorage.setItem(MAG_CONTENTS_VIEW_KEY, state.contentsView); } catch (_) {}
|
||||
renderContents();
|
||||
}
|
||||
@ -3471,7 +3517,7 @@ async function magInit() {
|
||||
}
|
||||
try {
|
||||
const savedView = localStorage.getItem(MAG_CONTENTS_VIEW_KEY);
|
||||
if (savedView === 'grid' || savedView === 'list') state.contentsView = savedView;
|
||||
if (MAG_CONTENTS_VIEW_CYCLE.includes(savedView)) state.contentsView = savedView;
|
||||
} catch (_) {}
|
||||
if (!$('mag-library-list')) return; // section not mounted
|
||||
magLoadLibrary();
|
||||
|
||||
@ -4935,6 +4935,10 @@ input[list="lang-codes"] {
|
||||
background: color-mix(in srgb, var(--yellow, #d97706) 10%, var(--surface)); }
|
||||
.mag-coverage-banner .mdi { color: var(--yellow, #d97706); flex-shrink: 0; margin-top: 2px; }
|
||||
.mag-coverage-ranges { display: flex; flex-wrap: wrap; gap: 6px; }
|
||||
.mag-pages-grid { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }
|
||||
.mag-pages-grid .mag-card-thumb { height: 120px; }
|
||||
.mag-card.is-uncovered { border-color: var(--yellow, #d97706);
|
||||
background: color-mix(in srgb, var(--yellow, #d97706) 6%, var(--surface)); }
|
||||
|
||||
.mag-audio-export { display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
|
||||
padding: 10px 14px; margin-bottom: 12px; border: 1px solid var(--border); border-radius: var(--radius);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user