From f713d0c0a9994c7a95d428277f7ffe9b23d12561 Mon Sep 17 00:00:00 2001 From: Codex Date: Fri, 9 Oct 2026 10:22:01 +0200 Subject: [PATCH] Complete Greatest covers and use Japanese navigation label --- docs/greatest.md | 4 +++- greatest/catalog.json | 10 +++++----- public/app.css | 2 +- public/common.js | 2 +- test/greatest.test.js | 1 + tools/test-greatest-ui.js | 2 ++ 6 files changed, 13 insertions(+), 8 deletions(-) diff --git a/docs/greatest.md b/docs/greatest.md index 7317832..42496c1 100644 --- a/docs/greatest.md +++ b/docs/greatest.md @@ -13,7 +13,9 @@ Within each cohort, **Rank by my vote** sorts flame → good → neutral → sku ## Covers and metadata -The one-time seed is curated from the research document. `node tools/enrich-greatest-catalog.js` is an **optional manual** public-AniList metadata refresh, paced at one request every 1.5 seconds. It only accepts matching manga title and **original serialization year**, leaving uncertain results blank rather than using a sequel or remake cover. The server makes **no AniList calls at runtime**. Browser covers are lazy-loaded from AniList's CDN; a title-letter card remains if a cover is missing or fails. Local library covers take precedence. +The one-time seed is curated from the research document. All 106 catalog entries now have a cover URL. Most were matched to public AniList manga metadata; the five initial gaps were checked individually. Phoenix (1954) and Nausicaä use matching AniList records; Kitarō uses Drawn & Quarterly's Kitaro edition, Someone Hertz uses Shonen Jump's first-volume cover, and Hitoner uses Shueisha's 2026 first-volume cover. Hitoner's AniList 2024 entry represents an earlier work, so it was **not** used for the 2026 series. The five added URLs returned image content on 2026-10-09, but remote hosts can change them; the UI still shows a title-letter fallback if an image fails. + +`node tools/enrich-greatest-catalog.js` is an **optional manual** public-AniList metadata refresh, paced at one request every 1.5 seconds. It only accepts matching manga title and **original serialization year**, leaving uncertain results untouched rather than using a sequel or remake cover. The server makes **no metadata calls at runtime**. Browser covers are lazy-loaded from their listed source; local library covers take precedence. Public catalog chapter counts are often absent; the UI says “Chapters unknown” rather than inventing one. For owned titles, the local library count wins. Status comes from AniList's releasing/finished flag or local library metadata, and may be unknown. Official source links may be publisher editions, chapter services, or an award/info lead; they do not imply all chapters are available in every country. No unverified hoster coverage is claimed. diff --git a/greatest/catalog.json b/greatest/catalog.json index 5f98418..d3ce4fb 100644 --- a/greatest/catalog.json +++ b/greatest/catalog.json @@ -51,7 +51,7 @@ "kind": "info", "url": "https://tezukaosamu.net/en/manga/656.html" }, - "cover": null, + "cover": "https://s4.anilist.co/file/anilistcdn/media/manga/cover/medium/b109836-NwYxXNYLkjxQ.jpg", "chapters": null, "status": "unknown" }, @@ -172,7 +172,7 @@ "kind": "edition", "url": "https://drawnandquarterly.com/books/kitaros-yokai-battles/" }, - "cover": null, + "cover": "https://drawnandquarterly.com/wp-content/uploads/biblioshare_attachments/9781770463196.png", "chapters": null, "status": "unknown" }, @@ -509,7 +509,7 @@ "kind": "edition", "url": "https://www.viz.com/manga-books/manga/nausicaa-of-the-valley-of-the-wind/all" }, - "cover": null, + "cover": "https://s4.anilist.co/file/anilistcdn/media/manga/cover/medium/bx30651-xLufeLnDPqpn.jpg", "chapters": null, "status": "unknown" }, @@ -2115,7 +2115,7 @@ "kind": "read", "url": "https://www.viz.com/shonenjump/chapters/someone-hertz" }, - "cover": null, + "cover": "https://www.shonenjump.com/j/comics/_comicimage/someone001.jpg", "chapters": null, "status": "unknown" }, @@ -2194,7 +2194,7 @@ "kind": "official award and reading lead", "url": "https://mangaplus.shueisha.co.jp/web_pages/2441/" }, - "cover": null, + "cover": "https://dosbg3xlm0x1t.cloudfront.net/images/items/9784088852317/500/9784088852317.jpg", "chapters": null, "status": "unknown" } diff --git a/public/app.css b/public/app.css index 742631b..0157d37 100644 --- a/public/app.css +++ b/public/app.css @@ -105,7 +105,7 @@ main { max-width: 1200px; margin: 0 auto; padding: 16px; } .nav .brand { font-size: 14px; } .nav .btn { padding-left: 7px; padding-right: 7px; font-size: 12px; } .nav-greatest { font-size: 0 !important; } - .nav-greatest::after { content: 'GOAT'; font-size: 12px; } + .nav-greatest::after { content: 'Saikō'; font-size: 12px; } .reading-summary { grid-template-columns: 1fr; gap: 5px; } .reading-summary-links { justify-content: flex-start; } } diff --git a/public/common.js b/public/common.js index ef928f2..f3a0c51 100644 --- a/public/common.js +++ b/public/common.js @@ -96,7 +96,7 @@ function topNav(me) { document.addEventListener('click', () => { panel.hidden = true; }); const nav = h('header', { class: 'nav' }, h('a', { class: 'brand', href: './' }, 'MangaCircus'), - h('a', { class: 'btn nav-greatest', href: 'greatest.html', title: 'Shijō Saikō no Manga · Greatest of All Time' }, 'Greatest'), + h('a', { class: 'btn nav-greatest', href: 'greatest.html', title: 'Shijō Saikō no Manga · Greatest of All Time', 'aria-label': 'Shijō Saikō no Manga' }, 'Shijō Saikō no Manga'), h('div', { class: 'grow' }), h('div', { class: 'bell-wrap' }, bell, panel), h('a', { class: 'btn', href: 'settings.html' }, 'Settings'), diff --git a/test/greatest.test.js b/test/greatest.test.js index d9faa4a..a619226 100644 --- a/test/greatest.test.js +++ b/test/greatest.test.js @@ -7,6 +7,7 @@ test('curated manga catalog has unique identities and ordered five-year periods' const catalog = loadCatalog(); assert.equal(catalog.periods.length, 13); assert.equal(catalog.entries.length, 106); + assert.ok(catalog.entries.every(entry => /^https:\/\//.test(entry.cover)), 'Every catalog title has cover artwork'); for (const [index, period] of catalog.periods.entries()) { const entries = catalog.entries.filter(entry => entry.period === period); assert.ok(entries.length >= 5 && entries.length <= 10); diff --git a/tools/test-greatest-ui.js b/tools/test-greatest-ui.js index 59a6498..e17dcdd 100644 --- a/tools/test-greatest-ui.js +++ b/tools/test-greatest-ui.js @@ -42,6 +42,7 @@ async function freePort() { page.on('pageerror', error => errors.push(error.message)); await page.goto(base + '/greatest.html'); await page.getByRole('heading', { name: 'Shijō Saikō no Manga' }).waitFor(); + assert.equal(await page.getByRole('link', { name: 'Shijō Saikō no Manga' }).textContent(), 'Shijō Saikō no Manga'); assert.equal(await page.locator('.greatest-period').count(), 13); assert.equal(await page.locator('.greatest-tile').count(), 106); const astro = page.locator('.greatest-tile').filter({ hasText: 'Astro Boy' }); @@ -54,6 +55,7 @@ async function freePort() { await page.getByRole('dialog').getByText('Community votes: 🔥 1').waitFor(); await page.getByRole('dialog').getByRole('button', { name: 'Close' }).click(); await page.setViewportSize({ width: 390, height: 844 }); + assert.equal(await page.locator('.nav-greatest').evaluate(el => getComputedStyle(el, '::after').content), '"Saikō"'); assert.ok(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth), 'Mobile horizontal overflow'); await page.getByRole('button', { name: '📚 In library' }).click(); assert.equal(await page.locator('.greatest-tile').count(), 0);