js.documents 7.16.2 → 7.17.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 CHANGED
@@ -270,7 +270,7 @@ const pdfBytes = docxToPdf(docxBytes, {
270
270
 
271
271
  The tree and the flat `ContentDocument` are one format in two encodings, related by three laws (stated on [document-schema.js#20](https://github.com/ExaDev/document-schema.js/issues/20), proven over this package's real corpus by the bijection suite in `src/convert/bijection.test.ts`): (i) `flattenTree(assembleTree(c))` reproduces `c` exactly, up to one declared normalisation (a present-but-empty sheet `embeddedObjects` array normalises to the field absent); (ii) effective-property equality holds universally — a factored and an unfactored serialisation of one document resolve to the same properties; (iii) minting is idempotent — factoring a second time produces the identical styles table.
272
272
 
273
- Three flat-form signals drive the grouping, and all three are reproduced exactly on the way back: `headingLevel`, `list.level`, and — since document-schema.js 4.2.0 — the `constructStart`/`constructEnd` block pair that delimits a fidelity construct (a docx SDT, an ODF field, a tracked-change span, a bookmark, a hyperlink region, a division). `decompose` promotes each marker pair to a construct group carrying the `ConstructDescriptor` and holding the delimited region as its children, decomposed on its own; `flattenTree` writes the pair back around that region. A construct is a semantic wrapper rather than a container, so it neither disturbs the enclosing heading/list nesting it sits inside nor resets the style chain resolving onto it — content inside a construct still inherits the ambient heading's or section's factored properties, exactly as if the construct were not there. Markers must pair up within one container's block flow: an unmatched `constructEnd`, or a `constructStart` a container never closes, throws document-schema.js's `ConstructMarkerImbalanceError` (carrying its `ConstructMarkerImbalance` payload, so the offending block index is available without parsing the message) rather than being repaired into a plausible tree. The format codecs emit and consume these markers on both sides today — ooxml.js's docx pair (SDTs, bookmarks, tracked changes, block-scoped fields), odf.js's odt reader for divisions, index wrappers, and cross-paragraph bookmarks with its writer covering the construct kinds it models and refusing the rest by name, markdown-codec, rtf-codec, and epub-codec in both directions, and this package's own PDF reconstruction emitting division and anchor pairs — so the marker machinery has real producers and consumers, not just the boundary transform. Reaching further than the codecs is still a mixed picture, not a blanket guarantee: `buildMarkdownText` passes markers through to markdown-codec's own bracket-resolving writer, which renders each construct it has a markdown spelling for (a footnote definition, a blockquote division, a titled image's link wrapper) and renders the rest transparently with a diagnostic — markdown-codec's own read side emits those pairs, so this package's editor and conversion round trips depend on it; building docx bytes from marker-carrying flat content writes a bookmark anchor's pair as real `w:bookmarkStart`/`w:bookmarkEnd` around the blocks it spans, and the odt builder does the same through `text:bookmark-start`/`-end` — the one construct family expressible append-only in either format's body flow — while the wrapper-shaped kinds (an SDT, a tracked change, a division) are dropped rather than written, since neither editor model has a surface for them; and the layout engines (`convertWordprocessingToLayout`, `convertShape`) silently skip a marker block during pagination — harmless there, since a marker carries no content of its own to render.
273
+ Three flat-form signals drive the grouping, and all three are reproduced exactly on the way back: `headingLevel`, `list.level`, and — since document-schema.js 4.2.0 — the `constructStart`/`constructEnd` block pair that delimits a fidelity construct (a docx SDT, an ODF field, a tracked-change span, a bookmark, a hyperlink region, a division). `decompose` promotes each marker pair to a construct group carrying the `ConstructDescriptor` and holding the delimited region as its children, decomposed on its own; `flattenTree` writes the pair back around that region. A construct is a semantic wrapper rather than a container, so it neither disturbs the enclosing heading/list nesting it sits inside nor resets the style chain resolving onto it — content inside a construct still inherits the ambient heading's or section's factored properties, exactly as if the construct were not there. Markers must pair up within one container's block flow: an unmatched `constructEnd`, or a `constructStart` a container never closes, throws document-schema.js's `ConstructMarkerImbalanceError` (carrying its `ConstructMarkerImbalance` payload, so the offending block index is available without parsing the message) rather than being repaired into a plausible tree. The format codecs emit and consume these markers on both sides today — ooxml.js's docx pair (SDTs, bookmarks, tracked changes, block-scoped fields), odf.js's odt reader for divisions, index wrappers, and cross-paragraph bookmarks with its writer covering the construct kinds it models and refusing the rest by name, markdown-codec, rtf-codec, and epub-codec in both directions, and this package's own PDF reconstruction emitting division and anchor pairs — so the marker machinery has real producers and consumers, not just the boundary transform. Reaching further than the codecs is still a mixed picture, not a blanket guarantee: `buildMarkdownText` passes markers through to markdown-codec's own bracket-resolving writer, which renders each construct it has a markdown spelling for (a footnote definition, a blockquote division, a titled image's link wrapper) and renders the rest transparently with a diagnostic — markdown-codec's own read side emits those pairs, so this package's editor and conversion round trips depend on it; building docx bytes from marker-carrying flat content writes a bookmark anchor's pair as real `w:bookmarkStart`/`w:bookmarkEnd` around the blocks it spans and a content control's pair as a real `w:sdt` region (the descriptor driving `w:sdtPr`, the blocks between the markers landing inside its own `w:sdtContent`), and the odt builder does the bookmark pair through `text:bookmark-start`/`-end` and a division's pair as a real `text:section` region the between-blocks land inside so the one wrapper kind each format natively spells at body scope now survives the build, round-tripping through its own reader as the identical construct pair — while the remaining kinds (a tracked change's region machinery, an ODF index wrapper's generated-content model) are dropped rather than written, since neither editor model has a surface for them; and the layout engines (`convertWordprocessingToLayout`, `convertShape`) silently skip a marker block during pagination — harmless there, since a marker carries no content of its own to render.
274
274
 
275
275
  `assembleTree` is the one constructor behind every construction site — decompose, then `factorStyles`, the minting pass that hoists property tuples occurring two or more times onto a group-wrapper ref plus a `styles` table entry (deterministic order; `frames`/`sourcePath`/`styleId` are per-node facts and never factor). The transform belongs to `document-schema.js`, which owns both encodings and publishes `assembleTree`, `decompose`, `flattenTree`, `factorStyles`, `ConstructMarkerImbalanceError`, and the `TreeChildren` type for any caller composing its own boundary — import them from there, not from this package. documents.js consumes that transform at its own boundary and re-exports none of it; the readers, builders, layout engines, and editors here keep producing and consuming the flat form, so the tree exists only where a `DocumentTree` is constructed or consumed:
276
276
 
@@ -22,20 +22,24 @@ var ConstructMarkerState = class {
22
22
  const id = this.nextBookmarkId;
23
23
  this.nextBookmarkId += 1;
24
24
  this.open.push({
25
- isBookmark: true,
25
+ kind: "bookmark",
26
26
  id
27
27
  });
28
28
  body.appendBookmarkStart(id, detail.name);
29
29
  return;
30
30
  }
31
- this.open.push({
32
- isBookmark: false,
33
- id: -1
34
- });
31
+ if (detail.kind === "contentControl") {
32
+ this.open.push({ kind: "region" });
33
+ body.openContentControlRegion(detail);
34
+ return;
35
+ }
36
+ this.open.push({ kind: "dropped" });
35
37
  }
36
38
  closeConstruct(body) {
37
39
  const entry = this.open.pop();
38
- if (entry?.isBookmark) body.appendBookmarkEnd(entry.id);
40
+ if (entry === void 0) return;
41
+ if (entry.kind === "bookmark") body.appendBookmarkEnd(entry.id);
42
+ else if (entry.kind === "region") body.closeRegion();
39
43
  }
40
44
  };
41
45
  function buildDocxPackage(content, options) {
@@ -21,20 +21,24 @@ var ConstructMarkerState = class {
21
21
  const id = this.nextBookmarkId;
22
22
  this.nextBookmarkId += 1;
23
23
  this.open.push({
24
- isBookmark: true,
24
+ kind: "bookmark",
25
25
  id
26
26
  });
27
27
  body.appendBookmarkStart(id, detail.name);
28
28
  return;
29
29
  }
30
- this.open.push({
31
- isBookmark: false,
32
- id: -1
33
- });
30
+ if (detail.kind === "contentControl") {
31
+ this.open.push({ kind: "region" });
32
+ body.openContentControlRegion(detail);
33
+ return;
34
+ }
35
+ this.open.push({ kind: "dropped" });
34
36
  }
35
37
  closeConstruct(body) {
36
38
  const entry = this.open.pop();
37
- if (entry?.isBookmark) body.appendBookmarkEnd(entry.id);
39
+ if (entry === void 0) return;
40
+ if (entry.kind === "bookmark") body.appendBookmarkEnd(entry.id);
41
+ else if (entry.kind === "region") body.closeRegion();
38
42
  }
39
43
  };
40
44
  function buildDocxPackage(content, options) {
@@ -35,15 +35,26 @@ var DocxBodyImpl = class {
35
35
  body;
36
36
  imageContext;
37
37
  pkg;
38
+ openRegions = [];
39
+ nextSdtId = 1;
38
40
  constructor(body, imageContext, pkg) {
39
41
  this.body = body;
40
42
  this.imageContext = imageContext;
41
43
  this.pkg = pkg;
42
44
  }
45
+ appendToBody(element) {
46
+ const region = this.openRegions.at(-1);
47
+ if (region !== void 0) {
48
+ region.children.push(element);
49
+ return;
50
+ }
51
+ this.body.children.splice(bodyInsertionPoint(this.body), 0, element);
52
+ }
43
53
  appendParagraph(init) {
44
54
  const paragraphElement = require_edit_docx_paragraph.buildParagraph(init);
45
- this.body.children.splice(bodyInsertionPoint(this.body), 0, paragraphElement);
46
- return new require_edit_docx_paragraph.DocxParagraph(this.body.children, paragraphElement, this.imageContext, this.pkg);
55
+ this.appendToBody(paragraphElement);
56
+ const container = this.openRegions.at(-1)?.children ?? this.body.children;
57
+ return new require_edit_docx_paragraph.DocxParagraph(container, paragraphElement, this.imageContext, this.pkg);
47
58
  }
48
59
  insertParagraphAt(index, init) {
49
60
  const paragraphElement = require_edit_docx_paragraph.buildParagraph(init);
@@ -54,22 +65,65 @@ var DocxBodyImpl = class {
54
65
  }
55
66
  appendTable(init) {
56
67
  const tableElement = require_edit_docx_table.buildTable(init);
57
- this.body.children.splice(bodyInsertionPoint(this.body), 0, tableElement);
58
- return new require_edit_docx_table.DocxTable(this.body.children, tableElement);
68
+ this.appendToBody(tableElement);
69
+ const container = this.openRegions.at(-1)?.children ?? this.body.children;
70
+ return new require_edit_docx_table.DocxTable(container, tableElement);
59
71
  }
60
72
  appendPageBreak() {
61
73
  const run = require_xml_fragment.el("w:r", {}, [require_xml_fragment.el("w:br", { "w:type": "page" })]);
62
74
  const paragraph = require_xml_fragment.el("w:p", {}, [run]);
63
- this.body.children.splice(bodyInsertionPoint(this.body), 0, paragraph);
75
+ this.appendToBody(paragraph);
64
76
  }
65
77
  appendBookmarkStart(id, name) {
66
- this.body.children.splice(bodyInsertionPoint(this.body), 0, require_xml_fragment.el("w:bookmarkStart", {
78
+ this.appendToBody(require_xml_fragment.el("w:bookmarkStart", {
67
79
  "w:id": String(id),
68
80
  "w:name": require_xml_entities.encodeXmlText(name)
69
81
  }));
70
82
  }
71
83
  appendBookmarkEnd(id) {
72
- this.body.children.splice(bodyInsertionPoint(this.body), 0, require_xml_fragment.el("w:bookmarkEnd", { "w:id": String(id) }));
84
+ this.appendToBody(require_xml_fragment.el("w:bookmarkEnd", { "w:id": String(id) }));
85
+ }
86
+ openContentControlRegion(descriptor) {
87
+ const id = this.nextSdtId;
88
+ this.nextSdtId += 1;
89
+ const sdtPr = [require_xml_fragment.el("w:id", { "w:val": String(id) })];
90
+ if (descriptor.alias !== void 0) sdtPr.push(require_xml_fragment.el("w:alias", { "w:val": require_xml_entities.encodeXmlText(descriptor.alias) }));
91
+ if (descriptor.tag !== void 0) sdtPr.push(require_xml_fragment.el("w:tag", { "w:val": require_xml_entities.encodeXmlText(descriptor.tag) }));
92
+ if (descriptor.lock !== void 0) sdtPr.push(require_xml_fragment.el("w:lock", { "w:val": descriptor.lock === "content" ? "contentLocked" : descriptor.lock === "container" ? "sdtLocked" : "sdtContentLocked" }));
93
+ switch (descriptor.controlType) {
94
+ case "plainText":
95
+ sdtPr.push(require_xml_fragment.el("w:text", {}));
96
+ break;
97
+ case "comboBox":
98
+ case "dropDown":
99
+ sdtPr.push(require_xml_fragment.el(descriptor.controlType === "comboBox" ? "w:comboBox" : "w:dropDownList", {}, (descriptor.options ?? []).map((option) => require_xml_fragment.el("w:listItem", {
100
+ "w:displayText": require_xml_entities.encodeXmlText(option),
101
+ "w:value": require_xml_entities.encodeXmlText(option)
102
+ }))));
103
+ break;
104
+ case "date":
105
+ sdtPr.push(require_xml_fragment.el("w:date", descriptor.value === void 0 ? {} : { "w:fullDate": require_xml_entities.encodeXmlText(descriptor.value) }));
106
+ break;
107
+ case "checkbox":
108
+ sdtPr.push(require_xml_fragment.el("w:checkbox", {}, [require_xml_fragment.el("w:checked", { "w:val": descriptor.checked === true ? "true" : "false" })]));
109
+ break;
110
+ case "picture":
111
+ sdtPr.push(require_xml_fragment.el("w:picture", {}));
112
+ break;
113
+ case "group":
114
+ sdtPr.push(require_xml_fragment.el("w:group", {}));
115
+ break;
116
+ case "repeatingSection":
117
+ sdtPr.push(require_xml_fragment.el("w:repeatingSection", {}));
118
+ break;
119
+ case "index": sdtPr.push(require_xml_fragment.el("w:docPartObj", {}, [require_xml_fragment.el("w:docPartGallery", { "w:val": "Table of Contents" })]));
120
+ }
121
+ const sdtContent = require_xml_fragment.el("w:sdtContent", {}, []);
122
+ this.appendToBody(require_xml_fragment.el("w:sdt", {}, [require_xml_fragment.el("w:sdtPr", {}, sdtPr), sdtContent]));
123
+ this.openRegions.push(sdtContent);
124
+ }
125
+ closeRegion() {
126
+ this.openRegions.pop();
73
127
  }
74
128
  };
75
129
  var DocxEditor = class {
@@ -1,7 +1,7 @@
1
1
  import { t as ClockPort } from "../../clock-C7SUuYN0.cjs";
2
2
  import { DocxParagraph, ParagraphInit } from "./paragraph.cjs";
3
3
  import { DocxTable, TableInit } from "./table.cjs";
4
- import { LayoutMetadata } from "document-schema.js";
4
+ import { ContentControlDescriptor, LayoutMetadata } from "document-schema.js";
5
5
  import { Package } from "ooxml.js";
6
6
  //#region src/edit/docx/editor.d.ts
7
7
  interface DocxBody {
@@ -11,6 +11,8 @@ interface DocxBody {
11
11
  appendPageBreak(): void;
12
12
  appendBookmarkStart(id: number, name: string): void;
13
13
  appendBookmarkEnd(id: number): void;
14
+ openContentControlRegion(descriptor: ContentControlDescriptor): void;
15
+ closeRegion(): void;
14
16
  }
15
17
  declare class DocxEditor {
16
18
  readonly body: DocxBody;
@@ -2,7 +2,7 @@ import { t as ClockPort } from "../../clock-C7SUuYN0.js";
2
2
  import { DocxParagraph, ParagraphInit } from "./paragraph.js";
3
3
  import { DocxTable, TableInit } from "./table.js";
4
4
  import { Package } from "ooxml.js";
5
- import { LayoutMetadata } from "document-schema.js";
5
+ import { ContentControlDescriptor, LayoutMetadata } from "document-schema.js";
6
6
  //#region src/edit/docx/editor.d.ts
7
7
  interface DocxBody {
8
8
  insertParagraphAt(index: number, init?: ParagraphInit): DocxParagraph;
@@ -11,6 +11,8 @@ interface DocxBody {
11
11
  appendPageBreak(): void;
12
12
  appendBookmarkStart(id: number, name: string): void;
13
13
  appendBookmarkEnd(id: number): void;
14
+ openContentControlRegion(descriptor: ContentControlDescriptor): void;
15
+ closeRegion(): void;
14
16
  }
15
17
  declare class DocxEditor {
16
18
  readonly body: DocxBody;
@@ -34,15 +34,26 @@ var DocxBodyImpl = class {
34
34
  body;
35
35
  imageContext;
36
36
  pkg;
37
+ openRegions = [];
38
+ nextSdtId = 1;
37
39
  constructor(body, imageContext, pkg) {
38
40
  this.body = body;
39
41
  this.imageContext = imageContext;
40
42
  this.pkg = pkg;
41
43
  }
44
+ appendToBody(element) {
45
+ const region = this.openRegions.at(-1);
46
+ if (region !== void 0) {
47
+ region.children.push(element);
48
+ return;
49
+ }
50
+ this.body.children.splice(bodyInsertionPoint(this.body), 0, element);
51
+ }
42
52
  appendParagraph(init) {
43
53
  const paragraphElement = buildParagraph(init);
44
- this.body.children.splice(bodyInsertionPoint(this.body), 0, paragraphElement);
45
- return new DocxParagraph(this.body.children, paragraphElement, this.imageContext, this.pkg);
54
+ this.appendToBody(paragraphElement);
55
+ const container = this.openRegions.at(-1)?.children ?? this.body.children;
56
+ return new DocxParagraph(container, paragraphElement, this.imageContext, this.pkg);
46
57
  }
47
58
  insertParagraphAt(index, init) {
48
59
  const paragraphElement = buildParagraph(init);
@@ -53,22 +64,65 @@ var DocxBodyImpl = class {
53
64
  }
54
65
  appendTable(init) {
55
66
  const tableElement = buildTable(init);
56
- this.body.children.splice(bodyInsertionPoint(this.body), 0, tableElement);
57
- return new DocxTable(this.body.children, tableElement);
67
+ this.appendToBody(tableElement);
68
+ const container = this.openRegions.at(-1)?.children ?? this.body.children;
69
+ return new DocxTable(container, tableElement);
58
70
  }
59
71
  appendPageBreak() {
60
72
  const run = el("w:r", {}, [el("w:br", { "w:type": "page" })]);
61
73
  const paragraph = el("w:p", {}, [run]);
62
- this.body.children.splice(bodyInsertionPoint(this.body), 0, paragraph);
74
+ this.appendToBody(paragraph);
63
75
  }
64
76
  appendBookmarkStart(id, name) {
65
- this.body.children.splice(bodyInsertionPoint(this.body), 0, el("w:bookmarkStart", {
77
+ this.appendToBody(el("w:bookmarkStart", {
66
78
  "w:id": String(id),
67
79
  "w:name": encodeXmlText(name)
68
80
  }));
69
81
  }
70
82
  appendBookmarkEnd(id) {
71
- this.body.children.splice(bodyInsertionPoint(this.body), 0, el("w:bookmarkEnd", { "w:id": String(id) }));
83
+ this.appendToBody(el("w:bookmarkEnd", { "w:id": String(id) }));
84
+ }
85
+ openContentControlRegion(descriptor) {
86
+ const id = this.nextSdtId;
87
+ this.nextSdtId += 1;
88
+ const sdtPr = [el("w:id", { "w:val": String(id) })];
89
+ if (descriptor.alias !== void 0) sdtPr.push(el("w:alias", { "w:val": encodeXmlText(descriptor.alias) }));
90
+ if (descriptor.tag !== void 0) sdtPr.push(el("w:tag", { "w:val": encodeXmlText(descriptor.tag) }));
91
+ if (descriptor.lock !== void 0) sdtPr.push(el("w:lock", { "w:val": descriptor.lock === "content" ? "contentLocked" : descriptor.lock === "container" ? "sdtLocked" : "sdtContentLocked" }));
92
+ switch (descriptor.controlType) {
93
+ case "plainText":
94
+ sdtPr.push(el("w:text", {}));
95
+ break;
96
+ case "comboBox":
97
+ case "dropDown":
98
+ sdtPr.push(el(descriptor.controlType === "comboBox" ? "w:comboBox" : "w:dropDownList", {}, (descriptor.options ?? []).map((option) => el("w:listItem", {
99
+ "w:displayText": encodeXmlText(option),
100
+ "w:value": encodeXmlText(option)
101
+ }))));
102
+ break;
103
+ case "date":
104
+ sdtPr.push(el("w:date", descriptor.value === void 0 ? {} : { "w:fullDate": encodeXmlText(descriptor.value) }));
105
+ break;
106
+ case "checkbox":
107
+ sdtPr.push(el("w:checkbox", {}, [el("w:checked", { "w:val": descriptor.checked === true ? "true" : "false" })]));
108
+ break;
109
+ case "picture":
110
+ sdtPr.push(el("w:picture", {}));
111
+ break;
112
+ case "group":
113
+ sdtPr.push(el("w:group", {}));
114
+ break;
115
+ case "repeatingSection":
116
+ sdtPr.push(el("w:repeatingSection", {}));
117
+ break;
118
+ case "index": sdtPr.push(el("w:docPartObj", {}, [el("w:docPartGallery", { "w:val": "Table of Contents" })]));
119
+ }
120
+ const sdtContent = el("w:sdtContent", {}, []);
121
+ this.appendToBody(el("w:sdt", {}, [el("w:sdtPr", {}, sdtPr), sdtContent]));
122
+ this.openRegions.push(sdtContent);
123
+ }
124
+ closeRegion() {
125
+ this.openRegions.pop();
72
126
  }
73
127
  };
74
128
  var DocxEditor = class {
@@ -194,20 +194,24 @@ var ConstructMarkerState = class {
194
194
  const detail = marker.descriptor;
195
195
  if (detail.kind === "anchor" && detail.anchorType === "bookmark") {
196
196
  this.open.push({
197
- isBookmark: true,
197
+ kind: "bookmark",
198
198
  name: detail.name
199
199
  });
200
200
  body.appendBookmarkStart(detail.name);
201
201
  return;
202
202
  }
203
- this.open.push({
204
- isBookmark: false,
205
- name: ""
206
- });
203
+ if (detail.kind === "division") {
204
+ this.open.push({ kind: "region" });
205
+ body.openDivisionRegion(detail);
206
+ return;
207
+ }
208
+ this.open.push({ kind: "dropped" });
207
209
  }
208
210
  closeConstruct(body) {
209
211
  const entry = this.open.pop();
210
- if (entry?.isBookmark) body.appendBookmarkEnd(entry.name);
212
+ if (entry === void 0) return;
213
+ if (entry.kind === "bookmark") body.appendBookmarkEnd(entry.name);
214
+ else if (entry.kind === "region") body.closeRegion();
211
215
  }
212
216
  };
213
217
  function appendBlock(body, block, markers) {
@@ -193,20 +193,24 @@ var ConstructMarkerState = class {
193
193
  const detail = marker.descriptor;
194
194
  if (detail.kind === "anchor" && detail.anchorType === "bookmark") {
195
195
  this.open.push({
196
- isBookmark: true,
196
+ kind: "bookmark",
197
197
  name: detail.name
198
198
  });
199
199
  body.appendBookmarkStart(detail.name);
200
200
  return;
201
201
  }
202
- this.open.push({
203
- isBookmark: false,
204
- name: ""
205
- });
202
+ if (detail.kind === "division") {
203
+ this.open.push({ kind: "region" });
204
+ body.openDivisionRegion(detail);
205
+ return;
206
+ }
207
+ this.open.push({ kind: "dropped" });
206
208
  }
207
209
  closeConstruct(body) {
208
210
  const entry = this.open.pop();
209
- if (entry?.isBookmark) body.appendBookmarkEnd(entry.name);
211
+ if (entry === void 0) return;
212
+ if (entry.kind === "bookmark") body.appendBookmarkEnd(entry.name);
213
+ else if (entry.kind === "region") body.closeRegion();
210
214
  }
211
215
  };
212
216
  function appendBlock(body, block, markers) {
@@ -31,42 +31,52 @@ function findOfficeText(contentRoot) {
31
31
  var OdtBodyImpl = class {
32
32
  officeText;
33
33
  pkg;
34
+ openDivisions = [];
34
35
  constructor(officeText, pkg) {
35
36
  this.officeText = officeText;
36
37
  this.pkg = pkg;
37
38
  }
39
+ containerChildren() {
40
+ return this.openDivisions.at(-1)?.section.children ?? this.officeText.children;
41
+ }
38
42
  appendParagraph(init) {
39
43
  const paragraphElement = require_edit_odt_paragraph.buildParagraph(this.pkg, init);
40
- this.officeText.children.push(paragraphElement);
41
- return new require_edit_odt_paragraph.OdtParagraph(this.officeText.children, paragraphElement, this.pkg);
44
+ const container = this.containerChildren();
45
+ container.push(paragraphElement);
46
+ return new require_edit_odt_paragraph.OdtParagraph(container, paragraphElement, this.pkg);
42
47
  }
43
48
  appendTable(init) {
44
49
  const tableElement = require_edit_odt_table.buildTable(this.pkg, init);
45
- this.officeText.children.push(tableElement);
46
- return new require_edit_odt_table.OdtTable(this.officeText.children, tableElement, this.pkg);
50
+ const container = this.containerChildren();
51
+ container.push(tableElement);
52
+ return new require_edit_odt_table.OdtTable(container, tableElement, this.pkg);
47
53
  }
48
54
  appendList() {
49
55
  const listElement = require_edit_odt_list.buildList(this.pkg);
50
- this.officeText.children.push(listElement);
51
- return new require_edit_odt_list.OdtList(this.officeText.children, listElement, this.pkg);
56
+ const container = this.containerChildren();
57
+ container.push(listElement);
58
+ return new require_edit_odt_list.OdtList(container, listElement, this.pkg);
52
59
  }
53
60
  appendFormula(formula, frame) {
54
61
  const paragraphElement = require_edit_odt_paragraph.buildParagraph(this.pkg);
55
62
  paragraphElement.children.push(require_edit_odt_formula.insertFormulaFrameMedia(this.pkg, frame, formula));
56
- this.officeText.children.push(paragraphElement);
57
- return new require_edit_odt_paragraph.OdtParagraph(this.officeText.children, paragraphElement, this.pkg);
63
+ const container = this.containerChildren();
64
+ container.push(paragraphElement);
65
+ return new require_edit_odt_paragraph.OdtParagraph(container, paragraphElement, this.pkg);
58
66
  }
59
67
  appendEmbeddedObject(object, directory) {
60
68
  const paragraphElement = require_edit_odt_paragraph.buildParagraph(this.pkg);
61
69
  paragraphElement.children.push((0, odf_js.writeEmbeddedObject)(object, directory, this.pkg));
62
- this.officeText.children.push(paragraphElement);
63
- return new require_edit_odt_paragraph.OdtParagraph(this.officeText.children, paragraphElement, this.pkg);
70
+ const container = this.containerChildren();
71
+ container.push(paragraphElement);
72
+ return new require_edit_odt_paragraph.OdtParagraph(container, paragraphElement, this.pkg);
64
73
  }
65
74
  appendVectors(vectors) {
66
75
  const paragraphElement = require_edit_odt_paragraph.buildParagraph(this.pkg);
67
76
  for (const vector of vectors) paragraphElement.children.push(require_edit_odg_vector.buildVectorElement(this.pkg, vector, { textFlowAnchored: true }));
68
- this.officeText.children.push(paragraphElement);
69
- return new require_edit_odt_paragraph.OdtParagraph(this.officeText.children, paragraphElement, this.pkg);
77
+ const container = this.containerChildren();
78
+ container.push(paragraphElement);
79
+ return new require_edit_odt_paragraph.OdtParagraph(container, paragraphElement, this.pkg);
70
80
  }
71
81
  appendPageBreak() {
72
82
  const paragraphElement = require_edit_odt_paragraph.buildParagraph(this.pkg);
@@ -74,10 +84,10 @@ var OdtBodyImpl = class {
74
84
  name: "text:style-name",
75
85
  value: require_edit_odt_automatic_styles.ensurePageBreakStyleName(this.pkg)
76
86
  });
77
- this.officeText.children.push(paragraphElement);
87
+ this.containerChildren().push(paragraphElement);
78
88
  }
79
89
  appendBookmarkStart(name) {
80
- this.officeText.children.push({
90
+ this.containerChildren().push({
81
91
  type: "element",
82
92
  tag: "text:bookmark-start",
83
93
  attributes: [{
@@ -88,7 +98,7 @@ var OdtBodyImpl = class {
88
98
  });
89
99
  }
90
100
  appendBookmarkEnd(name) {
91
- this.officeText.children.push({
101
+ this.containerChildren().push({
92
102
  type: "element",
93
103
  tag: "text:bookmark-end",
94
104
  attributes: [{
@@ -98,6 +108,51 @@ var OdtBodyImpl = class {
98
108
  children: []
99
109
  });
100
110
  }
111
+ openDivisionRegion(descriptor) {
112
+ const attributes = [];
113
+ if (descriptor.name !== void 0) attributes.push({
114
+ name: "text:name",
115
+ value: require_xml_entities.encodeXmlText(descriptor.name)
116
+ });
117
+ if (descriptor.protected === true) attributes.push({
118
+ name: "text:protected",
119
+ value: "true"
120
+ });
121
+ const section = {
122
+ type: "element",
123
+ tag: "text:section",
124
+ attributes,
125
+ children: []
126
+ };
127
+ this.containerChildren().push(section);
128
+ this.openDivisions.push({
129
+ section,
130
+ linked: descriptor.linked
131
+ });
132
+ }
133
+ closeRegion() {
134
+ const entry = this.openDivisions.pop();
135
+ if (entry === void 0) return;
136
+ if (entry.linked !== void 0) {
137
+ const sourceAttributes = [{
138
+ name: "xlink:type",
139
+ value: "simple"
140
+ }, {
141
+ name: "xlink:href",
142
+ value: require_xml_entities.encodeXmlText(entry.linked.href)
143
+ }];
144
+ if (entry.linked.sectionName !== void 0) sourceAttributes.push({
145
+ name: "text:section-name",
146
+ value: require_xml_entities.encodeXmlText(entry.linked.sectionName)
147
+ });
148
+ entry.section.children.push({
149
+ type: "element",
150
+ tag: "text:section-source",
151
+ attributes: sourceAttributes,
152
+ children: []
153
+ });
154
+ }
155
+ }
101
156
  };
102
157
  var OdtEditor = class {
103
158
  body;
@@ -2,7 +2,7 @@ import { t as ClockPort } from "../../clock-C7SUuYN0.cjs";
2
2
  import { n as ParagraphInit, t as OdtParagraph } from "../../paragraph-CRaLSzWz.cjs";
3
3
  import { t as OdtList } from "../../list-C_UPIZWM.cjs";
4
4
  import { a as TableInit, n as OdtTable } from "../../table-COmzfIOe.cjs";
5
- import { Box, ContentFormula, ContentVector, LayoutMetadata } from "document-schema.js";
5
+ import { Box, ContentFormula, ContentVector, DivisionDescriptor, LayoutMetadata } from "document-schema.js";
6
6
  import { Package } from "odf.js";
7
7
  //#region src/edit/odt/editor.d.ts
8
8
  interface OdtBody {
@@ -14,6 +14,8 @@ interface OdtBody {
14
14
  appendVectors(vectors: readonly ContentVector[]): OdtParagraph;
15
15
  appendBookmarkStart(name: string): void;
16
16
  appendBookmarkEnd(name: string): void;
17
+ openDivisionRegion(descriptor: DivisionDescriptor): void;
18
+ closeRegion(): void;
17
19
  }
18
20
  declare class OdtEditor {
19
21
  readonly body: OdtBody;
@@ -2,7 +2,7 @@ import { t as ClockPort } from "../../clock-C7SUuYN0.js";
2
2
  import { n as ParagraphInit, t as OdtParagraph } from "../../paragraph-D4f705GG.js";
3
3
  import { t as OdtList } from "../../list-C2GWIe6E.js";
4
4
  import { a as TableInit, n as OdtTable } from "../../table-BmMyqRML.js";
5
- import { Box, ContentFormula, ContentVector, LayoutMetadata } from "document-schema.js";
5
+ import { Box, ContentFormula, ContentVector, DivisionDescriptor, LayoutMetadata } from "document-schema.js";
6
6
  import { Package } from "odf.js";
7
7
  //#region src/edit/odt/editor.d.ts
8
8
  interface OdtBody {
@@ -14,6 +14,8 @@ interface OdtBody {
14
14
  appendVectors(vectors: readonly ContentVector[]): OdtParagraph;
15
15
  appendBookmarkStart(name: string): void;
16
16
  appendBookmarkEnd(name: string): void;
17
+ openDivisionRegion(descriptor: DivisionDescriptor): void;
18
+ closeRegion(): void;
17
19
  }
18
20
  declare class OdtEditor {
19
21
  readonly body: OdtBody;
@@ -30,42 +30,52 @@ function findOfficeText(contentRoot) {
30
30
  var OdtBodyImpl = class {
31
31
  officeText;
32
32
  pkg;
33
+ openDivisions = [];
33
34
  constructor(officeText, pkg) {
34
35
  this.officeText = officeText;
35
36
  this.pkg = pkg;
36
37
  }
38
+ containerChildren() {
39
+ return this.openDivisions.at(-1)?.section.children ?? this.officeText.children;
40
+ }
37
41
  appendParagraph(init) {
38
42
  const paragraphElement = buildParagraph(this.pkg, init);
39
- this.officeText.children.push(paragraphElement);
40
- return new OdtParagraph(this.officeText.children, paragraphElement, this.pkg);
43
+ const container = this.containerChildren();
44
+ container.push(paragraphElement);
45
+ return new OdtParagraph(container, paragraphElement, this.pkg);
41
46
  }
42
47
  appendTable(init) {
43
48
  const tableElement = buildTable(this.pkg, init);
44
- this.officeText.children.push(tableElement);
45
- return new OdtTable(this.officeText.children, tableElement, this.pkg);
49
+ const container = this.containerChildren();
50
+ container.push(tableElement);
51
+ return new OdtTable(container, tableElement, this.pkg);
46
52
  }
47
53
  appendList() {
48
54
  const listElement = buildList(this.pkg);
49
- this.officeText.children.push(listElement);
50
- return new OdtList(this.officeText.children, listElement, this.pkg);
55
+ const container = this.containerChildren();
56
+ container.push(listElement);
57
+ return new OdtList(container, listElement, this.pkg);
51
58
  }
52
59
  appendFormula(formula, frame) {
53
60
  const paragraphElement = buildParagraph(this.pkg);
54
61
  paragraphElement.children.push(insertFormulaFrameMedia(this.pkg, frame, formula));
55
- this.officeText.children.push(paragraphElement);
56
- return new OdtParagraph(this.officeText.children, paragraphElement, this.pkg);
62
+ const container = this.containerChildren();
63
+ container.push(paragraphElement);
64
+ return new OdtParagraph(container, paragraphElement, this.pkg);
57
65
  }
58
66
  appendEmbeddedObject(object, directory) {
59
67
  const paragraphElement = buildParagraph(this.pkg);
60
68
  paragraphElement.children.push(writeEmbeddedObject(object, directory, this.pkg));
61
- this.officeText.children.push(paragraphElement);
62
- return new OdtParagraph(this.officeText.children, paragraphElement, this.pkg);
69
+ const container = this.containerChildren();
70
+ container.push(paragraphElement);
71
+ return new OdtParagraph(container, paragraphElement, this.pkg);
63
72
  }
64
73
  appendVectors(vectors) {
65
74
  const paragraphElement = buildParagraph(this.pkg);
66
75
  for (const vector of vectors) paragraphElement.children.push(buildVectorElement(this.pkg, vector, { textFlowAnchored: true }));
67
- this.officeText.children.push(paragraphElement);
68
- return new OdtParagraph(this.officeText.children, paragraphElement, this.pkg);
76
+ const container = this.containerChildren();
77
+ container.push(paragraphElement);
78
+ return new OdtParagraph(container, paragraphElement, this.pkg);
69
79
  }
70
80
  appendPageBreak() {
71
81
  const paragraphElement = buildParagraph(this.pkg);
@@ -73,10 +83,10 @@ var OdtBodyImpl = class {
73
83
  name: "text:style-name",
74
84
  value: ensurePageBreakStyleName(this.pkg)
75
85
  });
76
- this.officeText.children.push(paragraphElement);
86
+ this.containerChildren().push(paragraphElement);
77
87
  }
78
88
  appendBookmarkStart(name) {
79
- this.officeText.children.push({
89
+ this.containerChildren().push({
80
90
  type: "element",
81
91
  tag: "text:bookmark-start",
82
92
  attributes: [{
@@ -87,7 +97,7 @@ var OdtBodyImpl = class {
87
97
  });
88
98
  }
89
99
  appendBookmarkEnd(name) {
90
- this.officeText.children.push({
100
+ this.containerChildren().push({
91
101
  type: "element",
92
102
  tag: "text:bookmark-end",
93
103
  attributes: [{
@@ -97,6 +107,51 @@ var OdtBodyImpl = class {
97
107
  children: []
98
108
  });
99
109
  }
110
+ openDivisionRegion(descriptor) {
111
+ const attributes = [];
112
+ if (descriptor.name !== void 0) attributes.push({
113
+ name: "text:name",
114
+ value: encodeXmlText$1(descriptor.name)
115
+ });
116
+ if (descriptor.protected === true) attributes.push({
117
+ name: "text:protected",
118
+ value: "true"
119
+ });
120
+ const section = {
121
+ type: "element",
122
+ tag: "text:section",
123
+ attributes,
124
+ children: []
125
+ };
126
+ this.containerChildren().push(section);
127
+ this.openDivisions.push({
128
+ section,
129
+ linked: descriptor.linked
130
+ });
131
+ }
132
+ closeRegion() {
133
+ const entry = this.openDivisions.pop();
134
+ if (entry === void 0) return;
135
+ if (entry.linked !== void 0) {
136
+ const sourceAttributes = [{
137
+ name: "xlink:type",
138
+ value: "simple"
139
+ }, {
140
+ name: "xlink:href",
141
+ value: encodeXmlText$1(entry.linked.href)
142
+ }];
143
+ if (entry.linked.sectionName !== void 0) sourceAttributes.push({
144
+ name: "text:section-name",
145
+ value: encodeXmlText$1(entry.linked.sectionName)
146
+ });
147
+ entry.section.children.push({
148
+ type: "element",
149
+ tag: "text:section-source",
150
+ attributes: sourceAttributes,
151
+ children: []
152
+ });
153
+ }
154
+ }
100
155
  };
101
156
  var OdtEditor = class {
102
157
  body;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js.documents",
3
- "version": "7.16.2",
3
+ "version": "7.17.0",
4
4
  "description": "Bidirectional docx/pptx <-> PDF conversion and a read+write editable OOXML document model, built on ooxml.js and Zod 4 codecs.",
5
5
  "type": "module",
6
6
  "repository": {