From ab1051638395a80c7d1d6612c5bbae77873f710f Mon Sep 17 00:00:00 2001 From: mARTin-B78 Date: Tue, 22 Sep 2026 23:14:27 +0200 Subject: [PATCH] Release v1.49.0 --- CHANGELOG.md | 10 +++++- VERSION | 2 +- static/index.html | 16 +++++----- static/js/magazine.js | 55 +++++++++++++++++++++++++++++++++ static/sections/s-magazine.html | 1 + static/style.css | 6 ++++ 6 files changed, 80 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8714754..c85fda4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,13 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi --- +## [1.49.0] — 2026-09-22 + +### Added +- **Magazine: a page-coverage gap finder.** Approve only ever advances to the next detected article, so a page never grouped into any article at all — an ad, a missed piece, anything detection skipped entirely — was never surfaced by that workflow. Confirmed live: 73 of 204 pages (36%) in one real issue belonged to no article whatsoever. The article list now shows a banner listing every such gap as page ranges, each a one-click jump straight into Review at that page (the review UI's own page navigation already isn't restricted to one article's pages — the missing piece was purely that nothing pointed there). + +--- + ## [1.48.2] — 2026-09-22 ### Changed @@ -4379,7 +4386,8 @@ Initial feature-complete release. --- -[Unreleased]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.48.2...HEAD +[Unreleased]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.49.0...HEAD +[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 [1.48.0]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.47.1...v1.48.0 diff --git a/VERSION b/VERSION index 4b12da2..7f3a46a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.48.2 +1.49.0 diff --git a/static/index.html b/static/index.html index 5e4d5be..4adb799 100644 --- a/static/index.html +++ b/static/index.html @@ -10,7 +10,7 @@ - + @@ -27,12 +27,12 @@ - - - - - - + + + + + + @@ -403,7 +403,7 @@ window.toggleNavTree = function(treeId, chevronId) { - + diff --git a/static/js/magazine.js b/static/js/magazine.js index 254e22e..d601c47 100644 --- a/static/js/magazine.js +++ b/static/js/magazine.js @@ -619,6 +619,59 @@ function confidenceLabel(c) { return { cls: 'bad', text: Math.round(c * 100) + '% · little body copy — likely not an article', badge: Math.round(c * 100) + '%' }; } +// ── Page coverage ──────────────────────────────────────────────────────── +// "Approve" only ever advances to the next ARTICLE, so a page that never +// got grouped into any article -- an ad, a missed piece, a page the +// detection pass just skipped -- is never surfaced by that workflow at all. +// Confirmed live: 73 of 204 pages (36%) in one real issue belonged to no +// article whatsoever. This finds those gaps and gives a one-click way into +// Review at an arbitrary page, which the review UI already supports +// (magReviewGoMagazinePage is not bounded to the anchor article's own +// pages) -- the missing piece was purely that nothing pointed there. +function magUncoveredPageRanges() { + const total = state.doc?.pageCount || 0; + if (!total) return []; + const covered = new Set(); + state.articles.forEach((a) => (a.pages || []).forEach((p) => covered.add(p))); + const gaps = []; + let start = null; + for (let p = 1; p <= total; p++) { + if (!covered.has(p)) { if (start === null) start = p; } + else if (start !== null) { gaps.push([start, p - 1]); start = null; } + } + if (start !== null) gaps.push([start, total]); + return gaps; +} + +function renderCoverageBanner() { + const el = $('mag-coverage-banner'); + if (!el) return; + const gaps = magUncoveredPageRanges(); + if (!gaps.length) { el.hidden = true; el.innerHTML = ''; return; } + const pageCount = gaps.reduce((n, [a, b]) => n + (b - a + 1), 0); + el.hidden = false; + el.innerHTML = + ' ' + + '' + pageCount + ' page' + (pageCount === 1 ? '' : 's') + ' (' + gaps.length + ' gap' + (gaps.length === 1 ? '' : 's') + + ') ' + (pageCount === 1 ? "isn't" : "aren't") + ' part of any detected article, and will never come up just by approving articles one by one:' + + '' + + gaps.map(([a, b]) => '').join('') + + ''; +} + +// Opens Review at a specific page even when no article covers it, by +// anchoring on whichever approved-or-not article is nearest that page (the +// review UI needs *an* article as context, but its own page navigation is +// not restricted to that article's pages) and then jumping. +async function magReviewPage(pageNumber) { + if (!state.articles.length) return; + const anchor = state.articles.slice().sort((a, b) => + Math.abs((a.pages?.[0] ?? 0) - pageNumber) - Math.abs((b.pages?.[0] ?? 0) - pageNumber))[0]; + await magReview(anchor.id); + magReviewGoMagazinePage(pageNumber); +} + function renderContents() { const host = $('mag-contents'); if (!host) return; @@ -631,6 +684,7 @@ function renderContents() { host.innerHTML = '

No articles yet. Run ingestion to detect them.

'; return; } + renderCoverageBanner(); const bySection = new Map(); state.articles.forEach((a) => { const key = a.section || 'Articles'; @@ -3452,6 +3506,7 @@ root.magContentsToggleSelect = magContentsToggleSelect; root.magContentsCommitTitle = magContentsCommitTitle; root.magGenerateAllAudio = magGenerateAllAudio; root.magTogglePlaylist = magTogglePlaylist; +root.magReviewPage = magReviewPage; root.magPlaylistPlay = magPlaylistPlay; root.magContentsToggleArticle = magContentsToggleArticle; root.magContentsSelectAllVisible = magContentsSelectAllVisible; diff --git a/static/sections/s-magazine.html b/static/sections/s-magazine.html index 6c714a6..4cbd9eb 100644 --- a/static/sections/s-magazine.html +++ b/static/sections/s-magazine.html @@ -79,6 +79,7 @@ +
diff --git a/static/style.css b/static/style.css index fcaee50..7ca16ab 100644 --- a/static/style.css +++ b/static/style.css @@ -4930,6 +4930,12 @@ input[list="lang-codes"] { .mag-bulkbar-progress { flex: 1; min-width: 0; } .mag-bulkbar-actions { display: flex; gap: 6px; margin-left: auto; } +.mag-coverage-banner { display: flex; align-items: flex-start; gap: 10px; flex-wrap: wrap; + padding: 10px 14px; margin-bottom: 12px; border: 1px solid var(--yellow, #d97706); border-radius: var(--radius); + 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-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); background: var(--panel); }