diff --git a/apps/docs/document-api/common-workflows.mdx b/apps/docs/document-api/common-workflows.mdx
index aae4c77972..9784381c73 100644
--- a/apps/docs/document-api/common-workflows.mdx
+++ b/apps/docs/document-api/common-workflows.mdx
@@ -139,6 +139,46 @@ Use `query.match` (not `find`) for this workflow. `query.match` returns `BlockNo
For direct single-operation calls, prefer `item.target`. For plans or multi-step edits, prefer `item.handle.ref` so every step reuses the same resolved match.
+## Chain table mutations with returned refs
+
+For non-destructive table-targeted mutations, reuse `result.table.nodeId` from the previous success result. You do not need an intermediate `find()` between calls.
+
+```ts
+const created = editor.doc.create.table({
+ rows: 2,
+ columns: 2,
+});
+
+if (!created.success || !created.table) return;
+
+const bordered = editor.doc.tables.setBorder({
+ nodeId: created.table.nodeId,
+ edge: 'top',
+ lineStyle: 'single',
+ lineWeightPt: 1,
+ color: '000000',
+});
+
+if (!bordered.success || !bordered.table) return;
+
+const inserted = editor.doc.tables.insertColumn({
+ tableNodeId: bordered.table.nodeId,
+ columnIndex: 0,
+ position: 'right',
+});
+
+if (!inserted.success || !inserted.table) return;
+
+editor.doc.tables.setCellSpacing({
+ nodeId: inserted.table.nodeId,
+ spacingPt: 2,
+});
+```
+
+
+This handoff contract applies to table-targeted calls. Cell-targeted `tables.setBorder`, `tables.clearBorder`, `tables.setShading`, and `tables.clearShading` still return the targeted `tableCell` address today.
+
+
## Build a selection explicitly with ranges.resolve
Use `ranges.resolve` when you already know the anchor points and want a transparent `SelectionTarget` plus a reusable mutation-ready ref:
@@ -240,7 +280,7 @@ editor2.destroy();
```
-`nodeId` stability depends on the ID source. For DOCX-imported content, `nodeId` comes from `paraId` when available and is best-effort stable across loads. For nodes created at runtime, it falls back to `sdBlockId`, which is volatile.
+`nodeId` stability depends on the ID source. For DOCX-imported content, `nodeId` comes from `paraId` when available and is best-effort stable across loads. Runtime-created content is still not guaranteed stable across loads; many nodes use session-scoped editor identity, while some structures such as tables or table cells may expose deterministic fallback IDs instead of raw `sdBlockId`.
diff --git a/apps/docs/document-api/overview.mdx b/apps/docs/document-api/overview.mdx
index f4e68b91b2..5f2d388496 100644
--- a/apps/docs/document-api/overview.mdx
+++ b/apps/docs/document-api/overview.mdx
@@ -69,15 +69,15 @@ Use `item.handle.ref` when you want multiple operations or a mutation plan to re
For DOCX documents, `nodeId` is derived from the file's native `w14:paraId` attribute. In practice, this is usually stable when you reopen the same unchanged DOCX across separate editor sessions, machines, or headless CLI pipelines.
-For nodes created at runtime (not imported from DOCX), `nodeId` falls back to `sdBlockId`, a UUID generated when the editor opens. This fallback is volatile and changes on every load.
+For nodes created at runtime (not imported from DOCX), `nodeId` is still best-effort only. Many runtime nodes use session-scoped editor identity, and some structures such as tables or table cells may expose deterministic fallback IDs instead of a raw UUID-like `sdBlockId`. Either way, cross-session stability is not guaranteed.
| ID source | Stable across loads? | When used |
|-----------|---------------------|-----------|
| `paraId` (from DOCX) | Best effort (usually stable for unchanged DOCX blocks) | Paragraphs and table rows imported from DOCX |
-| `sdBlockId` (runtime) | No (session-scoped) | Nodes created programmatically before first export |
+| Runtime-derived ID | No guarantee (often session-scoped; some table addresses use deterministic fallbacks) | Nodes created programmatically before first export |
-If you need to reference blocks across separate editor sessions, use `editor.doc.query.match()` (or persist `nodeId` and reconstruct a `NodeAddress`) — don't read `node.attrs.sdBlockId` directly. The Document API resolves `paraId` first for DOCX-imported content.
+If you need to reference blocks across separate editor sessions, use `editor.doc.query.match()` (or persist `nodeId` and reconstruct a `NodeAddress`) — don't read `node.attrs.sdBlockId` directly. The Document API resolves `paraId` first for DOCX-imported content and may derive safer public IDs for some runtime structures such as tables.
diff --git a/apps/docs/document-api/reference/create/table.mdx b/apps/docs/document-api/reference/create/table.mdx
index 91f3833b55..5335e4d36c 100644
--- a/apps/docs/document-api/reference/create/table.mdx
+++ b/apps/docs/document-api/reference/create/table.mdx
@@ -67,6 +67,10 @@ Returns a CreateTableResult with the new table block ID and address.
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+On success, `result.table` is the created table address. Reuse `result.table.nodeId` for follow-up table operations in the same session.
+
+
### Example response
```json
@@ -75,7 +79,7 @@ Returns a CreateTableResult with the new table block ID and address.
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/index.mdx b/apps/docs/document-api/reference/index.mdx
index 9fe319813b..3b1c1a7a2b 100644
--- a/apps/docs/document-api/reference/index.mdx
+++ b/apps/docs/document-api/reference/index.mdx
@@ -68,7 +68,7 @@ The tables below are grouped by namespace.
| getMarkdown | editor.doc.getMarkdown(...) | Extract the document content as a Markdown string. |
| getHtml | editor.doc.getHtml(...) | Extract the document content as an HTML string. |
| markdownToFragment | editor.doc.markdownToFragment(...) | Convert a Markdown string into an SDM/1 structural fragment. |
-| info | editor.doc.info(...) | Return document summary info including word, character, paragraph, heading, table, image, comment, tracked-change, SDT-field, and list counts, plus outline and capabilities. |
+| info | editor.doc.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. |
| clearContent | editor.doc.clearContent(...) | Clear all document body content, leaving a single empty paragraph. |
| insert | editor.doc.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. |
| replace | editor.doc.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. |
diff --git a/apps/docs/document-api/reference/info.mdx b/apps/docs/document-api/reference/info.mdx
index 8b4c4d0459..28db0e02ca 100644
--- a/apps/docs/document-api/reference/info.mdx
+++ b/apps/docs/document-api/reference/info.mdx
@@ -1,7 +1,7 @@
---
title: info
sidebarTitle: info
-description: Return document summary info including word, character, paragraph, heading, table, image, comment, tracked-change, SDT-field, and list counts, plus outline and capabilities.
+description: Return document summary info including word, character, paragraph, heading, table, image, comment, tracked-change, SDT-field, list, and page counts, plus outline and capabilities.
---
{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */}
@@ -10,7 +10,7 @@ description: Return document summary info including word, character, paragraph,
## Summary
-Return document summary info including word, character, paragraph, heading, table, image, comment, tracked-change, SDT-field, and list counts, plus outline and capabilities.
+Return document summary info including word, character, paragraph, heading, table, image, comment, tracked-change, SDT-field, list, and page counts, plus outline and capabilities.
- Operation ID: `info`
- API member path: `editor.doc.info(...)`
@@ -22,7 +22,7 @@ Return document summary info including word, character, paragraph, heading, tabl
## Expected result
-Returns a DocumentInfo object with counts (words, characters, paragraphs, headings, tables, images, comments, trackedChanges, sdtFields, lists), document outline, capability flags, and revision.
+Returns a DocumentInfo object with counts (words, characters, paragraphs, headings, tables, images, comments, trackedChanges, sdtFields, lists, and optionally pages when pagination is active), document outline, capability flags, and revision.
## Input fields
@@ -49,6 +49,7 @@ _No fields._
| `counts.headings` | integer | yes | |
| `counts.images` | integer | yes | |
| `counts.lists` | integer | yes | |
+| `counts.pages` | integer | no | |
| `counts.paragraphs` | integer | yes | |
| `counts.sdtFields` | integer | yes | |
| `counts.tables` | integer | yes | |
@@ -73,6 +74,7 @@ _No fields._
"headings": 3,
"images": 2,
"lists": 1,
+ "pages": 1,
"paragraphs": 12,
"sdtFields": 1,
"tables": 1,
@@ -157,6 +159,9 @@ _No fields._
"lists": {
"type": "integer"
},
+ "pages": {
+ "type": "integer"
+ },
"paragraphs": {
"type": "integer"
},
diff --git a/apps/docs/document-api/reference/tables/apply-border-preset.mdx b/apps/docs/document-api/reference/tables/apply-border-preset.mdx
index fa7093598c..0328d6fb74 100644
--- a/apps/docs/document-api/reference/tables/apply-border-preset.mdx
+++ b/apps/docs/document-api/reference/tables/apply-border-preset.mdx
@@ -38,12 +38,11 @@ _No fields._
```json
{
- "nodeId": "node-def456",
"preset": "box",
"target": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
}
}
```
@@ -71,6 +70,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -79,7 +82,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/tables/clear-border.mdx b/apps/docs/document-api/reference/tables/clear-border.mdx
index a8d6afb6ba..d674bfd508 100644
--- a/apps/docs/document-api/reference/tables/clear-border.mdx
+++ b/apps/docs/document-api/reference/tables/clear-border.mdx
@@ -39,11 +39,10 @@ _No fields._
```json
{
"edge": "top",
- "nodeId": "node-def456",
"target": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
}
}
```
@@ -71,6 +70,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -79,7 +82,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/tables/clear-cell-spacing.mdx b/apps/docs/document-api/reference/tables/clear-cell-spacing.mdx
index 0179ad49c1..8da209650c 100644
--- a/apps/docs/document-api/reference/tables/clear-cell-spacing.mdx
+++ b/apps/docs/document-api/reference/tables/clear-cell-spacing.mdx
@@ -38,11 +38,10 @@ _No fields._
```json
{
- "nodeId": "node-def456",
"target": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
}
}
```
@@ -70,6 +69,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -78,7 +81,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/tables/clear-contents.mdx b/apps/docs/document-api/reference/tables/clear-contents.mdx
index 919cc3343c..a64edc7b14 100644
--- a/apps/docs/document-api/reference/tables/clear-contents.mdx
+++ b/apps/docs/document-api/reference/tables/clear-contents.mdx
@@ -38,11 +38,10 @@ _No fields._
```json
{
- "nodeId": "node-def456",
"target": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
}
}
```
@@ -70,6 +69,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -78,7 +81,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/tables/clear-shading.mdx b/apps/docs/document-api/reference/tables/clear-shading.mdx
index 90aac8e32f..2e62b9e1e8 100644
--- a/apps/docs/document-api/reference/tables/clear-shading.mdx
+++ b/apps/docs/document-api/reference/tables/clear-shading.mdx
@@ -38,11 +38,10 @@ _No fields._
```json
{
- "nodeId": "node-def456",
"target": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
}
}
```
@@ -70,6 +69,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -78,7 +81,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/tables/clear-style.mdx b/apps/docs/document-api/reference/tables/clear-style.mdx
index a1e923643d..8c4f822a84 100644
--- a/apps/docs/document-api/reference/tables/clear-style.mdx
+++ b/apps/docs/document-api/reference/tables/clear-style.mdx
@@ -38,11 +38,10 @@ _No fields._
```json
{
- "nodeId": "node-def456",
"target": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
}
}
```
@@ -70,6 +69,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -78,7 +81,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/tables/convert-from-text.mdx b/apps/docs/document-api/reference/tables/convert-from-text.mdx
index 9fd6756a1f..4ed8044cea 100644
--- a/apps/docs/document-api/reference/tables/convert-from-text.mdx
+++ b/apps/docs/document-api/reference/tables/convert-from-text.mdx
@@ -39,11 +39,10 @@ _No fields._
```json
{
"delimiter": "tab",
- "nodeId": "node-def456",
"target": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
}
}
```
@@ -71,6 +70,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -79,7 +82,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/tables/convert-to-text.mdx b/apps/docs/document-api/reference/tables/convert-to-text.mdx
index e658aa58ea..97a77b3057 100644
--- a/apps/docs/document-api/reference/tables/convert-to-text.mdx
+++ b/apps/docs/document-api/reference/tables/convert-to-text.mdx
@@ -39,11 +39,10 @@ _No fields._
```json
{
"delimiter": "tab",
- "nodeId": "node-def456",
"target": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
}
}
```
@@ -71,6 +70,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -79,7 +82,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/tables/delete-cell.mdx b/apps/docs/document-api/reference/tables/delete-cell.mdx
index 760f679525..65fd7a204c 100644
--- a/apps/docs/document-api/reference/tables/delete-cell.mdx
+++ b/apps/docs/document-api/reference/tables/delete-cell.mdx
@@ -39,11 +39,10 @@ _No fields._
```json
{
"mode": "shiftLeft",
- "nodeId": "node-def456",
"target": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
}
}
```
@@ -71,6 +70,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -79,7 +82,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/tables/delete-column.mdx b/apps/docs/document-api/reference/tables/delete-column.mdx
index 1cff7f06a8..39dde97859 100644
--- a/apps/docs/document-api/reference/tables/delete-column.mdx
+++ b/apps/docs/document-api/reference/tables/delete-column.mdx
@@ -39,11 +39,10 @@ _No fields._
```json
{
"columnIndex": 1,
- "tableNodeId": "example",
"tableTarget": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
}
}
```
@@ -71,6 +70,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -79,7 +82,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/tables/delete-row.mdx b/apps/docs/document-api/reference/tables/delete-row.mdx
index 795449da50..9351b2c16e 100644
--- a/apps/docs/document-api/reference/tables/delete-row.mdx
+++ b/apps/docs/document-api/reference/tables/delete-row.mdx
@@ -46,16 +46,10 @@ _No fields._
```json
{
- "nodeId": "node-def456",
"tableTarget": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
- },
- "target": {
- "kind": "block",
- "nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
}
}
```
@@ -83,6 +77,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -91,7 +89,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/tables/delete.mdx b/apps/docs/document-api/reference/tables/delete.mdx
index b8429d2e6b..99f9e1c464 100644
--- a/apps/docs/document-api/reference/tables/delete.mdx
+++ b/apps/docs/document-api/reference/tables/delete.mdx
@@ -38,11 +38,10 @@ _No fields._
```json
{
- "nodeId": "node-def456",
"target": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
}
}
```
@@ -70,6 +69,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -78,7 +81,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/tables/distribute-columns.mdx b/apps/docs/document-api/reference/tables/distribute-columns.mdx
index 4a77999425..232e07ab02 100644
--- a/apps/docs/document-api/reference/tables/distribute-columns.mdx
+++ b/apps/docs/document-api/reference/tables/distribute-columns.mdx
@@ -42,11 +42,10 @@ _No fields._
"end": 10,
"start": 0
},
- "nodeId": "node-def456",
"target": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
}
}
```
@@ -74,6 +73,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -82,7 +85,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/tables/distribute-rows.mdx b/apps/docs/document-api/reference/tables/distribute-rows.mdx
index b5bca6f90e..1967d55b63 100644
--- a/apps/docs/document-api/reference/tables/distribute-rows.mdx
+++ b/apps/docs/document-api/reference/tables/distribute-rows.mdx
@@ -38,11 +38,10 @@ _No fields._
```json
{
- "nodeId": "node-def456",
"target": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
}
}
```
@@ -70,6 +69,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -78,7 +81,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/tables/get-cells.mdx b/apps/docs/document-api/reference/tables/get-cells.mdx
index 92afdc13fc..f519679270 100644
--- a/apps/docs/document-api/reference/tables/get-cells.mdx
+++ b/apps/docs/document-api/reference/tables/get-cells.mdx
@@ -38,12 +38,11 @@ _No fields._
```json
{
- "nodeId": "node-def456",
"rowIndex": 1,
"target": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
}
}
```
diff --git a/apps/docs/document-api/reference/tables/get-properties.mdx b/apps/docs/document-api/reference/tables/get-properties.mdx
index ff99559300..6d275ae55d 100644
--- a/apps/docs/document-api/reference/tables/get-properties.mdx
+++ b/apps/docs/document-api/reference/tables/get-properties.mdx
@@ -38,11 +38,10 @@ _No fields._
```json
{
- "nodeId": "node-def456",
"target": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
}
}
```
diff --git a/apps/docs/document-api/reference/tables/get.mdx b/apps/docs/document-api/reference/tables/get.mdx
index ed8c3226ce..749f792792 100644
--- a/apps/docs/document-api/reference/tables/get.mdx
+++ b/apps/docs/document-api/reference/tables/get.mdx
@@ -38,11 +38,10 @@ _No fields._
```json
{
- "nodeId": "node-def456",
"target": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
}
}
```
diff --git a/apps/docs/document-api/reference/tables/index.mdx b/apps/docs/document-api/reference/tables/index.mdx
index 16aa3a02b7..3509affd8a 100644
--- a/apps/docs/document-api/reference/tables/index.mdx
+++ b/apps/docs/document-api/reference/tables/index.mdx
@@ -12,6 +12,10 @@ description: Tables operation reference from the canonical Document API contract
Table structure, layout, styling, and cell operations.
+
+For non-destructive table-targeted mutations, reuse `result.table.nodeId` from the previous success result instead of re-running `find()`. Cell-targeted border/shading calls may still return a `tableCell` address.
+
+
| Operation | Member path | Mutates | Idempotency | Tracked | Dry run |
| --- | --- | --- | --- | --- | --- |
| tables.convertFromText | `tables.convertFromText` | Yes | `non-idempotent` | No | Yes |
diff --git a/apps/docs/document-api/reference/tables/insert-cell.mdx b/apps/docs/document-api/reference/tables/insert-cell.mdx
index 8bf9ecedbb..668611cd0c 100644
--- a/apps/docs/document-api/reference/tables/insert-cell.mdx
+++ b/apps/docs/document-api/reference/tables/insert-cell.mdx
@@ -39,11 +39,10 @@ _No fields._
```json
{
"mode": "shiftRight",
- "nodeId": "node-def456",
"target": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
}
}
```
@@ -71,6 +70,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -79,7 +82,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/tables/insert-column.mdx b/apps/docs/document-api/reference/tables/insert-column.mdx
index 45db92077f..9b481debb9 100644
--- a/apps/docs/document-api/reference/tables/insert-column.mdx
+++ b/apps/docs/document-api/reference/tables/insert-column.mdx
@@ -41,11 +41,10 @@ _No fields._
"columnIndex": 1,
"count": 1,
"position": "left",
- "tableNodeId": "example",
"tableTarget": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
}
}
```
@@ -73,6 +72,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -81,7 +84,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/tables/insert-row.mdx b/apps/docs/document-api/reference/tables/insert-row.mdx
index 52f76a3525..4c291aee4b 100644
--- a/apps/docs/document-api/reference/tables/insert-row.mdx
+++ b/apps/docs/document-api/reference/tables/insert-row.mdx
@@ -46,17 +46,11 @@ _No fields._
```json
{
- "nodeId": "node-def456",
"position": "above",
"tableTarget": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
- },
- "target": {
- "kind": "block",
- "nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
}
}
```
@@ -84,6 +78,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -92,7 +90,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/tables/merge-cells.mdx b/apps/docs/document-api/reference/tables/merge-cells.mdx
index 93ef2217e3..b6369fd26c 100644
--- a/apps/docs/document-api/reference/tables/merge-cells.mdx
+++ b/apps/docs/document-api/reference/tables/merge-cells.mdx
@@ -46,11 +46,10 @@ _No fields._
"columnIndex": 1,
"rowIndex": 1
},
- "tableNodeId": "example",
"tableTarget": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
}
}
```
@@ -78,6 +77,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -86,7 +89,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/tables/move.mdx b/apps/docs/document-api/reference/tables/move.mdx
index 4722dee631..c8fa3d7748 100644
--- a/apps/docs/document-api/reference/tables/move.mdx
+++ b/apps/docs/document-api/reference/tables/move.mdx
@@ -41,11 +41,10 @@ _No fields._
"destination": {
"kind": "documentStart"
},
- "nodeId": "node-def456",
"target": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
}
}
```
@@ -73,6 +72,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -81,7 +84,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/tables/set-alt-text.mdx b/apps/docs/document-api/reference/tables/set-alt-text.mdx
index f9ba336ba1..0abc0ec0b0 100644
--- a/apps/docs/document-api/reference/tables/set-alt-text.mdx
+++ b/apps/docs/document-api/reference/tables/set-alt-text.mdx
@@ -38,11 +38,10 @@ _No fields._
```json
{
- "nodeId": "node-def456",
"target": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"title": "example"
}
@@ -71,6 +70,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -79,7 +82,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/tables/set-border.mdx b/apps/docs/document-api/reference/tables/set-border.mdx
index 495d108249..820f900851 100644
--- a/apps/docs/document-api/reference/tables/set-border.mdx
+++ b/apps/docs/document-api/reference/tables/set-border.mdx
@@ -42,11 +42,10 @@ _No fields._
"edge": "top",
"lineStyle": "example",
"lineWeightPt": 12.5,
- "nodeId": "node-def456",
"target": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
}
}
```
@@ -74,6 +73,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -82,7 +85,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/tables/set-cell-padding.mdx b/apps/docs/document-api/reference/tables/set-cell-padding.mdx
index a12a164336..0a384bf78a 100644
--- a/apps/docs/document-api/reference/tables/set-cell-padding.mdx
+++ b/apps/docs/document-api/reference/tables/set-cell-padding.mdx
@@ -40,12 +40,11 @@ _No fields._
{
"bottomPt": 12.5,
"leftPt": 12.5,
- "nodeId": "node-def456",
"rightPt": 12.5,
"target": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"topPt": 12.5
}
@@ -74,6 +73,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -82,7 +85,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/tables/set-cell-properties.mdx b/apps/docs/document-api/reference/tables/set-cell-properties.mdx
index cc98204b3e..b95e2d2828 100644
--- a/apps/docs/document-api/reference/tables/set-cell-properties.mdx
+++ b/apps/docs/document-api/reference/tables/set-cell-properties.mdx
@@ -38,12 +38,11 @@ _No fields._
```json
{
- "nodeId": "node-def456",
"preferredWidthPt": 12.5,
"target": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
}
}
```
@@ -71,6 +70,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -79,7 +82,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/tables/set-cell-spacing.mdx b/apps/docs/document-api/reference/tables/set-cell-spacing.mdx
index 1fc3bd30ca..c9abb7b23b 100644
--- a/apps/docs/document-api/reference/tables/set-cell-spacing.mdx
+++ b/apps/docs/document-api/reference/tables/set-cell-spacing.mdx
@@ -38,12 +38,11 @@ _No fields._
```json
{
- "nodeId": "node-def456",
"spacingPt": 12.5,
"target": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
}
}
```
@@ -71,6 +70,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -79,7 +82,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/tables/set-column-width.mdx b/apps/docs/document-api/reference/tables/set-column-width.mdx
index 17af31c505..04616c192e 100644
--- a/apps/docs/document-api/reference/tables/set-column-width.mdx
+++ b/apps/docs/document-api/reference/tables/set-column-width.mdx
@@ -39,11 +39,10 @@ _No fields._
```json
{
"columnIndex": 1,
- "tableNodeId": "example",
"tableTarget": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"widthPt": 12.5
}
@@ -72,6 +71,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -80,7 +83,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/tables/set-layout.mdx b/apps/docs/document-api/reference/tables/set-layout.mdx
index 5ec464c5ea..eb7f70421a 100644
--- a/apps/docs/document-api/reference/tables/set-layout.mdx
+++ b/apps/docs/document-api/reference/tables/set-layout.mdx
@@ -38,12 +38,11 @@ _No fields._
```json
{
- "nodeId": "node-def456",
"preferredWidth": 12.5,
"target": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
}
}
```
@@ -71,6 +70,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -79,7 +82,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/tables/set-row-height.mdx b/apps/docs/document-api/reference/tables/set-row-height.mdx
index 8bbe9e537c..4995924c76 100644
--- a/apps/docs/document-api/reference/tables/set-row-height.mdx
+++ b/apps/docs/document-api/reference/tables/set-row-height.mdx
@@ -47,17 +47,11 @@ _No fields._
```json
{
"heightPt": 12.5,
- "nodeId": "node-def456",
"rule": "atLeast",
"tableTarget": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
- },
- "target": {
- "kind": "block",
- "nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
}
}
```
@@ -85,6 +79,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -93,7 +91,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/tables/set-row-options.mdx b/apps/docs/document-api/reference/tables/set-row-options.mdx
index a41a7084c4..e237099434 100644
--- a/apps/docs/document-api/reference/tables/set-row-options.mdx
+++ b/apps/docs/document-api/reference/tables/set-row-options.mdx
@@ -46,16 +46,10 @@ _No fields._
```json
{
- "nodeId": "node-def456",
"tableTarget": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
- },
- "target": {
- "kind": "block",
- "nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
}
}
```
@@ -83,6 +77,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -91,7 +89,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/tables/set-shading.mdx b/apps/docs/document-api/reference/tables/set-shading.mdx
index 56870e039b..26acc3df68 100644
--- a/apps/docs/document-api/reference/tables/set-shading.mdx
+++ b/apps/docs/document-api/reference/tables/set-shading.mdx
@@ -39,11 +39,10 @@ _No fields._
```json
{
"color": "example",
- "nodeId": "node-def456",
"target": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
}
}
```
@@ -71,6 +70,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -79,7 +82,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/tables/set-style-option.mdx b/apps/docs/document-api/reference/tables/set-style-option.mdx
index abcaa196eb..6ee304e646 100644
--- a/apps/docs/document-api/reference/tables/set-style-option.mdx
+++ b/apps/docs/document-api/reference/tables/set-style-option.mdx
@@ -40,11 +40,10 @@ _No fields._
{
"enabled": true,
"flag": "headerRow",
- "nodeId": "node-def456",
"target": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
}
}
```
@@ -72,6 +71,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -80,7 +83,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/tables/set-style.mdx b/apps/docs/document-api/reference/tables/set-style.mdx
index dfb83c1fb7..6ca5ac4efb 100644
--- a/apps/docs/document-api/reference/tables/set-style.mdx
+++ b/apps/docs/document-api/reference/tables/set-style.mdx
@@ -38,12 +38,11 @@ _No fields._
```json
{
- "nodeId": "node-def456",
"styleId": "style-001",
"target": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
}
}
```
@@ -71,6 +70,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -79,7 +82,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/tables/set-table-padding.mdx b/apps/docs/document-api/reference/tables/set-table-padding.mdx
index b5f0e3ce3a..291b3a1098 100644
--- a/apps/docs/document-api/reference/tables/set-table-padding.mdx
+++ b/apps/docs/document-api/reference/tables/set-table-padding.mdx
@@ -40,12 +40,11 @@ _No fields._
{
"bottomPt": 12.5,
"leftPt": 12.5,
- "nodeId": "node-def456",
"rightPt": 12.5,
"target": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"topPt": 12.5
}
@@ -74,6 +73,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -82,7 +85,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/tables/sort.mdx b/apps/docs/document-api/reference/tables/sort.mdx
index b44c57e435..d35831f74b 100644
--- a/apps/docs/document-api/reference/tables/sort.mdx
+++ b/apps/docs/document-api/reference/tables/sort.mdx
@@ -45,11 +45,10 @@ _No fields._
"type": "text"
}
],
- "nodeId": "node-def456",
"target": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
}
}
```
@@ -77,6 +76,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -85,7 +88,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/tables/split-cell.mdx b/apps/docs/document-api/reference/tables/split-cell.mdx
index 8a94d4ee7a..5d0374d72c 100644
--- a/apps/docs/document-api/reference/tables/split-cell.mdx
+++ b/apps/docs/document-api/reference/tables/split-cell.mdx
@@ -39,12 +39,11 @@ _No fields._
```json
{
"columns": 1,
- "nodeId": "node-def456",
"rows": 1,
"target": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
}
}
```
@@ -72,6 +71,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -80,7 +83,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/tables/split.mdx b/apps/docs/document-api/reference/tables/split.mdx
index d5a4f527dc..4109072316 100644
--- a/apps/docs/document-api/reference/tables/split.mdx
+++ b/apps/docs/document-api/reference/tables/split.mdx
@@ -39,11 +39,10 @@ _No fields._
```json
{
"atRowIndex": 1,
- "nodeId": "node-def456",
"target": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
}
}
```
@@ -71,6 +70,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -79,7 +82,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/apps/docs/document-api/reference/tables/unmerge-cells.mdx b/apps/docs/document-api/reference/tables/unmerge-cells.mdx
index d451f388ea..58aac0130d 100644
--- a/apps/docs/document-api/reference/tables/unmerge-cells.mdx
+++ b/apps/docs/document-api/reference/tables/unmerge-cells.mdx
@@ -38,11 +38,10 @@ _No fields._
```json
{
- "nodeId": "node-def456",
"target": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
}
}
```
@@ -70,6 +69,10 @@ _No fields._
| `failure.message` | string | yes | |
| `success` | `false` | yes | Constant: `false` |
+
+When present, `result.table` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass `result.table.nodeId` to the next table operation instead of re-running `find()`. Destructive operations may omit `table`, and cell-targeted border/shading calls may still return a `tableCell` address.
+
+
### Example response
```json
@@ -78,7 +81,7 @@ _No fields._
"table": {
"kind": "block",
"nodeId": "node-def456",
- "nodeType": "paragraph"
+ "nodeType": "table"
},
"trackedChangeRefs": [
{
diff --git a/packages/document-api/scripts/lib/reference-docs-artifacts.ts b/packages/document-api/scripts/lib/reference-docs-artifacts.ts
index 02ed12894d..477ac40320 100644
--- a/packages/document-api/scripts/lib/reference-docs-artifacts.ts
+++ b/packages/document-api/scripts/lib/reference-docs-artifacts.ts
@@ -682,6 +682,101 @@ function isObjectRecord(value: unknown): value is Record {
return typeof value === 'object' && value !== null && !Array.isArray(value);
}
+function makeExampleBlockAddress(nodeType: string): Record {
+ return {
+ kind: 'block',
+ nodeId: 'node-def456',
+ nodeType,
+ };
+}
+
+function isTableReferenceOperation(operationId: ContractOperationSnapshot['operationId']): boolean {
+ return operationId === 'create.table' || operationId.startsWith('tables.');
+}
+
+function normalizeTableOperationInputExample(
+ operationId: ContractOperationSnapshot['operationId'],
+ input: unknown,
+): unknown {
+ if (!isTableReferenceOperation(operationId) || operationId === 'create.table' || !isObjectRecord(input)) {
+ return input;
+ }
+
+ const clone = structuredClone(input) as Record;
+
+ if (isObjectRecord(clone.tableTarget)) {
+ clone.tableTarget = makeExampleBlockAddress('table');
+ delete clone.tableNodeId;
+ delete clone.target;
+ delete clone.nodeId;
+ return clone;
+ }
+
+ if (isObjectRecord(clone.target)) {
+ clone.target = makeExampleBlockAddress('table');
+ delete clone.nodeId;
+ }
+
+ return clone;
+}
+
+function normalizeTableOperationOutputExample(
+ operationId: ContractOperationSnapshot['operationId'],
+ output: unknown,
+): unknown {
+ if (!isTableReferenceOperation(operationId) || !isObjectRecord(output)) {
+ return output;
+ }
+
+ const clone = structuredClone(output) as Record;
+ if (isObjectRecord(clone.table)) {
+ clone.table = makeExampleBlockAddress('table');
+ }
+ return clone;
+}
+
+function schemaHasTopLevelField(schema: JsonSchema, $defs: Defs, fieldName: string, depth = 0): boolean {
+ if (depth > 8) return false;
+
+ const { resolved } = resolveRef(schema, $defs);
+ const flat = flattenAllOf(resolved, $defs);
+ const properties = flat.properties as Record | undefined;
+ if (properties && Object.prototype.hasOwnProperty.call(properties, fieldName)) {
+ return true;
+ }
+
+ for (const keyword of ['oneOf', 'anyOf'] as const) {
+ const variants = flat[keyword];
+ if (!Array.isArray(variants)) continue;
+ for (const variant of variants as JsonSchema[]) {
+ if (schemaHasTopLevelField(variant, $defs, fieldName, depth + 1)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+function renderTableResultNote(operation: ContractOperationSnapshot, $defs: Defs): string {
+ if (
+ !isTableReferenceOperation(operation.operationId) ||
+ !schemaHasTopLevelField(operation.schemas.output, $defs, 'table')
+ ) {
+ return '';
+ }
+
+ if (operation.operationId === 'create.table') {
+ return `
+On success, \`result.table\` is the created table address. Reuse \`result.table.nodeId\` for follow-up table operations in the same session.
+`;
+ }
+
+ return `
+When present, \`result.table\` is the follow-up address to reuse after this call. For non-destructive table-targeted mutations, pass \`result.table.nodeId\` to the next table operation instead of re-running \`find()\`. Destructive operations may omit \`table\`, and cell-targeted border/shading calls may still return a \`tableCell\` address.
+`;
+}
+
function buildCapabilitiesOutputExample(snapshot: ReturnType): unknown {
const operation = snapshot.operations.find((entry) => entry.operationId === 'capabilities.get');
if (!operation) return {};
@@ -789,9 +884,12 @@ function getOperationExamples(
},
};
+ const input = inputOverrides[operation.operationId] ?? generateExample(operation.schemas.input, snapshot.$defs);
+ const output = outputOverrides[operation.operationId] ?? generateExample(operation.schemas.output, snapshot.$defs);
+
return {
- input: inputOverrides[operation.operationId] ?? generateExample(operation.schemas.input, snapshot.$defs),
- output: outputOverrides[operation.operationId] ?? generateExample(operation.schemas.output, snapshot.$defs),
+ input: normalizeTableOperationInputExample(operation.operationId, input),
+ output: normalizeTableOperationOutputExample(operation.operationId, output),
};
}
@@ -858,6 +956,7 @@ function renderOperationPage(
const inputFields = renderFieldSections(operation.schemas.input, $defs);
const outputFields = renderFieldSections(operation.schemas.output, $defs);
+ const tableResultNote = renderTableResultNote(operation, $defs);
const { input: inputExample, output: outputExample } = getOperationExamples(operation, snapshot);
const stepOpsSection = renderStepOpsSection(operation);
@@ -912,7 +1011,7 @@ ${stableStringify(inputExample)}
## Output fields
-${outputFields}
+${outputFields}${tableResultNote ? `\n\n${tableResultNote}` : ''}
### Example response
@@ -970,7 +1069,15 @@ ${GENERATED_MARKER}
[Back to full reference](${toRelativeDocHref(group.pagePath, REFERENCE_INDEX_PATH)})
-${group.definition.description}
+${group.definition.description}${
+ group.definition.key === 'tables'
+ ? `
+
+
+For non-destructive table-targeted mutations, reuse \`result.table.nodeId\` from the previous success result instead of re-running \`find()\`. Cell-targeted border/shading calls may still return a \`tableCell\` address.
+`
+ : ''
+ }
| Operation | Member path | Mutates | Idempotency | Tracked | Dry run |
| --- | --- | --- | --- | --- | --- |
diff --git a/packages/document-api/src/types/table-operations.types.ts b/packages/document-api/src/types/table-operations.types.ts
index 1f88fb6174..e3853f1312 100644
--- a/packages/document-api/src/types/table-operations.types.ts
+++ b/packages/document-api/src/types/table-operations.types.ts
@@ -81,6 +81,13 @@ export type TableCreateLocation =
/**
* Generic success result for table mutation operations.
+ *
+ * For non-destructive table-targeted mutations, `table` is the canonical
+ * post-mutation table reference. Use `table.nodeId` to target the same table
+ * in subsequent operations — no intermediate `find()` needed.
+ *
+ * `table` is `undefined` for destructive operations (delete, convertToText)
+ * and in rare cases where post-mutation re-resolution fails.
*/
export interface TableMutationSuccess {
success: true;
diff --git a/packages/super-editor/src/document-api-adapters/helpers/table-target-resolver.ts b/packages/super-editor/src/document-api-adapters/helpers/table-target-resolver.ts
index 08c16e5cdd..9125dc8a56 100644
--- a/packages/super-editor/src/document-api-adapters/helpers/table-target-resolver.ts
+++ b/packages/super-editor/src/document-api-adapters/helpers/table-target-resolver.ts
@@ -450,6 +450,43 @@ export function getTableColumnCount(tableNode: ProseMirrorNode): number {
return count;
}
+// ---------------------------------------------------------------------------
+// Post-mutation re-resolution
+// ---------------------------------------------------------------------------
+
+/**
+ * Re-resolves a table's address after a mutation has been dispatched.
+ *
+ * Uses transaction position mapping as the primary strategy, with
+ * nodeId-based fallback for DOCX tables with stable primary IDs.
+ *
+ * Returns `undefined` if the table cannot be re-resolved — callers
+ * should NOT fall back to the pre-mutation address.
+ */
+export function resolvePostMutationTableAddress(
+ editor: Editor,
+ preMutationPos: number,
+ preMutationNodeId: string,
+ tr: { mapping: { map(pos: number, assoc?: number): number } },
+): BlockNodeAddress | undefined {
+ const index = getBlockIndex(editor);
+
+ // Strategy 1: Map pre-mutation position through the transaction.
+ const mappedPos = tr.mapping.map(preMutationPos);
+ const candidate = index.candidates.find((c) => c.pos === mappedPos && c.nodeType === 'table');
+ if (candidate) return toBlockAddress(candidate);
+
+ // Strategy 2: Look up by pre-mutation nodeId (works for DOCX tables with stable paraId).
+ try {
+ const found = findBlockByNodeIdOnly(index, preMutationNodeId);
+ if (found.nodeType === 'table') return toBlockAddress(found);
+ } catch {
+ // Not found or ambiguous — fall through.
+ }
+
+ return undefined;
+}
+
// ---------------------------------------------------------------------------
// Failure helper
// ---------------------------------------------------------------------------
diff --git a/packages/super-editor/src/document-api-adapters/tables-adapter.ref-handoff.test.ts b/packages/super-editor/src/document-api-adapters/tables-adapter.ref-handoff.test.ts
new file mode 100644
index 0000000000..ee4453c494
--- /dev/null
+++ b/packages/super-editor/src/document-api-adapters/tables-adapter.ref-handoff.test.ts
@@ -0,0 +1,210 @@
+/* @vitest-environment jsdom */
+
+import { afterEach, beforeAll, describe, expect, it } from 'vitest';
+import { initTestEditor, loadTestDataForEditorTests } from '@tests/helpers/helpers.js';
+import type { Editor } from '../core/Editor.js';
+import {
+ createTableAdapter,
+ tablesMoveAdapter,
+ tablesSetBorderAdapter,
+ tablesInsertColumnAdapter,
+ tablesMergeCellsAdapter,
+ tablesSetCellSpacingAdapter,
+ tablesSetShadingAdapter,
+ tablesGetCellsAdapter,
+} from './tables-adapter.js';
+
+type LoadedDocData = Awaited>;
+
+const DIRECT = { changeMode: 'direct' } as const;
+
+function requireTableNodeId(result: { success: boolean; table?: { nodeId?: string } }, label: string): string {
+ if (!result.success) {
+ throw new Error(`${label} failed: expected success.`);
+ }
+ const nodeId = (result as { table?: { nodeId?: string } }).table?.nodeId;
+ if (!nodeId) {
+ throw new Error(`${label}: expected result.table.nodeId to be defined.`);
+ }
+ return nodeId;
+}
+
+describe('SD-2126: post-mutation table ref handoff', () => {
+ let docData: LoadedDocData;
+ let editor: Editor | undefined;
+
+ beforeAll(async () => {
+ docData = await loadTestDataForEditorTests('blank-doc.docx');
+ });
+
+ afterEach(() => {
+ editor?.destroy();
+ editor = undefined;
+ });
+
+ function createEditor(): Editor {
+ const result = initTestEditor({
+ content: docData.docx,
+ media: docData.media,
+ mediaFiles: docData.mediaFiles,
+ fonts: docData.fonts,
+ useImmediateSetTimeout: false,
+ });
+ editor = result.editor;
+ return editor;
+ }
+
+ it('chains create → setBorder → insertColumn → mergeCells → setCellSpacing without find()', () => {
+ const ed = createEditor();
+
+ // Step 1: create.table
+ const createResult = createTableAdapter(ed, { rows: 3, columns: 3, at: { kind: 'documentEnd' } }, DIRECT);
+ const id1 = requireTableNodeId(createResult, 'create.table');
+
+ // Step 2: setBorder using create's ref
+ const borderResult = tablesSetBorderAdapter(
+ ed,
+ { nodeId: id1, edge: 'top', lineStyle: 'single', lineWeightPt: 1, color: '000000' },
+ DIRECT,
+ );
+ const id2 = requireTableNodeId(borderResult, 'tables.setBorder');
+
+ // Step 3: insertColumn using setBorder's ref
+ const colResult = tablesInsertColumnAdapter(ed, { tableNodeId: id2, columnIndex: 0, position: 'right' }, DIRECT);
+ const id3 = requireTableNodeId(colResult, 'tables.insertColumn');
+
+ // Step 4: mergeCells using insertColumn's ref
+ const mergeResult = tablesMergeCellsAdapter(
+ ed,
+ { tableNodeId: id3, start: { rowIndex: 0, columnIndex: 0 }, end: { rowIndex: 0, columnIndex: 1 } },
+ DIRECT,
+ );
+ const id4 = requireTableNodeId(mergeResult, 'tables.mergeCells');
+
+ // Step 5: setCellSpacing using mergeCells' ref
+ const spacingResult = tablesSetCellSpacingAdapter(ed, { nodeId: id4, spacingPt: 2 }, DIRECT);
+ const id5 = requireTableNodeId(spacingResult, 'tables.setCellSpacing');
+
+ // Final ref should still be resolvable
+ expect(id5).toBeTruthy();
+ });
+
+ it('returns a resolvable ref for a runtime-created table with volatile sdBlockId', () => {
+ const ed = createEditor();
+
+ // Runtime tables get UUID sdBlockId (volatile). The returned nodeId should
+ // be the canonical position-based fallback, not the raw UUID.
+ const createResult = createTableAdapter(ed, { rows: 2, columns: 2, at: { kind: 'documentEnd' } }, DIRECT);
+ const id1 = requireTableNodeId(createResult, 'create.table');
+
+ // Mutate — this changes positions/structure.
+ const colResult = tablesInsertColumnAdapter(ed, { tableNodeId: id1, columnIndex: 0, position: 'right' }, DIRECT);
+ const id2 = requireTableNodeId(colResult, 'tables.insertColumn');
+
+ // The returned ref should work for a follow-up operation.
+ const borderResult = tablesSetBorderAdapter(
+ ed,
+ { nodeId: id2, edge: 'bottom', lineStyle: 'single', lineWeightPt: 0.5, color: 'FF0000' },
+ DIRECT,
+ );
+ expect(borderResult.success).toBe(true);
+ expect((borderResult as { table?: { nodeId?: string } }).table?.nodeId).toBeTruthy();
+ });
+
+ it('cell-targeted setBorder preserves current cell address behavior', () => {
+ const ed = createEditor();
+
+ // Create a table and find a cell to target.
+ const createResult = createTableAdapter(ed, { rows: 2, columns: 2, at: { kind: 'documentEnd' } }, DIRECT);
+ expect(createResult.success).toBe(true);
+
+ // Find a cell nodeId via tablesGetCellsAdapter.
+ const tableNodeId = requireTableNodeId(createResult, 'create.table');
+ const cellsResult = tablesGetCellsAdapter(ed, { nodeId: tableNodeId });
+ expect(cellsResult.cells.length).toBeGreaterThan(0);
+ const cellNodeId = cellsResult.cells[0]!.nodeId;
+ expect(cellNodeId).toBeTruthy();
+
+ // Target the cell with setBorder.
+ const borderResult = tablesSetBorderAdapter(
+ ed,
+ { nodeId: cellNodeId!, edge: 'top', lineStyle: 'single', lineWeightPt: 1, color: '000000' },
+ DIRECT,
+ );
+
+ expect(borderResult.success).toBe(true);
+ // Cell-targeted ops return the cell address (not parent table) — unchanged behavior.
+ const table = (borderResult as { table?: { nodeType?: string } }).table;
+ expect(table?.nodeType).toBe('tableCell');
+ });
+
+ it('tables.move returns a chainable ref after relocating the table', () => {
+ const ed = createEditor();
+
+ // Create two tables so there is a meaningful destination.
+ createTableAdapter(ed, { rows: 2, columns: 2, at: { kind: 'documentEnd' } }, DIRECT);
+ const createResult2 = createTableAdapter(ed, { rows: 2, columns: 2, at: { kind: 'documentEnd' } }, DIRECT);
+ const tableId = requireTableNodeId(createResult2, 'create.table (second)');
+
+ // Move the second table to the document start.
+ const moveResult = tablesMoveAdapter(ed, { nodeId: tableId, destination: { kind: 'documentStart' } }, DIRECT);
+ const movedId = requireTableNodeId(moveResult, 'tables.move');
+
+ // The returned ref should be usable for a follow-up mutation.
+ const borderResult = tablesSetBorderAdapter(
+ ed,
+ { nodeId: movedId, edge: 'top', lineStyle: 'single', lineWeightPt: 1, color: '000000' },
+ DIRECT,
+ );
+ expect(borderResult.success).toBe(true);
+ expect((borderResult as { table?: { nodeId?: string } }).table?.nodeId).toBeTruthy();
+ });
+
+ it('every in-scope mutation returns a defined table ref (invariant check)', () => {
+ const ed = createEditor();
+
+ const createResult = createTableAdapter(ed, { rows: 3, columns: 3, at: { kind: 'documentEnd' } }, DIRECT);
+ const id = requireTableNodeId(createResult, 'create.table');
+
+ // setBorder (table-targeted)
+ const r1 = tablesSetBorderAdapter(
+ ed,
+ { nodeId: id, edge: 'top', lineStyle: 'single', lineWeightPt: 1, color: '000000' },
+ DIRECT,
+ );
+ expect(r1.success).toBe(true);
+ expect((r1 as { table?: unknown }).table).toBeDefined();
+
+ const id2 = requireTableNodeId(r1, 'setBorder');
+
+ // insertColumn
+ const r2 = tablesInsertColumnAdapter(ed, { tableNodeId: id2, columnIndex: 0, position: 'right' }, DIRECT);
+ expect(r2.success).toBe(true);
+ expect((r2 as { table?: unknown }).table).toBeDefined();
+
+ const id3 = requireTableNodeId(r2, 'insertColumn');
+
+ // mergeCells
+ const r3 = tablesMergeCellsAdapter(
+ ed,
+ { tableNodeId: id3, start: { rowIndex: 0, columnIndex: 0 }, end: { rowIndex: 0, columnIndex: 1 } },
+ DIRECT,
+ );
+ expect(r3.success).toBe(true);
+ expect((r3 as { table?: unknown }).table).toBeDefined();
+
+ const id4 = requireTableNodeId(r3, 'mergeCells');
+
+ // setCellSpacing
+ const r4 = tablesSetCellSpacingAdapter(ed, { nodeId: id4, spacingPt: 2 }, DIRECT);
+ expect(r4.success).toBe(true);
+ expect((r4 as { table?: unknown }).table).toBeDefined();
+
+ const id5 = requireTableNodeId(r4, 'setCellSpacing');
+
+ // setShading (table-targeted)
+ const r5 = tablesSetShadingAdapter(ed, { nodeId: id5, color: 'CCCCCC' }, DIRECT);
+ expect(r5.success).toBe(true);
+ expect((r5 as { table?: unknown }).table).toBeDefined();
+ });
+});
diff --git a/packages/super-editor/src/document-api-adapters/tables-adapter.ts b/packages/super-editor/src/document-api-adapters/tables-adapter.ts
index 3c59a0eee1..c9961cd600 100644
--- a/packages/super-editor/src/document-api-adapters/tables-adapter.ts
+++ b/packages/super-editor/src/document-api-adapters/tables-adapter.ts
@@ -65,6 +65,7 @@ import {
resolveColumnLocator,
resolveCellLocator,
resolveMergeRangeLocator,
+ resolvePostMutationTableAddress,
getTableColumnCount,
toTableFailure,
} from './helpers/table-target-resolver.js';
@@ -919,7 +920,7 @@ export function tablesClearContentsAdapter(
applyDirectMutationMeta(tr);
editor.dispatch(tr);
clearIndexCache(editor);
- return buildTableSuccess(address);
+ return buildTableSuccess(resolvePostMutationTableAddress(editor, candidate.pos, address.nodeId, tr));
} catch {
return toTableFailure('INVALID_TARGET', 'Table content clearing could not be applied.');
}
@@ -935,7 +936,7 @@ export function tablesMoveAdapter(
): TableMutationResult {
rejectTrackedMode('tables.move', options);
- const { candidate } = resolveTableLocator(editor, input, 'tables.move');
+ const { candidate, address } = resolveTableLocator(editor, input, 'tables.move');
if (options?.dryRun) {
return buildTableSuccess(toBlockAddress(candidate));
@@ -963,9 +964,22 @@ export function tablesMoveAdapter(
editor.dispatch(tr);
clearIndexCache(editor);
- // Resolve the table at its new position to return its address.
- // The nodeId is preserved because we moved the same node.
- return buildTableSuccess();
+ // For move, position mapping fails (the node was deleted and re-inserted
+ // at a new location). Try nodeId-based resolution first (works for DOCX
+ // tables with stable paraId), then fall back to sdBlockId lookup for
+ // runtime tables whose position-based nodeId changed.
+ let freshAddress = resolvePostMutationTableAddress(editor, candidate.pos, address.nodeId, tr);
+ if (!freshAddress) {
+ const sdBlockId = (tableSlice.attrs as Record).sdBlockId;
+ if (typeof sdBlockId === 'string') {
+ const index = getBlockIndex(editor);
+ const found = index.candidates.find(
+ (c) => c.nodeType === 'table' && (c.node.attrs as Record).sdBlockId === sdBlockId,
+ );
+ if (found) freshAddress = toBlockAddress(found);
+ }
+ }
+ return buildTableSuccess(freshAddress);
} catch {
return toTableFailure('INVALID_TARGET', 'Table move could not be applied.');
}
@@ -1028,7 +1042,7 @@ export function tablesSetLayoutAdapter(
applyDirectMutationMeta(tr);
editor.dispatch(tr);
clearIndexCache(editor);
- return buildTableSuccess(address);
+ return buildTableSuccess(resolvePostMutationTableAddress(editor, candidate.pos, address.nodeId, tr));
} catch {
return toTableFailure('INVALID_TARGET', 'Table layout update could not be applied.');
}
@@ -1071,7 +1085,7 @@ export function tablesSetAltTextAdapter(
applyDirectMutationMeta(tr);
editor.dispatch(tr);
clearIndexCache(editor);
- return buildTableSuccess(address);
+ return buildTableSuccess(resolvePostMutationTableAddress(editor, candidate.pos, address.nodeId, tr));
} catch {
return toTableFailure('INVALID_TARGET', 'Table alt text update could not be applied.');
}
@@ -1131,7 +1145,7 @@ export function tablesInsertRowAdapter(
else applyDirectMutationMeta(tr);
editor.dispatch(tr);
clearIndexCache(editor);
- return buildTableSuccess(table.address);
+ return buildTableSuccess(resolvePostMutationTableAddress(editor, table.candidate.pos, table.address.nodeId, tr));
} catch {
return toTableFailure('INVALID_TARGET', 'Row insertion could not be applied.');
}
@@ -1204,7 +1218,7 @@ export function tablesDeleteRowAdapter(
else applyDirectMutationMeta(tr);
editor.dispatch(tr);
clearIndexCache(editor);
- return buildTableSuccess(table.address);
+ return buildTableSuccess(resolvePostMutationTableAddress(editor, table.candidate.pos, table.address.nodeId, tr));
} catch {
return toTableFailure('INVALID_TARGET', 'Row deletion could not be applied.');
}
@@ -1249,7 +1263,7 @@ export function tablesSetRowHeightAdapter(
applyDirectMutationMeta(tr);
editor.dispatch(tr);
clearIndexCache(editor);
- return buildTableSuccess(table.address);
+ return buildTableSuccess(resolvePostMutationTableAddress(editor, table.candidate.pos, table.address.nodeId, tr));
} catch {
return toTableFailure('INVALID_TARGET', 'Row height update could not be applied.');
}
@@ -1317,7 +1331,7 @@ export function tablesDistributeRowsAdapter(
applyDirectMutationMeta(tr);
editor.dispatch(tr);
clearIndexCache(editor);
- return buildTableSuccess(address);
+ return buildTableSuccess(resolvePostMutationTableAddress(editor, candidate.pos, address.nodeId, tr));
} catch {
return toTableFailure('INVALID_TARGET', 'Row distribution could not be applied.');
}
@@ -1366,7 +1380,7 @@ export function tablesSetRowOptionsAdapter(
applyDirectMutationMeta(tr);
editor.dispatch(tr);
clearIndexCache(editor);
- return buildTableSuccess(table.address);
+ return buildTableSuccess(resolvePostMutationTableAddress(editor, table.candidate.pos, table.address.nodeId, tr));
} catch {
return toTableFailure('INVALID_TARGET', 'Row options update could not be applied.');
}
@@ -1423,7 +1437,7 @@ export function tablesInsertColumnAdapter(
else applyDirectMutationMeta(tr);
editor.dispatch(tr);
clearIndexCache(editor);
- return buildTableSuccess(table.address);
+ return buildTableSuccess(resolvePostMutationTableAddress(editor, table.candidate.pos, table.address.nodeId, tr));
} catch {
return toTableFailure('INVALID_TARGET', 'Column insertion could not be applied.');
}
@@ -1475,7 +1489,7 @@ export function tablesDeleteColumnAdapter(
else applyDirectMutationMeta(tr);
editor.dispatch(tr);
clearIndexCache(editor);
- return buildTableSuccess(table.address);
+ return buildTableSuccess(resolvePostMutationTableAddress(editor, table.candidate.pos, table.address.nodeId, tr));
} catch {
return toTableFailure('INVALID_TARGET', 'Column deletion could not be applied.');
}
@@ -1545,7 +1559,7 @@ export function tablesSetColumnWidthAdapter(
applyDirectMutationMeta(tr);
editor.dispatch(tr);
clearIndexCache(editor);
- return buildTableSuccess(table.address);
+ return buildTableSuccess(resolvePostMutationTableAddress(editor, table.candidate.pos, table.address.nodeId, tr));
} catch {
return toTableFailure('INVALID_TARGET', 'Column width update could not be applied.');
}
@@ -1648,7 +1662,7 @@ export function tablesDistributeColumnsAdapter(
applyDirectMutationMeta(tr);
editor.dispatch(tr);
clearIndexCache(editor);
- return buildTableSuccess(address);
+ return buildTableSuccess(resolvePostMutationTableAddress(editor, candidate.pos, address.nodeId, tr));
} catch {
return toTableFailure('INVALID_TARGET', 'Column distribution could not be applied.');
}
@@ -2085,7 +2099,7 @@ export function tablesInsertCellAdapter(
applyDirectMutationMeta(tr);
editor.dispatch(tr);
clearIndexCache(editor);
- return buildTableSuccess(table.address);
+ return buildTableSuccess(resolvePostMutationTableAddress(editor, table.candidate.pos, table.address.nodeId, tr));
} catch {
return toTableFailure('INVALID_TARGET', 'Cell insertion could not be applied.');
}
@@ -2203,7 +2217,7 @@ export function tablesDeleteCellAdapter(
applyDirectMutationMeta(tr);
editor.dispatch(tr);
clearIndexCache(editor);
- return buildTableSuccess(table.address);
+ return buildTableSuccess(resolvePostMutationTableAddress(editor, table.candidate.pos, table.address.nodeId, tr));
} catch {
return toTableFailure('INVALID_TARGET', 'Cell deletion could not be applied.');
}
@@ -2298,7 +2312,7 @@ export function tablesMergeCellsAdapter(
applyDirectMutationMeta(tr);
editor.dispatch(tr);
clearIndexCache(editor);
- return buildTableSuccess(table.address);
+ return buildTableSuccess(resolvePostMutationTableAddress(editor, table.candidate.pos, table.address.nodeId, tr));
} catch {
return toTableFailure('INVALID_TARGET', 'Cell merge could not be applied.');
}
@@ -2354,7 +2368,7 @@ export function tablesUnmergeCellsAdapter(
applyDirectMutationMeta(tr);
editor.dispatch(tr);
clearIndexCache(editor);
- return buildTableSuccess(table.address);
+ return buildTableSuccess(resolvePostMutationTableAddress(editor, table.candidate.pos, table.address.nodeId, tr));
} catch {
return toTableFailure('INVALID_TARGET', 'Cell unmerge could not be applied.');
}
@@ -2509,7 +2523,7 @@ export function tablesSplitCellAdapter(
applyDirectMutationMeta(tr);
editor.dispatch(tr);
clearIndexCache(editor);
- return buildTableSuccess(table.address);
+ return buildTableSuccess(resolvePostMutationTableAddress(editor, table.candidate.pos, table.address.nodeId, tr));
} catch {
return toTableFailure('INVALID_TARGET', 'Cell split could not be applied.');
}
@@ -2571,7 +2585,7 @@ export function tablesSetCellPropertiesAdapter(
applyDirectMutationMeta(tr);
editor.dispatch(tr);
clearIndexCache(editor);
- return buildTableSuccess(table.address);
+ return buildTableSuccess(resolvePostMutationTableAddress(editor, table.candidate.pos, table.address.nodeId, tr));
} catch {
return toTableFailure('INVALID_TARGET', 'Cell properties update could not be applied.');
}
@@ -2673,7 +2687,7 @@ export function tablesSortAdapter(
applyDirectMutationMeta(tr);
editor.dispatch(tr);
clearIndexCache(editor);
- return buildTableSuccess(address);
+ return buildTableSuccess(resolvePostMutationTableAddress(editor, candidate.pos, address.nodeId, tr));
} catch {
return toTableFailure('INVALID_TARGET', 'Table sort could not be applied.');
}
@@ -2713,7 +2727,7 @@ export function tablesSetStyleAdapter(
applyDirectMutationMeta(tr);
editor.dispatch(tr);
clearIndexCache(editor);
- return buildTableSuccess(address);
+ return buildTableSuccess(resolvePostMutationTableAddress(editor, candidate.pos, address.nodeId, tr));
} catch {
return toTableFailure('INVALID_TARGET', 'Table style assignment could not be applied.');
}
@@ -2749,7 +2763,7 @@ export function tablesClearStyleAdapter(
applyDirectMutationMeta(tr);
editor.dispatch(tr);
clearIndexCache(editor);
- return buildTableSuccess(address);
+ return buildTableSuccess(resolvePostMutationTableAddress(editor, candidate.pos, address.nodeId, tr));
} catch {
return toTableFailure('INVALID_TARGET', 'Table style removal could not be applied.');
}
@@ -2808,7 +2822,7 @@ export function tablesSetStyleOptionAdapter(
applyDirectMutationMeta(tr);
editor.dispatch(tr);
clearIndexCache(editor);
- return buildTableSuccess(address);
+ return buildTableSuccess(resolvePostMutationTableAddress(editor, candidate.pos, address.nodeId, tr));
} catch {
return toTableFailure('INVALID_TARGET', 'Table style option could not be applied.');
}
@@ -2903,6 +2917,9 @@ export function tablesSetBorderAdapter(
applyDirectMutationMeta(tr);
editor.dispatch(tr);
clearIndexCache(editor);
+ if (resolved.scope === 'table') {
+ return buildTableSuccess(resolvePostMutationTableAddress(editor, resolved.pos, resolved.address.nodeId, tr));
+ }
return buildTableSuccess(resolved.address);
} catch {
return toTableFailure('INVALID_TARGET', 'Border update could not be applied.');
@@ -2956,6 +2973,9 @@ export function tablesClearBorderAdapter(
applyDirectMutationMeta(tr);
editor.dispatch(tr);
clearIndexCache(editor);
+ if (resolved.scope === 'table') {
+ return buildTableSuccess(resolvePostMutationTableAddress(editor, resolved.pos, resolved.address.nodeId, tr));
+ }
return buildTableSuccess(resolved.address);
} catch {
return toTableFailure('INVALID_TARGET', 'Border clear could not be applied.');
@@ -3015,7 +3035,7 @@ export function tablesApplyBorderPresetAdapter(
applyDirectMutationMeta(tr);
editor.dispatch(tr);
clearIndexCache(editor);
- return buildTableSuccess(address);
+ return buildTableSuccess(resolvePostMutationTableAddress(editor, candidate.pos, address.nodeId, tr));
} catch {
return toTableFailure('INVALID_TARGET', 'Border preset could not be applied.');
}
@@ -3085,6 +3105,9 @@ export function tablesSetShadingAdapter(
applyDirectMutationMeta(tr);
editor.dispatch(tr);
clearIndexCache(editor);
+ if (resolved.scope === 'table') {
+ return buildTableSuccess(resolvePostMutationTableAddress(editor, resolved.pos, resolved.address.nodeId, tr));
+ }
return buildTableSuccess(resolved.address);
} catch {
return toTableFailure('INVALID_TARGET', 'Shading update could not be applied.');
@@ -3153,6 +3176,9 @@ export function tablesClearShadingAdapter(
applyDirectMutationMeta(tr);
editor.dispatch(tr);
clearIndexCache(editor);
+ if (resolved.scope === 'table') {
+ return buildTableSuccess(resolvePostMutationTableAddress(editor, resolved.pos, resolved.address.nodeId, tr));
+ }
return buildTableSuccess(resolved.address);
} catch {
return toTableFailure('INVALID_TARGET', 'Shading clear could not be applied.');
@@ -3200,7 +3226,7 @@ export function tablesSetTablePaddingAdapter(
applyDirectMutationMeta(tr);
editor.dispatch(tr);
clearIndexCache(editor);
- return buildTableSuccess(address);
+ return buildTableSuccess(resolvePostMutationTableAddress(editor, candidate.pos, address.nodeId, tr));
} catch {
return toTableFailure('INVALID_TARGET', 'Table padding could not be applied.');
}
@@ -3251,7 +3277,7 @@ export function tablesSetCellPaddingAdapter(
applyDirectMutationMeta(tr);
editor.dispatch(tr);
clearIndexCache(editor);
- return buildTableSuccess(table.address);
+ return buildTableSuccess(resolvePostMutationTableAddress(editor, table.candidate.pos, table.address.nodeId, tr));
} catch {
return toTableFailure('INVALID_TARGET', 'Cell padding could not be applied.');
}
@@ -3291,7 +3317,7 @@ export function tablesSetCellSpacingAdapter(
applyDirectMutationMeta(tr);
editor.dispatch(tr);
clearIndexCache(editor);
- return buildTableSuccess(address);
+ return buildTableSuccess(resolvePostMutationTableAddress(editor, candidate.pos, address.nodeId, tr));
} catch {
return toTableFailure('INVALID_TARGET', 'Cell spacing could not be applied.');
}
@@ -3329,7 +3355,7 @@ export function tablesClearCellSpacingAdapter(
applyDirectMutationMeta(tr);
editor.dispatch(tr);
clearIndexCache(editor);
- return buildTableSuccess(address);
+ return buildTableSuccess(resolvePostMutationTableAddress(editor, candidate.pos, address.nodeId, tr));
} catch {
return toTableFailure('INVALID_TARGET', 'Cell spacing removal could not be applied.');
}