mirror of
https://github.com/StepanovPlaton/SSAUGenerator.git
synced 2026-11-01 10:49:08 +04:00
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:
127
pptx/AGENTS.md
127
pptx/AGENTS.md
@@ -1,66 +1,94 @@
|
||||
# AGENTS.md — генератор lab-gen-pptx
|
||||
# AGENTS.md — работа с презентацией PPTX
|
||||
|
||||
`lab-gen-pptx` собирает презентацию **PPTX** из обычных **Markdown**-файлов по
|
||||
шаблону Самарского университета. Поведение задаётся файлом `config.yaml`.
|
||||
Генератор `pptx/` собирает презентацию **PPTX** из обычных **Markdown**-файлов по
|
||||
шаблону Самарского университета. Этот файл — инструкция агенту по созданию и
|
||||
правке конкретной презентации. Поведение задаётся `config.yaml` проекта, менять код
|
||||
генератора не нужно.
|
||||
|
||||
## 1. Требования
|
||||
|
||||
- Node.js 18+.
|
||||
- Node.js 18+, зависимости ставятся командой `npm install` в папке `pptx/`.
|
||||
- Зависимости: `pptxgenjs`, `js-yaml`.
|
||||
- Для директив `::: image` с файлами `.puml` — Java 11+ (берётся из
|
||||
`JAVA_HOME`, иначе `java` из PATH) и файл `plantuml.jar` (см. `config.plantuml`).
|
||||
- Для директив `::: image` с файлами `.puml` — Java 11+ (из `JAVA_HOME`, иначе
|
||||
`java` из PATH) и файл `plantuml.jar` (см. `config.plantuml`).
|
||||
|
||||
## 2. Состав
|
||||
## 2. Раскладка проекта
|
||||
|
||||
Проект лежит в `content/<проект>/pptx/` рядом с `config.yaml`:
|
||||
|
||||
```
|
||||
config.yaml # настройки (тема, тексты, таблицы, список слайдов)
|
||||
content/
|
||||
slides/*.md # по одному Markdown-файлу на слайд
|
||||
assets/ # задники (title-bg.jpeg, content-bg.jpg, final-bg.jpeg),
|
||||
# готовые PNG/JPEG и исходники PlantUML (*.puml)
|
||||
presentation.pptx # результат сборки (ключ presentation.output)
|
||||
.plantuml-cache/ # кэш отрендеренных PlantUML (ключ plantuml.cacheDir)
|
||||
src/ # cli.js, config.js, parse.js, render.js, plantuml.js, build.js
|
||||
content/<проект>/
|
||||
├── assets/ # общие ресурсы проекта (общие с docx)
|
||||
│ ├── title-bg.jpeg # задники
|
||||
│ ├── content-bg.jpg
|
||||
│ ├── final-bg.jpeg
|
||||
│ └── images/ # готовые PNG/JPEG и исходники .puml
|
||||
└── pptx/
|
||||
├── config.yaml
|
||||
└── slides/
|
||||
├── 00-title.md
|
||||
└── 01-*.md
|
||||
```
|
||||
|
||||
Все относительные пути считаются **от каталога `config.yaml`**.
|
||||
Все относительные пути считаются **от каталога `config.yaml`**. Общие с документом
|
||||
ресурсы указывайте через `../assets` (например,
|
||||
`backgrounds.title: ../assets/title-bg.jpeg`). Каталог контента — ключ
|
||||
`content.dir` (по умолчанию `.` — папка самого `config.yaml`).
|
||||
|
||||
Общий `plantuml.jar` монорепозитория лежит в корне, в `tools/plantuml.jar`
|
||||
(`plantuml.jar: ../tools/plantuml.jar`). Для автономного дистрибутива положите
|
||||
`tools/plantuml.jar` рядом с `config.yaml` и задайте путь `tools/plantuml.jar`.
|
||||
Презентация собирается в папку проекта и называется по имени проекта:
|
||||
`content/<проект>/<проект>.pptx`. Значение ключа `presentation.output` при такой
|
||||
раскладке — `../<проект>.pptx`.
|
||||
|
||||
Структуру проекта создавайте командой `--init`: она формирует `config.yaml`,
|
||||
примеры слайдов и общий каталог `content/<проект>/assets/` (задники), который
|
||||
используется и генератором `docx`.
|
||||
|
||||
Готовый пример со всеми возможностями — `content/example/pptx/`.
|
||||
|
||||
## 3. Командная строка
|
||||
|
||||
Из корня репозитория:
|
||||
|
||||
```
|
||||
lab-gen-pptx [config.yaml] [опции]
|
||||
node pptx/src/cli.js [config.yaml] [опции]
|
||||
|
||||
-c, --config <file> путь к config.yaml (по умолчанию config.yaml)
|
||||
-o, --output <file> переопределить имя выходного файла
|
||||
--check только проверить конфиг и ссылки
|
||||
--init создать шаблон config.yaml/slides/assets и выйти
|
||||
-v, --verbose подробный вывод
|
||||
-h, --help справка
|
||||
--version версия
|
||||
```
|
||||
|
||||
Типичный цикл:
|
||||
|
||||
```powershell
|
||||
npm run check
|
||||
npm run generate
|
||||
node pptx/src/cli.js -c content/<проект>/pptx/config.yaml --init # создать структуру проекта
|
||||
node pptx/src/cli.js -c content/<проект>/pptx/config.yaml --check # проверить
|
||||
node pptx/src/cli.js -c content/<проект>/pptx/config.yaml # собрать
|
||||
```
|
||||
|
||||
После любого изменения исходников (`config.yaml`, слайдов, ресурсов `assets/`)
|
||||
**пересоберите презентацию**, чтобы результат соответствовал исходникам.
|
||||
|
||||
Если `config.yaml` не найден, инструмент сам создаёт шаблон (`config.yaml`,
|
||||
примеры слайдов и общий `assets/`) и завершается.
|
||||
|
||||
## 4. Схема `config.yaml`
|
||||
|
||||
| Раздел | Назначение |
|
||||
|---|---|
|
||||
| `presentation` | `output`, `author`, `track` |
|
||||
| `theme` | `width`/`height` (дюймы), `font`, `colors`, `backgrounds`, `slideNumber` |
|
||||
| `text` | макеты текста для типов слайдов `titleSlide`, `section`, `content`, `final` |
|
||||
| `text` | макеты текста для типов `titleSlide`, `section`, `content`, `final` |
|
||||
| `table` | размер шрифта, цвета шапки/ячеек/границ |
|
||||
| `callout` | плашка-вывод: заливка, рамка, цвета и размеры шрифтов |
|
||||
| `metrics` | карточки-метрики: заливка, цвета, размеры значения и подписи |
|
||||
| `flow` | нативная схема-цепочка: заливка, рамка, стрелки, размер |
|
||||
| `flow` | схема-цепочка: заливка, рамка, стрелки, размер |
|
||||
| `chart` | палитра, размер шрифта, легенда, высота диаграммы |
|
||||
| `plantuml` | рендеринг `*.puml` (см. п. 5) |
|
||||
| `content.dir` | каталог контента |
|
||||
| `content.dir` | каталог контента (по умолчанию `.`) |
|
||||
| `slides` | список слайдов: `{ type, file }` |
|
||||
|
||||
Типы слайдов: `title`, `section`, `content`, `final`. Каждому типу соответствует
|
||||
@@ -93,7 +121,7 @@ npm run generate
|
||||
| 1 | 2 |
|
||||
:::
|
||||
|
||||
::: image assets/pic.png
|
||||
::: image ../assets/images/pic.png
|
||||
подпись под рисунком
|
||||
:::
|
||||
|
||||
@@ -127,9 +155,9 @@ npm run generate
|
||||
```
|
||||
|
||||
Параметр `side=left|right` ставит плашку в левую или правую колонку слайда.
|
||||
Если у слайда есть блоки с `side`, они верстаются в две колонки (левая/правая),
|
||||
а остальные блоки — как обычно. Типовое сочетание: `::: image ... side=left`
|
||||
и `::: callout ... side=right`.
|
||||
Если у слайда есть блоки с `side`, они верстаются в две колонки, а остальные
|
||||
блоки — как обычно. Не смешивайте на одном слайде обычный текст и колонки
|
||||
(`side`) — они накладываются.
|
||||
|
||||
### Директива `::: metrics`
|
||||
|
||||
@@ -145,8 +173,7 @@ npm run generate
|
||||
|
||||
### Директива `::: flow`
|
||||
|
||||
Нативная схема-цепочка из скруглённых блоков со стрелками. Шаги разделяются
|
||||
`->` (или `→`), можно писать по одному на строку:
|
||||
Схема-цепочка из скруглённых блоков со стрелками. Шаги разделяются `->` (или `→`):
|
||||
|
||||
```md
|
||||
::: flow
|
||||
@@ -154,14 +181,14 @@ Sprint Planning -> Спринт -> Sprint Review -> Ретроспектива
|
||||
:::
|
||||
```
|
||||
|
||||
Если ширина блока становится малой, схема автоматически разворачивается
|
||||
Если блоки становятся слишком узкими, схема автоматически разворачивается
|
||||
вертикально.
|
||||
|
||||
### Директива `::: chart <тип> "Заголовок"`
|
||||
|
||||
Диаграмма на основе GFM-таблицы: первая колонка — категории, остальные —
|
||||
числовые ряды (заголовки колонок = имена рядов). Поддерживаемые типы:
|
||||
`bar`, `bar3d`, `line`, `pie`, `doughnut`, `area`, `radar`, `scatter`.
|
||||
числовые ряды (заголовки колонок = имена рядов). Типы: `bar`, `bar3d`, `line`,
|
||||
`pie`, `doughnut`, `area`, `radar`, `scatter`.
|
||||
|
||||
```md
|
||||
::: chart doughnut "Новинки, которые не выживают"
|
||||
@@ -174,38 +201,38 @@ Sprint Planning -> Спринт -> Sprint Review -> Ретроспектива
|
||||
|
||||
### Директива `::: image` и PlantUML
|
||||
|
||||
`file` — относительно `content.dir` (`content/`). Если файл оканчивается на
|
||||
`.puml` (`.plantuml`, `.pu`, `.iuml`), он рендерится в PNG через `plantuml.jar`
|
||||
(см. `config.plantuml`) и вставляется как обычный рисунок:
|
||||
`file` — относительно `content.dir`. Если файл оканчивается на `.puml`
|
||||
(`.plantuml`, `.pu`, `.iuml`), он рендерится в PNG через `plantuml.jar` и
|
||||
вставляется как обычный рисунок:
|
||||
|
||||
```md
|
||||
::: image assets/scrum-cycle.puml height=3.2
|
||||
::: image ../assets/images/scrum-cycle.puml height=3.2
|
||||
Цикл Scrum: от Product Backlog до масштабирования товара
|
||||
:::
|
||||
```
|
||||
|
||||
Подпись (строка под директивой) выводится под рисунком курсивом. Растровые
|
||||
PNG/JPEG вставляются как есть. Необязательный параметр `height=<дюймы>`
|
||||
(`h=`) ограничивает высоту рисунка, чтобы уместить под ним другие блоки;
|
||||
пропорции сохраняются, рисунок центрируется. Параметр `side=left|right`
|
||||
ставит рисунок в колонку (см. `::: callout`).
|
||||
PNG/JPEG вставляются как есть. Параметр `height=<дюймы>` (`h=`) ограничивает
|
||||
высоту рисунка (пропорции сохраняются, рисунок центрируется). `side=left|right`
|
||||
ставит рисунок в колонку.
|
||||
|
||||
## 6. Задники
|
||||
|
||||
Изображения-задники извлечены из `presentation_16.pptx` в
|
||||
`content/assets/` (`title-bg.jpeg`, `content-bg.jpg`, `final-bg.jpeg`). При замене
|
||||
шаблона положите новые файлы и обновите пути в `theme.backgrounds`.
|
||||
Задники лежат в общем каталоге ресурсов проекта
|
||||
(`content/<проект>/assets/`: `title-bg.jpeg`, `content-bg.jpg`, `final-bg.jpeg`).
|
||||
При замене шаблона положите новые файлы и обновите пути в `theme.backgrounds`.
|
||||
|
||||
## 7. Шрифт
|
||||
|
||||
В шаблоне используется фирменный `Elektra Text Pro`, который обычно не установлен
|
||||
в системе (иначе Word подставляет случайный шрифт). Поэтому по умолчанию задан
|
||||
`Segoe UI` — близкий современный sans. Альтернативы: `Calibri`, `Candara`,
|
||||
`Corbel`, `Noto Sans`, `Arial`. Смените `theme.font` при необходимости.
|
||||
В исходном шаблоне используется фирменный `Elektra Text Pro`, который обычно не
|
||||
установлен в системе. Поэтому по умолчанию задан `Segoe UI` — близкий современный
|
||||
sans. Альтернативы: `Calibri`, `Candara`, `Corbel`, `Noto Sans`, `Arial`.
|
||||
Смените `theme.font` при необходимости.
|
||||
|
||||
## 8. Проверка результата
|
||||
|
||||
Презентацию можно отрендерить для визуальной проверки через LibreOffice:
|
||||
`--check` проверяет конфиг и наличие слайдов/ресурсов. Презентацию можно
|
||||
отрендерить для визуальной проверки через LibreOffice:
|
||||
|
||||
```powershell
|
||||
soffice --headless --convert-to pdf --outdir . presentation.pptx
|
||||
|
||||
130
pptx/config.yaml
130
pptx/config.yaml
@@ -1,130 +0,0 @@
|
||||
# Конфигурация генератора презентаций lab-gen-pptx.
|
||||
# Шаблон: Самарский университет, presentation_16.pptx.
|
||||
# Все относительные пути считаются от каталога этого файла.
|
||||
|
||||
presentation:
|
||||
output: presentation.pptx
|
||||
author: "Степанов П.А."
|
||||
track: "ДОП 11"
|
||||
group: "6403-020302D"
|
||||
|
||||
theme:
|
||||
width: 13.333
|
||||
height: 7.5
|
||||
# Шрифт презентации. Elektra Text Pro (фирменный) не установлен в системе,
|
||||
# поэтому используется близкий современный sans. Замените при необходимости.
|
||||
font: "Segoe UI"
|
||||
colors:
|
||||
white: "FFFFFF"
|
||||
ink: "1F2933"
|
||||
brand: "1B87C9"
|
||||
muted: "6B7280"
|
||||
accent: "0E6BA8"
|
||||
tint: "EAF4FB"
|
||||
line: "BFD3DD"
|
||||
backgrounds:
|
||||
title: content/assets/title-bg.jpeg
|
||||
content: content/assets/content-bg.jpg
|
||||
final: content/assets/final-bg.jpeg
|
||||
slideNumber:
|
||||
show: true
|
||||
x: 12.17
|
||||
y: 6.86
|
||||
w: 0.5
|
||||
h: 0.42
|
||||
fontSize: 14
|
||||
# color — для тёмных фонов (разделитель), contentColor — для белых (контент)
|
||||
color: "FFFFFF"
|
||||
contentColor: "FFFFFF"
|
||||
bold: true
|
||||
align: "center"
|
||||
|
||||
text:
|
||||
titleSlide:
|
||||
title: { x: 6.7, y: 1.85, w: 5.95, h: 1.9, fontSize: 28, align: center, color: white }
|
||||
subtitle: { x: 6.7, y: 3.75, w: 5.95, h: 0.7, fontSize: 16, align: center, color: white }
|
||||
body: { x: 6.7, y: 4.65, w: 5.95, h: 1.7, fontSize: 14, align: center, color: white }
|
||||
section:
|
||||
title: { x: 6.4, y: 2.55, w: 6.35, h: 2.4, fontSize: 26, bold: true, align: center, color: white }
|
||||
content:
|
||||
title: { x: 0.6, y: 0.20, w: 12.15, h: 0.66, fontSize: 24, align: center, color: white, valign: middle }
|
||||
body: { x: 0.75, y: 0.98, w: 11.85, h: 5.7, fontSize: 18, align: left, color: ink, valign: top }
|
||||
bulletSize: 18
|
||||
subBulletSize: 16
|
||||
final:
|
||||
title: { x: 6.7, y: 2.85, w: 5.95, h: 1.6, fontSize: 28, bold: true, align: center, color: white }
|
||||
body: { x: 6.7, y: 4.65, w: 5.95, h: 1.7, fontSize: 14, align: center, color: white }
|
||||
|
||||
table:
|
||||
fontSize: 14
|
||||
headerFill: "1B87C9"
|
||||
headerColor: "FFFFFF"
|
||||
cellColor: "1F2933"
|
||||
borderColor: "BFD3DD"
|
||||
|
||||
callout:
|
||||
fill: "EAF4FB"
|
||||
border: "1B87C9"
|
||||
titleColor: "0E6BA8"
|
||||
textColor: "1F2933"
|
||||
titleFontSize: 18
|
||||
fontSize: 15
|
||||
lineWidth: 1.25
|
||||
|
||||
metrics:
|
||||
fill: "1B87C9"
|
||||
valueColor: "FFFFFF"
|
||||
labelColor: "EAF4FB"
|
||||
valueFontSize: 30
|
||||
labelFontSize: 13
|
||||
gap: 0.2
|
||||
height: 1.25
|
||||
|
||||
flow:
|
||||
fill: "EAF4FB"
|
||||
border: "1B87C9"
|
||||
textColor: "1F2933"
|
||||
arrowColor: "1B87C9"
|
||||
fontSize: 13
|
||||
height: 1.0
|
||||
gap: 0.4
|
||||
|
||||
chart:
|
||||
palette: ["1B87C9", "7FB2D9", "0E6BA8", "BFD3DD"]
|
||||
fontSize: 12
|
||||
legend: true
|
||||
height: 3.4
|
||||
|
||||
# Рендеринг PlantUML (::: image assets/schema.puml).
|
||||
# Требуется Java 11+ (берётся из JAVA_HOME, иначе java из PATH) и plantuml.jar.
|
||||
# Общий jar монорепозитория лежит в корне: tools/plantuml.jar.
|
||||
plantuml:
|
||||
enabled: true
|
||||
java: auto
|
||||
jar: ../tools/plantuml.jar
|
||||
format: png
|
||||
charset: UTF-8
|
||||
cache: true
|
||||
cacheDir: .plantuml-cache
|
||||
timeoutMs: 30000
|
||||
|
||||
content:
|
||||
dir: content
|
||||
|
||||
slides:
|
||||
- { type: title, file: slides/00-title.md }
|
||||
- { type: content, file: slides/01-agenda.md }
|
||||
- { type: content, file: slides/01-task.md }
|
||||
- { type: content, file: slides/02-why-scrum.md }
|
||||
- { type: content, file: slides/03-roles.md }
|
||||
- { type: content, file: slides/04-events.md }
|
||||
- { type: content, file: slides/12-scrum-cycle.md }
|
||||
- { type: content, file: slides/05-launch.md }
|
||||
- { type: section, file: slides/06-spheres.md }
|
||||
- { type: content, file: slides/07a-spheres-1-2.md }
|
||||
- { type: content, file: slides/07b-spheres-3-4.md }
|
||||
- { type: content, file: slides/07c-spheres-5-6.md }
|
||||
- { type: content, file: slides/07d-spheres-7-8.md }
|
||||
- { type: content, file: slides/07e-spheres-9-10.md }
|
||||
- { type: content, file: slides/10-conclusion.md }
|
||||
- { type: final, file: slides/11-final.md }
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 377 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 365 KiB |
@@ -1,24 +0,0 @@
|
||||
@startuml
|
||||
skinparam defaultFontName "Times New Roman"
|
||||
skinparam defaultFontSize 13
|
||||
skinparam shadowing false
|
||||
skinparam ArrowColor #1B87C9
|
||||
skinparam rectangleBorderColor #1B87C9
|
||||
skinparam rectangleBackgroundColor #EAF4FB
|
||||
|
||||
rectangle "Product Backlog — требования к товару" as PB
|
||||
rectangle "Sprint Planning" as SP
|
||||
rectangle "Спринт 1–4 недели" as S
|
||||
rectangle "Increment — пробный продукт" as I
|
||||
rectangle "Sprint Review — обратная связь рынка" as R
|
||||
rectangle "Sprint Retrospective" as RET
|
||||
rectangle "Масштабирование товара" as M
|
||||
|
||||
PB --> SP
|
||||
SP --> S
|
||||
S --> I
|
||||
I --> R
|
||||
R --> RET
|
||||
RET --> SP : следующий спринт
|
||||
R --> M : спрос подтверждён
|
||||
@enduml
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 187 KiB |
@@ -1,13 +0,0 @@
|
||||
# Современные проектные технологии
|
||||
|
||||
## Метод выведения нового товара на рынок: Agile / Scrum
|
||||
|
||||
Степанов П.А., группа 6403-020302D
|
||||
|
||||
Слушатель программы профессиональной переподготовки «Современные проектные технологии»
|
||||
|
||||
ИОТ-трек «ДОП 11»
|
||||
|
||||
::: notes
|
||||
Добрый день, уважаемые члены итоговой аттестационной комиссии. Меня зовут Степанов П.А., я слушатель программы профессиональной переподготовки «Современные проектные технологии», ИОТ-трек «ДОП 11», группа 6403-020302D. Разрешите представить решение итогового задания по теме «Современные проектные технологии» — метод выведения нового товара на рынок.
|
||||
:::
|
||||
@@ -1,12 +0,0 @@
|
||||
# План доклада
|
||||
|
||||
- ==Метод== выведения нового товара: Agile / Scrum
|
||||
- Почему выбран именно Scrum
|
||||
- Роли, артефакты и события методологии
|
||||
- Как спринты проверяют спрос на рынке
|
||||
- Применение в ==десяти сферах== деятельности
|
||||
- Выводы и практический результат
|
||||
|
||||
::: notes
|
||||
Структура доклада. Сначала я обосную выбор метода — Agile и Scrum. Затем покажу, как устроена методология: роли, артефакты и события. Отдельно объясню, как спринты позволяют проверять спрос на рынке. После этого докажу универсальность метода на десяти сферах деятельности: от производства до банковских услуг. В завершение приведу выводы и практический результат.
|
||||
:::
|
||||
@@ -1,19 +0,0 @@
|
||||
# Постановка задачи
|
||||
|
||||
- Выбрать метод выведения нового товара на рынок
|
||||
- Обосновать его применимость в ==десяти сферах== деятельности
|
||||
- Показать, как метод работает в каждой сфере
|
||||
|
||||
::: metrics
|
||||
70% | новых товаров уходят с рынка
|
||||
10 | сфер применения метода
|
||||
4 | недели — максимальная длина спринта
|
||||
:::
|
||||
|
||||
::: callout "Проблема и решение"
|
||||
Запуск новинки — это **высокий риск**. Нужен управляемый и повторяемый процесс вывода товара — таким процессом является **Agile / Scrum**.
|
||||
:::
|
||||
|
||||
::: notes
|
||||
Задача итогового задания — выбрать метод выведения нового товара на рынок и обосновать его в десяти сферах: от производства до банковских услуг. Запуск новинки всегда сопряжён с риском: до семи из десяти новых товаров не выживают на рынке. Поэтому нужен управляемый и повторяемый процесс, и в качестве такого метода я выбрал Agile и его самую популярную методологию — Scrum.
|
||||
:::
|
||||
@@ -1,18 +0,0 @@
|
||||
# Почему выбран Agile / Scrum
|
||||
|
||||
- Agile — гибкий подход к управлению проектами, закреплён в ==манифесте 2001 года==
|
||||
- Scrum — самая распространённая Agile-методология в мире
|
||||
- Работа идёт короткими итерациями — *спринтами* от одной до четырёх недель
|
||||
- Каждый спринт даёт готовый прирост продукта — ==инкремент==
|
||||
- Обратная связь с рынком собирается на каждом шаге, а не в конце проекта
|
||||
|
||||
::: chart doughnut "Новинки, которые не выживают на рынке"
|
||||
| Итог | Доля, % |
|
||||
|---|---|
|
||||
| Успешные новинки | 30 |
|
||||
| Уходят с рынка | 70 |
|
||||
:::
|
||||
|
||||
::: notes
|
||||
Почему именно Agile и Scrum. Agile — это гибкий подход к управлению проектами, закреплённый в манифесте 2001 года. Scrum — самая распространённая Agile-методология. Работа разбивается на короткие спринты длиной от одной до четырёх недель. Каждый спринт завершается готовым приростом продукта. Благодаря этому обратная связь с рынком собирается постоянно, а не только в конце. Именно поэтому гибкий подход снижает риск: как показывают оценки, до семи из десяти новинок не выживают при традиционном запуске.
|
||||
:::
|
||||
@@ -1,21 +0,0 @@
|
||||
# Scrum: роли и артефакты
|
||||
|
||||
- **Product Owner** — отвечает за ценность товара и ведёт Product Backlog
|
||||
- **Scrum Master** — убирает препятствия и следит за соблюдением процесса
|
||||
- **Команда разработки** — от пяти до девяти специалистов, создающих продукт
|
||||
|
||||
::: table
|
||||
| Артефакт Scrum | Назначение |
|
||||
|---|---|
|
||||
| Product Backlog | все требования к новому товару |
|
||||
| Sprint Backlog | план работ текущего спринта |
|
||||
| Increment | готовый к показу прирост продукта |
|
||||
:::
|
||||
|
||||
::: callout "Прозрачность"
|
||||
Все артефакты видны команде и заказчику, а критерии готовности зафиксированы в ==Definition of Done==.
|
||||
:::
|
||||
|
||||
::: notes
|
||||
В Scrum три роли. Product Owner отвечает за ценность товара и ведёт Product Backlog — список всех требований. Scrum Master убирает препятствия и следит за соблюдением процесса. Команда разработки из пяти–девяти специалистов создаёт продукт. Три артефакта: Product Backlog со всеми требованиями, Sprint Backlog с планом текущего спринта и Increment — готовый прирост продукта. Единое понимание готовности фиксируется в критериях Definition of Done.
|
||||
:::
|
||||
@@ -1,18 +0,0 @@
|
||||
# Scrum: события спринта
|
||||
|
||||
::: flow
|
||||
Sprint Planning -> Спринт -> Sprint Review -> Ретроспектива
|
||||
:::
|
||||
|
||||
- **Sprint Planning** — планируем, что войдёт в спринт
|
||||
- **Daily Scrum** — пятнадцать минут на синхронизацию команды
|
||||
- **Sprint Review** — показываем прирост заказчику и рынку
|
||||
- **Sprint Retrospective** — улучшаем процесс работы команды
|
||||
|
||||
::: callout "Результат спринта"
|
||||
Каждый спринт заканчивается рабочим ==инкрементом== — кандидатом на рынок.
|
||||
:::
|
||||
|
||||
::: notes
|
||||
Спринт состоит из четырёх событий. На Sprint Planning команда решает, что войдёт в спринт. Daily Scrum — пятнадцатиминутная синхронизация каждый день. На Sprint Review прирост показывают заказчику и рынку. На Sprint Retrospective команда улучшает свой процесс. Каждый спринт заканчивается рабочим инкрементом, который можно показать рынку. Далее цикл повторяется: планирование, спринт, демонстрация, ретроспектива.
|
||||
:::
|
||||
@@ -1,18 +0,0 @@
|
||||
# Как Scrum выводит товар на рынок
|
||||
|
||||
::: flow
|
||||
Спринт 0: исследование -> MVP -> Пилот и метрики -> Масштабирование
|
||||
:::
|
||||
|
||||
- **Спринт 0** — исследование рынка, выбор сегмента, формирование Product Backlog
|
||||
- **Спринты 1–2** — создание MVP: минимальной версии товара для первых клиентов
|
||||
- **Спринты 3–4** — пилот на ограниченной аудитории и сбор метрик
|
||||
- **Спринт 5 и далее** — доработка по обратной связи и масштабирование
|
||||
|
||||
::: callout "Итог"
|
||||
Рынок проверяется ==дешёвым инкрементом==, а не дорогим запуском «с нуля».
|
||||
:::
|
||||
|
||||
::: notes
|
||||
Как Scrum выводит товар на рынок. Спринт ноль — исследование рынка и формирование списка требований. Спринты один и два — создание минимального продукта для первых клиентов. Спринты три и четыре — пилот на ограниченной аудитории и сбор метрик. Дальше — доработка по обратной связи и масштабирование. Демонстрация реальным клиентам позволяет проверить спрос до крупных вложений. Итог: рынок проверяется дешёвым инкрементом, а не дорогим запуском с нуля.
|
||||
:::
|
||||
@@ -1,7 +0,0 @@
|
||||
# Agile / Scrum в десяти сферах деятельности
|
||||
|
||||
Обоснование универсальности выбранного метода
|
||||
|
||||
::: notes
|
||||
Теперь покажу, что метод Agile и Scrum применим не только в IT, но и в десяти различных сферах деятельности.
|
||||
:::
|
||||
@@ -1,21 +0,0 @@
|
||||
# Сферы 1–2: производство и связь
|
||||
|
||||
::: callout "1. Производство — новая линейка упаковки"
|
||||
- Новый товар: линейка упаковки для розницы.
|
||||
- Пилотный спринт: пробная партия на ==одной линии==.
|
||||
- Метрика Sprint Review: себестоимость, доля брака, скорость переналадки.
|
||||
- Решение: масштабирование на остальные линии по подтверждённому спросу.
|
||||
- Особенность: переналадка одной линии дешевле полного перезапуска.
|
||||
:::
|
||||
|
||||
::: callout "2. Связь — тариф «Умный дом»"
|
||||
- Новый товар: тариф «Умный дом» для абонентов.
|
||||
- Пилотный спринт: подключение ==100 абонентов==.
|
||||
- Метрика Sprint Review: число подключений, средний чек, отток.
|
||||
- Решение: доработка состава услуг и вывод тарифа на массовый рынок.
|
||||
- Особенность: пилот ограничен абонентской базой и не грозит массовым оттоком.
|
||||
:::
|
||||
|
||||
::: notes
|
||||
Первые две сферы. В производстве новая линейка упаковки запускается пробной партией на одной линии: замеряют себестоимость, долю брака и скорость переналадки. По результатам Sprint Review производство масштабируется на остальные линии. В связи тариф «Умный дом» тестируется на ста абонентах: анализируют подключения, средний чек и отток, после чего состав услуг уточняется и тариф выводится на массовый рынок. Общая логика: ограниченный пилот, замер метрики, затем масштабирование.
|
||||
:::
|
||||
@@ -1,21 +0,0 @@
|
||||
# Сферы 3–4: транспорт и строительство
|
||||
|
||||
::: callout "3. Транспорт — каршеринг мини-фургонов"
|
||||
- Новый товар: сервис каршеринга мини-фургонов.
|
||||
- Пилотный спринт: ==пять машин== в одном районе.
|
||||
- Метрика Sprint Review: загрузка, среднее время аренды, спрос.
|
||||
- Решение: расширение автопарка и запуск в новых районах.
|
||||
- Особенность: экономика сервиса проверяется до закупки техники.
|
||||
:::
|
||||
|
||||
::: callout "4. Строительство — модульные дома"
|
||||
- Новый товар: модульные дома для застройки.
|
||||
- Пилотный спринт: возведение ==одного пилотного модуля== и показ заказчикам.
|
||||
- Метрика Sprint Review: затраты на монтаж, сроки, замечания.
|
||||
- Решение: серийный модуль с учётом правок и тиражирование.
|
||||
- Особенность: типовой проект снижает стоимость серии.
|
||||
:::
|
||||
|
||||
::: notes
|
||||
Транспорт и строительство. В транспорте сервис каршеринга мини-фургонов стартует с пяти машин в одном районе: оценивают загрузку, среднее время аренды и спрос. Если экономика подтверждается, автопарк расширяется. В строительстве сначала возводится один пилотный модульный дом и показывается заказчикам: фиксируют затраты, сроки и замечания, после чего серийный модуль тиражируется. Итеративная проверка снижает риск ошибок в типовом проекте.
|
||||
:::
|
||||
@@ -1,21 +0,0 @@
|
||||
# Сферы 5–6: коммерция и юридические услуги
|
||||
|
||||
::: callout "5. Коммерция — подписка на наборы продуктов"
|
||||
- Новый товар: подписка на наборы продуктов.
|
||||
- Пилотный спринт: бокс-пилот на ==50 клиентах==.
|
||||
- Метрика Sprint Review: конверсия, средний чек, удержание.
|
||||
- Решение: доработка ассортимента и условий подписки.
|
||||
- Особенность: состав набора гибко меняется по спросу.
|
||||
:::
|
||||
|
||||
::: callout "6. Юридические услуги — пакет «юрфирма на аутсорсе»"
|
||||
- Новый товар: пакет «юрфирма на аутсорсе».
|
||||
- Пилотный спринт: пробный месяц для ==5 клиентов==.
|
||||
- Метрика Sprint Review: востребованность, трудозатраты, повторные обращения.
|
||||
- Решение: тарифы и состав пакета формируются по отзывам.
|
||||
- Особенность: расширение штата юристов только после подтверждения спроса.
|
||||
:::
|
||||
|
||||
::: notes
|
||||
Коммерция и юридические услуги. В коммерции подписка на продуктовые наборы запускается бокс-пилотом на пятидесяти клиентах: измеряют конверсию, средний чек и удержание, после чего уточняют ассортимент и условия. В юридических услугах пакет «юрфирма на аутсорсе» предлагается пяти клиентам на пробный месяц: оценивают востребованность и трудозатраты. Пилот позволяет выстроить услугу под реальные ожидания клиентов, не вкладываясь сразу в расширение штата.
|
||||
:::
|
||||
@@ -1,21 +0,0 @@
|
||||
# Сферы 7–8: организация праздников и бытовые услуги
|
||||
|
||||
::: callout "7. Организация праздников — онлайн-конструктор мероприятий"
|
||||
- Новый товар: онлайн-конструктор мероприятий.
|
||||
- Пилотный спринт: ==один сценарий== на пробной группе.
|
||||
- Метрика Sprint Review: вовлечённость, повторные заказы, средняя стоимость.
|
||||
- Решение: каталог успешных сценариев и автоматизация заказов.
|
||||
- Особенность: непопулярные сценарии не масштабируются и не продвигаются.
|
||||
:::
|
||||
|
||||
::: callout "8. Бытовые услуги — подписка на уборку"
|
||||
- Новый товар: подписка на уборку.
|
||||
- Пилотный спринт: ==20 подписчиков== в одном районе.
|
||||
- Метрика Sprint Review: удержание, число заказов, загрузка исполнителей.
|
||||
- Решение: стандарты качества и масштабирование на другие районы.
|
||||
- Особенность: логистика и качество отрабатываются в пилоте.
|
||||
:::
|
||||
|
||||
::: notes
|
||||
Организация праздников и бытовые услуги. Онлайн-конструктор мероприятий тестируется на одной пробной группе: смотрят вовлечённость, повторные заказы и среднюю стоимость. Успешный сценарий становится основой каталога. Подписка на уборку стартует с двадцати подписчиков в одном районе: измеряют удержание, число заказов и загрузку исполнителей, после чего налаживают логистику и стандарты качества и выходят на другие районы.
|
||||
:::
|
||||
@@ -1,21 +0,0 @@
|
||||
# Сферы 9–10: IT-услуги и банки
|
||||
|
||||
::: callout "9. IT-услуги — модуль SaaS-платформы"
|
||||
- Новый товар: новый модуль SaaS-платформы.
|
||||
- Пилотный спринт: ==бета на 10 клиентов==.
|
||||
- Метрика Sprint Review: использование, частота ошибок, NPS.
|
||||
- Решение: состав следующего релиза по обратной связи.
|
||||
- Особенность: регулярные релизы — естественный режим гибкой разработки.
|
||||
:::
|
||||
|
||||
::: callout "10. Банковские услуги — вклад «Цель»"
|
||||
- Новый товар: вклад «Цель» в мобильном приложении.
|
||||
- Пилотный спринт: тест на ==ограниченной группе пользователей==.
|
||||
- Метрика Sprint Review: доля открытий, средний срок, удержание.
|
||||
- Решение: полное внедрение после согласования с подразделениями банка.
|
||||
- Особенность: пилот проводится в контролируемых регулируемых условиях.
|
||||
:::
|
||||
|
||||
::: notes
|
||||
Последние две сферы. В IT новый модуль SaaS-платформы выходит в бету на десять клиентов: измеряют использование, частоту ошибок и NPS, а обратная связь определяет состав следующего релиза — это естественный режим гибкой разработки. В банковских услугах вклад «Цель» тестируется на ограниченной группе пользователей приложения: оценивают долю открытий, срок и удержание. Даже в регулируемой сфере пилот в контролируемых условиях позволяет безопасно проверить спрос до полного внедрения.
|
||||
:::
|
||||
@@ -1,17 +0,0 @@
|
||||
# Выводы
|
||||
|
||||
- Agile/Scrum — ==универсальный метод== выведения товара на рынок
|
||||
- Спринты заменяют один дорогой запуск серией дешёвых проверок
|
||||
- Роли, артефакты и события делают процесс прозрачным и управляемым
|
||||
- Метод работает везде, где есть клиент и обратная связь — от производства до банков
|
||||
- Результат — снижение риска и стоимости ошибки при запуске новинки
|
||||
|
||||
::: metrics
|
||||
10 | сфер применения
|
||||
30% | новинок выживают
|
||||
4 | недели — длина спринта
|
||||
:::
|
||||
|
||||
::: notes
|
||||
Таким образом, Agile и Scrum — универсальный метод выведения товара на рынок. Спринты заменяют один дорогой запуск серией дешёвых проверок. Роли, артефакты и события делают процесс прозрачным и управляемым. Метод работает везде, где есть клиент и обратная связь: от производства до банковских услуг. Результат — снижение риска и стоимости ошибки при запуске новинки.
|
||||
:::
|
||||
@@ -1,9 +0,0 @@
|
||||
# Спасибо за внимание
|
||||
|
||||
Степанов П.А., группа 6403-020302D
|
||||
|
||||
Слушатель программы профессиональной переподготовки «Современные проектные технологии», ИОТ-трек «ДОП 11»
|
||||
|
||||
::: notes
|
||||
На этом мой доклад закончен, спасибо за внимание. Готов ответить на ваши вопросы.
|
||||
:::
|
||||
@@ -1,17 +0,0 @@
|
||||
# Жизненный цикл Scrum
|
||||
|
||||
::: image assets/scrum-cycle.puml side=left
|
||||
Цикл Scrum: от Product Backlog до масштабирования товара
|
||||
:::
|
||||
|
||||
::: callout "Как замкнут цикл" side=right
|
||||
- Product Backlog фиксирует требования к товару.
|
||||
- Спринт длится 1–4 недели и завершается готовым ==инкрементом==.
|
||||
- Sprint Review показывает инкремент рынку, ретроспектива улучшает процесс.
|
||||
- Подтверждённый спрос ведёт к масштабированию, иначе цикл повторяется.
|
||||
- Каждый виток спирали снижает неопределённость и стоимость ошибки.
|
||||
:::
|
||||
|
||||
::: notes
|
||||
На этой схеме слева показан цикл Scrum. Из Product Backlog формируется план спринта. Спринт длится от одной до четырёх недель и завершается инкрементом — готовым пробным продуктом. На Sprint Review рынок даёт обратную связь, после чего команда проводит ретроспективу. Если спрос подтверждён, товар масштабируется; если нет — цикл повторяется с уточнёнными требованиями. Каждый виток снижает неопределённость.
|
||||
:::
|
||||
6
pptx/package-lock.json
generated
6
pptx/package-lock.json
generated
@@ -1,18 +1,18 @@
|
||||
{
|
||||
"name": "lab-gen-pptx",
|
||||
"name": "ssau-generator-pptx",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "lab-gen-pptx",
|
||||
"name": "ssau-generator-pptx",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"js-yaml": "^4.1.0",
|
||||
"pptxgenjs": "^3.12.0"
|
||||
},
|
||||
"bin": {
|
||||
"lab-gen-pptx": "src/cli.js"
|
||||
"ssau-generator-pptx": "src/cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"name": "lab-gen-pptx",
|
||||
"name": "ssau-generator-pptx",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"description": "Генератор презентаций PPTX из Markdown по шаблону Самарского университета",
|
||||
"description": "SSAU Generator PPTX — генератор презентаций PPTX из Markdown по шаблону Самарского университета",
|
||||
"bin": {
|
||||
"lab-gen-pptx": "src/cli.js"
|
||||
"ssau-generator-pptx": "src/cli.js"
|
||||
},
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const { build } = require("./build");
|
||||
const { ensureScaffold } = require("./scaffold");
|
||||
|
||||
function parseArgs(argv) {
|
||||
const options = { config: "config.yaml" };
|
||||
@@ -20,6 +22,9 @@ function parseArgs(argv) {
|
||||
case "--check":
|
||||
options.check = true;
|
||||
break;
|
||||
case "--init":
|
||||
options.init = true;
|
||||
break;
|
||||
case "--verbose":
|
||||
case "-v":
|
||||
options.verbose = true;
|
||||
@@ -43,15 +48,16 @@ function parseArgs(argv) {
|
||||
}
|
||||
|
||||
function printHelp() {
|
||||
console.log(`lab-gen-pptx — генератор презентаций PPTX из Markdown
|
||||
console.log(`SSAU Generator PPTX — генератор презентаций PPTX из Markdown
|
||||
|
||||
Использование:
|
||||
lab-gen-pptx [config.yaml] [опции]
|
||||
node pptx/src/cli.js [config.yaml] [опции]
|
||||
|
||||
Опции:
|
||||
-c, --config <file> путь к config.yaml (по умолчанию config.yaml)
|
||||
-o, --output <file> переопределить имя выходного файла
|
||||
--check только проверить конфигурацию и ссылки
|
||||
--init создать шаблон config.yaml/slides/assets и выйти
|
||||
-v, --verbose подробный вывод
|
||||
-h, --help показать справку
|
||||
--version показать версию
|
||||
@@ -68,6 +74,26 @@ async function main() {
|
||||
}
|
||||
|
||||
const configPath = path.resolve(options.config);
|
||||
|
||||
if (options.init || !fs.existsSync(configPath)) {
|
||||
const targetDir = path.dirname(configPath);
|
||||
try {
|
||||
const created = await ensureScaffold(targetDir);
|
||||
if (created.length) {
|
||||
console.log("Создан шаблон генератора:");
|
||||
created.forEach((file) => console.log(" " + path.relative(targetDir, file)));
|
||||
} else {
|
||||
console.log("Файлы уже существуют, ничего не создано.");
|
||||
}
|
||||
console.log("");
|
||||
console.log("Отредактируйте config.yaml и файлы в slides/, затем запустите генератор снова.");
|
||||
} catch (error) {
|
||||
console.error(error.message);
|
||||
process.exitCode = 1;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await build({ config: configPath, check: options.check, output: options.output });
|
||||
const { config, issues } = result;
|
||||
|
||||
@@ -24,9 +24,9 @@ const DEFAULTS = {
|
||||
line: "BFD3DD",
|
||||
},
|
||||
backgrounds: {
|
||||
title: "content/assets/title-bg.jpeg",
|
||||
content: "content/assets/content-bg.jpg",
|
||||
final: "content/assets/final-bg.jpeg",
|
||||
title: "assets/title-bg.jpeg",
|
||||
content: "assets/content-bg.jpg",
|
||||
final: "assets/final-bg.jpeg",
|
||||
},
|
||||
slideNumber: {
|
||||
show: true,
|
||||
@@ -110,7 +110,7 @@ const DEFAULTS = {
|
||||
timeoutMs: 30000,
|
||||
},
|
||||
content: {
|
||||
dir: "content",
|
||||
dir: ".",
|
||||
},
|
||||
slides: [],
|
||||
};
|
||||
|
||||
@@ -114,7 +114,7 @@ function cacheFileName(sourceBuffer, cfg) {
|
||||
}
|
||||
|
||||
async function renderCached(sourceBuffer, cfg) {
|
||||
const dir = cfg.cacheDir || path.join(os.tmpdir(), "lab-gen-pptx-plantuml");
|
||||
const dir = cfg.cacheDir || path.join(os.tmpdir(), "ssau-generator-pptx-plantuml");
|
||||
const target = path.join(dir, cacheFileName(sourceBuffer, cfg));
|
||||
|
||||
if (cfg.cache && fs.existsSync(target)) {
|
||||
|
||||
68
pptx/src/png.js
Normal file
68
pptx/src/png.js
Normal file
@@ -0,0 +1,68 @@
|
||||
"use strict";
|
||||
|
||||
const zlib = require("zlib");
|
||||
|
||||
const CRC_TABLE = (() => {
|
||||
const table = new Int32Array(256);
|
||||
for (let n = 0; n < 256; n += 1) {
|
||||
let c = n;
|
||||
for (let k = 0; k < 8; k += 1) {
|
||||
c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
|
||||
}
|
||||
table[n] = c;
|
||||
}
|
||||
return table;
|
||||
})();
|
||||
|
||||
function crc32(buffer) {
|
||||
let crc = -1;
|
||||
for (let i = 0; i < buffer.length; i += 1) {
|
||||
crc = CRC_TABLE[(crc ^ buffer[i]) & 0xff] ^ (crc >>> 8);
|
||||
}
|
||||
return (crc ^ -1) >>> 0;
|
||||
}
|
||||
|
||||
function chunk(type, data) {
|
||||
const length = Buffer.alloc(4);
|
||||
length.writeUInt32BE(data.length, 0);
|
||||
const typeBuf = Buffer.from(type, "ascii");
|
||||
const crc = Buffer.alloc(4);
|
||||
crc.writeUInt32BE(crc32(Buffer.concat([typeBuf, data])), 0);
|
||||
return Buffer.concat([length, typeBuf, data, crc]);
|
||||
}
|
||||
|
||||
/** Кодирует PNG, получая цвет пикселя через getPixel(x, y) -> [r,g,b]. */
|
||||
function encodePng(width, height, getPixel) {
|
||||
const signature = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
|
||||
const ihdr = Buffer.alloc(13);
|
||||
ihdr.writeUInt32BE(width, 0);
|
||||
ihdr.writeUInt32BE(height, 4);
|
||||
ihdr[8] = 8;
|
||||
ihdr[9] = 2;
|
||||
|
||||
const rowLength = width * 3;
|
||||
const raw = Buffer.alloc((rowLength + 1) * height);
|
||||
for (let y = 0; y < height; y += 1) {
|
||||
const offset = y * (rowLength + 1);
|
||||
for (let x = 0; x < width; x += 1) {
|
||||
const [r, g, b] = getPixel(x, y);
|
||||
const px = offset + 1 + x * 3;
|
||||
raw[px] = r;
|
||||
raw[px + 1] = g;
|
||||
raw[px + 2] = b;
|
||||
}
|
||||
}
|
||||
|
||||
return Buffer.concat([
|
||||
signature,
|
||||
chunk("IHDR", ihdr),
|
||||
chunk("IDAT", zlib.deflateSync(raw, { level: 9 })),
|
||||
chunk("IEND", Buffer.alloc(0)),
|
||||
]);
|
||||
}
|
||||
|
||||
function solidPng(width, height, color) {
|
||||
return encodePng(width, height, () => color);
|
||||
}
|
||||
|
||||
module.exports = { solidPng, encodePng };
|
||||
115
pptx/src/scaffold.js
Normal file
115
pptx/src/scaffold.js
Normal 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 };
|
||||
Reference in New Issue
Block a user