diff --git a/apps/docs/document-api/available-operations.mdx b/apps/docs/document-api/available-operations.mdx
index 413706b39a..d2047a2ebd 100644
--- a/apps/docs/document-api/available-operations.mdx
+++ b/apps/docs/document-api/available-operations.mdx
@@ -33,7 +33,7 @@ Use the tables below to see what operations are available and where each one is
| Hyperlinks | 6 | 0 | 6 | [Reference](/document-api/reference/hyperlinks/index) |
| Images | 27 | 0 | 27 | [Reference](/document-api/reference/images/index) |
| Index | 11 | 0 | 11 | [Reference](/document-api/reference/index/index) |
-| Lists | 29 | 0 | 29 | [Reference](/document-api/reference/lists/index) |
+| Lists | 36 | 0 | 36 | [Reference](/document-api/reference/lists/index) |
| Mutations | 2 | 0 | 2 | [Reference](/document-api/reference/mutations/index) |
| Paragraph Formatting | 17 | 0 | 17 | [Reference](/document-api/reference/format/paragraph/index) |
| Paragraph Styles | 2 | 0 | 2 | [Reference](/document-api/reference/styles/paragraph/index) |
@@ -306,6 +306,13 @@ Use the tables below to see what operations are available and where each one is
| editor.doc.lists.setLevelTrailingCharacter(...) | [`lists.setLevelTrailingCharacter`](/document-api/reference/lists/set-level-trailing-character) |
| editor.doc.lists.setLevelMarkerFont(...) | [`lists.setLevelMarkerFont`](/document-api/reference/lists/set-level-marker-font) |
| editor.doc.lists.clearLevelOverrides(...) | [`lists.clearLevelOverrides`](/document-api/reference/lists/clear-level-overrides) |
+| editor.doc.lists.getStyle(...) | [`lists.getStyle`](/document-api/reference/lists/get-style) |
+| editor.doc.lists.applyStyle(...) | [`lists.applyStyle`](/document-api/reference/lists/apply-style) |
+| editor.doc.lists.restartAt(...) | [`lists.restartAt`](/document-api/reference/lists/restart-at) |
+| editor.doc.lists.setLevelNumberStyle(...) | [`lists.setLevelNumberStyle`](/document-api/reference/lists/set-level-number-style) |
+| editor.doc.lists.setLevelText(...) | [`lists.setLevelText`](/document-api/reference/lists/set-level-text) |
+| editor.doc.lists.setLevelStart(...) | [`lists.setLevelStart`](/document-api/reference/lists/set-level-start) |
+| editor.doc.lists.setLevelLayout(...) | [`lists.setLevelLayout`](/document-api/reference/lists/set-level-layout) |
| editor.doc.mutations.preview(...) | [`mutations.preview`](/document-api/reference/mutations/preview) |
| editor.doc.mutations.apply(...) | [`mutations.apply`](/document-api/reference/mutations/apply) |
| editor.doc.format.paragraph.resetDirectFormatting(...) | [`format.paragraph.resetDirectFormatting`](/document-api/reference/format/paragraph/reset-direct-formatting) |
diff --git a/apps/docs/document-api/reference/_generated-manifest.json b/apps/docs/document-api/reference/_generated-manifest.json
index 104d6f07a8..2bb82f4d69 100644
--- a/apps/docs/document-api/reference/_generated-manifest.json
+++ b/apps/docs/document-api/reference/_generated-manifest.json
@@ -278,6 +278,7 @@
"apps/docs/document-api/reference/info.mdx",
"apps/docs/document-api/reference/insert.mdx",
"apps/docs/document-api/reference/lists/apply-preset.mdx",
+ "apps/docs/document-api/reference/lists/apply-style.mdx",
"apps/docs/document-api/reference/lists/apply-template.mdx",
"apps/docs/document-api/reference/lists/attach.mdx",
"apps/docs/document-api/reference/lists/can-continue-previous.mdx",
@@ -288,6 +289,7 @@
"apps/docs/document-api/reference/lists/convert-to-text.mdx",
"apps/docs/document-api/reference/lists/create.mdx",
"apps/docs/document-api/reference/lists/detach.mdx",
+ "apps/docs/document-api/reference/lists/get-style.mdx",
"apps/docs/document-api/reference/lists/get.mdx",
"apps/docs/document-api/reference/lists/indent.mdx",
"apps/docs/document-api/reference/lists/index.mdx",
@@ -295,14 +297,19 @@
"apps/docs/document-api/reference/lists/join.mdx",
"apps/docs/document-api/reference/lists/list.mdx",
"apps/docs/document-api/reference/lists/outdent.mdx",
+ "apps/docs/document-api/reference/lists/restart-at.mdx",
"apps/docs/document-api/reference/lists/separate.mdx",
"apps/docs/document-api/reference/lists/set-level-alignment.mdx",
"apps/docs/document-api/reference/lists/set-level-bullet.mdx",
"apps/docs/document-api/reference/lists/set-level-indents.mdx",
+ "apps/docs/document-api/reference/lists/set-level-layout.mdx",
"apps/docs/document-api/reference/lists/set-level-marker-font.mdx",
+ "apps/docs/document-api/reference/lists/set-level-number-style.mdx",
"apps/docs/document-api/reference/lists/set-level-numbering.mdx",
"apps/docs/document-api/reference/lists/set-level-picture-bullet.mdx",
"apps/docs/document-api/reference/lists/set-level-restart.mdx",
+ "apps/docs/document-api/reference/lists/set-level-start.mdx",
+ "apps/docs/document-api/reference/lists/set-level-text.mdx",
"apps/docs/document-api/reference/lists/set-level-trailing-character.mdx",
"apps/docs/document-api/reference/lists/set-level.mdx",
"apps/docs/document-api/reference/lists/set-type.mdx",
@@ -567,7 +574,14 @@
"lists.setLevelIndents",
"lists.setLevelTrailingCharacter",
"lists.setLevelMarkerFont",
- "lists.clearLevelOverrides"
+ "lists.clearLevelOverrides",
+ "lists.getStyle",
+ "lists.applyStyle",
+ "lists.restartAt",
+ "lists.setLevelNumberStyle",
+ "lists.setLevelText",
+ "lists.setLevelStart",
+ "lists.setLevelLayout"
],
"pagePath": "apps/docs/document-api/reference/lists/index.mdx",
"title": "Lists"
@@ -962,5 +976,5 @@
}
],
"marker": "{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */}",
- "sourceHash": "7c98e8a222685ebb7801111b454a2e5bc6ef18b1ce4a9d7d576a5e48aa69bd5f"
+ "sourceHash": "455ce1c9b3cf75b22ae1ebc76bddb66f31f70f0ab46cea0f178063c87e476dbd"
}
diff --git a/apps/docs/document-api/reference/capabilities/get.mdx b/apps/docs/document-api/reference/capabilities/get.mdx
index edb3ba65e4..06d2367c67 100644
--- a/apps/docs/document-api/reference/capabilities/get.mdx
+++ b/apps/docs/document-api/reference/capabilities/get.mdx
@@ -1547,6 +1547,11 @@ _No fields._
| `operations.lists.applyPreset.dryRun` | boolean | yes | |
| `operations.lists.applyPreset.reasons` | enum[] | no | |
| `operations.lists.applyPreset.tracked` | boolean | yes | |
+| `operations.lists.applyStyle` | object | yes | |
+| `operations.lists.applyStyle.available` | boolean | yes | |
+| `operations.lists.applyStyle.dryRun` | boolean | yes | |
+| `operations.lists.applyStyle.reasons` | enum[] | no | |
+| `operations.lists.applyStyle.tracked` | boolean | yes | |
| `operations.lists.applyTemplate` | object | yes | |
| `operations.lists.applyTemplate.available` | boolean | yes | |
| `operations.lists.applyTemplate.dryRun` | boolean | yes | |
@@ -1602,6 +1607,11 @@ _No fields._
| `operations.lists.get.dryRun` | boolean | yes | |
| `operations.lists.get.reasons` | enum[] | no | |
| `operations.lists.get.tracked` | boolean | yes | |
+| `operations.lists.getStyle` | object | yes | |
+| `operations.lists.getStyle.available` | boolean | yes | |
+| `operations.lists.getStyle.dryRun` | boolean | yes | |
+| `operations.lists.getStyle.reasons` | enum[] | no | |
+| `operations.lists.getStyle.tracked` | boolean | yes | |
| `operations.lists.indent` | object | yes | |
| `operations.lists.indent.available` | boolean | yes | |
| `operations.lists.indent.dryRun` | boolean | yes | |
@@ -1627,6 +1637,11 @@ _No fields._
| `operations.lists.outdent.dryRun` | boolean | yes | |
| `operations.lists.outdent.reasons` | enum[] | no | |
| `operations.lists.outdent.tracked` | boolean | yes | |
+| `operations.lists.restartAt` | object | yes | |
+| `operations.lists.restartAt.available` | boolean | yes | |
+| `operations.lists.restartAt.dryRun` | boolean | yes | |
+| `operations.lists.restartAt.reasons` | enum[] | no | |
+| `operations.lists.restartAt.tracked` | boolean | yes | |
| `operations.lists.separate` | object | yes | |
| `operations.lists.separate.available` | boolean | yes | |
| `operations.lists.separate.dryRun` | boolean | yes | |
@@ -1652,11 +1667,21 @@ _No fields._
| `operations.lists.setLevelIndents.dryRun` | boolean | yes | |
| `operations.lists.setLevelIndents.reasons` | enum[] | no | |
| `operations.lists.setLevelIndents.tracked` | boolean | yes | |
+| `operations.lists.setLevelLayout` | object | yes | |
+| `operations.lists.setLevelLayout.available` | boolean | yes | |
+| `operations.lists.setLevelLayout.dryRun` | boolean | yes | |
+| `operations.lists.setLevelLayout.reasons` | enum[] | no | |
+| `operations.lists.setLevelLayout.tracked` | boolean | yes | |
| `operations.lists.setLevelMarkerFont` | object | yes | |
| `operations.lists.setLevelMarkerFont.available` | boolean | yes | |
| `operations.lists.setLevelMarkerFont.dryRun` | boolean | yes | |
| `operations.lists.setLevelMarkerFont.reasons` | enum[] | no | |
| `operations.lists.setLevelMarkerFont.tracked` | boolean | yes | |
+| `operations.lists.setLevelNumberStyle` | object | yes | |
+| `operations.lists.setLevelNumberStyle.available` | boolean | yes | |
+| `operations.lists.setLevelNumberStyle.dryRun` | boolean | yes | |
+| `operations.lists.setLevelNumberStyle.reasons` | enum[] | no | |
+| `operations.lists.setLevelNumberStyle.tracked` | boolean | yes | |
| `operations.lists.setLevelNumbering` | object | yes | |
| `operations.lists.setLevelNumbering.available` | boolean | yes | |
| `operations.lists.setLevelNumbering.dryRun` | boolean | yes | |
@@ -1672,6 +1697,16 @@ _No fields._
| `operations.lists.setLevelRestart.dryRun` | boolean | yes | |
| `operations.lists.setLevelRestart.reasons` | enum[] | no | |
| `operations.lists.setLevelRestart.tracked` | boolean | yes | |
+| `operations.lists.setLevelStart` | object | yes | |
+| `operations.lists.setLevelStart.available` | boolean | yes | |
+| `operations.lists.setLevelStart.dryRun` | boolean | yes | |
+| `operations.lists.setLevelStart.reasons` | enum[] | no | |
+| `operations.lists.setLevelStart.tracked` | boolean | yes | |
+| `operations.lists.setLevelText` | object | yes | |
+| `operations.lists.setLevelText.available` | boolean | yes | |
+| `operations.lists.setLevelText.dryRun` | boolean | yes | |
+| `operations.lists.setLevelText.reasons` | enum[] | no | |
+| `operations.lists.setLevelText.tracked` | boolean | yes | |
| `operations.lists.setLevelTrailingCharacter` | object | yes | |
| `operations.lists.setLevelTrailingCharacter.available` | boolean | yes | |
| `operations.lists.setLevelTrailingCharacter.dryRun` | boolean | yes | |
@@ -3663,6 +3698,11 @@ _No fields._
"dryRun": true,
"tracked": false
},
+ "lists.applyStyle": {
+ "available": true,
+ "dryRun": true,
+ "tracked": false
+ },
"lists.applyTemplate": {
"available": true,
"dryRun": true,
@@ -3718,6 +3758,11 @@ _No fields._
"dryRun": false,
"tracked": false
},
+ "lists.getStyle": {
+ "available": true,
+ "dryRun": false,
+ "tracked": false
+ },
"lists.indent": {
"available": true,
"dryRun": true,
@@ -3743,6 +3788,11 @@ _No fields._
"dryRun": true,
"tracked": false
},
+ "lists.restartAt": {
+ "available": true,
+ "dryRun": true,
+ "tracked": false
+ },
"lists.separate": {
"available": true,
"dryRun": true,
@@ -3768,6 +3818,11 @@ _No fields._
"dryRun": true,
"tracked": false
},
+ "lists.setLevelLayout": {
+ "available": true,
+ "dryRun": true,
+ "tracked": false
+ },
"lists.setLevelMarkerFont": {
"available": true,
"dryRun": true,
@@ -3778,6 +3833,11 @@ _No fields._
"dryRun": true,
"tracked": false
},
+ "lists.setLevelNumberStyle": {
+ "available": true,
+ "dryRun": true,
+ "tracked": false
+ },
"lists.setLevelPictureBullet": {
"available": true,
"dryRun": true,
@@ -3788,6 +3848,16 @@ _No fields._
"dryRun": true,
"tracked": false
},
+ "lists.setLevelStart": {
+ "available": true,
+ "dryRun": true,
+ "tracked": false
+ },
+ "lists.setLevelText": {
+ "available": true,
+ "dryRun": true,
+ "tracked": false
+ },
"lists.setLevelTrailingCharacter": {
"available": true,
"dryRun": true,
@@ -14811,6 +14881,41 @@ _No fields._
],
"type": "object"
},
+ "lists.applyStyle": {
+ "additionalProperties": false,
+ "properties": {
+ "available": {
+ "type": "boolean"
+ },
+ "dryRun": {
+ "type": "boolean"
+ },
+ "reasons": {
+ "items": {
+ "enum": [
+ "COMMAND_UNAVAILABLE",
+ "HELPER_UNAVAILABLE",
+ "OPERATION_UNAVAILABLE",
+ "TRACKED_MODE_UNAVAILABLE",
+ "DRY_RUN_UNAVAILABLE",
+ "NAMESPACE_UNAVAILABLE",
+ "STYLES_PART_MISSING",
+ "COLLABORATION_ACTIVE"
+ ]
+ },
+ "type": "array"
+ },
+ "tracked": {
+ "type": "boolean"
+ }
+ },
+ "required": [
+ "available",
+ "tracked",
+ "dryRun"
+ ],
+ "type": "object"
+ },
"lists.applyTemplate": {
"additionalProperties": false,
"properties": {
@@ -15196,6 +15301,41 @@ _No fields._
],
"type": "object"
},
+ "lists.getStyle": {
+ "additionalProperties": false,
+ "properties": {
+ "available": {
+ "type": "boolean"
+ },
+ "dryRun": {
+ "type": "boolean"
+ },
+ "reasons": {
+ "items": {
+ "enum": [
+ "COMMAND_UNAVAILABLE",
+ "HELPER_UNAVAILABLE",
+ "OPERATION_UNAVAILABLE",
+ "TRACKED_MODE_UNAVAILABLE",
+ "DRY_RUN_UNAVAILABLE",
+ "NAMESPACE_UNAVAILABLE",
+ "STYLES_PART_MISSING",
+ "COLLABORATION_ACTIVE"
+ ]
+ },
+ "type": "array"
+ },
+ "tracked": {
+ "type": "boolean"
+ }
+ },
+ "required": [
+ "available",
+ "tracked",
+ "dryRun"
+ ],
+ "type": "object"
+ },
"lists.indent": {
"additionalProperties": false,
"properties": {
@@ -15371,6 +15511,41 @@ _No fields._
],
"type": "object"
},
+ "lists.restartAt": {
+ "additionalProperties": false,
+ "properties": {
+ "available": {
+ "type": "boolean"
+ },
+ "dryRun": {
+ "type": "boolean"
+ },
+ "reasons": {
+ "items": {
+ "enum": [
+ "COMMAND_UNAVAILABLE",
+ "HELPER_UNAVAILABLE",
+ "OPERATION_UNAVAILABLE",
+ "TRACKED_MODE_UNAVAILABLE",
+ "DRY_RUN_UNAVAILABLE",
+ "NAMESPACE_UNAVAILABLE",
+ "STYLES_PART_MISSING",
+ "COLLABORATION_ACTIVE"
+ ]
+ },
+ "type": "array"
+ },
+ "tracked": {
+ "type": "boolean"
+ }
+ },
+ "required": [
+ "available",
+ "tracked",
+ "dryRun"
+ ],
+ "type": "object"
+ },
"lists.separate": {
"additionalProperties": false,
"properties": {
@@ -15546,6 +15721,41 @@ _No fields._
],
"type": "object"
},
+ "lists.setLevelLayout": {
+ "additionalProperties": false,
+ "properties": {
+ "available": {
+ "type": "boolean"
+ },
+ "dryRun": {
+ "type": "boolean"
+ },
+ "reasons": {
+ "items": {
+ "enum": [
+ "COMMAND_UNAVAILABLE",
+ "HELPER_UNAVAILABLE",
+ "OPERATION_UNAVAILABLE",
+ "TRACKED_MODE_UNAVAILABLE",
+ "DRY_RUN_UNAVAILABLE",
+ "NAMESPACE_UNAVAILABLE",
+ "STYLES_PART_MISSING",
+ "COLLABORATION_ACTIVE"
+ ]
+ },
+ "type": "array"
+ },
+ "tracked": {
+ "type": "boolean"
+ }
+ },
+ "required": [
+ "available",
+ "tracked",
+ "dryRun"
+ ],
+ "type": "object"
+ },
"lists.setLevelMarkerFont": {
"additionalProperties": false,
"properties": {
@@ -15616,6 +15826,41 @@ _No fields._
],
"type": "object"
},
+ "lists.setLevelNumberStyle": {
+ "additionalProperties": false,
+ "properties": {
+ "available": {
+ "type": "boolean"
+ },
+ "dryRun": {
+ "type": "boolean"
+ },
+ "reasons": {
+ "items": {
+ "enum": [
+ "COMMAND_UNAVAILABLE",
+ "HELPER_UNAVAILABLE",
+ "OPERATION_UNAVAILABLE",
+ "TRACKED_MODE_UNAVAILABLE",
+ "DRY_RUN_UNAVAILABLE",
+ "NAMESPACE_UNAVAILABLE",
+ "STYLES_PART_MISSING",
+ "COLLABORATION_ACTIVE"
+ ]
+ },
+ "type": "array"
+ },
+ "tracked": {
+ "type": "boolean"
+ }
+ },
+ "required": [
+ "available",
+ "tracked",
+ "dryRun"
+ ],
+ "type": "object"
+ },
"lists.setLevelPictureBullet": {
"additionalProperties": false,
"properties": {
@@ -15686,6 +15931,76 @@ _No fields._
],
"type": "object"
},
+ "lists.setLevelStart": {
+ "additionalProperties": false,
+ "properties": {
+ "available": {
+ "type": "boolean"
+ },
+ "dryRun": {
+ "type": "boolean"
+ },
+ "reasons": {
+ "items": {
+ "enum": [
+ "COMMAND_UNAVAILABLE",
+ "HELPER_UNAVAILABLE",
+ "OPERATION_UNAVAILABLE",
+ "TRACKED_MODE_UNAVAILABLE",
+ "DRY_RUN_UNAVAILABLE",
+ "NAMESPACE_UNAVAILABLE",
+ "STYLES_PART_MISSING",
+ "COLLABORATION_ACTIVE"
+ ]
+ },
+ "type": "array"
+ },
+ "tracked": {
+ "type": "boolean"
+ }
+ },
+ "required": [
+ "available",
+ "tracked",
+ "dryRun"
+ ],
+ "type": "object"
+ },
+ "lists.setLevelText": {
+ "additionalProperties": false,
+ "properties": {
+ "available": {
+ "type": "boolean"
+ },
+ "dryRun": {
+ "type": "boolean"
+ },
+ "reasons": {
+ "items": {
+ "enum": [
+ "COMMAND_UNAVAILABLE",
+ "HELPER_UNAVAILABLE",
+ "OPERATION_UNAVAILABLE",
+ "TRACKED_MODE_UNAVAILABLE",
+ "DRY_RUN_UNAVAILABLE",
+ "NAMESPACE_UNAVAILABLE",
+ "STYLES_PART_MISSING",
+ "COLLABORATION_ACTIVE"
+ ]
+ },
+ "type": "array"
+ },
+ "tracked": {
+ "type": "boolean"
+ }
+ },
+ "required": [
+ "available",
+ "tracked",
+ "dryRun"
+ ],
+ "type": "object"
+ },
"lists.setLevelTrailingCharacter": {
"additionalProperties": false,
"properties": {
@@ -18793,6 +19108,13 @@ _No fields._
"lists.setLevelTrailingCharacter",
"lists.setLevelMarkerFont",
"lists.clearLevelOverrides",
+ "lists.getStyle",
+ "lists.applyStyle",
+ "lists.restartAt",
+ "lists.setLevelNumberStyle",
+ "lists.setLevelText",
+ "lists.setLevelStart",
+ "lists.setLevelLayout",
"comments.create",
"comments.patch",
"comments.delete",
diff --git a/apps/docs/document-api/reference/index.mdx b/apps/docs/document-api/reference/index.mdx
index a45e8c27e9..b9d86a11c0 100644
--- a/apps/docs/document-api/reference/index.mdx
+++ b/apps/docs/document-api/reference/index.mdx
@@ -27,7 +27,7 @@ Document API is currently alpha and subject to breaking changes.
| Sections | 18 | 0 | 18 | [Open](/document-api/reference/sections/index) |
| Format | 44 | 1 | 45 | [Open](/document-api/reference/format/index) |
| Styles | 1 | 0 | 1 | [Open](/document-api/reference/styles/index) |
-| Lists | 29 | 0 | 29 | [Open](/document-api/reference/lists/index) |
+| Lists | 36 | 0 | 36 | [Open](/document-api/reference/lists/index) |
| Comments | 5 | 0 | 5 | [Open](/document-api/reference/comments/index) |
| Track Changes | 3 | 0 | 3 | [Open](/document-api/reference/track-changes/index) |
| Query | 1 | 0 | 1 | [Open](/document-api/reference/query/index) |
@@ -185,7 +185,7 @@ The tables below are grouped by namespace.
| lists.list | editor.doc.lists.list(...) | List all list nodes in the document, optionally filtered by scope. |
| lists.get | editor.doc.lists.get(...) | Retrieve a specific list node by target. |
| lists.insert | editor.doc.lists.insert(...) | Insert a new list item before or after an existing list item. The new item inherits the target list context. |
-| lists.create | editor.doc.lists.create(...) | Create a new list from one or more paragraphs, or convert existing paragraphs into a new list. |
+| lists.create | editor.doc.lists.create(...) | Create a new list from one or more paragraphs. Supports optional preset or style for new sequences. When sequence.mode is "continuePrevious", preset and style are not allowed — the new items inherit formatting from the previous sequence. |
| lists.attach | editor.doc.lists.attach(...) | Convert non-list paragraphs to list items under an existing list sequence. |
| lists.detach | editor.doc.lists.detach(...) | Remove numbering properties from list items, converting them to plain paragraphs. |
| lists.indent | editor.doc.lists.indent(...) | Increase the indentation level of a list item. |
@@ -199,11 +199,11 @@ The tables below are grouped by namespace.
| lists.canContinuePrevious | editor.doc.lists.canContinuePrevious(...) | Check whether the target sequence can continue numbering from a previous compatible sequence. |
| lists.setLevelRestart | editor.doc.lists.setLevelRestart(...) | Set the restart behavior for a specific list level. |
| lists.convertToText | editor.doc.lists.convertToText(...) | Convert list items to plain paragraphs, optionally prepending the rendered marker text. |
-| lists.applyTemplate | editor.doc.lists.applyTemplate(...) | Apply a captured ListTemplate to the target list, optionally filtered to specific levels. |
+| lists.applyTemplate | editor.doc.lists.applyTemplate(...) | Advanced alias for lists.applyStyle. Apply a captured ListTemplate to the target list (abstract-scoped, no clone-on-write). |
| lists.applyPreset | editor.doc.lists.applyPreset(...) | Apply a built-in list formatting preset to the target list. |
| lists.setType | editor.doc.lists.setType(...) | Convert a list to ordered or bullet and merge adjacent compatible sequences to preserve continuous numbering. |
-| lists.captureTemplate | editor.doc.lists.captureTemplate(...) | Capture the formatting of a list as a reusable ListTemplate. |
-| lists.setLevelNumbering | editor.doc.lists.setLevelNumbering(...) | Set the numbering format, pattern, and optional start value for a specific list level. |
+| lists.captureTemplate | editor.doc.lists.captureTemplate(...) | Advanced alias for lists.getStyle. Capture list formatting from the abstract definition only (does not merge lvlOverride formatting). |
+| lists.setLevelNumbering | editor.doc.lists.setLevelNumbering(...) | Advanced alias for lists.setLevelNumberStyle/setLevelText/setLevelStart. Set format, pattern, and start in one call (abstract-scoped, no clone-on-write). |
| lists.setLevelBullet | editor.doc.lists.setLevelBullet(...) | Set the bullet marker text for a specific list level. |
| lists.setLevelPictureBullet | editor.doc.lists.setLevelPictureBullet(...) | Set a picture bullet for a specific list level by its OOXML lvlPicBulletId. |
| lists.setLevelAlignment | editor.doc.lists.setLevelAlignment(...) | Set the marker alignment (left, center, right) for a specific list level. |
@@ -211,6 +211,13 @@ The tables below are grouped by namespace.
| lists.setLevelTrailingCharacter | editor.doc.lists.setLevelTrailingCharacter(...) | Set the trailing character (tab, space, nothing) after the marker for a specific list level. |
| lists.setLevelMarkerFont | editor.doc.lists.setLevelMarkerFont(...) | Set the font family used for the marker character at a specific list level. |
| lists.clearLevelOverrides | editor.doc.lists.clearLevelOverrides(...) | Remove instance-level overrides for a specific list level, restoring abstract definition values. |
+| lists.getStyle | editor.doc.lists.getStyle(...) | Read the effective reusable style of a list, including instance-level overrides. Returns a ListStyle that can be applied to other lists via lists.applyStyle. |
+| lists.applyStyle | editor.doc.lists.applyStyle(...) | Apply a reusable list style to the target list. Sequence-local: if the abstract definition is shared with other lists, it is cloned first to avoid affecting them. |
+| lists.restartAt | editor.doc.lists.restartAt(...) | Restart numbering at the target list item with a specific value. If the item is mid-sequence, it is separated first. |
+| lists.setLevelNumberStyle | editor.doc.lists.setLevelNumberStyle(...) | Set the numbering style (e.g. decimal, lowerLetter, upperRoman) for a specific list level. Rejects "bullet" — use setLevelBullet instead. Sequence-local: clones shared definitions. |
+| lists.setLevelText | editor.doc.lists.setLevelText(...) | Set the level text pattern (e.g. "%1.", "(%1)") for a specific list level. Uses OOXML level-placeholder syntax. Sequence-local: clones shared definitions. |
+| lists.setLevelStart | editor.doc.lists.setLevelStart(...) | Set the start value for a specific list level. Rejects bullet levels and non-positive values. Sequence-local: clones shared definitions. |
+| lists.setLevelLayout | editor.doc.lists.setLevelLayout(...) | Set the layout properties (alignment, indentation, trailing character, tab stop) for a specific list level. Accepts partial updates — omitted fields are left unchanged. Sequence-local: clones shared definitions. |
#### Comments
diff --git a/apps/docs/document-api/reference/lists/apply-style.mdx b/apps/docs/document-api/reference/lists/apply-style.mdx
new file mode 100644
index 0000000000..b56c8a0ae8
--- /dev/null
+++ b/apps/docs/document-api/reference/lists/apply-style.mdx
@@ -0,0 +1,341 @@
+---
+title: lists.applyStyle
+sidebarTitle: lists.applyStyle
+description: "Apply a reusable list style to the target list. Sequence-local: if the abstract definition is shared with other lists, it is cloned first to avoid affecting them."
+---
+
+{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */}
+
+> Alpha: Document API is currently alpha and subject to breaking changes.
+
+## Summary
+
+Apply a reusable list style to the target list. Sequence-local: if the abstract definition is shared with other lists, it is cloned first to avoid affecting them.
+
+- Operation ID: `lists.applyStyle`
+- API member path: `editor.doc.lists.applyStyle(...)`
+- Mutates document: `yes`
+- Idempotency: `conditional`
+- Supports tracked mode: `no`
+- Supports dry run: `yes`
+- Deterministic target resolution: `yes`
+
+## Expected result
+
+Returns a ListsMutateItemResult receipt; reports NO_OP if all levels already match.
+
+## Input fields
+
+| Field | Type | Required | Description |
+| --- | --- | --- | --- |
+| `levels` | integer[] | no | |
+| `style` | object | yes | |
+| `style.levels` | object[] | yes | |
+| `style.version` | `1` | yes | Constant: `1` |
+| `target` | ListItemAddress | yes | ListItemAddress |
+| `target.kind` | `"block"` | yes | Constant: `"block"` |
+| `target.nodeId` | string | yes | |
+| `target.nodeType` | `"listItem"` | yes | Constant: `"listItem"` |
+
+### Example request
+
+```json
+{
+ "levels": [
+ 1
+ ],
+ "style": {
+ "levels": [
+ {
+ "level": 1,
+ "lvlText": "example",
+ "numFmt": "example"
+ }
+ ],
+ "version": 1
+ },
+ "target": {
+ "kind": "block",
+ "nodeId": "node-def456",
+ "nodeType": "listItem"
+ }
+}
+```
+
+## Output fields
+
+### Variant 1 (success=true)
+
+| Field | Type | Required | Description |
+| --- | --- | --- | --- |
+| `item` | ListItemAddress | yes | ListItemAddress |
+| `item.kind` | `"block"` | yes | Constant: `"block"` |
+| `item.nodeId` | string | yes | |
+| `item.nodeType` | `"listItem"` | yes | Constant: `"listItem"` |
+| `success` | `true` | yes | Constant: `true` |
+
+### Variant 2 (success=false)
+
+| Field | Type | Required | Description |
+| --- | --- | --- | --- |
+| `failure` | object | yes | |
+| `failure.code` | enum | yes | `"NO_OP"`, `"INVALID_TARGET"`, `"INVALID_INPUT"`, `"LEVEL_OUT_OF_RANGE"` |
+| `failure.details` | any | no | |
+| `failure.message` | string | yes | |
+| `success` | `false` | yes | Constant: `false` |
+
+### Example response
+
+```json
+{
+ "item": {
+ "kind": "block",
+ "nodeId": "node-def456",
+ "nodeType": "listItem"
+ },
+ "success": true
+}
+```
+
+## Pre-apply throws
+
+- `TARGET_NOT_FOUND`
+- `CAPABILITY_UNAVAILABLE`
+- `INVALID_TARGET`
+- `INVALID_INPUT`
+
+## Non-applied failure codes
+
+- `NO_OP`
+- `INVALID_TARGET`
+- `INVALID_INPUT`
+- `LEVEL_OUT_OF_RANGE`
+
+## Raw schemas
+
+
+```json
+{
+ "additionalProperties": false,
+ "properties": {
+ "levels": {
+ "items": {
+ "maximum": 8,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "type": "array"
+ },
+ "style": {
+ "additionalProperties": false,
+ "properties": {
+ "levels": {
+ "items": {
+ "additionalProperties": false,
+ "properties": {
+ "alignment": {
+ "enum": [
+ "left",
+ "center",
+ "right"
+ ]
+ },
+ "indents": {
+ "additionalProperties": false,
+ "properties": {
+ "firstLine": {
+ "type": "integer"
+ },
+ "hanging": {
+ "type": "integer"
+ },
+ "left": {
+ "type": "integer"
+ }
+ },
+ "type": "object"
+ },
+ "level": {
+ "maximum": 8,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "lvlText": {
+ "type": "string"
+ },
+ "markerFont": {
+ "type": "string"
+ },
+ "numFmt": {
+ "type": "string"
+ },
+ "pictureBulletId": {
+ "type": "integer"
+ },
+ "start": {
+ "type": "integer"
+ },
+ "tabStopAt": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "trailingCharacter": {
+ "enum": [
+ "tab",
+ "space",
+ "nothing"
+ ]
+ }
+ },
+ "required": [
+ "level"
+ ],
+ "type": "object"
+ },
+ "type": "array"
+ },
+ "version": {
+ "const": 1
+ }
+ },
+ "required": [
+ "version",
+ "levels"
+ ],
+ "type": "object"
+ },
+ "target": {
+ "$ref": "#/$defs/ListItemAddress"
+ }
+ },
+ "required": [
+ "target",
+ "style"
+ ],
+ "type": "object"
+}
+```
+
+
+
+```json
+{
+ "oneOf": [
+ {
+ "additionalProperties": false,
+ "properties": {
+ "item": {
+ "$ref": "#/$defs/ListItemAddress"
+ },
+ "success": {
+ "const": true
+ }
+ },
+ "required": [
+ "success",
+ "item"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "failure": {
+ "additionalProperties": false,
+ "properties": {
+ "code": {
+ "enum": [
+ "NO_OP",
+ "INVALID_TARGET",
+ "INVALID_INPUT",
+ "LEVEL_OUT_OF_RANGE"
+ ]
+ },
+ "details": {},
+ "message": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "code",
+ "message"
+ ],
+ "type": "object"
+ },
+ "success": {
+ "const": false
+ }
+ },
+ "required": [
+ "success",
+ "failure"
+ ],
+ "type": "object"
+ }
+ ]
+}
+```
+
+
+
+```json
+{
+ "additionalProperties": false,
+ "properties": {
+ "item": {
+ "$ref": "#/$defs/ListItemAddress"
+ },
+ "success": {
+ "const": true
+ }
+ },
+ "required": [
+ "success",
+ "item"
+ ],
+ "type": "object"
+}
+```
+
+
+
+```json
+{
+ "additionalProperties": false,
+ "properties": {
+ "failure": {
+ "additionalProperties": false,
+ "properties": {
+ "code": {
+ "enum": [
+ "NO_OP",
+ "INVALID_TARGET",
+ "INVALID_INPUT",
+ "LEVEL_OUT_OF_RANGE"
+ ]
+ },
+ "details": {},
+ "message": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "code",
+ "message"
+ ],
+ "type": "object"
+ },
+ "success": {
+ "const": false
+ }
+ },
+ "required": [
+ "success",
+ "failure"
+ ],
+ "type": "object"
+}
+```
+
diff --git a/apps/docs/document-api/reference/lists/apply-template.mdx b/apps/docs/document-api/reference/lists/apply-template.mdx
index c34187c1ab..f938d5b9ed 100644
--- a/apps/docs/document-api/reference/lists/apply-template.mdx
+++ b/apps/docs/document-api/reference/lists/apply-template.mdx
@@ -1,7 +1,7 @@
---
title: lists.applyTemplate
sidebarTitle: lists.applyTemplate
-description: Apply a captured ListTemplate to the target list, optionally filtered to specific levels.
+description: Advanced alias for lists.applyStyle. Apply a captured ListTemplate to the target list (abstract-scoped, no clone-on-write).
---
{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */}
@@ -10,7 +10,7 @@ description: Apply a captured ListTemplate to the target list, optionally filter
## Summary
-Apply a captured ListTemplate to the target list, optionally filtered to specific levels.
+Advanced alias for lists.applyStyle. Apply a captured ListTemplate to the target list (abstract-scoped, no clone-on-write).
- Operation ID: `lists.applyTemplate`
- API member path: `editor.doc.lists.applyTemplate(...)`
diff --git a/apps/docs/document-api/reference/lists/capture-template.mdx b/apps/docs/document-api/reference/lists/capture-template.mdx
index 4b38b91a21..58a6cb35ef 100644
--- a/apps/docs/document-api/reference/lists/capture-template.mdx
+++ b/apps/docs/document-api/reference/lists/capture-template.mdx
@@ -1,7 +1,7 @@
---
title: lists.captureTemplate
sidebarTitle: lists.captureTemplate
-description: Capture the formatting of a list as a reusable ListTemplate.
+description: Advanced alias for lists.getStyle. Capture list formatting from the abstract definition only (does not merge lvlOverride formatting).
---
{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */}
@@ -10,7 +10,7 @@ description: Capture the formatting of a list as a reusable ListTemplate.
## Summary
-Capture the formatting of a list as a reusable ListTemplate.
+Advanced alias for lists.getStyle. Capture list formatting from the abstract definition only (does not merge lvlOverride formatting).
- Operation ID: `lists.captureTemplate`
- API member path: `editor.doc.lists.captureTemplate(...)`
diff --git a/apps/docs/document-api/reference/lists/create.mdx b/apps/docs/document-api/reference/lists/create.mdx
index ed7b2d7d5a..78a4363832 100644
--- a/apps/docs/document-api/reference/lists/create.mdx
+++ b/apps/docs/document-api/reference/lists/create.mdx
@@ -1,7 +1,7 @@
---
title: lists.create
sidebarTitle: lists.create
-description: Create a new list from one or more paragraphs, or convert existing paragraphs into a new list.
+description: "Create a new list from one or more paragraphs. Supports optional preset or style for new sequences. When sequence.mode is \"continuePrevious\", preset and style are not allowed — the new items inherit formatting from the previous sequence."
---
{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */}
@@ -10,7 +10,7 @@ description: Create a new list from one or more paragraphs, or convert existing
## Summary
-Create a new list from one or more paragraphs, or convert existing paragraphs into a new list.
+Create a new list from one or more paragraphs. Supports optional preset or style for new sequences. When sequence.mode is "continuePrevious", preset and style are not allowed — the new items inherit formatting from the previous sequence.
- Operation ID: `lists.create`
- API member path: `editor.doc.lists.create(...)`
@@ -26,16 +26,7 @@ Returns a ListsCreateResult with the new listId and the first item address.
## Input fields
-| Field | Type | Required | Description |
-| --- | --- | --- | --- |
-| `at` | BlockAddress | no | BlockAddress |
-| `at.kind` | `"block"` | no | Constant: `"block"` |
-| `at.nodeId` | string | no | |
-| `at.nodeType` | `"paragraph"` | no | Constant: `"paragraph"` |
-| `kind` | enum | yes | `"ordered"`, `"bullet"` |
-| `level` | integer | no | |
-| `mode` | enum | yes | `"empty"`, `"fromParagraphs"` |
-| `target` | BlockAddressOrRange | no | BlockAddressOrRange |
+_No fields._
### Example request
@@ -46,7 +37,6 @@ Returns a ListsCreateResult with the new listId and the first item address.
"nodeId": "node-def456",
"nodeType": "paragraph"
},
- "kind": "ordered",
"mode": "empty",
"target": {
"kind": "block",
@@ -74,7 +64,7 @@ Returns a ListsCreateResult with the new listId and the first item address.
| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `failure` | object | yes | |
-| `failure.code` | enum | yes | `"INVALID_TARGET"`, `"LEVEL_OUT_OF_RANGE"` |
+| `failure.code` | enum | yes | `"INVALID_TARGET"`, `"LEVEL_OUT_OF_RANGE"`, `"INVALID_INPUT"`, `"NO_COMPATIBLE_PREVIOUS"` |
| `failure.details` | any | no | |
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
@@ -98,11 +88,14 @@ Returns a ListsCreateResult with the new listId and the first item address.
- `TARGET_NOT_FOUND`
- `CAPABILITY_UNAVAILABLE`
- `INVALID_TARGET`
+- `INVALID_INPUT`
## Non-applied failure codes
- `INVALID_TARGET`
- `LEVEL_OUT_OF_RANGE`
+- `INVALID_INPUT`
+- `NO_COMPATIBLE_PREVIOUS`
## Raw schemas
@@ -110,20 +103,64 @@ Returns a ListsCreateResult with the new listId and the first item address.
```json
{
"additionalProperties": false,
- "else": {
- "required": [
- "mode",
- "kind",
- "target"
- ]
- },
- "if": {
- "properties": {
- "mode": {
- "const": "empty"
+ "allOf": [
+ {
+ "else": {
+ "required": [
+ "mode",
+ "target"
+ ]
+ },
+ "if": {
+ "properties": {
+ "mode": {
+ "const": "empty"
+ }
+ }
+ },
+ "then": {
+ "required": [
+ "mode",
+ "at"
+ ]
+ }
+ },
+ {
+ "if": {
+ "properties": {
+ "sequence": {
+ "properties": {
+ "mode": {
+ "const": "continuePrevious"
+ }
+ },
+ "required": [
+ "mode"
+ ]
+ }
+ },
+ "required": [
+ "sequence"
+ ]
+ },
+ "then": {
+ "not": {
+ "anyOf": [
+ {
+ "required": [
+ "preset"
+ ]
+ },
+ {
+ "required": [
+ "style"
+ ]
+ }
+ ]
+ }
}
}
- },
+ ],
"properties": {
"at": {
"$ref": "#/$defs/BlockAddress"
@@ -145,21 +182,139 @@ Returns a ListsCreateResult with the new listId and the first item address.
"fromParagraphs"
]
},
+ "preset": {
+ "enum": [
+ "decimal",
+ "decimalParenthesis",
+ "lowerLetter",
+ "upperLetter",
+ "lowerRoman",
+ "upperRoman",
+ "disc",
+ "circle",
+ "square",
+ "dash"
+ ]
+ },
+ "sequence": {
+ "oneOf": [
+ {
+ "additionalProperties": false,
+ "properties": {
+ "mode": {
+ "const": "new"
+ },
+ "startAt": {
+ "minimum": 1,
+ "type": "integer"
+ }
+ },
+ "required": [
+ "mode"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "mode": {
+ "const": "continuePrevious"
+ }
+ },
+ "required": [
+ "mode"
+ ],
+ "type": "object"
+ }
+ ]
+ },
+ "style": {
+ "additionalProperties": false,
+ "properties": {
+ "levels": {
+ "items": {
+ "additionalProperties": false,
+ "properties": {
+ "alignment": {
+ "enum": [
+ "left",
+ "center",
+ "right"
+ ]
+ },
+ "indents": {
+ "additionalProperties": false,
+ "properties": {
+ "firstLine": {
+ "type": "integer"
+ },
+ "hanging": {
+ "type": "integer"
+ },
+ "left": {
+ "type": "integer"
+ }
+ },
+ "type": "object"
+ },
+ "level": {
+ "maximum": 8,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "lvlText": {
+ "type": "string"
+ },
+ "markerFont": {
+ "type": "string"
+ },
+ "numFmt": {
+ "type": "string"
+ },
+ "pictureBulletId": {
+ "type": "integer"
+ },
+ "start": {
+ "type": "integer"
+ },
+ "tabStopAt": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "trailingCharacter": {
+ "enum": [
+ "tab",
+ "space",
+ "nothing"
+ ]
+ }
+ },
+ "required": [
+ "level"
+ ],
+ "type": "object"
+ },
+ "type": "array"
+ },
+ "version": {
+ "const": 1
+ }
+ },
+ "required": [
+ "version",
+ "levels"
+ ],
+ "type": "object"
+ },
"target": {
"$ref": "#/$defs/BlockAddressOrRange"
}
},
"required": [
- "mode",
- "kind"
+ "mode"
],
- "then": {
- "required": [
- "mode",
- "kind",
- "at"
- ]
- },
"type": "object"
}
```
@@ -198,7 +353,9 @@ Returns a ListsCreateResult with the new listId and the first item address.
"code": {
"enum": [
"INVALID_TARGET",
- "LEVEL_OUT_OF_RANGE"
+ "LEVEL_OUT_OF_RANGE",
+ "INVALID_INPUT",
+ "NO_COMPATIBLE_PREVIOUS"
]
},
"details": {},
@@ -263,7 +420,9 @@ Returns a ListsCreateResult with the new listId and the first item address.
"code": {
"enum": [
"INVALID_TARGET",
- "LEVEL_OUT_OF_RANGE"
+ "LEVEL_OUT_OF_RANGE",
+ "INVALID_INPUT",
+ "NO_COMPATIBLE_PREVIOUS"
]
},
"details": {},
diff --git a/apps/docs/document-api/reference/lists/get-style.mdx b/apps/docs/document-api/reference/lists/get-style.mdx
new file mode 100644
index 0000000000..997ee11c69
--- /dev/null
+++ b/apps/docs/document-api/reference/lists/get-style.mdx
@@ -0,0 +1,401 @@
+---
+title: lists.getStyle
+sidebarTitle: lists.getStyle
+description: Read the effective reusable style of a list, including instance-level overrides. Returns a ListStyle that can be applied to other lists via lists.applyStyle.
+---
+
+{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */}
+
+> Alpha: Document API is currently alpha and subject to breaking changes.
+
+## Summary
+
+Read the effective reusable style of a list, including instance-level overrides. Returns a ListStyle that can be applied to other lists via lists.applyStyle.
+
+- Operation ID: `lists.getStyle`
+- API member path: `editor.doc.lists.getStyle(...)`
+- Mutates document: `no`
+- Idempotency: `idempotent`
+- Supports tracked mode: `no`
+- Supports dry run: `no`
+- Deterministic target resolution: `yes`
+
+## Expected result
+
+Returns a ListsGetStyleResult containing the captured style.
+
+## Input fields
+
+| Field | Type | Required | Description |
+| --- | --- | --- | --- |
+| `levels` | integer[] | no | |
+| `target` | ListItemAddress | yes | ListItemAddress |
+| `target.kind` | `"block"` | yes | Constant: `"block"` |
+| `target.nodeId` | string | yes | |
+| `target.nodeType` | `"listItem"` | yes | Constant: `"listItem"` |
+
+### Example request
+
+```json
+{
+ "levels": [
+ 1
+ ],
+ "target": {
+ "kind": "block",
+ "nodeId": "node-def456",
+ "nodeType": "listItem"
+ }
+}
+```
+
+## Output fields
+
+### Variant 1 (success=true)
+
+| Field | Type | Required | Description |
+| --- | --- | --- | --- |
+| `style` | object | yes | |
+| `style.levels` | object[] | yes | |
+| `style.version` | `1` | yes | Constant: `1` |
+| `success` | `true` | yes | Constant: `true` |
+
+### Variant 2 (success=false)
+
+| Field | Type | Required | Description |
+| --- | --- | --- | --- |
+| `failure` | object | yes | |
+| `failure.code` | enum | yes | `"INVALID_TARGET"`, `"INVALID_INPUT"`, `"LEVEL_OUT_OF_RANGE"` |
+| `failure.details` | any | no | |
+| `failure.message` | string | yes | |
+| `success` | `false` | yes | Constant: `false` |
+
+### Example response
+
+```json
+{
+ "style": {
+ "levels": [
+ {
+ "level": 1,
+ "lvlText": "example",
+ "numFmt": "example"
+ }
+ ],
+ "version": 1
+ },
+ "success": true
+}
+```
+
+## Pre-apply throws
+
+- `TARGET_NOT_FOUND`
+- `INVALID_TARGET`
+- `INVALID_INPUT`
+
+## Non-applied failure codes
+
+- `INVALID_TARGET`
+- `INVALID_INPUT`
+- `LEVEL_OUT_OF_RANGE`
+
+## Raw schemas
+
+
+```json
+{
+ "additionalProperties": false,
+ "properties": {
+ "levels": {
+ "items": {
+ "maximum": 8,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "type": "array"
+ },
+ "target": {
+ "$ref": "#/$defs/ListItemAddress"
+ }
+ },
+ "required": [
+ "target"
+ ],
+ "type": "object"
+}
+```
+
+
+
+```json
+{
+ "oneOf": [
+ {
+ "additionalProperties": false,
+ "properties": {
+ "style": {
+ "additionalProperties": false,
+ "properties": {
+ "levels": {
+ "items": {
+ "additionalProperties": false,
+ "properties": {
+ "alignment": {
+ "enum": [
+ "left",
+ "center",
+ "right"
+ ]
+ },
+ "indents": {
+ "additionalProperties": false,
+ "properties": {
+ "firstLine": {
+ "type": "integer"
+ },
+ "hanging": {
+ "type": "integer"
+ },
+ "left": {
+ "type": "integer"
+ }
+ },
+ "type": "object"
+ },
+ "level": {
+ "maximum": 8,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "lvlText": {
+ "type": "string"
+ },
+ "markerFont": {
+ "type": "string"
+ },
+ "numFmt": {
+ "type": "string"
+ },
+ "pictureBulletId": {
+ "type": "integer"
+ },
+ "start": {
+ "type": "integer"
+ },
+ "tabStopAt": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "trailingCharacter": {
+ "enum": [
+ "tab",
+ "space",
+ "nothing"
+ ]
+ }
+ },
+ "required": [
+ "level"
+ ],
+ "type": "object"
+ },
+ "type": "array"
+ },
+ "version": {
+ "const": 1
+ }
+ },
+ "required": [
+ "version",
+ "levels"
+ ],
+ "type": "object"
+ },
+ "success": {
+ "const": true
+ }
+ },
+ "required": [
+ "success",
+ "style"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "failure": {
+ "additionalProperties": false,
+ "properties": {
+ "code": {
+ "enum": [
+ "INVALID_TARGET",
+ "INVALID_INPUT",
+ "LEVEL_OUT_OF_RANGE"
+ ]
+ },
+ "details": {},
+ "message": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "code",
+ "message"
+ ],
+ "type": "object"
+ },
+ "success": {
+ "const": false
+ }
+ },
+ "required": [
+ "success",
+ "failure"
+ ],
+ "type": "object"
+ }
+ ]
+}
+```
+
+
+
+```json
+{
+ "additionalProperties": false,
+ "properties": {
+ "style": {
+ "additionalProperties": false,
+ "properties": {
+ "levels": {
+ "items": {
+ "additionalProperties": false,
+ "properties": {
+ "alignment": {
+ "enum": [
+ "left",
+ "center",
+ "right"
+ ]
+ },
+ "indents": {
+ "additionalProperties": false,
+ "properties": {
+ "firstLine": {
+ "type": "integer"
+ },
+ "hanging": {
+ "type": "integer"
+ },
+ "left": {
+ "type": "integer"
+ }
+ },
+ "type": "object"
+ },
+ "level": {
+ "maximum": 8,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "lvlText": {
+ "type": "string"
+ },
+ "markerFont": {
+ "type": "string"
+ },
+ "numFmt": {
+ "type": "string"
+ },
+ "pictureBulletId": {
+ "type": "integer"
+ },
+ "start": {
+ "type": "integer"
+ },
+ "tabStopAt": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "trailingCharacter": {
+ "enum": [
+ "tab",
+ "space",
+ "nothing"
+ ]
+ }
+ },
+ "required": [
+ "level"
+ ],
+ "type": "object"
+ },
+ "type": "array"
+ },
+ "version": {
+ "const": 1
+ }
+ },
+ "required": [
+ "version",
+ "levels"
+ ],
+ "type": "object"
+ },
+ "success": {
+ "const": true
+ }
+ },
+ "required": [
+ "success",
+ "style"
+ ],
+ "type": "object"
+}
+```
+
+
+
+```json
+{
+ "additionalProperties": false,
+ "properties": {
+ "failure": {
+ "additionalProperties": false,
+ "properties": {
+ "code": {
+ "enum": [
+ "INVALID_TARGET",
+ "INVALID_INPUT",
+ "LEVEL_OUT_OF_RANGE"
+ ]
+ },
+ "details": {},
+ "message": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "code",
+ "message"
+ ],
+ "type": "object"
+ },
+ "success": {
+ "const": false
+ }
+ },
+ "required": [
+ "success",
+ "failure"
+ ],
+ "type": "object"
+}
+```
+
diff --git a/apps/docs/document-api/reference/lists/index.mdx b/apps/docs/document-api/reference/lists/index.mdx
index c6e7bd162d..9cb748e0f7 100644
--- a/apps/docs/document-api/reference/lists/index.mdx
+++ b/apps/docs/document-api/reference/lists/index.mdx
@@ -43,4 +43,11 @@ List inspection and list mutations.
| lists.setLevelTrailingCharacter | `lists.setLevelTrailingCharacter` | Yes | `conditional` | No | Yes |
| lists.setLevelMarkerFont | `lists.setLevelMarkerFont` | Yes | `conditional` | No | Yes |
| lists.clearLevelOverrides | `lists.clearLevelOverrides` | Yes | `conditional` | No | Yes |
+| lists.getStyle | `lists.getStyle` | No | `idempotent` | No | No |
+| lists.applyStyle | `lists.applyStyle` | Yes | `conditional` | No | Yes |
+| lists.restartAt | `lists.restartAt` | Yes | `non-idempotent` | No | Yes |
+| lists.setLevelNumberStyle | `lists.setLevelNumberStyle` | Yes | `conditional` | No | Yes |
+| lists.setLevelText | `lists.setLevelText` | Yes | `conditional` | No | Yes |
+| lists.setLevelStart | `lists.setLevelStart` | Yes | `conditional` | No | Yes |
+| lists.setLevelLayout | `lists.setLevelLayout` | Yes | `conditional` | No | Yes |
diff --git a/apps/docs/document-api/reference/lists/restart-at.mdx b/apps/docs/document-api/reference/lists/restart-at.mdx
new file mode 100644
index 0000000000..05d57e4748
--- /dev/null
+++ b/apps/docs/document-api/reference/lists/restart-at.mdx
@@ -0,0 +1,239 @@
+---
+title: lists.restartAt
+sidebarTitle: lists.restartAt
+description: Restart numbering at the target list item with a specific value. If the item is mid-sequence, it is separated first.
+---
+
+{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */}
+
+> Alpha: Document API is currently alpha and subject to breaking changes.
+
+## Summary
+
+Restart numbering at the target list item with a specific value. If the item is mid-sequence, it is separated first.
+
+- Operation ID: `lists.restartAt`
+- API member path: `editor.doc.lists.restartAt(...)`
+- Mutates document: `yes`
+- Idempotency: `non-idempotent`
+- Supports tracked mode: `no`
+- Supports dry run: `yes`
+- Deterministic target resolution: `yes`
+
+## Expected result
+
+Returns a ListsMutateItemResult receipt.
+
+## Input fields
+
+| Field | Type | Required | Description |
+| --- | --- | --- | --- |
+| `startAt` | integer | yes | |
+| `target` | ListItemAddress | yes | ListItemAddress |
+| `target.kind` | `"block"` | yes | Constant: `"block"` |
+| `target.nodeId` | string | yes | |
+| `target.nodeType` | `"listItem"` | yes | Constant: `"listItem"` |
+
+### Example request
+
+```json
+{
+ "startAt": 1,
+ "target": {
+ "kind": "block",
+ "nodeId": "node-def456",
+ "nodeType": "listItem"
+ }
+}
+```
+
+## Output fields
+
+### Variant 1 (success=true)
+
+| Field | Type | Required | Description |
+| --- | --- | --- | --- |
+| `item` | ListItemAddress | yes | ListItemAddress |
+| `item.kind` | `"block"` | yes | Constant: `"block"` |
+| `item.nodeId` | string | yes | |
+| `item.nodeType` | `"listItem"` | yes | Constant: `"listItem"` |
+| `success` | `true` | yes | Constant: `true` |
+
+### Variant 2 (success=false)
+
+| Field | Type | Required | Description |
+| --- | --- | --- | --- |
+| `failure` | object | yes | |
+| `failure.code` | enum | yes | `"NO_OP"`, `"INVALID_TARGET"`, `"INVALID_INPUT"` |
+| `failure.details` | any | no | |
+| `failure.message` | string | yes | |
+| `success` | `false` | yes | Constant: `false` |
+
+### Example response
+
+```json
+{
+ "item": {
+ "kind": "block",
+ "nodeId": "node-def456",
+ "nodeType": "listItem"
+ },
+ "success": true
+}
+```
+
+## Pre-apply throws
+
+- `TARGET_NOT_FOUND`
+- `CAPABILITY_UNAVAILABLE`
+- `INVALID_TARGET`
+- `INVALID_INPUT`
+
+## Non-applied failure codes
+
+- `NO_OP`
+- `INVALID_TARGET`
+- `INVALID_INPUT`
+
+## Raw schemas
+
+
+```json
+{
+ "additionalProperties": false,
+ "properties": {
+ "startAt": {
+ "minimum": 1,
+ "type": "integer"
+ },
+ "target": {
+ "$ref": "#/$defs/ListItemAddress"
+ }
+ },
+ "required": [
+ "target",
+ "startAt"
+ ],
+ "type": "object"
+}
+```
+
+
+
+```json
+{
+ "oneOf": [
+ {
+ "additionalProperties": false,
+ "properties": {
+ "item": {
+ "$ref": "#/$defs/ListItemAddress"
+ },
+ "success": {
+ "const": true
+ }
+ },
+ "required": [
+ "success",
+ "item"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "failure": {
+ "additionalProperties": false,
+ "properties": {
+ "code": {
+ "enum": [
+ "NO_OP",
+ "INVALID_TARGET",
+ "INVALID_INPUT"
+ ]
+ },
+ "details": {},
+ "message": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "code",
+ "message"
+ ],
+ "type": "object"
+ },
+ "success": {
+ "const": false
+ }
+ },
+ "required": [
+ "success",
+ "failure"
+ ],
+ "type": "object"
+ }
+ ]
+}
+```
+
+
+
+```json
+{
+ "additionalProperties": false,
+ "properties": {
+ "item": {
+ "$ref": "#/$defs/ListItemAddress"
+ },
+ "success": {
+ "const": true
+ }
+ },
+ "required": [
+ "success",
+ "item"
+ ],
+ "type": "object"
+}
+```
+
+
+
+```json
+{
+ "additionalProperties": false,
+ "properties": {
+ "failure": {
+ "additionalProperties": false,
+ "properties": {
+ "code": {
+ "enum": [
+ "NO_OP",
+ "INVALID_TARGET",
+ "INVALID_INPUT"
+ ]
+ },
+ "details": {},
+ "message": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "code",
+ "message"
+ ],
+ "type": "object"
+ },
+ "success": {
+ "const": false
+ }
+ },
+ "required": [
+ "success",
+ "failure"
+ ],
+ "type": "object"
+}
+```
+
diff --git a/apps/docs/document-api/reference/lists/set-level-layout.mdx b/apps/docs/document-api/reference/lists/set-level-layout.mdx
new file mode 100644
index 0000000000..c4a2c9877b
--- /dev/null
+++ b/apps/docs/document-api/reference/lists/set-level-layout.mdx
@@ -0,0 +1,289 @@
+---
+title: lists.setLevelLayout
+sidebarTitle: lists.setLevelLayout
+description: "Set the layout properties (alignment, indentation, trailing character, tab stop) for a specific list level. Accepts partial updates — omitted fields are left unchanged. Sequence-local: clones shared definitions."
+---
+
+{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */}
+
+> Alpha: Document API is currently alpha and subject to breaking changes.
+
+## Summary
+
+Set the layout properties (alignment, indentation, trailing character, tab stop) for a specific list level. Accepts partial updates — omitted fields are left unchanged. Sequence-local: clones shared definitions.
+
+- Operation ID: `lists.setLevelLayout`
+- API member path: `editor.doc.lists.setLevelLayout(...)`
+- Mutates document: `yes`
+- Idempotency: `conditional`
+- Supports tracked mode: `no`
+- Supports dry run: `yes`
+- Deterministic target resolution: `yes`
+
+## Expected result
+
+Returns a ListsMutateItemResult receipt; reports NO_OP if all values already match.
+
+## Input fields
+
+| Field | Type | Required | Description |
+| --- | --- | --- | --- |
+| `layout` | object | yes | |
+| `layout.alignedAt` | integer | no | |
+| `layout.alignment` | enum | no | `"left"`, `"center"`, `"right"` |
+| `layout.followCharacter` | enum | no | `"tab"`, `"space"`, `"nothing"` |
+| `layout.tabStopAt` | any | no | |
+| `layout.textIndentAt` | integer | no | |
+| `level` | integer | yes | |
+| `target` | ListItemAddress | yes | ListItemAddress |
+| `target.kind` | `"block"` | yes | Constant: `"block"` |
+| `target.nodeId` | string | yes | |
+| `target.nodeType` | `"listItem"` | yes | Constant: `"listItem"` |
+
+### Example request
+
+```json
+{
+ "layout": {
+ "alignedAt": 1,
+ "alignment": "left"
+ },
+ "level": 1,
+ "target": {
+ "kind": "block",
+ "nodeId": "node-def456",
+ "nodeType": "listItem"
+ }
+}
+```
+
+## Output fields
+
+### Variant 1 (success=true)
+
+| Field | Type | Required | Description |
+| --- | --- | --- | --- |
+| `item` | ListItemAddress | yes | ListItemAddress |
+| `item.kind` | `"block"` | yes | Constant: `"block"` |
+| `item.nodeId` | string | yes | |
+| `item.nodeType` | `"listItem"` | yes | Constant: `"listItem"` |
+| `success` | `true` | yes | Constant: `true` |
+
+### Variant 2 (success=false)
+
+| Field | Type | Required | Description |
+| --- | --- | --- | --- |
+| `failure` | object | yes | |
+| `failure.code` | enum | yes | `"NO_OP"`, `"INVALID_TARGET"`, `"INVALID_INPUT"`, `"LEVEL_OUT_OF_RANGE"`, `"LEVEL_NOT_FOUND"` |
+| `failure.details` | any | no | |
+| `failure.message` | string | yes | |
+| `success` | `false` | yes | Constant: `false` |
+
+### Example response
+
+```json
+{
+ "item": {
+ "kind": "block",
+ "nodeId": "node-def456",
+ "nodeType": "listItem"
+ },
+ "success": true
+}
+```
+
+## Pre-apply throws
+
+- `TARGET_NOT_FOUND`
+- `CAPABILITY_UNAVAILABLE`
+- `INVALID_TARGET`
+- `INVALID_INPUT`
+
+## Non-applied failure codes
+
+- `NO_OP`
+- `INVALID_TARGET`
+- `INVALID_INPUT`
+- `LEVEL_OUT_OF_RANGE`
+- `LEVEL_NOT_FOUND`
+
+## Raw schemas
+
+
+```json
+{
+ "additionalProperties": false,
+ "properties": {
+ "layout": {
+ "additionalProperties": false,
+ "properties": {
+ "alignedAt": {
+ "type": "integer"
+ },
+ "alignment": {
+ "enum": [
+ "left",
+ "center",
+ "right"
+ ]
+ },
+ "followCharacter": {
+ "enum": [
+ "tab",
+ "space",
+ "nothing"
+ ]
+ },
+ "tabStopAt": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "textIndentAt": {
+ "type": "integer"
+ }
+ },
+ "type": "object"
+ },
+ "level": {
+ "maximum": 8,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "target": {
+ "$ref": "#/$defs/ListItemAddress"
+ }
+ },
+ "required": [
+ "target",
+ "level",
+ "layout"
+ ],
+ "type": "object"
+}
+```
+
+
+
+```json
+{
+ "oneOf": [
+ {
+ "additionalProperties": false,
+ "properties": {
+ "item": {
+ "$ref": "#/$defs/ListItemAddress"
+ },
+ "success": {
+ "const": true
+ }
+ },
+ "required": [
+ "success",
+ "item"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "failure": {
+ "additionalProperties": false,
+ "properties": {
+ "code": {
+ "enum": [
+ "NO_OP",
+ "INVALID_TARGET",
+ "INVALID_INPUT",
+ "LEVEL_OUT_OF_RANGE",
+ "LEVEL_NOT_FOUND"
+ ]
+ },
+ "details": {},
+ "message": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "code",
+ "message"
+ ],
+ "type": "object"
+ },
+ "success": {
+ "const": false
+ }
+ },
+ "required": [
+ "success",
+ "failure"
+ ],
+ "type": "object"
+ }
+ ]
+}
+```
+
+
+
+```json
+{
+ "additionalProperties": false,
+ "properties": {
+ "item": {
+ "$ref": "#/$defs/ListItemAddress"
+ },
+ "success": {
+ "const": true
+ }
+ },
+ "required": [
+ "success",
+ "item"
+ ],
+ "type": "object"
+}
+```
+
+
+
+```json
+{
+ "additionalProperties": false,
+ "properties": {
+ "failure": {
+ "additionalProperties": false,
+ "properties": {
+ "code": {
+ "enum": [
+ "NO_OP",
+ "INVALID_TARGET",
+ "INVALID_INPUT",
+ "LEVEL_OUT_OF_RANGE",
+ "LEVEL_NOT_FOUND"
+ ]
+ },
+ "details": {},
+ "message": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "code",
+ "message"
+ ],
+ "type": "object"
+ },
+ "success": {
+ "const": false
+ }
+ },
+ "required": [
+ "success",
+ "failure"
+ ],
+ "type": "object"
+}
+```
+
diff --git a/apps/docs/document-api/reference/lists/set-level-number-style.mdx b/apps/docs/document-api/reference/lists/set-level-number-style.mdx
new file mode 100644
index 0000000000..4154303637
--- /dev/null
+++ b/apps/docs/document-api/reference/lists/set-level-number-style.mdx
@@ -0,0 +1,252 @@
+---
+title: lists.setLevelNumberStyle
+sidebarTitle: lists.setLevelNumberStyle
+description: "Set the numbering style (e.g. decimal, lowerLetter, upperRoman) for a specific list level. Rejects \"bullet\" — use setLevelBullet instead. Sequence-local: clones shared definitions."
+---
+
+{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */}
+
+> Alpha: Document API is currently alpha and subject to breaking changes.
+
+## Summary
+
+Set the numbering style (e.g. decimal, lowerLetter, upperRoman) for a specific list level. Rejects "bullet" — use setLevelBullet instead. Sequence-local: clones shared definitions.
+
+- Operation ID: `lists.setLevelNumberStyle`
+- API member path: `editor.doc.lists.setLevelNumberStyle(...)`
+- Mutates document: `yes`
+- Idempotency: `conditional`
+- Supports tracked mode: `no`
+- Supports dry run: `yes`
+- Deterministic target resolution: `yes`
+
+## Expected result
+
+Returns a ListsMutateItemResult receipt; reports NO_OP if the value already matches.
+
+## Input fields
+
+| Field | Type | Required | Description |
+| --- | --- | --- | --- |
+| `level` | integer | yes | |
+| `numberStyle` | string | yes | |
+| `target` | ListItemAddress | yes | ListItemAddress |
+| `target.kind` | `"block"` | yes | Constant: `"block"` |
+| `target.nodeId` | string | yes | |
+| `target.nodeType` | `"listItem"` | yes | Constant: `"listItem"` |
+
+### Example request
+
+```json
+{
+ "level": 1,
+ "numberStyle": "example",
+ "target": {
+ "kind": "block",
+ "nodeId": "node-def456",
+ "nodeType": "listItem"
+ }
+}
+```
+
+## Output fields
+
+### Variant 1 (success=true)
+
+| Field | Type | Required | Description |
+| --- | --- | --- | --- |
+| `item` | ListItemAddress | yes | ListItemAddress |
+| `item.kind` | `"block"` | yes | Constant: `"block"` |
+| `item.nodeId` | string | yes | |
+| `item.nodeType` | `"listItem"` | yes | Constant: `"listItem"` |
+| `success` | `true` | yes | Constant: `true` |
+
+### Variant 2 (success=false)
+
+| Field | Type | Required | Description |
+| --- | --- | --- | --- |
+| `failure` | object | yes | |
+| `failure.code` | enum | yes | `"NO_OP"`, `"INVALID_TARGET"`, `"INVALID_INPUT"`, `"LEVEL_OUT_OF_RANGE"`, `"LEVEL_NOT_FOUND"` |
+| `failure.details` | any | no | |
+| `failure.message` | string | yes | |
+| `success` | `false` | yes | Constant: `false` |
+
+### Example response
+
+```json
+{
+ "item": {
+ "kind": "block",
+ "nodeId": "node-def456",
+ "nodeType": "listItem"
+ },
+ "success": true
+}
+```
+
+## Pre-apply throws
+
+- `TARGET_NOT_FOUND`
+- `CAPABILITY_UNAVAILABLE`
+- `INVALID_TARGET`
+- `INVALID_INPUT`
+
+## Non-applied failure codes
+
+- `NO_OP`
+- `INVALID_TARGET`
+- `INVALID_INPUT`
+- `LEVEL_OUT_OF_RANGE`
+- `LEVEL_NOT_FOUND`
+
+## Raw schemas
+
+
+```json
+{
+ "additionalProperties": false,
+ "properties": {
+ "level": {
+ "maximum": 8,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "numberStyle": {
+ "type": "string"
+ },
+ "target": {
+ "$ref": "#/$defs/ListItemAddress"
+ }
+ },
+ "required": [
+ "target",
+ "level",
+ "numberStyle"
+ ],
+ "type": "object"
+}
+```
+
+
+
+```json
+{
+ "oneOf": [
+ {
+ "additionalProperties": false,
+ "properties": {
+ "item": {
+ "$ref": "#/$defs/ListItemAddress"
+ },
+ "success": {
+ "const": true
+ }
+ },
+ "required": [
+ "success",
+ "item"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "failure": {
+ "additionalProperties": false,
+ "properties": {
+ "code": {
+ "enum": [
+ "NO_OP",
+ "INVALID_TARGET",
+ "INVALID_INPUT",
+ "LEVEL_OUT_OF_RANGE",
+ "LEVEL_NOT_FOUND"
+ ]
+ },
+ "details": {},
+ "message": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "code",
+ "message"
+ ],
+ "type": "object"
+ },
+ "success": {
+ "const": false
+ }
+ },
+ "required": [
+ "success",
+ "failure"
+ ],
+ "type": "object"
+ }
+ ]
+}
+```
+
+
+
+```json
+{
+ "additionalProperties": false,
+ "properties": {
+ "item": {
+ "$ref": "#/$defs/ListItemAddress"
+ },
+ "success": {
+ "const": true
+ }
+ },
+ "required": [
+ "success",
+ "item"
+ ],
+ "type": "object"
+}
+```
+
+
+
+```json
+{
+ "additionalProperties": false,
+ "properties": {
+ "failure": {
+ "additionalProperties": false,
+ "properties": {
+ "code": {
+ "enum": [
+ "NO_OP",
+ "INVALID_TARGET",
+ "INVALID_INPUT",
+ "LEVEL_OUT_OF_RANGE",
+ "LEVEL_NOT_FOUND"
+ ]
+ },
+ "details": {},
+ "message": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "code",
+ "message"
+ ],
+ "type": "object"
+ },
+ "success": {
+ "const": false
+ }
+ },
+ "required": [
+ "success",
+ "failure"
+ ],
+ "type": "object"
+}
+```
+
diff --git a/apps/docs/document-api/reference/lists/set-level-numbering.mdx b/apps/docs/document-api/reference/lists/set-level-numbering.mdx
index 16c6acebe2..3cec858d67 100644
--- a/apps/docs/document-api/reference/lists/set-level-numbering.mdx
+++ b/apps/docs/document-api/reference/lists/set-level-numbering.mdx
@@ -1,7 +1,7 @@
---
title: lists.setLevelNumbering
sidebarTitle: lists.setLevelNumbering
-description: Set the numbering format, pattern, and optional start value for a specific list level.
+description: Advanced alias for lists.setLevelNumberStyle/setLevelText/setLevelStart. Set format, pattern, and start in one call (abstract-scoped, no clone-on-write).
---
{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */}
@@ -10,7 +10,7 @@ description: Set the numbering format, pattern, and optional start value for a s
## Summary
-Set the numbering format, pattern, and optional start value for a specific list level.
+Advanced alias for lists.setLevelNumberStyle/setLevelText/setLevelStart. Set format, pattern, and start in one call (abstract-scoped, no clone-on-write).
- Operation ID: `lists.setLevelNumbering`
- API member path: `editor.doc.lists.setLevelNumbering(...)`
diff --git a/apps/docs/document-api/reference/lists/set-level-start.mdx b/apps/docs/document-api/reference/lists/set-level-start.mdx
new file mode 100644
index 0000000000..7af437ec9a
--- /dev/null
+++ b/apps/docs/document-api/reference/lists/set-level-start.mdx
@@ -0,0 +1,253 @@
+---
+title: lists.setLevelStart
+sidebarTitle: lists.setLevelStart
+description: "Set the start value for a specific list level. Rejects bullet levels and non-positive values. Sequence-local: clones shared definitions."
+---
+
+{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */}
+
+> Alpha: Document API is currently alpha and subject to breaking changes.
+
+## Summary
+
+Set the start value for a specific list level. Rejects bullet levels and non-positive values. Sequence-local: clones shared definitions.
+
+- Operation ID: `lists.setLevelStart`
+- API member path: `editor.doc.lists.setLevelStart(...)`
+- Mutates document: `yes`
+- Idempotency: `conditional`
+- Supports tracked mode: `no`
+- Supports dry run: `yes`
+- Deterministic target resolution: `yes`
+
+## Expected result
+
+Returns a ListsMutateItemResult receipt; reports NO_OP if the value already matches.
+
+## Input fields
+
+| Field | Type | Required | Description |
+| --- | --- | --- | --- |
+| `level` | integer | yes | |
+| `startAt` | integer | yes | |
+| `target` | ListItemAddress | yes | ListItemAddress |
+| `target.kind` | `"block"` | yes | Constant: `"block"` |
+| `target.nodeId` | string | yes | |
+| `target.nodeType` | `"listItem"` | yes | Constant: `"listItem"` |
+
+### Example request
+
+```json
+{
+ "level": 1,
+ "startAt": 1,
+ "target": {
+ "kind": "block",
+ "nodeId": "node-def456",
+ "nodeType": "listItem"
+ }
+}
+```
+
+## Output fields
+
+### Variant 1 (success=true)
+
+| Field | Type | Required | Description |
+| --- | --- | --- | --- |
+| `item` | ListItemAddress | yes | ListItemAddress |
+| `item.kind` | `"block"` | yes | Constant: `"block"` |
+| `item.nodeId` | string | yes | |
+| `item.nodeType` | `"listItem"` | yes | Constant: `"listItem"` |
+| `success` | `true` | yes | Constant: `true` |
+
+### Variant 2 (success=false)
+
+| Field | Type | Required | Description |
+| --- | --- | --- | --- |
+| `failure` | object | yes | |
+| `failure.code` | enum | yes | `"NO_OP"`, `"INVALID_TARGET"`, `"INVALID_INPUT"`, `"LEVEL_OUT_OF_RANGE"`, `"LEVEL_NOT_FOUND"` |
+| `failure.details` | any | no | |
+| `failure.message` | string | yes | |
+| `success` | `false` | yes | Constant: `false` |
+
+### Example response
+
+```json
+{
+ "item": {
+ "kind": "block",
+ "nodeId": "node-def456",
+ "nodeType": "listItem"
+ },
+ "success": true
+}
+```
+
+## Pre-apply throws
+
+- `TARGET_NOT_FOUND`
+- `CAPABILITY_UNAVAILABLE`
+- `INVALID_TARGET`
+- `INVALID_INPUT`
+
+## Non-applied failure codes
+
+- `NO_OP`
+- `INVALID_TARGET`
+- `INVALID_INPUT`
+- `LEVEL_OUT_OF_RANGE`
+- `LEVEL_NOT_FOUND`
+
+## Raw schemas
+
+
+```json
+{
+ "additionalProperties": false,
+ "properties": {
+ "level": {
+ "maximum": 8,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "startAt": {
+ "minimum": 1,
+ "type": "integer"
+ },
+ "target": {
+ "$ref": "#/$defs/ListItemAddress"
+ }
+ },
+ "required": [
+ "target",
+ "level",
+ "startAt"
+ ],
+ "type": "object"
+}
+```
+
+
+
+```json
+{
+ "oneOf": [
+ {
+ "additionalProperties": false,
+ "properties": {
+ "item": {
+ "$ref": "#/$defs/ListItemAddress"
+ },
+ "success": {
+ "const": true
+ }
+ },
+ "required": [
+ "success",
+ "item"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "failure": {
+ "additionalProperties": false,
+ "properties": {
+ "code": {
+ "enum": [
+ "NO_OP",
+ "INVALID_TARGET",
+ "INVALID_INPUT",
+ "LEVEL_OUT_OF_RANGE",
+ "LEVEL_NOT_FOUND"
+ ]
+ },
+ "details": {},
+ "message": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "code",
+ "message"
+ ],
+ "type": "object"
+ },
+ "success": {
+ "const": false
+ }
+ },
+ "required": [
+ "success",
+ "failure"
+ ],
+ "type": "object"
+ }
+ ]
+}
+```
+
+
+
+```json
+{
+ "additionalProperties": false,
+ "properties": {
+ "item": {
+ "$ref": "#/$defs/ListItemAddress"
+ },
+ "success": {
+ "const": true
+ }
+ },
+ "required": [
+ "success",
+ "item"
+ ],
+ "type": "object"
+}
+```
+
+
+
+```json
+{
+ "additionalProperties": false,
+ "properties": {
+ "failure": {
+ "additionalProperties": false,
+ "properties": {
+ "code": {
+ "enum": [
+ "NO_OP",
+ "INVALID_TARGET",
+ "INVALID_INPUT",
+ "LEVEL_OUT_OF_RANGE",
+ "LEVEL_NOT_FOUND"
+ ]
+ },
+ "details": {},
+ "message": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "code",
+ "message"
+ ],
+ "type": "object"
+ },
+ "success": {
+ "const": false
+ }
+ },
+ "required": [
+ "success",
+ "failure"
+ ],
+ "type": "object"
+}
+```
+
diff --git a/apps/docs/document-api/reference/lists/set-level-text.mdx b/apps/docs/document-api/reference/lists/set-level-text.mdx
new file mode 100644
index 0000000000..08c01a21fc
--- /dev/null
+++ b/apps/docs/document-api/reference/lists/set-level-text.mdx
@@ -0,0 +1,251 @@
+---
+title: lists.setLevelText
+sidebarTitle: lists.setLevelText
+description: "Set the level text pattern (e.g. \"%1.\", \"(%1)\") for a specific list level. Uses OOXML level-placeholder syntax. Sequence-local: clones shared definitions."
+---
+
+{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */}
+
+> Alpha: Document API is currently alpha and subject to breaking changes.
+
+## Summary
+
+Set the level text pattern (e.g. "%1.", "(%1)") for a specific list level. Uses OOXML level-placeholder syntax. Sequence-local: clones shared definitions.
+
+- Operation ID: `lists.setLevelText`
+- API member path: `editor.doc.lists.setLevelText(...)`
+- Mutates document: `yes`
+- Idempotency: `conditional`
+- Supports tracked mode: `no`
+- Supports dry run: `yes`
+- Deterministic target resolution: `yes`
+
+## Expected result
+
+Returns a ListsMutateItemResult receipt; reports NO_OP if the value already matches.
+
+## Input fields
+
+| Field | Type | Required | Description |
+| --- | --- | --- | --- |
+| `level` | integer | yes | |
+| `target` | ListItemAddress | yes | ListItemAddress |
+| `target.kind` | `"block"` | yes | Constant: `"block"` |
+| `target.nodeId` | string | yes | |
+| `target.nodeType` | `"listItem"` | yes | Constant: `"listItem"` |
+| `text` | string | yes | |
+
+### Example request
+
+```json
+{
+ "level": 1,
+ "target": {
+ "kind": "block",
+ "nodeId": "node-def456",
+ "nodeType": "listItem"
+ },
+ "text": "Hello, world."
+}
+```
+
+## Output fields
+
+### Variant 1 (success=true)
+
+| Field | Type | Required | Description |
+| --- | --- | --- | --- |
+| `item` | ListItemAddress | yes | ListItemAddress |
+| `item.kind` | `"block"` | yes | Constant: `"block"` |
+| `item.nodeId` | string | yes | |
+| `item.nodeType` | `"listItem"` | yes | Constant: `"listItem"` |
+| `success` | `true` | yes | Constant: `true` |
+
+### Variant 2 (success=false)
+
+| Field | Type | Required | Description |
+| --- | --- | --- | --- |
+| `failure` | object | yes | |
+| `failure.code` | enum | yes | `"NO_OP"`, `"INVALID_TARGET"`, `"INVALID_INPUT"`, `"LEVEL_OUT_OF_RANGE"`, `"LEVEL_NOT_FOUND"` |
+| `failure.details` | any | no | |
+| `failure.message` | string | yes | |
+| `success` | `false` | yes | Constant: `false` |
+
+### Example response
+
+```json
+{
+ "item": {
+ "kind": "block",
+ "nodeId": "node-def456",
+ "nodeType": "listItem"
+ },
+ "success": true
+}
+```
+
+## Pre-apply throws
+
+- `TARGET_NOT_FOUND`
+- `CAPABILITY_UNAVAILABLE`
+- `INVALID_TARGET`
+
+## Non-applied failure codes
+
+- `NO_OP`
+- `INVALID_TARGET`
+- `INVALID_INPUT`
+- `LEVEL_OUT_OF_RANGE`
+- `LEVEL_NOT_FOUND`
+
+## Raw schemas
+
+
+```json
+{
+ "additionalProperties": false,
+ "properties": {
+ "level": {
+ "maximum": 8,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "target": {
+ "$ref": "#/$defs/ListItemAddress"
+ },
+ "text": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "target",
+ "level",
+ "text"
+ ],
+ "type": "object"
+}
+```
+
+
+
+```json
+{
+ "oneOf": [
+ {
+ "additionalProperties": false,
+ "properties": {
+ "item": {
+ "$ref": "#/$defs/ListItemAddress"
+ },
+ "success": {
+ "const": true
+ }
+ },
+ "required": [
+ "success",
+ "item"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "failure": {
+ "additionalProperties": false,
+ "properties": {
+ "code": {
+ "enum": [
+ "NO_OP",
+ "INVALID_TARGET",
+ "INVALID_INPUT",
+ "LEVEL_OUT_OF_RANGE",
+ "LEVEL_NOT_FOUND"
+ ]
+ },
+ "details": {},
+ "message": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "code",
+ "message"
+ ],
+ "type": "object"
+ },
+ "success": {
+ "const": false
+ }
+ },
+ "required": [
+ "success",
+ "failure"
+ ],
+ "type": "object"
+ }
+ ]
+}
+```
+
+
+
+```json
+{
+ "additionalProperties": false,
+ "properties": {
+ "item": {
+ "$ref": "#/$defs/ListItemAddress"
+ },
+ "success": {
+ "const": true
+ }
+ },
+ "required": [
+ "success",
+ "item"
+ ],
+ "type": "object"
+}
+```
+
+
+
+```json
+{
+ "additionalProperties": false,
+ "properties": {
+ "failure": {
+ "additionalProperties": false,
+ "properties": {
+ "code": {
+ "enum": [
+ "NO_OP",
+ "INVALID_TARGET",
+ "INVALID_INPUT",
+ "LEVEL_OUT_OF_RANGE",
+ "LEVEL_NOT_FOUND"
+ ]
+ },
+ "details": {},
+ "message": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "code",
+ "message"
+ ],
+ "type": "object"
+ },
+ "success": {
+ "const": false
+ }
+ },
+ "required": [
+ "success",
+ "failure"
+ ],
+ "type": "object"
+}
+```
+
diff --git a/apps/docs/document-engine/sdks.mdx b/apps/docs/document-engine/sdks.mdx
index 4c4f776fcb..e59d84eeee 100644
--- a/apps/docs/document-engine/sdks.mdx
+++ b/apps/docs/document-engine/sdks.mdx
@@ -380,7 +380,7 @@ The SDKs expose all operations from the [Document API](/document-api/overview) p
| `doc.getMarkdown` | `get-markdown` | Extract the document content as a Markdown string. |
| `doc.getHtml` | `get-html` | Extract the document content as an HTML string. |
| `doc.markdownToFragment` | `markdown-to-fragment` | Convert a Markdown string into an SDM/1 structural fragment. |
-| `doc.info` | `info` | Return document summary info including word, character, paragraph, heading, table, image, comment, tracked-change, SDT-field, and list counts, plus outline and capabilities. |
+| `doc.info` | `info` | Return document summary info including word, character, paragraph, heading, table, image, comment, tracked-change, SDT-field, list, and page counts, plus outline and capabilities. |
| `doc.clearContent` | `clear-content` | Clear all document body content, leaving a single empty paragraph. |
| `doc.insert` | `insert` | Insert content into the document. Two input shapes: legacy string-based (value + type) inserts inline content at a text position within an existing block; structural SDFragment (content) inserts one or more blocks as siblings relative to a BlockNodeAddress target. When target is omitted, content appends at the end of the document. Legacy mode supports text (default), markdown, and html content types via the `type` field. Structural mode uses `placement` (before/after/insideStart/insideEnd) to position relative to the target block. |
| `doc.replace` | `replace` | Replace content at a contiguous document selection. Text path accepts a SelectionTarget or ref plus replacement text. Structural path accepts a BlockNodeAddress (replaces whole block), SelectionTarget (expands to full covered block boundaries), or ref plus SDFragment content. |
@@ -641,7 +641,7 @@ The SDKs expose all operations from the [Document API](/document-api/overview) p
| `doc.lists.list` | `lists list` | List all list nodes in the document, optionally filtered by scope. |
| `doc.lists.get` | `lists get` | Retrieve a specific list node by target. |
| `doc.lists.insert` | `lists insert` | Insert a new list item before or after an existing list item. The new item inherits the target list context. |
-| `doc.lists.create` | `lists create` | Create a new list from one or more paragraphs, or convert existing paragraphs into a new list. |
+| `doc.lists.create` | `lists create` | Create a new list from one or more paragraphs. Supports optional preset or style for new sequences. When sequence.mode is "continuePrevious", preset and style are not allowed — the new items inherit formatting from the previous sequence. |
| `doc.lists.attach` | `lists attach` | Convert non-list paragraphs to list items under an existing list sequence. |
| `doc.lists.detach` | `lists detach` | Remove numbering properties from list items, converting them to plain paragraphs. |
| `doc.lists.indent` | `lists indent` | Increase the indentation level of a list item. |
@@ -655,11 +655,11 @@ The SDKs expose all operations from the [Document API](/document-api/overview) p
| `doc.lists.canContinuePrevious` | `lists can-continue-previous` | Check whether the target sequence can continue numbering from a previous compatible sequence. |
| `doc.lists.setLevelRestart` | `lists set-level-restart` | Set the restart behavior for a specific list level. |
| `doc.lists.convertToText` | `lists convert-to-text` | Convert list items to plain paragraphs, optionally prepending the rendered marker text. |
-| `doc.lists.applyTemplate` | `lists apply-template` | Apply a captured ListTemplate to the target list, optionally filtered to specific levels. |
+| `doc.lists.applyTemplate` | `lists apply-template` | Advanced alias for lists.applyStyle. Apply a captured ListTemplate to the target list (abstract-scoped, no clone-on-write). |
| `doc.lists.applyPreset` | `lists apply-preset` | Apply a built-in list formatting preset to the target list. |
| `doc.lists.setType` | `lists set-type` | Convert a list to ordered or bullet and merge adjacent compatible sequences to preserve continuous numbering. |
-| `doc.lists.captureTemplate` | `lists capture-template` | Capture the formatting of a list as a reusable ListTemplate. |
-| `doc.lists.setLevelNumbering` | `lists set-level-numbering` | Set the numbering format, pattern, and optional start value for a specific list level. |
+| `doc.lists.captureTemplate` | `lists capture-template` | Advanced alias for lists.getStyle. Capture list formatting from the abstract definition only (does not merge lvlOverride formatting). |
+| `doc.lists.setLevelNumbering` | `lists set-level-numbering` | Advanced alias for lists.setLevelNumberStyle/setLevelText/setLevelStart. Set format, pattern, and start in one call (abstract-scoped, no clone-on-write). |
| `doc.lists.setLevelBullet` | `lists set-level-bullet` | Set the bullet marker text for a specific list level. |
| `doc.lists.setLevelPictureBullet` | `lists set-level-picture-bullet` | Set a picture bullet for a specific list level by its OOXML lvlPicBulletId. |
| `doc.lists.setLevelAlignment` | `lists set-level-alignment` | Set the marker alignment (left, center, right) for a specific list level. |
@@ -667,6 +667,13 @@ The SDKs expose all operations from the [Document API](/document-api/overview) p
| `doc.lists.setLevelTrailingCharacter` | `lists set-level-trailing-character` | Set the trailing character (tab, space, nothing) after the marker for a specific list level. |
| `doc.lists.setLevelMarkerFont` | `lists set-level-marker-font` | Set the font family used for the marker character at a specific list level. |
| `doc.lists.clearLevelOverrides` | `lists clear-level-overrides` | Remove instance-level overrides for a specific list level, restoring abstract definition values. |
+| `doc.lists.getStyle` | `lists get-style` | Read the effective reusable style of a list, including instance-level overrides. Returns a ListStyle that can be applied to other lists via lists.applyStyle. |
+| `doc.lists.applyStyle` | `lists apply-style` | Apply a reusable list style to the target list. Sequence-local: if the abstract definition is shared with other lists, it is cloned first to avoid affecting them. |
+| `doc.lists.restartAt` | `lists restart-at` | Restart numbering at the target list item with a specific value. If the item is mid-sequence, it is separated first. |
+| `doc.lists.setLevelNumberStyle` | `lists set-level-number-style` | Set the numbering style (e.g. decimal, lowerLetter, upperRoman) for a specific list level. Rejects "bullet" — use setLevelBullet instead. Sequence-local: clones shared definitions. |
+| `doc.lists.setLevelText` | `lists set-level-text` | Set the level text pattern (e.g. "%1.", "(%1)") for a specific list level. Uses OOXML level-placeholder syntax. Sequence-local: clones shared definitions. |
+| `doc.lists.setLevelStart` | `lists set-level-start` | Set the start value for a specific list level. Rejects bullet levels and non-positive values. Sequence-local: clones shared definitions. |
+| `doc.lists.setLevelLayout` | `lists set-level-layout` | Set the layout properties (alignment, indentation, trailing character, tab stop) for a specific list level. Accepts partial updates — omitted fields are left unchanged. Sequence-local: clones shared definitions. |
#### Tables
@@ -818,7 +825,7 @@ The SDKs expose all operations from the [Document API](/document-api/overview) p
| `doc.get_markdown` | `get-markdown` | Extract the document content as a Markdown string. |
| `doc.get_html` | `get-html` | Extract the document content as an HTML string. |
| `doc.markdown_to_fragment` | `markdown-to-fragment` | Convert a Markdown string into an SDM/1 structural fragment. |
-| `doc.info` | `info` | Return document summary info including word, character, paragraph, heading, table, image, comment, tracked-change, SDT-field, and list counts, plus outline and capabilities. |
+| `doc.info` | `info` | Return document summary info including word, character, paragraph, heading, table, image, comment, tracked-change, SDT-field, list, and page counts, plus outline and capabilities. |
| `doc.clear_content` | `clear-content` | Clear all document body content, leaving a single empty paragraph. |
| `doc.insert` | `insert` | Insert content into the document. Two input shapes: legacy string-based (value + type) inserts inline content at a text position within an existing block; structural SDFragment (content) inserts one or more blocks as siblings relative to a BlockNodeAddress target. When target is omitted, content appends at the end of the document. Legacy mode supports text (default), markdown, and html content types via the `type` field. Structural mode uses `placement` (before/after/insideStart/insideEnd) to position relative to the target block. |
| `doc.replace` | `replace` | Replace content at a contiguous document selection. Text path accepts a SelectionTarget or ref plus replacement text. Structural path accepts a BlockNodeAddress (replaces whole block), SelectionTarget (expands to full covered block boundaries), or ref plus SDFragment content. |
@@ -1079,7 +1086,7 @@ The SDKs expose all operations from the [Document API](/document-api/overview) p
| `doc.lists.list` | `lists list` | List all list nodes in the document, optionally filtered by scope. |
| `doc.lists.get` | `lists get` | Retrieve a specific list node by target. |
| `doc.lists.insert` | `lists insert` | Insert a new list item before or after an existing list item. The new item inherits the target list context. |
-| `doc.lists.create` | `lists create` | Create a new list from one or more paragraphs, or convert existing paragraphs into a new list. |
+| `doc.lists.create` | `lists create` | Create a new list from one or more paragraphs. Supports optional preset or style for new sequences. When sequence.mode is "continuePrevious", preset and style are not allowed — the new items inherit formatting from the previous sequence. |
| `doc.lists.attach` | `lists attach` | Convert non-list paragraphs to list items under an existing list sequence. |
| `doc.lists.detach` | `lists detach` | Remove numbering properties from list items, converting them to plain paragraphs. |
| `doc.lists.indent` | `lists indent` | Increase the indentation level of a list item. |
@@ -1093,11 +1100,11 @@ The SDKs expose all operations from the [Document API](/document-api/overview) p
| `doc.lists.can_continue_previous` | `lists can-continue-previous` | Check whether the target sequence can continue numbering from a previous compatible sequence. |
| `doc.lists.set_level_restart` | `lists set-level-restart` | Set the restart behavior for a specific list level. |
| `doc.lists.convert_to_text` | `lists convert-to-text` | Convert list items to plain paragraphs, optionally prepending the rendered marker text. |
-| `doc.lists.apply_template` | `lists apply-template` | Apply a captured ListTemplate to the target list, optionally filtered to specific levels. |
+| `doc.lists.apply_template` | `lists apply-template` | Advanced alias for lists.applyStyle. Apply a captured ListTemplate to the target list (abstract-scoped, no clone-on-write). |
| `doc.lists.apply_preset` | `lists apply-preset` | Apply a built-in list formatting preset to the target list. |
| `doc.lists.set_type` | `lists set-type` | Convert a list to ordered or bullet and merge adjacent compatible sequences to preserve continuous numbering. |
-| `doc.lists.capture_template` | `lists capture-template` | Capture the formatting of a list as a reusable ListTemplate. |
-| `doc.lists.set_level_numbering` | `lists set-level-numbering` | Set the numbering format, pattern, and optional start value for a specific list level. |
+| `doc.lists.capture_template` | `lists capture-template` | Advanced alias for lists.getStyle. Capture list formatting from the abstract definition only (does not merge lvlOverride formatting). |
+| `doc.lists.set_level_numbering` | `lists set-level-numbering` | Advanced alias for lists.setLevelNumberStyle/setLevelText/setLevelStart. Set format, pattern, and start in one call (abstract-scoped, no clone-on-write). |
| `doc.lists.set_level_bullet` | `lists set-level-bullet` | Set the bullet marker text for a specific list level. |
| `doc.lists.set_level_picture_bullet` | `lists set-level-picture-bullet` | Set a picture bullet for a specific list level by its OOXML lvlPicBulletId. |
| `doc.lists.set_level_alignment` | `lists set-level-alignment` | Set the marker alignment (left, center, right) for a specific list level. |
@@ -1105,6 +1112,13 @@ The SDKs expose all operations from the [Document API](/document-api/overview) p
| `doc.lists.set_level_trailing_character` | `lists set-level-trailing-character` | Set the trailing character (tab, space, nothing) after the marker for a specific list level. |
| `doc.lists.set_level_marker_font` | `lists set-level-marker-font` | Set the font family used for the marker character at a specific list level. |
| `doc.lists.clear_level_overrides` | `lists clear-level-overrides` | Remove instance-level overrides for a specific list level, restoring abstract definition values. |
+| `doc.lists.get_style` | `lists get-style` | Read the effective reusable style of a list, including instance-level overrides. Returns a ListStyle that can be applied to other lists via lists.applyStyle. |
+| `doc.lists.apply_style` | `lists apply-style` | Apply a reusable list style to the target list. Sequence-local: if the abstract definition is shared with other lists, it is cloned first to avoid affecting them. |
+| `doc.lists.restart_at` | `lists restart-at` | Restart numbering at the target list item with a specific value. If the item is mid-sequence, it is separated first. |
+| `doc.lists.set_level_number_style` | `lists set-level-number-style` | Set the numbering style (e.g. decimal, lowerLetter, upperRoman) for a specific list level. Rejects "bullet" — use setLevelBullet instead. Sequence-local: clones shared definitions. |
+| `doc.lists.set_level_text` | `lists set-level-text` | Set the level text pattern (e.g. "%1.", "(%1)") for a specific list level. Uses OOXML level-placeholder syntax. Sequence-local: clones shared definitions. |
+| `doc.lists.set_level_start` | `lists set-level-start` | Set the start value for a specific list level. Rejects bullet levels and non-positive values. Sequence-local: clones shared definitions. |
+| `doc.lists.set_level_layout` | `lists set-level-layout` | Set the layout properties (alignment, indentation, trailing character, tab stop) for a specific list level. Accepts partial updates — omitted fields are left unchanged. Sequence-local: clones shared definitions. |
#### Tables
diff --git a/packages/document-api/src/contract/operation-definitions.ts b/packages/document-api/src/contract/operation-definitions.ts
index 1f59e228b0..5abf184735 100644
--- a/packages/document-api/src/contract/operation-definitions.ts
+++ b/packages/document-api/src/contract/operation-definitions.ts
@@ -1305,15 +1305,16 @@ export const OPERATION_DEFINITIONS = {
},
'lists.create': {
memberPath: 'lists.create',
- description: 'Create a new list from one or more paragraphs, or convert existing paragraphs into a new list.',
+ description:
+ 'Create a new list from one or more paragraphs. Supports optional preset or style for new sequences. When sequence.mode is "continuePrevious", preset and style are not allowed — the new items inherit formatting from the previous sequence.',
expectedResult: 'Returns a ListsCreateResult with the new listId and the first item address.',
requiresDocumentContext: true,
metadata: mutationOperation({
idempotency: 'non-idempotent',
supportsDryRun: true,
supportsTrackedMode: false,
- possibleFailureCodes: ['INVALID_TARGET', 'LEVEL_OUT_OF_RANGE'],
- throws: [...T_NOT_FOUND_CAPABLE, 'INVALID_TARGET'],
+ possibleFailureCodes: ['INVALID_TARGET', 'LEVEL_OUT_OF_RANGE', 'INVALID_INPUT', 'NO_COMPATIBLE_PREVIOUS'],
+ throws: [...T_NOT_FOUND_CAPABLE, 'INVALID_TARGET', 'INVALID_INPUT'],
}),
referenceDocPath: 'lists/create.mdx',
referenceGroup: 'lists',
@@ -1528,7 +1529,8 @@ export const OPERATION_DEFINITIONS = {
// SD-1973 — List formatting and templates
'lists.applyTemplate': {
memberPath: 'lists.applyTemplate',
- description: 'Apply a captured ListTemplate to the target list, optionally filtered to specific levels.',
+ description:
+ 'Advanced alias for lists.applyStyle. Apply a captured ListTemplate to the target list (abstract-scoped, no clone-on-write).',
expectedResult: 'Returns a ListsMutateItemResult receipt; reports NO_OP if all levels already match.',
requiresDocumentContext: true,
metadata: mutationOperation({
@@ -1577,7 +1579,8 @@ export const OPERATION_DEFINITIONS = {
},
'lists.captureTemplate': {
memberPath: 'lists.captureTemplate',
- description: 'Capture the formatting of a list as a reusable ListTemplate.',
+ description:
+ 'Advanced alias for lists.getStyle. Capture list formatting from the abstract definition only (does not merge lvlOverride formatting).',
expectedResult: 'Returns a ListsCaptureTemplateResult containing the captured template.',
requiresDocumentContext: true,
metadata: readOperation({
@@ -1590,7 +1593,8 @@ export const OPERATION_DEFINITIONS = {
},
'lists.setLevelNumbering': {
memberPath: 'lists.setLevelNumbering',
- description: 'Set the numbering format, pattern, and optional start value for a specific list level.',
+ description:
+ 'Advanced alias for lists.setLevelNumberStyle/setLevelText/setLevelStart. Set format, pattern, and start in one call (abstract-scoped, no clone-on-write).',
expectedResult: 'Returns a ListsMutateItemResult receipt; reports NO_OP if the level already matches.',
requiresDocumentContext: true,
metadata: mutationOperation({
@@ -1716,6 +1720,118 @@ export const OPERATION_DEFINITIONS = {
referenceGroup: 'lists',
},
+ // SD-2025 — User-facing list style operations
+ 'lists.getStyle': {
+ memberPath: 'lists.getStyle',
+ description:
+ 'Read the effective reusable style of a list, including instance-level overrides. Returns a ListStyle that can be applied to other lists via lists.applyStyle.',
+ expectedResult: 'Returns a ListsGetStyleResult containing the captured style.',
+ requiresDocumentContext: true,
+ metadata: readOperation({
+ idempotency: 'idempotent',
+ throws: ['TARGET_NOT_FOUND', 'INVALID_TARGET', 'INVALID_INPUT'],
+ possibleFailureCodes: ['INVALID_TARGET', 'INVALID_INPUT', 'LEVEL_OUT_OF_RANGE'],
+ }),
+ referenceDocPath: 'lists/get-style.mdx',
+ referenceGroup: 'lists',
+ },
+ 'lists.applyStyle': {
+ memberPath: 'lists.applyStyle',
+ description:
+ 'Apply a reusable list style to the target list. Sequence-local: if the abstract definition is shared with other lists, it is cloned first to avoid affecting them.',
+ expectedResult: 'Returns a ListsMutateItemResult receipt; reports NO_OP if all levels already match.',
+ requiresDocumentContext: true,
+ metadata: mutationOperation({
+ idempotency: 'conditional',
+ supportsDryRun: true,
+ supportsTrackedMode: false,
+ possibleFailureCodes: ['NO_OP', 'INVALID_TARGET', 'INVALID_INPUT', 'LEVEL_OUT_OF_RANGE'],
+ throws: [...T_NOT_FOUND_CAPABLE, 'INVALID_TARGET', 'INVALID_INPUT'],
+ }),
+ referenceDocPath: 'lists/apply-style.mdx',
+ referenceGroup: 'lists',
+ },
+ 'lists.restartAt': {
+ memberPath: 'lists.restartAt',
+ description:
+ 'Restart numbering at the target list item with a specific value. If the item is mid-sequence, it is separated first.',
+ expectedResult: 'Returns a ListsMutateItemResult receipt.',
+ requiresDocumentContext: true,
+ metadata: mutationOperation({
+ idempotency: 'non-idempotent',
+ supportsDryRun: true,
+ supportsTrackedMode: false,
+ possibleFailureCodes: ['NO_OP', 'INVALID_TARGET', 'INVALID_INPUT'],
+ throws: [...T_NOT_FOUND_CAPABLE, 'INVALID_TARGET', 'INVALID_INPUT'],
+ }),
+ referenceDocPath: 'lists/restart-at.mdx',
+ referenceGroup: 'lists',
+ },
+ 'lists.setLevelNumberStyle': {
+ memberPath: 'lists.setLevelNumberStyle',
+ description:
+ 'Set the numbering style (e.g. decimal, lowerLetter, upperRoman) for a specific list level. Rejects "bullet" — use setLevelBullet instead. Sequence-local: clones shared definitions.',
+ expectedResult: 'Returns a ListsMutateItemResult receipt; reports NO_OP if the value already matches.',
+ requiresDocumentContext: true,
+ metadata: mutationOperation({
+ idempotency: 'conditional',
+ supportsDryRun: true,
+ supportsTrackedMode: false,
+ possibleFailureCodes: ['NO_OP', 'INVALID_TARGET', 'INVALID_INPUT', 'LEVEL_OUT_OF_RANGE', 'LEVEL_NOT_FOUND'],
+ throws: [...T_NOT_FOUND_CAPABLE, 'INVALID_TARGET', 'INVALID_INPUT'],
+ }),
+ referenceDocPath: 'lists/set-level-number-style.mdx',
+ referenceGroup: 'lists',
+ },
+ 'lists.setLevelText': {
+ memberPath: 'lists.setLevelText',
+ description:
+ 'Set the level text pattern (e.g. "%1.", "(%1)") for a specific list level. Uses OOXML level-placeholder syntax. Sequence-local: clones shared definitions.',
+ expectedResult: 'Returns a ListsMutateItemResult receipt; reports NO_OP if the value already matches.',
+ requiresDocumentContext: true,
+ metadata: mutationOperation({
+ idempotency: 'conditional',
+ supportsDryRun: true,
+ supportsTrackedMode: false,
+ possibleFailureCodes: ['NO_OP', 'INVALID_TARGET', 'INVALID_INPUT', 'LEVEL_OUT_OF_RANGE', 'LEVEL_NOT_FOUND'],
+ throws: [...T_NOT_FOUND_CAPABLE, 'INVALID_TARGET'],
+ }),
+ referenceDocPath: 'lists/set-level-text.mdx',
+ referenceGroup: 'lists',
+ },
+ 'lists.setLevelStart': {
+ memberPath: 'lists.setLevelStart',
+ description:
+ 'Set the start value for a specific list level. Rejects bullet levels and non-positive values. Sequence-local: clones shared definitions.',
+ expectedResult: 'Returns a ListsMutateItemResult receipt; reports NO_OP if the value already matches.',
+ requiresDocumentContext: true,
+ metadata: mutationOperation({
+ idempotency: 'conditional',
+ supportsDryRun: true,
+ supportsTrackedMode: false,
+ possibleFailureCodes: ['NO_OP', 'INVALID_TARGET', 'INVALID_INPUT', 'LEVEL_OUT_OF_RANGE', 'LEVEL_NOT_FOUND'],
+ throws: [...T_NOT_FOUND_CAPABLE, 'INVALID_TARGET', 'INVALID_INPUT'],
+ }),
+ referenceDocPath: 'lists/set-level-start.mdx',
+ referenceGroup: 'lists',
+ },
+ 'lists.setLevelLayout': {
+ memberPath: 'lists.setLevelLayout',
+ description:
+ 'Set the layout properties (alignment, indentation, trailing character, tab stop) for a specific list level. Accepts partial updates — omitted fields are left unchanged. Sequence-local: clones shared definitions.',
+ expectedResult: 'Returns a ListsMutateItemResult receipt; reports NO_OP if all values already match.',
+ requiresDocumentContext: true,
+ metadata: mutationOperation({
+ idempotency: 'conditional',
+ supportsDryRun: true,
+ supportsTrackedMode: false,
+ possibleFailureCodes: ['NO_OP', 'INVALID_TARGET', 'INVALID_INPUT', 'LEVEL_OUT_OF_RANGE', 'LEVEL_NOT_FOUND'],
+ throws: [...T_NOT_FOUND_CAPABLE, 'INVALID_TARGET', 'INVALID_INPUT'],
+ }),
+ referenceDocPath: 'lists/set-level-layout.mdx',
+ referenceGroup: 'lists',
+ },
+
'comments.create': {
memberPath: 'comments.create',
description: 'Create a new comment thread (or reply when parentCommentId is given).',
diff --git a/packages/document-api/src/contract/operation-registry.ts b/packages/document-api/src/contract/operation-registry.ts
index 7158ff066e..59e1640922 100644
--- a/packages/document-api/src/contract/operation-registry.ts
+++ b/packages/document-api/src/contract/operation-registry.ts
@@ -103,6 +103,14 @@ import type {
ListsSetLevelTrailingCharacterInput,
ListsSetLevelMarkerFontInput,
ListsClearLevelOverridesInput,
+ ListsGetStyleInput,
+ ListsGetStyleResult,
+ ListsApplyStyleInput,
+ ListsRestartAtInput,
+ ListsSetLevelNumberStyleInput,
+ ListsSetLevelTextInput,
+ ListsSetLevelStartInput,
+ ListsSetLevelLayoutInput,
} from '../lists/lists.types.js';
import type {
ParagraphMutationResult,
@@ -690,6 +698,19 @@ export interface OperationRegistry extends FormatInlineAliasOperationRegistry {
output: ListsMutateItemResult;
};
+ // --- lists.* (SD-2025 user-facing) ---
+ 'lists.getStyle': { input: ListsGetStyleInput; options: never; output: ListsGetStyleResult };
+ 'lists.applyStyle': { input: ListsApplyStyleInput; options: MutationOptions; output: ListsMutateItemResult };
+ 'lists.restartAt': { input: ListsRestartAtInput; options: MutationOptions; output: ListsMutateItemResult };
+ 'lists.setLevelNumberStyle': {
+ input: ListsSetLevelNumberStyleInput;
+ options: MutationOptions;
+ output: ListsMutateItemResult;
+ };
+ 'lists.setLevelText': { input: ListsSetLevelTextInput; options: MutationOptions; output: ListsMutateItemResult };
+ 'lists.setLevelStart': { input: ListsSetLevelStartInput; options: MutationOptions; output: ListsMutateItemResult };
+ 'lists.setLevelLayout': { input: ListsSetLevelLayoutInput; options: MutationOptions; output: ListsMutateItemResult };
+
// --- sections.* ---
'sections.list': { input: SectionsListQuery | undefined; options: never; output: SectionsListResult };
'sections.get': { input: SectionsGetInput; options: never; output: SectionInfo };
diff --git a/packages/document-api/src/contract/schemas.ts b/packages/document-api/src/contract/schemas.ts
index c44a234f81..8a35aa04e9 100644
--- a/packages/document-api/src/contract/schemas.ts
+++ b/packages/document-api/src/contract/schemas.ts
@@ -3476,12 +3476,74 @@ const operationSchemas: Record = {
target: ref('BlockAddressOrRange'),
kind: listKindSchema,
level: { type: 'integer', minimum: 0, maximum: 8 },
+ preset: {
+ enum: [
+ 'decimal',
+ 'decimalParenthesis',
+ 'lowerLetter',
+ 'upperLetter',
+ 'lowerRoman',
+ 'upperRoman',
+ 'disc',
+ 'circle',
+ 'square',
+ 'dash',
+ ],
+ },
+ style: objectSchema(
+ {
+ version: { const: 1 },
+ levels: arraySchema(
+ objectSchema(
+ {
+ level: { type: 'integer', minimum: 0, maximum: 8 },
+ numFmt: { type: 'string' },
+ lvlText: { type: 'string' },
+ start: { type: 'integer' },
+ alignment: { enum: ['left', 'center', 'right'] },
+ indents: objectSchema({
+ left: { type: 'integer' },
+ hanging: { type: 'integer' },
+ firstLine: { type: 'integer' },
+ }),
+ trailingCharacter: { enum: ['tab', 'space', 'nothing'] },
+ markerFont: { type: 'string' },
+ pictureBulletId: { type: 'integer' },
+ tabStopAt: { type: ['integer', 'null'] },
+ },
+ ['level'],
+ ),
+ ),
+ },
+ ['version', 'levels'],
+ ),
+ sequence: {
+ oneOf: [
+ objectSchema({ mode: { const: 'new' }, startAt: { type: 'integer', minimum: 1 } }, ['mode']),
+ objectSchema({ mode: { const: 'continuePrevious' } }, ['mode']),
+ ],
+ },
},
- required: ['mode', 'kind'],
+ required: ['mode'],
additionalProperties: false,
- if: { properties: { mode: { const: 'empty' } } },
- then: { required: ['mode', 'kind', 'at'] },
- else: { required: ['mode', 'kind', 'target'] },
+ allOf: [
+ // mode-conditional: 'empty' requires 'at', 'fromParagraphs' requires 'target'
+ {
+ if: { properties: { mode: { const: 'empty' } } },
+ then: { required: ['mode', 'at'] },
+ else: { required: ['mode', 'target'] },
+ },
+ // continuePrevious is incompatible with preset/style
+ {
+ if: {
+ properties: { sequence: { properties: { mode: { const: 'continuePrevious' } }, required: ['mode'] } },
+ required: ['sequence'],
+ },
+ then: {
+ not: { anyOf: [{ required: ['preset'] }, { required: ['style'] }] },
+ },
+ },
+ ],
},
output: {
oneOf: [
@@ -3929,6 +3991,164 @@ const operationSchemas: Record = {
failure: listsFailureSchemaFor('lists.clearLevelOverrides'),
},
+ // SD-2025 — User-facing list style operations
+ 'lists.getStyle': (() => {
+ const listLevelTemplateSchema = objectSchema(
+ {
+ level: { type: 'integer', minimum: 0, maximum: 8 },
+ numFmt: { type: 'string' },
+ lvlText: { type: 'string' },
+ start: { type: 'integer' },
+ alignment: { enum: ['left', 'center', 'right'] },
+ indents: objectSchema({
+ left: { type: 'integer' },
+ hanging: { type: 'integer' },
+ firstLine: { type: 'integer' },
+ }),
+ trailingCharacter: { enum: ['tab', 'space', 'nothing'] },
+ markerFont: { type: 'string' },
+ pictureBulletId: { type: 'integer' },
+ tabStopAt: { type: ['integer', 'null'] },
+ },
+ ['level'],
+ );
+ const styleSchema = objectSchema(
+ {
+ version: { const: 1 },
+ levels: arraySchema(listLevelTemplateSchema),
+ },
+ ['version', 'levels'],
+ );
+ const successSchema = objectSchema(
+ {
+ success: { const: true },
+ style: styleSchema,
+ },
+ ['success', 'style'],
+ );
+ return {
+ input: objectSchema(
+ {
+ target: listItemAddressSchema,
+ levels: arraySchema({ type: 'integer', minimum: 0, maximum: 8 }),
+ },
+ ['target'],
+ ),
+ output: { oneOf: [successSchema, listsFailureSchemaFor('lists.getStyle')] },
+ success: successSchema,
+ failure: listsFailureSchemaFor('lists.getStyle'),
+ };
+ })(),
+ 'lists.applyStyle': {
+ input: objectSchema(
+ {
+ target: listItemAddressSchema,
+ style: objectSchema(
+ {
+ version: { const: 1 },
+ levels: arraySchema(
+ objectSchema(
+ {
+ level: { type: 'integer', minimum: 0, maximum: 8 },
+ numFmt: { type: 'string' },
+ lvlText: { type: 'string' },
+ start: { type: 'integer' },
+ alignment: { enum: ['left', 'center', 'right'] },
+ indents: objectSchema({
+ left: { type: 'integer' },
+ hanging: { type: 'integer' },
+ firstLine: { type: 'integer' },
+ }),
+ trailingCharacter: { enum: ['tab', 'space', 'nothing'] },
+ markerFont: { type: 'string' },
+ pictureBulletId: { type: 'integer' },
+ tabStopAt: { type: ['integer', 'null'] },
+ },
+ ['level'],
+ ),
+ ),
+ },
+ ['version', 'levels'],
+ ),
+ levels: arraySchema({ type: 'integer', minimum: 0, maximum: 8 }),
+ },
+ ['target', 'style'],
+ ),
+ output: listsMutateItemResultSchemaFor('lists.applyStyle'),
+ success: listsMutateItemSuccessSchema,
+ failure: listsFailureSchemaFor('lists.applyStyle'),
+ },
+ 'lists.restartAt': {
+ input: objectSchema(
+ {
+ target: listItemAddressSchema,
+ startAt: { type: 'integer', minimum: 1 },
+ },
+ ['target', 'startAt'],
+ ),
+ output: listsMutateItemResultSchemaFor('lists.restartAt'),
+ success: listsMutateItemSuccessSchema,
+ failure: listsFailureSchemaFor('lists.restartAt'),
+ },
+ 'lists.setLevelNumberStyle': {
+ input: objectSchema(
+ {
+ target: listItemAddressSchema,
+ level: { type: 'integer', minimum: 0, maximum: 8 },
+ numberStyle: { type: 'string' },
+ },
+ ['target', 'level', 'numberStyle'],
+ ),
+ output: listsMutateItemResultSchemaFor('lists.setLevelNumberStyle'),
+ success: listsMutateItemSuccessSchema,
+ failure: listsFailureSchemaFor('lists.setLevelNumberStyle'),
+ },
+ 'lists.setLevelText': {
+ input: objectSchema(
+ {
+ target: listItemAddressSchema,
+ level: { type: 'integer', minimum: 0, maximum: 8 },
+ text: { type: 'string' },
+ },
+ ['target', 'level', 'text'],
+ ),
+ output: listsMutateItemResultSchemaFor('lists.setLevelText'),
+ success: listsMutateItemSuccessSchema,
+ failure: listsFailureSchemaFor('lists.setLevelText'),
+ },
+ 'lists.setLevelStart': {
+ input: objectSchema(
+ {
+ target: listItemAddressSchema,
+ level: { type: 'integer', minimum: 0, maximum: 8 },
+ startAt: { type: 'integer', minimum: 1 },
+ },
+ ['target', 'level', 'startAt'],
+ ),
+ output: listsMutateItemResultSchemaFor('lists.setLevelStart'),
+ success: listsMutateItemSuccessSchema,
+ failure: listsFailureSchemaFor('lists.setLevelStart'),
+ },
+ 'lists.setLevelLayout': {
+ input: objectSchema(
+ {
+ target: listItemAddressSchema,
+ level: { type: 'integer', minimum: 0, maximum: 8 },
+ layout: objectSchema({
+ alignment: { enum: ['left', 'center', 'right'] },
+ alignedAt: { type: 'integer' },
+ textIndentAt: { type: 'integer' },
+ followCharacter: { enum: ['tab', 'space', 'nothing'] },
+ tabStopAt: { type: ['integer', 'null'] },
+ }),
+ },
+ ['target', 'level', 'layout'],
+ ),
+ output: listsMutateItemResultSchemaFor('lists.setLevelLayout'),
+ success: listsMutateItemSuccessSchema,
+ failure: listsFailureSchemaFor('lists.setLevelLayout'),
+ },
+
'comments.create': {
input: objectSchema(
{
diff --git a/packages/document-api/src/index.ts b/packages/document-api/src/index.ts
index 6fea2206b3..826ca9ca84 100644
--- a/packages/document-api/src/index.ts
+++ b/packages/document-api/src/index.ts
@@ -159,6 +159,14 @@ import type {
ListsSetLevelMarkerFontInput,
ListsClearLevelOverridesInput,
ListsSetTypeInput,
+ ListsGetStyleInput,
+ ListsGetStyleResult,
+ ListsApplyStyleInput,
+ ListsRestartAtInput,
+ ListsSetLevelNumberStyleInput,
+ ListsSetLevelTextInput,
+ ListsSetLevelStartInput,
+ ListsSetLevelLayoutInput,
} from './lists/lists.types.js';
import {
executeListsGet,
@@ -190,6 +198,13 @@ import {
executeListsSetLevelMarkerFont,
executeListsClearLevelOverrides,
executeListsSetType,
+ executeListsGetStyle,
+ executeListsApplyStyle,
+ executeListsRestartAt,
+ executeListsSetLevelNumberStyle,
+ executeListsSetLevelText,
+ executeListsSetLevelStart,
+ executeListsSetLevelLayout,
} from './lists/lists.js';
import { executeReplace, type ReplaceInput } from './replace/replace.js';
import type { CreateAdapter, CreateApi } from './create/create.js';
@@ -1217,6 +1232,18 @@ export type {
ListsSetLevelMarkerFontInput,
ListsClearLevelOverridesInput,
ListsSetTypeInput,
+ ListStyle,
+ ListLevelStyle,
+ ListLevelLayout,
+ ListsGetStyleInput,
+ ListsGetStyleResult,
+ ListsGetStyleSuccessResult,
+ ListsApplyStyleInput,
+ ListsRestartAtInput,
+ ListsSetLevelNumberStyleInput,
+ ListsSetLevelTextInput,
+ ListsSetLevelStartInput,
+ ListsSetLevelLayoutInput,
} from './lists/lists.types.js';
export {
LIST_KINDS,
@@ -2085,6 +2112,29 @@ export function createDocumentApi(adapters: DocumentApiAdapters): DocumentApi {
setType(input: ListsSetTypeInput, options?: MutationOptions): ListsMutateItemResult {
return executeListsSetType(adapters.lists, input, options);
},
+
+ // SD-2025 user-facing operations
+ getStyle(input: ListsGetStyleInput): ListsGetStyleResult {
+ return executeListsGetStyle(adapters.lists, input);
+ },
+ applyStyle(input: ListsApplyStyleInput, options?: MutationOptions): ListsMutateItemResult {
+ return executeListsApplyStyle(adapters.lists, input, options);
+ },
+ restartAt(input: ListsRestartAtInput, options?: MutationOptions): ListsMutateItemResult {
+ return executeListsRestartAt(adapters.lists, input, options);
+ },
+ setLevelNumberStyle(input: ListsSetLevelNumberStyleInput, options?: MutationOptions): ListsMutateItemResult {
+ return executeListsSetLevelNumberStyle(adapters.lists, input, options);
+ },
+ setLevelText(input: ListsSetLevelTextInput, options?: MutationOptions): ListsMutateItemResult {
+ return executeListsSetLevelText(adapters.lists, input, options);
+ },
+ setLevelStart(input: ListsSetLevelStartInput, options?: MutationOptions): ListsMutateItemResult {
+ return executeListsSetLevelStart(adapters.lists, input, options);
+ },
+ setLevelLayout(input: ListsSetLevelLayoutInput, options?: MutationOptions): ListsMutateItemResult {
+ return executeListsSetLevelLayout(adapters.lists, input, options);
+ },
},
sections: {
list(query?: SectionsListQuery): SectionsListResult {
diff --git a/packages/document-api/src/invoke/invoke.ts b/packages/document-api/src/invoke/invoke.ts
index f85d1142ba..87a3594f90 100644
--- a/packages/document-api/src/invoke/invoke.ts
+++ b/packages/document-api/src/invoke/invoke.ts
@@ -147,6 +147,15 @@ export function buildDispatchTable(api: DocumentApi): TypedDispatchTable {
'lists.setLevelMarkerFont': (input, options) => api.lists.setLevelMarkerFont(input, options),
'lists.clearLevelOverrides': (input, options) => api.lists.clearLevelOverrides(input, options),
+ // --- lists.* (SD-2025 user-facing) ---
+ 'lists.getStyle': (input) => api.lists.getStyle(input),
+ 'lists.applyStyle': (input, options) => api.lists.applyStyle(input, options),
+ 'lists.restartAt': (input, options) => api.lists.restartAt(input, options),
+ 'lists.setLevelNumberStyle': (input, options) => api.lists.setLevelNumberStyle(input, options),
+ 'lists.setLevelText': (input, options) => api.lists.setLevelText(input, options),
+ 'lists.setLevelStart': (input, options) => api.lists.setLevelStart(input, options),
+ 'lists.setLevelLayout': (input, options) => api.lists.setLevelLayout(input, options),
+
// --- sections.* ---
'sections.list': (input) => api.sections.list(input),
'sections.get': (input) => api.sections.get(input),
diff --git a/packages/document-api/src/lists/lists.ts b/packages/document-api/src/lists/lists.ts
index 58e41611fb..2de3fb2e33 100644
--- a/packages/document-api/src/lists/lists.ts
+++ b/packages/document-api/src/lists/lists.ts
@@ -42,6 +42,14 @@ import type {
ListsSetLevelMarkerFontInput,
ListsClearLevelOverridesInput,
ListsSetTypeInput,
+ ListsGetStyleInput,
+ ListsGetStyleResult,
+ ListsApplyStyleInput,
+ ListsRestartAtInput,
+ ListsSetLevelNumberStyleInput,
+ ListsSetLevelTextInput,
+ ListsSetLevelStartInput,
+ ListsSetLevelLayoutInput,
} from './lists.types.js';
export type {
@@ -85,6 +93,14 @@ export type {
ListsSetLevelMarkerFontInput,
ListsClearLevelOverridesInput,
ListsSetTypeInput,
+ ListsGetStyleInput,
+ ListsGetStyleResult,
+ ListsApplyStyleInput,
+ ListsRestartAtInput,
+ ListsSetLevelNumberStyleInput,
+ ListsSetLevelTextInput,
+ ListsSetLevelStartInput,
+ ListsSetLevelLayoutInput,
} from './lists.types.js';
/**
@@ -142,6 +158,15 @@ export interface ListsAdapter {
// SD-2052 compound operation
setType(input: ListsSetTypeInput, options?: MutationOptions): ListsMutateItemResult;
+
+ // SD-2025 user-facing operations
+ getStyle(input: ListsGetStyleInput): ListsGetStyleResult;
+ applyStyle(input: ListsApplyStyleInput, options?: MutationOptions): ListsMutateItemResult;
+ restartAt(input: ListsRestartAtInput, options?: MutationOptions): ListsMutateItemResult;
+ setLevelNumberStyle(input: ListsSetLevelNumberStyleInput, options?: MutationOptions): ListsMutateItemResult;
+ setLevelText(input: ListsSetLevelTextInput, options?: MutationOptions): ListsMutateItemResult;
+ setLevelStart(input: ListsSetLevelStartInput, options?: MutationOptions): ListsMutateItemResult;
+ setLevelLayout(input: ListsSetLevelLayoutInput, options?: MutationOptions): ListsMutateItemResult;
}
export type ListsApi = ListsAdapter;
@@ -405,3 +430,66 @@ export function executeListsSetType(
validateListTarget(input, 'lists.setType');
return adapter.setType(input, normalizeMutationOptions(options));
}
+
+// ---------------------------------------------------------------------------
+// Execute wrappers — SD-2025 user-facing operations
+// ---------------------------------------------------------------------------
+
+export function executeListsGetStyle(adapter: ListsAdapter, input: ListsGetStyleInput): ListsGetStyleResult {
+ validateListTarget(input, 'lists.getStyle');
+ return adapter.getStyle(input);
+}
+
+export function executeListsApplyStyle(
+ adapter: ListsAdapter,
+ input: ListsApplyStyleInput,
+ options?: MutationOptions,
+): ListsMutateItemResult {
+ validateListTarget(input, 'lists.applyStyle');
+ return adapter.applyStyle(input, normalizeMutationOptions(options));
+}
+
+export function executeListsRestartAt(
+ adapter: ListsAdapter,
+ input: ListsRestartAtInput,
+ options?: MutationOptions,
+): ListsMutateItemResult {
+ validateListTarget(input, 'lists.restartAt');
+ return adapter.restartAt(input, normalizeMutationOptions(options));
+}
+
+export function executeListsSetLevelNumberStyle(
+ adapter: ListsAdapter,
+ input: ListsSetLevelNumberStyleInput,
+ options?: MutationOptions,
+): ListsMutateItemResult {
+ validateListTarget(input, 'lists.setLevelNumberStyle');
+ return adapter.setLevelNumberStyle(input, normalizeMutationOptions(options));
+}
+
+export function executeListsSetLevelText(
+ adapter: ListsAdapter,
+ input: ListsSetLevelTextInput,
+ options?: MutationOptions,
+): ListsMutateItemResult {
+ validateListTarget(input, 'lists.setLevelText');
+ return adapter.setLevelText(input, normalizeMutationOptions(options));
+}
+
+export function executeListsSetLevelStart(
+ adapter: ListsAdapter,
+ input: ListsSetLevelStartInput,
+ options?: MutationOptions,
+): ListsMutateItemResult {
+ validateListTarget(input, 'lists.setLevelStart');
+ return adapter.setLevelStart(input, normalizeMutationOptions(options));
+}
+
+export function executeListsSetLevelLayout(
+ adapter: ListsAdapter,
+ input: ListsSetLevelLayoutInput,
+ options?: MutationOptions,
+): ListsMutateItemResult {
+ validateListTarget(input, 'lists.setLevelLayout');
+ return adapter.setLevelLayout(input, normalizeMutationOptions(options));
+}
diff --git a/packages/document-api/src/lists/lists.types.ts b/packages/document-api/src/lists/lists.types.ts
index 60d4a8057f..cf9acaf0ed 100644
--- a/packages/document-api/src/lists/lists.types.ts
+++ b/packages/document-api/src/lists/lists.types.ts
@@ -154,9 +154,49 @@ export interface ListTargetInput {
// Input types — new SD-1272 operations
// ---------------------------------------------------------------------------
+/**
+ * Create a new list from existing paragraphs.
+ *
+ * When `sequence.mode` is `'continuePrevious'`, `preset` and `style` are
+ * not allowed — the new items inherit formatting from the previous sequence.
+ */
export type ListsCreateInput =
- | { mode: 'empty'; at: BlockAddress; kind: ListKind; level?: number }
- | { mode: 'fromParagraphs'; target: BlockAddress | BlockRange; kind: ListKind; level?: number };
+ | {
+ mode: 'empty';
+ at: BlockAddress;
+ kind?: ListKind;
+ level?: number;
+ preset?: ListPresetId;
+ style?: ListStyle;
+ sequence?: { mode: 'new'; startAt?: number };
+ }
+ | {
+ mode: 'empty';
+ at: BlockAddress;
+ kind?: ListKind;
+ level?: number;
+ preset?: never;
+ style?: never;
+ sequence: { mode: 'continuePrevious' };
+ }
+ | {
+ mode: 'fromParagraphs';
+ target: BlockAddress | BlockRange;
+ kind?: ListKind;
+ level?: number;
+ preset?: ListPresetId;
+ style?: ListStyle;
+ sequence?: { mode: 'new'; startAt?: number };
+ }
+ | {
+ mode: 'fromParagraphs';
+ target: BlockAddress | BlockRange;
+ kind?: ListKind;
+ level?: number;
+ preset?: never;
+ style?: never;
+ sequence: { mode: 'continuePrevious' };
+ };
export interface ListsAttachInput {
target: BlockAddress | BlockRange;
@@ -232,6 +272,7 @@ export interface ListLevelTemplate {
trailingCharacter?: TrailingCharacter;
markerFont?: string;
pictureBulletId?: number;
+ tabStopAt?: number | null;
}
/** A full list template: an array of level snapshots. */
@@ -240,6 +281,33 @@ export interface ListTemplate {
levels: ListLevelTemplate[];
}
+// ---------------------------------------------------------------------------
+// SD-2025 user-facing style aliases and new types
+// ---------------------------------------------------------------------------
+
+/** Reusable list style object — alias of ListTemplate for user-facing naming. */
+export type ListStyle = ListTemplate;
+
+/** Reusable level style — alias of ListLevelTemplate for user-facing naming. */
+export type ListLevelStyle = ListLevelTemplate;
+
+/**
+ * Dialog-shaped layout input for `lists.setLevelLayout`.
+ * All numeric values are in twips.
+ *
+ * Partial-update semantics:
+ * - undefined (omitted): leave current value unchanged
+ * - null (explicit): remove/clear the value (only valid for tabStopAt)
+ * - present value: set it
+ */
+export interface ListLevelLayout {
+ alignment?: LevelAlignment;
+ alignedAt?: number;
+ textIndentAt?: number;
+ followCharacter?: TrailingCharacter;
+ tabStopAt?: number | null;
+}
+
// ---------------------------------------------------------------------------
// Input types — SD-1973 formatting operations
// ---------------------------------------------------------------------------
@@ -321,6 +389,61 @@ export interface ListsClearLevelOverridesInput {
level: number;
}
+// ---------------------------------------------------------------------------
+// Input types — SD-2025 user-facing operations
+// ---------------------------------------------------------------------------
+
+export interface ListsGetStyleInput {
+ target: ListItemAddress;
+ levels?: number[];
+}
+
+export interface ListsApplyStyleInput {
+ target: ListItemAddress;
+ style: ListStyle;
+ levels?: number[];
+}
+
+export interface ListsRestartAtInput {
+ target: ListItemAddress;
+ startAt: number;
+}
+
+export interface ListsSetLevelNumberStyleInput {
+ target: ListItemAddress;
+ level: number;
+ numberStyle: string;
+}
+
+export interface ListsSetLevelTextInput {
+ target: ListItemAddress;
+ level: number;
+ text: string;
+}
+
+export interface ListsSetLevelStartInput {
+ target: ListItemAddress;
+ level: number;
+ startAt: number;
+}
+
+export interface ListsSetLevelLayoutInput {
+ target: ListItemAddress;
+ level: number;
+ layout: ListLevelLayout;
+}
+
+// ---------------------------------------------------------------------------
+// Result types — SD-2025
+// ---------------------------------------------------------------------------
+
+export interface ListsGetStyleSuccessResult {
+ success: true;
+ style: ListStyle;
+}
+
+export type ListsGetStyleResult = ListsGetStyleSuccessResult | ListsFailureResult;
+
// ---------------------------------------------------------------------------
// Result types — SD-1973
// ---------------------------------------------------------------------------
diff --git a/packages/super-editor/src/core/helpers/list-level-formatting-helpers.js b/packages/super-editor/src/core/helpers/list-level-formatting-helpers.js
index 4997906167..ff21ecb2e1 100644
--- a/packages/super-editor/src/core/helpers/list-level-formatting-helpers.js
+++ b/packages/super-editor/src/core/helpers/list-level-formatting-helpers.js
@@ -126,7 +126,7 @@ function hasLevel(editor, abstractNumId, ilvl) {
* Read all formatting properties from a raw `w:lvl` element.
* @param {Object} lvlEl
* @param {number} ilvl
- * @returns {{ level: number, numFmt?: string, lvlText?: string, start?: number, alignment?: string, indents?: { left?: number, hanging?: number, firstLine?: number }, trailingCharacter?: string, markerFont?: string, pictureBulletId?: number }}
+ * @returns {{ level: number, numFmt?: string, lvlText?: string, start?: number, alignment?: string, indents?: { left?: number, hanging?: number, firstLine?: number }, trailingCharacter?: string, markerFont?: string, pictureBulletId?: number, tabStopAt?: number }}
*/
function readLevelProperties(lvlEl, ilvl) {
/** @type {any} */
@@ -160,6 +160,10 @@ function readLevelProperties(lvlEl, ilvl) {
if (Object.keys(indents).length > 0) props.indents = indents;
}
+ // Read tab stop from w:pPr/w:tabs/w:tab within w:lvl
+ const tabStopVal = readLevelTabStop(pPr);
+ if (tabStopVal != null) props.tabStopAt = tabStopVal;
+
const rPr = lvlEl.elements?.find((el) => el.name === 'w:rPr');
const rFonts = rPr?.elements?.find((el) => el.name === 'w:rFonts');
if (rFonts?.attributes?.['w:ascii']) {
@@ -169,6 +173,186 @@ function readLevelProperties(lvlEl, ilvl) {
return props;
}
+// ──────────────────────────────────────────────────────────────────────────────
+// Tab Stop Read/Write Helpers
+// ──────────────────────────────────────────────────────────────────────────────
+
+/**
+ * Read the first tab stop position from a `w:pPr` element.
+ * List-level tab stops are stored in `w:pPr/w:tabs/w:tab` within the `w:lvl`.
+ * @param {Object | undefined} pPr
+ * @returns {number | undefined}
+ */
+function readLevelTabStop(pPr) {
+ if (!pPr?.elements) return undefined;
+ const tabs = pPr.elements.find((el) => el.name === 'w:tabs');
+ if (!tabs?.elements) return undefined;
+ const tab = tabs.elements.find((el) => el.name === 'w:tab');
+ if (!tab?.attributes?.['w:pos']) return undefined;
+ return Number(tab.attributes['w:pos']);
+}
+
+/**
+ * Set or remove the list-level tab stop.
+ * @param {Object} lvlEl - The `w:lvl` element.
+ * @param {number | null} value - Position in twips, or null to remove.
+ * @returns {boolean} True if anything changed.
+ */
+function mutateLevelTabStop(lvlEl, value) {
+ const pPr = findOrCreateChild(lvlEl, 'w:pPr');
+
+ if (value === null) {
+ // Remove the tab stop
+ const tabsIdx = pPr.elements.findIndex((el) => el.name === 'w:tabs');
+ if (tabsIdx === -1) return false;
+ pPr.elements.splice(tabsIdx, 1);
+ return true;
+ }
+
+ const tabs = findOrCreateChild(pPr, 'w:tabs');
+ const existing = tabs.elements.find((el) => el.name === 'w:tab');
+ const posStr = String(value);
+
+ if (existing) {
+ if (existing.attributes?.['w:pos'] === posStr && existing.attributes?.['w:val'] === 'num') return false;
+ existing.attributes = { ...existing.attributes, 'w:val': 'num', 'w:pos': posStr };
+ return true;
+ }
+
+ tabs.elements.push({
+ type: 'element',
+ name: 'w:tab',
+ attributes: { 'w:val': 'num', 'w:pos': posStr },
+ });
+ return true;
+}
+
+/**
+ * Composite setter: resolve abstract + level, then mutate tab stop.
+ * @param {import('../Editor').Editor} editor
+ * @param {number} abstractNumId
+ * @param {number} ilvl
+ * @param {number | null} value
+ * @returns {boolean}
+ */
+function setLevelTabStop(editor, abstractNumId, ilvl, value) {
+ const resolved = resolveAbstractLevel(editor, abstractNumId, ilvl);
+ if (!resolved) return false;
+ return mutateLevelTabStop(resolved.lvlEl, value);
+}
+
+// ──────────────────────────────────────────────────────────────────────────────
+// Marker-Mode Normalization Helpers
+// ──────────────────────────────────────────────────────────────────────────────
+
+/**
+ * Clear the `w:lvlPicBulletId` element from a level if it exists.
+ * Used for marker-mode normalization when switching away from picture bullets.
+ * @param {Object} lvlEl
+ * @returns {boolean} True if an element was removed.
+ */
+function clearPictureBulletId(lvlEl) {
+ if (!lvlEl.elements) return false;
+ const idx = lvlEl.elements.findIndex((el) => el.name === 'w:lvlPicBulletId');
+ if (idx === -1) return false;
+ lvlEl.elements.splice(idx, 1);
+ return true;
+}
+
+/**
+ * Set numFmt only (for setLevelNumberStyle). Rejects 'bullet'.
+ * Clears lvlPicBulletId if present (marker-mode normalization).
+ * @param {Object} lvlEl
+ * @param {string} numFmt
+ * @returns {boolean}
+ */
+function mutateLevelNumberStyle(lvlEl, numFmt) {
+ let changed = setChildAttr(lvlEl, 'w:numFmt', numFmt);
+ changed = clearPictureBulletId(lvlEl) || changed;
+ return changed;
+}
+
+/**
+ * Composite setter for setLevelNumberStyle.
+ * @param {import('../Editor').Editor} editor
+ * @param {number} abstractNumId
+ * @param {number} ilvl
+ * @param {string} numFmt
+ * @returns {boolean}
+ */
+function setLevelNumberStyle(editor, abstractNumId, ilvl, numFmt) {
+ const resolved = resolveAbstractLevel(editor, abstractNumId, ilvl);
+ if (!resolved) return false;
+ return mutateLevelNumberStyle(resolved.lvlEl, numFmt);
+}
+
+/**
+ * Set lvlText only (for setLevelText).
+ * @param {import('../Editor').Editor} editor
+ * @param {number} abstractNumId
+ * @param {number} ilvl
+ * @param {string} text
+ * @returns {boolean}
+ */
+function setLevelText(editor, abstractNumId, ilvl, text) {
+ const resolved = resolveAbstractLevel(editor, abstractNumId, ilvl);
+ if (!resolved) return false;
+ return setChildAttr(resolved.lvlEl, 'w:lvlText', text);
+}
+
+/**
+ * Set start value only (for setLevelStart).
+ * @param {import('../Editor').Editor} editor
+ * @param {number} abstractNumId
+ * @param {number} ilvl
+ * @param {number} start
+ * @returns {boolean}
+ */
+function setLevelStart(editor, abstractNumId, ilvl, start) {
+ const resolved = resolveAbstractLevel(editor, abstractNumId, ilvl);
+ if (!resolved) return false;
+ return setChildAttr(resolved.lvlEl, 'w:start', String(start));
+}
+
+/**
+ * Apply a partial level-style object to a raw `w:lvl` element.
+ * This preserves unspecified properties already present on the level.
+ *
+ * @param {Object} lvlEl
+ * @param {Object} entry
+ * @returns {boolean}
+ */
+function applyLevelPropertiesToElement(lvlEl, entry) {
+ let changed = false;
+
+ if (entry.numFmt != null || entry.lvlText != null) {
+ const fmtParams = {};
+ if (entry.numFmt != null) fmtParams.numFmt = entry.numFmt;
+ if (entry.lvlText != null) fmtParams.lvlText = entry.lvlText;
+ if (entry.start != null) fmtParams.start = entry.start;
+
+ if (fmtParams.numFmt != null && fmtParams.lvlText != null) {
+ changed = mutateLevelNumberingFormat(lvlEl, fmtParams) || changed;
+ } else {
+ if (fmtParams.numFmt != null) changed = setChildAttr(lvlEl, 'w:numFmt', fmtParams.numFmt) || changed;
+ if (fmtParams.lvlText != null) changed = setChildAttr(lvlEl, 'w:lvlText', fmtParams.lvlText) || changed;
+ if (fmtParams.start != null) changed = setChildAttr(lvlEl, 'w:start', String(fmtParams.start)) || changed;
+ }
+ } else if (entry.start != null) {
+ changed = setChildAttr(lvlEl, 'w:start', String(entry.start)) || changed;
+ }
+
+ if (entry.alignment != null) changed = mutateLevelAlignment(lvlEl, entry.alignment) || changed;
+ if (entry.indents != null) changed = mutateLevelIndents(lvlEl, entry.indents) || changed;
+ if (entry.trailingCharacter != null)
+ changed = mutateLevelTrailingCharacter(lvlEl, entry.trailingCharacter) || changed;
+ if (entry.markerFont != null) changed = mutateLevelMarkerFont(lvlEl, entry.markerFont) || changed;
+ if (entry.pictureBulletId != null) changed = mutateLevelPictureBulletId(lvlEl, entry.pictureBulletId) || changed;
+ if (entry.tabStopAt !== undefined) changed = mutateLevelTabStop(lvlEl, entry.tabStopAt) || changed;
+
+ return changed;
+}
+
// ──────────────────────────────────────────────────────────────────────────────
// Raw XML Mutators (no sync, no emit)
// ──────────────────────────────────────────────────────────────────────────────
@@ -438,6 +622,60 @@ function clearLevelOverride(editor, numId, ilvl) {
return true;
}
+/**
+ * Fold formatting from `w:lvlOverride/w:lvl` into the target abstract level,
+ * then remove only the `w:lvl` child while preserving any `w:startOverride`.
+ *
+ * This lets sequence-local style edits operate on the effective visible style
+ * without dropping restart state stored on the numbering instance.
+ *
+ * @param {import('../Editor').Editor} editor
+ * @param {number} abstractNumId
+ * @param {number} numId
+ * @param {number} ilvl
+ * @returns {boolean}
+ */
+function materializeLevelFormattingOverride(editor, abstractNumId, numId, ilvl) {
+ const resolved = resolveAbstractLevel(editor, abstractNumId, ilvl);
+ const numDef = editor.converter.numbering?.definitions?.[numId];
+ if (!resolved || !numDef?.elements) return false;
+
+ const ilvlStr = String(ilvl);
+ const overrideIndex = numDef.elements.findIndex(
+ (el) => el.name === 'w:lvlOverride' && el.attributes?.['w:ilvl'] === ilvlStr,
+ );
+ if (overrideIndex === -1) return false;
+
+ const overrideEl = numDef.elements[overrideIndex];
+ if (!overrideEl?.elements) return false;
+
+ const lvlIndex = overrideEl.elements.findIndex((el) => el.name === 'w:lvl');
+ if (lvlIndex === -1) return false;
+
+ const lvlEl = overrideEl.elements[lvlIndex];
+ const props = readLevelProperties(lvlEl, ilvl);
+ const abstractChanged = applyLevelPropertiesToElement(resolved.lvlEl, props);
+ const lvlRestartElements =
+ lvlEl.elements?.filter((el) => el.name === 'w:lvlRestart').map((el) => deepCloneElement(el)) ?? [];
+
+ overrideEl.elements.splice(lvlIndex, 1);
+ if (lvlRestartElements.length > 0) {
+ overrideEl.elements.push({
+ type: 'element',
+ name: 'w:lvl',
+ attributes: { 'w:ilvl': ilvlStr },
+ elements: lvlRestartElements,
+ });
+ }
+
+ let overrideChanged = true;
+ if (overrideEl.elements.length === 0) {
+ numDef.elements.splice(overrideIndex, 1);
+ }
+
+ return abstractChanged || overrideChanged;
+}
+
// ──────────────────────────────────────────────────────────────────────────────
// Template Capture
// ──────────────────────────────────────────────────────────────────────────────
@@ -499,31 +737,7 @@ function applyTemplateToAbstract(editor, abstractNumId, template, levels) {
for (const ilvl of targetLevels) {
const entry = templateByLevel.get(ilvl);
const lvlEl = findLevelElement(abstract, ilvl);
-
- if (entry.numFmt != null || entry.lvlText != null) {
- const fmtParams = {};
- if (entry.numFmt != null) fmtParams.numFmt = entry.numFmt;
- if (entry.lvlText != null) fmtParams.lvlText = entry.lvlText;
- if (entry.start != null) fmtParams.start = entry.start;
-
- if (fmtParams.numFmt != null && fmtParams.lvlText != null) {
- anyChanged = mutateLevelNumberingFormat(lvlEl, fmtParams) || anyChanged;
- } else {
- if (fmtParams.numFmt != null) anyChanged = setChildAttr(lvlEl, 'w:numFmt', fmtParams.numFmt) || anyChanged;
- if (fmtParams.lvlText != null) anyChanged = setChildAttr(lvlEl, 'w:lvlText', fmtParams.lvlText) || anyChanged;
- if (fmtParams.start != null) anyChanged = setChildAttr(lvlEl, 'w:start', String(fmtParams.start)) || anyChanged;
- }
- } else if (entry.start != null) {
- anyChanged = setChildAttr(lvlEl, 'w:start', String(entry.start)) || anyChanged;
- }
-
- if (entry.alignment != null) anyChanged = mutateLevelAlignment(lvlEl, entry.alignment) || anyChanged;
- if (entry.indents != null) anyChanged = mutateLevelIndents(lvlEl, entry.indents) || anyChanged;
- if (entry.trailingCharacter != null)
- anyChanged = mutateLevelTrailingCharacter(lvlEl, entry.trailingCharacter) || anyChanged;
- if (entry.markerFont != null) anyChanged = mutateLevelMarkerFont(lvlEl, entry.markerFont) || anyChanged;
- if (entry.pictureBulletId != null)
- anyChanged = mutateLevelPictureBulletId(lvlEl, entry.pictureBulletId) || anyChanged;
+ anyChanged = applyLevelPropertiesToElement(lvlEl, entry) || anyChanged;
}
return { changed: anyChanged };
@@ -594,6 +808,412 @@ function getPresetTemplate(presetId) {
return PRESET_TEMPLATES[presetId];
}
+// ──────────────────────────────────────────────────────────────────────────────
+// Layout Composite Mutation
+// ──────────────────────────────────────────────────────────────────────────────
+
+/**
+ * Apply dialog-shaped layout properties to a level element.
+ *
+ * Indent mapping:
+ * textIndentAt → w:ind/@w:left
+ * alignedAt → derives w:ind/@w:hanging = textIndentAt - alignedAt
+ *
+ * Partial-update: omitted fields are untouched.
+ * Only tabStopAt accepts explicit null (remove).
+ *
+ * @param {Object} lvlEl
+ * @param {{ alignment?: string, alignedAt?: number, textIndentAt?: number, followCharacter?: string, tabStopAt?: number | null }} layout
+ * @returns {{ changed: boolean, error?: string }}
+ */
+function mutateLevelLayout(lvlEl, layout) {
+ let changed = false;
+
+ // Alignment
+ if (layout.alignment != null) {
+ changed = mutateLevelAlignment(lvlEl, layout.alignment) || changed;
+ }
+
+ // Trailing character (followCharacter)
+ if (layout.followCharacter != null) {
+ changed = mutateLevelTrailingCharacter(lvlEl, layout.followCharacter) || changed;
+ }
+
+ // Tab stop
+ if (layout.tabStopAt !== undefined) {
+ changed = mutateLevelTabStop(lvlEl, layout.tabStopAt) || changed;
+ }
+
+ // Indents (dialog → OOXML conversion)
+ const hasAlignedAt = layout.alignedAt != null;
+ const hasTextIndentAt = layout.textIndentAt != null;
+
+ if (hasAlignedAt || hasTextIndentAt) {
+ const pPr = lvlEl.elements?.find((el) => el.name === 'w:pPr');
+ const ind = pPr?.elements?.find((el) => el.name === 'w:ind');
+ const existingLeft = ind?.attributes?.['w:left'] != null ? Number(ind.attributes['w:left']) : undefined;
+ const existingHanging = ind?.attributes?.['w:hanging'] != null ? Number(ind.attributes['w:hanging']) : undefined;
+ const existingFirstLine =
+ ind?.attributes?.['w:firstLine'] != null ? Number(ind.attributes['w:firstLine']) : undefined;
+
+ // Compute existing alignedAt from current indent state
+ let existingAlignedAt;
+ if (existingLeft != null) {
+ if (existingHanging != null) {
+ existingAlignedAt = existingLeft - existingHanging;
+ } else if (existingFirstLine != null) {
+ existingAlignedAt = existingLeft + existingFirstLine;
+ } else {
+ existingAlignedAt = existingLeft;
+ }
+ }
+
+ let newLeft, newHanging;
+
+ if (hasAlignedAt && hasTextIndentAt) {
+ newLeft = layout.textIndentAt;
+ newHanging = layout.textIndentAt - layout.alignedAt;
+ } else if (hasTextIndentAt) {
+ newLeft = layout.textIndentAt;
+ newHanging = existingAlignedAt != null ? layout.textIndentAt - existingAlignedAt : 0;
+ } else if (hasAlignedAt) {
+ if (existingLeft == null) {
+ return { changed, error: 'INVALID_INPUT' };
+ }
+ newLeft = existingLeft;
+ newHanging = existingLeft - layout.alignedAt;
+ }
+
+ if (newLeft != null) {
+ // Always normalize to hanging (remove firstLine if present)
+ const indents = { left: newLeft, hanging: newHanging ?? 0 };
+ changed = mutateLevelIndents(lvlEl, indents) || changed;
+ }
+ }
+
+ return { changed };
+}
+
+/**
+ * Composite setter for setLevelLayout.
+ * @param {import('../Editor').Editor} editor
+ * @param {number} abstractNumId
+ * @param {number} ilvl
+ * @param {{ alignment?: string, alignedAt?: number, textIndentAt?: number, followCharacter?: string, tabStopAt?: number | null }} layout
+ * @returns {{ changed: boolean, error?: string }}
+ */
+function setLevelLayout(editor, abstractNumId, ilvl, layout) {
+ const resolved = resolveAbstractLevel(editor, abstractNumId, ilvl);
+ if (!resolved) return { changed: false };
+ return mutateLevelLayout(resolved.lvlEl, layout);
+}
+
+// ──────────────────────────────────────────────────────────────────────────────
+// Effective Style Capture (abstract + lvlOverride merge)
+// ──────────────────────────────────────────────────────────────────────────────
+
+/**
+ * Capture the effective style of a list: abstract definition properties merged
+ * with any instance-level lvlOverride formatting. Excludes startOverride
+ * (sequence state, not style).
+ *
+ * @param {import('../Editor').Editor} editor
+ * @param {number} abstractNumId
+ * @param {number} numId
+ * @param {number[] | undefined} levels
+ * @returns {{ version: 1, levels: Array