pptx-cli 1.3.4__tar.gz → 1.3.5__tar.gz
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.
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/PKG-INFO +1 -1
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/src/pptx_cli/__init__.py +1 -1
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/src/pptx_cli/cli.py +32 -1
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/src/pptx_cli/commands/schema.py +127 -63
- pptx_cli-1.3.5/tests/test_schema.py +134 -0
- pptx_cli-1.3.4/tests/test_schema.py +0 -80
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/.claude/settings.local.json +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/.editorconfig +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/.github/copilot-instructions.md +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/.github/instructions/backend.instructions.md +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/.github/instructions/testing.instructions.md +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/.github/skills/pptx/SKILL.md +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/.github/skills/pptx/references/deck-spec.md +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/.github/skills/pptx-deck-builder/SKILL.md +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/.github/skills/pptx-deck-builder/references/excal-diagrams.md +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/.github/skills/pptx-deck-builder/references/mckinsey-style.md +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/.github/skills/pptx-deck-builder/references/pptx-workflow.md +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/.github/workflows/ci.yml +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/.github/workflows/publish.yml +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/.gitignore +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/.python-version +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/AGENTS.md +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/ARCHITECTURE.md +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/CHANGELOG.md +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/CLI-MANIFEST.md +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/CONTRIBUTING.md +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/DECISIONS/ADR-0001-initial-architecture.md +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/LICENSE +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/PRD.md +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/PREVIEW-FUTURE.md +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/PROJECT.md +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/README.md +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/SCAFFOLD.md +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/SECURITY.md +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/TESTING.md +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/docs/DOMAIN.md +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/docs/GLOSSARY.md +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/docs/ROADMAP.md +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/docs/SCAFFOLDING-NOTES.md +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/guardrails/.gitignore +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/guardrails/guardrails.jsonl +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/guardrails/links.jsonl +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/guardrails/references.jsonl +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/guardrails/taxonomy.json +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/guardrails-explorer.html +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/pyproject.toml +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/pyrightconfig.json +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/scripts/bump_version.py +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/src/pptx_cli/__main__.py +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/src/pptx_cli/commands/__init__.py +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/src/pptx_cli/commands/compose.py +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/src/pptx_cli/commands/guide.py +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/src/pptx_cli/commands/init.py +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/src/pptx_cli/commands/inspect.py +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/src/pptx_cli/commands/manifest_ops.py +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/src/pptx_cli/commands/validate.py +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/src/pptx_cli/commands/wrapper.py +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/src/pptx_cli/core/__init__.py +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/src/pptx_cli/core/composition.py +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/src/pptx_cli/core/ids.py +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/src/pptx_cli/core/io.py +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/src/pptx_cli/core/manifest_store.py +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/src/pptx_cli/core/markdown.py +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/src/pptx_cli/core/runtime.py +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/src/pptx_cli/core/template.py +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/src/pptx_cli/core/validation.py +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/src/pptx_cli/core/versioning.py +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/src/pptx_cli/models/__init__.py +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/src/pptx_cli/models/envelope.py +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/src/pptx_cli/models/manifest.py +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/tests/conftest.py +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/tests/test_cli.py +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/tests/test_versioning.py +0 -0
- {pptx_cli-1.3.4 → pptx_cli-1.3.5}/uv.lock +0 -0
|
@@ -245,13 +245,44 @@ def schema_command(
|
|
|
245
245
|
Path | None,
|
|
246
246
|
typer.Option("--template", help="Path to a manifest package directory."),
|
|
247
247
|
] = None,
|
|
248
|
+
no_template: Annotated[
|
|
249
|
+
bool,
|
|
250
|
+
typer.Option("--no-template", help="Emit generic schema without template layouts."),
|
|
251
|
+
] = False,
|
|
248
252
|
no_copy: Annotated[
|
|
249
253
|
bool,
|
|
250
254
|
typer.Option("--no-copy", help="Skip copying output to the clipboard."),
|
|
251
255
|
] = False,
|
|
252
256
|
) -> None:
|
|
253
257
|
"""Print the deck-spec YAML reference (for pasting into LLM prompts)."""
|
|
254
|
-
|
|
258
|
+
if template is not None and no_template:
|
|
259
|
+
typer.echo("Error: --template and --no-template are mutually exclusive.", err=True)
|
|
260
|
+
raise typer.Exit(code=1)
|
|
261
|
+
|
|
262
|
+
effective_template = template
|
|
263
|
+
if not no_template and effective_template is None:
|
|
264
|
+
# Auto-discover manifest in cwd
|
|
265
|
+
for candidate in [Path("manifest.yaml"), Path("manifest/manifest.yaml")]:
|
|
266
|
+
if candidate.exists():
|
|
267
|
+
effective_template = candidate.parent
|
|
268
|
+
break
|
|
269
|
+
if effective_template is None:
|
|
270
|
+
# Fall back to generic schema with a hint
|
|
271
|
+
typer.echo(
|
|
272
|
+
"Hint: use --template <dir> for a layout-aware schema, "
|
|
273
|
+
"or --no-template for the generic version.",
|
|
274
|
+
err=True,
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
if effective_template is not None and not (effective_template / "manifest.yaml").exists():
|
|
278
|
+
typer.echo(
|
|
279
|
+
f"Error: manifest not found at '{effective_template / 'manifest.yaml'}'.\n"
|
|
280
|
+
f"Run 'pptx init <template.pptx> --out {effective_template}' first.",
|
|
281
|
+
err=True,
|
|
282
|
+
)
|
|
283
|
+
raise typer.Exit(code=1)
|
|
284
|
+
|
|
285
|
+
text = build_schema_document(effective_template)
|
|
255
286
|
typer.echo(text)
|
|
256
287
|
if not no_copy:
|
|
257
288
|
if copy_to_clipboard(text):
|
|
@@ -42,10 +42,10 @@ def copy_to_clipboard(text: str) -> bool:
|
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
# ---------------------------------------------------------------------------
|
|
45
|
-
#
|
|
45
|
+
# Shared constants
|
|
46
46
|
# ---------------------------------------------------------------------------
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
_OUTPUT_FORMAT = """\
|
|
49
49
|
<output-format>
|
|
50
50
|
IMPORTANT: Your output MUST be a YAML deck spec, not a binary .pptx file.
|
|
51
51
|
Do NOT attempt to generate PowerPoint XML, base64 data, or any format
|
|
@@ -56,8 +56,54 @@ Preferred: offer the YAML as a downloadable file (e.g. a download link or
|
|
|
56
56
|
file attachment named "deck.yaml") so the user can save it directly.
|
|
57
57
|
Fallback: emit a single ```yaml code block the user can copy-paste into
|
|
58
58
|
a .yaml file and then run `pptx deck build -f deck.yaml`.
|
|
59
|
-
</output-format>
|
|
59
|
+
</output-format>"""
|
|
60
60
|
|
|
61
|
+
_STYLE_GUIDE = """\
|
|
62
|
+
<style-guide>
|
|
63
|
+
Follow these principles to produce decision-grade slides.
|
|
64
|
+
|
|
65
|
+
Structure
|
|
66
|
+
- Lead with the answer. Do not build up to the conclusion.
|
|
67
|
+
- Pyramid Principle: governing thought -> supporting arguments -> evidence.
|
|
68
|
+
- Arguments must be MECE (mutually exclusive, collectively exhaustive).
|
|
69
|
+
- One slide = one message. Two insights -> two slides.
|
|
70
|
+
|
|
71
|
+
Titles
|
|
72
|
+
- Use action titles, not topic labels. The title states the takeaway.
|
|
73
|
+
Bad: "Market overview"
|
|
74
|
+
Good: "Nordic retail banking margins will remain under pressure through 2027"
|
|
75
|
+
- Title storyline reads on its own - skim only titles and understand the
|
|
76
|
+
full argument.
|
|
77
|
+
- Formulas:
|
|
78
|
+
Insight: "[What happened] because [driver]"
|
|
79
|
+
Comparison: "[A] outperforms [B] on [criterion]"
|
|
80
|
+
Implication: "[Fact] puts [objective] at risk"
|
|
81
|
+
Recommendation: "[Org] should [action] to achieve [outcome]"
|
|
82
|
+
|
|
83
|
+
Executive summary slide
|
|
84
|
+
- Situation: what context everyone agrees on.
|
|
85
|
+
- Complication: what changed or created urgency.
|
|
86
|
+
- Answer: the recommended response.
|
|
87
|
+
- Support: 2-3 reasons the answer is correct.
|
|
88
|
+
|
|
89
|
+
Deck sequence (typical)
|
|
90
|
+
1. Title page
|
|
91
|
+
2. Executive summary (situation -> complication -> resolution)
|
|
92
|
+
3. Context / problem framing (only enough to orient)
|
|
93
|
+
4. Analysis body (current state -> root causes -> options -> recommendation)
|
|
94
|
+
5. Recommendation (explicit, decision-ready)
|
|
95
|
+
6. Implementation / roadmap
|
|
96
|
+
7. Risks and mitigations
|
|
97
|
+
8. Appendix (source data, benchmarks, methodology)
|
|
98
|
+
|
|
99
|
+
Body content
|
|
100
|
+
- Every claim backed by evidence. The body proves the headline.
|
|
101
|
+
- Quantify. Replace vague adjectives with measured claims.
|
|
102
|
+
- Keep text tight: verbs, short bullets, no filler.
|
|
103
|
+
- Move backup detail to appendix - main story stays focused.
|
|
104
|
+
</style-guide>"""
|
|
105
|
+
|
|
106
|
+
_GENERIC_SLIDE_SCHEMA = """\
|
|
61
107
|
<slide-schema>
|
|
62
108
|
Use this schema to draft presentation content. Layout assignment and
|
|
63
109
|
template binding happen later, so focus on the slides themselves.
|
|
@@ -101,8 +147,9 @@ slides:
|
|
|
101
147
|
kind: image
|
|
102
148
|
path: path/to/image.png
|
|
103
149
|
image_fit: fit
|
|
104
|
-
</slide-schema>
|
|
150
|
+
</slide-schema>"""
|
|
105
151
|
|
|
152
|
+
_GENERIC_CONTENT_RULES = """\
|
|
106
153
|
<content-rules>
|
|
107
154
|
- Each slide MUST have a "title".
|
|
108
155
|
- Use "body" for the main text area. Markdown is auto-detected
|
|
@@ -113,56 +160,42 @@ slides:
|
|
|
113
160
|
- "notes" is optional speaker-notes text (markdown ok).
|
|
114
161
|
- Do NOT include a "layout" key - layout is assigned later when
|
|
115
162
|
binding to a corporate template.
|
|
116
|
-
</content-rules>
|
|
117
|
-
|
|
118
|
-
<style-guide>
|
|
119
|
-
Follow these principles to produce decision-grade slides.
|
|
163
|
+
</content-rules>"""
|
|
120
164
|
|
|
121
|
-
|
|
122
|
-
-
|
|
123
|
-
-
|
|
124
|
-
|
|
125
|
-
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
6. Implementation / roadmap
|
|
152
|
-
7. Risks and mitigations
|
|
153
|
-
8. Appendix (source data, benchmarks, methodology)
|
|
154
|
-
|
|
155
|
-
Body content
|
|
156
|
-
- Every claim backed by evidence. The body proves the headline.
|
|
157
|
-
- Quantify. Replace vague adjectives with measured claims.
|
|
158
|
-
- Keep text tight: verbs, short bullets, no filler.
|
|
159
|
-
- Move backup detail to appendix - main story stays focused.
|
|
160
|
-
</style-guide>
|
|
161
|
-
"""
|
|
165
|
+
_TEMPLATE_CONTENT_RULES = """\
|
|
166
|
+
<content-rules>
|
|
167
|
+
- Each slide MUST have a "layout" key matching one of the layout IDs
|
|
168
|
+
listed in the <layouts> section.
|
|
169
|
+
- Each slide MUST have a "content" dict that maps placeholder names
|
|
170
|
+
(from the layout's placeholders) to values.
|
|
171
|
+
- Content type dispatch:
|
|
172
|
+
- Plain text or markdown: provide a string value directly.
|
|
173
|
+
Markdown is auto-detected (headings, bullets, **bold**, *italic*).
|
|
174
|
+
- Image: { kind: image, path: path/to/image.png, image_fit: fit }
|
|
175
|
+
image_fit accepts: "fit" (default) or "cover".
|
|
176
|
+
- Table: { kind: table, columns: [...], rows: [[...], ...] }
|
|
177
|
+
- Chart: { kind: chart, chart_type: column_clustered, categories: [...],
|
|
178
|
+
series: [{ name: ..., values: [...] }] }
|
|
179
|
+
chart_type accepts: column_clustered, bar_clustered, line, pie.
|
|
180
|
+
- Respect "required" and "max_lines" guidance from the layout definitions.
|
|
181
|
+
- "notes" is optional speaker-notes text (markdown ok) at the slide level.
|
|
182
|
+
</content-rules>"""
|
|
183
|
+
|
|
184
|
+
# Recompose generic schema from shared pieces
|
|
185
|
+
_GENERIC_SCHEMA = (
|
|
186
|
+
_OUTPUT_FORMAT
|
|
187
|
+
+ "\n\n"
|
|
188
|
+
+ _GENERIC_SLIDE_SCHEMA
|
|
189
|
+
+ "\n\n"
|
|
190
|
+
+ _GENERIC_CONTENT_RULES
|
|
191
|
+
+ "\n\n"
|
|
192
|
+
+ _STYLE_GUIDE
|
|
193
|
+
+ "\n"
|
|
194
|
+
)
|
|
162
195
|
|
|
163
196
|
|
|
164
197
|
# ---------------------------------------------------------------------------
|
|
165
|
-
# Template-enriched schema
|
|
198
|
+
# Template-enriched schema helpers
|
|
166
199
|
# ---------------------------------------------------------------------------
|
|
167
200
|
|
|
168
201
|
|
|
@@ -207,21 +240,54 @@ def _example_slide(layout: LayoutContract) -> dict[str, Any]:
|
|
|
207
240
|
return slide
|
|
208
241
|
|
|
209
242
|
|
|
210
|
-
def
|
|
211
|
-
"""Return the
|
|
243
|
+
def _build_layouts_section(manifest: ManifestDocument) -> str:
|
|
244
|
+
"""Return the <layouts> XML section with all layout definitions."""
|
|
212
245
|
layouts_ref: dict[str, Any] = {}
|
|
213
|
-
examples: list[dict[str, Any]] = []
|
|
214
|
-
|
|
215
246
|
for layout in manifest.layouts:
|
|
216
247
|
layouts_ref[layout.id] = _layout_section(layout)
|
|
248
|
+
layouts_yaml = yaml.safe_dump(layouts_ref, sort_keys=False, allow_unicode=True, width=120)
|
|
249
|
+
return f"<layouts>\n{layouts_yaml}</layouts>"
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def _build_deck_schema_section(manifest: ManifestDocument) -> str:
|
|
253
|
+
"""Return the <deck-schema> XML section with DeckSpec example."""
|
|
254
|
+
examples: list[dict[str, Any]] = []
|
|
255
|
+
# Use up to 4 representative layouts for examples
|
|
256
|
+
for layout in manifest.layouts[:4]:
|
|
217
257
|
examples.append(_example_slide(layout))
|
|
218
258
|
|
|
219
|
-
|
|
220
|
-
"
|
|
221
|
-
|
|
222
|
-
|
|
259
|
+
example_spec: dict[str, Any] = {
|
|
260
|
+
"metadata": {
|
|
261
|
+
"title": "<deck title>",
|
|
262
|
+
"author": "<author name>",
|
|
263
|
+
},
|
|
264
|
+
"slides": examples,
|
|
223
265
|
}
|
|
224
|
-
|
|
266
|
+
example_yaml = yaml.safe_dump(example_spec, sort_keys=False, allow_unicode=True, width=120)
|
|
267
|
+
|
|
268
|
+
prose = (
|
|
269
|
+
f"Use this schema to draft presentation content for the "
|
|
270
|
+
f'"{manifest.template.name}" template.\n'
|
|
271
|
+
f"Each slide must reference a layout from the <layouts> section and "
|
|
272
|
+
f"provide content for its placeholders.\n\n"
|
|
273
|
+
)
|
|
274
|
+
return f"<deck-schema>\n{prose}{example_yaml}</deck-schema>"
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def _build_template_schema(manifest: ManifestDocument) -> str:
|
|
278
|
+
"""Compose the unified template-bound schema document."""
|
|
279
|
+
parts = [
|
|
280
|
+
_OUTPUT_FORMAT,
|
|
281
|
+
"",
|
|
282
|
+
_build_deck_schema_section(manifest),
|
|
283
|
+
"",
|
|
284
|
+
_build_layouts_section(manifest),
|
|
285
|
+
"",
|
|
286
|
+
_TEMPLATE_CONTENT_RULES,
|
|
287
|
+
"",
|
|
288
|
+
_STYLE_GUIDE,
|
|
289
|
+
]
|
|
290
|
+
return "\n".join(parts) + "\n"
|
|
225
291
|
|
|
226
292
|
|
|
227
293
|
# ---------------------------------------------------------------------------
|
|
@@ -231,9 +297,7 @@ def _build_template_section(manifest: ManifestDocument) -> str:
|
|
|
231
297
|
|
|
232
298
|
def build_schema_document(template_dir: Path | None = None) -> str:
|
|
233
299
|
"""Return the full reference document as a string."""
|
|
234
|
-
parts = [_GENERIC_SCHEMA]
|
|
235
300
|
if template_dir is not None:
|
|
236
301
|
manifest = load_effective_manifest(template_dir)
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
return "\n".join(parts)
|
|
302
|
+
return _build_template_schema(manifest)
|
|
303
|
+
return _GENERIC_SCHEMA
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from unittest.mock import patch
|
|
4
|
+
|
|
5
|
+
from typer.testing import CliRunner
|
|
6
|
+
|
|
7
|
+
from pptx_cli.cli import app
|
|
8
|
+
from pptx_cli.commands.schema import build_schema_document
|
|
9
|
+
|
|
10
|
+
runner = CliRunner()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_generic_schema_contains_deck_structure() -> None:
|
|
14
|
+
text = build_schema_document()
|
|
15
|
+
assert "<slide-schema>" in text
|
|
16
|
+
assert "metadata:" in text
|
|
17
|
+
assert "slides:" in text
|
|
18
|
+
assert "title:" in text
|
|
19
|
+
assert "body:" in text
|
|
20
|
+
assert "notes:" in text
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_generic_schema_documents_content_types() -> None:
|
|
24
|
+
text = build_schema_document()
|
|
25
|
+
assert "kind: image" in text
|
|
26
|
+
assert "kind: table" in text
|
|
27
|
+
assert "kind: chart" in text
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_generic_schema_uses_xml_sections() -> None:
|
|
31
|
+
text = build_schema_document()
|
|
32
|
+
assert "<slide-schema>" in text
|
|
33
|
+
assert "</slide-schema>" in text
|
|
34
|
+
assert "<content-rules>" in text
|
|
35
|
+
assert "</content-rules>" in text
|
|
36
|
+
assert "<style-guide>" in text
|
|
37
|
+
assert "</style-guide>" in text
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_generic_schema_omits_layout() -> None:
|
|
41
|
+
text = build_schema_document()
|
|
42
|
+
assert "Do NOT include" in text
|
|
43
|
+
assert "layout is assigned later" in text
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_schema_command_outputs_text(tmp_path: str) -> None:
|
|
47
|
+
result = runner.invoke(app, ["schema", "--no-template", "--no-copy"])
|
|
48
|
+
assert result.exit_code == 0
|
|
49
|
+
assert "metadata:" in result.stdout
|
|
50
|
+
assert "slides:" in result.stdout
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_schema_command_copies_to_clipboard() -> None:
|
|
54
|
+
with patch("pptx_cli.cli.copy_to_clipboard", return_value=True) as mock_copy:
|
|
55
|
+
result = runner.invoke(app, ["schema", "--no-template"])
|
|
56
|
+
assert result.exit_code == 0
|
|
57
|
+
mock_copy.assert_called_once()
|
|
58
|
+
assert "copied to clipboard" in result.stderr
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_schema_command_no_copy_skips_clipboard() -> None:
|
|
62
|
+
with patch("pptx_cli.cli.copy_to_clipboard") as mock_copy:
|
|
63
|
+
result = runner.invoke(app, ["schema", "--no-template", "--no-copy"])
|
|
64
|
+
assert result.exit_code == 0
|
|
65
|
+
mock_copy.assert_not_called()
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_schema_with_template(template_path: str, manifest_dir: str) -> None:
|
|
69
|
+
# First init a manifest so we can point --template at it
|
|
70
|
+
init_result = runner.invoke(
|
|
71
|
+
app,
|
|
72
|
+
["init", str(template_path), "--out", str(manifest_dir), "--format", "json"],
|
|
73
|
+
)
|
|
74
|
+
assert init_result.exit_code == 0
|
|
75
|
+
|
|
76
|
+
result = runner.invoke(app, ["schema", "--template", str(manifest_dir), "--no-copy"])
|
|
77
|
+
assert result.exit_code == 0
|
|
78
|
+
assert "<layouts>" in result.stdout
|
|
79
|
+
assert "<deck-schema>" in result.stdout
|
|
80
|
+
assert "layout:" in result.stdout
|
|
81
|
+
assert "content:" in result.stdout
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_template_schema_unified_sections(template_path: str, manifest_dir: str) -> None:
|
|
85
|
+
init_result = runner.invoke(
|
|
86
|
+
app,
|
|
87
|
+
["init", str(template_path), "--out", str(manifest_dir), "--format", "json"],
|
|
88
|
+
)
|
|
89
|
+
assert init_result.exit_code == 0
|
|
90
|
+
|
|
91
|
+
result = runner.invoke(app, ["schema", "--template", str(manifest_dir), "--no-copy"])
|
|
92
|
+
assert result.exit_code == 0
|
|
93
|
+
assert "<deck-schema>" in result.stdout
|
|
94
|
+
assert "<layouts>" in result.stdout
|
|
95
|
+
assert "<content-rules>" in result.stdout
|
|
96
|
+
assert "<style-guide>" in result.stdout
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def test_template_schema_has_layout_in_examples(template_path: str, manifest_dir: str) -> None:
|
|
100
|
+
init_result = runner.invoke(
|
|
101
|
+
app,
|
|
102
|
+
["init", str(template_path), "--out", str(manifest_dir), "--format", "json"],
|
|
103
|
+
)
|
|
104
|
+
assert init_result.exit_code == 0
|
|
105
|
+
|
|
106
|
+
result = runner.invoke(app, ["schema", "--template", str(manifest_dir), "--no-copy"])
|
|
107
|
+
assert result.exit_code == 0
|
|
108
|
+
assert "layout:" in result.stdout
|
|
109
|
+
assert "content:" in result.stdout
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def test_template_schema_no_generic_layout_warning(template_path: str, manifest_dir: str) -> None:
|
|
113
|
+
init_result = runner.invoke(
|
|
114
|
+
app,
|
|
115
|
+
["init", str(template_path), "--out", str(manifest_dir), "--format", "json"],
|
|
116
|
+
)
|
|
117
|
+
assert init_result.exit_code == 0
|
|
118
|
+
|
|
119
|
+
result = runner.invoke(app, ["schema", "--template", str(manifest_dir), "--no-copy"])
|
|
120
|
+
assert result.exit_code == 0
|
|
121
|
+
assert "Do NOT include" not in result.stdout
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def test_no_template_flag_emits_generic() -> None:
|
|
125
|
+
result = runner.invoke(app, ["schema", "--no-template", "--no-copy"])
|
|
126
|
+
assert result.exit_code == 0
|
|
127
|
+
assert "<slide-schema>" in result.stdout
|
|
128
|
+
assert "Do NOT include" in result.stdout
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def test_template_and_no_template_conflict() -> None:
|
|
132
|
+
result = runner.invoke(app, ["schema", "--template", "some/dir", "--no-template", "--no-copy"])
|
|
133
|
+
assert result.exit_code != 0
|
|
134
|
+
assert "mutually exclusive" in result.stderr
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from unittest.mock import patch
|
|
4
|
-
|
|
5
|
-
from typer.testing import CliRunner
|
|
6
|
-
|
|
7
|
-
from pptx_cli.cli import app
|
|
8
|
-
from pptx_cli.commands.schema import build_schema_document
|
|
9
|
-
|
|
10
|
-
runner = CliRunner()
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def test_generic_schema_contains_deck_structure() -> None:
|
|
14
|
-
text = build_schema_document()
|
|
15
|
-
assert "<slide-schema>" in text
|
|
16
|
-
assert "metadata:" in text
|
|
17
|
-
assert "slides:" in text
|
|
18
|
-
assert "title:" in text
|
|
19
|
-
assert "body:" in text
|
|
20
|
-
assert "notes:" in text
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def test_generic_schema_documents_content_types() -> None:
|
|
24
|
-
text = build_schema_document()
|
|
25
|
-
assert "kind: image" in text
|
|
26
|
-
assert "kind: table" in text
|
|
27
|
-
assert "kind: chart" in text
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def test_generic_schema_uses_xml_sections() -> None:
|
|
31
|
-
text = build_schema_document()
|
|
32
|
-
assert "<slide-schema>" in text
|
|
33
|
-
assert "</slide-schema>" in text
|
|
34
|
-
assert "<content-rules>" in text
|
|
35
|
-
assert "</content-rules>" in text
|
|
36
|
-
assert "<style-guide>" in text
|
|
37
|
-
assert "</style-guide>" in text
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def test_generic_schema_omits_layout() -> None:
|
|
41
|
-
text = build_schema_document()
|
|
42
|
-
assert "Do NOT include" in text
|
|
43
|
-
assert "layout is assigned later" in text
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def test_schema_command_outputs_text(tmp_path: str) -> None:
|
|
47
|
-
result = runner.invoke(app, ["schema", "--no-copy"])
|
|
48
|
-
assert result.exit_code == 0
|
|
49
|
-
assert "metadata:" in result.stdout
|
|
50
|
-
assert "slides:" in result.stdout
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
def test_schema_command_copies_to_clipboard() -> None:
|
|
54
|
-
with patch("pptx_cli.cli.copy_to_clipboard", return_value=True) as mock_copy:
|
|
55
|
-
result = runner.invoke(app, ["schema"])
|
|
56
|
-
assert result.exit_code == 0
|
|
57
|
-
mock_copy.assert_called_once()
|
|
58
|
-
assert "copied to clipboard" in result.stderr
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def test_schema_command_no_copy_skips_clipboard() -> None:
|
|
62
|
-
with patch("pptx_cli.cli.copy_to_clipboard") as mock_copy:
|
|
63
|
-
result = runner.invoke(app, ["schema", "--no-copy"])
|
|
64
|
-
assert result.exit_code == 0
|
|
65
|
-
mock_copy.assert_not_called()
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
def test_schema_with_template(template_path: str, manifest_dir: str) -> None:
|
|
69
|
-
# First init a manifest so we can point --template at it
|
|
70
|
-
init_result = runner.invoke(
|
|
71
|
-
app,
|
|
72
|
-
["init", str(template_path), "--out", str(manifest_dir), "--format", "json"],
|
|
73
|
-
)
|
|
74
|
-
assert init_result.exit_code == 0
|
|
75
|
-
|
|
76
|
-
result = runner.invoke(app, ["schema", "--template", str(manifest_dir), "--no-copy"])
|
|
77
|
-
assert result.exit_code == 0
|
|
78
|
-
assert "template:" in result.stdout
|
|
79
|
-
assert "layouts:" in result.stdout
|
|
80
|
-
assert "placeholders:" in result.stdout
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pptx_cli-1.3.4 → pptx_cli-1.3.5}/.github/skills/pptx-deck-builder/references/excal-diagrams.md
RENAMED
|
File without changes
|
{pptx_cli-1.3.4 → pptx_cli-1.3.5}/.github/skills/pptx-deck-builder/references/mckinsey-style.md
RENAMED
|
File without changes
|
{pptx_cli-1.3.4 → pptx_cli-1.3.5}/.github/skills/pptx-deck-builder/references/pptx-workflow.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|