Skip to content

Commit 2543739

Browse files
Garfield-yinclaude
andcommitted
docs: stop exposing how a conversion is produced
The format matrix labelled cells "chained", "local" and "copy", which said something about DeckRender's internals and nothing a user can act on. Worse, it read as a hierarchy: a supported route looked second class because it happened to take two steps instead of one. Supported is supported. The matrix now says yes / soon / not supported, in the CLI, the README and docs/formats.md alike. `--json` still reports the exact route for tooling that wants it. Error and caveat text followed the same rule — messages named the backend task or the engine that produced them ("DeckOps has no converter for it", "the local engine"), which told users about the architecture rather than about their document. They now describe the capability: before: --width is not supported for .key input. The backend task convertor.keynote2image accepts no resolution parameters. after: --width is not supported for .key input. Rendering .key offers no resolution control. The first-page limitation on Pages and Numbers stays stated plainly, since that changes what you receive rather than how it was made. Also corrects the README, which claimed rendering always runs in the cloud — untrue since Pages and Numbers started rendering locally. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent afd1aa7 commit 2543739

9 files changed

Lines changed: 107 additions & 110 deletions

File tree

README.md

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -74,23 +74,23 @@ $ deckrender deck.pptx --json
7474
deckrender formats
7575
```
7676
77-
| Input | → image | → pdf | → video |
78-
| ----------------------- | -------- | ---------- | ---------- |
79-
| `.pptx` `.ppt` | | | |
80-
| `.pdf` | | copy | 🕓 |
81-
| `.key` | | | 🕓 |
82-
| `.docx` `.doc` | chained | | |
83-
| `.xlsx` | 🕓 | 🕓 | |
84-
| `.pages` | local ⚠️ | local ⚠️ | |
85-
| `.numbers` | local ⚠️ | local ⚠️ | |
86-
| `.html` `.htm` and URLs | | chained ⚠️ | chained ⚠️ |
87-
| `.md` | | | |
77+
| Input | → image | → pdf | → video |
78+
| -------------- | ------- | ----- | ------- |
79+
| `.pptx` `.ppt` ||||
80+
| `.pdf` || | 🕓 |
81+
| `.key` ||| 🕓 |
82+
| `.docx` `.doc` | | ||
83+
| `.xlsx` | 🕓 | 🕓 ||
84+
| `.pages` | |||
85+
| `.numbers` | |||
86+
| `.html` + URLs ||| |
87+
| `.md` ||||
8888
8989
Image output supports `png`, `jpg` and `webp` via `--image-format`.
9090
91-
**local** runs on this machine with no network — Pages and Numbers have no cloud converter, so DeckRender extracts the preview embedded in the document (first page only). **🕓** is planned but not built; those report `not_implemented` with a message naming what is blocking them.
91+
**🕓** is planned but not built; those report `not_implemented` with a message naming what is blocking them. Unsupported pairs fail with a clear message rather than producing something approximate.
9292
93-
"chained" means DeckRender runs more than one backend task — slower, and sometimes with a fidelity note. Every render reports the exact task chain it used in `--json`'s `route` field, so nothing is hidden. Unsupported pairs fail with a clear message rather than producing something approximate.
93+
Pages and Numbers render their embedded first-page preview — see [`docs/formats.md`](docs/formats.md) for that and the other per-format notes.
9494
9595
Full detail, including which flags each route accepts: [`docs/formats.md`](docs/formats.md).
9696
@@ -164,7 +164,7 @@ Input (file | URL | stdin)
164164
165165
Rendering is performed by [`@deckops/sdk`](https://www.npmjs.com/package/@deckops/sdk). DeckRender contributes the input model, the render routing, artifact naming, and a stable output contract.
166166
167-
Rendering always runs in the DeckFlow cloud today. A local engine (Playwright for HTML, pdfium for PDF) is planned for v0.2the `RenderEngine` seam is already in place, so adding it will not disturb the rest of the pipeline.
167+
Most rendering runs in the DeckFlow cloud; a few formats are handled on your machine. Which is which is an implementation detail`deckrender formats` reports what works, and `--json` reports how it was produced if you need to know.
168168
169169
## Contributing
170170

docs/cli.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ Pages are 1-based and ranges are inclusive. Requesting a page past the end of th
108108

109109
`--pages` filters at download time — **the backend still renders every page**, so this saves bandwidth and disk, not compute or cost. It does not apply to single-file output (pdf, video) or to HTML and Markdown, which produce one image.
110110

111-
On chained webp routes the filter is applied before the per-frame conversion, so unwanted frames are never converted.
111+
For webp output the filter is applied before conversion, so narrowing the range also narrows the work.
112112

113113
## Commands
114114

docs/errors.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ Planned, but not built. The message says "coming soon" and names what is blockin
7878

7979
```
8080
Error: Rendering .xlsx to pdf is coming soon — not supported yet.
81-
Spreadsheet rendering needs a layout engine DeckRender does not ship yet, and
82-
DeckOps has no converter for it. Track it in docs/roadmap.md.
81+
Spreadsheet rendering needs a layout engine DeckRender does not have yet.
82+
Track it in docs/roadmap.md.
8383
```
8484

8585
The distinction from `unsupported_format` matters: one is worth waiting for, the

docs/formats.md

Lines changed: 51 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -7,32 +7,28 @@ deckrender formats --json # machine-readable, includes the backend task chain
77

88
## What converts to what
99

10-
| Input | → image | → pdf | → video |
11-
| ----------------------- | -------- | ---------- | ---------- |
12-
| `.pptx` `.ppt` | | | |
13-
| `.pdf` | | copy | 🕓 |
14-
| `.key` | | | 🕓 |
15-
| `.docx` `.doc` | chained | | |
16-
| `.xlsx` | 🕓 | 🕓 | |
17-
| `.pages` | local ⚠️ | local ⚠️ | |
18-
| `.numbers` | local ⚠️ | local ⚠️ | |
19-
| `.html` `.htm` and URLs | | chained ⚠️ | chained ⚠️ |
20-
| `.md` | | | |
10+
| Input | → image | → pdf | → video |
11+
| ----------------------- | ------- | ----- | ------- |
12+
| `.pptx` `.ppt` ||||
13+
| `.pdf` || | 🕓 |
14+
| `.key` ||| 🕓 |
15+
| `.docx` `.doc` | | ||
16+
| `.xlsx` | 🕓 | 🕓 ||
17+
| `.pages` | | | |
18+
| `.numbers` | | | |
19+
| `.html` `.htm` and URLs ||| |
20+
| `.md` ||||
2121

2222
Image output supports `png`, `jpg` and `webp`.
2323

24-
**chained** means more than one backend task runs. It is slower, and `--json`'s `route` always shows the exact chain:
24+
**🕓** means planned but not built. Those fail with `not_implemented` and a message naming what is blocking them, rather than pretending the combination is impossible.
25+
26+
How a conversion is produced — one step or several, in the cloud or on this machine — is DeckRender's problem, not yours. `--json` reports the exact route if you want it:
2527

2628
```json
2729
"route": ["convertor.doc2pdf", "convertor.pdf2image"]
2830
```
2931

30-
**copy** means the input is already in the target format, so DeckRender copies it rather than re-rendering.
31-
32-
**local** means it runs on this machine — no network, no credentials.
33-
34-
**🕓** means planned but not built. Those fail with `not_implemented` and a message naming what is blocking them, rather than pretending the combination is impossible.
35-
3632
An unsupported pair fails immediately with the alternatives spelled out:
3733

3834
```
@@ -43,7 +39,7 @@ Error: Cannot render .pdf to video.
4339

4440
## Which flags each route accepts
4541

46-
Sizing and encoding options land on whichever backend task actually produces the image. For chained routes that is the last one — which is why `docx → image` accepts `--width` while `docx → pdf` does not.
42+
Not every option applies everywhere. What a route can honour depends on how the image is produced, which is why `docx → image` accepts `--width` while `docx → pdf` does not.
4743

4844
| Input → image | `--width` / `--scale` | `--image-format` | `--pages` |
4945
| ------------------- | ---------------------------- | ---------------- | ----------- |
@@ -59,49 +55,60 @@ Sizing and encoding options land on whichever backend task actually produces the
5955

6056
PDF and video output accept no sizing, encoding, quality or page options — the backend tasks have no such parameters.
6157

62-
## Notes on specific routes
63-
64-
### html → pdf ⚠️
65-
66-
HTML is rebuilt as PPTX before PDF export, so layout is constrained by the slide model. DeckRender warns on stderr and reports a `caveat` in `--json`.
67-
68-
For a faithful HTML→PDF today, render to image instead. A native path arrives with the local engine in v0.2.
69-
70-
### URLs
71-
72-
The page is fetched by DeckRender and given a `<base href>` so relative assets resolve. The resulting HTML is uploaded as the cloud task's source file. Scripts still execute — the backend renders in a real browser.
73-
74-
### webp
75-
76-
Each frame becomes its own conversion task, four at a time. A 40-slide deck rendered to webp creates 41 tasks. Combining with `--pages` filters first, so `--pages 1-2` on that deck creates 3 tasks rather than 41.
58+
## Per-format notes
7759

78-
### Pages and Numbers ⚠️
60+
Everything in the matrix works. These notes are about what the output _is_ — the
61+
cases where the result differs from a plain full-fidelity render.
7962

80-
DeckOps has no converter for these. Every iWork document embeds a `preview.jpg` of its **first page**, so DeckRender extracts that instead of refusing the format — no network, no credentials, no dependencies.
63+
### Pages and Numbers — first page only
8164

82-
It is a preview, not a render: one page, at whatever resolution iWork saved, so `--width`, `--scale`, `--quality` and `--image-format` have nothing to act on. `--format pdf` wraps that JPEG in a single-page PDF.
65+
These render the preview iWork embeds in the document, which covers the **first
66+
page only**, at whatever resolution iWork saved. `--width`, `--scale`,
67+
`--quality` and `--image-format` have nothing to act on; `--format pdf` returns
68+
that single page as a PDF.
8369

84-
Both shapes iWork writes are accepted — a single file, and the directory bundle Finder shows as one document:
70+
Both shapes iWork writes are accepted — a single file, and the directory bundle
71+
Finder shows as one document:
8572

8673
```bash
8774
deckrender report.pages -o preview.jpg
8875
deckrender ~/Documents/report.pages -o preview.pdf # directory bundle
8976
```
9077

91-
Keynote does **not** use this path — `.key` has real cloud converters and renders every slide.
78+
Keynote is unaffected: `.key` renders every slide.
79+
80+
### HTML to PDF and video — laid out as slides
9281

93-
### Spreadsheets 🕓
82+
HTML is reconstructed as a slide document before export, so layout follows the
83+
slide model rather than the browser's. The video is a slideshow of that
84+
reconstruction, not a capture of the live page. DeckRender warns on stderr and
85+
reports a `caveat` in `--json`.
9486

95-
`.xlsx` has no cloud converter, and rendering a workbook locally needs a real layout pass. It reports `not_implemented` rather than failing silently. `.numbers` falls back to the embedded preview above.
87+
For the most faithful HTML output today, render to image.
88+
89+
### URLs
90+
91+
The page is fetched and given a `<base href>` so relative assets resolve.
92+
Scripts still execute — rendering happens in a real browser.
9693

9794
### Video
9895

99-
`.pptx`, `.ppt` and `.html` convert to video. The backend accepts no video parameters, so `--fps`, `--duration` and `--transition` fail with an explanation rather than being quietly ignored. PDF and Keynote to video are planned and need local frame assembly. See the [roadmap](roadmap.md).
96+
`.pptx`, `.ppt` and `.html` convert to video. There are no video parameters yet,
97+
so `--fps`, `--duration` and `--transition` fail with an explanation rather than
98+
being quietly ignored. PDF and Keynote to video are on the
99+
[roadmap](roadmap.md).
100+
101+
### webp
102+
103+
Each frame is converted individually, four at a time, so a long deck takes
104+
noticeably longer in webp than in png. `--pages` is applied first, so narrowing
105+
the range also narrows the work.
100106

101107
### No SVG
102108

103-
DeckOps has no task that emits SVG, so `--image-format svg` is not offered. Offering a value that always fails would be worse than leaving it out.
109+
Nothing in the pipeline emits SVG, so `--image-format svg` is not offered.
110+
Offering a value that always fails would be worse than leaving it out.
104111

105112
## Behind the matrix
106113

107-
Every cloud route maps to a task type in [`@deckops/sdk`](https://www.npmjs.com/package/@deckops/sdk); `deckrender formats --json` names the exact task chain for each one. The matrix is verified end to end by `scripts/conformance.mjs`, which renders a real document through every combination and diffs the result against this page.
114+
`deckrender formats --json` reports how each combination is produced, if you need it. The matrix is verified end to end by `scripts/conformance.mjs`, which renders a real document through every combination and diffs the result against this page.

docs/roadmap.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@ Also planned: switch URL input to the backend's `html.getByURL` once it reaches
2222

2323
Combinations the CLI marks `soon` and reports as `not_implemented`:
2424

25-
| Combination | Blocked on |
26-
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
27-
| `.xlsx` → image, pdf | A spreadsheet layout engine. DeckOps has no converter, and a workbook needs a real layout pass — not something to fake from an embedded preview. |
28-
| `.pdf` → video | Local frame assembly (ffmpeg), not bundled. |
29-
| `.key` → video | Local frame assembly (ffmpeg), not bundled. |
25+
| Combination | Blocked on |
26+
| -------------------- | ------------------------------------------------------------------------------------------------------------------ |
27+
| `.xlsx` → image, pdf | A spreadsheet layout engine. A workbook needs a real layout pass — not something to fake from an embedded preview. |
28+
| `.pdf` → video | Local frame assembly (ffmpeg), not bundled. |
29+
| `.key` → video | Local frame assembly (ffmpeg), not bundled. |
3030

31-
`.pages` and `.numbers` reached coverage a different way: they have no cloud converter either, but every iWork document embeds a first-page `preview.jpg`, and extracting that needs only a ZIP reader. That ships today as the `local` engine, labelled as a preview rather than a render. A real renderer for them stays on this list in spiritthe preview is a floor, not the goal.
31+
`.pages` and `.numbers` are supported today by rendering the first-page preview every iWork document embeds. That is a floor, not the goal: a full renderer for them stays on this list in spirit, and `docs/formats.md` states the first-page limitation plainly so nobody is surprised.
3232

3333
## v0.3 — video parameters
3434

src/cli/commands/formats.ts

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -27,42 +27,33 @@ export function registerFormatsCommand(program: Command, modeOf: (cmd: Command)
2727

2828
lines.push(
2929
'',
30-
`${chalk.green('yes')} one backend task`,
31-
`${chalk.yellow('chained')} several tasks; slower, and fidelity notes may apply`,
32-
`${chalk.green('copy')} already in the target format, copied without re-rendering`,
33-
`${chalk.cyan('local')} rendered on this machine, no network`,
34-
`${chalk.magenta('soon')} planned, not built yet`,
35-
chalk.dim('— not supported'),
30+
`${chalk.green('yes')} supported`,
31+
`${chalk.magenta('soon')} planned, not built yet`,
32+
chalk.dim('— not supported'),
3633
'',
3734
chalk.dim('Image output also supports --image-format png|jpg|webp.'),
38-
chalk.dim('Full details, including per-route flag support: docs/formats.md')
35+
chalk.dim('Per-format notes and flag support: docs/formats.md')
3936
);
4037

4138
reporter.say(lines.join('\n'), buildJsonMatrix());
4239
});
4340
}
4441

42+
/**
43+
* One matrix cell.
44+
*
45+
* Deliberately says only whether the combination works. How it is produced —
46+
* one backend task, a chain of them, or an engine on this machine — is an
47+
* implementation detail that would only invite users to read some supported
48+
* routes as second class. `--json` still carries the full route for tooling
49+
* that needs it.
50+
*/
4551
function cell(source: SourceFormat, target: TargetFormat): string {
46-
const route = ROUTES[source]?.[target];
47-
48-
if (!route) {
49-
// Pad before colouring: ANSI escapes would otherwise count toward width.
50-
return plannedReason(source, target) ? chalk.magenta('soon'.padEnd(10)) : chalk.dim('—'.padEnd(10));
52+
// Pad before colouring: ANSI escapes would otherwise count toward width.
53+
if (ROUTES[source]?.[target]) {
54+
return chalk.green('yes'.padEnd(10));
5155
}
52-
53-
const label =
54-
route.kind === 'passthrough'
55-
? 'copy'
56-
: route.kind === 'local'
57-
? 'local'
58-
: route.kind === 'direct'
59-
? 'yes'
60-
: 'chained';
61-
const padded = label.padEnd(10);
62-
63-
if (route.kind === 'derived') return chalk.yellow(padded);
64-
if (route.kind === 'local') return chalk.cyan(padded);
65-
return chalk.green(padded);
56+
return plannedReason(source, target) ? chalk.magenta('soon'.padEnd(10)) : chalk.dim('—'.padEnd(10));
6657
}
6758

6859
function buildJsonMatrix(): Record<string, unknown> {

src/core/plan.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ function unsupportedFormat(source: SourceFormat, target: TargetFormat): DeckRend
136136
const message =
137137
supported.length > 0
138138
? `Cannot render .${source} to ${target}. Supported outputs for .${source}: ${supported.join(', ')}.`
139-
: `Cannot render .${source}. Neither DeckOps nor the local engine handles this format.`;
139+
: `Cannot render .${source}. This format is not supported.`;
140140

141141
return DeckRenderError.unsupportedFormat(message, {
142142
hint: 'Run `deckrender formats` or see docs/formats.md for the full support matrix.',
@@ -201,7 +201,7 @@ function validateOptions(input: PlanInput, routeTasks: DeckTaskType[], warnings:
201201

202202
if (capability.resolution.kind === 'none') {
203203
const workaround =
204-
`The backend task ${imageTask} accepts no resolution parameters. ` +
204+
`Rendering .${input.source} offers no resolution control. ` +
205205
'Workaround: render to PDF first, then run deckrender on the PDF.';
206206
if (effective.width !== undefined) {
207207
reject('width', '--width', `--width is not supported for .${input.source} input.`, workaround);
@@ -214,7 +214,7 @@ function validateOptions(input: PlanInput, routeTasks: DeckTaskType[], warnings:
214214
'quality',
215215
'--quality',
216216
`--quality is not supported for .${input.source} input.`,
217-
`The backend task ${imageTask} accepts no resolution or encoding parameters.`
217+
`Rendering .${input.source} offers no resolution or encoding control.`
218218
);
219219
}
220220
}
@@ -224,7 +224,7 @@ function validateOptions(input: PlanInput, routeTasks: DeckTaskType[], warnings:
224224
'imageFormat',
225225
'--image-format',
226226
`--image-format jpg is not supported for .${input.source} input.`,
227-
`The backend task ${imageTask} always emits PNG. Use png or webp.`
227+
`Rendering .${input.source} always produces PNG. Use png or webp.`
228228
);
229229
}
230230

@@ -233,7 +233,7 @@ function validateOptions(input: PlanInput, routeTasks: DeckTaskType[], warnings:
233233
'pages',
234234
'--pages',
235235
`--pages is not supported for .${input.source} input, which renders to a single image.`,
236-
`The backend task ${imageTask} produces one full-page image.`
236+
`Rendering .${input.source} produces one full-page image.`
237237
);
238238
}
239239
}

0 commit comments

Comments
 (0)