Skip to content

Commit 2230435

Browse files
TMHSDigitalclaude
andcommitted
Thumbnails: tools and fonts need a pinned preview, CC-BY previews name the creator
The gate asked only whether the entry's licence is open. A program's GPL or a font's OFL does not cover the project's logo, splash or social card, so tools and fonts entries now qualify only with a maintainer-checked preview: URL; 40 such records and files are removed. CC-BY entries need attribution_string or publisher, the caption prefers the recorded credit line, and cards carry it as a title. The thumbs script now passes category and attribution_string to the gate. CONTRIBUTING and site/README describe the rule and the itch cover caveat. Closes #132 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
1 parent 0511ac4 commit 2230435

49 files changed

Lines changed: 69 additions & 380 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎CONTRIBUTING.md‎

Lines changed: 8 additions & 1 deletion

‎site/README.md‎

Lines changed: 5 additions & 3 deletions

‎site/build.mjs‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { fileURLToPath } from "node:url";
1717
import { baseHeadHtml, commercialLabel, headAssetsHtml, esc, latestAllowedDate, OG_CARD_NAME, sponsorButtonHtml } from "./lib/shared.mjs";
1818
import { filmstripHtml, groupedEntriesHtml } from "./lib/cards.mjs";
1919
import { categoryPageHtml } from "./lib/category-page.mjs";
20-
import { filmstripPick, thumbRelPath } from "./lib/thumbs.mjs";
20+
import { filmstripPick, isAttributionLicence, previewCredit, thumbRelPath } from "./lib/thumbs.mjs";
2121
import { parseFrontmatter, summaryFromBody } from "./lib/frontmatter.mjs";
2222
import { listEntryFiles } from "./lib/entry-files.mjs";
2323
import { entryPageHtml, scriptJson } from "./lib/entry-page.mjs";
@@ -131,7 +131,13 @@ function loadEntries(vocab) {
131131
summary: summaryFromBody(body),
132132
// The publisher's preview, when the licence gate let the script keep one.
133133
thumb: thumbs[String(meta.id)]
134-
? { path: thumbRelPath(String(meta.id)), width: thumbs[String(meta.id)].width, height: thumbs[String(meta.id)].height }
134+
? {
135+
path: thumbRelPath(String(meta.id)),
136+
width: thumbs[String(meta.id)].width,
137+
height: thumbs[String(meta.id)].height,
138+
// CC-BY: the creator is named on the card too, not only on the entry page.
139+
...(isAttributionLicence(meta.license) ? { credit: previewCredit(meta) } : {}),
140+
}
135141
: null,
136142
...(meta.grid_dimensions ? { grid_dimensions: String(meta.grid_dimensions) } : {}),
137143
...(meta.camera_perspective ? { camera_perspective: String(meta.camera_perspective) } : {}),

‎site/lib/cards.mjs‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ export function tileHtml(entry, label = entry.category) {
3030
export function thumbHtml(entry, root = "", label) {
3131
const t = entry.thumb;
3232
if (t && t.path) {
33-
return `<img class="entry-thumb-img" src="${esc(root)}${esc(t.path)}" width="${Number(t.width) || 640}" height="${Number(t.height) || 400}" alt="" loading="lazy" decoding="async">`;
33+
const credit = t.credit ? ` title="${esc(t.credit)}"` : "";
34+
return `<img class="entry-thumb-img" src="${esc(root)}${esc(t.path)}" width="${Number(t.width) || 640}" height="${Number(t.height) || 400}" alt=""${credit} loading="lazy" decoding="async">`;
3435
}
3536
return tileHtml(entry, label);
3637
}

‎site/lib/lib.test.mjs‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -922,6 +922,14 @@ eq("thumbs: publisherKey falls back to host", publisherKey({ url: "https://www.k
922922
eq("thumbs: publisherKey prefers publisher", publisherKey({ publisher: "Kenney", url: "https://x/" }), "Kenney");
923923
eq("thumbs: credit with publisher", previewCredit({ publisher: "Kenney", license: "CC0" }), "Preview image from Kenney, shown under CC0");
924924
eq("thumbs: credit without publisher", previewCredit({ license: "CC-BY-4.0" }), "Preview image from the source page, shown under CC-BY-4.0");
925+
eq("thumbs: CC-BY credit prefers the recorded credit line", previewCredit({ license: "CC-BY-4.0", publisher: "Dareful", attribution_string: "Video by Dareful.com" }), "Video by Dareful.com. Preview image shown under CC-BY-4.0");
926+
eq("thumbs: CC-BY credit falls back to publisher", previewCredit({ license: "CC-BY-3.0", publisher: "Kevin MacLeod" }), "Kevin MacLeod. Preview image shown under CC-BY-3.0");
927+
eq("thumbs: tools need a pinned preview", previewEligibility({ license: "GPL-3.0-or-later", status: "active", category: "tools", url: "https://www.gimp.org/" }).ok, false);
928+
eq("thumbs: fonts need a pinned preview", previewEligibility({ license: "SIL OFL", status: "active", category: "fonts", url: "https://rsms.me/inter/" }).reason.startsWith("category fonts:"), true);
929+
eq("thumbs: a pinned preview admits a tool", previewEligibility({ license: "MIT", status: "active", category: "tools", url: "https://ldtk.io/", preview: "https://ldtk.io/shot.png" }).ok, true);
930+
eq("thumbs: 3d page image is the asset", previewEligibility({ license: "CC0", status: "active", category: "3d", url: "https://x.itch.io/a" }).ok, true);
931+
eq("thumbs: CC-BY without a creator refused", previewEligibility({ license: "CC-BY-4.0", status: "active", category: "audio", url: "https://x.itch.io/a" }).reason, "licence CC-BY-4.0 needs a creator to credit; set attribution_string or publisher");
932+
eq("thumbs: CC-BY with attribution_string admitted", previewEligibility({ license: "CC-BY-4.0", status: "active", category: "audio", url: "https://x.itch.io/a", attribution_string: "Sound by X" }).ok, true);
925933
{
926934
const t = { path: "thumbs/x.webp", width: 640, height: 400 };
927935
const pool = [

‎site/lib/thumbs.mjs‎

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,19 @@ export const PREVIEW_LICENSES = new Set([
4040
/** Hosts whose og:image is a social card or a logo, never an asset preview. */
4141
export const SKIP_HOSTS = new Set(["github.com", "www.github.com", "fonts.google.com"]);
4242

43+
/**
44+
* Categories where the page's og:image is the project's logo, splash screen
45+
* or social card rather than the asset. The program's or font's licence does
46+
* not cover that artwork, so these entries only get a thumbnail from a
47+
* maintainer-checked `preview:` URL.
48+
*/
49+
export const LOGO_CATEGORIES = new Set(["tools", "fonts"]);
50+
51+
/** Licences whose terms require naming the creator wherever the work is shown. */
52+
export function isAttributionLicence(license) {
53+
return /^CC-BY/.test(String(license));
54+
}
55+
4356
function hostOf(url) {
4457
try {
4558
return new URL(String(url)).hostname.toLowerCase();
@@ -59,8 +72,18 @@ export function previewEligibility(entry) {
5972
if (!PREVIEW_LICENSES.has(String(entry.license))) {
6073
return { ok: false, reason: `licence ${entry.license} is not in the preview allowlist` };
6174
}
75+
const pinned = /^https:\/\//.test(preview);
76+
if (LOGO_CATEGORIES.has(String(entry.category)) && !pinned) {
77+
return {
78+
ok: false,
79+
reason: `category ${entry.category}: the page image is a logo, splash or social card that the ${entry.license} licence does not cover; pin a checked image with preview:`,
80+
};
81+
}
82+
if (isAttributionLicence(entry.license) && !entry.attribution_string && !entry.publisher) {
83+
return { ok: false, reason: `licence ${entry.license} needs a creator to credit; set attribution_string or publisher` };
84+
}
6285
const host = hostOf(entry.url);
63-
if (SKIP_HOSTS.has(host) && !/^https:\/\//.test(preview)) return { ok: false, reason: `host ${host} is skipped` };
86+
if (SKIP_HOSTS.has(host) && !pinned) return { ok: false, reason: `host ${host} is skipped` };
6487
return { ok: true, reason: null };
6588
}
6689

@@ -152,10 +175,19 @@ export function publisherKey(entry) {
152175
return hostOf(entry.url).replace(/^www\./, "");
153176
}
154177

155-
/** The credit line shown under a preview image. */
178+
/**
179+
* The credit line shown under a preview image. For attribution licences the
180+
* recorded credit line comes first, then the publisher: CC-BY requires the
181+
* creator to be named wherever the work is shown.
182+
*/
156183
export function previewCredit(entry) {
184+
const licence = String(entry.license);
185+
if (isAttributionLicence(licence)) {
186+
const creator = entry.attribution_string ? String(entry.attribution_string) : entry.publisher ? String(entry.publisher) : "";
187+
if (creator) return `${creator.replace(/[.\s]+$/, "")}. Preview image shown under ${licence}`;
188+
}
157189
const from = entry.publisher ? String(entry.publisher) : "the source page";
158-
return `Preview image from ${from}, shown under ${entry.license}`;
190+
return `Preview image from ${from}, shown under ${licence}`;
159191
}
160192

161193
/**
-5.87 KB
Binary file not shown.

‎site/public/thumbs/audacity.webp‎

-16.6 KB
Binary file not shown.

‎site/public/thumbs/blender.webp‎

-15.4 KB
Binary file not shown.

‎site/public/thumbs/blockbench.webp‎

-10.2 KB
Binary file not shown.

0 commit comments

Comments
 (0)