move to indexedb and add about page
All checks were successful
Deploy Sokko G / deploy (push) Successful in 7s

This commit is contained in:
Shinuwa 2026-07-24 12:09:45 +02:00
parent a8809afdfd
commit 772b9951de
12 changed files with 855 additions and 249 deletions

View file

@ -31,6 +31,7 @@ export function validateSiteContent(site) {
"navigation.home",
"navigation.toolboxes",
"navigation.games",
"navigation.about",
"navigation.mobileGames",
"sidebar.badge",
"sidebar.note",
@ -56,8 +57,14 @@ export function validateSiteContent(site) {
"home.origin.visualAlt",
"home.origin.dialogueAriaLabel",
"home.origin.caption",
"home.aboutLink",
"home.legal.copyright",
"home.legal.disclaimer",
"about.eyebrow",
"about.title",
"about.description",
"about.toolsTitle",
"about.limitsTitle",
"toolboxes.eyebrow",
"toolboxes.title",
"toolboxes.newButton",
@ -139,6 +146,37 @@ export function validateSiteContent(site) {
assertStringArray(site, "toolboxes.storageHelp.items", 3);
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");
aboutLimits.forEach((limit, index) => {
if (typeof limit === "string") {
assert.ok(limit.trim(), `about.limits[${index}] must not be empty`);
return;
}
assert.equal(typeof limit, "object", `about.limits[${index}] must be a string or object`);
assertNonEmptyString({ limit }, "limit.icon");
assertNonEmptyString({ limit }, "limit.title");
assertNonEmptyString({ limit }, "limit.text");
});
const aboutSections = valueAt(site, "about.sections");
assert.ok(Array.isArray(aboutSections), "about.sections must be an array");
assert.ok(aboutSections.length >= 2, "about.sections must contain at least 2 items");
aboutSections.forEach((section) => {
assertNonEmptyString({ section }, "section.title");
assertNonEmptyString({ section }, "section.text");
});
const aboutTools = valueAt(site, "about.tools");
assert.ok(Array.isArray(aboutTools), "about.tools must be an array");
assert.ok(aboutTools.length >= 1, "about.tools must contain at least 1 item");
aboutTools.forEach((tool) => {
assertNonEmptyString({ tool }, "tool.icon");
assertNonEmptyString({ tool }, "tool.name");
assertNonEmptyString({ tool }, "tool.description");
});
const lines = valueAt(site, "home.origin.lines");
assert.ok(Array.isArray(lines), "home.origin.lines must be an array");
assert.ok(lines.length >= 2, "home.origin.lines must contain at least 2 items");

View file

@ -16,6 +16,8 @@ test("react application defines the expected local toolbox primitives", async ()
const styleNebula = await readFile("website/src/styles/_nebula.scss", "utf8");
const styleTokens = await readFile("website/src/styles/_tokens.scss", "utf8");
const styleBase = await readFile("website/src/styles/_base.scss", "utf8");
const styleMixins = await readFile("website/src/styles/_mixins.scss", "utf8");
const styleShell = await readFile("website/src/styles/_shell.scss", "utf8");
const styleCards = await readFile("website/src/styles/_cards.scss", "utf8");
const styleHome = await readFile("website/src/styles/_home.scss", "utf8");
const styleToolboxes = await readFile("website/src/styles/_toolboxes.scss", "utf8");
@ -53,9 +55,12 @@ test("react application defines the expected local toolbox primitives", async ()
const textImport = await readFile("website/src/features/toolboxes/modules/textImport.js", "utf8");
const reorderHook = await readFile("website/src/hooks/usePointerReorder.js", "utf8");
const bodyScrollLock = await readFile("website/src/utils/bodyScrollLock.js", "utf8");
const indexedDbStorage = await readFile("website/src/utils/indexedDbStorage.js", "utf8");
assert.match(source, /sokkog:toolboxes/);
assert.match(source, /sokkog:game-toolbox-links/);
assert.match(source, /indexedDbStorage\.js/);
assert.match(indexedDbStorage, /const DB_NAME = "sokkog"/);
assert.match(indexedDbStorage, /const KV_STORE = "kv"/);
assert.match(indexedDbStorage, /const MODULE_STORE = "modules"/);
assert.match(source, /styles\/main\.scss/);
assert.match(styles, /@use "tokens"/);
assert.match(styles, /@use "nebula"/);
@ -71,6 +76,13 @@ test("react application defines the expected local toolbox primitives", async ()
assert.match(styleBase, /\.import-icon-button/);
assert.match(styleHome, /\.dialogue-line i/);
assert.match(styleHome, /font-style: italic/);
assert.match(styleHome, /\.about-page/);
assert.match(styleHome, /\.nebula-panel/);
assert.match(styleMixins, /@mixin gold-panel-frame/);
assert.match(styleHome, /\.about-story-section/);
assert.match(styleHome, /\.about-tools-list/);
assert.match(styleHome, /\.about-tool-item \.module-icon/);
assert.match(styleShell, /\.sidebar-about-link/);
assert.match(styleToolboxes, /\.module-icon-abacus/);
assert.match(styleToolboxes, /\.module-icon-calculator/);
assert.match(styleToolboxes, /\.calculator-module/);
@ -183,7 +195,8 @@ test("react application defines the expected local toolbox primitives", async ()
assert.match(linksModule, /copyText/);
assert.match(source, /DEFAULT_SITE_CONTENT/);
assert.match(source, /\/data\/site\.json/);
assert.match(source, /APP_STORAGE_LIMIT_BYTES/);
assert.match(source, /useIndexedToolboxes/);
assert.match(source, /getStorageEstimate/);
assert.match(source, /DEFAULT_TOOLBOX_ICON/);
assert.match(source, /TOOLBOX_ICON_FILES/);
assert.match(source, /function normalizeToolboxIcon/);
@ -193,7 +206,10 @@ test("react application defines the expected local toolbox primitives", async ()
assert.match(source, /draggingToolboxId/);
assert.match(source, /toolbox-card-drag-handle/);
assert.match(source, /function StorageQuota/);
assert.match(source, /function writeStorageValue/);
assert.match(source, /function AboutPage/);
assert.match(source, /route === "\/about"/);
assert.match(source, /sidebar-about-link/);
assert.match(source, /module-icon-\$\{tool\.icon \|\| "notepad"\}/);
assert.match(source, /role="progressbar"/);
assert.match(source, /function createGlobalExportPayload/);
assert.match(source, /async function importAllToolboxes/);