Rename project to SSAU Generator and restructure content

- Move projects into content/<project>/{docx,pptx} with shared assets/
- Flatten config paths; output = project name in project folder
- Add pptx --init scaffold; remove docx exe build
- Add feature-complete content/example; ignore content/* except example
- Rewrite README and AGENTS.md
This commit is contained in:
2026-10-11 23:25:22 +04:00
parent 35611e2a91
commit bf7816ab7c
80 changed files with 1166 additions and 3432 deletions

115
pptx/src/scaffold.js Normal file
View File

@@ -0,0 +1,115 @@
"use strict";
const fs = require("fs");
const path = require("path");
const yaml = require("js-yaml");
const { DEFAULTS, deepMerge } = require("./config");
const { solidPng } = require("./png");
const CONFIG_HEADER = `# Конфигурация генератора SSAU Generator PPTX.
# Все относительные пути считаются от каталога этого файла.
# Любой ключ можно опустить — будет использовано значение по умолчанию.
`;
const SAMPLE = {
"slides/00-title.md": `# Название презентации
## Подзаголовок
`,
"slides/01-content.md": `# Первый слайд
- первый пункт списка;
- второй пункт списка:
- вложенный пункт;
Обычный абзац с **полужирным**, *курсивом* и ==выделением==.
`,
"slides/02-final.md": `# Спасибо за внимание
`,
};
// Цвета задников: [R, G, B].
const BRAND = [27, 135, 201];
const BACKGROUNDS = {
"title-bg.png": BRAND,
"content-bg.png": [255, 255, 255],
"final-bg.png": BRAND,
};
function isGeneratorDir(baseDir) {
const base = path.basename(baseDir).toLowerCase();
return base === "docx" || base === "pptx";
}
/**
* Раскладка проекта: имя проекта = имя папки проекта, общие ресурсы в
* content/<проект>/assets/, результат — content/<проект>/<проект>.pptx.
*/
function projectLayout(baseDir) {
const isSub = isGeneratorDir(baseDir);
const projectDir = isSub ? path.dirname(baseDir) : baseDir;
const projectName = path.basename(projectDir) || "project";
return {
projectDir,
projectName,
assetsRel: isSub ? "../assets" : "assets",
assetsDir: path.join(projectDir, "assets"),
outputRel: isSub ? `../${projectName}.pptx` : `${projectName}.pptx`,
};
}
function defaultConfigYaml(layout) {
const config = deepMerge(DEFAULTS, {
presentation: { output: layout.outputRel },
theme: {
backgrounds: {
title: `${layout.assetsRel}/title-bg.png`,
content: `${layout.assetsRel}/content-bg.png`,
final: `${layout.assetsRel}/final-bg.png`,
},
},
content: { dir: "." },
slides: [
{ type: "title", file: "slides/00-title.md" },
{ type: "content", file: "slides/01-content.md" },
{ type: "final", file: "slides/02-final.md" },
],
});
return (
CONFIG_HEADER +
yaml.dump(config, { noRefs: true, lineWidth: -1, sortKeys: false })
);
}
/**
* Создаёт config.yaml, примеры слайдов и общие ресурсы проекта
* (content/<проект>/assets/), если их ещё нет.
*/
async function ensureScaffold(baseDir) {
const layout = projectLayout(baseDir);
const created = [];
const write = (target, content) => {
if (fs.existsSync(target)) return;
fs.mkdirSync(path.dirname(target), { recursive: true });
if (content && typeof content === "object" && content.binary) {
fs.writeFileSync(target, content.binary);
} else {
fs.writeFileSync(target, content, "utf8");
}
created.push(target);
};
write(path.join(baseDir, "config.yaml"), defaultConfigYaml(layout));
for (const [name, content] of Object.entries(SAMPLE)) {
write(path.join(baseDir, name), content);
}
for (const [name, color] of Object.entries(BACKGROUNDS)) {
write(path.join(layout.assetsDir, name), { binary: solidPng(1920, 1080, color) });
}
return created;
}
module.exports = { projectLayout, defaultConfigYaml, ensureScaffold, SAMPLE };