@k2works/claude-code-booster 4.5.0 → 4.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/assets/.claude/skills/creating-slides/SKILL.md +86 -0
- package/lib/assets/.claude/skills/creating-slides/agents/openai.yaml +4 -0
- package/lib/assets/.claude/skills/creating-slides/references/manga-css.md +45 -0
- package/lib/assets/.claude/skills/creating-slides/scripts/slide_tools.py +239 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/01-container-and-docker-basics.md +332 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/02-container-deployment.md +776 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/03-practical-container-build-deploy.md +457 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/04-multi-container-application.md +869 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/05-kubernetes-introduction.md +576 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/06-kubernetes-deploy-cluster.md +611 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/07-kubernetes-advanced.md +612 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/08-kubernetes-packaging.md +509 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/09-container-operations.md +537 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/10-optimal-container-image.md +631 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/11-continuous-delivery.md +598 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/12-container-use-cases.md +454 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/13-case-monolith-compose-vs-kustomize.md +337 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/14-case-event-driven-kustomize-vs-helm.md +315 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/15-case-escqrs-axon-kustomize-vs-helm.md +288 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/16-case-escqrs-kafka-kustomize-vs-helm.md +247 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/17-case-comparison-summary.md +132 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/18-case-load-testing.md +294 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/appendix-a-dev-tools-setup.md +265 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/appendix-b-orchestration-environments.md +521 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/appendix-c-tips.md +510 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-01-comic.png +0 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-02-comic.png +0 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-03-comic.png +0 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-04-comic.png +0 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-05-comic.png +0 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-06-comic.png +0 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-07-comic.png +0 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-08-comic.png +0 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-09-comic.png +0 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-10-comic.png +0 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-11-comic.png +0 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-12-comic.png +0 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/images/overview-comic.png +0 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/index.md +82 -0
- package/lib/assets/docs/article/getting-start-docker-kubernetes/outline.md +211 -0
- package/lib/assets/docs/article/index.md +3 -1
- package/lib/assets/mkdocs.yml +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: creating-slides
|
|
3
|
+
description: Marp Markdown スライドデッキの作成・拡張・再生成を支援。PlantUML 図表、AI 画像バリアント、漫画画像バリアント、PPTX/HTML 出力に対応。`tmp/k2works-wiki/スライド/YYYYMMDD` などの日本語技術スライドフォルダの構築・更新、コードサンプル追加、ビジュアル要約スライド追加、`スライド.images.md` / `スライド.images.ai.md` / `スライド.images.ai.manga.md` の生成、Marp HTML/PPTX デッキの再生成時に使用。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Creating Slides
|
|
7
|
+
|
|
8
|
+
## Core Workflow
|
|
9
|
+
|
|
10
|
+
1. Read the target slide folder and the reference slide Markdown first.
|
|
11
|
+
- Check `スライド.md`, `スライド.images.md`, `スライド.images.ai.md`, and any manga variant if present.
|
|
12
|
+
- Use `rg` for headings, image references, code sample lists, and reference links.
|
|
13
|
+
2. Gather source material before editing.
|
|
14
|
+
- Prefer `docs/article` for conceptual content.
|
|
15
|
+
- Use `apps` or language-specific app folders for concrete code samples.
|
|
16
|
+
- Browse only when the user requests current external references or a live website.
|
|
17
|
+
3. Edit `スライド.md` as the canonical source.
|
|
18
|
+
- Keep Japanese prose concise.
|
|
19
|
+
- Keep Japanese and ASCII terms separated by a half-width space.
|
|
20
|
+
- Add code slides near related language groups.
|
|
21
|
+
- Update structure, comparison tables, and references when adding samples.
|
|
22
|
+
4. Regenerate derived Markdown and outputs.
|
|
23
|
+
- Run `scripts/slide_tools.py build <slide-dir>`.
|
|
24
|
+
- For AI image variants, ensure generated Markdown references `images-ai/diagram-ai-NN.png`.
|
|
25
|
+
- For manga variants, ensure generated Markdown references `images-manga/*.png`.
|
|
26
|
+
5. Verify outputs.
|
|
27
|
+
- Count PPTX slides with `unzip -Z1 '<deck>.pptx' 'ppt/slides/slide*.xml' | wc -l`.
|
|
28
|
+
- Open rendered HTML with Playwright when visual layout matters.
|
|
29
|
+
- Check representative slide screenshots, especially image-heavy and code-heavy slides.
|
|
30
|
+
- Remove temporary `render-check-*.png` files before finishing.
|
|
31
|
+
|
|
32
|
+
## Commands
|
|
33
|
+
|
|
34
|
+
Use the bundled script from this skill directory:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
python ~/.codex/skills/creating-slides/scripts/slide_tools.py build tmp/k2works-wiki/スライド/20260602
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Useful subcommands:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
python ~/.codex/skills/creating-slides/scripts/slide_tools.py extract <slide-dir>
|
|
44
|
+
python ~/.codex/skills/creating-slides/scripts/slide_tools.py make-ai <slide-dir>
|
|
45
|
+
python ~/.codex/skills/creating-slides/scripts/slide_tools.py make-manga <slide-dir>
|
|
46
|
+
python ~/.codex/skills/creating-slides/scripts/slide_tools.py marp <slide-dir>
|
|
47
|
+
python ~/.codex/skills/creating-slides/scripts/slide_tools.py count <slide-dir>
|
|
48
|
+
python ~/.codex/skills/creating-slides/scripts/slide_tools.py split-summary <slide-dir> images-manga/summary-manga.png
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Image Variants
|
|
52
|
+
|
|
53
|
+
Use the `imagegen` skill when creating new AI or manga bitmap assets.
|
|
54
|
+
|
|
55
|
+
Project-bound generated images must be copied into the slide folder, normally:
|
|
56
|
+
|
|
57
|
+
- `images-ai/diagram-ai-NN.png` for AI diagram replacements.
|
|
58
|
+
- `images-manga/manga-*.png` for manga story panels.
|
|
59
|
+
- `images-manga/summary-manga.png` and split files for tall summary comics.
|
|
60
|
+
|
|
61
|
+
For tall manga summaries, do not place the full vertical image on one 16:9 slide. Split it into section images and use multiple slides so text remains readable:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
python ~/.codex/skills/creating-slides/scripts/slide_tools.py split-summary <slide-dir> images-manga/summary-manga.png --parts 5
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Then replace a single summary image slide with numbered slides that reference the split files.
|
|
68
|
+
|
|
69
|
+
## Manga Markdown Pattern
|
|
70
|
+
|
|
71
|
+
When converting AI Markdown to a manga variant, preserve prose and replace only diagram image references with `figure.diagram` blocks:
|
|
72
|
+
|
|
73
|
+
```html
|
|
74
|
+
<figure class="diagram wide">
|
|
75
|
+
<img src="images-manga/manga-01-overview.png" alt="...">
|
|
76
|
+
</figure>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Read `references/manga-css.md` when a manga variant needs CSS or layout tuning.
|
|
80
|
+
|
|
81
|
+
## Validation Notes
|
|
82
|
+
|
|
83
|
+
- Marp PPTX generation needs `--allow-local-files` when local images are referenced.
|
|
84
|
+
- `npx --yes @marp-team/marp-cli` is the expected renderer unless the project has a local renderer.
|
|
85
|
+
- PlantUML must be available for diagram rendering; if missing, report that diagram PNG generation could not be completed.
|
|
86
|
+
- If HTML keyboard navigation jumps unexpectedly during Playwright checks, inspect the DOM for `section` elements and image sizes directly.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Manga Slide CSS
|
|
2
|
+
|
|
3
|
+
Use this CSS pattern in Marp decks that mix text slides and manga images:
|
|
4
|
+
|
|
5
|
+
```css
|
|
6
|
+
section img {
|
|
7
|
+
display: block;
|
|
8
|
+
max-width: 100%;
|
|
9
|
+
height: auto;
|
|
10
|
+
}
|
|
11
|
+
section .diagram {
|
|
12
|
+
margin: 10px auto 14px;
|
|
13
|
+
text-align: center;
|
|
14
|
+
}
|
|
15
|
+
section .diagram img {
|
|
16
|
+
width: 100%;
|
|
17
|
+
max-width: 980px;
|
|
18
|
+
max-height: 420px;
|
|
19
|
+
object-fit: contain;
|
|
20
|
+
margin: 0 auto;
|
|
21
|
+
}
|
|
22
|
+
section .diagram.wide img {
|
|
23
|
+
max-width: 1040px;
|
|
24
|
+
max-height: 470px;
|
|
25
|
+
}
|
|
26
|
+
section .diagram.compact img {
|
|
27
|
+
max-width: 760px;
|
|
28
|
+
max-height: 320px;
|
|
29
|
+
}
|
|
30
|
+
section.manga-summary {
|
|
31
|
+
padding: 24px 60px;
|
|
32
|
+
}
|
|
33
|
+
section.manga-summary h3 {
|
|
34
|
+
margin: 0 0 0.2em;
|
|
35
|
+
}
|
|
36
|
+
section.manga-summary img {
|
|
37
|
+
max-width: 100%;
|
|
38
|
+
max-height: 600px;
|
|
39
|
+
display: block;
|
|
40
|
+
margin: 0 auto;
|
|
41
|
+
object-fit: contain;
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
For tall summary comics, prefer 4-6 cropped slides over one full-height image. Verify each crop renders at roughly 800 px or wider on a 1280 px viewport.
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import re
|
|
6
|
+
import shutil
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
START_RE = re.compile(r"^@(startuml|startmindmap)\b.*$", re.MULTILINE)
|
|
13
|
+
END_RE = re.compile(r"^@(enduml|endmindmap)\b.*$", re.MULTILINE)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def run(cmd: list[str], cwd: Path) -> None:
|
|
17
|
+
print("+", " ".join(cmd), f"(cwd={cwd})")
|
|
18
|
+
subprocess.run(cmd, cwd=cwd, check=True)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def extract(slide_dir: Path) -> None:
|
|
22
|
+
src = slide_dir / "スライド.md"
|
|
23
|
+
dst = slide_dir / "スライド.images.md"
|
|
24
|
+
img_dir = slide_dir / "images"
|
|
25
|
+
if not src.exists():
|
|
26
|
+
raise SystemExit(f"missing canonical markdown: {src}")
|
|
27
|
+
img_dir.mkdir(exist_ok=True)
|
|
28
|
+
text = src.read_text(encoding="utf-8")
|
|
29
|
+
out: list[str] = []
|
|
30
|
+
cursor = 0
|
|
31
|
+
index = 0
|
|
32
|
+
fence_open_re = re.compile(r"```[a-zA-Z]*\s*\n")
|
|
33
|
+
fence_close_re = re.compile(r"\n```")
|
|
34
|
+
while True:
|
|
35
|
+
start = START_RE.search(text, cursor)
|
|
36
|
+
if not start:
|
|
37
|
+
out.append(text[cursor:])
|
|
38
|
+
break
|
|
39
|
+
end = END_RE.search(text, start.end())
|
|
40
|
+
if not end:
|
|
41
|
+
raise SystemExit(f"missing @enduml/@endmindmap after offset {start.start()}")
|
|
42
|
+
index += 1
|
|
43
|
+
block = text[start.start() : end.end()]
|
|
44
|
+
(img_dir / f"diagram-{index:02d}.puml").write_text(block + "\n", encoding="utf-8")
|
|
45
|
+
before = text[cursor : start.start()]
|
|
46
|
+
m_open = None
|
|
47
|
+
for match in fence_open_re.finditer(before):
|
|
48
|
+
m_open = match
|
|
49
|
+
m_close = fence_close_re.search(text, end.end())
|
|
50
|
+
if m_open and m_close:
|
|
51
|
+
replace_start = cursor + m_open.start()
|
|
52
|
+
replace_end = m_close.end()
|
|
53
|
+
out.append(text[cursor:replace_start])
|
|
54
|
+
out.append(f"")
|
|
55
|
+
cursor = replace_end
|
|
56
|
+
else:
|
|
57
|
+
out.append(text[cursor : start.start()])
|
|
58
|
+
out.append(f"")
|
|
59
|
+
cursor = end.end()
|
|
60
|
+
dst.write_text("".join(out), encoding="utf-8")
|
|
61
|
+
print(f"Extracted {index} diagrams -> {img_dir}")
|
|
62
|
+
print(f"Wrote {dst}")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def render_plantuml(slide_dir: Path) -> None:
|
|
66
|
+
puml_files = sorted((slide_dir / "images").glob("*.puml"))
|
|
67
|
+
if puml_files:
|
|
68
|
+
run(["plantuml", "-tpng", *[str(p.relative_to(slide_dir)) for p in puml_files]], slide_dir)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def make_ai(slide_dir: Path) -> None:
|
|
72
|
+
src = slide_dir / "スライド.images.md"
|
|
73
|
+
dst = slide_dir / "スライド.images.ai.md"
|
|
74
|
+
if not src.exists():
|
|
75
|
+
extract(slide_dir)
|
|
76
|
+
text = src.read_text(encoding="utf-8")
|
|
77
|
+
text = re.sub(r"images/diagram-(\d\d)\.png", r"images-ai/diagram-ai-\1.png", text)
|
|
78
|
+
text = text.replace(
|
|
79
|
+
"section img {\n max-height: 420px;\n }",
|
|
80
|
+
"section img {\n display: block;\n max-width: 100%;\n max-height: 420px;\n height: auto;\n object-fit: contain;\n margin: 0 auto;\n }",
|
|
81
|
+
)
|
|
82
|
+
dst.write_text(text, encoding="utf-8")
|
|
83
|
+
print(f"Wrote {dst}")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
MANGA_REPLACEMENTS = {
|
|
87
|
+
"01": ("manga-01-overview.png", "diagram wide", "概要を描いた漫画"),
|
|
88
|
+
"02": ("manga-02-side-effect.png", "diagram", "副作用を描いた漫画"),
|
|
89
|
+
"03": ("manga-03-hard-to-test.png", "diagram", "テスト困難性を描いた漫画"),
|
|
90
|
+
"04": ("manga-04-core-shell.png", "diagram", "純粋なコアと不純なシェルを描いた漫画"),
|
|
91
|
+
"05": ("manga-05-io-plan.png", "diagram", "IO の実行計画を描いた漫画"),
|
|
92
|
+
"06": ("manga-06-execution-order.png", "diagram wide", "実行順序を描いた漫画"),
|
|
93
|
+
"07": ("manga-07-resource-safety.png", "diagram", "Resource の安全性を描いた漫画"),
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def manga_css(text: str) -> str:
|
|
98
|
+
text = text.replace("font-size: 0.82em;", "font-size: 0.85em;")
|
|
99
|
+
text = text.replace("font-size: 0.68em;", "font-size: 0.7em;")
|
|
100
|
+
needle = "section img {\n max-height: 420px;\n }"
|
|
101
|
+
repl = """section img {
|
|
102
|
+
display: block;
|
|
103
|
+
max-width: 100%;
|
|
104
|
+
height: auto;
|
|
105
|
+
}
|
|
106
|
+
section .diagram {
|
|
107
|
+
margin: 10px auto 14px;
|
|
108
|
+
text-align: center;
|
|
109
|
+
}
|
|
110
|
+
section .diagram img {
|
|
111
|
+
width: 100%;
|
|
112
|
+
max-width: 980px;
|
|
113
|
+
max-height: 420px;
|
|
114
|
+
object-fit: contain;
|
|
115
|
+
margin: 0 auto;
|
|
116
|
+
}
|
|
117
|
+
section .diagram.wide img {
|
|
118
|
+
max-width: 1040px;
|
|
119
|
+
max-height: 470px;
|
|
120
|
+
}
|
|
121
|
+
section .diagram.compact img {
|
|
122
|
+
max-width: 760px;
|
|
123
|
+
max-height: 320px;
|
|
124
|
+
}
|
|
125
|
+
section .columns .diagram {
|
|
126
|
+
margin-top: 0;
|
|
127
|
+
}
|
|
128
|
+
section .columns .diagram img {
|
|
129
|
+
max-height: 330px;
|
|
130
|
+
}"""
|
|
131
|
+
return text.replace(needle, repl)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def make_manga(slide_dir: Path) -> None:
|
|
135
|
+
src = slide_dir / "スライド.images.ai.md"
|
|
136
|
+
dst = slide_dir / "スライド.images.ai.manga.md"
|
|
137
|
+
if not src.exists():
|
|
138
|
+
make_ai(slide_dir)
|
|
139
|
+
text = manga_css(src.read_text(encoding="utf-8"))
|
|
140
|
+
for num, (filename, klass, alt) in MANGA_REPLACEMENTS.items():
|
|
141
|
+
image = slide_dir / "images-manga" / filename
|
|
142
|
+
if not image.exists():
|
|
143
|
+
continue
|
|
144
|
+
block = f'<figure class="{klass}">\n <img src="images-manga/{filename}" alt="{alt}">\n</figure>'
|
|
145
|
+
text = text.replace(f"", block)
|
|
146
|
+
dst.write_text(text, encoding="utf-8")
|
|
147
|
+
print(f"Wrote {dst}")
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def marp(slide_dir: Path) -> None:
|
|
151
|
+
targets = [
|
|
152
|
+
("スライド.images.md", "スライド.html", ["--html"]),
|
|
153
|
+
("スライド.images.md", "スライド.pptx", ["--pptx", "--allow-local-files"]),
|
|
154
|
+
("スライド.images.ai.md", "スライド.images.ai.html", ["--html"]),
|
|
155
|
+
("スライド.images.ai.md", "スライド.images.ai.pptx", ["--pptx", "--allow-local-files"]),
|
|
156
|
+
("スライド.images.ai.manga.md", "スライド.images.ai.manga.html", ["--html"]),
|
|
157
|
+
("スライド.images.ai.manga.md", "スライド.images.ai.manga.pptx", ["--pptx", "--allow-local-files"]),
|
|
158
|
+
]
|
|
159
|
+
for src, dst, flags in targets:
|
|
160
|
+
if (slide_dir / src).exists():
|
|
161
|
+
run(["npx", "--yes", "@marp-team/marp-cli", src, "-o", dst, *flags], slide_dir)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def count(slide_dir: Path) -> None:
|
|
165
|
+
for pptx in sorted(slide_dir.glob("*.pptx")):
|
|
166
|
+
proc = subprocess.run(
|
|
167
|
+
["unzip", "-Z1", str(pptx), "ppt/slides/slide*.xml"],
|
|
168
|
+
check=True,
|
|
169
|
+
text=True,
|
|
170
|
+
capture_output=True,
|
|
171
|
+
)
|
|
172
|
+
print(f"{pptx.name}: {len(proc.stdout.splitlines())}")
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def split_summary(slide_dir: Path, image_rel: str, parts: int) -> None:
|
|
176
|
+
src = slide_dir / image_rel
|
|
177
|
+
if not src.exists():
|
|
178
|
+
raise SystemExit(f"missing image: {src}")
|
|
179
|
+
try:
|
|
180
|
+
proc = subprocess.run(
|
|
181
|
+
["sips", "-g", "pixelWidth", "-g", "pixelHeight", str(src)],
|
|
182
|
+
check=True,
|
|
183
|
+
text=True,
|
|
184
|
+
capture_output=True,
|
|
185
|
+
)
|
|
186
|
+
except FileNotFoundError as exc:
|
|
187
|
+
raise SystemExit("split-summary requires macOS sips") from exc
|
|
188
|
+
nums = [int(x) for x in re.findall(r"pixel(?:Width|Height):\s+(\d+)", proc.stdout)]
|
|
189
|
+
if len(nums) < 2:
|
|
190
|
+
raise SystemExit(f"could not read image size from sips output: {proc.stdout}")
|
|
191
|
+
width, height = nums[0], nums[1]
|
|
192
|
+
stem = src.stem
|
|
193
|
+
chunk = height // parts
|
|
194
|
+
for idx in range(parts):
|
|
195
|
+
offset = idx * chunk if idx else 1
|
|
196
|
+
crop_h = chunk if idx < parts - 1 else height - idx * chunk - 1
|
|
197
|
+
out = src.with_name(f"{stem}-{idx + 1:02d}.png")
|
|
198
|
+
shutil.copy2(src, out)
|
|
199
|
+
run(["sips", "-c", str(crop_h), str(width), "--cropOffset", str(offset), "0", str(out)], slide_dir)
|
|
200
|
+
print(f"Wrote {out.relative_to(slide_dir)}")
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def build(slide_dir: Path) -> None:
|
|
204
|
+
extract(slide_dir)
|
|
205
|
+
render_plantuml(slide_dir)
|
|
206
|
+
make_ai(slide_dir)
|
|
207
|
+
make_manga(slide_dir)
|
|
208
|
+
marp(slide_dir)
|
|
209
|
+
count(slide_dir)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def main() -> None:
|
|
213
|
+
parser = argparse.ArgumentParser()
|
|
214
|
+
parser.add_argument("command", choices=["extract", "make-ai", "make-manga", "marp", "count", "build", "split-summary"])
|
|
215
|
+
parser.add_argument("slide_dir")
|
|
216
|
+
parser.add_argument("image", nargs="?")
|
|
217
|
+
parser.add_argument("--parts", type=int, default=5)
|
|
218
|
+
args = parser.parse_args()
|
|
219
|
+
slide_dir = Path(args.slide_dir).expanduser().resolve()
|
|
220
|
+
if args.command == "extract":
|
|
221
|
+
extract(slide_dir)
|
|
222
|
+
elif args.command == "make-ai":
|
|
223
|
+
make_ai(slide_dir)
|
|
224
|
+
elif args.command == "make-manga":
|
|
225
|
+
make_manga(slide_dir)
|
|
226
|
+
elif args.command == "marp":
|
|
227
|
+
marp(slide_dir)
|
|
228
|
+
elif args.command == "count":
|
|
229
|
+
count(slide_dir)
|
|
230
|
+
elif args.command == "build":
|
|
231
|
+
build(slide_dir)
|
|
232
|
+
elif args.command == "split-summary":
|
|
233
|
+
if not args.image:
|
|
234
|
+
raise SystemExit("split-summary requires an image path relative to slide-dir")
|
|
235
|
+
split_summary(slide_dir, args.image, args.parts)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
if __name__ == "__main__":
|
|
239
|
+
main()
|