@ansonlai/docx-redline-js 0.8.2 → 0.8.4
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/CHANGELOG.md +75 -1
- package/README.md +28 -13
- package/core/list-targeting.js +12 -10
- package/dist/docx-redline-js.esm.js +837 -549
- package/dist/docx-redline-js.esm.js.map +4 -4
- package/dist/docx-redline-js.esm.min.js +93 -90
- package/dist/docx-redline-js.esm.min.js.map +4 -4
- package/dist/docx-redline.bundle.cjs +837 -549
- package/dist/docx-redline.bundle.cjs.map +4 -4
- package/dist/docx-redline.bundle.js +837 -549
- package/dist/docx-redline.bundle.js.map +4 -4
- package/docs/AGENT_KNOWLEDGE_BASE.md +4 -1
- package/docs/schemas/document-operations.schema.json +168 -168
- package/engine/oxml-engine.js +215 -195
- package/engine/reconstruction-mode.js +183 -183
- package/index.d.ts +4 -0
- package/index.js +36 -1
- package/node/cli.js +2 -1
- package/orchestration/list-structural-fallback.js +530 -518
- package/package.json +1 -1
- package/pipeline/list-generation.js +203 -168
- package/pipeline/serialization.js +111 -94
- package/services/batch-operation-orchestrator.js +6 -0
- package/services/document-inspection.js +10 -7
- package/services/document-operation-applier.js +2 -0
- package/services/document-operation-contract.js +553 -537
- package/services/document-operation-mutations.js +1140 -1011
- package/services/numbering-helpers.js +108 -0
- package/services/operation-batch-compiler.js +440 -436
- package/services/operation-heuristics.js +197 -173
- package/services/revision-comment-management.js +601 -594
- package/services/standalone-operation-runner.d.ts +429 -427
- package/services/table-reconciliation.js +48 -46
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,80 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 0.8.4
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **`textOccurrence` on `format` / `character-format`:** a 1-based match of `textToFormat` inside the resolved paragraph,
|
|
8
|
+
independent of `target.occurrence` (which selects the paragraph). Out of range fails with `PATCH_SOURCE_NOT_FOUND`;
|
|
9
|
+
calls without it still read `target.occurrence`. CLI capability `format-text-occurrence-v1`; contract stays version 8.
|
|
10
|
+
- **`applyRedlineToOxml` numbering options:** `numberingXml` and `numberingIdState` remap generated list definitions to
|
|
11
|
+
IDs that do not collide with the source numbering.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- **Soft-break text can be converted to a list:** a paragraph whose `A. …`, `B. …` items are separated by `<w:br/>` was
|
|
16
|
+
refused with `TARGET_NOT_FOUND`, and an explicit list request identical to typed markers was a no-op. In-run `\n`/`\t`
|
|
17
|
+
now serialize back to `<w:br/>`/`<w:tab/>`, including inside `w:delText`, so Reject All restores the breaks.
|
|
18
|
+
- **Localized `replacements` work in paragraphs with soft line breaks:** they were refused with `INVALID_OPERATION`.
|
|
19
|
+
A `find` within one line now applies and keeps the breaks; a `find` or `replace` that spans or adds a break is refused.
|
|
20
|
+
- **Generated bullets no longer reuse existing numbering IDs:** bullets generated into a document that already defined
|
|
21
|
+
`numId 1` continued an existing decimal list in Word. The bare runner and `applyRedlineToOxml` now allocate new IDs
|
|
22
|
+
from the supplied `numberingXml`, as `openDocx` already did.
|
|
23
|
+
- **Appending a table after a paragraph keeps its underline:** the paragraph was tracked as deleted and re-inserted as
|
|
24
|
+
plain text, so a prior same-author underline, or a `++underline++` sent with the table, was lost on Accept All. The
|
|
25
|
+
source paragraph is now kept in place.
|
|
26
|
+
- **Word's Reject All removes generated tables:** tracked tables were wrapped in one `w:ins` around `w:tbl`, which Word
|
|
27
|
+
does not recognize. Reject All left an empty table, and a table at the end of a document could hang Word. Tables now
|
|
28
|
+
use row-level `w:trPr/w:ins` with inserted cell content and marks, and `rejectTrackedChangesInOoxml` removes a table
|
|
29
|
+
once all of its rows are rejected. Confirmed in Word.
|
|
30
|
+
|
|
31
|
+
### Tests
|
|
32
|
+
|
|
33
|
+
- New `tests/soft_break_list_conversion_tests.mjs`, `tests/soft_break_localized_replacements_tests.mjs`,
|
|
34
|
+
`tests/generated_bullet_numbering_collision_tests.mjs`, `tests/format_text_occurrence_tests.mjs` and
|
|
35
|
+
`tests/table_append_fidelity_tests.mjs`.
|
|
36
|
+
|
|
37
|
+
### Known limitations
|
|
38
|
+
|
|
39
|
+
- Localized restore in the rejected view still refuses paragraphs with line breaks.
|
|
40
|
+
- The table-append path covers one paragraph followed by one table.
|
|
41
|
+
- Under `merge-same-author`, editing the text of a paragraph that carries your own pending formatting change still drops
|
|
42
|
+
that formatting on Accept All.
|
|
43
|
+
|
|
44
|
+
## 0.8.3
|
|
45
|
+
|
|
46
|
+
### Fixed
|
|
47
|
+
|
|
48
|
+
- **Reject All no longer leaves an empty paragraph after an inserted paragraph:** `modified: "<original>\n<new>"` on a plain
|
|
49
|
+
paragraph did not track the new paragraph's mark as inserted. It now does.
|
|
50
|
+
- **Reject All no longer merges source paragraphs after a list range replacement:** replacing a multi-paragraph list range
|
|
51
|
+
(`target` + `targetEnd`) collapsed the sources into one deleted paragraph, so Reject All gave results such as
|
|
52
|
+
`Bullet Root ABullet Insertion Anchor`, `AlphaGamma` for an empty middle paragraph, and lost both paragraphs of an
|
|
53
|
+
all-empty range. Each source paragraph, empty ones included, now keeps its own tracked-deleted paragraph. Confirmed in Word.
|
|
54
|
+
- **Inspection no longer reports historical paragraph properties as current:** `list`, `styleId` and `headingLevel` (and list
|
|
55
|
+
targeting) could read `numPr`, `pStyle` or `outlineLvl` stored under `w:pPrChange`. Only the paragraph's direct `w:pPr`
|
|
56
|
+
is read now.
|
|
57
|
+
- **`openDocx` list range replacement no longer renumbers or re-points lists:** deleted source paragraphs were remapped to a
|
|
58
|
+
new generic `numId` (Reject All restored them with the wrong list type), new items started a separate list (untouched
|
|
59
|
+
following items restarted at 1), and unused numbering definitions were added. Deleted source paragraphs now keep their
|
|
60
|
+
`numPr`, and a same-kind replacement reuses the source `numId`, leaving `numbering.xml` unchanged. Reuse applies when the
|
|
61
|
+
range uses one `numId` and the generated level formats match; format-changing edits still allocate new definitions, and
|
|
62
|
+
definitions created earlier in the same batch are not reused. Confirmed in Word.
|
|
63
|
+
- **Explicit list start values are applied:** generated list fragments without `xmlns:w` failed to parse in
|
|
64
|
+
`orchestration/list-structural-fallback.js`, so `7. Header` lost its `startOverride` and xmldom logged parse warnings.
|
|
65
|
+
|
|
66
|
+
### Tests
|
|
67
|
+
|
|
68
|
+
- New `tests/list_reject_fidelity_tests.mjs` and `tests/facade_list_numbering_tests.mjs`, with the Word-authored fixture
|
|
69
|
+
`tests/fixtures/agentic-lists/nested-lists-source.docx`; extended inspection and list structural fallback suites.
|
|
70
|
+
|
|
71
|
+
### Known limitations
|
|
72
|
+
|
|
73
|
+
- Plain paragraph to list conversion and list format changes are not yet supported canonically; see
|
|
74
|
+
`docs/plans/2026-09-30-canonical-list-operations.md`.
|
|
75
|
+
- `1. Header` to `1. Header` on a bare `document.xml` (not a packaged `.docx`) can fail with `RECEIPT_RECONCILIATION_FAILED`.
|
|
76
|
+
|
|
77
|
+
## 0.8.2
|
|
4
78
|
|
|
5
79
|
### Fixed
|
|
6
80
|
|
package/README.md
CHANGED
|
@@ -20,19 +20,34 @@ Converts AI-generated or programmatic text/markdown edits into valid Office Open
|
|
|
20
20
|
- Package plumbing helpers for numbering.xml, comments.xml, content types, and relationships
|
|
21
21
|
- Zero host dependencies: works in Node.js, browsers, Deno, and similar JS runtimes with DOM parsing support
|
|
22
22
|
|
|
23
|
-
## What's New in 0.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
23
|
+
## What's New in 0.8.x
|
|
24
|
+
|
|
25
|
+
The current release is 0.8.4. The CLI contract remains version 8 throughout the 0.8 line.
|
|
26
|
+
|
|
27
|
+
- **0.8.0, universal document facade:** `openDocx` and `DocxDocument` are exported from the primary entry point and work
|
|
28
|
+
on `Uint8Array` input in any JavaScript runtime. ZIP handling uses `fflate` and SHA-256 is pure JavaScript, so there are no
|
|
29
|
+
Node built-in dependencies. The XML provider falls back to `@xmldom/xmldom` automatically, and the
|
|
30
|
+
`@ansonlai/docx-redline-js/bundle` export provides zero-dependency single-file bundles for sandboxes such as n8n Code
|
|
31
|
+
nodes. The `/node` entry point keeps its existing API. See the [0.8.0 release notes](./docs/releases/0.8.0.md).
|
|
32
|
+
- **0.8.1, headers, footers and comment threads:** an operation's `part` field edits headers and footers;
|
|
33
|
+
`inspect().headersFooters` lists them. Comment threads can be resolved or reopened with `doc.resolveComment` or the
|
|
34
|
+
`comment_resolve` operation, and comments can be deleted by id with `doc.deleteComments({ ids })` or
|
|
35
|
+
`docx-redline delete-comments --comment-id`. See the [0.8.1 release notes](./docs/releases/0.8.1.md).
|
|
36
|
+
- **0.8.2, fixes:** a replacement beside a hyperlink no longer pulls adjacent plain text into the link, and Reject All no
|
|
37
|
+
longer reorders text around a manual line break. See the [0.8.2 release notes](./docs/releases/0.8.2.md).
|
|
38
|
+
- **0.8.3, list fixes:** Reject All now restores list edits exactly (no extra empty paragraph after an inserted
|
|
39
|
+
paragraph, no merged source paragraphs after a list-range replacement, original list numbering kept through `openDocx`),
|
|
40
|
+
and inspection no longer reports tracked historical list, style or heading properties as current. See the
|
|
41
|
+
[0.8.3 release notes](./docs/releases/0.8.3.md).
|
|
42
|
+
- **0.8.4, fixes:** localized replacements and list conversion work in paragraphs with soft line breaks, generated
|
|
43
|
+
bullets get their own numbering IDs, `format` accepts `textOccurrence` to format a later match, and tracked tables use
|
|
44
|
+
Word's row-level markup, so Word's Reject All removes them and an appended table keeps the paragraph's formatting. See
|
|
45
|
+
the [0.8.4 release notes](./docs/releases/0.8.4.md).
|
|
46
|
+
|
|
47
|
+
Version 0.7.0 introduced contract-8 localized edits: a small `find`/`replace` request against an inspected target instead
|
|
48
|
+
of a complete paragraph, plus an optional speculative form that fails without writing when the anchor is missing or
|
|
49
|
+
ambiguous. They remain available unchanged; see the [0.7.0 release notes](./docs/releases/0.7.0.md).
|
|
50
|
+
|
|
36
51
|
## Documentation Index
|
|
37
52
|
|
|
38
53
|
| Document | Description |
|
package/core/list-targeting.js
CHANGED
|
@@ -9,12 +9,13 @@ import {
|
|
|
9
9
|
} from './paragraph-targeting.js';
|
|
10
10
|
import { parseListItem, stripListMarker } from '../pipeline/list-markers.js';
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
/** Direct element child by local name; never crosses w:pPrChange / w:rPrChange boundaries. */
|
|
13
|
+
function getDirectChildByLocalName(node, localName) {
|
|
14
|
+
if (!node) return null;
|
|
15
|
+
for (const child of Array.from(node.childNodes || [])) {
|
|
16
|
+
if (child?.nodeType === 1 && child.localName === localName) return child;
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
function readValAttribute(element) {
|
|
@@ -178,17 +179,18 @@ function buildListEntriesForInsertion(parsedItems, normalizedTargetText, anchorL
|
|
|
178
179
|
export function getParagraphListInfo(paragraph) {
|
|
179
180
|
if (!paragraph) return null;
|
|
180
181
|
|
|
181
|
-
|
|
182
|
+
// Current properties only: direct w:pPr/w:numPr, never historical w:pPrChange/w:pPr.
|
|
183
|
+
const pPr = getDirectChildByLocalName(paragraph, 'pPr');
|
|
182
184
|
if (!pPr) return null;
|
|
183
|
-
const numPr =
|
|
185
|
+
const numPr = getDirectChildByLocalName(pPr, 'numPr');
|
|
184
186
|
if (!numPr) return null;
|
|
185
|
-
const numIdEl =
|
|
187
|
+
const numIdEl = getDirectChildByLocalName(numPr, 'numId');
|
|
186
188
|
if (!numIdEl) return null;
|
|
187
189
|
|
|
188
190
|
const numId = readValAttribute(numIdEl);
|
|
189
191
|
if (!numId || numId === '0') return null;
|
|
190
192
|
|
|
191
|
-
const ilvlEl =
|
|
193
|
+
const ilvlEl = getDirectChildByLocalName(numPr, 'ilvl');
|
|
192
194
|
const ilvlRaw = readValAttribute(ilvlEl);
|
|
193
195
|
const ilvl = Number.parseInt(ilvlRaw || '0', 10);
|
|
194
196
|
|