document-cli 1.11.11 → 1.11.13
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/README.md +58 -11
- package/dist/cli.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,6 +14,7 @@ graph TD
|
|
|
14
14
|
pdfcodec("pdf-codec")
|
|
15
15
|
mdcodec("markdown-codec")
|
|
16
16
|
documents("documents.js")
|
|
17
|
+
mcp("document-mcp")
|
|
17
18
|
cli("document-cli")
|
|
18
19
|
|
|
19
20
|
schema --> ooxml
|
|
@@ -25,8 +26,11 @@ graph TD
|
|
|
25
26
|
odf --> documents
|
|
26
27
|
pdfcodec --> documents
|
|
27
28
|
mdcodec --> documents
|
|
29
|
+
documents --> mcp
|
|
30
|
+
pdfcodec --> mcp
|
|
28
31
|
documents --> cli
|
|
29
32
|
odf --> cli
|
|
33
|
+
pdfcodec --> cli
|
|
30
34
|
|
|
31
35
|
click schema "https://github.com/ExaDev/document-schema.js" "document-schema.js"
|
|
32
36
|
click ooxml "https://github.com/ExaDev/ooxml.js" "ooxml.js"
|
|
@@ -34,6 +38,7 @@ graph TD
|
|
|
34
38
|
click pdfcodec "https://github.com/ExaDev/pdf-codec" "pdf-codec"
|
|
35
39
|
click mdcodec "https://github.com/ExaDev/markdown-codec" "markdown-codec"
|
|
36
40
|
click documents "https://github.com/ExaDev/documents.js" "documents.js"
|
|
41
|
+
click mcp "https://github.com/ExaDev/document-mcp" "document-mcp"
|
|
37
42
|
click cli "https://github.com/ExaDev/document-cli" "document-cli"
|
|
38
43
|
|
|
39
44
|
style cli fill:#f9a825,stroke:#333,stroke-width:3px
|
|
@@ -41,7 +46,9 @@ graph TD
|
|
|
41
46
|
|
|
42
47
|
## Why
|
|
43
48
|
|
|
44
|
-
`documents.js` is a library, not a tool — everything it does happens through function calls from TypeScript/JavaScript. Most people who want to convert a docx to a PDF, extract an `.odb` table to CSV, or poke at a PDF's structure from a terminal don't want to write a script to do it. `document-cli` is that missing entry point: every one of documents.js's 27 direct conversion pairs, its generic converter, its `.odm`/`.odb` extraction functions
|
|
49
|
+
`documents.js` is a library, not a tool — everything it does happens through function calls from TypeScript/JavaScript. Most people who want to convert a docx to a PDF, extract an `.odb` table to CSV, or poke at a PDF's structure from a terminal don't want to write a script to do it. `document-cli` is that missing entry point: every one of documents.js's 27 direct conversion pairs, its generic converter, its `.odm`/`.odb` extraction functions (including a bounded SQL engine over an `.odb`'s own tables and full report rendering), its PDF inspector, and its document metadata/source-font introspection become a single command-line invocation, and its seven live-view editors (docx/pptx/odt/odp/ods/odg/markdown) become a keyboard-driven terminal app that never needs a code editor open at all.
|
|
50
|
+
|
|
51
|
+
[`document-mcp`](https://github.com/ExaDev/document-mcp) is the sibling frontend over the identical `documents.js` library — an MCP server rather than a terminal CLI/TUI — so the two are independent consumers of one shared implementation, each exposing whatever subset of it suits a human at a terminal versus an MCP-speaking agent.
|
|
45
52
|
|
|
46
53
|
The CLI and the TUI are deliberately not two separate implementations of the same logic. The TUI's own document-opening, saving, and PDF-export code (`src/tui/format/`) calls the identical `documents.js` functions the CLI commands call — `openDocx`/`createDocx`/`docxToPdf` and their five siblings per format, plus `readOdbTables`/`readPdf` for the two read-only sources — so there is exactly one place either surface can drift from what documents.js itself does: nowhere.
|
|
47
54
|
|
|
@@ -82,7 +89,7 @@ document-cli convert report.docx report.pdf
|
|
|
82
89
|
document-cli convert report.docx - --to pdf > report.pdf
|
|
83
90
|
```
|
|
84
91
|
|
|
85
|
-
**`formats`** — lists every `source -> target` pair the commands above support (`--json` for a machine-readable array), plus a pointer to the commands not on that list because they don't fit the source/target shape (`odm-to-pdf`, `odb-to-csv`, `odb-to-xlsx`, `odb-tables`, `odb-forms`, `odb-reports`, `pdf-inspect`, `from-package`).
|
|
92
|
+
**`formats`** — lists every `source -> target` pair the commands above support (`--json` for a machine-readable array), plus a pointer to the commands not on that list because they don't fit the source/target shape (`odm-to-pdf`, `odb-to-csv`, `odb-to-xlsx`, `odb-tables`, `odb-forms`, `odb-reports`, `pdf-inspect`, `from-package`, `fonts`, `docx-extras`, `metadata`, `set-metadata`).
|
|
86
93
|
|
|
87
94
|
**`from-package <input> [output]`** — reads back a `DocumentPackage` JSON file a previous conversion wrote via `--dump-package` (below) and exports it to a real target format, closing the round trip `--dump-package` otherwise has no return path for. Target resolution matches `convert`: an output path's own extension, or `--to <format>` when it doesn't have one. `pdf` writes the package's own `layout` half directly (`writePdf`); every other format builds a fresh package from the `content` half through the identical `buildXPackage` function the matching `pdf-to-X`/bridge conversion already uses. `xlsx` and `odf` are rejected outright — documents.js exposes no `ContentDocument`-to-xlsx builder (convert to `ods` and run `ods-to-xlsx` instead) and a formula document has no write path from `ContentDocument` at all. Only a file genuinely written by `--dump-package` round-trips here; anything else fails with its `$schema` mismatch named:
|
|
88
95
|
|
|
@@ -108,6 +115,19 @@ document-cli odb-forms sales.odb
|
|
|
108
115
|
document-cli odb-reports sales.odb --json
|
|
109
116
|
```
|
|
110
117
|
|
|
118
|
+
**`odb-query <input>`** — runs a bounded single-table `SELECT` over every table an embedded `.odb` extracts (documents.js's own `src/odb/sql/` engine — no database anywhere in the path), given directly via `--sql <text>` or by naming one of the `.odb`'s own saved queries via `--query <savedName>` (the two are mutually exclusive, and one of them is required). `--json` emits the bare `{ columns, rows }` result set; otherwise a plain-text table:
|
|
119
|
+
|
|
120
|
+
```sh
|
|
121
|
+
document-cli odb-query sales.odb --sql "SELECT REGION, SUM(AMOUNT) FROM SALES GROUP BY REGION"
|
|
122
|
+
document-cli odb-query sales.odb --query HighValueSales --json
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**`odb-render-report <input> [output]`** — renders one of an `.odb`'s own reports to a real document: its data-source command resolved and run through the same SQL engine `odb-query` uses, its `rpt:` formulas evaluated, its bands laid out, then built into `docx`, `odt`, or `pdf` — the only three targets a rendered report can become, since `readOdbReportContent` always produces a `wordprocessing`-variant document and no other format has a wordprocessing counterpart to build one into. `--report <name>` selects which report when the `.odb` declares more than one; `--to <format>` when the target can't be inferred from the output path:
|
|
126
|
+
|
|
127
|
+
```sh
|
|
128
|
+
document-cli odb-render-report sales.odb SalesByRegion.pdf --report SalesByRegion
|
|
129
|
+
```
|
|
130
|
+
|
|
111
131
|
**`pdf-inspect <input>`** — reports a PDF's page count, per-page size and item-kind histogram, document metadata, and embedded image formats, without converting it to anything. `--full` dumps the entire parsed `LayoutDocument` as JSON instead of the summary:
|
|
112
132
|
|
|
113
133
|
```sh
|
|
@@ -115,11 +135,36 @@ document-cli pdf-inspect report.pdf
|
|
|
115
135
|
document-cli pdf-inspect report.pdf --json
|
|
116
136
|
```
|
|
117
137
|
|
|
138
|
+
**`fonts <input>`** — lists every source-embedded font face a docx/pptx/odt/odp/ods/odg document carries (family, weight/style, byte length) — the same embedded faces every `<format>-to-pdf` conversion already extracts and renders through automatically (see [Real fonts](#real-fonts) below); this command just reports what's there without converting anything. Rejects a format with no source-embedded-font concept at all (xlsx, pdf, markdown, odf), naming it:
|
|
139
|
+
|
|
140
|
+
```sh
|
|
141
|
+
document-cli fonts report.docx
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**`docx-extras <input>`** — prints a docx's own comments, footnotes, headers, footers, and numbering definitions: data `readDocxContent`'s `ContentDocument` shape has nowhere to carry, so documents.js reads it through a second, independent pass (`readDocxExtras`) over the same package. `--json` emits the raw `DocxExtras` object:
|
|
145
|
+
|
|
146
|
+
```sh
|
|
147
|
+
document-cli docx-extras report.docx
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**`metadata <input>`** — prints a document's own title/author/subject/keywords/creator/producer/created/modified metadata, for any of the ten supported formats (docx, pptx, xlsx, odt, odp, ods, odg, odf, markdown, pdf). `--json` emits the raw metadata object:
|
|
151
|
+
|
|
152
|
+
```sh
|
|
153
|
+
document-cli metadata report.pdf
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**`set-metadata <input> [output]`** — patches a document's own title/author/subject/keywords, leaving every other field untouched (`--set-title`, `--set-author`, `--set-subject`, `--set-keywords` — the last a comma-separated list); source and target format must match, so run `convert`/`from-package` first if a different target format is also needed. A `pdf` source/target patches the parsed PDF directly (`writePdf`) with no layout engine involved — genuinely lossless for everything else on the page; every other format rebuilds a fresh package from that format's own `ContentDocument`, which for docx specifically is lossy (it drops everything `docx-extras` covers, since `buildDocxPackage` has no way to carry that data through a `ContentDocument`-only rebuild):
|
|
157
|
+
|
|
158
|
+
```sh
|
|
159
|
+
document-cli set-metadata report.docx report.docx --set-title "Q3 Report" --set-author "Finance"
|
|
160
|
+
document-cli set-metadata report.docx report.odt --set-keywords "draft,internal"
|
|
161
|
+
```
|
|
162
|
+
|
|
118
163
|
**`tui [file]`** — launches the interactive terminal app; see [The TUI](#the-tui) below.
|
|
119
164
|
|
|
120
165
|
### Shared flags
|
|
121
166
|
|
|
122
|
-
The 27 explicit conversions, `convert`, `odm-to-pdf`, `odb-to-xlsx`,
|
|
167
|
+
The 27 explicit conversions, `convert`, `odm-to-pdf`, `odb-to-xlsx`, `odb-to-csv`, `set-metadata`, and `odb-render-report` — every command that reads one file and writes one — share:
|
|
123
168
|
|
|
124
169
|
| Flag | Meaning |
|
|
125
170
|
|---|---|
|
|
@@ -129,7 +174,7 @@ The 27 explicit conversions, `convert`, `odm-to-pdf`, `odb-to-xlsx`, and `odb-to
|
|
|
129
174
|
| `-q, --quiet` | Suppress diagnostic and summary output (the JSON result-summary line still prints in `--json` mode, so a script consuming NDJSON always gets a terminating record). |
|
|
130
175
|
| `--verbose` | Include a full stack trace in the error line when the run fails. |
|
|
131
176
|
|
|
132
|
-
`--dump-package <file>` is one flag further, registered only on the 27 explicit conversions and `convert` — it writes the intermediate `DocumentPackage` (content + layout) that conversion built to a JSON file, tagged with its own `$schema` so `from-package` (above) can read it back in. Every conversion populates one except `odf-to-pdf`, which accepts but never invokes its own `onDocument` callback (a standalone formula document has no `ContentDocument`/`LayoutDocument` pivot behind it); the ten PDF-bypassing bridges (`odt-to-docx`/`docx-to-odt`, `odp-to-pptx`/`pptx-to-odp`, `ods-to-xlsx`/`xlsx-to-ods`, `markdown-to-docx`/`docx-to-markdown`, `markdown-to-odt`/`odt-to-markdown`) populate one too, just with `layout` always `undefined`, since a bridge never runs a layout engine. `odm-to-pdf`/`odb
|
|
177
|
+
`--dump-package <file>` is one flag further, registered only on the 27 explicit conversions and `convert` — it writes the intermediate `DocumentPackage` (content + layout) that conversion built to a JSON file, tagged with its own `$schema` so `from-package` (above) can read it back in. Every conversion populates one except `odf-to-pdf`, which accepts but never invokes its own `onDocument` callback (a standalone formula document has no `ContentDocument`/`LayoutDocument` pivot behind it); the ten PDF-bypassing bridges (`odt-to-docx`/`docx-to-odt`, `odp-to-pptx`/`pptx-to-odp`, `ods-to-xlsx`/`xlsx-to-ods`, `markdown-to-docx`/`docx-to-markdown`, `markdown-to-odt`/`odt-to-markdown`) populate one too, just with `layout` always `undefined`, since a bridge never runs a layout engine. `odm-to-pdf`/`odb-*`/`set-metadata` don't expose the flag at all, since none of them goes through `DocumentConverter.convert` in the first place. `odb-tables`, `odb-forms`, `odb-reports`, `fonts`, `docx-extras`, `metadata`, `formats`, and `pdf-inspect` each take only their own `--json` (plus `pdf-inspect`'s own `--full`); `odb-query` takes `--sql <text>`/`--query <savedName>` (mutually exclusive) alongside its own `--json`, with none of the shared flags above since it only reads and writes nothing; `from-package` and `set-metadata` each take `--to <format>` alongside the shared flags in this table; `odb-render-report` takes `--report <name>` and `--to <format>` alongside the shared flags and the font flags below; `tui` takes no flags at all, only an optional positional file.
|
|
133
178
|
|
|
134
179
|
### Real fonts
|
|
135
180
|
|
|
@@ -146,7 +191,7 @@ The flag is repeatable, takes a `.ttf`/`.otf` path, and needs **no accompanying
|
|
|
146
191
|
|
|
147
192
|
`--report-font-substitutions` prints each face that resolved to something other than what the document asked for, as it happens, with its structured fields intact (`--json` makes it one more NDJSON record: `{"type":"font-substitution","requestedFamily":"Calibri",…}`). Without it, the same fallbacks are still reported — the `font/substituted` diagnostic lines every conversion already emits — just as rendered messages after the fact rather than structured events as they occur.
|
|
148
193
|
|
|
149
|
-
Both flags are registered only where they can do something: the nine `<format>-to-pdf` conversions, `convert`,
|
|
194
|
+
Both flags are registered only where they can do something: the nine `<format>-to-pdf` conversions, `convert`, `odm-to-pdf`, and `odb-render-report`. The last two register them unconditionally (like `convert`, whose target isn't known until the output path or `--to` resolves) even though a `docx`/`odt` render has nothing to resolve fonts against — the same non-pdf no-op every other command in this list already documents. A `pdf-to-<format>` reconstruction reads a PDF's own already-positioned glyphs and a format-to-format bridge runs no layout engine at all, so neither resolves a typeface and neither advertises the flags.
|
|
150
195
|
|
|
151
196
|
Diagnostics and the summary line always go to stderr; stdout is reserved for the converted bytes on any command writing to `-`.
|
|
152
197
|
|
|
@@ -157,7 +202,7 @@ Diagnostics and the summary line always go to stderr; stdout is reserved for the
|
|
|
157
202
|
| `0` | Success. |
|
|
158
203
|
| `1` | The input was unusable — a malformed or encrypted PDF, or any other conversion failure not covered by the codes below. |
|
|
159
204
|
| `2` | A usage error — bad flags, conflicting output destinations, an unrecognised format, or (for a bare/`--help`/`--version` invocation) commander's own exit path. |
|
|
160
|
-
| `3` | documents.js needs more information to finish, not a different file — an unresolved `.odm` chapter,
|
|
205
|
+
| `3` | documents.js needs more information to finish, not a different file — an unresolved `.odm` chapter, a `.odb` table that wasn't specified (or wasn't found, or has no embedded engine at all, or uses an unsupported HSQLDB script serialisation), or a `.odb` report that wasn't specified when the database declares more than one. |
|
|
161
206
|
| `124` | The run's own `--timeout` elapsed before it finished. |
|
|
162
207
|
| `130` | Interrupted by `SIGINT` (Ctrl+C). |
|
|
163
208
|
|
|
@@ -165,11 +210,11 @@ Diagnostics and the summary line always go to stderr; stdout is reserved for the
|
|
|
165
210
|
|
|
166
211
|
Launch it either bare (`document-cli`, with no arguments) or explicitly with `document-cli tui [file]` — both open the same app; the explicit form additionally opens `file` immediately, skipping the launcher screen. The TUI needs an interactive terminal: a bare invocation with redirected stdout prints help text instead, and an explicit `tui` invocation with redirected stdout fails outright, since there's no terminal for Ink to draw into.
|
|
167
212
|
|
|
168
|
-
It supports the same seven formats documents.js's live-view editors cover — docx, pptx, odt, odp, ods, odg, markdown — each with a full navigate/edit/save experience built on that format's editor (paragraphs and runs for docx/odt/markdown, slides and shapes for pptx/odp, sheets and cells for ods, pages and vectors/shapes for odg), plus undo (whole-document snapshots taken before each committed mutation), search, a command palette, and PDF export straight from the open document. On a pptx or odp slide, `a` from the shape list also adds a real table (rows then columns, a two-step prompt) alongside the existing textbox/image choices, and `n` opens the slide's own speaker notes for either format — `PptxSlide` and `OdpSlide` both carry a real `.notes` getter/setter, so notes editing was never odp-specific, only gated that way until this phase removed the gate.
|
|
213
|
+
It supports the same seven formats documents.js's live-view editors cover — docx, pptx, odt, odp, ods, odg, markdown — each with a full navigate/edit/save experience built on that format's editor (paragraphs and runs for docx/odt/markdown, slides and shapes for pptx/odp, sheets and cells for ods, pages and vectors/shapes for odg), plus undo (whole-document snapshots taken before each committed mutation), search, a command palette, and PDF export straight from the open document. On a pptx or odp slide, `a` from the shape list also adds a real table (rows then columns, a two-step prompt) alongside the existing textbox/image choices, and `n` opens the slide's own speaker notes for either format — `PptxSlide` and `OdpSlide` both carry a real `.notes` getter/setter, so notes editing was never odp-specific, only gated that way until this phase removed the gate. On a docx document, `x` from the paragraph/table list opens a read-only view of the document's own comments, footnotes, headers/footers, and numbering definitions — a docx-only concept with no odt equivalent, and the TUI counterpart to the `docx-extras` command, rendered through the identical `src/docx-extras-format.ts` line formatter so the two can't drift apart. `m`, global to every screen with a document open, shows that document's own title/author/subject/keywords/etc metadata read-only — every format the `metadata` command covers, including `.odb`/`.pdf`/`.xlsx`; documents.js's live-view editors have no metadata setter to mutate in place, so this TUI has no matching write screen (use the `set-metadata` command for that).
|
|
169
214
|
|
|
170
215
|
Markdown (`.md`/`.markdown`) shares the same paragraph/run/table body-list screens docx and odt already use, through documents.js's own `MarkdownEditor` (`openMarkdown`/`createMarkdownEditor`) — a genuine live view over a mutable `ContentDocument`, the same live-view contract every other editor here follows, even though there is no `XmlElement` tree underneath it the way there is for docx/odt (`MarkdownEditor.toMarkdownText()` re-serialises the whole document fresh on every call, rather than exposing a `toBytes()`). Appending a paragraph, appending a run, and toggling bold/italic all go through the identical reducer actions docx/odt use; a markdown run has no underline, colour, font family, or font size at all (CommonMark/GFM has no construct for any of the four), so those keys — along with image insertion, which `MarkdownParagraph` has no counterpart for — are simply absent from a markdown paragraph's own key hints rather than opening a prompt that could only end in a warning. A markdown table can be created and its cells edited through the same 'T' wizard and table-view screens docx/odt use, but GFM tables have no cell-merge concept, so a merge requested alongside table creation still creates the table (unmerged) and reports why the merge itself didn't happen. `:view-source` (markdown documents only) shows the literal text the document was opened with side by side with what a save would write right now — these can genuinely differ even with no edits made this session, from a heading-style, bullet-marker, or line-ending choice the writer normalises. Every save re-serialises the whole document fresh through `buildMarkdownText`, a deliberate, permanent consequence of structured editing rather than something to work around. Diagnostics from the read side (a clamped heading level, a dropped front-matter key, a fenced code block's own info string with nowhere to go, …) now surface into the same diagnostics panel a PDF export's own substitutions already populate, the moment a `.md` file is opened, not only on export. documents.js's own `createMarkdownEditor()` exists now, but this TUI does not yet wire a "new markdown document" flow into `:new`/the new-document picker, so a markdown document can still only be opened from an existing file.
|
|
171
216
|
|
|
172
|
-
Three further formats open read-only: a `.odb` browses its tables and rows with no write path at all (documents.js's own `.odb` support has no write direction to offer), a `.pdf` browses its pages and positioned items rather than being edited in place, and a `.xlsx` opens as a converted PDF preview — documents.js has no xlsx editor to hold a live view into, so opening one runs `xlsxToPdf` once at open time and browses the result through the identical page-list/page-items/item-detail screens a real `.pdf` uses, with the original bytes kept alongside so a later export re-runs `xlsxToPdf` with the caller's own fonts and diagnostics rather than reusing the fixed preview conversion. A `.odb` additionally browses its *structure* alongside its data: `f` from the table list opens the form browser and `r` the report browser, each listing what the database declares and opening one to show it in full — a form's own data source and field-bound controls (sub-forms nested under their parent), a report's data-source command, band and group structure, and every `rpt:` formula. Both are rendered through the same `src/odb-structure.ts` the `odb-forms`/`odb-reports` commands print, so the two views cannot drift apart, and search filters by line (`/SUM` narrows a long report to its aggregate expressions). A standalone `.odf` formula document has no TUI editor either — nothing to edit interactively, only a PDF conversion.
|
|
217
|
+
Three further formats open read-only: a `.odb` browses its tables and rows with no write path at all (documents.js's own `.odb` support has no write direction to offer), a `.pdf` browses its pages and positioned items rather than being edited in place, and a `.xlsx` opens as a converted PDF preview — documents.js has no xlsx editor to hold a live view into, so opening one runs `xlsxToPdf` once at open time and browses the result through the identical page-list/page-items/item-detail screens a real `.pdf` uses, with the original bytes kept alongside so a later export re-runs `xlsxToPdf` with the caller's own fonts and diagnostics rather than reusing the fixed preview conversion. A `.odb` additionally browses its *structure* alongside its data: `f` from the table list opens the form browser and `r` the report browser, each listing what the database declares and opening one to show it in full — a form's own data source and field-bound controls (sub-forms nested under their parent), a report's data-source command, band and group structure, and every `rpt:` formula. Both are rendered through the same `src/odb-structure.ts` the `odb-forms`/`odb-reports` commands print, so the two views cannot drift apart, and search filters by line (`/SUM` narrows a long report to its aggregate expressions). `Enter` on a report's own detail screen renders it — its data-source command resolved, its `rpt:` formulas evaluated, its bands laid out — to a real `docx`/`odt`/`pdf` file, through the same two-field destination-path-then-font-list form the PDF-export screen below uses; the TUI counterpart to the `odb-render-report` command. A standalone `.odf` formula document has no TUI editor either — nothing to edit interactively, only a PDF conversion.
|
|
173
218
|
|
|
174
219
|
The export-to-PDF screen (`e` from any editor screen) is a two-field form: a destination path, then an optional comma-separated list of local `.ttf`/`.otf` paths, which are the same `--font-file` faces the CLI takes and are derived the same way — each font's family, weight, and slope come from the file itself. `Enter` on the path field moves to the fonts field and `Enter` there exports, so leaving fonts empty is still "type a path, press Enter twice". Comma-separated rather than space-separated because a font path routinely contains spaces and almost never a comma. A face falling back to a substitute is reported into the same diagnostics panel a character substitution already is, and a bad font path fails the export with the file named, before anything is written to the destination.
|
|
175
220
|
|
|
@@ -186,8 +231,10 @@ The global bindings below apply everywhere; individual screens (a docx run's own
|
|
|
186
231
|
| `PageUp` / `PageDown` | Scroll a page at a time |
|
|
187
232
|
| `Home` / `End` | Jump to the first or last item |
|
|
188
233
|
| `a` | Append a new item to the current list |
|
|
234
|
+
| `m` | Show the open document's metadata (read-only) |
|
|
189
235
|
| `Ctrl+S` | Save the open document |
|
|
190
236
|
| `Ctrl+W` | Close the open document |
|
|
237
|
+
| `Ctrl+Z` | Undo the last change |
|
|
191
238
|
| `q` / `Ctrl+C` | Quit |
|
|
192
239
|
| `:` | Open the command palette |
|
|
193
240
|
| `/` | Search within the current screen |
|
|
@@ -198,7 +245,7 @@ The global bindings below apply everywhere; individual screens (a docx run's own
|
|
|
198
245
|
|
|
199
246
|
The package splits into two independent layers sharing one thin format-detection module, `src/format.ts` (extension ⇄ `DocumentFormat` inference), so a change to how a format is recognised from a path never needs making twice:
|
|
200
247
|
|
|
201
|
-
- **`src/commands/` + `src/runtime/`** is the CLI proper. `commands/shared.ts`'s `buildConversionAction(source, target)` is the one implementation behind every `<source>-to-<target>` command and the generic `convert` — it partially applies a format pair and hands back a ready commander action, so the conversion-running logic (read input, call `createLocalDocumentConverter().convert`, write output, report diagnostics, map errors to exit codes) exists exactly once regardless of which of the 27 pairs is invoked. `commands/{odm,odb,pdf-inspect}.ts` each call their own documents.js function directly instead, since none of
|
|
248
|
+
- **`src/commands/` + `src/runtime/`** is the CLI proper. `commands/shared.ts`'s `buildConversionAction(source, target)` is the one implementation behind every `<source>-to-<target>` command and the generic `convert` — it partially applies a format pair and hands back a ready commander action, so the conversion-running logic (read input, call `createLocalDocumentConverter().convert`, write output, report diagnostics, map errors to exit codes) exists exactly once regardless of which of the 27 pairs is invoked. `commands/{odm,odb,pdf-inspect,fonts,docx-extras,metadata}.ts` each call their own documents.js function directly instead, since none of them fits the generic `DocumentConverter` port's bytes-in/bytes-out shape (`odmToPdf` needs a `resolveSubDocument` callback, `.odb` extraction/query/report-rendering has no PDF conversion or port entry at all, `pdf-inspect` reads without converting, and `fonts`/`docx-extras`/`metadata` each read a document without producing one). `commands/set-metadata.ts` also bypasses `buildConversionAction` despite writing a file, since `setDocumentMetadata(source, target, bytes, overrides)` patches a document rather than converting one and requires source and target to match. `src/odb-structure.ts` sits alongside `src/format.ts` as a module shared by both layers: it turns an `OdbForm`/`OdbReport` into a flat array of already-indented lines, which the `odb-forms`/`odb-reports` commands join with newlines and the TUI's own form/report detail screens render one per list row; `src/docx-extras-format.ts` and `src/sql-result-format.ts` are the same idea for their own commands — the first turns a `DocxExtras` value into the flat line list `docx-extras` prints and the TUI's own `DocxExtrasScreen` renders, the second turns an `SqlResultSet` into an aligned plain-text table for `odb-query`. `src/runtime/` holds the process-level concerns every command shares: `abort.ts`'s `createRuntimeSignal` (one `SIGINT` listener and an optional timeout, combined into a single signal), `io.ts`'s stdin/stdout/file `-`-aware read and write helpers, `exit-codes.ts`'s exit-code constants and `mapErrorToExit`, `diagnostics.ts`'s stderr reporter, `fonts.ts`'s `loadProvidedFonts` (validating every `--font-file` path into documents.js's own `ProvidedFont` shape, shared by every font-accepting command and the TUI's own PDF-export and report-render screens), and `metadata-format.ts`'s `formatMetadataLines` (shared by `metadata`, `pdf-inspect`, and the TUI's own metadata screen).
|
|
202
249
|
- **`src/tui/`** is the Ink app, entered lazily. `src/cli.ts` only imports `./tui/index.js` inside a dynamic `import()`, called just once dispatch has already decided the TUI is actually running — a plain `document-cli docx-to-pdf a b` invocation never loads React, Ink, or any TUI screen module at all, and `tsdown.config.ts`'s bin build correctly code-splits the TUI into its own lazily-loaded chunk as a result. Inside the TUI, `state/reducer.ts` and `state/types.ts` hold the single `AppState` (a screen stack, the open document, undo history, overlays), `format/open-document.ts` is the one place bytes become an open document for every format, and `screens/editors/<format>/` holds each format's own screen components — reusing shared building blocks (`screens/shared/paragraph-family.tsx`, `slide-family.tsx`) between docx/odt and pptx/odp respectively, the same way documents.js's own odp editor reuses its odt paragraph/run classes.
|
|
203
250
|
- **`src/index.ts`** re-exports the CLI's command-layer, format, and exit-code logic (not the TUI, which stays behind its own lazy import) as this package's `"."` library export, for a caller that wants `document-cli`'s conversion-running logic directly rather than spawning the bin as a subprocess.
|
|
204
251
|
- **One package, two npm names.** `package.json`'s `bin` field lists `document-cli` and `doculi` unconditionally, both pointing at the same built entry point — there is no separate build, alias package, or npm alias mechanism involved, just two keys in one `bin` object.
|
|
@@ -216,10 +263,10 @@ pnpm test:smoke # tsdown, then vitest run --project smoke -- spawns the built
|
|
|
216
263
|
|
|
217
264
|
## Gotchas
|
|
218
265
|
|
|
219
|
-
- **The lazy TUI import is load-bearing, not incidental.** `src/cli.ts` computes the dispatch token before doing anything else and only reaches `await import('./tui/index.js')` on the bare/`tui` branch — every other command path (all 27 explicit conversions, `convert`, `formats`, `odm-to-pdf`, `odb-*`, `pdf-inspect`) never touches that import at all. This is what keeps a scripted, high-frequency CLI invocation from paying React/Ink's module-load cost on every call.
|
|
266
|
+
- **The lazy TUI import is load-bearing, not incidental.** `src/cli.ts` computes the dispatch token before doing anything else and only reaches `await import('./tui/index.js')` on the bare/`tui` branch — every other command path (all 27 explicit conversions, `convert`, `formats`, `from-package`, `odm-to-pdf`, `odb-*`, `pdf-inspect`, `fonts`, `docx-extras`, `metadata`, `set-metadata`) never touches that import at all. This is what keeps a scripted, high-frequency CLI invocation from paying React/Ink's module-load cost on every call.
|
|
220
267
|
- **A bare invocation and an explicit `tui` invocation fail differently on non-interactive stdout.** `document-cli` with no arguments and redirected stdout prints help and exits `0`, on the assumption that a bare invocation piped somewhere was more likely a forgotten argument than a deliberate TUI request. `document-cli tui` with redirected stdout is unambiguous, so it fails outright (exit `2`) rather than silently reinterpreting it as a help request.
|
|
221
268
|
- **`tsdown.config.ts` disables `fixedExtension` on both build passes.** `platform: 'node'` defaults tsdown's `fixedExtension` to `true`, which would emit `dist/cli.mjs`/`dist/index.mjs` regardless of `package.json`'s own `"type": "module"` — mismatching the `.js`/`.cjs` paths `bin` and `exports` actually name. Both entries set `fixedExtension: false` explicitly so the build output matches what's published.
|
|
222
|
-
- **`readOdbTables` needs `
|
|
269
|
+
- **`readOdbTables` needs `decodeOdbPackage` from `documents.js`, not `decodePackage`.** `documents.js`'s own `decodePackage` re-exports `ooxml.js`'s OOXML-only reader and cannot decode an ODF `.odb` container at all; `decodeDocumentPackage(format, bytes)` dispatches to `odf.js` internally for every real `DocumentFormat` member (odt/odp/ods/odg/odf), but `.odb` is deliberately not one of those (it has no PDF conversion and no write direction — see documents.js's own README), so it has no format string to pass either. `decodeOdbPackage` is the `.odb`-specific sibling that decodes the identical raw ODF container directly — `commands/odb.ts` and the TUI's `format/open-document.ts` both use it for exactly this reason. `odf.js` stays a genuine direct dependency regardless: the TUI's own ods spreadsheet grid (`screens/editors/ods/spreadsheet-grid.tsx`) imports `cellReference`/`columnIndexToLetters` from it directly for A1-style cell/column labelling, a small utility need unrelated to package decoding.
|
|
223
270
|
- **A TUI screen must call an editor's own accessors fresh on every render, never cache them.** documents.js's live-view editors (`DocxRun`, `OdtParagraph`, `OdsCell`, ...) mutate the real XML tree in place — `editor.paragraphs()`/`slide.shapes()`/`sheet.cell(r, c)` called once and stored in `useState`/`useMemo` goes stale the instant any screen mutates the underlying document, with nothing in the type system or in React to catch it (see `src/tui/state/types.ts`'s own doc comment on `Screen`).
|
|
224
271
|
- **The conversions this CLI runs carry the same fidelity limits documents.js itself documents** — most notably that PDF-pivot conversions are not round-trip-lossless (line wrapping drifts under standard-14 font substitution, justified text renders left-aligned, tables and vector shapes don't reconstruct from a PDF), and that `pdf-to-ods` recovers only what a spreadsheet printed, never a real number/date/formula. None of that is specific to this CLI or the TUI; see documents.js's own README, particularly its [Fidelity](https://github.com/ExaDev/documents.js#fidelity) section, for the full, format-by-format account.
|
|
225
272
|
|
package/dist/cli.js
CHANGED
package/dist/index.cjs
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "document-cli",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.13",
|
|
4
4
|
"description": "CLI and interactive Ink TUI for documents.js: every docx/pptx/odt/odp/ods/odg/odf/pdf/odm/odb/xlsx/markdown conversion, bridge, and editor as a scriptable command or a terminal app.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|