From 0b9c9638b15d991a0c150026b34f36682ef35705 Mon Sep 17 00:00:00 2001 From: Oleg Proskurin Date: Tue, 21 Apr 2026 20:27:16 +0700 Subject: [PATCH] feat: add screenshots --- CLAUDE.md | 38 +- bs-config.cjs | 21 + src/examples/space-worksheet.html | 537 -------------- src/examples/space-worksheet2.html | 659 ------------------ src/scripts/post-generate.mjs | 47 ++ src/scripts/take-screenshots.mjs | 116 +++ .../docs/collecting-asteroids-1.template.html | 318 ++++----- .../docs/collecting-asteroids-2.template.html | 316 ++++----- .../docs/collecting-asteroids-3.template.html | 316 ++++----- .../collecting-asteroids/scripts/generate.mjs | 2 + tasks/space-exploration/docs/space-demo.html | 44 +- .../docs/space-exploration-1-example.html | 132 ++-- .../docs/space-exploration-1.template.html | 132 ++-- .../docs/space-worksheet2-example.html | 132 ++-- .../docs/space-worksheet2.template.html | 264 +++---- .../docs/space-worksheet3.template.html | 396 +++++------ tasks/space-exploration/scripts/generate.mjs | 2 + .../docs/space-route-1.template.html | 184 ++--- tasks/space-route/editor.html | 504 ++++---------- tasks/space-route/scripts/generate.mjs | 2 + 20 files changed, 1472 insertions(+), 2690 deletions(-) delete mode 100644 src/examples/space-worksheet.html delete mode 100644 src/examples/space-worksheet2.html create mode 100644 src/scripts/post-generate.mjs create mode 100644 src/scripts/take-screenshots.mjs diff --git a/CLAUDE.md b/CLAUDE.md index c91b5d3..803b09f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -101,12 +101,15 @@ When the user asks to create a new document of an existing type: ## Generation Pipeline ``` -.template.html + .data.json (if exists) → scripts/generate.mjs → .output.html +.template.html + .data.json (if exists) → scripts/generate.mjs → .output.html + screenshots ``` - Same pipeline for both Claude-initiated regeneration and editor-save flow - If no `.data.json` exists → template.html copied as-is to output.html -- `generate.mjs` is a per-task-type script, not a global one +- `generate.mjs` is a per-task-type script; it calls `postGenerate()` from `src/scripts/post-generate.mjs` at the end +- `postGenerate()` runs `src/scripts/take-screenshots.mjs` which produces per-page PNG screenshots in `tasks/{type}/temp/{docId}-page-{N}.png` +- Screenshots are automatically regenerated on every generate — old ones are deleted before new ones are written +- **All new task types must include `postGenerate()` call in their `generate.mjs`** — this is part of the standard pipeline ## Editor System @@ -122,11 +125,12 @@ http://localhost:3300/tasks/{type}/editor.html?file={docId} 2. Editor POST `/api/save-edits` with `{ taskType, docId, data }` 3. Server writes `docs/{docId}.data.json` 4. Server computes diff → writes `temp/{docId}.diff.json` -5. Server runs `generate.mjs` to regenerate output.html +5. Server runs `generate.mjs` → regenerates output.html + screenshots **Claude reviewing editor changes:** ```bash -cat tasks/{type}/temp/{docId}.diff.json +cat tasks/{type}/temp/{docId}.diff.json # see what changed +# Read screenshot PNGs from tasks/{type}/temp/{docId}-page-{N}.png to verify visually ``` ## Index Page @@ -146,8 +150,9 @@ Claude Code is the central orchestrator. Scripts are tools for Claude, not auton - Claude runs a script, reads its output, decides what to do next **Allowed write exceptions:** -- `generate.mjs` — writes `.output.html` (template+data→output, deterministic) +- `generate.mjs` — writes `.output.html` + screenshots via `postGenerate()` (template+data→output, deterministic) - `generate-pdf.mjs` — writes PDF (final artifact) +- `take-screenshots.mjs` — writes page PNGs to `temp/` (called by `postGenerate`, part of generate pipeline) - `/api/save-edits` — writes `.data.json` + `.diff.json` + runs `generate.mjs` (user-initiated from editor UI) **Example of correct flow:** @@ -166,6 +171,29 @@ During sessions, when the user gives feedback or instructions specific to a task If confirmed → write to `tasks/{type}/CLAUDE.md`. General rules that apply to all types → root CLAUDE.md (this file). +## Visual Verification + +**MANDATORY for:** refactoring, new features, new task types, new documents, any significant changes to templates or generation logic. + +### Automatic screenshots (passive) + +Every `generate.mjs` run produces per-page screenshots in `tasks/{type}/temp/{docId}-page-{N}.png`. These are always up-to-date — read them with the Read tool to verify results without asking the user. + +### Chrome DevTools MCP (interactive) + +Use `chrome-devtools-mcp` tools (`navigate_page`, `take_screenshot`) to check pages in the live browser when the dev server is running. This is useful for: +- Checking editor functionality (drag, keyboard, save) +- Verifying hover/click states +- Inspecting specific elements + +### When to verify + +- **After generating a new document** — read at least page 1 screenshot to confirm images load and layout is correct +- **After refactoring** — check all affected task types +- **After editing template.html** — regenerate and check screenshots +- **After modifying asset paths or server config** — check all types +- **Do not ask the user to verify** what you can check yourself via screenshots + ## HTML Generation Guidelines - **OUTPUT MUST BE STATIC HTML.** Template files must contain only static markup — no embedded ` - - - - Исследуй Планету - - - - - - -
- -
-
- -
- -
-
- Итого собрано на планете:   -
-
- -
- -
- -
-

Исследуй Планету

-

Собери ресурсы, решая примеры!

-
-
- -
- -
-
- - 12 + 7 + 2 =   -
-
- -
-
- - 9 + 5 − 1 =   -
-
- -
-
- - 14 + 6 + 3 =   -
-
- -
-
- - 11 + 4 − 2 =   -
-
- -
-
- - 8 + 8 + 1 =   -
-
- -
-
- - 15 + 5 − 3 =   -
-
- -
-
- - 10 + 7 + 2 =   -
-
- -
-
- - 13 + 4 − 1 =   -
-
- -
-
- - 16 + 6 + 3 =   -
-
- -
-
- - 9 + 8 − 2 =   -
-
- -
-
- - 11 + 5 + 1 =   -
-
- -
-
- - 14 + 7 − 3 =   -
-
- -
-
- - 8 + 6 + 2 =   -
-
- -
-
- - 12 + 4 − 1 =   -
-
- -
-
- - 15 + 8 + 3 =   -
-
- -
-
- - 10 + 5 − 2 =   -
-
- -
-
- - 13 + 7 + 1 =   -
-
- -
-
- - 16 + 4 − 3 =   -
-
- -
-
- - 9 + 6 + 2 =   -
-
- -
-
- - 11 + 8 − 1 =   -
-
- -
-
-
- - -
- -
-
- -
- -
-
- Итого собрано на планете:   -
-
- -
- -
- -
-

Исследуй Планету

-

Собери ресурсы, решая примеры!

-
-
- -
- -
-
- - 5 × 3 + 4 =   -
-
- -
-
- - 5 × 1 − 2 =   -
-
- -
-
- - 5 × 5 + 6 =   -
-
- -
-
- - 5 × 2 − 1 =   -
-
- -
-
- - 5 × 4 + 3 =   -
-
- -
-
- - 5 × 1 + 5 =   -
-
- -
-
- - 5 × 3 − 3 =   -
-
- -
-
- - 5 × 5 + 2 =   -
-
- -
-
- - 5 × 2 + 6 =   -
-
- -
-
- - 5 × 4 − 2 =   -
-
- -
-
- - 5 × 1 + 4 =   -
-
- -
-
- - 5 × 3 + 1 =   -
-
- -
-
- - 5 × 5 − 1 =   -
-
- -
-
- - 5 × 2 + 3 =   -
-
- -
-
- - 5 × 4 + 5 =   -
-
- -
-
- - 5 × 1 − 3 =   -
-
- -
-
- - 5 × 3 + 6 =   -
-
- -
-
- - 5 × 2 − 2 =   -
-
- -
-
- - 5 × 5 + 4 =   -
-
- -
-
- - 5 × 4 − 1 =   -
-
- -
-
-
- - -
- -
-
- -
- -
-
- Итого собрано на планете:   -
-
- -
- -
- -
-

Исследуй Планету

-

Собери ресурсы, решая примеры!

-
-
- -
- -
-
- - 3 × 4 + 5 =   -
-
- -
-
- - 2 × 3 − 2 =   -
-
- -
-
- - 4 × 4 + 7 =   -
-
- -
-
- - 1 × 3 + 6 =   -
-
- -
-
- - 3 × 2 − 3 =   -
-
- -
-
- - 4 × 3 + 2 =   -
-
- -
-
- - 2 × 4 − 1 =   -
-
- -
-
- - 1 × 4 + 8 =   -
-
- -
-
- - 3 × 3 − 4 =   -
-
- -
-
- - 4 × 2 + 3 =   -
-
- -
-
- - 2 × 2 + 5 =   -
-
- -
-
- - 1 × 2 + 7 =   -
-
- -
-
- - 4 × 4 − 3 =   -
-
- -
-
- - 3 × 1 + 4 =   -
-
- -
-
- - 2 × 3 + 6 =   -
-
- -
-
- - 4 × 1 − 2 =   -
-
- -
-
- - 3 × 4 + 8 =   -
-
- -
-
- - 1 × 3 + 3 =   -
-
- -
-
- - 2 × 4 + 1 =   -
-
- -
-
- - 4 × 3 − 4 =   -
-
- -
-
-
- - - diff --git a/src/examples/space-worksheet2.html b/src/examples/space-worksheet2.html deleted file mode 100644 index 5019b5c..0000000 --- a/src/examples/space-worksheet2.html +++ /dev/null @@ -1,659 +0,0 @@ - - - - - - - - - - Исследуй Планету - - - - - - -
- -
-
- -
- -
-
- Итого собрано на планете:   -
-
- -
- -
- -
-

Исследуй Планету

-

Собери ресурсы, решая примеры!

-
-
- -
- -
-
- -
- 18 + 7 − 3 =   -
-
-
- -
-
- -
- 15 − 9 + 4 =   -
-
-
- -
-
- -
- 22 + 6 − 5 =   -
-
-
- -
-
- -
- 12 − 8 + 2 =   -
-
-
- -
-
- -
- 20 + 10 + 3 =   -
-
-
- -
-
- -
- 16 − 7 − 4 =   -
-
-
- -
-
- -
- 24 + 8 + 5 =   -
-
-
- -
-
- -
- 14 − 6 + 3 =   -
-
-
- -
-
- -
- 19 + 9 − 2 =   -
-
-
- -
-
- -
- 13 − 10 + 5 =   -
-
-
- -
-
- -
- 21 + 7 + 4 =   -
-
-
- -
-
- -
- 17 − 8 − 3 =   -
-
-
- -
-
- -
- 23 + 6 − 4 =   -
-
-
- -
-
- -
- 12 + 10 − 5 =   -
-
-
- -
-
- -
- 15 + 9 + 2 =   -
-
-
- -
-
- -
- 20 − 7 + 3 =   -
-
-
- -
-
- -
- 24 − 6 − 5 =   -
-
-
- -
-
- -
- 18 + 8 − 2 =   -
-
-
- -
-
- -
- 14 + 10 + 4 =   -
-
-
- -
-
- -
- 16 − 9 + 5 =   -
-
-
- -
-
-
- - -
- -
-
- -
- -
-
- Итого собрано на планете:   -
-
- -
- -
- -
-

Исследуй Планету

-

Собери ресурсы, решая примеры!

-
-
- -
Посчитай пятёрками: 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60 для зарядки расщепителя
- -
- -
-
- -
- 5 + 5 + 5 =   -
-
-
- -
-
- -
- 5 × 7 =   -
-
-
- -
-
- -
- 5 × 3 + 5 =   -
-
-
- -
-
- -
- 5 + 5 =   -
-
-
- -
-
- -
- 5 × 11 =   -
-
-
- -
-
- -
- 5 × 5 − 5 =   -
-
-
- -
-
- -
- 5 + 5 + 5 + 5 =   -
-
-
- -
-
- -
- 5 × 9 =   -
-
-
- -
-
- -
- 5 × 6 + 5 =   -
-
-
- -
-
- -
- 5 × 2 =   -
-
-
- -
-
- -
- 5 × 8 − 5 =   -
-
-
- -
-
- -
- 5 × 4 =   -
-
-
- -
-
- -
- 5 × 10 + 5 =   -
-
-
- -
-
- -
- 5 + 5 + 5 =   -
-
-
- -
-
- -
- 5 × 12 =   -
-
-
- -
-
- -
- 5 × 3 − 5 =   -
-
-
- -
-
- -
- 5 × 1 =   -
-
-
- -
-
- -
- 5 × 7 − 5 =   -
-
-
- -
-
- -
- 5 × 8 + 5 =   -
-
-
- -
-
- -
- 5 × 6 =   -
-
-
- -
-
-
- - -
- -
-
- -
- -
-
- Итого собрано на планете:   -
-
- -
- -
- -
-

Исследуй Планету

-

Собери ресурсы, решая примеры!

-
-
- -
- -
-
- -
- 3 × 2 + 2 × 1 =   -
-
-
- -
-
- -
- 4 × 3 + 3 × 2 =   -
-
-
- -
-
- -
- 2 × 1 + 3 × 1 =   -
-
-
- -
-
- -
- 4 × 2 + 2 × 2 =   -
-
-
- -
-
- -
- 3 × 3 + 2 × 1 =   -
-
-
- -
-
- -
- 2 × 3 + 3 × 2 =   -
-
-
- -
-
- -
- 4 × 1 + 2 × 2 =   -
-
-
- -
-
- -
- 3 × 1 + 3 × 1 =   -
-
-
- -
-
- -
- 4 × 3 + 2 × 1 =   -
-
-
- -
-
- -
- 2 × 2 + 3 × 2 =   -
-
-
- -
-
- -
- 3 × 3 + 3 × 2 =   -
-
-
- -
-
- -
- 4 × 1 + 2 × 1 =   -
-
-
- -
-
- -
- 2 × 3 + 2 × 2 =   -
-
-
- -
-
- -
- 4 × 2 + 3 × 1 =   -
-
-
- -
-
- -
- 3 × 1 + 2 × 2 =   -
-
-
- -
-
- -
- 4 × 3 + 3 × 1 =   -
-
-
- -
-
- -
- 2 × 1 + 2 × 1 =   -
-
-
- -
-
- -
- 3 × 2 + 3 × 2 =   -
-
-
- -
-
- -
- 4 × 2 + 2 × 1 =   -
-
-
- -
-
- -
- 2 × 2 + 3 × 1 =   -
-
-
- -
-
-
- - - diff --git a/src/scripts/post-generate.mjs b/src/scripts/post-generate.mjs new file mode 100644 index 0000000..7286c69 --- /dev/null +++ b/src/scripts/post-generate.mjs @@ -0,0 +1,47 @@ +/** + * Post-generate step: take per-page screenshots of the output HTML. + * Called at the end of each generate.mjs. + * + * Usage: import and call as function, or run standalone. + * import { postGenerate } from '../../src/scripts/post-generate.mjs'; + * await postGenerate(outputPath); + * + * Standalone: node post-generate.mjs + */ + +import { resolve } from 'path'; +import { fileURLToPath } from 'url'; +import { dirname } from 'path'; +import { execFile } from 'child_process'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const SCREENSHOT_SCRIPT = resolve(__dirname, 'take-screenshots.mjs'); + +export async function postGenerate(htmlPath) { + const absPath = resolve(htmlPath); + return new Promise((resolve, reject) => { + execFile('node', [SCREENSHOT_SCRIPT, absPath], { timeout: 60000 }, (err, stdout, stderr) => { + if (err) { + console.error('Screenshots failed:', stderr || err.message); + // Non-fatal — don't block generation + resolve(null); + return; + } + try { + const result = JSON.parse(stdout); + console.log(`Screenshots: ${result.pages} pages → ${result.screenshots[0].split('/').slice(-2).join('/')}`); + resolve(result); + } catch { + console.log(stdout); + resolve(null); + } + }); + }); +} + +// Standalone mode +if (process.argv[1] && resolve(process.argv[1]) === resolve(fileURLToPath(import.meta.url))) { + const file = process.argv[2]; + if (!file) { console.error('Usage: node post-generate.mjs '); process.exit(1); } + postGenerate(file); +} diff --git a/src/scripts/take-screenshots.mjs b/src/scripts/take-screenshots.mjs new file mode 100644 index 0000000..cb3afc7 --- /dev/null +++ b/src/scripts/take-screenshots.mjs @@ -0,0 +1,116 @@ +#!/usr/bin/env node + +/** + * Take per-page screenshots of a worksheet HTML file. + * + * Usage: node take-screenshots.mjs + * Example: node take-screenshots.mjs tasks/collecting-asteroids/docs/collecting-asteroids-1.output.html + * + * Output: tasks/{type}/temp/{docId}-page-1.png, ...-page-2.png, etc. + * Old screenshots for the same docId are deleted before writing new ones. + * + * Starts a temporary HTTP server to resolve /assets/ paths correctly. + */ + +import puppeteer from 'puppeteer'; +import { resolve, basename, dirname, join } from 'path'; +import { existsSync, mkdirSync, readdirSync, unlinkSync } from 'fs'; +import { createServer } from 'http'; +import { readFile } from 'fs/promises'; +import { fileURLToPath } from 'url'; +import { extname } from 'path'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const PROJECT_ROOT = resolve(__dirname, '../..'); + +const MIME_TYPES = { + '.html': 'text/html', '.css': 'text/css', '.js': 'application/javascript', + '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', + '.webp': 'image/webp', '.svg': 'image/svg+xml', '.json': 'application/json', +}; + +async function main() { + const htmlPath = process.argv[2]; + if (!htmlPath) { + console.error('Usage: node take-screenshots.mjs '); + process.exit(1); + } + + const absolutePath = resolve(htmlPath); + if (!existsSync(absolutePath)) { + console.error(`File not found: ${absolutePath}`); + process.exit(1); + } + + // Determine output dir: find the temp/ folder for this task type + // Path pattern: tasks/{type}/docs/{docId}.something.html + const relPath = absolutePath.replace(PROJECT_ROOT + '/', ''); + const parts = relPath.split('/'); + // parts: ['tasks', '{type}', 'docs', '{docId}.output.html'] or similar + const taskType = parts[1]; // e.g. 'collecting-asteroids' + const fileName = parts[parts.length - 1]; + const docId = fileName.replace(/\.(output|template)\.html$/, ''); + + const tempDir = join(PROJECT_ROOT, 'tasks', taskType, 'temp'); + mkdirSync(tempDir, { recursive: true }); + + // Clean old screenshots for this docId + const prefix = docId + '-page-'; + readdirSync(tempDir) + .filter(f => f.startsWith(prefix) && f.endsWith('.png')) + .forEach(f => unlinkSync(join(tempDir, f))); + + // Start temporary HTTP server + const server = createServer(async (req, res) => { + let filePath = join(PROJECT_ROOT, decodeURIComponent(req.url)); + try { + const data = await readFile(filePath); + const ext = extname(filePath); + res.writeHead(200, { 'Content-Type': MIME_TYPES[ext] || 'application/octet-stream' }); + res.end(data); + } catch { + res.writeHead(404); + res.end('Not found'); + } + }); + + await new Promise(r => server.listen(0, '127.0.0.1', r)); + const port = server.address().port; + + // Build URL relative to project root + const urlPath = '/' + relPath; + + try { + const browser = await puppeteer.launch({ headless: true, args: ['--no-sandbox'] }); + const page = await browser.newPage(); + + await page.setViewport({ width: 794, height: 1123, deviceScaleFactor: 2 }); + + await page.goto(`http://127.0.0.1:${port}${urlPath}`, { waitUntil: 'networkidle0', timeout: 30000 }); + + // Find all A4 page elements + const pageHandles = await page.$$('.w-\\[210mm\\]'); + + if (pageHandles.length === 0) { + console.error('No pages found (no .w-[210mm] elements)'); + await browser.close(); + server.close(); + process.exit(1); + } + + const screenshots = []; + for (let i = 0; i < pageHandles.length; i++) { + const screenshotPath = join(tempDir, `${docId}-page-${i + 1}.png`); + await pageHandles[i].screenshot({ path: screenshotPath }); + screenshots.push(screenshotPath); + } + const pageCount = pageHandles.length; + + await browser.close(); + console.log(JSON.stringify({ docId, pages: pageCount, screenshots }, null, 2)); + } finally { + server.close(); + } +} + +main().catch(e => { console.error(e); process.exit(1); }); diff --git a/tasks/collecting-asteroids/docs/collecting-asteroids-1.template.html b/tasks/collecting-asteroids/docs/collecting-asteroids-1.template.html index 9cd7364..ec8ee0f 100644 --- a/tasks/collecting-asteroids/docs/collecting-asteroids-1.template.html +++ b/tasks/collecting-asteroids/docs/collecting-asteroids-1.template.html @@ -27,33 +27,33 @@
- +
- +

Собери Астероиды

Загрузи трюмы кораблей!

-
5
-
5
-
7
+
5
+
5
+
7
-
2
-
3
-
1
-
4
-
1
-
2
-
4
-
6
-
5
-
3
-
5
-
3
+
2
+
3
+
1
+
4
+
1
+
2
+
4
+
6
+
5
+
3
+
5
+
3
@@ -62,34 +62,34 @@
- +
- +

Собери Астероиды

Загрузи трюмы кораблей!

-
7
-
7
-
10
+
7
+
7
+
10
-
1
-
2
-
4
-
2
-
2
-
3
-
1
-
3
-
2
-
4
-
5
-
6
-
5
+
1
+
2
+
4
+
2
+
2
+
3
+
1
+
3
+
2
+
4
+
5
+
6
+
5
@@ -98,33 +98,33 @@
- +
- +

Собери Астероиды

Загрузи трюмы кораблей!

-
5
-
5
-
6
+
5
+
5
+
6
-
3
-
2
-
1
-
1
-
3
-
2
-
2
-
2
-
4
-
5
-
6
-
4
+
3
+
2
+
1
+
1
+
3
+
2
+
2
+
2
+
4
+
5
+
6
+
4
@@ -133,33 +133,33 @@
- +
- +

Собери Астероиды

Загрузи трюмы кораблей!

-
8
-
8
-
6
+
8
+
8
+
6
-
3
-
5
-
2
-
3
-
3
-
2
-
4
-
5
-
1
-
6
-
1
-
4
+
3
+
5
+
2
+
3
+
3
+
2
+
4
+
5
+
1
+
6
+
1
+
4
@@ -168,32 +168,32 @@
- +
- +

Собери Астероиды

Загрузи трюмы кораблей!

-
4
-
4
-
4
+
4
+
4
+
4
-
4
-
1
-
3
-
1
-
1
-
2
-
5
-
6
-
3
-
5
-
6
+
4
+
1
+
3
+
1
+
1
+
2
+
5
+
6
+
3
+
5
+
6
@@ -202,35 +202,35 @@
- +
- +

Собери Астероиды

Загрузи трюмы кораблей!

-
10
-
10
-
8
+
10
+
10
+
8
-
1
-
2
-
3
-
4
-
2
-
3
-
5
-
1
-
3
-
4
-
6
-
5
-
2
-
6
+
1
+
2
+
3
+
4
+
2
+
3
+
5
+
1
+
3
+
4
+
6
+
5
+
2
+
6
@@ -239,33 +239,33 @@
- +
- +

Собери Астероиды

Загрузи трюмы кораблей!

-
6
-
9
-
12
+
6
+
9
+
12
-
2
-
4
-
1
-
3
-
5
-
2
-
4
-
3
-
3
-
6
-
5
-
1
+
2
+
4
+
1
+
3
+
5
+
2
+
4
+
3
+
3
+
6
+
5
+
1
@@ -274,34 +274,34 @@
- +
- +

Собери Астероиды

Загрузи трюмы кораблей!

-
14
-
10
-
16
+
14
+
10
+
16
-
4
-
5
-
5
-
4
-
6
-
3
-
4
-
4
-
5
-
6
-
2
-
3
-
6
+
4
+
5
+
5
+
4
+
6
+
3
+
4
+
4
+
5
+
6
+
2
+
3
+
6
@@ -310,36 +310,36 @@
- +
- +

Собери Астероиды

Загрузи трюмы кораблей!

-
5
-
10
-
15
+
5
+
10
+
15
-
2
-
3
-
1
-
2
-
3
-
4
-
1
-
2
-
3
-
4
-
5
-
6
-
5
-
4
-
6
+
2
+
3
+
1
+
2
+
3
+
4
+
1
+
2
+
3
+
4
+
5
+
6
+
5
+
4
+
6
diff --git a/tasks/collecting-asteroids/docs/collecting-asteroids-2.template.html b/tasks/collecting-asteroids/docs/collecting-asteroids-2.template.html index cc4c18e..eb41a9e 100644 --- a/tasks/collecting-asteroids/docs/collecting-asteroids-2.template.html +++ b/tasks/collecting-asteroids/docs/collecting-asteroids-2.template.html @@ -27,33 +27,33 @@
- +
- +

Собери Астероиды

Загрузи трюмы кораблей!

-
10
-
10
-
14
+
10
+
10
+
14
-
6
-
4
-
2
-
8
-
10
-
4
-
6
-
12
-
10
-
8
-
2
-
12
+
6
+
4
+
2
+
8
+
10
+
4
+
6
+
12
+
10
+
8
+
2
+
12
@@ -62,33 +62,33 @@
- +
- +

Собери Астероиды

Загрузи трюмы кораблей!

-
14
-
14
-
20
+
14
+
14
+
20
-
6
-
8
-
4
-
10
-
2
-
12
-
6
-
4
-
8
-
2
-
10
-
6
+
6
+
8
+
4
+
10
+
2
+
12
+
6
+
4
+
8
+
2
+
10
+
6
@@ -97,33 +97,33 @@
- +
- +

Собери Астероиды

Загрузи трюмы кораблей!

-
10
-
10
-
12
+
10
+
10
+
12
-
4
-
8
-
2
-
6
-
10
-
4
-
2
-
6
-
8
-
12
-
10
-
4
+
4
+
8
+
2
+
6
+
10
+
4
+
2
+
6
+
8
+
12
+
10
+
4
@@ -132,33 +132,33 @@
- +
- +

Собери Астероиды

Загрузи трюмы кораблей!

-
16
-
16
-
12
+
16
+
16
+
12
-
8
-
6
-
10
-
4
-
12
-
2
-
6
-
8
-
12
-
4
-
10
-
8
+
8
+
6
+
10
+
4
+
12
+
2
+
6
+
8
+
12
+
4
+
10
+
8
@@ -167,32 +167,32 @@
- +
- +

Собери Астероиды

Загрузи трюмы кораблей!

-
8
-
8
-
8
+
8
+
8
+
8
-
6
-
2
-
8
-
4
-
10
-
2
-
12
-
4
-
6
-
8
-
10
+
6
+
2
+
8
+
4
+
10
+
2
+
12
+
4
+
6
+
8
+
10
@@ -201,35 +201,35 @@
- +
- +

Собери Астероиды

Загрузи трюмы кораблей!

-
20
-
20
-
16
+
20
+
20
+
16
-
4
-
6
-
8
-
10
-
2
-
6
-
12
-
4
-
2
-
8
-
10
-
6
-
4
-
12
+
4
+
6
+
8
+
10
+
2
+
6
+
12
+
4
+
2
+
8
+
10
+
6
+
4
+
12
@@ -238,33 +238,33 @@
- +
- +

Собери Астероиды

Загрузи трюмы кораблей!

-
12
-
18
-
24
+
12
+
18
+
24
-
6
-
4
-
2
-
10
-
8
-
4
-
12
-
6
-
8
-
2
-
10
-
6
+
6
+
4
+
2
+
10
+
8
+
4
+
12
+
6
+
8
+
2
+
10
+
6
@@ -273,34 +273,34 @@
- +
- +

Собери Астероиды

Загрузи трюмы кораблей!

-
28
-
20
-
32
+
28
+
20
+
32
-
10
-
6
-
8
-
4
-
12
-
2
-
6
-
10
-
8
-
12
-
4
-
6
-
10
+
10
+
6
+
8
+
4
+
12
+
2
+
6
+
10
+
8
+
12
+
4
+
6
+
10
@@ -309,36 +309,36 @@
- +
- +

Собери Астероиды

Загрузи трюмы кораблей!

-
10
-
20
-
30
+
10
+
20
+
30
-
6
-
4
-
2
-
8
-
10
-
4
-
6
-
12
-
2
-
8
-
6
-
10
-
4
-
12
-
8
+
6
+
4
+
2
+
8
+
10
+
4
+
6
+
12
+
2
+
8
+
6
+
10
+
4
+
12
+
8
diff --git a/tasks/collecting-asteroids/docs/collecting-asteroids-3.template.html b/tasks/collecting-asteroids/docs/collecting-asteroids-3.template.html index d02e812..147f6d8 100644 --- a/tasks/collecting-asteroids/docs/collecting-asteroids-3.template.html +++ b/tasks/collecting-asteroids/docs/collecting-asteroids-3.template.html @@ -27,33 +27,33 @@
- +
- +

Собери Астероиды

Загрузи трюмы кораблей!

-
10
-
11
-
12
+
10
+
11
+
12
-
3
-
2
-
2
-
4
-
4
-
2
-
4
-
6
-
6
-
4
-
2
-
6
+
3
+
2
+
2
+
4
+
4
+
2
+
4
+
6
+
6
+
4
+
2
+
6
@@ -62,33 +62,33 @@
- +
- +

Собери Астероиды

Загрузи трюмы кораблей!

-
12
-
12
-
18
+
12
+
12
+
18
-
4
-
5
-
3
-
6
-
2
-
7
-
4
-
3
-
5
-
2
-
6
-
4
+
4
+
5
+
3
+
6
+
2
+
7
+
4
+
3
+
5
+
2
+
6
+
4
@@ -97,33 +97,33 @@
- +
- +

Собери Астероиды

Загрузи трюмы кораблей!

-
7
-
5
-
6
+
7
+
5
+
6
-
4
-
8
-
3
-
9
-
10
-
2
-
12
-
2
-
4
-
10
-
8
-
3
+
4
+
8
+
3
+
9
+
10
+
2
+
12
+
2
+
4
+
10
+
8
+
3
@@ -132,33 +132,33 @@
- +
- +

Собери Астероиды

Загрузи трюмы кораблей!

-
15
-
10
-
25
+
15
+
10
+
25
-
5
-
3
-
5
-
5
-
3
-
5
-
5
-
2
-
5
-
5
-
2
-
5
+
5
+
3
+
5
+
5
+
3
+
5
+
5
+
2
+
5
+
5
+
2
+
5
@@ -167,32 +167,32 @@
- +
- +

Собери Астероиды

Загрузи трюмы кораблей!

-
13
-
11
-
15
+
13
+
11
+
15
-
2
-
1
-
4
-
2
-
3
-
2
-
6
-
5
-
6
-
6
-
4
+
2
+
1
+
4
+
2
+
3
+
2
+
6
+
5
+
6
+
6
+
4
@@ -201,35 +201,35 @@
- +
- +

Собери Астероиды

Загрузи трюмы кораблей!

-
18
-
18
-
14
+
18
+
18
+
14
-
2
-
7
-
6
-
6
-
1
-
4
-
6
-
3
-
2
-
4
-
5
-
4
-
2
-
6
+
2
+
7
+
6
+
6
+
1
+
4
+
6
+
3
+
2
+
4
+
5
+
4
+
2
+
6
@@ -238,33 +238,33 @@
- +
- +

Собери Астероиды

Загрузи трюмы кораблей!

-
25
-
21
-
32
+
25
+
21
+
32
-
4
-
8
-
2
-
8
-
11
-
3
-
7
-
10
-
6
-
4
-
13
-
5
+
4
+
8
+
2
+
8
+
11
+
3
+
7
+
10
+
6
+
4
+
13
+
5
@@ -273,34 +273,34 @@
- +
- +

Собери Астероиды

Загрузи трюмы кораблей!

-
26
-
28
-
30
+
26
+
28
+
30
-
8
-
10
-
7
-
5
-
8
-
3
-
5
-
10
-
5
-
7
-
10
-
7
-
6
+
8
+
10
+
7
+
5
+
8
+
3
+
5
+
10
+
5
+
7
+
10
+
7
+
6
@@ -309,36 +309,36 @@
- +
- +

Собери Астероиды

Загрузи трюмы кораблей!

-
26
-
18
-
34
+
26
+
18
+
34
-
6
-
3
-
2
-
7
-
7
-
5
-
5
-
9
-
2
-
8
-
4
-
6
-
3
-
12
-
5
+
6
+
3
+
2
+
7
+
7
+
5
+
5
+
9
+
2
+
8
+
4
+
6
+
3
+
12
+
5
diff --git a/tasks/collecting-asteroids/scripts/generate.mjs b/tasks/collecting-asteroids/scripts/generate.mjs index f5af939..09d8026 100644 --- a/tasks/collecting-asteroids/scripts/generate.mjs +++ b/tasks/collecting-asteroids/scripts/generate.mjs @@ -24,6 +24,7 @@ import { readFileSync, writeFileSync, existsSync } from 'fs'; import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; +import { postGenerate } from '../../../src/scripts/post-generate.mjs'; const __dirname = dirname(fileURLToPath(import.meta.url)); const docsDir = join(__dirname, '..', 'docs'); @@ -53,6 +54,7 @@ if (existsSync(dataPath)) { writeFileSync(outputPath, html); console.log(`Generated: ${outputPath}`); +await postGenerate(outputPath); function applyData(html, data) { if (!data.pages) return html; diff --git a/tasks/space-exploration/docs/space-demo.html b/tasks/space-exploration/docs/space-demo.html index f613e09..ad7c26b 100644 --- a/tasks/space-exploration/docs/space-demo.html +++ b/tasks/space-exploration/docs/space-demo.html @@ -31,7 +31,7 @@
- +
@@ -43,7 +43,7 @@