Add toolbox library page with editable examples
All checks were successful
Deploy Sokko G / deploy (push) Successful in 7s

This commit is contained in:
Shinuwa 2026-08-03 11:37:59 +02:00
parent 8eb622e05d
commit 9b72f4934c
20 changed files with 4774 additions and 31 deletions

View file

@ -32,18 +32,27 @@ export function validateSiteContent(site) {
"navigation.home",
"navigation.toolboxes",
"navigation.games",
"navigation.library",
"navigation.about",
"navigation.mobileLibrary",
"navigation.mobileGames",
"sidebar.badge",
"sidebar.note",
"topbar.dashboard",
"topbar.toolbox",
"topbar.library",
"topbar.games",
"gamesPage.eyebrow",
"gamesPage.title",
"gamesPage.description",
"gamesPage.emptyTitle",
"gamesPage.emptyText",
"library.eyebrow",
"library.title",
"library.description",
"library.summaryLabel",
"library.tocLabel",
"library.toolsLabel",
"home.hero.eyebrow",
"home.hero.title",
"home.hero.description",
@ -65,6 +74,7 @@ export function validateSiteContent(site) {
"about.title",
"about.description",
"about.toolsTitle",
"about.toolsLibraryLink",
"about.limitsTitle",
"about.contribute.eyebrow",
"about.contribute.title",
@ -76,6 +86,7 @@ export function validateSiteContent(site) {
"toolboxes.eyebrow",
"toolboxes.title",
"toolboxes.newButton",
"toolboxes.libraryLink",
"toolboxes.importAll",
"toolboxes.exportAll",
"toolboxes.importOne",
@ -340,6 +351,16 @@ export function validateSiteContent(site) {
assertStringArray(site, "toolboxes.storageHelp.items", 3);
const librarySummaryItems = valueAt(site, "library.summaryItems");
assert.ok(Array.isArray(librarySummaryItems), "library.summaryItems must be an array");
assert.ok(librarySummaryItems.length >= 3, "library.summaryItems must contain at least 3 items");
librarySummaryItems.forEach((item, index) => {
assertNonEmptyString({ item }, `item.icon`);
assertNonEmptyString({ item }, `item.title`);
assertNonEmptyString({ item }, `item.text`);
assert.equal(typeof item, "object", `library.summaryItems[${index}] must be an object`);
});
const aboutLimits = valueAt(site, "about.limits");
assert.ok(Array.isArray(aboutLimits), "about.limits must be an array");
assert.ok(aboutLimits.length >= 3, "about.limits must contain at least 3 items");
@ -381,6 +402,43 @@ export function validateSiteContent(site) {
});
}
export function validateLibraryData(payload) {
assert.equal(typeof payload?.toolbox, "object", "library.toolbox must be an object");
assert.equal(typeof payload?.modules, "object", "library.modules must be an object");
assertNonEmptyString(payload, "toolbox.name");
assert.ok(Array.isArray(payload.toolbox.modules), "library.toolbox.modules must be an array");
assert.ok(payload.toolbox.modules.length >= 1, "library.toolbox.modules must not be empty");
const moduleIds = new Set();
payload.toolbox.modules.forEach((module, index) => {
assertNonEmptyString({ module }, "module.id");
assertNonEmptyString({ module }, "module.type");
assert.ok(!moduleIds.has(module.id), `library.toolbox.modules[${index}].id must be unique`);
moduleIds.add(module.id);
assert.equal(typeof payload.modules[module.id], "object", `library.modules.${module.id} must be an object`);
});
["notepad", "checklist", "images", "links", "counters", "combos", "calculator", "table", "timer", "taskPlanner", "imageAnnotation"].forEach((type) => {
assert.ok(payload.toolbox.modules.some((module) => module.type === type), `library must include a ${type} example`);
});
const imagesModule = payload.toolbox.modules.find((module) => module.type === "images");
const imagesData = payload.modules[imagesModule.id];
assert.ok(Array.isArray(imagesData.images), "library images example must contain images");
assert.ok(imagesData.images.length >= 1, "library images example must not be empty");
imagesData.images.forEach((image, index) => {
assertNonEmptyString({ image }, "image.id");
assertNonEmptyString({ image }, "image.dataUrl");
assert.match(image.dataUrl, /^data:image\//, `library images[${index}].dataUrl must be an inline image`);
});
const taskModule = payload.toolbox.modules.find((module) => module.type === "taskPlanner");
const taskData = payload.modules[taskModule.id];
assert.ok(Array.isArray(taskData.tasks), "library task planner example must contain tasks");
assert.ok(Array.isArray(taskData.relations), "library task planner example must contain relations");
assert.ok(taskData.relations.length >= 1, "library task planner example must include a parent/child relation");
}
function assertSnakeCase(value, path) {
assert.equal(typeof value, "string", `${path} must be a string`);
assert.match(value, SNAKE_CASE_RE, `${path} must be snake_case`);