bun-docx 0.12.0 → 0.14.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/README.md +70 -16
- package/dist/index.js +7687 -3592
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -85,8 +85,10 @@ Open `mnda-filled.docx` in Word: tracked changes and comments appear in the revi
|
|
|
85
85
|
docx read FILE [--from LOC] [--to LOC] [--accepted | --baseline | --current] [--comments]
|
|
86
86
|
docx read FILE --ast # JSON-AST instead of Markdown (disables the markdown-only flags)
|
|
87
87
|
docx find FILE QUERY [--regex] [--ignore-case] [--all] [--nth N] [--current | --baseline] [--exact] [--json]
|
|
88
|
+
docx find FILE (--highlight COLOR|any | --color HEX | --bold | --italic | --underline) [--all] [--json] # find by formatting (no QUERY)
|
|
88
89
|
docx wc FILE [LOCATOR] [--accepted | --baseline | --current] [--json]
|
|
89
90
|
docx outline FILE [--style-prefix S] [--json]
|
|
91
|
+
docx styles FILE [--used] [--at STYLEID] [--json] # the style catalog (not in the body) — what --style NAMEs exist
|
|
90
92
|
docx render FILE [--out DIR] [--engine word|libreoffice|auto] [--dpi N] [--pages 1-N] [--format png|jpg]
|
|
91
93
|
|
|
92
94
|
docx comments list FILE [--include-resolved] [--thread cN]
|
|
@@ -100,25 +102,73 @@ docx info schema [--ts]
|
|
|
100
102
|
docx info locators [--json]
|
|
101
103
|
```
|
|
102
104
|
|
|
103
|
-
|
|
105
|
+
`docx read` surfaces structural facts the Markdown body can't show as HTML-comment
|
|
106
|
+
annotations (`<!-- docx:TYPE … -->`). These are **read-time visibility hints** — the
|
|
107
|
+
agent can SEE the structure, but the importer drops them (the structure survives
|
|
108
|
+
normal edits in place, `read --ast` is the lossless view, and `docx sections` /
|
|
109
|
+
`docx tables …` manage it). They're emitted **deviation-only**
|
|
110
|
+
(only when a value differs from the document default, so a plain document stays
|
|
111
|
+
clean):
|
|
112
|
+
|
|
113
|
+
- **Section breaks** render as `<!-- docx:section sN cols="2" type="continuous" -->`
|
|
114
|
+
on their own line — never a bare `---` (that's a thematic break, and emitting it
|
|
115
|
+
for a section silently turned layout into border paragraphs). A hand-authored
|
|
116
|
+
`---` now unambiguously means a thematic break.
|
|
117
|
+
- **Page geometry** rides a leading `<!-- docx:page orientation="landscape"
|
|
118
|
+
size="…in" margins="…in" text-width="…in" -->` note when the page deviates from
|
|
119
|
+
US-Letter-portrait-1″ — `text-width` is the usable column width. Exact twips are
|
|
120
|
+
in `read --ast` (on each section break: `pageWidth`/`pageHeight`/`pageOrientation`/
|
|
121
|
+
`margin*`).
|
|
122
|
+
- **Tables** carry a leading `<!-- docx:table t0 widths="1,2,3in" borders="double" -->`
|
|
123
|
+
when columns are uneven or borders deviate from the default, plus a per-cell
|
|
124
|
+
`<!-- docx:cell t0:r0c0 gridSpan="2" vMerge="continue" shading="FFE699" -->`
|
|
125
|
+
note on merged/shaded cells — so structure invisible in GFM is visible
|
|
126
|
+
(`Table.borders` / `TableCell.shading` in `read --ast`).
|
|
127
|
+
- **Images** trail a `<!-- docx:image img0 size="6.2x4.1in" float="yes" wrap="square" align="center" overflow="yes" -->`
|
|
128
|
+
note: `size` always (the `` alone doesn't say "6in wide"), and
|
|
129
|
+
`float`/`wrap`/`align`/`overflow` only when they deviate (an inline, in-bounds
|
|
130
|
+
image shows just its size). `overflow` flags an image wider than the usable text
|
|
131
|
+
column (`ImageRun.floating`/`wrap`/`align` + EMU extents in `read --ast`).
|
|
132
|
+
|
|
133
|
+
### Mutate (change FILE in place; `--dry-run`, `-v` everywhere; `-o PATH` on every mutator except `create`, whose positional FILE is already the output)
|
|
104
134
|
|
|
105
135
|
```sh
|
|
106
|
-
docx create FILE [--title T] [--author A] [--text "..." | --from PATH.md]
|
|
136
|
+
docx create FILE [--title T] [--author A] [--text "..." | --from PATH.md | --from -] [--force]
|
|
107
137
|
docx insert FILE (--after | --before) LOCATOR <content> # LOCATOR = pN | tN | sN | tN:rRcC:pK
|
|
108
|
-
docx edit FILE --at LOCATOR <content> # LOCATOR = pN | pN-pM | sN | eqN
|
|
138
|
+
docx edit FILE --at LOCATOR <content> # LOCATOR = pN | pN:S-E | pN-pM | sN | eqN | tN:rRcC:pK[:S-E]
|
|
109
139
|
docx delete FILE --at LOCATOR # LOCATOR = pN | pN-pM | tN | sN
|
|
110
|
-
docx
|
|
111
|
-
|
|
140
|
+
docx sections FILE --at LOCATOR --columns N [--type T] # LOCATOR = pN-pM | pN (wrap a range in N columns) | sN (recount an existing section). Multi-column layout lives HERE, not in insert.
|
|
141
|
+
docx replace FILE PATTERN REPLACEMENT [--regex] [--ignore-case] [--all] [--limit N] [--current | --baseline] [--exact] [--track] [--dry-run]
|
|
142
|
+
|
|
143
|
+
# Batch — apply many changes from ONE read (no re-reading between edits). Keys
|
|
144
|
+
# on each JSONL line mirror the command's flags; all locators address the doc as
|
|
145
|
+
# read. insert/edit also accept --batch - to read JSONL from stdin.
|
|
146
|
+
docx edit FILE --batch fills.jsonl # { at, <one of: text|clear|markdown|runs|code|task>, style?, … }
|
|
147
|
+
docx insert FILE --batch additions.jsonl # { after|before, <content>, style?, color?, … }
|
|
148
|
+
docx replace FILE --batch script.jsonl # { pattern, replacement, regex?, all?, limit?, … } applied in order
|
|
149
|
+
docx delete FILE --batch drop.jsonl # { at } per line — whole blocks (pN/tN/cell), resolved live-first
|
|
150
|
+
|
|
151
|
+
# All four of insert/edit/delete/replace accept --track to record that one
|
|
152
|
+
# invocation as a tracked change even when the doc's track-changes toggle is off.
|
|
153
|
+
#
|
|
112
154
|
# insert/edit content selectors (run "docx insert --help" / "docx edit --help" for the full list):
|
|
113
155
|
# --text "..." [--style NAME] [--alignment A] [--color HEX] [--bold] [--italic] [--url URL]
|
|
156
|
+
# (a newline in --text becomes a line break <w:br/>, a tab becomes <w:tab/> — verse/addresses stay line-per-line)
|
|
157
|
+
# edit --tabs right fix a line whose tabbed-over content WRAPS (read flags it as `docx:layout … warn`,
|
|
158
|
+
# and prints ONE consolidated fix-all summary at the top): swaps the fragile LEFT tab for a RIGHT tab
|
|
159
|
+
# flush at the margin so a long value (e.g. a city) never wraps. Rides along with --text, works
|
|
160
|
+
# per-entry in --batch, and on a RANGE (edit --at pN-pM --tabs right) cures every tab line at once.
|
|
161
|
+
# edit --text "" rejected (it would leave an invisible blank paragraph) — use `delete` to remove the
|
|
162
|
+
# line, or `--runs '[]'` to blank it but keep an empty spacer.
|
|
114
163
|
# --runs '[{"type":"text","text":"X","bold":true}]'
|
|
115
|
-
# --markdown "..." | --markdown-file PATH # GFM + math + CriticMarkup
|
|
164
|
+
# --markdown "..." | --markdown-file PATH # GFM + math + CriticMarkup + inline HTML formatting → blocks
|
|
116
165
|
# --code "..." | --code-file PATH [--language LANG]
|
|
117
|
-
# --equation "x^2 + y^2" [--display
|
|
166
|
+
# --equation "x^2 + y^2" [--display] (insert; edit also accepts --inline)
|
|
167
|
+
# --clear bold,italic,highlight,color,size,font,…|all (edit; strip run formatting, keep text)
|
|
118
168
|
# --task checked|unchecked | --list bullet|ordered [--list-level N] (insert)
|
|
119
169
|
# --task checked|unchecked (edit, flip in place)
|
|
120
170
|
# --table --rows N --cols N [--widths "A,B,C"] [--table-width V] [--borders S] [--layout L] (insert)
|
|
121
|
-
# --image SRC [--alt T] [--width IN] [--height IN] (insert; SRC = path, data: URI, or http(s) URL)
|
|
171
|
+
# --image SRC [--alt T] [--width IN] [--height IN] [--caption "Figure 1: …"] (insert; SRC = path, data: URI, or http(s) URL; --caption adds a Word "Caption"-styled line under the figure)
|
|
122
172
|
# --page-break | --column-break | --section [--columns N] [--type T] (insert)
|
|
123
173
|
|
|
124
174
|
docx comments add FILE --at LOCATOR --text "..." [--author NAME] [--current | --baseline]
|
|
@@ -152,9 +202,11 @@ docx tables merge FILE --at tN:rR1cC1-rR2cC2
|
|
|
152
202
|
docx tables unmerge FILE --at tN:rRcC
|
|
153
203
|
docx tables borders FILE --at tN [--style single|double|none] [--size N] [--color HEX]
|
|
154
204
|
|
|
155
|
-
docx track-changes
|
|
156
|
-
docx track-changes accept FILE (--at tcN [--at tcM ...] | --all)
|
|
157
|
-
docx track-changes reject FILE (--at tcN [--at tcM ...] | --all)
|
|
205
|
+
docx track-changes on|off FILE
|
|
206
|
+
docx track-changes accept FILE (--at tcN [--at tcM ...] | --at revN | --all)
|
|
207
|
+
docx track-changes reject FILE (--at tcN [--at tcM ...] | --at revN | --all)
|
|
208
|
+
# A del+ins REPLACE pair shares a "group": "revN" in `list`; `--at revN` accepts/rejects
|
|
209
|
+
# both halves in one call (no re-list between them — tcN ids renumber after each accept).
|
|
158
210
|
```
|
|
159
211
|
|
|
160
212
|
> **One rule to memorize: addressing an existing thing is always `--at`.**
|
|
@@ -178,7 +230,7 @@ The CLI is built for non-interactive agents. **Exit code is the success signal**
|
|
|
178
230
|
| Command class | Default stdout on success | `--verbose` |
|
|
179
231
|
| ------------- | ------------------------- | ----------- |
|
|
180
232
|
| **Mutator that mints a new handle** — `comments add`→`cN`, `comments reply`→`cN`, `footnotes/endnotes add`→`fnN`/`enN`, `hyperlinks add`→`linkN`, `insert`→the new `pN` | the bare locator(s), **one per line** (a multi-block `--markdown` insert prints several) | full `{ok:true,…}` ack |
|
|
181
|
-
| **Mutator with no new handle** — `edit`, `delete`, `replace`, `create`, `comments resolve/delete`, `images replace/delete`, `hyperlinks replace/delete`, `footnotes/endnotes edit/delete`, `tables *`, `track-changes accept/reject` & toggle | **
|
|
233
|
+
| **Mutator with no new handle** — `edit`, `delete`, `replace`, `create`, `comments resolve/delete`, `images replace/delete`, `hyperlinks replace/delete`, `footnotes/endnotes edit/delete`, `tables *`, `track-changes accept/reject` & toggle | **one-line confirmation** — `<operation> <target>` (e.g. `edit t1:r0c1:p0`, `edit 7 changes`, `replace 0 occurrences replaced`) (exit `0`) | full `{ok:true,…}` ack |
|
|
182
234
|
| `find` | matched span locators, one per line (no matches → nothing, exit `0`) | `--json` → `{ totalMatches, query, view, matches:[…], normalizedQuery? }` |
|
|
183
235
|
| `wc` | the bare count (whole-doc adds a tab-separated `sections` column, like `wc`) | `--json` → `{ words, scope, view, sections? }` |
|
|
184
236
|
| `outline` | indented `LOCATOR⇥TEXT` tree (two spaces per level) | `--json` → nested `[{ id, locator, level, style, text, children }]` |
|
|
@@ -227,7 +279,7 @@ cN imgN linkN fnN enN tcN eqN entity ids (comment / image / hyper
|
|
|
227
279
|
| Form | Accepted by |
|
|
228
280
|
| ---- | ----------- |
|
|
229
281
|
| `pN`, `tN`, `sN`, `tN:rRcC:pK` (blocks) | `read --from/--to`, `insert --after/--before`, `wc`, `comments add` |
|
|
230
|
-
| `pN`, `pN-pM`, `sN`, `eqN` | `edit --at` |
|
|
282
|
+
| `pN`, `pN:S-E`, `pN-pM`, `sN`, `eqN`, `tN:rRcC:pK`, `tN:rRcC:pK:S-E` | `edit --at` (span/cell forms strip or replace just that range) |
|
|
231
283
|
| `pN`, `pN-pM`, `tN`, `sN` | `delete --at` |
|
|
232
284
|
| `pN:S-E`, `pN:S-pM:E`, `tN:rRcC:pK:S-E` (spans) | `comments add --at`, `hyperlinks add --at` (single paragraph), `find`/`wc` results |
|
|
233
285
|
| `pN[:offset]` (point) | `footnotes/endnotes add --at` |
|
|
@@ -265,7 +317,7 @@ docx read doc.docx --current # → CriticMarkup {++ins++}[
|
|
|
265
317
|
docx track-changes accept doc.docx --at tc0 --at tc2 # or --all
|
|
266
318
|
```
|
|
267
319
|
|
|
268
|
-
`read` has three tracked-change views: default
|
|
320
|
+
`read` has three tracked-change views: default **`--accepted`** renders clean text — drops subtractive edits and inlines additive ones (the post-accept document); **`--current`** shows CriticMarkup with `[^tcN]` footnotes; **`--baseline`** does the reverse of accepted (the pre-change document). `find`, `replace`, `wc`, and `comments add` honor the same `--accepted`/`--baseline`/`--current` flags so offsets stay consistent across commands. Add `--comments` to `read` to append `[^cN]` footnotes for comment spans.
|
|
269
321
|
|
|
270
322
|
## How It Works
|
|
271
323
|
|
|
@@ -275,11 +327,13 @@ docx track-changes accept doc.docx --at tc0 --at tc2 # or --all
|
|
|
275
327
|
|
|
276
328
|
**Span-aware comments & hyperlinks.** `comments add --at p3:5-20` (and `hyperlinks add`) find the runs containing offsets 5 and 20, split them at the boundaries (preserving `<w:rPr>` on both halves), and insert markers between the slices. Comments authored by older tools that lack `w14:paraId` (required by `commentsExtended.xml`) get a fresh paraId injected automatically on resolve/reply.
|
|
277
329
|
|
|
278
|
-
**Tracked changes.** With `<w:trackChanges/>` set, `insert`/`edit`/`delete`/`replace` emit native `<w:ins>`/`<w:del>` (attributed via `--author`, `$DOCX_AUTHOR`, or `
|
|
330
|
+
**Tracked changes.** With `<w:trackChanges/>` set, `insert`/`edit`/`delete`/`replace` emit native `<w:ins>`/`<w:del>` (attributed via `--author`, `$DOCX_AUTHOR`, or `Reviewer`); pass `--track` to one of those commands (or the `tables` verbs / `images delete`) to track just that invocation even when the doc toggle is off. `edit --at pN --text` runs a word-level diff so unchanged words keep their formatting and only changed words are wrapped — the same shape Word produces mid-tracking. `accept`/`reject` handle run-level ins/del/moveFrom/moveTo, `sectPrChange`, paragraph-mark ins/del, and the table-structural revisions (rowIns/rowDel, cellIns/cellDel, tblGridChange, tcPrChange). OOXML has no tracked-change construct for hyperlink edits or image swaps, so under tracking those emit a `[docx-cli]` audit comment instead of a fake revision (image *deletion* is honest removal — it wraps a real `<w:del>`).
|
|
279
331
|
|
|
280
332
|
**Rich content.** Images insert from a path, `data:` URI, or `http(s)` URL (bounded fetch; HEIC→JPEG transcode; SVG sanitized; non-public/metadata addresses refused at every redirect hop). Equations round-trip OOXML `<m:oMath>` ↔ LaTeX (reconstructed, not legacy plaintext) — authored via temml (LaTeX→MathML) plus an in-house MathML→OMML adapter, no LGPL deps. Code blocks emit one `CodeBlock`-styled paragraph per line with optional lowlight syntax highlighting (37 bundled languages); they collapse back to a GFM fenced block on read. GFM task lists round-trip Word's checkbox content control (and the Word-for-Web Wingdings-glyph variant), surfacing as `taskState` in the AST. Tables operate on a merge-aware logical grid so `gridSpan`/`vMerge` cells map onto physical `<w:tc>`, and structural edits refuse to bisect an existing merge.
|
|
281
333
|
|
|
282
|
-
**Markdown dialect.** `create --from`, `insert/edit --markdown`, and the note bodies all parse the same GFM + math + CriticMarkup dialect (remark + remark-gfm + remark-math), composing the existing OOXML emitters. `read` emits a compatible dialect, so the read → edit → write loop round-trips (lossless for paragraphs, lists, and nested blockquotes; code/tables/math/headings inside a blockquote intentionally escape to top level on import).
|
|
334
|
+
**Markdown dialect.** `create --from`, `insert/edit --markdown`, and the note bodies all parse the same GFM + math + CriticMarkup + inline-HTML-formatting dialect (remark + remark-gfm + remark-math + an in-house inline-surgery transform), composing the existing OOXML emitters. `read` emits a compatible dialect, so the read → edit → write loop round-trips (lossless for paragraphs, lists, and nested blockquotes; code/tables/math/headings inside a blockquote intentionally escape to top level on import). `read --ast` is the fully lossless JSON form.
|
|
335
|
+
|
|
336
|
+
**Run formatting beyond bold/italic.** Properties markdown has no native syntax for — text color, theme color, highlight, shading, underline (all 18 styles + color), super/subscript, small/all caps, font, and size — are emitted as the **HTML a markdown reader actually renders**, so the output looks right in GitHub, VS Code, Obsidian, and browsers (Pandoc `[text]{…}` spans render as literal brackets in all of those). `read` emits semantic tags where they exist — `<mark>overdue</mark>`, `<sup>x</sup>`, `<sub>2</sub>` — a `<span style="color:#C00000">…</span>` for the CSS-expressible properties, and `data-*` attributes for the OOXML-only ones CSS can't express (theme colors, underline styles); `insert/edit --markdown` parses them back losslessly, and a leading `<!-- docx:base font="Arial" size="8pt" -->` note declares the document's dominant font/size once so the body isn't buried in per-run repetition. Bold/italic/strike/code/links stay native (`**`/`*`/`~~`/`` ` ``/`[](…)`). Because the inline-surgery transform scans whole sibling sequences, a CriticMarkup marker or span can straddle other formatting — `{++**bold insertion**++}` is tracked correctly. An invalid enum value (e.g. a bogus highlight name) fails with a clear error rather than silently vanishing. Inserted plain content inherits the surrounding paragraph's font/size so it blends in.
|
|
283
337
|
|
|
284
338
|
**Visual verification.** `docx render` is the only command that needs an external runtime: it drives Microsoft Word (macOS via `osascript`, Windows via PowerShell COM — the ground-truth renderer) or LibreOffice (`soffice`, cross-platform) to produce a PDF, then rasterizes in-process via the bundled `@hyzyla/pdfium` WASM package — no poppler/pdftoppm/ImageMagick needed. Agents that consume PNGs use this to verify edits, diff accept/reject before-vs-after, or generate screenshots.
|
|
285
339
|
|