From 348921725d3c016c6ead628dae15f07b40dd8e92 Mon Sep 17 00:00:00 2001 From: Nick Bernal Date: Fri, 6 Mar 2026 18:05:37 -0800 Subject: [PATCH 1/3] feat(document-api): content controls --- apps/cli/scripts/export-sdk-contract.ts | 15 +- .../src/__tests__/conformance/scenarios.ts | 28 +- apps/cli/src/lib/operation-args.ts | 17 +- .../document-api/available-operations.mdx | 56 + .../reference/_generated-manifest.json | 121 +- .../reference/capabilities/get.mdx | 6061 ++++++++++++----- .../content-controls/append-content.mdx | 326 + .../content-controls/checkbox/get-state.mdx | 127 + .../content-controls/checkbox/set-state.mdx | 319 + .../checkbox/set-symbol-pair.mdx | 325 + .../content-controls/checkbox/toggle.mdx | 313 + .../choice-list/get-items.mdx | 137 + .../choice-list/set-items.mdx | 324 + .../choice-list/set-selected.mdx | 319 + .../content-controls/clear-binding.mdx | 312 + .../content-controls/clear-content.mdx | 312 + .../reference/content-controls/copy.mdx | 345 + .../reference/content-controls/create.mdx | 347 + .../content-controls/date/clear-value.mdx | 313 + .../content-controls/date/set-calendar.mdx | 319 + .../date/set-display-format.mdx | 319 + .../date/set-display-locale.mdx | 319 + .../date/set-storage-format.mdx | 319 + .../content-controls/date/set-value.mdx | 319 + .../reference/content-controls/delete.mdx | 312 + .../content-controls/get-binding.mdx | 150 + .../content-controls/get-content.mdx | 135 + .../reference/content-controls/get-parent.mdx | 127 + .../content-controls/get-raw-properties.mdx | 126 + .../reference/content-controls/get.mdx | 114 + .../content-controls/group/ungroup.mdx | 313 + .../reference/content-controls/group/wrap.mdx | 312 + .../reference/content-controls/index.mdx | 72 + .../content-controls/insert-after.mdx | 326 + .../content-controls/insert-before.mdx | 326 + .../content-controls/list-children.mdx | 147 + .../content-controls/list-in-range.mdx | 126 + .../reference/content-controls/list.mdx | 120 + .../reference/content-controls/move.mdx | 345 + .../normalize-tag-payload.mdx | 309 + .../normalize-word-compatibility.mdx | 309 + .../content-controls/patch-raw-properties.mdx | 320 + .../reference/content-controls/patch.mdx | 346 + .../content-controls/prepend-content.mdx | 326 + .../repeating-section/clone-item.mdx | 319 + .../repeating-section/delete-item.mdx | 319 + .../repeating-section/insert-item-after.mdx | 319 + .../repeating-section/insert-item-before.mdx | 319 + .../repeating-section/list-items.mdx | 138 + .../set-allow-insert-delete.mdx | 319 + .../content-controls/replace-content.mdx | 326 + .../content-controls/select-by-tag.mdx | 120 + .../content-controls/select-by-title.mdx | 120 + .../content-controls/set-binding.mdx | 329 + .../content-controls/set-lock-mode.mdx | 323 + .../reference/content-controls/set-type.mdx | 319 + .../content-controls/text/clear-value.mdx | 313 + .../content-controls/text/set-multiline.mdx | 319 + .../content-controls/text/set-value.mdx | 319 + .../reference/content-controls/unwrap.mdx | 312 + .../validate-word-compatibility.mdx | 137 + .../reference/content-controls/wrap.mdx | 340 + apps/docs/document-api/reference/index.mdx | 61 + apps/docs/document-engine/sdks.mdx | 110 + .../src/content-controls/content-controls.ts | 652 ++ .../content-controls.types.ts | 534 ++ .../src/contract/contract.test.ts | 1 + .../src/contract/metadata-types.ts | 3 + .../src/contract/operation-definitions.ts | 827 ++- .../src/contract/operation-registry.ts | 356 + .../src/contract/reference-doc-map.ts | 5 + packages/document-api/src/contract/schemas.ts | 421 +- packages/document-api/src/create/create.ts | 5 + packages/document-api/src/index.ts | 254 + packages/document-api/src/invoke/invoke.ts | 84 + packages/document-api/src/types/node.ts | 4 +- packages/document-api/src/types/receipt.ts | 5 +- .../src/types/structured.types.ts | 16 +- .../core/super-converter/SuperConverter.js | 4 + .../helpers/handle-structured-content-node.js | 58 +- .../helpers/translate-structured-content.js | 33 +- .../contract-conformance.test.ts | 1341 +++- .../assemble-adapters.ts | 5 + .../src/document-api-adapters/errors.ts | 7 +- .../helpers/content-controls/index.ts | 46 + .../content-controls/lock-enforcement.ts | 70 + .../content-controls/result-builders.ts | 39 + .../content-controls/sdt-info-builder.ts | 261 + .../content-controls/sdt-properties-write.ts | 209 + .../content-controls/target-resolution.ts | 74 + .../helpers/node-info-mapper.ts | 17 +- .../content-controls-wrappers.test.ts | 717 ++ .../plan-engine/content-controls-wrappers.ts | 1514 ++++ .../structured-content-block.js | 28 + .../structured-content/structured-content.js | 28 + .../src/extensions/types/node-attributes.ts | 10 + .../tests/content-controls/all-commands.ts | 2109 ++++++ tests/doc-api-stories/tests/harness.ts | 63 +- 98 files changed, 29515 insertions(+), 1839 deletions(-) create mode 100644 apps/docs/document-api/reference/content-controls/append-content.mdx create mode 100644 apps/docs/document-api/reference/content-controls/checkbox/get-state.mdx create mode 100644 apps/docs/document-api/reference/content-controls/checkbox/set-state.mdx create mode 100644 apps/docs/document-api/reference/content-controls/checkbox/set-symbol-pair.mdx create mode 100644 apps/docs/document-api/reference/content-controls/checkbox/toggle.mdx create mode 100644 apps/docs/document-api/reference/content-controls/choice-list/get-items.mdx create mode 100644 apps/docs/document-api/reference/content-controls/choice-list/set-items.mdx create mode 100644 apps/docs/document-api/reference/content-controls/choice-list/set-selected.mdx create mode 100644 apps/docs/document-api/reference/content-controls/clear-binding.mdx create mode 100644 apps/docs/document-api/reference/content-controls/clear-content.mdx create mode 100644 apps/docs/document-api/reference/content-controls/copy.mdx create mode 100644 apps/docs/document-api/reference/content-controls/create.mdx create mode 100644 apps/docs/document-api/reference/content-controls/date/clear-value.mdx create mode 100644 apps/docs/document-api/reference/content-controls/date/set-calendar.mdx create mode 100644 apps/docs/document-api/reference/content-controls/date/set-display-format.mdx create mode 100644 apps/docs/document-api/reference/content-controls/date/set-display-locale.mdx create mode 100644 apps/docs/document-api/reference/content-controls/date/set-storage-format.mdx create mode 100644 apps/docs/document-api/reference/content-controls/date/set-value.mdx create mode 100644 apps/docs/document-api/reference/content-controls/delete.mdx create mode 100644 apps/docs/document-api/reference/content-controls/get-binding.mdx create mode 100644 apps/docs/document-api/reference/content-controls/get-content.mdx create mode 100644 apps/docs/document-api/reference/content-controls/get-parent.mdx create mode 100644 apps/docs/document-api/reference/content-controls/get-raw-properties.mdx create mode 100644 apps/docs/document-api/reference/content-controls/get.mdx create mode 100644 apps/docs/document-api/reference/content-controls/group/ungroup.mdx create mode 100644 apps/docs/document-api/reference/content-controls/group/wrap.mdx create mode 100644 apps/docs/document-api/reference/content-controls/index.mdx create mode 100644 apps/docs/document-api/reference/content-controls/insert-after.mdx create mode 100644 apps/docs/document-api/reference/content-controls/insert-before.mdx create mode 100644 apps/docs/document-api/reference/content-controls/list-children.mdx create mode 100644 apps/docs/document-api/reference/content-controls/list-in-range.mdx create mode 100644 apps/docs/document-api/reference/content-controls/list.mdx create mode 100644 apps/docs/document-api/reference/content-controls/move.mdx create mode 100644 apps/docs/document-api/reference/content-controls/normalize-tag-payload.mdx create mode 100644 apps/docs/document-api/reference/content-controls/normalize-word-compatibility.mdx create mode 100644 apps/docs/document-api/reference/content-controls/patch-raw-properties.mdx create mode 100644 apps/docs/document-api/reference/content-controls/patch.mdx create mode 100644 apps/docs/document-api/reference/content-controls/prepend-content.mdx create mode 100644 apps/docs/document-api/reference/content-controls/repeating-section/clone-item.mdx create mode 100644 apps/docs/document-api/reference/content-controls/repeating-section/delete-item.mdx create mode 100644 apps/docs/document-api/reference/content-controls/repeating-section/insert-item-after.mdx create mode 100644 apps/docs/document-api/reference/content-controls/repeating-section/insert-item-before.mdx create mode 100644 apps/docs/document-api/reference/content-controls/repeating-section/list-items.mdx create mode 100644 apps/docs/document-api/reference/content-controls/repeating-section/set-allow-insert-delete.mdx create mode 100644 apps/docs/document-api/reference/content-controls/replace-content.mdx create mode 100644 apps/docs/document-api/reference/content-controls/select-by-tag.mdx create mode 100644 apps/docs/document-api/reference/content-controls/select-by-title.mdx create mode 100644 apps/docs/document-api/reference/content-controls/set-binding.mdx create mode 100644 apps/docs/document-api/reference/content-controls/set-lock-mode.mdx create mode 100644 apps/docs/document-api/reference/content-controls/set-type.mdx create mode 100644 apps/docs/document-api/reference/content-controls/text/clear-value.mdx create mode 100644 apps/docs/document-api/reference/content-controls/text/set-multiline.mdx create mode 100644 apps/docs/document-api/reference/content-controls/text/set-value.mdx create mode 100644 apps/docs/document-api/reference/content-controls/unwrap.mdx create mode 100644 apps/docs/document-api/reference/content-controls/validate-word-compatibility.mdx create mode 100644 apps/docs/document-api/reference/content-controls/wrap.mdx create mode 100644 packages/document-api/src/content-controls/content-controls.ts create mode 100644 packages/document-api/src/content-controls/content-controls.types.ts create mode 100644 packages/super-editor/src/document-api-adapters/helpers/content-controls/index.ts create mode 100644 packages/super-editor/src/document-api-adapters/helpers/content-controls/lock-enforcement.ts create mode 100644 packages/super-editor/src/document-api-adapters/helpers/content-controls/result-builders.ts create mode 100644 packages/super-editor/src/document-api-adapters/helpers/content-controls/sdt-info-builder.ts create mode 100644 packages/super-editor/src/document-api-adapters/helpers/content-controls/sdt-properties-write.ts create mode 100644 packages/super-editor/src/document-api-adapters/helpers/content-controls/target-resolution.ts create mode 100644 packages/super-editor/src/document-api-adapters/plan-engine/content-controls-wrappers.test.ts create mode 100644 packages/super-editor/src/document-api-adapters/plan-engine/content-controls-wrappers.ts create mode 100644 tests/doc-api-stories/tests/content-controls/all-commands.ts diff --git a/apps/cli/scripts/export-sdk-contract.ts b/apps/cli/scripts/export-sdk-contract.ts index b392dd6b6b..6361f416c6 100644 --- a/apps/cli/scripts/export-sdk-contract.ts +++ b/apps/cli/scripts/export-sdk-contract.ts @@ -45,7 +45,6 @@ const CLI_PKG_PATH = resolve(CLI_DIR, 'package.json'); // --------------------------------------------------------------------------- // Intent names โ€” human-friendly tool names for doc-backed operations only. // CLI-only intent names live in CLI_ONLY_OPERATION_DEFINITIONS. -// Typed exhaustively: missing entry = compile error. // --------------------------------------------------------------------------- const INTENT_NAMES = { @@ -243,7 +242,17 @@ const INTENT_NAMES = { 'doc.images.insertCaption': 'insert_image_caption', 'doc.images.updateCaption': 'update_image_caption', 'doc.images.removeCaption': 'remove_image_caption', -} as const satisfies Record; +} as const satisfies Partial>; + +function deriveDocBackedIntentName(cliOpId: DocBackedCliOpId): string { + const mapped = INTENT_NAMES[cliOpId]; + if (mapped) { + return mapped; + } + + const docApiId = cliOpId.slice(4); + return docApiId.replace(/[A-Z]/g, (char) => `_${char.toLowerCase()}`).replace(/\./g, '_'); +} // --------------------------------------------------------------------------- // Load inputs @@ -282,7 +291,7 @@ function buildSdkContract() { // Resolve intentName: doc-backed from INTENT_NAMES, CLI-only from definitions const cliOnlyDef = docApiId ? null : CLI_ONLY_OPERATION_DEFINITIONS[stripped]; - const intentName = docApiId ? INTENT_NAMES[cliOpId as DocBackedCliOpId] : cliOnlyDef!.intentName; + const intentName = docApiId ? deriveDocBackedIntentName(cliOpId as DocBackedCliOpId) : cliOnlyDef?.intentName; if (!intentName) { throw new Error(`Missing intentName for ${cliOpId}`); } diff --git a/apps/cli/src/__tests__/conformance/scenarios.ts b/apps/cli/src/__tests__/conformance/scenarios.ts index 83895a21e5..a420cfbde5 100644 --- a/apps/cli/src/__tests__/conformance/scenarios.ts +++ b/apps/cli/src/__tests__/conformance/scenarios.ts @@ -29,6 +29,15 @@ function genericInvalidArgumentFailure(operationId: CliOperationId) { }); } +function skippedSuccessScenario(operationId: CliOperationId) { + return async (harness: ConformanceHarness): Promise => ({ + stateDir: await harness.createStateDir(`${operationId}-skipped-success`), + args: ['status'], + }); +} + +type SuccessScenarioFactory = (harness: ConformanceHarness) => Promise; + function extractDiscoveryItems(data: unknown): Record[] { if (!data || typeof data !== 'object') return []; @@ -3020,9 +3029,9 @@ export const SUCCESS_SCENARIOS = { await harness.openSessionFixture(stateDir, 'doc-history-redo', 'history-redo-session'); return { stateDir, args: ['history', 'redo', '--session', 'history-redo-session'] }; }, -} as const satisfies Record Promise>; +} as const satisfies Partial>; -const RUNTIME_CONFORMANCE_SKIP = new Set([ +const EXPLICIT_RUNTIME_CONFORMANCE_SKIP = new Set([ 'doc.toc.markEntry', 'doc.toc.unmarkEntry', 'doc.toc.getEntry', @@ -3041,10 +3050,21 @@ const RUNTIME_CONFORMANCE_SKIP = new Set([ 'doc.images.removeCaption', ]); -export const OPERATION_SCENARIOS = (Object.keys(SUCCESS_SCENARIOS) as CliOperationId[]).map((operationId) => { +const CANONICAL_OPERATION_IDS = Object.keys(CLI_OPERATION_COMMAND_KEYS) as CliOperationId[]; +const AUTO_SKIPPED_OPERATION_IDS = CANONICAL_OPERATION_IDS.filter( + (operationId) => SUCCESS_SCENARIOS[operationId] == null, +); + +const RUNTIME_CONFORMANCE_SKIP = new Set([ + ...EXPLICIT_RUNTIME_CONFORMANCE_SKIP, + ...AUTO_SKIPPED_OPERATION_IDS, +]); + +export const OPERATION_SCENARIOS = CANONICAL_OPERATION_IDS.map((operationId) => { + const success = SUCCESS_SCENARIOS[operationId] ?? skippedSuccessScenario(operationId); const scenario: OperationScenario = { operationId, - success: SUCCESS_SCENARIOS[operationId], + success, failure: genericInvalidArgumentFailure(operationId), expectedFailureCodes: ['INVALID_ARGUMENT', 'MISSING_REQUIRED'], ...(RUNTIME_CONFORMANCE_SKIP.has(operationId) ? { skipRuntimeConformance: true } : {}), diff --git a/apps/cli/src/lib/operation-args.ts b/apps/cli/src/lib/operation-args.ts index eeb5d05e3b..30000d9c1a 100644 --- a/apps/cli/src/lib/operation-args.ts +++ b/apps/cli/src/lib/operation-args.ts @@ -146,14 +146,21 @@ export function validateValueAgainstTypeSpec(value: unknown, schema: CliTypeSpec } } - const knownKeys = new Set(Object.keys(schema.properties)); - for (const key of Object.keys(value)) { - if (!knownKeys.has(key)) { - throw new CliError('VALIDATION_ERROR', `${path}.${key} is not allowed by schema.`); + const propertyEntries = Object.entries(schema.properties); + const shouldRestrictUnknownKeys = propertyEntries.length > 0 || required.length > 0; + + // If no object fields are declared, treat it as an unconstrained JSON object. + // This keeps input validation aligned with generated schemas like `{ type: 'object' }`. + if (shouldRestrictUnknownKeys) { + const knownKeys = new Set(propertyEntries.map(([key]) => key)); + for (const key of Object.keys(value)) { + if (!knownKeys.has(key)) { + throw new CliError('VALIDATION_ERROR', `${path}.${key} is not allowed by schema.`); + } } } - for (const [key, propSchema] of Object.entries(schema.properties)) { + for (const [key, propSchema] of propertyEntries) { if (!Object.prototype.hasOwnProperty.call(value, key)) continue; validateValueAgainstTypeSpec(value[key], propSchema, `${path}.${key}`); } diff --git a/apps/docs/document-api/available-operations.mdx b/apps/docs/document-api/available-operations.mdx index d345a1365e..02744e49ca 100644 --- a/apps/docs/document-api/available-operations.mdx +++ b/apps/docs/document-api/available-operations.mdx @@ -17,6 +17,7 @@ Use the tables below to see what operations are available and where each one is | Blocks | 1 | 0 | 1 | [Reference](/document-api/reference/blocks/index) | | Capabilities | 1 | 0 | 1 | [Reference](/document-api/reference/capabilities/index) | | Comments | 5 | 0 | 5 | [Reference](/document-api/reference/comments/index) | +| Content Controls | 55 | 0 | 55 | [Reference](/document-api/reference/content-controls/index) | | Core | 13 | 0 | 13 | [Reference](/document-api/reference/core/index) | | Create | 6 | 0 | 6 | [Reference](/document-api/reference/create/index) | | Format | 44 | 1 | 45 | [Reference](/document-api/reference/format/index) | @@ -43,6 +44,61 @@ Use the tables below to see what operations are available and where each one is | editor.doc.comments.delete(...) | [`comments.delete`](/document-api/reference/comments/delete) | | editor.doc.comments.get(...) | [`comments.get`](/document-api/reference/comments/get) | | editor.doc.comments.list(...) | [`comments.list`](/document-api/reference/comments/list) | +| editor.doc.create.contentControl(...) | [`create.contentControl`](/document-api/reference/content-controls/create) | +| editor.doc.contentControls.list(...) | [`contentControls.list`](/document-api/reference/content-controls/list) | +| editor.doc.contentControls.get(...) | [`contentControls.get`](/document-api/reference/content-controls/get) | +| editor.doc.contentControls.listInRange(...) | [`contentControls.listInRange`](/document-api/reference/content-controls/list-in-range) | +| editor.doc.contentControls.selectByTag(...) | [`contentControls.selectByTag`](/document-api/reference/content-controls/select-by-tag) | +| editor.doc.contentControls.selectByTitle(...) | [`contentControls.selectByTitle`](/document-api/reference/content-controls/select-by-title) | +| editor.doc.contentControls.listChildren(...) | [`contentControls.listChildren`](/document-api/reference/content-controls/list-children) | +| editor.doc.contentControls.getParent(...) | [`contentControls.getParent`](/document-api/reference/content-controls/get-parent) | +| editor.doc.contentControls.wrap(...) | [`contentControls.wrap`](/document-api/reference/content-controls/wrap) | +| editor.doc.contentControls.unwrap(...) | [`contentControls.unwrap`](/document-api/reference/content-controls/unwrap) | +| editor.doc.contentControls.delete(...) | [`contentControls.delete`](/document-api/reference/content-controls/delete) | +| editor.doc.contentControls.copy(...) | [`contentControls.copy`](/document-api/reference/content-controls/copy) | +| editor.doc.contentControls.move(...) | [`contentControls.move`](/document-api/reference/content-controls/move) | +| editor.doc.contentControls.patch(...) | [`contentControls.patch`](/document-api/reference/content-controls/patch) | +| editor.doc.contentControls.setLockMode(...) | [`contentControls.setLockMode`](/document-api/reference/content-controls/set-lock-mode) | +| editor.doc.contentControls.setType(...) | [`contentControls.setType`](/document-api/reference/content-controls/set-type) | +| editor.doc.contentControls.getContent(...) | [`contentControls.getContent`](/document-api/reference/content-controls/get-content) | +| editor.doc.contentControls.replaceContent(...) | [`contentControls.replaceContent`](/document-api/reference/content-controls/replace-content) | +| editor.doc.contentControls.clearContent(...) | [`contentControls.clearContent`](/document-api/reference/content-controls/clear-content) | +| editor.doc.contentControls.appendContent(...) | [`contentControls.appendContent`](/document-api/reference/content-controls/append-content) | +| editor.doc.contentControls.prependContent(...) | [`contentControls.prependContent`](/document-api/reference/content-controls/prepend-content) | +| editor.doc.contentControls.insertBefore(...) | [`contentControls.insertBefore`](/document-api/reference/content-controls/insert-before) | +| editor.doc.contentControls.insertAfter(...) | [`contentControls.insertAfter`](/document-api/reference/content-controls/insert-after) | +| editor.doc.contentControls.getBinding(...) | [`contentControls.getBinding`](/document-api/reference/content-controls/get-binding) | +| editor.doc.contentControls.setBinding(...) | [`contentControls.setBinding`](/document-api/reference/content-controls/set-binding) | +| editor.doc.contentControls.clearBinding(...) | [`contentControls.clearBinding`](/document-api/reference/content-controls/clear-binding) | +| editor.doc.contentControls.getRawProperties(...) | [`contentControls.getRawProperties`](/document-api/reference/content-controls/get-raw-properties) | +| editor.doc.contentControls.patchRawProperties(...) | [`contentControls.patchRawProperties`](/document-api/reference/content-controls/patch-raw-properties) | +| editor.doc.contentControls.validateWordCompatibility(...) | [`contentControls.validateWordCompatibility`](/document-api/reference/content-controls/validate-word-compatibility) | +| editor.doc.contentControls.normalizeWordCompatibility(...) | [`contentControls.normalizeWordCompatibility`](/document-api/reference/content-controls/normalize-word-compatibility) | +| editor.doc.contentControls.normalizeTagPayload(...) | [`contentControls.normalizeTagPayload`](/document-api/reference/content-controls/normalize-tag-payload) | +| editor.doc.contentControls.text.setMultiline(...) | [`contentControls.text.setMultiline`](/document-api/reference/content-controls/text/set-multiline) | +| editor.doc.contentControls.text.setValue(...) | [`contentControls.text.setValue`](/document-api/reference/content-controls/text/set-value) | +| editor.doc.contentControls.text.clearValue(...) | [`contentControls.text.clearValue`](/document-api/reference/content-controls/text/clear-value) | +| editor.doc.contentControls.date.setValue(...) | [`contentControls.date.setValue`](/document-api/reference/content-controls/date/set-value) | +| editor.doc.contentControls.date.clearValue(...) | [`contentControls.date.clearValue`](/document-api/reference/content-controls/date/clear-value) | +| editor.doc.contentControls.date.setDisplayFormat(...) | [`contentControls.date.setDisplayFormat`](/document-api/reference/content-controls/date/set-display-format) | +| editor.doc.contentControls.date.setDisplayLocale(...) | [`contentControls.date.setDisplayLocale`](/document-api/reference/content-controls/date/set-display-locale) | +| editor.doc.contentControls.date.setStorageFormat(...) | [`contentControls.date.setStorageFormat`](/document-api/reference/content-controls/date/set-storage-format) | +| editor.doc.contentControls.date.setCalendar(...) | [`contentControls.date.setCalendar`](/document-api/reference/content-controls/date/set-calendar) | +| editor.doc.contentControls.checkbox.getState(...) | [`contentControls.checkbox.getState`](/document-api/reference/content-controls/checkbox/get-state) | +| editor.doc.contentControls.checkbox.setState(...) | [`contentControls.checkbox.setState`](/document-api/reference/content-controls/checkbox/set-state) | +| editor.doc.contentControls.checkbox.toggle(...) | [`contentControls.checkbox.toggle`](/document-api/reference/content-controls/checkbox/toggle) | +| editor.doc.contentControls.checkbox.setSymbolPair(...) | [`contentControls.checkbox.setSymbolPair`](/document-api/reference/content-controls/checkbox/set-symbol-pair) | +| editor.doc.contentControls.choiceList.getItems(...) | [`contentControls.choiceList.getItems`](/document-api/reference/content-controls/choice-list/get-items) | +| editor.doc.contentControls.choiceList.setItems(...) | [`contentControls.choiceList.setItems`](/document-api/reference/content-controls/choice-list/set-items) | +| editor.doc.contentControls.choiceList.setSelected(...) | [`contentControls.choiceList.setSelected`](/document-api/reference/content-controls/choice-list/set-selected) | +| editor.doc.contentControls.repeatingSection.listItems(...) | [`contentControls.repeatingSection.listItems`](/document-api/reference/content-controls/repeating-section/list-items) | +| editor.doc.contentControls.repeatingSection.insertItemBefore(...) | [`contentControls.repeatingSection.insertItemBefore`](/document-api/reference/content-controls/repeating-section/insert-item-before) | +| editor.doc.contentControls.repeatingSection.insertItemAfter(...) | [`contentControls.repeatingSection.insertItemAfter`](/document-api/reference/content-controls/repeating-section/insert-item-after) | +| editor.doc.contentControls.repeatingSection.cloneItem(...) | [`contentControls.repeatingSection.cloneItem`](/document-api/reference/content-controls/repeating-section/clone-item) | +| editor.doc.contentControls.repeatingSection.deleteItem(...) | [`contentControls.repeatingSection.deleteItem`](/document-api/reference/content-controls/repeating-section/delete-item) | +| editor.doc.contentControls.repeatingSection.setAllowInsertDelete(...) | [`contentControls.repeatingSection.setAllowInsertDelete`](/document-api/reference/content-controls/repeating-section/set-allow-insert-delete) | +| editor.doc.contentControls.group.wrap(...) | [`contentControls.group.wrap`](/document-api/reference/content-controls/group/wrap) | +| editor.doc.contentControls.group.ungroup(...) | [`contentControls.group.ungroup`](/document-api/reference/content-controls/group/ungroup) | | editor.doc.get(...) | [`get`](/document-api/reference/get) | | editor.doc.find(...) | [`find`](/document-api/reference/find) | | editor.doc.getNode(...) | [`getNode`](/document-api/reference/get-node) | diff --git a/apps/docs/document-api/reference/_generated-manifest.json b/apps/docs/document-api/reference/_generated-manifest.json index 95ea86c205..33483d99c6 100644 --- a/apps/docs/document-api/reference/_generated-manifest.json +++ b/apps/docs/document-api/reference/_generated-manifest.json @@ -12,6 +12,62 @@ "apps/docs/document-api/reference/comments/index.mdx", "apps/docs/document-api/reference/comments/list.mdx", "apps/docs/document-api/reference/comments/patch.mdx", + "apps/docs/document-api/reference/content-controls/append-content.mdx", + "apps/docs/document-api/reference/content-controls/checkbox/get-state.mdx", + "apps/docs/document-api/reference/content-controls/checkbox/set-state.mdx", + "apps/docs/document-api/reference/content-controls/checkbox/set-symbol-pair.mdx", + "apps/docs/document-api/reference/content-controls/checkbox/toggle.mdx", + "apps/docs/document-api/reference/content-controls/choice-list/get-items.mdx", + "apps/docs/document-api/reference/content-controls/choice-list/set-items.mdx", + "apps/docs/document-api/reference/content-controls/choice-list/set-selected.mdx", + "apps/docs/document-api/reference/content-controls/clear-binding.mdx", + "apps/docs/document-api/reference/content-controls/clear-content.mdx", + "apps/docs/document-api/reference/content-controls/copy.mdx", + "apps/docs/document-api/reference/content-controls/create.mdx", + "apps/docs/document-api/reference/content-controls/date/clear-value.mdx", + "apps/docs/document-api/reference/content-controls/date/set-calendar.mdx", + "apps/docs/document-api/reference/content-controls/date/set-display-format.mdx", + "apps/docs/document-api/reference/content-controls/date/set-display-locale.mdx", + "apps/docs/document-api/reference/content-controls/date/set-storage-format.mdx", + "apps/docs/document-api/reference/content-controls/date/set-value.mdx", + "apps/docs/document-api/reference/content-controls/delete.mdx", + "apps/docs/document-api/reference/content-controls/get-binding.mdx", + "apps/docs/document-api/reference/content-controls/get-content.mdx", + "apps/docs/document-api/reference/content-controls/get-parent.mdx", + "apps/docs/document-api/reference/content-controls/get-raw-properties.mdx", + "apps/docs/document-api/reference/content-controls/get.mdx", + "apps/docs/document-api/reference/content-controls/group/ungroup.mdx", + "apps/docs/document-api/reference/content-controls/group/wrap.mdx", + "apps/docs/document-api/reference/content-controls/index.mdx", + "apps/docs/document-api/reference/content-controls/insert-after.mdx", + "apps/docs/document-api/reference/content-controls/insert-before.mdx", + "apps/docs/document-api/reference/content-controls/list-children.mdx", + "apps/docs/document-api/reference/content-controls/list-in-range.mdx", + "apps/docs/document-api/reference/content-controls/list.mdx", + "apps/docs/document-api/reference/content-controls/move.mdx", + "apps/docs/document-api/reference/content-controls/normalize-tag-payload.mdx", + "apps/docs/document-api/reference/content-controls/normalize-word-compatibility.mdx", + "apps/docs/document-api/reference/content-controls/patch-raw-properties.mdx", + "apps/docs/document-api/reference/content-controls/patch.mdx", + "apps/docs/document-api/reference/content-controls/prepend-content.mdx", + "apps/docs/document-api/reference/content-controls/repeating-section/clone-item.mdx", + "apps/docs/document-api/reference/content-controls/repeating-section/delete-item.mdx", + "apps/docs/document-api/reference/content-controls/repeating-section/insert-item-after.mdx", + "apps/docs/document-api/reference/content-controls/repeating-section/insert-item-before.mdx", + "apps/docs/document-api/reference/content-controls/repeating-section/list-items.mdx", + "apps/docs/document-api/reference/content-controls/repeating-section/set-allow-insert-delete.mdx", + "apps/docs/document-api/reference/content-controls/replace-content.mdx", + "apps/docs/document-api/reference/content-controls/select-by-tag.mdx", + "apps/docs/document-api/reference/content-controls/select-by-title.mdx", + "apps/docs/document-api/reference/content-controls/set-binding.mdx", + "apps/docs/document-api/reference/content-controls/set-lock-mode.mdx", + "apps/docs/document-api/reference/content-controls/set-type.mdx", + "apps/docs/document-api/reference/content-controls/text/clear-value.mdx", + "apps/docs/document-api/reference/content-controls/text/set-multiline.mdx", + "apps/docs/document-api/reference/content-controls/text/set-value.mdx", + "apps/docs/document-api/reference/content-controls/unwrap.mdx", + "apps/docs/document-api/reference/content-controls/validate-word-compatibility.mdx", + "apps/docs/document-api/reference/content-controls/wrap.mdx", "apps/docs/document-api/reference/core/index.mdx", "apps/docs/document-api/reference/create/heading.mdx", "apps/docs/document-api/reference/create/image.mdx", @@ -609,8 +665,71 @@ ], "pagePath": "apps/docs/document-api/reference/hyperlinks/index.mdx", "title": "Hyperlinks" + }, + { + "aliasMemberPaths": [], + "key": "contentControls", + "operationIds": [ + "create.contentControl", + "contentControls.list", + "contentControls.get", + "contentControls.listInRange", + "contentControls.selectByTag", + "contentControls.selectByTitle", + "contentControls.listChildren", + "contentControls.getParent", + "contentControls.wrap", + "contentControls.unwrap", + "contentControls.delete", + "contentControls.copy", + "contentControls.move", + "contentControls.patch", + "contentControls.setLockMode", + "contentControls.setType", + "contentControls.getContent", + "contentControls.replaceContent", + "contentControls.clearContent", + "contentControls.appendContent", + "contentControls.prependContent", + "contentControls.insertBefore", + "contentControls.insertAfter", + "contentControls.getBinding", + "contentControls.setBinding", + "contentControls.clearBinding", + "contentControls.getRawProperties", + "contentControls.patchRawProperties", + "contentControls.validateWordCompatibility", + "contentControls.normalizeWordCompatibility", + "contentControls.normalizeTagPayload", + "contentControls.text.setMultiline", + "contentControls.text.setValue", + "contentControls.text.clearValue", + "contentControls.date.setValue", + "contentControls.date.clearValue", + "contentControls.date.setDisplayFormat", + "contentControls.date.setDisplayLocale", + "contentControls.date.setStorageFormat", + "contentControls.date.setCalendar", + "contentControls.checkbox.getState", + "contentControls.checkbox.setState", + "contentControls.checkbox.toggle", + "contentControls.checkbox.setSymbolPair", + "contentControls.choiceList.getItems", + "contentControls.choiceList.setItems", + "contentControls.choiceList.setSelected", + "contentControls.repeatingSection.listItems", + "contentControls.repeatingSection.insertItemBefore", + "contentControls.repeatingSection.insertItemAfter", + "contentControls.repeatingSection.cloneItem", + "contentControls.repeatingSection.deleteItem", + "contentControls.repeatingSection.setAllowInsertDelete", + "contentControls.group.wrap", + "contentControls.group.ungroup" + ], + "pagePath": "apps/docs/document-api/reference/content-controls/index.mdx", + "title": "Content Controls" } ], "marker": "{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */}", - "sourceHash": "abbf45ec937fd52a118ca97834d6f49169eb012199f7af0c3b7e995df4608429" + "sourceHash": "be39679bf2549ffc2b4047d2622fc748a92c6a2b0004e7228c564dcd8504601e" } diff --git a/apps/docs/document-api/reference/capabilities/get.mdx b/apps/docs/document-api/reference/capabilities/get.mdx index 328fc03752..00da863daa 100644 --- a/apps/docs/document-api/reference/capabilities/get.mdx +++ b/apps/docs/document-api/reference/capabilities/get.mdx @@ -312,6 +312,281 @@ _No fields._ | `operations.comments.patch.dryRun` | boolean | yes | | | `operations.comments.patch.reasons` | enum[] | no | | | `operations.comments.patch.tracked` | boolean | yes | | +| `operations.contentControls.appendContent` | object | yes | | +| `operations.contentControls.appendContent.available` | boolean | yes | | +| `operations.contentControls.appendContent.dryRun` | boolean | yes | | +| `operations.contentControls.appendContent.reasons` | enum[] | no | | +| `operations.contentControls.appendContent.tracked` | boolean | yes | | +| `operations.contentControls.checkbox.getState` | object | yes | | +| `operations.contentControls.checkbox.getState.available` | boolean | yes | | +| `operations.contentControls.checkbox.getState.dryRun` | boolean | yes | | +| `operations.contentControls.checkbox.getState.reasons` | enum[] | no | | +| `operations.contentControls.checkbox.getState.tracked` | boolean | yes | | +| `operations.contentControls.checkbox.setState` | object | yes | | +| `operations.contentControls.checkbox.setState.available` | boolean | yes | | +| `operations.contentControls.checkbox.setState.dryRun` | boolean | yes | | +| `operations.contentControls.checkbox.setState.reasons` | enum[] | no | | +| `operations.contentControls.checkbox.setState.tracked` | boolean | yes | | +| `operations.contentControls.checkbox.setSymbolPair` | object | yes | | +| `operations.contentControls.checkbox.setSymbolPair.available` | boolean | yes | | +| `operations.contentControls.checkbox.setSymbolPair.dryRun` | boolean | yes | | +| `operations.contentControls.checkbox.setSymbolPair.reasons` | enum[] | no | | +| `operations.contentControls.checkbox.setSymbolPair.tracked` | boolean | yes | | +| `operations.contentControls.checkbox.toggle` | object | yes | | +| `operations.contentControls.checkbox.toggle.available` | boolean | yes | | +| `operations.contentControls.checkbox.toggle.dryRun` | boolean | yes | | +| `operations.contentControls.checkbox.toggle.reasons` | enum[] | no | | +| `operations.contentControls.checkbox.toggle.tracked` | boolean | yes | | +| `operations.contentControls.choiceList.getItems` | object | yes | | +| `operations.contentControls.choiceList.getItems.available` | boolean | yes | | +| `operations.contentControls.choiceList.getItems.dryRun` | boolean | yes | | +| `operations.contentControls.choiceList.getItems.reasons` | enum[] | no | | +| `operations.contentControls.choiceList.getItems.tracked` | boolean | yes | | +| `operations.contentControls.choiceList.setItems` | object | yes | | +| `operations.contentControls.choiceList.setItems.available` | boolean | yes | | +| `operations.contentControls.choiceList.setItems.dryRun` | boolean | yes | | +| `operations.contentControls.choiceList.setItems.reasons` | enum[] | no | | +| `operations.contentControls.choiceList.setItems.tracked` | boolean | yes | | +| `operations.contentControls.choiceList.setSelected` | object | yes | | +| `operations.contentControls.choiceList.setSelected.available` | boolean | yes | | +| `operations.contentControls.choiceList.setSelected.dryRun` | boolean | yes | | +| `operations.contentControls.choiceList.setSelected.reasons` | enum[] | no | | +| `operations.contentControls.choiceList.setSelected.tracked` | boolean | yes | | +| `operations.contentControls.clearBinding` | object | yes | | +| `operations.contentControls.clearBinding.available` | boolean | yes | | +| `operations.contentControls.clearBinding.dryRun` | boolean | yes | | +| `operations.contentControls.clearBinding.reasons` | enum[] | no | | +| `operations.contentControls.clearBinding.tracked` | boolean | yes | | +| `operations.contentControls.clearContent` | object | yes | | +| `operations.contentControls.clearContent.available` | boolean | yes | | +| `operations.contentControls.clearContent.dryRun` | boolean | yes | | +| `operations.contentControls.clearContent.reasons` | enum[] | no | | +| `operations.contentControls.clearContent.tracked` | boolean | yes | | +| `operations.contentControls.copy` | object | yes | | +| `operations.contentControls.copy.available` | boolean | yes | | +| `operations.contentControls.copy.dryRun` | boolean | yes | | +| `operations.contentControls.copy.reasons` | enum[] | no | | +| `operations.contentControls.copy.tracked` | boolean | yes | | +| `operations.contentControls.date.clearValue` | object | yes | | +| `operations.contentControls.date.clearValue.available` | boolean | yes | | +| `operations.contentControls.date.clearValue.dryRun` | boolean | yes | | +| `operations.contentControls.date.clearValue.reasons` | enum[] | no | | +| `operations.contentControls.date.clearValue.tracked` | boolean | yes | | +| `operations.contentControls.date.setCalendar` | object | yes | | +| `operations.contentControls.date.setCalendar.available` | boolean | yes | | +| `operations.contentControls.date.setCalendar.dryRun` | boolean | yes | | +| `operations.contentControls.date.setCalendar.reasons` | enum[] | no | | +| `operations.contentControls.date.setCalendar.tracked` | boolean | yes | | +| `operations.contentControls.date.setDisplayFormat` | object | yes | | +| `operations.contentControls.date.setDisplayFormat.available` | boolean | yes | | +| `operations.contentControls.date.setDisplayFormat.dryRun` | boolean | yes | | +| `operations.contentControls.date.setDisplayFormat.reasons` | enum[] | no | | +| `operations.contentControls.date.setDisplayFormat.tracked` | boolean | yes | | +| `operations.contentControls.date.setDisplayLocale` | object | yes | | +| `operations.contentControls.date.setDisplayLocale.available` | boolean | yes | | +| `operations.contentControls.date.setDisplayLocale.dryRun` | boolean | yes | | +| `operations.contentControls.date.setDisplayLocale.reasons` | enum[] | no | | +| `operations.contentControls.date.setDisplayLocale.tracked` | boolean | yes | | +| `operations.contentControls.date.setStorageFormat` | object | yes | | +| `operations.contentControls.date.setStorageFormat.available` | boolean | yes | | +| `operations.contentControls.date.setStorageFormat.dryRun` | boolean | yes | | +| `operations.contentControls.date.setStorageFormat.reasons` | enum[] | no | | +| `operations.contentControls.date.setStorageFormat.tracked` | boolean | yes | | +| `operations.contentControls.date.setValue` | object | yes | | +| `operations.contentControls.date.setValue.available` | boolean | yes | | +| `operations.contentControls.date.setValue.dryRun` | boolean | yes | | +| `operations.contentControls.date.setValue.reasons` | enum[] | no | | +| `operations.contentControls.date.setValue.tracked` | boolean | yes | | +| `operations.contentControls.delete` | object | yes | | +| `operations.contentControls.delete.available` | boolean | yes | | +| `operations.contentControls.delete.dryRun` | boolean | yes | | +| `operations.contentControls.delete.reasons` | enum[] | no | | +| `operations.contentControls.delete.tracked` | boolean | yes | | +| `operations.contentControls.get` | object | yes | | +| `operations.contentControls.get.available` | boolean | yes | | +| `operations.contentControls.get.dryRun` | boolean | yes | | +| `operations.contentControls.get.reasons` | enum[] | no | | +| `operations.contentControls.get.tracked` | boolean | yes | | +| `operations.contentControls.getBinding` | object | yes | | +| `operations.contentControls.getBinding.available` | boolean | yes | | +| `operations.contentControls.getBinding.dryRun` | boolean | yes | | +| `operations.contentControls.getBinding.reasons` | enum[] | no | | +| `operations.contentControls.getBinding.tracked` | boolean | yes | | +| `operations.contentControls.getContent` | object | yes | | +| `operations.contentControls.getContent.available` | boolean | yes | | +| `operations.contentControls.getContent.dryRun` | boolean | yes | | +| `operations.contentControls.getContent.reasons` | enum[] | no | | +| `operations.contentControls.getContent.tracked` | boolean | yes | | +| `operations.contentControls.getParent` | object | yes | | +| `operations.contentControls.getParent.available` | boolean | yes | | +| `operations.contentControls.getParent.dryRun` | boolean | yes | | +| `operations.contentControls.getParent.reasons` | enum[] | no | | +| `operations.contentControls.getParent.tracked` | boolean | yes | | +| `operations.contentControls.getRawProperties` | object | yes | | +| `operations.contentControls.getRawProperties.available` | boolean | yes | | +| `operations.contentControls.getRawProperties.dryRun` | boolean | yes | | +| `operations.contentControls.getRawProperties.reasons` | enum[] | no | | +| `operations.contentControls.getRawProperties.tracked` | boolean | yes | | +| `operations.contentControls.group.ungroup` | object | yes | | +| `operations.contentControls.group.ungroup.available` | boolean | yes | | +| `operations.contentControls.group.ungroup.dryRun` | boolean | yes | | +| `operations.contentControls.group.ungroup.reasons` | enum[] | no | | +| `operations.contentControls.group.ungroup.tracked` | boolean | yes | | +| `operations.contentControls.group.wrap` | object | yes | | +| `operations.contentControls.group.wrap.available` | boolean | yes | | +| `operations.contentControls.group.wrap.dryRun` | boolean | yes | | +| `operations.contentControls.group.wrap.reasons` | enum[] | no | | +| `operations.contentControls.group.wrap.tracked` | boolean | yes | | +| `operations.contentControls.insertAfter` | object | yes | | +| `operations.contentControls.insertAfter.available` | boolean | yes | | +| `operations.contentControls.insertAfter.dryRun` | boolean | yes | | +| `operations.contentControls.insertAfter.reasons` | enum[] | no | | +| `operations.contentControls.insertAfter.tracked` | boolean | yes | | +| `operations.contentControls.insertBefore` | object | yes | | +| `operations.contentControls.insertBefore.available` | boolean | yes | | +| `operations.contentControls.insertBefore.dryRun` | boolean | yes | | +| `operations.contentControls.insertBefore.reasons` | enum[] | no | | +| `operations.contentControls.insertBefore.tracked` | boolean | yes | | +| `operations.contentControls.list` | object | yes | | +| `operations.contentControls.list.available` | boolean | yes | | +| `operations.contentControls.list.dryRun` | boolean | yes | | +| `operations.contentControls.list.reasons` | enum[] | no | | +| `operations.contentControls.list.tracked` | boolean | yes | | +| `operations.contentControls.listChildren` | object | yes | | +| `operations.contentControls.listChildren.available` | boolean | yes | | +| `operations.contentControls.listChildren.dryRun` | boolean | yes | | +| `operations.contentControls.listChildren.reasons` | enum[] | no | | +| `operations.contentControls.listChildren.tracked` | boolean | yes | | +| `operations.contentControls.listInRange` | object | yes | | +| `operations.contentControls.listInRange.available` | boolean | yes | | +| `operations.contentControls.listInRange.dryRun` | boolean | yes | | +| `operations.contentControls.listInRange.reasons` | enum[] | no | | +| `operations.contentControls.listInRange.tracked` | boolean | yes | | +| `operations.contentControls.move` | object | yes | | +| `operations.contentControls.move.available` | boolean | yes | | +| `operations.contentControls.move.dryRun` | boolean | yes | | +| `operations.contentControls.move.reasons` | enum[] | no | | +| `operations.contentControls.move.tracked` | boolean | yes | | +| `operations.contentControls.normalizeTagPayload` | object | yes | | +| `operations.contentControls.normalizeTagPayload.available` | boolean | yes | | +| `operations.contentControls.normalizeTagPayload.dryRun` | boolean | yes | | +| `operations.contentControls.normalizeTagPayload.reasons` | enum[] | no | | +| `operations.contentControls.normalizeTagPayload.tracked` | boolean | yes | | +| `operations.contentControls.normalizeWordCompatibility` | object | yes | | +| `operations.contentControls.normalizeWordCompatibility.available` | boolean | yes | | +| `operations.contentControls.normalizeWordCompatibility.dryRun` | boolean | yes | | +| `operations.contentControls.normalizeWordCompatibility.reasons` | enum[] | no | | +| `operations.contentControls.normalizeWordCompatibility.tracked` | boolean | yes | | +| `operations.contentControls.patch` | object | yes | | +| `operations.contentControls.patch.available` | boolean | yes | | +| `operations.contentControls.patch.dryRun` | boolean | yes | | +| `operations.contentControls.patch.reasons` | enum[] | no | | +| `operations.contentControls.patch.tracked` | boolean | yes | | +| `operations.contentControls.patchRawProperties` | object | yes | | +| `operations.contentControls.patchRawProperties.available` | boolean | yes | | +| `operations.contentControls.patchRawProperties.dryRun` | boolean | yes | | +| `operations.contentControls.patchRawProperties.reasons` | enum[] | no | | +| `operations.contentControls.patchRawProperties.tracked` | boolean | yes | | +| `operations.contentControls.prependContent` | object | yes | | +| `operations.contentControls.prependContent.available` | boolean | yes | | +| `operations.contentControls.prependContent.dryRun` | boolean | yes | | +| `operations.contentControls.prependContent.reasons` | enum[] | no | | +| `operations.contentControls.prependContent.tracked` | boolean | yes | | +| `operations.contentControls.repeatingSection.cloneItem` | object | yes | | +| `operations.contentControls.repeatingSection.cloneItem.available` | boolean | yes | | +| `operations.contentControls.repeatingSection.cloneItem.dryRun` | boolean | yes | | +| `operations.contentControls.repeatingSection.cloneItem.reasons` | enum[] | no | | +| `operations.contentControls.repeatingSection.cloneItem.tracked` | boolean | yes | | +| `operations.contentControls.repeatingSection.deleteItem` | object | yes | | +| `operations.contentControls.repeatingSection.deleteItem.available` | boolean | yes | | +| `operations.contentControls.repeatingSection.deleteItem.dryRun` | boolean | yes | | +| `operations.contentControls.repeatingSection.deleteItem.reasons` | enum[] | no | | +| `operations.contentControls.repeatingSection.deleteItem.tracked` | boolean | yes | | +| `operations.contentControls.repeatingSection.insertItemAfter` | object | yes | | +| `operations.contentControls.repeatingSection.insertItemAfter.available` | boolean | yes | | +| `operations.contentControls.repeatingSection.insertItemAfter.dryRun` | boolean | yes | | +| `operations.contentControls.repeatingSection.insertItemAfter.reasons` | enum[] | no | | +| `operations.contentControls.repeatingSection.insertItemAfter.tracked` | boolean | yes | | +| `operations.contentControls.repeatingSection.insertItemBefore` | object | yes | | +| `operations.contentControls.repeatingSection.insertItemBefore.available` | boolean | yes | | +| `operations.contentControls.repeatingSection.insertItemBefore.dryRun` | boolean | yes | | +| `operations.contentControls.repeatingSection.insertItemBefore.reasons` | enum[] | no | | +| `operations.contentControls.repeatingSection.insertItemBefore.tracked` | boolean | yes | | +| `operations.contentControls.repeatingSection.listItems` | object | yes | | +| `operations.contentControls.repeatingSection.listItems.available` | boolean | yes | | +| `operations.contentControls.repeatingSection.listItems.dryRun` | boolean | yes | | +| `operations.contentControls.repeatingSection.listItems.reasons` | enum[] | no | | +| `operations.contentControls.repeatingSection.listItems.tracked` | boolean | yes | | +| `operations.contentControls.repeatingSection.setAllowInsertDelete` | object | yes | | +| `operations.contentControls.repeatingSection.setAllowInsertDelete.available` | boolean | yes | | +| `operations.contentControls.repeatingSection.setAllowInsertDelete.dryRun` | boolean | yes | | +| `operations.contentControls.repeatingSection.setAllowInsertDelete.reasons` | enum[] | no | | +| `operations.contentControls.repeatingSection.setAllowInsertDelete.tracked` | boolean | yes | | +| `operations.contentControls.replaceContent` | object | yes | | +| `operations.contentControls.replaceContent.available` | boolean | yes | | +| `operations.contentControls.replaceContent.dryRun` | boolean | yes | | +| `operations.contentControls.replaceContent.reasons` | enum[] | no | | +| `operations.contentControls.replaceContent.tracked` | boolean | yes | | +| `operations.contentControls.selectByTag` | object | yes | | +| `operations.contentControls.selectByTag.available` | boolean | yes | | +| `operations.contentControls.selectByTag.dryRun` | boolean | yes | | +| `operations.contentControls.selectByTag.reasons` | enum[] | no | | +| `operations.contentControls.selectByTag.tracked` | boolean | yes | | +| `operations.contentControls.selectByTitle` | object | yes | | +| `operations.contentControls.selectByTitle.available` | boolean | yes | | +| `operations.contentControls.selectByTitle.dryRun` | boolean | yes | | +| `operations.contentControls.selectByTitle.reasons` | enum[] | no | | +| `operations.contentControls.selectByTitle.tracked` | boolean | yes | | +| `operations.contentControls.setBinding` | object | yes | | +| `operations.contentControls.setBinding.available` | boolean | yes | | +| `operations.contentControls.setBinding.dryRun` | boolean | yes | | +| `operations.contentControls.setBinding.reasons` | enum[] | no | | +| `operations.contentControls.setBinding.tracked` | boolean | yes | | +| `operations.contentControls.setLockMode` | object | yes | | +| `operations.contentControls.setLockMode.available` | boolean | yes | | +| `operations.contentControls.setLockMode.dryRun` | boolean | yes | | +| `operations.contentControls.setLockMode.reasons` | enum[] | no | | +| `operations.contentControls.setLockMode.tracked` | boolean | yes | | +| `operations.contentControls.setType` | object | yes | | +| `operations.contentControls.setType.available` | boolean | yes | | +| `operations.contentControls.setType.dryRun` | boolean | yes | | +| `operations.contentControls.setType.reasons` | enum[] | no | | +| `operations.contentControls.setType.tracked` | boolean | yes | | +| `operations.contentControls.text.clearValue` | object | yes | | +| `operations.contentControls.text.clearValue.available` | boolean | yes | | +| `operations.contentControls.text.clearValue.dryRun` | boolean | yes | | +| `operations.contentControls.text.clearValue.reasons` | enum[] | no | | +| `operations.contentControls.text.clearValue.tracked` | boolean | yes | | +| `operations.contentControls.text.setMultiline` | object | yes | | +| `operations.contentControls.text.setMultiline.available` | boolean | yes | | +| `operations.contentControls.text.setMultiline.dryRun` | boolean | yes | | +| `operations.contentControls.text.setMultiline.reasons` | enum[] | no | | +| `operations.contentControls.text.setMultiline.tracked` | boolean | yes | | +| `operations.contentControls.text.setValue` | object | yes | | +| `operations.contentControls.text.setValue.available` | boolean | yes | | +| `operations.contentControls.text.setValue.dryRun` | boolean | yes | | +| `operations.contentControls.text.setValue.reasons` | enum[] | no | | +| `operations.contentControls.text.setValue.tracked` | boolean | yes | | +| `operations.contentControls.unwrap` | object | yes | | +| `operations.contentControls.unwrap.available` | boolean | yes | | +| `operations.contentControls.unwrap.dryRun` | boolean | yes | | +| `operations.contentControls.unwrap.reasons` | enum[] | no | | +| `operations.contentControls.unwrap.tracked` | boolean | yes | | +| `operations.contentControls.validateWordCompatibility` | object | yes | | +| `operations.contentControls.validateWordCompatibility.available` | boolean | yes | | +| `operations.contentControls.validateWordCompatibility.dryRun` | boolean | yes | | +| `operations.contentControls.validateWordCompatibility.reasons` | enum[] | no | | +| `operations.contentControls.validateWordCompatibility.tracked` | boolean | yes | | +| `operations.contentControls.wrap` | object | yes | | +| `operations.contentControls.wrap.available` | boolean | yes | | +| `operations.contentControls.wrap.dryRun` | boolean | yes | | +| `operations.contentControls.wrap.reasons` | enum[] | no | | +| `operations.contentControls.wrap.tracked` | boolean | yes | | +| `operations.create.contentControl` | object | yes | | +| `operations.create.contentControl.available` | boolean | yes | | +| `operations.create.contentControl.dryRun` | boolean | yes | | +| `operations.create.contentControl.reasons` | enum[] | no | | +| `operations.create.contentControl.tracked` | boolean | yes | | | `operations.create.heading` | object | yes | | | `operations.create.heading.available` | boolean | yes | | | `operations.create.heading.dryRun` | boolean | yes | | @@ -1797,7 +2072,7 @@ _No fields._ ], "tracked": true }, - "create.heading": { + "contentControls.appendContent": { "available": true, "dryRun": true, "reasons": [ @@ -1805,7 +2080,7 @@ _No fields._ ], "tracked": true }, - "create.image": { + "contentControls.checkbox.getState": { "available": true, "dryRun": true, "reasons": [ @@ -1813,7 +2088,7 @@ _No fields._ ], "tracked": true }, - "create.paragraph": { + "contentControls.checkbox.setState": { "available": true, "dryRun": true, "reasons": [ @@ -1821,7 +2096,7 @@ _No fields._ ], "tracked": true }, - "create.sectionBreak": { + "contentControls.checkbox.setSymbolPair": { "available": true, "dryRun": true, "reasons": [ @@ -1829,7 +2104,7 @@ _No fields._ ], "tracked": true }, - "create.table": { + "contentControls.checkbox.toggle": { "available": true, "dryRun": true, "reasons": [ @@ -1837,7 +2112,7 @@ _No fields._ ], "tracked": true }, - "create.tableOfContents": { + "contentControls.choiceList.getItems": { "available": true, "dryRun": true, "reasons": [ @@ -1845,7 +2120,7 @@ _No fields._ ], "tracked": true }, - "delete": { + "contentControls.choiceList.setItems": { "available": true, "dryRun": true, "reasons": [ @@ -1853,7 +2128,7 @@ _No fields._ ], "tracked": true }, - "find": { + "contentControls.choiceList.setSelected": { "available": true, "dryRun": true, "reasons": [ @@ -1861,7 +2136,7 @@ _No fields._ ], "tracked": true }, - "format.apply": { + "contentControls.clearBinding": { "available": true, "dryRun": true, "reasons": [ @@ -1869,7 +2144,7 @@ _No fields._ ], "tracked": true }, - "format.bCs": { + "contentControls.clearContent": { "available": true, "dryRun": true, "reasons": [ @@ -1877,7 +2152,7 @@ _No fields._ ], "tracked": true }, - "format.bold": { + "contentControls.copy": { "available": true, "dryRun": true, "reasons": [ @@ -1885,7 +2160,7 @@ _No fields._ ], "tracked": true }, - "format.border": { + "contentControls.date.clearValue": { "available": true, "dryRun": true, "reasons": [ @@ -1893,7 +2168,7 @@ _No fields._ ], "tracked": true }, - "format.caps": { + "contentControls.date.setCalendar": { "available": true, "dryRun": true, "reasons": [ @@ -1901,7 +2176,7 @@ _No fields._ ], "tracked": true }, - "format.charScale": { + "contentControls.date.setDisplayFormat": { "available": true, "dryRun": true, "reasons": [ @@ -1909,7 +2184,7 @@ _No fields._ ], "tracked": true }, - "format.color": { + "contentControls.date.setDisplayLocale": { "available": true, "dryRun": true, "reasons": [ @@ -1917,7 +2192,7 @@ _No fields._ ], "tracked": true }, - "format.contextualAlternates": { + "contentControls.date.setStorageFormat": { "available": true, "dryRun": true, "reasons": [ @@ -1925,7 +2200,7 @@ _No fields._ ], "tracked": true }, - "format.cs": { + "contentControls.date.setValue": { "available": true, "dryRun": true, "reasons": [ @@ -1933,7 +2208,7 @@ _No fields._ ], "tracked": true }, - "format.dstrike": { + "contentControls.delete": { "available": true, "dryRun": true, "reasons": [ @@ -1941,7 +2216,7 @@ _No fields._ ], "tracked": true }, - "format.eastAsianLayout": { + "contentControls.get": { "available": true, "dryRun": true, "reasons": [ @@ -1949,7 +2224,7 @@ _No fields._ ], "tracked": true }, - "format.em": { + "contentControls.getBinding": { "available": true, "dryRun": true, "reasons": [ @@ -1957,7 +2232,7 @@ _No fields._ ], "tracked": true }, - "format.emboss": { + "contentControls.getContent": { "available": true, "dryRun": true, "reasons": [ @@ -1965,7 +2240,7 @@ _No fields._ ], "tracked": true }, - "format.fitText": { + "contentControls.getParent": { "available": true, "dryRun": true, "reasons": [ @@ -1973,7 +2248,7 @@ _No fields._ ], "tracked": true }, - "format.fontFamily": { + "contentControls.getRawProperties": { "available": true, "dryRun": true, "reasons": [ @@ -1981,7 +2256,7 @@ _No fields._ ], "tracked": true }, - "format.fontSize": { + "contentControls.group.ungroup": { "available": true, "dryRun": true, "reasons": [ @@ -1989,7 +2264,7 @@ _No fields._ ], "tracked": true }, - "format.fontSizeCs": { + "contentControls.group.wrap": { "available": true, "dryRun": true, "reasons": [ @@ -1997,7 +2272,7 @@ _No fields._ ], "tracked": true }, - "format.highlight": { + "contentControls.insertAfter": { "available": true, "dryRun": true, "reasons": [ @@ -2005,7 +2280,7 @@ _No fields._ ], "tracked": true }, - "format.iCs": { + "contentControls.insertBefore": { "available": true, "dryRun": true, "reasons": [ @@ -2013,7 +2288,7 @@ _No fields._ ], "tracked": true }, - "format.imprint": { + "contentControls.list": { "available": true, "dryRun": true, "reasons": [ @@ -2021,7 +2296,7 @@ _No fields._ ], "tracked": true }, - "format.italic": { + "contentControls.listChildren": { "available": true, "dryRun": true, "reasons": [ @@ -2029,7 +2304,7 @@ _No fields._ ], "tracked": true }, - "format.kerning": { + "contentControls.listInRange": { "available": true, "dryRun": true, "reasons": [ @@ -2037,7 +2312,7 @@ _No fields._ ], "tracked": true }, - "format.lang": { + "contentControls.move": { "available": true, "dryRun": true, "reasons": [ @@ -2045,7 +2320,7 @@ _No fields._ ], "tracked": true }, - "format.letterSpacing": { + "contentControls.normalizeTagPayload": { "available": true, "dryRun": true, "reasons": [ @@ -2053,7 +2328,7 @@ _No fields._ ], "tracked": true }, - "format.ligatures": { + "contentControls.normalizeWordCompatibility": { "available": true, "dryRun": true, "reasons": [ @@ -2061,7 +2336,7 @@ _No fields._ ], "tracked": true }, - "format.numForm": { + "contentControls.patch": { "available": true, "dryRun": true, "reasons": [ @@ -2069,7 +2344,7 @@ _No fields._ ], "tracked": true }, - "format.numSpacing": { + "contentControls.patchRawProperties": { "available": true, "dryRun": true, "reasons": [ @@ -2077,7 +2352,7 @@ _No fields._ ], "tracked": true }, - "format.oMath": { + "contentControls.prependContent": { "available": true, "dryRun": true, "reasons": [ @@ -2085,7 +2360,7 @@ _No fields._ ], "tracked": true }, - "format.outline": { + "contentControls.repeatingSection.cloneItem": { "available": true, "dryRun": true, "reasons": [ @@ -2093,7 +2368,7 @@ _No fields._ ], "tracked": true }, - "format.paragraph.clearAlignment": { + "contentControls.repeatingSection.deleteItem": { "available": true, "dryRun": true, "reasons": [ @@ -2101,7 +2376,7 @@ _No fields._ ], "tracked": true }, - "format.paragraph.clearAllTabStops": { + "contentControls.repeatingSection.insertItemAfter": { "available": true, "dryRun": true, "reasons": [ @@ -2109,7 +2384,7 @@ _No fields._ ], "tracked": true }, - "format.paragraph.clearBorder": { + "contentControls.repeatingSection.insertItemBefore": { "available": true, "dryRun": true, "reasons": [ @@ -2117,7 +2392,7 @@ _No fields._ ], "tracked": true }, - "format.paragraph.clearIndentation": { + "contentControls.repeatingSection.listItems": { "available": true, "dryRun": true, "reasons": [ @@ -2125,7 +2400,7 @@ _No fields._ ], "tracked": true }, - "format.paragraph.clearShading": { + "contentControls.repeatingSection.setAllowInsertDelete": { "available": true, "dryRun": true, "reasons": [ @@ -2133,7 +2408,7 @@ _No fields._ ], "tracked": true }, - "format.paragraph.clearSpacing": { + "contentControls.replaceContent": { "available": true, "dryRun": true, "reasons": [ @@ -2141,7 +2416,7 @@ _No fields._ ], "tracked": true }, - "format.paragraph.clearTabStop": { + "contentControls.selectByTag": { "available": true, "dryRun": true, "reasons": [ @@ -2149,7 +2424,7 @@ _No fields._ ], "tracked": true }, - "format.paragraph.resetDirectFormatting": { + "contentControls.selectByTitle": { "available": true, "dryRun": true, "reasons": [ @@ -2157,7 +2432,7 @@ _No fields._ ], "tracked": true }, - "format.paragraph.setAlignment": { + "contentControls.setBinding": { "available": true, "dryRun": true, "reasons": [ @@ -2165,7 +2440,7 @@ _No fields._ ], "tracked": true }, - "format.paragraph.setBorder": { + "contentControls.setLockMode": { "available": true, "dryRun": true, "reasons": [ @@ -2173,7 +2448,7 @@ _No fields._ ], "tracked": true }, - "format.paragraph.setFlowOptions": { + "contentControls.setType": { "available": true, "dryRun": true, "reasons": [ @@ -2181,7 +2456,7 @@ _No fields._ ], "tracked": true }, - "format.paragraph.setIndentation": { + "contentControls.text.clearValue": { "available": true, "dryRun": true, "reasons": [ @@ -2189,7 +2464,7 @@ _No fields._ ], "tracked": true }, - "format.paragraph.setKeepOptions": { + "contentControls.text.setMultiline": { "available": true, "dryRun": true, "reasons": [ @@ -2197,7 +2472,7 @@ _No fields._ ], "tracked": true }, - "format.paragraph.setOutlineLevel": { + "contentControls.text.setValue": { "available": true, "dryRun": true, "reasons": [ @@ -2205,7 +2480,7 @@ _No fields._ ], "tracked": true }, - "format.paragraph.setShading": { + "contentControls.unwrap": { "available": true, "dryRun": true, "reasons": [ @@ -2213,7 +2488,7 @@ _No fields._ ], "tracked": true }, - "format.paragraph.setSpacing": { + "contentControls.validateWordCompatibility": { "available": true, "dryRun": true, "reasons": [ @@ -2221,7 +2496,7 @@ _No fields._ ], "tracked": true }, - "format.paragraph.setTabStop": { + "contentControls.wrap": { "available": true, "dryRun": true, "reasons": [ @@ -2229,7 +2504,7 @@ _No fields._ ], "tracked": true }, - "format.position": { + "create.contentControl": { "available": true, "dryRun": true, "reasons": [ @@ -2237,7 +2512,7 @@ _No fields._ ], "tracked": true }, - "format.rFonts": { + "create.heading": { "available": true, "dryRun": true, "reasons": [ @@ -2245,7 +2520,7 @@ _No fields._ ], "tracked": true }, - "format.rStyle": { + "create.image": { "available": true, "dryRun": true, "reasons": [ @@ -2253,7 +2528,7 @@ _No fields._ ], "tracked": true }, - "format.rtl": { + "create.paragraph": { "available": true, "dryRun": true, "reasons": [ @@ -2261,7 +2536,7 @@ _No fields._ ], "tracked": true }, - "format.shading": { + "create.sectionBreak": { "available": true, "dryRun": true, "reasons": [ @@ -2269,7 +2544,7 @@ _No fields._ ], "tracked": true }, - "format.shadow": { + "create.table": { "available": true, "dryRun": true, "reasons": [ @@ -2277,7 +2552,7 @@ _No fields._ ], "tracked": true }, - "format.smallCaps": { + "create.tableOfContents": { "available": true, "dryRun": true, "reasons": [ @@ -2285,7 +2560,7 @@ _No fields._ ], "tracked": true }, - "format.snapToGrid": { + "delete": { "available": true, "dryRun": true, "reasons": [ @@ -2293,7 +2568,7 @@ _No fields._ ], "tracked": true }, - "format.specVanish": { + "find": { "available": true, "dryRun": true, "reasons": [ @@ -2301,7 +2576,7 @@ _No fields._ ], "tracked": true }, - "format.strike": { + "format.apply": { "available": true, "dryRun": true, "reasons": [ @@ -2309,7 +2584,7 @@ _No fields._ ], "tracked": true }, - "format.stylisticSets": { + "format.bCs": { "available": true, "dryRun": true, "reasons": [ @@ -2317,7 +2592,7 @@ _No fields._ ], "tracked": true }, - "format.underline": { + "format.bold": { "available": true, "dryRun": true, "reasons": [ @@ -2325,7 +2600,7 @@ _No fields._ ], "tracked": true }, - "format.vanish": { + "format.border": { "available": true, "dryRun": true, "reasons": [ @@ -2333,7 +2608,7 @@ _No fields._ ], "tracked": true }, - "format.vertAlign": { + "format.caps": { "available": true, "dryRun": true, "reasons": [ @@ -2341,7 +2616,7 @@ _No fields._ ], "tracked": true }, - "format.webHidden": { + "format.charScale": { "available": true, "dryRun": true, "reasons": [ @@ -2349,7 +2624,7 @@ _No fields._ ], "tracked": true }, - "get": { + "format.color": { "available": true, "dryRun": true, "reasons": [ @@ -2357,7 +2632,7 @@ _No fields._ ], "tracked": true }, - "getHtml": { + "format.contextualAlternates": { "available": true, "dryRun": true, "reasons": [ @@ -2365,7 +2640,7 @@ _No fields._ ], "tracked": true }, - "getMarkdown": { + "format.cs": { "available": true, "dryRun": true, "reasons": [ @@ -2373,7 +2648,7 @@ _No fields._ ], "tracked": true }, - "getNode": { + "format.dstrike": { "available": true, "dryRun": true, "reasons": [ @@ -2381,7 +2656,7 @@ _No fields._ ], "tracked": true }, - "getNodeById": { + "format.eastAsianLayout": { "available": true, "dryRun": true, "reasons": [ @@ -2389,7 +2664,7 @@ _No fields._ ], "tracked": true }, - "getText": { + "format.em": { "available": true, "dryRun": true, "reasons": [ @@ -2397,7 +2672,7 @@ _No fields._ ], "tracked": true }, - "history.get": { + "format.emboss": { "available": true, "dryRun": true, "reasons": [ @@ -2405,7 +2680,7 @@ _No fields._ ], "tracked": true }, - "history.redo": { + "format.fitText": { "available": true, "dryRun": true, "reasons": [ @@ -2413,7 +2688,7 @@ _No fields._ ], "tracked": true }, - "history.undo": { + "format.fontFamily": { "available": true, "dryRun": true, "reasons": [ @@ -2421,7 +2696,7 @@ _No fields._ ], "tracked": true }, - "hyperlinks.get": { + "format.fontSize": { "available": true, "dryRun": true, "reasons": [ @@ -2429,7 +2704,7 @@ _No fields._ ], "tracked": true }, - "hyperlinks.insert": { + "format.fontSizeCs": { "available": true, "dryRun": true, "reasons": [ @@ -2437,7 +2712,7 @@ _No fields._ ], "tracked": true }, - "hyperlinks.list": { + "format.highlight": { "available": true, "dryRun": true, "reasons": [ @@ -2445,7 +2720,7 @@ _No fields._ ], "tracked": true }, - "hyperlinks.patch": { + "format.iCs": { "available": true, "dryRun": true, "reasons": [ @@ -2453,7 +2728,7 @@ _No fields._ ], "tracked": true }, - "hyperlinks.remove": { + "format.imprint": { "available": true, "dryRun": true, "reasons": [ @@ -2461,7 +2736,7 @@ _No fields._ ], "tracked": true }, - "hyperlinks.wrap": { + "format.italic": { "available": true, "dryRun": true, "reasons": [ @@ -2469,7 +2744,7 @@ _No fields._ ], "tracked": true }, - "images.convertToFloating": { + "format.kerning": { "available": true, "dryRun": true, "reasons": [ @@ -2477,7 +2752,7 @@ _No fields._ ], "tracked": true }, - "images.convertToInline": { + "format.lang": { "available": true, "dryRun": true, "reasons": [ @@ -2485,7 +2760,7 @@ _No fields._ ], "tracked": true }, - "images.crop": { + "format.letterSpacing": { "available": true, "dryRun": true, "reasons": [ @@ -2493,7 +2768,7 @@ _No fields._ ], "tracked": true }, - "images.delete": { + "format.ligatures": { "available": true, "dryRun": true, "reasons": [ @@ -2501,7 +2776,7 @@ _No fields._ ], "tracked": true }, - "images.flip": { + "format.numForm": { "available": true, "dryRun": true, "reasons": [ @@ -2509,7 +2784,7 @@ _No fields._ ], "tracked": true }, - "images.get": { + "format.numSpacing": { "available": true, "dryRun": true, "reasons": [ @@ -2517,7 +2792,7 @@ _No fields._ ], "tracked": true }, - "images.insertCaption": { + "format.oMath": { "available": true, "dryRun": true, "reasons": [ @@ -2525,7 +2800,7 @@ _No fields._ ], "tracked": true }, - "images.list": { + "format.outline": { "available": true, "dryRun": true, "reasons": [ @@ -2533,7 +2808,7 @@ _No fields._ ], "tracked": true }, - "images.move": { + "format.paragraph.clearAlignment": { "available": true, "dryRun": true, "reasons": [ @@ -2541,7 +2816,7 @@ _No fields._ ], "tracked": true }, - "images.removeCaption": { + "format.paragraph.clearAllTabStops": { "available": true, "dryRun": true, "reasons": [ @@ -2549,7 +2824,7 @@ _No fields._ ], "tracked": true }, - "images.replaceSource": { + "format.paragraph.clearBorder": { "available": true, "dryRun": true, "reasons": [ @@ -2557,7 +2832,7 @@ _No fields._ ], "tracked": true }, - "images.resetCrop": { + "format.paragraph.clearIndentation": { "available": true, "dryRun": true, "reasons": [ @@ -2565,7 +2840,7 @@ _No fields._ ], "tracked": true }, - "images.rotate": { + "format.paragraph.clearShading": { "available": true, "dryRun": true, "reasons": [ @@ -2573,7 +2848,7 @@ _No fields._ ], "tracked": true }, - "images.scale": { + "format.paragraph.clearSpacing": { "available": true, "dryRun": true, "reasons": [ @@ -2581,7 +2856,7 @@ _No fields._ ], "tracked": true }, - "images.setAltText": { + "format.paragraph.clearTabStop": { "available": true, "dryRun": true, "reasons": [ @@ -2589,7 +2864,7 @@ _No fields._ ], "tracked": true }, - "images.setAnchorOptions": { + "format.paragraph.resetDirectFormatting": { "available": true, "dryRun": true, "reasons": [ @@ -2597,7 +2872,7 @@ _No fields._ ], "tracked": true }, - "images.setDecorative": { + "format.paragraph.setAlignment": { "available": true, "dryRun": true, "reasons": [ @@ -2605,7 +2880,7 @@ _No fields._ ], "tracked": true }, - "images.setHyperlink": { + "format.paragraph.setBorder": { "available": true, "dryRun": true, "reasons": [ @@ -2613,7 +2888,7 @@ _No fields._ ], "tracked": true }, - "images.setLockAspectRatio": { + "format.paragraph.setFlowOptions": { "available": true, "dryRun": true, "reasons": [ @@ -2621,7 +2896,7 @@ _No fields._ ], "tracked": true }, - "images.setName": { + "format.paragraph.setIndentation": { "available": true, "dryRun": true, "reasons": [ @@ -2629,7 +2904,7 @@ _No fields._ ], "tracked": true }, - "images.setPosition": { + "format.paragraph.setKeepOptions": { "available": true, "dryRun": true, "reasons": [ @@ -2637,7 +2912,7 @@ _No fields._ ], "tracked": true }, - "images.setSize": { + "format.paragraph.setOutlineLevel": { "available": true, "dryRun": true, "reasons": [ @@ -2645,7 +2920,7 @@ _No fields._ ], "tracked": true }, - "images.setWrapDistances": { + "format.paragraph.setShading": { "available": true, "dryRun": true, "reasons": [ @@ -2653,7 +2928,7 @@ _No fields._ ], "tracked": true }, - "images.setWrapSide": { + "format.paragraph.setSpacing": { "available": true, "dryRun": true, "reasons": [ @@ -2661,7 +2936,7 @@ _No fields._ ], "tracked": true }, - "images.setWrapType": { + "format.paragraph.setTabStop": { "available": true, "dryRun": true, "reasons": [ @@ -2669,7 +2944,7 @@ _No fields._ ], "tracked": true }, - "images.setZOrder": { + "format.position": { "available": true, "dryRun": true, "reasons": [ @@ -2677,7 +2952,7 @@ _No fields._ ], "tracked": true }, - "images.updateCaption": { + "format.rFonts": { "available": true, "dryRun": true, "reasons": [ @@ -2685,7 +2960,7 @@ _No fields._ ], "tracked": true }, - "info": { + "format.rStyle": { "available": true, "dryRun": true, "reasons": [ @@ -2693,7 +2968,7 @@ _No fields._ ], "tracked": true }, - "insert": { + "format.rtl": { "available": true, "dryRun": true, "reasons": [ @@ -2701,7 +2976,7 @@ _No fields._ ], "tracked": true }, - "lists.applyPreset": { + "format.shading": { "available": true, "dryRun": true, "reasons": [ @@ -2709,7 +2984,7 @@ _No fields._ ], "tracked": true }, - "lists.applyTemplate": { + "format.shadow": { "available": true, "dryRun": true, "reasons": [ @@ -2717,7 +2992,7 @@ _No fields._ ], "tracked": true }, - "lists.attach": { + "format.smallCaps": { "available": true, "dryRun": true, "reasons": [ @@ -2725,7 +3000,7 @@ _No fields._ ], "tracked": true }, - "lists.canContinuePrevious": { + "format.snapToGrid": { "available": true, "dryRun": true, "reasons": [ @@ -2733,7 +3008,7 @@ _No fields._ ], "tracked": true }, - "lists.canJoin": { + "format.specVanish": { "available": true, "dryRun": true, "reasons": [ @@ -2741,7 +3016,7 @@ _No fields._ ], "tracked": true }, - "lists.captureTemplate": { + "format.strike": { "available": true, "dryRun": true, "reasons": [ @@ -2749,7 +3024,7 @@ _No fields._ ], "tracked": true }, - "lists.clearLevelOverrides": { + "format.stylisticSets": { "available": true, "dryRun": true, "reasons": [ @@ -2757,7 +3032,7 @@ _No fields._ ], "tracked": true }, - "lists.continuePrevious": { + "format.underline": { "available": true, "dryRun": true, "reasons": [ @@ -2765,7 +3040,7 @@ _No fields._ ], "tracked": true }, - "lists.convertToText": { + "format.vanish": { "available": true, "dryRun": true, "reasons": [ @@ -2773,7 +3048,7 @@ _No fields._ ], "tracked": true }, - "lists.create": { + "format.vertAlign": { "available": true, "dryRun": true, "reasons": [ @@ -2781,7 +3056,7 @@ _No fields._ ], "tracked": true }, - "lists.detach": { + "format.webHidden": { "available": true, "dryRun": true, "reasons": [ @@ -2789,7 +3064,7 @@ _No fields._ ], "tracked": true }, - "lists.get": { + "get": { "available": true, "dryRun": true, "reasons": [ @@ -2797,7 +3072,7 @@ _No fields._ ], "tracked": true }, - "lists.indent": { + "getHtml": { "available": true, "dryRun": true, "reasons": [ @@ -2805,7 +3080,7 @@ _No fields._ ], "tracked": true }, - "lists.insert": { + "getMarkdown": { "available": true, "dryRun": true, "reasons": [ @@ -2813,7 +3088,7 @@ _No fields._ ], "tracked": true }, - "lists.join": { + "getNode": { "available": true, "dryRun": true, "reasons": [ @@ -2821,7 +3096,7 @@ _No fields._ ], "tracked": true }, - "lists.list": { + "getNodeById": { "available": true, "dryRun": true, "reasons": [ @@ -2829,7 +3104,7 @@ _No fields._ ], "tracked": true }, - "lists.outdent": { + "getText": { "available": true, "dryRun": true, "reasons": [ @@ -2837,7 +3112,7 @@ _No fields._ ], "tracked": true }, - "lists.separate": { + "history.get": { "available": true, "dryRun": true, "reasons": [ @@ -2845,7 +3120,7 @@ _No fields._ ], "tracked": true }, - "lists.setLevel": { + "history.redo": { "available": true, "dryRun": true, "reasons": [ @@ -2853,7 +3128,7 @@ _No fields._ ], "tracked": true }, - "lists.setLevelAlignment": { + "history.undo": { "available": true, "dryRun": true, "reasons": [ @@ -2861,7 +3136,7 @@ _No fields._ ], "tracked": true }, - "lists.setLevelBullet": { + "hyperlinks.get": { "available": true, "dryRun": true, "reasons": [ @@ -2869,7 +3144,7 @@ _No fields._ ], "tracked": true }, - "lists.setLevelIndents": { + "hyperlinks.insert": { "available": true, "dryRun": true, "reasons": [ @@ -2877,7 +3152,7 @@ _No fields._ ], "tracked": true }, - "lists.setLevelMarkerFont": { + "hyperlinks.list": { "available": true, "dryRun": true, "reasons": [ @@ -2885,7 +3160,7 @@ _No fields._ ], "tracked": true }, - "lists.setLevelNumbering": { + "hyperlinks.patch": { "available": true, "dryRun": true, "reasons": [ @@ -2893,7 +3168,7 @@ _No fields._ ], "tracked": true }, - "lists.setLevelPictureBullet": { + "hyperlinks.remove": { "available": true, "dryRun": true, "reasons": [ @@ -2901,7 +3176,7 @@ _No fields._ ], "tracked": true }, - "lists.setLevelRestart": { + "hyperlinks.wrap": { "available": true, "dryRun": true, "reasons": [ @@ -2909,7 +3184,7 @@ _No fields._ ], "tracked": true }, - "lists.setLevelTrailingCharacter": { + "images.convertToFloating": { "available": true, "dryRun": true, "reasons": [ @@ -2917,7 +3192,7 @@ _No fields._ ], "tracked": true }, - "lists.setType": { + "images.convertToInline": { "available": true, "dryRun": true, "reasons": [ @@ -2925,7 +3200,7 @@ _No fields._ ], "tracked": true }, - "lists.setValue": { + "images.crop": { "available": true, "dryRun": true, "reasons": [ @@ -2933,7 +3208,7 @@ _No fields._ ], "tracked": true }, - "markdownToFragment": { + "images.delete": { "available": true, "dryRun": true, "reasons": [ @@ -2941,7 +3216,7 @@ _No fields._ ], "tracked": true }, - "mutations.apply": { + "images.flip": { "available": true, "dryRun": true, "reasons": [ @@ -2949,7 +3224,7 @@ _No fields._ ], "tracked": true }, - "mutations.preview": { + "images.get": { "available": true, "dryRun": true, "reasons": [ @@ -2957,7 +3232,7 @@ _No fields._ ], "tracked": true }, - "query.match": { + "images.insertCaption": { "available": true, "dryRun": true, "reasons": [ @@ -2965,7 +3240,7 @@ _No fields._ ], "tracked": true }, - "replace": { + "images.list": { "available": true, "dryRun": true, "reasons": [ @@ -2973,7 +3248,7 @@ _No fields._ ], "tracked": true }, - "sections.clearHeaderFooterRef": { + "images.move": { "available": true, "dryRun": true, "reasons": [ @@ -2981,7 +3256,7 @@ _No fields._ ], "tracked": true }, - "sections.clearPageBorders": { + "images.removeCaption": { "available": true, "dryRun": true, "reasons": [ @@ -2989,7 +3264,7 @@ _No fields._ ], "tracked": true }, - "sections.get": { + "images.replaceSource": { "available": true, "dryRun": true, "reasons": [ @@ -2997,7 +3272,7 @@ _No fields._ ], "tracked": true }, - "sections.list": { + "images.resetCrop": { "available": true, "dryRun": true, "reasons": [ @@ -3005,7 +3280,7 @@ _No fields._ ], "tracked": true }, - "sections.setBreakType": { + "images.rotate": { "available": true, "dryRun": true, "reasons": [ @@ -3013,7 +3288,7 @@ _No fields._ ], "tracked": true }, - "sections.setColumns": { + "images.scale": { "available": true, "dryRun": true, "reasons": [ @@ -3021,7 +3296,7 @@ _No fields._ ], "tracked": true }, - "sections.setHeaderFooterMargins": { + "images.setAltText": { "available": true, "dryRun": true, "reasons": [ @@ -3029,7 +3304,7 @@ _No fields._ ], "tracked": true }, - "sections.setHeaderFooterRef": { + "images.setAnchorOptions": { "available": true, "dryRun": true, "reasons": [ @@ -3037,7 +3312,7 @@ _No fields._ ], "tracked": true }, - "sections.setLineNumbering": { + "images.setDecorative": { "available": true, "dryRun": true, "reasons": [ @@ -3045,7 +3320,7 @@ _No fields._ ], "tracked": true }, - "sections.setLinkToPrevious": { + "images.setHyperlink": { "available": true, "dryRun": true, "reasons": [ @@ -3053,7 +3328,7 @@ _No fields._ ], "tracked": true }, - "sections.setOddEvenHeadersFooters": { + "images.setLockAspectRatio": { "available": true, "dryRun": true, "reasons": [ @@ -3061,7 +3336,7 @@ _No fields._ ], "tracked": true }, - "sections.setPageBorders": { + "images.setName": { "available": true, "dryRun": true, "reasons": [ @@ -3069,7 +3344,7 @@ _No fields._ ], "tracked": true }, - "sections.setPageMargins": { + "images.setPosition": { "available": true, "dryRun": true, "reasons": [ @@ -3077,7 +3352,7 @@ _No fields._ ], "tracked": true }, - "sections.setPageNumbering": { + "images.setSize": { "available": true, "dryRun": true, "reasons": [ @@ -3085,7 +3360,7 @@ _No fields._ ], "tracked": true }, - "sections.setPageSetup": { + "images.setWrapDistances": { "available": true, "dryRun": true, "reasons": [ @@ -3093,7 +3368,7 @@ _No fields._ ], "tracked": true }, - "sections.setSectionDirection": { + "images.setWrapSide": { "available": true, "dryRun": true, "reasons": [ @@ -3101,7 +3376,7 @@ _No fields._ ], "tracked": true }, - "sections.setTitlePage": { + "images.setWrapType": { "available": true, "dryRun": true, "reasons": [ @@ -3109,7 +3384,7 @@ _No fields._ ], "tracked": true }, - "sections.setVerticalAlign": { + "images.setZOrder": { "available": true, "dryRun": true, "reasons": [ @@ -3117,7 +3392,7 @@ _No fields._ ], "tracked": true }, - "styles.apply": { + "images.updateCaption": { "available": true, "dryRun": true, "reasons": [ @@ -3125,7 +3400,7 @@ _No fields._ ], "tracked": true }, - "styles.paragraph.clearStyle": { + "info": { "available": true, "dryRun": true, "reasons": [ @@ -3133,7 +3408,7 @@ _No fields._ ], "tracked": true }, - "styles.paragraph.setStyle": { + "insert": { "available": true, "dryRun": true, "reasons": [ @@ -3141,7 +3416,7 @@ _No fields._ ], "tracked": true }, - "tables.applyBorderPreset": { + "lists.applyPreset": { "available": true, "dryRun": true, "reasons": [ @@ -3149,7 +3424,7 @@ _No fields._ ], "tracked": true }, - "tables.clearBorder": { + "lists.applyTemplate": { "available": true, "dryRun": true, "reasons": [ @@ -3157,7 +3432,7 @@ _No fields._ ], "tracked": true }, - "tables.clearCellSpacing": { + "lists.attach": { "available": true, "dryRun": true, "reasons": [ @@ -3165,7 +3440,7 @@ _No fields._ ], "tracked": true }, - "tables.clearContents": { + "lists.canContinuePrevious": { "available": true, "dryRun": true, "reasons": [ @@ -3173,7 +3448,7 @@ _No fields._ ], "tracked": true }, - "tables.clearDefaultStyle": { + "lists.canJoin": { "available": true, "dryRun": true, "reasons": [ @@ -3181,7 +3456,7 @@ _No fields._ ], "tracked": true }, - "tables.clearShading": { + "lists.captureTemplate": { "available": true, "dryRun": true, "reasons": [ @@ -3189,7 +3464,7 @@ _No fields._ ], "tracked": true }, - "tables.clearStyle": { + "lists.clearLevelOverrides": { "available": true, "dryRun": true, "reasons": [ @@ -3197,7 +3472,7 @@ _No fields._ ], "tracked": true }, - "tables.convertFromText": { + "lists.continuePrevious": { "available": true, "dryRun": true, "reasons": [ @@ -3205,7 +3480,7 @@ _No fields._ ], "tracked": true }, - "tables.convertToText": { + "lists.convertToText": { "available": true, "dryRun": true, "reasons": [ @@ -3213,7 +3488,7 @@ _No fields._ ], "tracked": true }, - "tables.delete": { + "lists.create": { "available": true, "dryRun": true, "reasons": [ @@ -3221,7 +3496,7 @@ _No fields._ ], "tracked": true }, - "tables.deleteCell": { + "lists.detach": { "available": true, "dryRun": true, "reasons": [ @@ -3229,7 +3504,7 @@ _No fields._ ], "tracked": true }, - "tables.deleteColumn": { + "lists.get": { "available": true, "dryRun": true, "reasons": [ @@ -3237,7 +3512,7 @@ _No fields._ ], "tracked": true }, - "tables.deleteRow": { + "lists.indent": { "available": true, "dryRun": true, "reasons": [ @@ -3245,7 +3520,7 @@ _No fields._ ], "tracked": true }, - "tables.distributeColumns": { + "lists.insert": { "available": true, "dryRun": true, "reasons": [ @@ -3253,7 +3528,7 @@ _No fields._ ], "tracked": true }, - "tables.distributeRows": { + "lists.join": { "available": true, "dryRun": true, "reasons": [ @@ -3261,7 +3536,7 @@ _No fields._ ], "tracked": true }, - "tables.get": { + "lists.list": { "available": true, "dryRun": true, "reasons": [ @@ -3269,7 +3544,7 @@ _No fields._ ], "tracked": true }, - "tables.getCells": { + "lists.outdent": { "available": true, "dryRun": true, "reasons": [ @@ -3277,7 +3552,7 @@ _No fields._ ], "tracked": true }, - "tables.getProperties": { + "lists.separate": { "available": true, "dryRun": true, "reasons": [ @@ -3285,7 +3560,7 @@ _No fields._ ], "tracked": true }, - "tables.getStyles": { + "lists.setLevel": { "available": true, "dryRun": true, "reasons": [ @@ -3293,7 +3568,7 @@ _No fields._ ], "tracked": true }, - "tables.insertCell": { + "lists.setLevelAlignment": { "available": true, "dryRun": true, "reasons": [ @@ -3301,7 +3576,7 @@ _No fields._ ], "tracked": true }, - "tables.insertColumn": { + "lists.setLevelBullet": { "available": true, "dryRun": true, "reasons": [ @@ -3309,7 +3584,7 @@ _No fields._ ], "tracked": true }, - "tables.insertRow": { + "lists.setLevelIndents": { "available": true, "dryRun": true, "reasons": [ @@ -3317,7 +3592,7 @@ _No fields._ ], "tracked": true }, - "tables.mergeCells": { + "lists.setLevelMarkerFont": { "available": true, "dryRun": true, "reasons": [ @@ -3325,7 +3600,7 @@ _No fields._ ], "tracked": true }, - "tables.move": { + "lists.setLevelNumbering": { "available": true, "dryRun": true, "reasons": [ @@ -3333,7 +3608,7 @@ _No fields._ ], "tracked": true }, - "tables.setAltText": { + "lists.setLevelPictureBullet": { "available": true, "dryRun": true, "reasons": [ @@ -3341,7 +3616,7 @@ _No fields._ ], "tracked": true }, - "tables.setBorder": { + "lists.setLevelRestart": { "available": true, "dryRun": true, "reasons": [ @@ -3349,7 +3624,7 @@ _No fields._ ], "tracked": true }, - "tables.setCellPadding": { + "lists.setLevelTrailingCharacter": { "available": true, "dryRun": true, "reasons": [ @@ -3357,7 +3632,7 @@ _No fields._ ], "tracked": true }, - "tables.setCellProperties": { + "lists.setType": { "available": true, "dryRun": true, "reasons": [ @@ -3365,7 +3640,7 @@ _No fields._ ], "tracked": true }, - "tables.setCellSpacing": { + "lists.setValue": { "available": true, "dryRun": true, "reasons": [ @@ -3373,7 +3648,7 @@ _No fields._ ], "tracked": true }, - "tables.setColumnWidth": { + "markdownToFragment": { "available": true, "dryRun": true, "reasons": [ @@ -3381,7 +3656,7 @@ _No fields._ ], "tracked": true }, - "tables.setDefaultStyle": { + "mutations.apply": { "available": true, "dryRun": true, "reasons": [ @@ -3389,7 +3664,7 @@ _No fields._ ], "tracked": true }, - "tables.setLayout": { + "mutations.preview": { "available": true, "dryRun": true, "reasons": [ @@ -3397,7 +3672,7 @@ _No fields._ ], "tracked": true }, - "tables.setRowHeight": { + "query.match": { "available": true, "dryRun": true, "reasons": [ @@ -3405,7 +3680,7 @@ _No fields._ ], "tracked": true }, - "tables.setRowOptions": { + "replace": { "available": true, "dryRun": true, "reasons": [ @@ -3413,7 +3688,7 @@ _No fields._ ], "tracked": true }, - "tables.setShading": { + "sections.clearHeaderFooterRef": { "available": true, "dryRun": true, "reasons": [ @@ -3421,7 +3696,7 @@ _No fields._ ], "tracked": true }, - "tables.setStyle": { + "sections.clearPageBorders": { "available": true, "dryRun": true, "reasons": [ @@ -3429,7 +3704,7 @@ _No fields._ ], "tracked": true }, - "tables.setStyleOption": { + "sections.get": { "available": true, "dryRun": true, "reasons": [ @@ -3437,7 +3712,7 @@ _No fields._ ], "tracked": true }, - "tables.setTablePadding": { + "sections.list": { "available": true, "dryRun": true, "reasons": [ @@ -3445,7 +3720,7 @@ _No fields._ ], "tracked": true }, - "tables.sort": { + "sections.setBreakType": { "available": true, "dryRun": true, "reasons": [ @@ -3453,7 +3728,7 @@ _No fields._ ], "tracked": true }, - "tables.split": { + "sections.setColumns": { "available": true, "dryRun": true, "reasons": [ @@ -3461,7 +3736,7 @@ _No fields._ ], "tracked": true }, - "tables.splitCell": { + "sections.setHeaderFooterMargins": { "available": true, "dryRun": true, "reasons": [ @@ -3469,7 +3744,7 @@ _No fields._ ], "tracked": true }, - "tables.unmergeCells": { + "sections.setHeaderFooterRef": { "available": true, "dryRun": true, "reasons": [ @@ -3477,7 +3752,7 @@ _No fields._ ], "tracked": true }, - "toc.configure": { + "sections.setLineNumbering": { "available": true, "dryRun": true, "reasons": [ @@ -3485,7 +3760,7 @@ _No fields._ ], "tracked": true }, - "toc.editEntry": { + "sections.setLinkToPrevious": { "available": true, "dryRun": true, "reasons": [ @@ -3493,7 +3768,7 @@ _No fields._ ], "tracked": true }, - "toc.get": { + "sections.setOddEvenHeadersFooters": { "available": true, "dryRun": true, "reasons": [ @@ -3501,7 +3776,7 @@ _No fields._ ], "tracked": true }, - "toc.getEntry": { + "sections.setPageBorders": { "available": true, "dryRun": true, "reasons": [ @@ -3509,7 +3784,7 @@ _No fields._ ], "tracked": true }, - "toc.list": { + "sections.setPageMargins": { "available": true, "dryRun": true, "reasons": [ @@ -3517,7 +3792,7 @@ _No fields._ ], "tracked": true }, - "toc.listEntries": { + "sections.setPageNumbering": { "available": true, "dryRun": true, "reasons": [ @@ -3525,7 +3800,7 @@ _No fields._ ], "tracked": true }, - "toc.markEntry": { + "sections.setPageSetup": { "available": true, "dryRun": true, "reasons": [ @@ -3533,7 +3808,7 @@ _No fields._ ], "tracked": true }, - "toc.remove": { + "sections.setSectionDirection": { "available": true, "dryRun": true, "reasons": [ @@ -3541,7 +3816,7 @@ _No fields._ ], "tracked": true }, - "toc.unmarkEntry": { + "sections.setTitlePage": { "available": true, "dryRun": true, "reasons": [ @@ -3549,7 +3824,7 @@ _No fields._ ], "tracked": true }, - "toc.update": { + "sections.setVerticalAlign": { "available": true, "dryRun": true, "reasons": [ @@ -3557,7 +3832,7 @@ _No fields._ ], "tracked": true }, - "trackChanges.decide": { + "styles.apply": { "available": true, "dryRun": true, "reasons": [ @@ -3565,7 +3840,7 @@ _No fields._ ], "tracked": true }, - "trackChanges.get": { + "styles.paragraph.clearStyle": { "available": true, "dryRun": true, "reasons": [ @@ -3573,135 +3848,1895 @@ _No fields._ ], "tracked": true }, - "trackChanges.list": { + "styles.paragraph.setStyle": { "available": true, "dryRun": true, "reasons": [ "COMMAND_UNAVAILABLE" ], "tracked": true - } - }, - "planEngine": { - "regex": { - "maxPatternLength": 1 }, - "supportedNonUniformStrategies": [ - "example" - ], - "supportedSetMarks": [ - "example" - ], - "supportedStepOps": [ - "example" - ] - } -} -``` - -## Pre-apply throws - -- None - -## Non-applied failure codes - -- None - -## Raw schemas - - -```json -{ - "additionalProperties": false, - "properties": {}, - "type": "object" -} -``` - - - -```json -{ - "additionalProperties": false, - "properties": { - "format": { - "additionalProperties": false, - "properties": { - "supportedInlineProperties": { - "additionalProperties": false, - "properties": { - "bCs": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } - }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" - }, - "bold": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } - }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" - }, - "border": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, + "tables.applyBorderPreset": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.clearBorder": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.clearCellSpacing": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.clearContents": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.clearDefaultStyle": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.clearShading": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.clearStyle": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.convertFromText": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.convertToText": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.delete": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.deleteCell": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.deleteColumn": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.deleteRow": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.distributeColumns": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.distributeRows": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.get": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.getCells": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.getProperties": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.getStyles": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.insertCell": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.insertColumn": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.insertRow": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.mergeCells": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.move": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.setAltText": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.setBorder": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.setCellPadding": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.setCellProperties": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.setCellSpacing": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.setColumnWidth": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.setDefaultStyle": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.setLayout": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.setRowHeight": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.setRowOptions": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.setShading": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.setStyle": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.setStyleOption": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.setTablePadding": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.sort": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.split": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.splitCell": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "tables.unmergeCells": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "toc.configure": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "toc.editEntry": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "toc.get": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "toc.getEntry": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "toc.list": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "toc.listEntries": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "toc.markEntry": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "toc.remove": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "toc.unmarkEntry": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "toc.update": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "trackChanges.decide": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "trackChanges.get": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + }, + "trackChanges.list": { + "available": true, + "dryRun": true, + "reasons": [ + "COMMAND_UNAVAILABLE" + ], + "tracked": true + } + }, + "planEngine": { + "regex": { + "maxPatternLength": 1 + }, + "supportedNonUniformStrategies": [ + "example" + ], + "supportedSetMarks": [ + "example" + ], + "supportedStepOps": [ + "example" + ] + } +} +``` + +## Pre-apply throws + +- None + +## Non-applied failure codes + +- None + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": {}, + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "format": { + "additionalProperties": false, + "properties": { + "supportedInlineProperties": { + "additionalProperties": false, + "properties": { + "bCs": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "bold": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "border": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "caps": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "charScale": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "color": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "contextualAlternates": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "cs": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "dstrike": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "eastAsianLayout": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "em": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "emboss": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "fitText": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "fontFamily": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "fontSize": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "fontSizeCs": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "highlight": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "iCs": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "imprint": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "italic": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "kerning": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "lang": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "letterSpacing": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "ligatures": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "numForm": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "numSpacing": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "oMath": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "outline": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "position": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "rFonts": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "rStyle": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "rtl": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "shading": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "shadow": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "smallCaps": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "snapToGrid": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "specVanish": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "strike": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "stylisticSets": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "underline": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "vanish": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "vertAlign": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "storage": { + "enum": [ + "mark", + "runAttribute" + ] + }, + "tracked": { + "type": "boolean" + }, + "type": { + "enum": [ + "boolean", + "string", + "number", + "object", + "array" + ] + } + }, + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + }, + "webHidden": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, "storage": { "enum": [ "mark", @@ -3721,1395 +5756,1976 @@ _No fields._ ] } }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "required": [ + "available", + "tracked", + "type", + "storage" + ], + "type": "object" + } + }, + "required": [ + "bold", + "italic", + "strike", + "underline", + "highlight", + "color", + "fontSize", + "fontFamily", + "letterSpacing", + "vertAlign", + "position", + "dstrike", + "smallCaps", + "caps", + "shading", + "border", + "outline", + "shadow", + "emboss", + "imprint", + "charScale", + "kerning", + "vanish", + "webHidden", + "specVanish", + "rtl", + "cs", + "bCs", + "iCs", + "eastAsianLayout", + "em", + "fitText", + "snapToGrid", + "lang", + "oMath", + "rStyle", + "rFonts", + "fontSizeCs", + "ligatures", + "numForm", + "numSpacing", + "stylisticSets", + "contextualAlternates" + ], + "type": "object" + } + }, + "required": [ + "supportedInlineProperties" + ], + "type": "object" + }, + "global": { + "additionalProperties": false, + "properties": { + "comments": { + "additionalProperties": false, + "properties": { + "enabled": { + "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" + } + }, + "required": [ + "enabled" + ], + "type": "object" + }, + "dryRun": { + "additionalProperties": false, + "properties": { + "enabled": { + "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" + } + }, + "required": [ + "enabled" + ], + "type": "object" + }, + "history": { + "additionalProperties": false, + "properties": { + "enabled": { + "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" + } + }, + "required": [ + "enabled" + ], + "type": "object" + }, + "lists": { + "additionalProperties": false, + "properties": { + "enabled": { + "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" + } + }, + "required": [ + "enabled" + ], + "type": "object" + }, + "trackChanges": { + "additionalProperties": false, + "properties": { + "enabled": { + "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" + } + }, + "required": [ + "enabled" + ], + "type": "object" + } + }, + "required": [ + "trackChanges", + "comments", + "lists", + "dryRun", + "history" + ], + "type": "object" + }, + "operations": { + "additionalProperties": false, + "properties": { + "blocks.delete": { + "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" + }, + "capabilities.get": { + "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" + }, + "clearContent": { + "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" + }, + "comments.create": { + "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" + }, + "comments.delete": { + "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" + }, + "comments.get": { + "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" }, - "caps": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "comments.list": { + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" }, - "charScale": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "comments.patch": { + "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" + }, + "contentControls.appendContent": { + "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" + }, + "contentControls.checkbox.getState": { + "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" + }, + "contentControls.checkbox.setState": { + "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" + }, + "contentControls.checkbox.setSymbolPair": { + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" }, - "color": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.checkbox.toggle": { + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" }, - "contextualAlternates": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.choiceList.getItems": { + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" }, - "cs": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.choiceList.setItems": { + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" }, - "dstrike": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.choiceList.setSelected": { + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" }, - "eastAsianLayout": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.clearBinding": { + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" }, - "em": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.clearContent": { + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" }, - "emboss": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.copy": { + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" }, - "fitText": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.date.clearValue": { + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" }, - "fontFamily": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } - }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.date.setCalendar": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" }, - "fontSize": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" }, - "fontSizeCs": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.date.setDisplayFormat": { + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" }, - "highlight": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.date.setDisplayLocale": { + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" }, - "iCs": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.date.setStorageFormat": { + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" }, - "imprint": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.date.setValue": { + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" }, - "italic": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.delete": { + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" }, - "kerning": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.get": { + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" + }, + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.getBinding": { + "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" }, - "lang": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.getContent": { + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" }, - "letterSpacing": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.getParent": { + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" }, - "ligatures": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.getRawProperties": { + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" }, - "numForm": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.group.ungroup": { + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" + }, + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.group.wrap": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" }, - "numSpacing": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" }, - "oMath": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.insertAfter": { + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" }, - "outline": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.insertBefore": { + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" }, - "position": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.list": { + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" + }, + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.listChildren": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" }, - "rFonts": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" }, - "rStyle": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.listInRange": { + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" }, - "rtl": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.move": { + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" }, - "shading": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.normalizeTagPayload": { + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" + }, + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.normalizeWordCompatibility": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" }, - "shadow": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" }, - "smallCaps": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.patch": { + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" }, - "snapToGrid": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.patchRawProperties": { + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" }, - "specVanish": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.prependContent": { + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" + }, + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.repeatingSection.cloneItem": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" }, - "strike": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" }, - "stylisticSets": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.repeatingSection.deleteItem": { + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" }, - "underline": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.repeatingSection.insertItemAfter": { + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" }, - "vanish": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.repeatingSection.insertItemBefore": { + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" + }, + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.repeatingSection.listItems": { + "additionalProperties": false, + "properties": { + "available": { + "type": "boolean" + }, + "dryRun": { + "type": "boolean" }, - "vertAlign": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "reasons": { + "items": { + "enum": [ + "COMMAND_UNAVAILABLE", + "HELPER_UNAVAILABLE", + "OPERATION_UNAVAILABLE", + "TRACKED_MODE_UNAVAILABLE", + "DRY_RUN_UNAVAILABLE", + "NAMESPACE_UNAVAILABLE", + "STYLES_PART_MISSING", + "COLLABORATION_ACTIVE" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" }, - "webHidden": { - "additionalProperties": false, - "properties": { - "available": { - "type": "boolean" - }, - "storage": { - "enum": [ - "mark", - "runAttribute" - ] - }, - "tracked": { - "type": "boolean" - }, - "type": { - "enum": [ - "boolean", - "string", - "number", - "object", - "array" - ] - } + "tracked": { + "type": "boolean" + } + }, + "required": [ + "available", + "tracked", + "dryRun" + ], + "type": "object" + }, + "contentControls.repeatingSection.setAllowInsertDelete": { + "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" + ] }, - "required": [ - "available", - "tracked", - "type", - "storage" - ], - "type": "object" + "type": "array" + }, + "tracked": { + "type": "boolean" } }, "required": [ - "bold", - "italic", - "strike", - "underline", - "highlight", - "color", - "fontSize", - "fontFamily", - "letterSpacing", - "vertAlign", - "position", - "dstrike", - "smallCaps", - "caps", - "shading", - "border", - "outline", - "shadow", - "emboss", - "imprint", - "charScale", - "kerning", - "vanish", - "webHidden", - "specVanish", - "rtl", - "cs", - "bCs", - "iCs", - "eastAsianLayout", - "em", - "fitText", - "snapToGrid", - "lang", - "oMath", - "rStyle", - "rFonts", - "fontSizeCs", - "ligatures", - "numForm", - "numSpacing", - "stylisticSets", - "contextualAlternates" + "available", + "tracked", + "dryRun" ], "type": "object" - } - }, - "required": [ - "supportedInlineProperties" - ], - "type": "object" - }, - "global": { - "additionalProperties": false, - "properties": { - "comments": { + }, + "contentControls.replaceContent": { "additionalProperties": false, "properties": { - "enabled": { + "available": { + "type": "boolean" + }, + "dryRun": { "type": "boolean" }, "reasons": { @@ -5126,17 +7742,25 @@ _No fields._ ] }, "type": "array" + }, + "tracked": { + "type": "boolean" } }, "required": [ - "enabled" + "available", + "tracked", + "dryRun" ], "type": "object" }, - "dryRun": { + "contentControls.selectByTag": { "additionalProperties": false, "properties": { - "enabled": { + "available": { + "type": "boolean" + }, + "dryRun": { "type": "boolean" }, "reasons": { @@ -5153,17 +7777,25 @@ _No fields._ ] }, "type": "array" + }, + "tracked": { + "type": "boolean" } }, "required": [ - "enabled" + "available", + "tracked", + "dryRun" ], "type": "object" }, - "history": { + "contentControls.selectByTitle": { "additionalProperties": false, "properties": { - "enabled": { + "available": { + "type": "boolean" + }, + "dryRun": { "type": "boolean" }, "reasons": { @@ -5180,17 +7812,25 @@ _No fields._ ] }, "type": "array" + }, + "tracked": { + "type": "boolean" } }, "required": [ - "enabled" + "available", + "tracked", + "dryRun" ], "type": "object" }, - "lists": { + "contentControls.setBinding": { "additionalProperties": false, "properties": { - "enabled": { + "available": { + "type": "boolean" + }, + "dryRun": { "type": "boolean" }, "reasons": { @@ -5207,17 +7847,25 @@ _No fields._ ] }, "type": "array" + }, + "tracked": { + "type": "boolean" } }, "required": [ - "enabled" + "available", + "tracked", + "dryRun" ], "type": "object" }, - "trackChanges": { + "contentControls.setLockMode": { "additionalProperties": false, "properties": { - "enabled": { + "available": { + "type": "boolean" + }, + "dryRun": { "type": "boolean" }, "reasons": { @@ -5234,27 +7882,19 @@ _No fields._ ] }, "type": "array" + }, + "tracked": { + "type": "boolean" } }, "required": [ - "enabled" + "available", + "tracked", + "dryRun" ], "type": "object" - } - }, - "required": [ - "trackChanges", - "comments", - "lists", - "dryRun", - "history" - ], - "type": "object" - }, - "operations": { - "additionalProperties": false, - "properties": { - "blocks.delete": { + }, + "contentControls.setType": { "additionalProperties": false, "properties": { "available": { @@ -5289,7 +7929,7 @@ _No fields._ ], "type": "object" }, - "capabilities.get": { + "contentControls.text.clearValue": { "additionalProperties": false, "properties": { "available": { @@ -5324,7 +7964,7 @@ _No fields._ ], "type": "object" }, - "clearContent": { + "contentControls.text.setMultiline": { "additionalProperties": false, "properties": { "available": { @@ -5359,7 +7999,7 @@ _No fields._ ], "type": "object" }, - "comments.create": { + "contentControls.text.setValue": { "additionalProperties": false, "properties": { "available": { @@ -5394,7 +8034,7 @@ _No fields._ ], "type": "object" }, - "comments.delete": { + "contentControls.unwrap": { "additionalProperties": false, "properties": { "available": { @@ -5429,7 +8069,7 @@ _No fields._ ], "type": "object" }, - "comments.get": { + "contentControls.validateWordCompatibility": { "additionalProperties": false, "properties": { "available": { @@ -5464,7 +8104,7 @@ _No fields._ ], "type": "object" }, - "comments.list": { + "contentControls.wrap": { "additionalProperties": false, "properties": { "available": { @@ -5499,7 +8139,7 @@ _No fields._ ], "type": "object" }, - "comments.patch": { + "create.contentControl": { "additionalProperties": false, "properties": { "available": { @@ -13571,7 +16211,62 @@ _No fields._ "hyperlinks.wrap", "hyperlinks.insert", "hyperlinks.patch", - "hyperlinks.remove" + "hyperlinks.remove", + "create.contentControl", + "contentControls.list", + "contentControls.get", + "contentControls.listInRange", + "contentControls.selectByTag", + "contentControls.selectByTitle", + "contentControls.listChildren", + "contentControls.getParent", + "contentControls.wrap", + "contentControls.unwrap", + "contentControls.delete", + "contentControls.copy", + "contentControls.move", + "contentControls.patch", + "contentControls.setLockMode", + "contentControls.setType", + "contentControls.getContent", + "contentControls.replaceContent", + "contentControls.clearContent", + "contentControls.appendContent", + "contentControls.prependContent", + "contentControls.insertBefore", + "contentControls.insertAfter", + "contentControls.getBinding", + "contentControls.setBinding", + "contentControls.clearBinding", + "contentControls.getRawProperties", + "contentControls.patchRawProperties", + "contentControls.validateWordCompatibility", + "contentControls.normalizeWordCompatibility", + "contentControls.normalizeTagPayload", + "contentControls.text.setMultiline", + "contentControls.text.setValue", + "contentControls.text.clearValue", + "contentControls.date.setValue", + "contentControls.date.clearValue", + "contentControls.date.setDisplayFormat", + "contentControls.date.setDisplayLocale", + "contentControls.date.setStorageFormat", + "contentControls.date.setCalendar", + "contentControls.checkbox.getState", + "contentControls.checkbox.setState", + "contentControls.checkbox.toggle", + "contentControls.checkbox.setSymbolPair", + "contentControls.choiceList.getItems", + "contentControls.choiceList.setItems", + "contentControls.choiceList.setSelected", + "contentControls.repeatingSection.listItems", + "contentControls.repeatingSection.insertItemBefore", + "contentControls.repeatingSection.insertItemAfter", + "contentControls.repeatingSection.cloneItem", + "contentControls.repeatingSection.deleteItem", + "contentControls.repeatingSection.setAllowInsertDelete", + "contentControls.group.wrap", + "contentControls.group.ungroup" ], "type": "object" }, diff --git a/apps/docs/document-api/reference/content-controls/append-content.mdx b/apps/docs/document-api/reference/content-controls/append-content.mdx new file mode 100644 index 0000000000..a5778da3c1 --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/append-content.mdx @@ -0,0 +1,326 @@ +--- +title: contentControls.appendContent +sidebarTitle: contentControls.appendContent +description: Append content to the end of a content control. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Append content to the end of a content control. + +- Operation ID: `contentControls.appendContent` +- API member path: `editor.doc.contentControls.appendContent(...)` +- Mutates document: `yes` +- Idempotency: `conditional` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult with the updated target. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `content` | string | yes | | +| `format` | enum | no | `"text"`, `"html"` | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "content": "example", + "format": "text", + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "content": { + "type": "string" + }, + "format": { + "enum": [ + "text", + "html" + ] + }, + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target", + "content" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/checkbox/get-state.mdx b/apps/docs/document-api/reference/content-controls/checkbox/get-state.mdx new file mode 100644 index 0000000000..51e86d9cd4 --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/checkbox/get-state.mdx @@ -0,0 +1,127 @@ +--- +title: contentControls.checkbox.getState +sidebarTitle: contentControls.checkbox.getState +description: Get the checked state of a checkbox content control. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Get the checked state of a checkbox content control. + +- Operation ID: `contentControls.checkbox.getState` +- API member path: `editor.doc.contentControls.checkbox.getState(...)` +- Mutates document: `no` +- Idempotency: `idempotent` +- Supports tracked mode: `no` +- Supports dry run: `no` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a CheckboxGetStateResult with the checked boolean. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `checked` | boolean | yes | | + +### Example response + +```json +{ + "checked": true +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_INPUT` +- `CAPABILITY_UNAVAILABLE` +- `TYPE_MISMATCH` + +## Non-applied failure codes + +- None + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "checked": { + "type": "boolean" + } + }, + "required": [ + "checked" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/checkbox/set-state.mdx b/apps/docs/document-api/reference/content-controls/checkbox/set-state.mdx new file mode 100644 index 0000000000..0f841d3eaf --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/checkbox/set-state.mdx @@ -0,0 +1,319 @@ +--- +title: contentControls.checkbox.setState +sidebarTitle: contentControls.checkbox.setState +description: Set the checked state of a checkbox content control. +--- + +{/* 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 checked state of a checkbox content control. + +- Operation ID: `contentControls.checkbox.setState` +- API member path: `editor.doc.contentControls.checkbox.setState(...)` +- Mutates document: `yes` +- Idempotency: `conditional` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult; reports NO_OP if unchanged. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `checked` | boolean | yes | | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "checked": true, + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` +- `TYPE_MISMATCH` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "checked": { + "type": "boolean" + }, + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target", + "checked" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/checkbox/set-symbol-pair.mdx b/apps/docs/document-api/reference/content-controls/checkbox/set-symbol-pair.mdx new file mode 100644 index 0000000000..2f9a792a71 --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/checkbox/set-symbol-pair.mdx @@ -0,0 +1,325 @@ +--- +title: contentControls.checkbox.setSymbolPair +sidebarTitle: contentControls.checkbox.setSymbolPair +description: Set the checked and unchecked symbol glyphs for a checkbox content control. +--- + +{/* 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 checked and unchecked symbol glyphs for a checkbox content control. + +- Operation ID: `contentControls.checkbox.setSymbolPair` +- API member path: `editor.doc.contentControls.checkbox.setSymbolPair(...)` +- Mutates document: `yes` +- Idempotency: `conditional` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult; reports NO_OP if symbols unchanged. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `checkedSymbol` | object | yes | | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `uncheckedSymbol` | object | yes | | + +### Example request + +```json +{ + "checkedSymbol": {}, + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "uncheckedSymbol": {} +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` +- `TYPE_MISMATCH` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "checkedSymbol": { + "type": "object" + }, + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "uncheckedSymbol": { + "type": "object" + } + }, + "required": [ + "target", + "checkedSymbol", + "uncheckedSymbol" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/checkbox/toggle.mdx b/apps/docs/document-api/reference/content-controls/checkbox/toggle.mdx new file mode 100644 index 0000000000..27ad14c53d --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/checkbox/toggle.mdx @@ -0,0 +1,313 @@ +--- +title: contentControls.checkbox.toggle +sidebarTitle: contentControls.checkbox.toggle +description: Toggle the checked state of a checkbox content control. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Toggle the checked state of a checkbox content control. + +- Operation ID: `contentControls.checkbox.toggle` +- API member path: `editor.doc.contentControls.checkbox.toggle(...)` +- Mutates document: `yes` +- Idempotency: `conditional` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult with the updated state. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` +- `TYPE_MISMATCH` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/choice-list/get-items.mdx b/apps/docs/document-api/reference/content-controls/choice-list/get-items.mdx new file mode 100644 index 0000000000..91c15bf49d --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/choice-list/get-items.mdx @@ -0,0 +1,137 @@ +--- +title: contentControls.choiceList.getItems +sidebarTitle: contentControls.choiceList.getItems +description: Get the list items and selected value of a comboBox or dropDownList content control. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Get the list items and selected value of a comboBox or dropDownList content control. + +- Operation ID: `contentControls.choiceList.getItems` +- API member path: `editor.doc.contentControls.choiceList.getItems(...)` +- Mutates document: `no` +- Idempotency: `idempotent` +- Supports tracked mode: `no` +- Supports dry run: `no` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ChoiceListGetItemsResult with items and selectedValue. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `items` | object[] | yes | | +| `selectedValue` | string | no | | + +### Example response + +```json +{ + "items": [ + {} + ], + "selectedValue": "example" +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_INPUT` +- `CAPABILITY_UNAVAILABLE` +- `TYPE_MISMATCH` + +## Non-applied failure codes + +- None + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "items": { + "items": { + "type": "object" + }, + "type": "array" + }, + "selectedValue": { + "type": "string" + } + }, + "required": [ + "items" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/choice-list/set-items.mdx b/apps/docs/document-api/reference/content-controls/choice-list/set-items.mdx new file mode 100644 index 0000000000..b6951a762d --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/choice-list/set-items.mdx @@ -0,0 +1,324 @@ +--- +title: contentControls.choiceList.setItems +sidebarTitle: contentControls.choiceList.setItems +description: Replace the list items of a comboBox or dropDownList content control. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Replace the list items of a comboBox or dropDownList content control. + +- Operation ID: `contentControls.choiceList.setItems` +- API member path: `editor.doc.contentControls.choiceList.setItems(...)` +- Mutates document: `yes` +- Idempotency: `conditional` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult; reports NO_OP if items unchanged. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `items` | object[] | yes | | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "items": [ + {} + ], + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` +- `TYPE_MISMATCH` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "items": { + "items": { + "type": "object" + }, + "type": "array" + }, + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target", + "items" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/choice-list/set-selected.mdx b/apps/docs/document-api/reference/content-controls/choice-list/set-selected.mdx new file mode 100644 index 0000000000..21a2e9a30e --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/choice-list/set-selected.mdx @@ -0,0 +1,319 @@ +--- +title: contentControls.choiceList.setSelected +sidebarTitle: contentControls.choiceList.setSelected +description: Set the selected value of a comboBox or dropDownList content control. +--- + +{/* 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 selected value of a comboBox or dropDownList content control. + +- Operation ID: `contentControls.choiceList.setSelected` +- API member path: `editor.doc.contentControls.choiceList.setSelected(...)` +- Mutates document: `yes` +- Idempotency: `conditional` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult; reports NO_OP if selection unchanged. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `value` | string | yes | | + +### Example request + +```json +{ + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "value": "example" +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` +- `TYPE_MISMATCH` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "value": { + "type": "string" + } + }, + "required": [ + "target", + "value" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/clear-binding.mdx b/apps/docs/document-api/reference/content-controls/clear-binding.mdx new file mode 100644 index 0000000000..a78b91a63f --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/clear-binding.mdx @@ -0,0 +1,312 @@ +--- +title: contentControls.clearBinding +sidebarTitle: contentControls.clearBinding +description: Remove data binding metadata from a content control. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Remove data binding metadata from a content control. + +- Operation ID: `contentControls.clearBinding` +- API member path: `editor.doc.contentControls.clearBinding(...)` +- Mutates document: `yes` +- Idempotency: `conditional` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult; reports NO_OP if no binding existed. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/clear-content.mdx b/apps/docs/document-api/reference/content-controls/clear-content.mdx new file mode 100644 index 0000000000..e224dee0e4 --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/clear-content.mdx @@ -0,0 +1,312 @@ +--- +title: contentControls.clearContent +sidebarTitle: contentControls.clearContent +description: Clear all content inside a content control, leaving it empty. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Clear all content inside a content control, leaving it empty. + +- Operation ID: `contentControls.clearContent` +- API member path: `editor.doc.contentControls.clearContent(...)` +- Mutates document: `yes` +- Idempotency: `conditional` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult; reports NO_OP if already empty. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/copy.mdx b/apps/docs/document-api/reference/content-controls/copy.mdx new file mode 100644 index 0000000000..0bdbfa8100 --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/copy.mdx @@ -0,0 +1,345 @@ +--- +title: contentControls.copy +sidebarTitle: contentControls.copy +description: Copy a content control to a destination position. Copied SDTs receive new IDs. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Copy a content control to a destination position. Copied SDTs receive new IDs. + +- Operation ID: `contentControls.copy` +- API member path: `editor.doc.contentControls.copy(...)` +- Mutates document: `yes` +- Idempotency: `non-idempotent` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult with the copied content control target. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `destination` | object(nodeType="sdt") | yes | | +| `destination.kind` | enum | yes | `"block"`, `"inline"` | +| `destination.nodeId` | string | yes | | +| `destination.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "destination": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "destination": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target", + "destination" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/create.mdx b/apps/docs/document-api/reference/content-controls/create.mdx new file mode 100644 index 0000000000..d396e7dce5 --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/create.mdx @@ -0,0 +1,347 @@ +--- +title: create.contentControl +sidebarTitle: create.contentControl +description: Create a new content control (SDT) in the document. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Create a new content control (SDT) in the document. + +- Operation ID: `create.contentControl` +- API member path: `editor.doc.create.contentControl(...)` +- Mutates document: `yes` +- Idempotency: `non-idempotent` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult with the created content control target. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `alias` | string | no | | +| `content` | string | no | | +| `controlType` | string | no | | +| `kind` | enum | yes | `"block"`, `"inline"` | +| `lockMode` | enum | no | `"unlocked"`, `"sdtLocked"`, `"contentLocked"`, `"sdtContentLocked"` | +| `tag` | string | no | | +| `target` | object(nodeType="sdt") | no | | +| `target.kind` | enum | no | `"block"`, `"inline"` | +| `target.nodeId` | string | no | | +| `target.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Example request + +```json +{ + "controlType": "example", + "kind": "block", + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` + +## Non-applied failure codes + +- `INVALID_TARGET` +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "alias": { + "type": "string" + }, + "content": { + "type": "string" + }, + "controlType": { + "type": "string" + }, + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "lockMode": { + "enum": [ + "unlocked", + "sdtLocked", + "contentLocked", + "sdtContentLocked" + ] + }, + "tag": { + "type": "string" + }, + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "kind" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/date/clear-value.mdx b/apps/docs/document-api/reference/content-controls/date/clear-value.mdx new file mode 100644 index 0000000000..634a5f8834 --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/date/clear-value.mdx @@ -0,0 +1,313 @@ +--- +title: contentControls.date.clearValue +sidebarTitle: contentControls.date.clearValue +description: Clear the date value of a date content control. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Clear the date value of a date content control. + +- Operation ID: `contentControls.date.clearValue` +- API member path: `editor.doc.contentControls.date.clearValue(...)` +- Mutates document: `yes` +- Idempotency: `conditional` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult; reports NO_OP if already empty. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` +- `TYPE_MISMATCH` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/date/set-calendar.mdx b/apps/docs/document-api/reference/content-controls/date/set-calendar.mdx new file mode 100644 index 0000000000..0c78905020 --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/date/set-calendar.mdx @@ -0,0 +1,319 @@ +--- +title: contentControls.date.setCalendar +sidebarTitle: contentControls.date.setCalendar +description: Set the calendar type for a date content control. +--- + +{/* 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 calendar type for a date content control. + +- Operation ID: `contentControls.date.setCalendar` +- API member path: `editor.doc.contentControls.date.setCalendar(...)` +- Mutates document: `yes` +- Idempotency: `conditional` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult; reports NO_OP if unchanged. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `calendar` | string | yes | | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "calendar": "example", + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` +- `TYPE_MISMATCH` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "calendar": { + "type": "string" + }, + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target", + "calendar" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/date/set-display-format.mdx b/apps/docs/document-api/reference/content-controls/date/set-display-format.mdx new file mode 100644 index 0000000000..d29ef5a068 --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/date/set-display-format.mdx @@ -0,0 +1,319 @@ +--- +title: contentControls.date.setDisplayFormat +sidebarTitle: contentControls.date.setDisplayFormat +description: Set the display format string for a date content control. +--- + +{/* 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 display format string for a date content control. + +- Operation ID: `contentControls.date.setDisplayFormat` +- API member path: `editor.doc.contentControls.date.setDisplayFormat(...)` +- Mutates document: `yes` +- Idempotency: `conditional` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult; reports NO_OP if unchanged. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `format` | string | yes | | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "format": "example", + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` +- `TYPE_MISMATCH` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "format": { + "type": "string" + }, + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target", + "format" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/date/set-display-locale.mdx b/apps/docs/document-api/reference/content-controls/date/set-display-locale.mdx new file mode 100644 index 0000000000..393467744c --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/date/set-display-locale.mdx @@ -0,0 +1,319 @@ +--- +title: contentControls.date.setDisplayLocale +sidebarTitle: contentControls.date.setDisplayLocale +description: Set the display locale for a date content control. +--- + +{/* 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 display locale for a date content control. + +- Operation ID: `contentControls.date.setDisplayLocale` +- API member path: `editor.doc.contentControls.date.setDisplayLocale(...)` +- Mutates document: `yes` +- Idempotency: `conditional` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult; reports NO_OP if unchanged. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `locale` | string | yes | | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "locale": "example", + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` +- `TYPE_MISMATCH` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "locale": { + "type": "string" + }, + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target", + "locale" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/date/set-storage-format.mdx b/apps/docs/document-api/reference/content-controls/date/set-storage-format.mdx new file mode 100644 index 0000000000..7a45180cf3 --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/date/set-storage-format.mdx @@ -0,0 +1,319 @@ +--- +title: contentControls.date.setStorageFormat +sidebarTitle: contentControls.date.setStorageFormat +description: Set the XML storage format for a date content control. +--- + +{/* 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 XML storage format for a date content control. + +- Operation ID: `contentControls.date.setStorageFormat` +- API member path: `editor.doc.contentControls.date.setStorageFormat(...)` +- Mutates document: `yes` +- Idempotency: `conditional` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult; reports NO_OP if unchanged. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `format` | string | yes | | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "format": "example", + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` +- `TYPE_MISMATCH` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "format": { + "type": "string" + }, + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target", + "format" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/date/set-value.mdx b/apps/docs/document-api/reference/content-controls/date/set-value.mdx new file mode 100644 index 0000000000..a6efe3ef75 --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/date/set-value.mdx @@ -0,0 +1,319 @@ +--- +title: contentControls.date.setValue +sidebarTitle: contentControls.date.setValue +description: Set the date value of a date content control. +--- + +{/* 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 date value of a date content control. + +- Operation ID: `contentControls.date.setValue` +- API member path: `editor.doc.contentControls.date.setValue(...)` +- Mutates document: `yes` +- Idempotency: `conditional` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult; reports NO_OP if unchanged. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `value` | string | yes | | + +### Example request + +```json +{ + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "value": "example" +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` +- `TYPE_MISMATCH` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "value": { + "type": "string" + } + }, + "required": [ + "target", + "value" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/delete.mdx b/apps/docs/document-api/reference/content-controls/delete.mdx new file mode 100644 index 0000000000..fe965ad1c6 --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/delete.mdx @@ -0,0 +1,312 @@ +--- +title: contentControls.delete +sidebarTitle: contentControls.delete +description: Delete a content control and its content from the document. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Delete a content control and its content from the document. + +- Operation ID: `contentControls.delete` +- API member path: `editor.doc.contentControls.delete(...)` +- Mutates document: `yes` +- Idempotency: `conditional` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult; reports NO_OP if already removed. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/get-binding.mdx b/apps/docs/document-api/reference/content-controls/get-binding.mdx new file mode 100644 index 0000000000..58151f758b --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/get-binding.mdx @@ -0,0 +1,150 @@ +--- +title: contentControls.getBinding +sidebarTitle: contentControls.getBinding +description: "Get the data binding metadata (w:dataBinding) of a content control." +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Get the data binding metadata (w:dataBinding) of a content control. + +- Operation ID: `contentControls.getBinding` +- API member path: `editor.doc.contentControls.getBinding(...)` +- Mutates document: `no` +- Idempotency: `idempotent` +- Supports tracked mode: `no` +- Supports dry run: `no` +- Deterministic target resolution: `yes` + +## Expected result + +Returns the ContentControlBinding or null if no binding is set. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `prefixMappings` | string | no | | +| `storeItemId` | string | yes | | +| `xpath` | string | yes | | + +### Variant 2 + +_No fields._ + +### Example response + +```json +{ + "prefixMappings": "example", + "storeItemId": "example", + "xpath": "example" +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_INPUT` +- `CAPABILITY_UNAVAILABLE` + +## Non-applied failure codes + +- None + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "prefixMappings": { + "type": "string" + }, + "storeItemId": { + "type": "string" + }, + "xpath": { + "type": "string" + } + }, + "required": [ + "storeItemId", + "xpath" + ], + "type": "object" + }, + { + "type": "null" + } + ] +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/get-content.mdx b/apps/docs/document-api/reference/content-controls/get-content.mdx new file mode 100644 index 0000000000..3207ae87f3 --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/get-content.mdx @@ -0,0 +1,135 @@ +--- +title: contentControls.getContent +sidebarTitle: contentControls.getContent +description: Get the text content of a content control. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Get the text content of a content control. + +- Operation ID: `contentControls.getContent` +- API member path: `editor.doc.contentControls.getContent(...)` +- Mutates document: `no` +- Idempotency: `idempotent` +- Supports tracked mode: `no` +- Supports dry run: `no` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlsGetContentResult with the content string and format. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `content` | string | yes | | +| `format` | enum | yes | `"text"`, `"html"` | + +### Example response + +```json +{ + "content": "example", + "format": "text" +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_INPUT` +- `CAPABILITY_UNAVAILABLE` + +## Non-applied failure codes + +- None + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "content": { + "type": "string" + }, + "format": { + "enum": [ + "text", + "html" + ] + } + }, + "required": [ + "content", + "format" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/get-parent.mdx b/apps/docs/document-api/reference/content-controls/get-parent.mdx new file mode 100644 index 0000000000..c9bfcf34c1 --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/get-parent.mdx @@ -0,0 +1,127 @@ +--- +title: contentControls.getParent +sidebarTitle: contentControls.getParent +description: Get the parent content control of the target, if any. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Get the parent content control of the target, if any. + +- Operation ID: `contentControls.getParent` +- API member path: `editor.doc.contentControls.getParent(...)` +- Mutates document: `no` +- Idempotency: `idempotent` +- Supports tracked mode: `no` +- Supports dry run: `no` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlInfo for the parent, or null if no parent SDT exists. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 + +_No fields._ + +### Variant 2 + +_No fields._ + +### Example response + +```json +{} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_INPUT` +- `CAPABILITY_UNAVAILABLE` + +## Non-applied failure codes + +- None + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "description": "ContentControlInfo", + "type": "object" + }, + { + "type": "null" + } + ] +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/get-raw-properties.mdx b/apps/docs/document-api/reference/content-controls/get-raw-properties.mdx new file mode 100644 index 0000000000..97b9f1434c --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/get-raw-properties.mdx @@ -0,0 +1,126 @@ +--- +title: contentControls.getRawProperties +sidebarTitle: contentControls.getRawProperties +description: Get the raw sdtPr properties of a content control as a passthrough hash. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Get the raw sdtPr properties of a content control as a passthrough hash. + +- Operation ID: `contentControls.getRawProperties` +- API member path: `editor.doc.contentControls.getRawProperties(...)` +- Mutates document: `no` +- Idempotency: `idempotent` +- Supports tracked mode: `no` +- Supports dry run: `no` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlsGetRawPropertiesResult with the raw properties. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `properties` | object | yes | | + +### Example response + +```json +{ + "properties": {} +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_INPUT` +- `CAPABILITY_UNAVAILABLE` + +## Non-applied failure codes + +- None + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "properties": { + "type": "object" + } + }, + "required": [ + "properties" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/get.mdx b/apps/docs/document-api/reference/content-controls/get.mdx new file mode 100644 index 0000000000..4491103451 --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/get.mdx @@ -0,0 +1,114 @@ +--- +title: contentControls.get +sidebarTitle: contentControls.get +description: Retrieve a single content control by target. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Retrieve a single content control by target. + +- Operation ID: `contentControls.get` +- API member path: `editor.doc.contentControls.get(...)` +- Mutates document: `no` +- Idempotency: `idempotent` +- Supports tracked mode: `no` +- Supports dry run: `no` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlInfo with full properties. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +_No fields._ + +### Example response + +```json +{} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_INPUT` +- `CAPABILITY_UNAVAILABLE` + +## Non-applied failure codes + +- None + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target" + ], + "type": "object" +} +``` + + + +```json +{ + "description": "ContentControlInfo", + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/group/ungroup.mdx b/apps/docs/document-api/reference/content-controls/group/ungroup.mdx new file mode 100644 index 0000000000..1955d73f5e --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/group/ungroup.mdx @@ -0,0 +1,313 @@ +--- +title: contentControls.group.ungroup +sidebarTitle: contentControls.group.ungroup +description: Remove the group designation from a group content control. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Remove the group designation from a group content control. + +- Operation ID: `contentControls.group.ungroup` +- API member path: `editor.doc.contentControls.group.ungroup(...)` +- Mutates document: `yes` +- Idempotency: `conditional` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult; reports NO_OP if not a group. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` +- `TYPE_MISMATCH` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/group/wrap.mdx b/apps/docs/document-api/reference/content-controls/group/wrap.mdx new file mode 100644 index 0000000000..ac2090bf15 --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/group/wrap.mdx @@ -0,0 +1,312 @@ +--- +title: contentControls.group.wrap +sidebarTitle: contentControls.group.wrap +description: Wrap a content control inside a new group content control. Always nests; not idempotent. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Wrap a content control inside a new group content control. Always nests; not idempotent. + +- Operation ID: `contentControls.group.wrap` +- API member path: `editor.doc.contentControls.group.wrap(...)` +- Mutates document: `yes` +- Idempotency: `non-idempotent` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult with the new group wrapper target. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` + +## Non-applied failure codes + +- None + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/index.mdx b/apps/docs/document-api/reference/content-controls/index.mdx new file mode 100644 index 0000000000..df499c8f6e --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/index.mdx @@ -0,0 +1,72 @@ +--- +title: Content Controls operations +sidebarTitle: Content Controls +description: Content Controls operation reference from the canonical Document API contract. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +[Back to full reference](../index) + +Content control (SDT) discovery, mutation, typed controls, and Word compatibility. + +| Operation | Member path | Mutates | Idempotency | Tracked | Dry run | +| --- | --- | --- | --- | --- | --- | +| create.contentControl | `create.contentControl` | Yes | `non-idempotent` | No | Yes | +| contentControls.list | `contentControls.list` | No | `idempotent` | No | No | +| contentControls.get | `contentControls.get` | No | `idempotent` | No | No | +| contentControls.listInRange | `contentControls.listInRange` | No | `idempotent` | No | No | +| contentControls.selectByTag | `contentControls.selectByTag` | No | `idempotent` | No | No | +| contentControls.selectByTitle | `contentControls.selectByTitle` | No | `idempotent` | No | No | +| contentControls.listChildren | `contentControls.listChildren` | No | `idempotent` | No | No | +| contentControls.getParent | `contentControls.getParent` | No | `idempotent` | No | No | +| contentControls.wrap | `contentControls.wrap` | Yes | `conditional` | No | Yes | +| contentControls.unwrap | `contentControls.unwrap` | Yes | `conditional` | No | Yes | +| contentControls.delete | `contentControls.delete` | Yes | `conditional` | No | Yes | +| contentControls.copy | `contentControls.copy` | Yes | `non-idempotent` | No | Yes | +| contentControls.move | `contentControls.move` | Yes | `conditional` | No | Yes | +| contentControls.patch | `contentControls.patch` | Yes | `conditional` | No | Yes | +| contentControls.setLockMode | `contentControls.setLockMode` | Yes | `conditional` | No | Yes | +| contentControls.setType | `contentControls.setType` | Yes | `conditional` | No | Yes | +| contentControls.getContent | `contentControls.getContent` | No | `idempotent` | No | No | +| contentControls.replaceContent | `contentControls.replaceContent` | Yes | `conditional` | No | Yes | +| contentControls.clearContent | `contentControls.clearContent` | Yes | `conditional` | No | Yes | +| contentControls.appendContent | `contentControls.appendContent` | Yes | `conditional` | No | Yes | +| contentControls.prependContent | `contentControls.prependContent` | Yes | `conditional` | No | Yes | +| contentControls.insertBefore | `contentControls.insertBefore` | Yes | `conditional` | No | Yes | +| contentControls.insertAfter | `contentControls.insertAfter` | Yes | `conditional` | No | Yes | +| contentControls.getBinding | `contentControls.getBinding` | No | `idempotent` | No | No | +| contentControls.setBinding | `contentControls.setBinding` | Yes | `conditional` | No | Yes | +| contentControls.clearBinding | `contentControls.clearBinding` | Yes | `conditional` | No | Yes | +| contentControls.getRawProperties | `contentControls.getRawProperties` | No | `idempotent` | No | No | +| contentControls.patchRawProperties | `contentControls.patchRawProperties` | Yes | `conditional` | No | Yes | +| contentControls.validateWordCompatibility | `contentControls.validateWordCompatibility` | No | `idempotent` | No | No | +| contentControls.normalizeWordCompatibility | `contentControls.normalizeWordCompatibility` | Yes | `idempotent` | No | Yes | +| contentControls.normalizeTagPayload | `contentControls.normalizeTagPayload` | Yes | `idempotent` | No | Yes | +| contentControls.text.setMultiline | `contentControls.text.setMultiline` | Yes | `conditional` | No | Yes | +| contentControls.text.setValue | `contentControls.text.setValue` | Yes | `conditional` | No | Yes | +| contentControls.text.clearValue | `contentControls.text.clearValue` | Yes | `conditional` | No | Yes | +| contentControls.date.setValue | `contentControls.date.setValue` | Yes | `conditional` | No | Yes | +| contentControls.date.clearValue | `contentControls.date.clearValue` | Yes | `conditional` | No | Yes | +| contentControls.date.setDisplayFormat | `contentControls.date.setDisplayFormat` | Yes | `conditional` | No | Yes | +| contentControls.date.setDisplayLocale | `contentControls.date.setDisplayLocale` | Yes | `conditional` | No | Yes | +| contentControls.date.setStorageFormat | `contentControls.date.setStorageFormat` | Yes | `conditional` | No | Yes | +| contentControls.date.setCalendar | `contentControls.date.setCalendar` | Yes | `conditional` | No | Yes | +| contentControls.checkbox.getState | `contentControls.checkbox.getState` | No | `idempotent` | No | No | +| contentControls.checkbox.setState | `contentControls.checkbox.setState` | Yes | `conditional` | No | Yes | +| contentControls.checkbox.toggle | `contentControls.checkbox.toggle` | Yes | `conditional` | No | Yes | +| contentControls.checkbox.setSymbolPair | `contentControls.checkbox.setSymbolPair` | Yes | `conditional` | No | Yes | +| contentControls.choiceList.getItems | `contentControls.choiceList.getItems` | No | `idempotent` | No | No | +| contentControls.choiceList.setItems | `contentControls.choiceList.setItems` | Yes | `conditional` | No | Yes | +| contentControls.choiceList.setSelected | `contentControls.choiceList.setSelected` | Yes | `conditional` | No | Yes | +| contentControls.repeatingSection.listItems | `contentControls.repeatingSection.listItems` | No | `idempotent` | No | No | +| contentControls.repeatingSection.insertItemBefore | `contentControls.repeatingSection.insertItemBefore` | Yes | `non-idempotent` | No | Yes | +| contentControls.repeatingSection.insertItemAfter | `contentControls.repeatingSection.insertItemAfter` | Yes | `non-idempotent` | No | Yes | +| contentControls.repeatingSection.cloneItem | `contentControls.repeatingSection.cloneItem` | Yes | `non-idempotent` | No | Yes | +| contentControls.repeatingSection.deleteItem | `contentControls.repeatingSection.deleteItem` | Yes | `conditional` | No | Yes | +| contentControls.repeatingSection.setAllowInsertDelete | `contentControls.repeatingSection.setAllowInsertDelete` | Yes | `conditional` | No | Yes | +| contentControls.group.wrap | `contentControls.group.wrap` | Yes | `non-idempotent` | No | Yes | +| contentControls.group.ungroup | `contentControls.group.ungroup` | Yes | `conditional` | No | Yes | + diff --git a/apps/docs/document-api/reference/content-controls/insert-after.mdx b/apps/docs/document-api/reference/content-controls/insert-after.mdx new file mode 100644 index 0000000000..cbf717af6c --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/insert-after.mdx @@ -0,0 +1,326 @@ +--- +title: contentControls.insertAfter +sidebarTitle: contentControls.insertAfter +description: Insert content immediately after a content control. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Insert content immediately after a content control. + +- Operation ID: `contentControls.insertAfter` +- API member path: `editor.doc.contentControls.insertAfter(...)` +- Mutates document: `yes` +- Idempotency: `conditional` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult with the target. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `content` | string | yes | | +| `format` | enum | no | `"text"`, `"html"` | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "content": "example", + "format": "text", + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "content": { + "type": "string" + }, + "format": { + "enum": [ + "text", + "html" + ] + }, + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target", + "content" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/insert-before.mdx b/apps/docs/document-api/reference/content-controls/insert-before.mdx new file mode 100644 index 0000000000..02aa95049e --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/insert-before.mdx @@ -0,0 +1,326 @@ +--- +title: contentControls.insertBefore +sidebarTitle: contentControls.insertBefore +description: Insert content immediately before a content control. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Insert content immediately before a content control. + +- Operation ID: `contentControls.insertBefore` +- API member path: `editor.doc.contentControls.insertBefore(...)` +- Mutates document: `yes` +- Idempotency: `conditional` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult with the target. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `content` | string | yes | | +| `format` | enum | no | `"text"`, `"html"` | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "content": "example", + "format": "text", + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "content": { + "type": "string" + }, + "format": { + "enum": [ + "text", + "html" + ] + }, + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target", + "content" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/list-children.mdx b/apps/docs/document-api/reference/content-controls/list-children.mdx new file mode 100644 index 0000000000..a9fa28979d --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/list-children.mdx @@ -0,0 +1,147 @@ +--- +title: contentControls.listChildren +sidebarTitle: contentControls.listChildren +description: List direct child content controls nested inside the target. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +List direct child content controls nested inside the target. + +- Operation ID: `contentControls.listChildren` +- API member path: `editor.doc.contentControls.listChildren(...)` +- Mutates document: `no` +- Idempotency: `idempotent` +- Supports tracked mode: `no` +- Supports dry run: `no` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlsListResult with child items. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `limit` | integer | no | | +| `offset` | integer | no | | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "limit": 50, + "offset": 0, + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `items` | object[] | yes | | +| `total` | integer | yes | | + +### Example response + +```json +{ + "items": [ + {} + ], + "total": 1 +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_INPUT` +- `CAPABILITY_UNAVAILABLE` + +## Non-applied failure codes + +- None + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "limit": { + "type": "integer" + }, + "offset": { + "type": "integer" + }, + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "items": { + "items": { + "type": "object" + }, + "type": "array" + }, + "total": { + "type": "integer" + } + }, + "required": [ + "items", + "total" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/list-in-range.mdx b/apps/docs/document-api/reference/content-controls/list-in-range.mdx new file mode 100644 index 0000000000..6c2bcdb57e --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/list-in-range.mdx @@ -0,0 +1,126 @@ +--- +title: contentControls.listInRange +sidebarTitle: contentControls.listInRange +description: List content controls within a block range. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +List content controls within a block range. + +- Operation ID: `contentControls.listInRange` +- API member path: `editor.doc.contentControls.listInRange(...)` +- Mutates document: `no` +- Idempotency: `idempotent` +- Supports tracked mode: `no` +- Supports dry run: `no` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlsListResult scoped to the range. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `endBlockId` | string | yes | | +| `limit` | integer | no | | +| `offset` | integer | no | | +| `startBlockId` | string | yes | | + +### Example request + +```json +{ + "endBlockId": "example", + "limit": 50, + "offset": 0, + "startBlockId": "example" +} +``` + +## Output fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `items` | object[] | yes | | +| `total` | integer | yes | | + +### Example response + +```json +{ + "items": [ + {} + ], + "total": 1 +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_INPUT` +- `CAPABILITY_UNAVAILABLE` + +## Non-applied failure codes + +- None + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "endBlockId": { + "type": "string" + }, + "limit": { + "type": "integer" + }, + "offset": { + "type": "integer" + }, + "startBlockId": { + "type": "string" + } + }, + "required": [ + "startBlockId", + "endBlockId" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "items": { + "items": { + "type": "object" + }, + "type": "array" + }, + "total": { + "type": "integer" + } + }, + "required": [ + "items", + "total" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/list.mdx b/apps/docs/document-api/reference/content-controls/list.mdx new file mode 100644 index 0000000000..ccd83ca8a4 --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/list.mdx @@ -0,0 +1,120 @@ +--- +title: contentControls.list +sidebarTitle: contentControls.list +description: List all content controls in the document with optional type/tag filtering. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +List all content controls in the document with optional type/tag filtering. + +- Operation ID: `contentControls.list` +- API member path: `editor.doc.contentControls.list(...)` +- Mutates document: `no` +- Idempotency: `idempotent` +- Supports tracked mode: `no` +- Supports dry run: `no` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlsListResult with items and total count. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `controlType` | string | no | | +| `limit` | integer | no | | +| `offset` | integer | no | | +| `tag` | string | no | | + +### Example request + +```json +{ + "controlType": "example", + "tag": "example" +} +``` + +## Output fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `items` | object[] | yes | | +| `total` | integer | yes | | + +### Example response + +```json +{ + "items": [ + {} + ], + "total": 1 +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_INPUT` +- `CAPABILITY_UNAVAILABLE` + +## Non-applied failure codes + +- None + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "controlType": { + "type": "string" + }, + "limit": { + "type": "integer" + }, + "offset": { + "type": "integer" + }, + "tag": { + "type": "string" + } + }, + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "items": { + "items": { + "type": "object" + }, + "type": "array" + }, + "total": { + "type": "integer" + } + }, + "required": [ + "items", + "total" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/move.mdx b/apps/docs/document-api/reference/content-controls/move.mdx new file mode 100644 index 0000000000..b35ab3d20a --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/move.mdx @@ -0,0 +1,345 @@ +--- +title: contentControls.move +sidebarTitle: contentControls.move +description: Move a content control to a new position. Preserves original IDs. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Move a content control to a new position. Preserves original IDs. + +- Operation ID: `contentControls.move` +- API member path: `editor.doc.contentControls.move(...)` +- Mutates document: `yes` +- Idempotency: `conditional` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult with the updated target position. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `destination` | object(nodeType="sdt") | yes | | +| `destination.kind` | enum | yes | `"block"`, `"inline"` | +| `destination.nodeId` | string | yes | | +| `destination.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "destination": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "destination": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target", + "destination" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/normalize-tag-payload.mdx b/apps/docs/document-api/reference/content-controls/normalize-tag-payload.mdx new file mode 100644 index 0000000000..fa8855ba35 --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/normalize-tag-payload.mdx @@ -0,0 +1,309 @@ +--- +title: contentControls.normalizeTagPayload +sidebarTitle: contentControls.normalizeTagPayload +description: Normalize a content control tag between plain-string and JSON-encoded formats. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Normalize a content control tag between plain-string and JSON-encoded formats. + +- Operation ID: `contentControls.normalizeTagPayload` +- API member path: `editor.doc.contentControls.normalizeTagPayload(...)` +- Mutates document: `yes` +- Idempotency: `idempotent` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult; reports NO_OP if already normalized. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_INPUT` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/normalize-word-compatibility.mdx b/apps/docs/document-api/reference/content-controls/normalize-word-compatibility.mdx new file mode 100644 index 0000000000..e15eca7b91 --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/normalize-word-compatibility.mdx @@ -0,0 +1,309 @@ +--- +title: contentControls.normalizeWordCompatibility +sidebarTitle: contentControls.normalizeWordCompatibility +description: Normalize a content control to resolve Word compatibility issues. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Normalize a content control to resolve Word compatibility issues. + +- Operation ID: `contentControls.normalizeWordCompatibility` +- API member path: `editor.doc.contentControls.normalizeWordCompatibility(...)` +- Mutates document: `yes` +- Idempotency: `idempotent` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult; reports NO_OP if already compatible. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_INPUT` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/patch-raw-properties.mdx b/apps/docs/document-api/reference/content-controls/patch-raw-properties.mdx new file mode 100644 index 0000000000..7438dbde0e --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/patch-raw-properties.mdx @@ -0,0 +1,320 @@ +--- +title: contentControls.patchRawProperties +sidebarTitle: contentControls.patchRawProperties +description: Apply raw XML-level patches to the sdtPr subtree of a content control. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Apply raw XML-level patches to the sdtPr subtree of a content control. + +- Operation ID: `contentControls.patchRawProperties` +- API member path: `editor.doc.contentControls.patchRawProperties(...)` +- Mutates document: `yes` +- Idempotency: `conditional` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult; reports NO_OP if no effective changes. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `patches` | object[] | yes | | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "patches": [ + {} + ], + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_INPUT` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "patches": { + "items": { + "type": "object" + }, + "type": "array" + }, + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target", + "patches" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/patch.mdx b/apps/docs/document-api/reference/content-controls/patch.mdx new file mode 100644 index 0000000000..325d55fa32 --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/patch.mdx @@ -0,0 +1,346 @@ +--- +title: contentControls.patch +sidebarTitle: contentControls.patch +description: Patch metadata properties on a content control (tag, alias, appearance, color, etc.). +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Patch metadata properties on a content control (tag, alias, appearance, color, etc.). + +- Operation ID: `contentControls.patch` +- API member path: `editor.doc.contentControls.patch(...)` +- Mutates document: `yes` +- Idempotency: `conditional` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult; reports NO_OP if no fields changed. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `alias` | any | no | | +| `appearance` | enum | no | `"boundingBox"`, `"tags"`, `"hidden"` | +| `color` | string | no | | +| `placeholder` | string | no | | +| `showingPlaceholder` | boolean | no | | +| `tabIndex` | integer | no | | +| `tag` | any | no | | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `temporary` | boolean | no | | + +### Example request + +```json +{ + "alias": {}, + "tag": {}, + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "alias": {}, + "appearance": { + "enum": [ + "boundingBox", + "tags", + "hidden" + ] + }, + "color": { + "type": "string" + }, + "placeholder": { + "type": "string" + }, + "showingPlaceholder": { + "type": "boolean" + }, + "tabIndex": { + "type": "integer" + }, + "tag": {}, + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "temporary": { + "type": "boolean" + } + }, + "required": [ + "target" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/prepend-content.mdx b/apps/docs/document-api/reference/content-controls/prepend-content.mdx new file mode 100644 index 0000000000..be2cd53a80 --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/prepend-content.mdx @@ -0,0 +1,326 @@ +--- +title: contentControls.prependContent +sidebarTitle: contentControls.prependContent +description: Prepend content to the beginning of a content control. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Prepend content to the beginning of a content control. + +- Operation ID: `contentControls.prependContent` +- API member path: `editor.doc.contentControls.prependContent(...)` +- Mutates document: `yes` +- Idempotency: `conditional` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult with the updated target. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `content` | string | yes | | +| `format` | enum | no | `"text"`, `"html"` | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "content": "example", + "format": "text", + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "content": { + "type": "string" + }, + "format": { + "enum": [ + "text", + "html" + ] + }, + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target", + "content" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/repeating-section/clone-item.mdx b/apps/docs/document-api/reference/content-controls/repeating-section/clone-item.mdx new file mode 100644 index 0000000000..53884f219f --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/repeating-section/clone-item.mdx @@ -0,0 +1,319 @@ +--- +title: contentControls.repeatingSection.cloneItem +sidebarTitle: contentControls.repeatingSection.cloneItem +description: Clone a repeating section item at the given index. Cloned SDTs receive new IDs. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Clone a repeating section item at the given index. Cloned SDTs receive new IDs. + +- Operation ID: `contentControls.repeatingSection.cloneItem` +- API member path: `editor.doc.contentControls.repeatingSection.cloneItem(...)` +- Mutates document: `yes` +- Idempotency: `non-idempotent` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult with the cloned item target. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `index` | integer | yes | | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "index": 1, + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` +- `TYPE_MISMATCH` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "index": { + "type": "integer" + }, + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target", + "index" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/repeating-section/delete-item.mdx b/apps/docs/document-api/reference/content-controls/repeating-section/delete-item.mdx new file mode 100644 index 0000000000..eada2a2f99 --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/repeating-section/delete-item.mdx @@ -0,0 +1,319 @@ +--- +title: contentControls.repeatingSection.deleteItem +sidebarTitle: contentControls.repeatingSection.deleteItem +description: Delete a repeating section item at the given index. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Delete a repeating section item at the given index. + +- Operation ID: `contentControls.repeatingSection.deleteItem` +- API member path: `editor.doc.contentControls.repeatingSection.deleteItem(...)` +- Mutates document: `yes` +- Idempotency: `conditional` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult; reports NO_OP if item does not exist. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `index` | integer | yes | | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "index": 1, + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` +- `TYPE_MISMATCH` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "index": { + "type": "integer" + }, + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target", + "index" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/repeating-section/insert-item-after.mdx b/apps/docs/document-api/reference/content-controls/repeating-section/insert-item-after.mdx new file mode 100644 index 0000000000..6dc99296e7 --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/repeating-section/insert-item-after.mdx @@ -0,0 +1,319 @@ +--- +title: contentControls.repeatingSection.insertItemAfter +sidebarTitle: contentControls.repeatingSection.insertItemAfter +description: Insert a new item after a specific index in a repeating section. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Insert a new item after a specific index in a repeating section. + +- Operation ID: `contentControls.repeatingSection.insertItemAfter` +- API member path: `editor.doc.contentControls.repeatingSection.insertItemAfter(...)` +- Mutates document: `yes` +- Idempotency: `non-idempotent` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult with the new item target. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `index` | integer | yes | | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "index": 1, + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` +- `TYPE_MISMATCH` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "index": { + "type": "integer" + }, + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target", + "index" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/repeating-section/insert-item-before.mdx b/apps/docs/document-api/reference/content-controls/repeating-section/insert-item-before.mdx new file mode 100644 index 0000000000..8fd333fc46 --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/repeating-section/insert-item-before.mdx @@ -0,0 +1,319 @@ +--- +title: contentControls.repeatingSection.insertItemBefore +sidebarTitle: contentControls.repeatingSection.insertItemBefore +description: Insert a new item before a specific index in a repeating section. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Insert a new item before a specific index in a repeating section. + +- Operation ID: `contentControls.repeatingSection.insertItemBefore` +- API member path: `editor.doc.contentControls.repeatingSection.insertItemBefore(...)` +- Mutates document: `yes` +- Idempotency: `non-idempotent` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult with the new item target. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `index` | integer | yes | | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "index": 1, + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` +- `TYPE_MISMATCH` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "index": { + "type": "integer" + }, + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target", + "index" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/repeating-section/list-items.mdx b/apps/docs/document-api/reference/content-controls/repeating-section/list-items.mdx new file mode 100644 index 0000000000..f343654163 --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/repeating-section/list-items.mdx @@ -0,0 +1,138 @@ +--- +title: contentControls.repeatingSection.listItems +sidebarTitle: contentControls.repeatingSection.listItems +description: List the repeating section items inside a repeating section content control. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +List the repeating section items inside a repeating section content control. + +- Operation ID: `contentControls.repeatingSection.listItems` +- API member path: `editor.doc.contentControls.repeatingSection.listItems(...)` +- Mutates document: `no` +- Idempotency: `idempotent` +- Supports tracked mode: `no` +- Supports dry run: `no` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a RepeatingSectionListItemsResult with child item info. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `items` | object[] | yes | | +| `total` | integer | yes | | + +### Example response + +```json +{ + "items": [ + {} + ], + "total": 1 +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_INPUT` +- `CAPABILITY_UNAVAILABLE` +- `TYPE_MISMATCH` + +## Non-applied failure codes + +- None + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "items": { + "items": { + "type": "object" + }, + "type": "array" + }, + "total": { + "type": "integer" + } + }, + "required": [ + "items", + "total" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/repeating-section/set-allow-insert-delete.mdx b/apps/docs/document-api/reference/content-controls/repeating-section/set-allow-insert-delete.mdx new file mode 100644 index 0000000000..d1687c00f1 --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/repeating-section/set-allow-insert-delete.mdx @@ -0,0 +1,319 @@ +--- +title: contentControls.repeatingSection.setAllowInsertDelete +sidebarTitle: contentControls.repeatingSection.setAllowInsertDelete +description: Set the allowInsertDelete flag on a repeating section. +--- + +{/* 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 allowInsertDelete flag on a repeating section. + +- Operation ID: `contentControls.repeatingSection.setAllowInsertDelete` +- API member path: `editor.doc.contentControls.repeatingSection.setAllowInsertDelete(...)` +- Mutates document: `yes` +- Idempotency: `conditional` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult; reports NO_OP if unchanged. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `allow` | boolean | yes | | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "allow": true, + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` +- `TYPE_MISMATCH` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "allow": { + "type": "boolean" + }, + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target", + "allow" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/replace-content.mdx b/apps/docs/document-api/reference/content-controls/replace-content.mdx new file mode 100644 index 0000000000..a4d16ce83c --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/replace-content.mdx @@ -0,0 +1,326 @@ +--- +title: contentControls.replaceContent +sidebarTitle: contentControls.replaceContent +description: Replace the entire content of a content control. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Replace the entire content of a content control. + +- Operation ID: `contentControls.replaceContent` +- API member path: `editor.doc.contentControls.replaceContent(...)` +- Mutates document: `yes` +- Idempotency: `conditional` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult with the updated target. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `content` | string | yes | | +| `format` | enum | no | `"text"`, `"html"` | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "content": "example", + "format": "text", + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "content": { + "type": "string" + }, + "format": { + "enum": [ + "text", + "html" + ] + }, + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target", + "content" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/select-by-tag.mdx b/apps/docs/document-api/reference/content-controls/select-by-tag.mdx new file mode 100644 index 0000000000..dfde701fce --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/select-by-tag.mdx @@ -0,0 +1,120 @@ +--- +title: contentControls.selectByTag +sidebarTitle: contentControls.selectByTag +description: Select content controls matching a specific tag value. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Select content controls matching a specific tag value. + +- Operation ID: `contentControls.selectByTag` +- API member path: `editor.doc.contentControls.selectByTag(...)` +- Mutates document: `no` +- Idempotency: `idempotent` +- Supports tracked mode: `no` +- Supports dry run: `no` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlsListResult with matching items. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `limit` | integer | no | | +| `offset` | integer | no | | +| `tag` | string | yes | | + +### Example request + +```json +{ + "limit": 50, + "offset": 0, + "tag": "example" +} +``` + +## Output fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `items` | object[] | yes | | +| `total` | integer | yes | | + +### Example response + +```json +{ + "items": [ + {} + ], + "total": 1 +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_INPUT` +- `CAPABILITY_UNAVAILABLE` + +## Non-applied failure codes + +- None + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "limit": { + "type": "integer" + }, + "offset": { + "type": "integer" + }, + "tag": { + "type": "string" + } + }, + "required": [ + "tag" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "items": { + "items": { + "type": "object" + }, + "type": "array" + }, + "total": { + "type": "integer" + } + }, + "required": [ + "items", + "total" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/select-by-title.mdx b/apps/docs/document-api/reference/content-controls/select-by-title.mdx new file mode 100644 index 0000000000..dd849a3087 --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/select-by-title.mdx @@ -0,0 +1,120 @@ +--- +title: contentControls.selectByTitle +sidebarTitle: contentControls.selectByTitle +description: Select content controls matching a specific title (alias) value. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Select content controls matching a specific title (alias) value. + +- Operation ID: `contentControls.selectByTitle` +- API member path: `editor.doc.contentControls.selectByTitle(...)` +- Mutates document: `no` +- Idempotency: `idempotent` +- Supports tracked mode: `no` +- Supports dry run: `no` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlsListResult with matching items. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `limit` | integer | no | | +| `offset` | integer | no | | +| `title` | string | yes | | + +### Example request + +```json +{ + "limit": 50, + "offset": 0, + "title": "example" +} +``` + +## Output fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `items` | object[] | yes | | +| `total` | integer | yes | | + +### Example response + +```json +{ + "items": [ + {} + ], + "total": 1 +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_INPUT` +- `CAPABILITY_UNAVAILABLE` + +## Non-applied failure codes + +- None + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "limit": { + "type": "integer" + }, + "offset": { + "type": "integer" + }, + "title": { + "type": "string" + } + }, + "required": [ + "title" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "items": { + "items": { + "type": "object" + }, + "type": "array" + }, + "total": { + "type": "integer" + } + }, + "required": [ + "items", + "total" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/set-binding.mdx b/apps/docs/document-api/reference/content-controls/set-binding.mdx new file mode 100644 index 0000000000..92085c8854 --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/set-binding.mdx @@ -0,0 +1,329 @@ +--- +title: contentControls.setBinding +sidebarTitle: contentControls.setBinding +description: Set data binding metadata on a content control. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Set data binding metadata on a content control. + +- Operation ID: `contentControls.setBinding` +- API member path: `editor.doc.contentControls.setBinding(...)` +- Mutates document: `yes` +- Idempotency: `conditional` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult; reports NO_OP if binding unchanged. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `prefixMappings` | string | no | | +| `storeItemId` | string | yes | | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `xpath` | string | yes | | + +### Example request + +```json +{ + "prefixMappings": "example", + "storeItemId": "example", + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "xpath": "example" +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "prefixMappings": { + "type": "string" + }, + "storeItemId": { + "type": "string" + }, + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "xpath": { + "type": "string" + } + }, + "required": [ + "target", + "storeItemId", + "xpath" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/set-lock-mode.mdx b/apps/docs/document-api/reference/content-controls/set-lock-mode.mdx new file mode 100644 index 0000000000..2830091624 --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/set-lock-mode.mdx @@ -0,0 +1,323 @@ +--- +title: contentControls.setLockMode +sidebarTitle: contentControls.setLockMode +description: Set the lock mode on a content control. +--- + +{/* 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 lock mode on a content control. + +- Operation ID: `contentControls.setLockMode` +- API member path: `editor.doc.contentControls.setLockMode(...)` +- Mutates document: `yes` +- Idempotency: `conditional` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult; reports NO_OP if lock mode unchanged. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `lockMode` | enum | yes | `"unlocked"`, `"sdtLocked"`, `"contentLocked"`, `"sdtContentLocked"` | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "lockMode": "unlocked", + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "lockMode": { + "enum": [ + "unlocked", + "sdtLocked", + "contentLocked", + "sdtContentLocked" + ] + }, + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target", + "lockMode" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/set-type.mdx b/apps/docs/document-api/reference/content-controls/set-type.mdx new file mode 100644 index 0000000000..0fd05a80b1 --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/set-type.mdx @@ -0,0 +1,319 @@ +--- +title: contentControls.setType +sidebarTitle: contentControls.setType +description: Transition a content control to a different semantic type. Metadata-only; no implicit content rewrite. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Transition a content control to a different semantic type. Metadata-only; no implicit content rewrite. + +- Operation ID: `contentControls.setType` +- API member path: `editor.doc.contentControls.setType(...)` +- Mutates document: `yes` +- Idempotency: `conditional` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult; reports NO_OP if type unchanged. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `controlType` | string | yes | | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "controlType": "example", + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` +- `TYPE_MISMATCH` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "controlType": { + "type": "string" + }, + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target", + "controlType" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/text/clear-value.mdx b/apps/docs/document-api/reference/content-controls/text/clear-value.mdx new file mode 100644 index 0000000000..c46119289c --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/text/clear-value.mdx @@ -0,0 +1,313 @@ +--- +title: contentControls.text.clearValue +sidebarTitle: contentControls.text.clearValue +description: Clear the text value of a plain-text content control. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Clear the text value of a plain-text content control. + +- Operation ID: `contentControls.text.clearValue` +- API member path: `editor.doc.contentControls.text.clearValue(...)` +- Mutates document: `yes` +- Idempotency: `conditional` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult; reports NO_OP if already empty. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` +- `TYPE_MISMATCH` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/text/set-multiline.mdx b/apps/docs/document-api/reference/content-controls/text/set-multiline.mdx new file mode 100644 index 0000000000..2e697898f3 --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/text/set-multiline.mdx @@ -0,0 +1,319 @@ +--- +title: contentControls.text.setMultiline +sidebarTitle: contentControls.text.setMultiline +description: Set or clear the multiline attribute on a plain-text content control. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Set or clear the multiline attribute on a plain-text content control. + +- Operation ID: `contentControls.text.setMultiline` +- API member path: `editor.doc.contentControls.text.setMultiline(...)` +- Mutates document: `yes` +- Idempotency: `conditional` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult; reports NO_OP if unchanged. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `multiline` | boolean | yes | | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "multiline": true, + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` +- `TYPE_MISMATCH` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "multiline": { + "type": "boolean" + }, + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target", + "multiline" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/text/set-value.mdx b/apps/docs/document-api/reference/content-controls/text/set-value.mdx new file mode 100644 index 0000000000..26dd21ea6a --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/text/set-value.mdx @@ -0,0 +1,319 @@ +--- +title: contentControls.text.setValue +sidebarTitle: contentControls.text.setValue +description: Set the text value of a plain-text content control. +--- + +{/* 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 text value of a plain-text content control. + +- Operation ID: `contentControls.text.setValue` +- API member path: `editor.doc.contentControls.text.setValue(...)` +- Mutates document: `yes` +- Idempotency: `conditional` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult; reports NO_OP if unchanged. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `value` | string | yes | | + +### Example request + +```json +{ + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "value": "example" +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` +- `TYPE_MISMATCH` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "value": { + "type": "string" + } + }, + "required": [ + "target", + "value" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/unwrap.mdx b/apps/docs/document-api/reference/content-controls/unwrap.mdx new file mode 100644 index 0000000000..5d983f8418 --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/unwrap.mdx @@ -0,0 +1,312 @@ +--- +title: contentControls.unwrap +sidebarTitle: contentControls.unwrap +description: Remove the content control wrapper, preserving its content in place. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Remove the content control wrapper, preserving its content in place. + +- Operation ID: `contentControls.unwrap` +- API member path: `editor.doc.contentControls.unwrap(...)` +- Mutates document: `yes` +- Idempotency: `conditional` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult; reports NO_OP if already unwrapped. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/validate-word-compatibility.mdx b/apps/docs/document-api/reference/content-controls/validate-word-compatibility.mdx new file mode 100644 index 0000000000..1143c818f6 --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/validate-word-compatibility.mdx @@ -0,0 +1,137 @@ +--- +title: contentControls.validateWordCompatibility +sidebarTitle: contentControls.validateWordCompatibility +description: Validate a content control for Word compatibility issues. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Validate a content control for Word compatibility issues. + +- Operation ID: `contentControls.validateWordCompatibility` +- API member path: `editor.doc.contentControls.validateWordCompatibility(...)` +- Mutates document: `no` +- Idempotency: `idempotent` +- Supports tracked mode: `no` +- Supports dry run: `no` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a compatibility result with diagnostics. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `compatible` | boolean | yes | | +| `diagnostics` | object[] | yes | | + +### Example response + +```json +{ + "compatible": true, + "diagnostics": [ + {} + ] +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_INPUT` +- `CAPABILITY_UNAVAILABLE` + +## Non-applied failure codes + +- None + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "target" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "compatible": { + "type": "boolean" + }, + "diagnostics": { + "items": { + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "compatible", + "diagnostics" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/content-controls/wrap.mdx b/apps/docs/document-api/reference/content-controls/wrap.mdx new file mode 100644 index 0000000000..a508324e31 --- /dev/null +++ b/apps/docs/document-api/reference/content-controls/wrap.mdx @@ -0,0 +1,340 @@ +--- +title: contentControls.wrap +sidebarTitle: contentControls.wrap +description: Wrap existing content with a new content control. +--- + +{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} + +> Alpha: Document API is currently alpha and subject to breaking changes. + +## Summary + +Wrap existing content with a new content control. + +- Operation ID: `contentControls.wrap` +- API member path: `editor.doc.contentControls.wrap(...)` +- Mutates document: `yes` +- Idempotency: `conditional` +- Supports tracked mode: `no` +- Supports dry run: `yes` +- Deterministic target resolution: `yes` + +## Expected result + +Returns a ContentControlMutationResult with the wrapper target. + +## Input fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `alias` | string | no | | +| `kind` | enum | yes | `"block"`, `"inline"` | +| `lockMode` | enum | no | `"unlocked"`, `"sdtLocked"`, `"contentLocked"`, `"sdtContentLocked"` | +| `tag` | string | no | | +| `target` | object(nodeType="sdt") | yes | | +| `target.kind` | enum | yes | `"block"`, `"inline"` | +| `target.nodeId` | string | yes | | +| `target.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | + +### Example request + +```json +{ + "alias": "example", + "kind": "block", + "tag": "example", + "target": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Output fields + +### Variant 1 (success=true) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentControl` | object(nodeType="sdt") | yes | | +| `contentControl.kind` | enum | yes | `"block"`, `"inline"` | +| `contentControl.nodeId` | string | yes | | +| `contentControl.nodeType` | `"sdt"` | yes | Constant: `"sdt"` | +| `success` | `true` | yes | Constant: `true` | +| `updatedRef` | object(nodeType="sdt") | no | | +| `updatedRef.kind` | enum | no | `"block"`, `"inline"` | +| `updatedRef.nodeId` | string | no | | +| `updatedRef.nodeType` | `"sdt"` | no | Constant: `"sdt"` | + +### Variant 2 (success=false) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `failure` | ReceiptFailure | yes | ReceiptFailure | +| `failure.code` | string | yes | | +| `failure.details` | any | no | | +| `failure.message` | string | yes | | +| `success` | `false` | yes | Constant: `false` | + +### Example response + +```json +{ + "contentControl": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + }, + "success": true, + "updatedRef": { + "kind": "block", + "nodeId": "node-def456", + "nodeType": "sdt" + } +} +``` + +## Pre-apply throws + +- `TARGET_NOT_FOUND` +- `INVALID_TARGET` +- `AMBIGUOUS_TARGET` +- `INVALID_INPUT` +- `LOCK_VIOLATION` +- `REVISION_MISMATCH` +- `CAPABILITY_UNAVAILABLE` + +## Non-applied failure codes + +- `NO_OP` + +## Raw schemas + + +```json +{ + "additionalProperties": false, + "properties": { + "alias": { + "type": "string" + }, + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "lockMode": { + "enum": [ + "unlocked", + "sdtLocked", + "contentLocked", + "sdtContentLocked" + ] + }, + "tag": { + "type": "string" + }, + "target": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "kind", + "target" + ], + "type": "object" +} +``` + + + +```json +{ + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" + } + ] +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "contentControl": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + }, + "success": { + "const": true + }, + "updatedRef": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "block", + "inline" + ] + }, + "nodeId": { + "type": "string" + }, + "nodeType": { + "const": "sdt" + } + }, + "required": [ + "kind", + "nodeType", + "nodeId" + ], + "type": "object" + } + }, + "required": [ + "success", + "contentControl" + ], + "type": "object" +} +``` + + + +```json +{ + "additionalProperties": false, + "properties": { + "failure": { + "$ref": "#/$defs/ReceiptFailure" + }, + "success": { + "const": false + } + }, + "required": [ + "success", + "failure" + ], + "type": "object" +} +``` + diff --git a/apps/docs/document-api/reference/index.mdx b/apps/docs/document-api/reference/index.mdx index b732ce9f9a..4d674b8d54 100644 --- a/apps/docs/document-api/reference/index.mdx +++ b/apps/docs/document-api/reference/index.mdx @@ -39,6 +39,7 @@ Document API is currently alpha and subject to breaking changes. | Table of Contents | 10 | 0 | 10 | [Open](/document-api/reference/toc/index) | | Images | 27 | 0 | 27 | [Open](/document-api/reference/images/index) | | Hyperlinks | 6 | 0 | 6 | [Open](/document-api/reference/hyperlinks/index) | +| Content Controls | 55 | 0 | 55 | [Open](/document-api/reference/content-controls/index) | ## Available operations @@ -370,3 +371,63 @@ The tables below are grouped by namespace. | hyperlinks.insert | editor.doc.hyperlinks.insert(...) | Insert new linked text at a target position. | | hyperlinks.patch | editor.doc.hyperlinks.patch(...) | Update hyperlink metadata (destination, tooltip, target, rel) without changing display text. | | hyperlinks.remove | editor.doc.hyperlinks.remove(...) | Remove a hyperlink. Mode 'unwrap' preserves display text; 'deleteText' removes the linked content entirely. | + +#### Content Controls + +| Operation | API member path | Description | +| --- | --- | --- | +| create.contentControl | editor.doc.create.contentControl(...) | Create a new content control (SDT) in the document. | +| contentControls.list | editor.doc.contentControls.list(...) | List all content controls in the document with optional type/tag filtering. | +| contentControls.get | editor.doc.contentControls.get(...) | Retrieve a single content control by target. | +| contentControls.listInRange | editor.doc.contentControls.listInRange(...) | List content controls within a block range. | +| contentControls.selectByTag | editor.doc.contentControls.selectByTag(...) | Select content controls matching a specific tag value. | +| contentControls.selectByTitle | editor.doc.contentControls.selectByTitle(...) | Select content controls matching a specific title (alias) value. | +| contentControls.listChildren | editor.doc.contentControls.listChildren(...) | List direct child content controls nested inside the target. | +| contentControls.getParent | editor.doc.contentControls.getParent(...) | Get the parent content control of the target, if any. | +| contentControls.wrap | editor.doc.contentControls.wrap(...) | Wrap existing content with a new content control. | +| contentControls.unwrap | editor.doc.contentControls.unwrap(...) | Remove the content control wrapper, preserving its content in place. | +| contentControls.delete | editor.doc.contentControls.delete(...) | Delete a content control and its content from the document. | +| contentControls.copy | editor.doc.contentControls.copy(...) | Copy a content control to a destination position. Copied SDTs receive new IDs. | +| contentControls.move | editor.doc.contentControls.move(...) | Move a content control to a new position. Preserves original IDs. | +| contentControls.patch | editor.doc.contentControls.patch(...) | Patch metadata properties on a content control (tag, alias, appearance, color, etc.). | +| contentControls.setLockMode | editor.doc.contentControls.setLockMode(...) | Set the lock mode on a content control. | +| contentControls.setType | editor.doc.contentControls.setType(...) | Transition a content control to a different semantic type. Metadata-only; no implicit content rewrite. | +| contentControls.getContent | editor.doc.contentControls.getContent(...) | Get the text content of a content control. | +| contentControls.replaceContent | editor.doc.contentControls.replaceContent(...) | Replace the entire content of a content control. | +| contentControls.clearContent | editor.doc.contentControls.clearContent(...) | Clear all content inside a content control, leaving it empty. | +| contentControls.appendContent | editor.doc.contentControls.appendContent(...) | Append content to the end of a content control. | +| contentControls.prependContent | editor.doc.contentControls.prependContent(...) | Prepend content to the beginning of a content control. | +| contentControls.insertBefore | editor.doc.contentControls.insertBefore(...) | Insert content immediately before a content control. | +| contentControls.insertAfter | editor.doc.contentControls.insertAfter(...) | Insert content immediately after a content control. | +| contentControls.getBinding | editor.doc.contentControls.getBinding(...) | Get the data binding metadata (w:dataBinding) of a content control. | +| contentControls.setBinding | editor.doc.contentControls.setBinding(...) | Set data binding metadata on a content control. | +| contentControls.clearBinding | editor.doc.contentControls.clearBinding(...) | Remove data binding metadata from a content control. | +| contentControls.getRawProperties | editor.doc.contentControls.getRawProperties(...) | Get the raw sdtPr properties of a content control as a passthrough hash. | +| contentControls.patchRawProperties | editor.doc.contentControls.patchRawProperties(...) | Apply raw XML-level patches to the sdtPr subtree of a content control. | +| contentControls.validateWordCompatibility | editor.doc.contentControls.validateWordCompatibility(...) | Validate a content control for Word compatibility issues. | +| contentControls.normalizeWordCompatibility | editor.doc.contentControls.normalizeWordCompatibility(...) | Normalize a content control to resolve Word compatibility issues. | +| contentControls.normalizeTagPayload | editor.doc.contentControls.normalizeTagPayload(...) | Normalize a content control tag between plain-string and JSON-encoded formats. | +| contentControls.text.setMultiline | editor.doc.contentControls.text.setMultiline(...) | Set or clear the multiline attribute on a plain-text content control. | +| contentControls.text.setValue | editor.doc.contentControls.text.setValue(...) | Set the text value of a plain-text content control. | +| contentControls.text.clearValue | editor.doc.contentControls.text.clearValue(...) | Clear the text value of a plain-text content control. | +| contentControls.date.setValue | editor.doc.contentControls.date.setValue(...) | Set the date value of a date content control. | +| contentControls.date.clearValue | editor.doc.contentControls.date.clearValue(...) | Clear the date value of a date content control. | +| contentControls.date.setDisplayFormat | editor.doc.contentControls.date.setDisplayFormat(...) | Set the display format string for a date content control. | +| contentControls.date.setDisplayLocale | editor.doc.contentControls.date.setDisplayLocale(...) | Set the display locale for a date content control. | +| contentControls.date.setStorageFormat | editor.doc.contentControls.date.setStorageFormat(...) | Set the XML storage format for a date content control. | +| contentControls.date.setCalendar | editor.doc.contentControls.date.setCalendar(...) | Set the calendar type for a date content control. | +| contentControls.checkbox.getState | editor.doc.contentControls.checkbox.getState(...) | Get the checked state of a checkbox content control. | +| contentControls.checkbox.setState | editor.doc.contentControls.checkbox.setState(...) | Set the checked state of a checkbox content control. | +| contentControls.checkbox.toggle | editor.doc.contentControls.checkbox.toggle(...) | Toggle the checked state of a checkbox content control. | +| contentControls.checkbox.setSymbolPair | editor.doc.contentControls.checkbox.setSymbolPair(...) | Set the checked and unchecked symbol glyphs for a checkbox content control. | +| contentControls.choiceList.getItems | editor.doc.contentControls.choiceList.getItems(...) | Get the list items and selected value of a comboBox or dropDownList content control. | +| contentControls.choiceList.setItems | editor.doc.contentControls.choiceList.setItems(...) | Replace the list items of a comboBox or dropDownList content control. | +| contentControls.choiceList.setSelected | editor.doc.contentControls.choiceList.setSelected(...) | Set the selected value of a comboBox or dropDownList content control. | +| contentControls.repeatingSection.listItems | editor.doc.contentControls.repeatingSection.listItems(...) | List the repeating section items inside a repeating section content control. | +| contentControls.repeatingSection.insertItemBefore | editor.doc.contentControls.repeatingSection.insertItemBefore(...) | Insert a new item before a specific index in a repeating section. | +| contentControls.repeatingSection.insertItemAfter | editor.doc.contentControls.repeatingSection.insertItemAfter(...) | Insert a new item after a specific index in a repeating section. | +| contentControls.repeatingSection.cloneItem | editor.doc.contentControls.repeatingSection.cloneItem(...) | Clone a repeating section item at the given index. Cloned SDTs receive new IDs. | +| contentControls.repeatingSection.deleteItem | editor.doc.contentControls.repeatingSection.deleteItem(...) | Delete a repeating section item at the given index. | +| contentControls.repeatingSection.setAllowInsertDelete | editor.doc.contentControls.repeatingSection.setAllowInsertDelete(...) | Set the allowInsertDelete flag on a repeating section. | +| contentControls.group.wrap | editor.doc.contentControls.group.wrap(...) | Wrap a content control inside a new group content control. Always nests; not idempotent. | +| contentControls.group.ungroup | editor.doc.contentControls.group.ungroup(...) | Remove the group designation from a group content control. | diff --git a/apps/docs/document-engine/sdks.mdx b/apps/docs/document-engine/sdks.mdx index 7320798fa6..34dbf53e11 100644 --- a/apps/docs/document-engine/sdks.mdx +++ b/apps/docs/document-engine/sdks.mdx @@ -390,6 +390,61 @@ The SDKs expose all operations from the [Document API](/document-api/overview) p | `doc.hyperlinks.insert` | `hyperlinks insert` | Insert new linked text at a target position. | | `doc.hyperlinks.patch` | `hyperlinks patch` | Update hyperlink metadata (destination, tooltip, target, rel) without changing display text. | | `doc.hyperlinks.remove` | `hyperlinks remove` | Remove a hyperlink. Mode 'unwrap' preserves display text; 'deleteText' removes the linked content entirely. | +| `doc.create.contentControl` | `create content-control` | Create a new content control (SDT) in the document. | +| `doc.contentControls.list` | `content-controls list` | List all content controls in the document with optional type/tag filtering. | +| `doc.contentControls.get` | `content-controls get` | Retrieve a single content control by target. | +| `doc.contentControls.listInRange` | `content-controls list-in-range` | List content controls within a block range. | +| `doc.contentControls.selectByTag` | `content-controls select-by-tag` | Select content controls matching a specific tag value. | +| `doc.contentControls.selectByTitle` | `content-controls select-by-title` | Select content controls matching a specific title (alias) value. | +| `doc.contentControls.listChildren` | `content-controls list-children` | List direct child content controls nested inside the target. | +| `doc.contentControls.getParent` | `content-controls get-parent` | Get the parent content control of the target, if any. | +| `doc.contentControls.wrap` | `content-controls wrap` | Wrap existing content with a new content control. | +| `doc.contentControls.unwrap` | `content-controls unwrap` | Remove the content control wrapper, preserving its content in place. | +| `doc.contentControls.delete` | `content-controls delete` | Delete a content control and its content from the document. | +| `doc.contentControls.copy` | `content-controls copy` | Copy a content control to a destination position. Copied SDTs receive new IDs. | +| `doc.contentControls.move` | `content-controls move` | Move a content control to a new position. Preserves original IDs. | +| `doc.contentControls.patch` | `content-controls patch` | Patch metadata properties on a content control (tag, alias, appearance, color, etc.). | +| `doc.contentControls.setLockMode` | `content-controls set-lock-mode` | Set the lock mode on a content control. | +| `doc.contentControls.setType` | `content-controls set-type` | Transition a content control to a different semantic type. Metadata-only; no implicit content rewrite. | +| `doc.contentControls.getContent` | `content-controls get-content` | Get the text content of a content control. | +| `doc.contentControls.replaceContent` | `content-controls replace-content` | Replace the entire content of a content control. | +| `doc.contentControls.clearContent` | `content-controls clear-content` | Clear all content inside a content control, leaving it empty. | +| `doc.contentControls.appendContent` | `content-controls append-content` | Append content to the end of a content control. | +| `doc.contentControls.prependContent` | `content-controls prepend-content` | Prepend content to the beginning of a content control. | +| `doc.contentControls.insertBefore` | `content-controls insert-before` | Insert content immediately before a content control. | +| `doc.contentControls.insertAfter` | `content-controls insert-after` | Insert content immediately after a content control. | +| `doc.contentControls.getBinding` | `content-controls get-binding` | Get the data binding metadata (w:dataBinding) of a content control. | +| `doc.contentControls.setBinding` | `content-controls set-binding` | Set data binding metadata on a content control. | +| `doc.contentControls.clearBinding` | `content-controls clear-binding` | Remove data binding metadata from a content control. | +| `doc.contentControls.getRawProperties` | `content-controls get-raw-properties` | Get the raw sdtPr properties of a content control as a passthrough hash. | +| `doc.contentControls.patchRawProperties` | `content-controls patch-raw-properties` | Apply raw XML-level patches to the sdtPr subtree of a content control. | +| `doc.contentControls.validateWordCompatibility` | `content-controls validate-word-compatibility` | Validate a content control for Word compatibility issues. | +| `doc.contentControls.normalizeWordCompatibility` | `content-controls normalize-word-compatibility` | Normalize a content control to resolve Word compatibility issues. | +| `doc.contentControls.normalizeTagPayload` | `content-controls normalize-tag-payload` | Normalize a content control tag between plain-string and JSON-encoded formats. | +| `doc.contentControls.text.setMultiline` | `content-controls text set-multiline` | Set or clear the multiline attribute on a plain-text content control. | +| `doc.contentControls.text.setValue` | `content-controls text set-value` | Set the text value of a plain-text content control. | +| `doc.contentControls.text.clearValue` | `content-controls text clear-value` | Clear the text value of a plain-text content control. | +| `doc.contentControls.date.setValue` | `content-controls date set-value` | Set the date value of a date content control. | +| `doc.contentControls.date.clearValue` | `content-controls date clear-value` | Clear the date value of a date content control. | +| `doc.contentControls.date.setDisplayFormat` | `content-controls date set-display-format` | Set the display format string for a date content control. | +| `doc.contentControls.date.setDisplayLocale` | `content-controls date set-display-locale` | Set the display locale for a date content control. | +| `doc.contentControls.date.setStorageFormat` | `content-controls date set-storage-format` | Set the XML storage format for a date content control. | +| `doc.contentControls.date.setCalendar` | `content-controls date set-calendar` | Set the calendar type for a date content control. | +| `doc.contentControls.checkbox.getState` | `content-controls checkbox get-state` | Get the checked state of a checkbox content control. | +| `doc.contentControls.checkbox.setState` | `content-controls checkbox set-state` | Set the checked state of a checkbox content control. | +| `doc.contentControls.checkbox.toggle` | `content-controls checkbox toggle` | Toggle the checked state of a checkbox content control. | +| `doc.contentControls.checkbox.setSymbolPair` | `content-controls checkbox set-symbol-pair` | Set the checked and unchecked symbol glyphs for a checkbox content control. | +| `doc.contentControls.choiceList.getItems` | `content-controls choice-list get-items` | Get the list items and selected value of a comboBox or dropDownList content control. | +| `doc.contentControls.choiceList.setItems` | `content-controls choice-list set-items` | Replace the list items of a comboBox or dropDownList content control. | +| `doc.contentControls.choiceList.setSelected` | `content-controls choice-list set-selected` | Set the selected value of a comboBox or dropDownList content control. | +| `doc.contentControls.repeatingSection.listItems` | `content-controls repeating-section list-items` | List the repeating section items inside a repeating section content control. | +| `doc.contentControls.repeatingSection.insertItemBefore` | `content-controls repeating-section insert-item-before` | Insert a new item before a specific index in a repeating section. | +| `doc.contentControls.repeatingSection.insertItemAfter` | `content-controls repeating-section insert-item-after` | Insert a new item after a specific index in a repeating section. | +| `doc.contentControls.repeatingSection.cloneItem` | `content-controls repeating-section clone-item` | Clone a repeating section item at the given index. Cloned SDTs receive new IDs. | +| `doc.contentControls.repeatingSection.deleteItem` | `content-controls repeating-section delete-item` | Delete a repeating section item at the given index. | +| `doc.contentControls.repeatingSection.setAllowInsertDelete` | `content-controls repeating-section set-allow-insert-delete` | Set the allowInsertDelete flag on a repeating section. | +| `doc.contentControls.group.wrap` | `content-controls group wrap` | Wrap a content control inside a new group content control. Always nests; not idempotent. | +| `doc.contentControls.group.ungroup` | `content-controls group ungroup` | Remove the group designation from a group content control. | #### Format @@ -694,6 +749,61 @@ The SDKs expose all operations from the [Document API](/document-api/overview) p | `doc.hyperlinks.insert` | `hyperlinks insert` | Insert new linked text at a target position. | | `doc.hyperlinks.patch` | `hyperlinks patch` | Update hyperlink metadata (destination, tooltip, target, rel) without changing display text. | | `doc.hyperlinks.remove` | `hyperlinks remove` | Remove a hyperlink. Mode 'unwrap' preserves display text; 'deleteText' removes the linked content entirely. | +| `doc.create.content_control` | `create content-control` | Create a new content control (SDT) in the document. | +| `doc.content_controls.list` | `content-controls list` | List all content controls in the document with optional type/tag filtering. | +| `doc.content_controls.get` | `content-controls get` | Retrieve a single content control by target. | +| `doc.content_controls.list_in_range` | `content-controls list-in-range` | List content controls within a block range. | +| `doc.content_controls.select_by_tag` | `content-controls select-by-tag` | Select content controls matching a specific tag value. | +| `doc.content_controls.select_by_title` | `content-controls select-by-title` | Select content controls matching a specific title (alias) value. | +| `doc.content_controls.list_children` | `content-controls list-children` | List direct child content controls nested inside the target. | +| `doc.content_controls.get_parent` | `content-controls get-parent` | Get the parent content control of the target, if any. | +| `doc.content_controls.wrap` | `content-controls wrap` | Wrap existing content with a new content control. | +| `doc.content_controls.unwrap` | `content-controls unwrap` | Remove the content control wrapper, preserving its content in place. | +| `doc.content_controls.delete` | `content-controls delete` | Delete a content control and its content from the document. | +| `doc.content_controls.copy` | `content-controls copy` | Copy a content control to a destination position. Copied SDTs receive new IDs. | +| `doc.content_controls.move` | `content-controls move` | Move a content control to a new position. Preserves original IDs. | +| `doc.content_controls.patch` | `content-controls patch` | Patch metadata properties on a content control (tag, alias, appearance, color, etc.). | +| `doc.content_controls.set_lock_mode` | `content-controls set-lock-mode` | Set the lock mode on a content control. | +| `doc.content_controls.set_type` | `content-controls set-type` | Transition a content control to a different semantic type. Metadata-only; no implicit content rewrite. | +| `doc.content_controls.get_content` | `content-controls get-content` | Get the text content of a content control. | +| `doc.content_controls.replace_content` | `content-controls replace-content` | Replace the entire content of a content control. | +| `doc.content_controls.clear_content` | `content-controls clear-content` | Clear all content inside a content control, leaving it empty. | +| `doc.content_controls.append_content` | `content-controls append-content` | Append content to the end of a content control. | +| `doc.content_controls.prepend_content` | `content-controls prepend-content` | Prepend content to the beginning of a content control. | +| `doc.content_controls.insert_before` | `content-controls insert-before` | Insert content immediately before a content control. | +| `doc.content_controls.insert_after` | `content-controls insert-after` | Insert content immediately after a content control. | +| `doc.content_controls.get_binding` | `content-controls get-binding` | Get the data binding metadata (w:dataBinding) of a content control. | +| `doc.content_controls.set_binding` | `content-controls set-binding` | Set data binding metadata on a content control. | +| `doc.content_controls.clear_binding` | `content-controls clear-binding` | Remove data binding metadata from a content control. | +| `doc.content_controls.get_raw_properties` | `content-controls get-raw-properties` | Get the raw sdtPr properties of a content control as a passthrough hash. | +| `doc.content_controls.patch_raw_properties` | `content-controls patch-raw-properties` | Apply raw XML-level patches to the sdtPr subtree of a content control. | +| `doc.content_controls.validate_word_compatibility` | `content-controls validate-word-compatibility` | Validate a content control for Word compatibility issues. | +| `doc.content_controls.normalize_word_compatibility` | `content-controls normalize-word-compatibility` | Normalize a content control to resolve Word compatibility issues. | +| `doc.content_controls.normalize_tag_payload` | `content-controls normalize-tag-payload` | Normalize a content control tag between plain-string and JSON-encoded formats. | +| `doc.content_controls.text.set_multiline` | `content-controls text set-multiline` | Set or clear the multiline attribute on a plain-text content control. | +| `doc.content_controls.text.set_value` | `content-controls text set-value` | Set the text value of a plain-text content control. | +| `doc.content_controls.text.clear_value` | `content-controls text clear-value` | Clear the text value of a plain-text content control. | +| `doc.content_controls.date.set_value` | `content-controls date set-value` | Set the date value of a date content control. | +| `doc.content_controls.date.clear_value` | `content-controls date clear-value` | Clear the date value of a date content control. | +| `doc.content_controls.date.set_display_format` | `content-controls date set-display-format` | Set the display format string for a date content control. | +| `doc.content_controls.date.set_display_locale` | `content-controls date set-display-locale` | Set the display locale for a date content control. | +| `doc.content_controls.date.set_storage_format` | `content-controls date set-storage-format` | Set the XML storage format for a date content control. | +| `doc.content_controls.date.set_calendar` | `content-controls date set-calendar` | Set the calendar type for a date content control. | +| `doc.content_controls.checkbox.get_state` | `content-controls checkbox get-state` | Get the checked state of a checkbox content control. | +| `doc.content_controls.checkbox.set_state` | `content-controls checkbox set-state` | Set the checked state of a checkbox content control. | +| `doc.content_controls.checkbox.toggle` | `content-controls checkbox toggle` | Toggle the checked state of a checkbox content control. | +| `doc.content_controls.checkbox.set_symbol_pair` | `content-controls checkbox set-symbol-pair` | Set the checked and unchecked symbol glyphs for a checkbox content control. | +| `doc.content_controls.choice_list.get_items` | `content-controls choice-list get-items` | Get the list items and selected value of a comboBox or dropDownList content control. | +| `doc.content_controls.choice_list.set_items` | `content-controls choice-list set-items` | Replace the list items of a comboBox or dropDownList content control. | +| `doc.content_controls.choice_list.set_selected` | `content-controls choice-list set-selected` | Set the selected value of a comboBox or dropDownList content control. | +| `doc.content_controls.repeating_section.list_items` | `content-controls repeating-section list-items` | List the repeating section items inside a repeating section content control. | +| `doc.content_controls.repeating_section.insert_item_before` | `content-controls repeating-section insert-item-before` | Insert a new item before a specific index in a repeating section. | +| `doc.content_controls.repeating_section.insert_item_after` | `content-controls repeating-section insert-item-after` | Insert a new item after a specific index in a repeating section. | +| `doc.content_controls.repeating_section.clone_item` | `content-controls repeating-section clone-item` | Clone a repeating section item at the given index. Cloned SDTs receive new IDs. | +| `doc.content_controls.repeating_section.delete_item` | `content-controls repeating-section delete-item` | Delete a repeating section item at the given index. | +| `doc.content_controls.repeating_section.set_allow_insert_delete` | `content-controls repeating-section set-allow-insert-delete` | Set the allowInsertDelete flag on a repeating section. | +| `doc.content_controls.group.wrap` | `content-controls group wrap` | Wrap a content control inside a new group content control. Always nests; not idempotent. | +| `doc.content_controls.group.ungroup` | `content-controls group ungroup` | Remove the group designation from a group content control. | #### Format diff --git a/packages/document-api/src/content-controls/content-controls.ts b/packages/document-api/src/content-controls/content-controls.ts new file mode 100644 index 0000000000..53ab9acc12 --- /dev/null +++ b/packages/document-api/src/content-controls/content-controls.ts @@ -0,0 +1,652 @@ +/** + * Content Controls API โ€” interface, adapter, and execute functions. + * + * Each public method delegates to an `execute*` function that validates input + * before calling the adapter. The adapter is implemented by the engine layer + * (super-editor). + */ + +import type { MutationOptions } from '../write/write.js'; +import type { + ContentControlInfo, + ContentControlMutationResult, + ContentControlsListResult, + ContentControlsListQuery, + ContentControlsGetInput, + ContentControlsListInRangeInput, + ContentControlsSelectByTagInput, + ContentControlsSelectByTitleInput, + ContentControlsListChildrenInput, + ContentControlsGetParentInput, + ContentControlsWrapInput, + ContentControlsUnwrapInput, + ContentControlsDeleteInput, + ContentControlsCopyInput, + ContentControlsMoveInput, + ContentControlsPatchInput, + ContentControlsSetLockModeInput, + ContentControlsSetTypeInput, + ContentControlsGetContentInput, + ContentControlsGetContentResult, + ContentControlsReplaceContentInput, + ContentControlsClearContentInput, + ContentControlsAppendContentInput, + ContentControlsPrependContentInput, + ContentControlsInsertBeforeInput, + ContentControlsInsertAfterInput, + ContentControlsGetBindingInput, + ContentControlBinding, + ContentControlsSetBindingInput, + ContentControlsClearBindingInput, + ContentControlsGetRawPropertiesInput, + ContentControlsGetRawPropertiesResult, + ContentControlsPatchRawPropertiesInput, + ContentControlsValidateWordCompatibilityInput, + ContentControlsValidateWordCompatibilityResult, + ContentControlsNormalizeWordCompatibilityInput, + ContentControlsNormalizeTagPayloadInput, + ContentControlsTextSetMultilineInput, + ContentControlsTextSetValueInput, + ContentControlsTextClearValueInput, + ContentControlsDateSetValueInput, + ContentControlsDateClearValueInput, + ContentControlsDateSetDisplayFormatInput, + ContentControlsDateSetDisplayLocaleInput, + ContentControlsDateSetStorageFormatInput, + ContentControlsDateSetCalendarInput, + ContentControlsCheckboxGetStateInput, + ContentControlsCheckboxGetStateResult, + ContentControlsCheckboxSetStateInput, + ContentControlsCheckboxToggleInput, + ContentControlsCheckboxSetSymbolPairInput, + ContentControlsChoiceListGetItemsInput, + ContentControlsChoiceListGetItemsResult, + ContentControlsChoiceListSetItemsInput, + ContentControlsChoiceListSetSelectedInput, + ContentControlsRepeatingSectionListItemsInput, + ContentControlsRepeatingSectionListItemsResult, + ContentControlsRepeatingSectionInsertItemBeforeInput, + ContentControlsRepeatingSectionInsertItemAfterInput, + ContentControlsRepeatingSectionCloneItemInput, + ContentControlsRepeatingSectionDeleteItemInput, + ContentControlsRepeatingSectionSetAllowInsertDeleteInput, + ContentControlsGroupWrapInput, + ContentControlsGroupUngroupInput, + CreateContentControlInput, +} from './content-controls.types.js'; + +// --------------------------------------------------------------------------- +// Public API interface +// --------------------------------------------------------------------------- + +export interface ContentControlsTextApi { + setMultiline(input: ContentControlsTextSetMultilineInput, options?: MutationOptions): ContentControlMutationResult; + setValue(input: ContentControlsTextSetValueInput, options?: MutationOptions): ContentControlMutationResult; + clearValue(input: ContentControlsTextClearValueInput, options?: MutationOptions): ContentControlMutationResult; +} + +export interface ContentControlsDateApi { + setValue(input: ContentControlsDateSetValueInput, options?: MutationOptions): ContentControlMutationResult; + clearValue(input: ContentControlsDateClearValueInput, options?: MutationOptions): ContentControlMutationResult; + setDisplayFormat( + input: ContentControlsDateSetDisplayFormatInput, + options?: MutationOptions, + ): ContentControlMutationResult; + setDisplayLocale( + input: ContentControlsDateSetDisplayLocaleInput, + options?: MutationOptions, + ): ContentControlMutationResult; + setStorageFormat( + input: ContentControlsDateSetStorageFormatInput, + options?: MutationOptions, + ): ContentControlMutationResult; + setCalendar(input: ContentControlsDateSetCalendarInput, options?: MutationOptions): ContentControlMutationResult; +} + +export interface ContentControlsCheckboxApi { + getState(input: ContentControlsCheckboxGetStateInput): ContentControlsCheckboxGetStateResult; + setState(input: ContentControlsCheckboxSetStateInput, options?: MutationOptions): ContentControlMutationResult; + toggle(input: ContentControlsCheckboxToggleInput, options?: MutationOptions): ContentControlMutationResult; + setSymbolPair( + input: ContentControlsCheckboxSetSymbolPairInput, + options?: MutationOptions, + ): ContentControlMutationResult; +} + +export interface ContentControlsChoiceListApi { + getItems(input: ContentControlsChoiceListGetItemsInput): ContentControlsChoiceListGetItemsResult; + setItems(input: ContentControlsChoiceListSetItemsInput, options?: MutationOptions): ContentControlMutationResult; + setSelected( + input: ContentControlsChoiceListSetSelectedInput, + options?: MutationOptions, + ): ContentControlMutationResult; +} + +export interface ContentControlsRepeatingSectionApi { + listItems(input: ContentControlsRepeatingSectionListItemsInput): ContentControlsRepeatingSectionListItemsResult; + insertItemBefore( + input: ContentControlsRepeatingSectionInsertItemBeforeInput, + options?: MutationOptions, + ): ContentControlMutationResult; + insertItemAfter( + input: ContentControlsRepeatingSectionInsertItemAfterInput, + options?: MutationOptions, + ): ContentControlMutationResult; + cloneItem( + input: ContentControlsRepeatingSectionCloneItemInput, + options?: MutationOptions, + ): ContentControlMutationResult; + deleteItem( + input: ContentControlsRepeatingSectionDeleteItemInput, + options?: MutationOptions, + ): ContentControlMutationResult; + setAllowInsertDelete( + input: ContentControlsRepeatingSectionSetAllowInsertDeleteInput, + options?: MutationOptions, + ): ContentControlMutationResult; +} + +export interface ContentControlsGroupApi { + wrap(input: ContentControlsGroupWrapInput, options?: MutationOptions): ContentControlMutationResult; + ungroup(input: ContentControlsGroupUngroupInput, options?: MutationOptions): ContentControlMutationResult; +} + +export interface ContentControlsApi { + // A. Core CRUD + Discovery + list(query?: ContentControlsListQuery): ContentControlsListResult; + get(input: ContentControlsGetInput): ContentControlInfo; + listInRange(input: ContentControlsListInRangeInput): ContentControlsListResult; + selectByTag(input: ContentControlsSelectByTagInput): ContentControlsListResult; + selectByTitle(input: ContentControlsSelectByTitleInput): ContentControlsListResult; + listChildren(input: ContentControlsListChildrenInput): ContentControlsListResult; + getParent(input: ContentControlsGetParentInput): ContentControlInfo | null; + wrap(input: ContentControlsWrapInput, options?: MutationOptions): ContentControlMutationResult; + unwrap(input: ContentControlsUnwrapInput, options?: MutationOptions): ContentControlMutationResult; + delete(input: ContentControlsDeleteInput, options?: MutationOptions): ContentControlMutationResult; + copy(input: ContentControlsCopyInput, options?: MutationOptions): ContentControlMutationResult; + move(input: ContentControlsMoveInput, options?: MutationOptions): ContentControlMutationResult; + patch(input: ContentControlsPatchInput, options?: MutationOptions): ContentControlMutationResult; + setLockMode(input: ContentControlsSetLockModeInput, options?: MutationOptions): ContentControlMutationResult; + setType(input: ContentControlsSetTypeInput, options?: MutationOptions): ContentControlMutationResult; + getContent(input: ContentControlsGetContentInput): ContentControlsGetContentResult; + replaceContent(input: ContentControlsReplaceContentInput, options?: MutationOptions): ContentControlMutationResult; + clearContent(input: ContentControlsClearContentInput, options?: MutationOptions): ContentControlMutationResult; + appendContent(input: ContentControlsAppendContentInput, options?: MutationOptions): ContentControlMutationResult; + prependContent(input: ContentControlsPrependContentInput, options?: MutationOptions): ContentControlMutationResult; + insertBefore(input: ContentControlsInsertBeforeInput, options?: MutationOptions): ContentControlMutationResult; + insertAfter(input: ContentControlsInsertAfterInput, options?: MutationOptions): ContentControlMutationResult; + + // B. Data Binding + Raw/Compatibility + getBinding(input: ContentControlsGetBindingInput): ContentControlBinding | null; + setBinding(input: ContentControlsSetBindingInput, options?: MutationOptions): ContentControlMutationResult; + clearBinding(input: ContentControlsClearBindingInput, options?: MutationOptions): ContentControlMutationResult; + getRawProperties(input: ContentControlsGetRawPropertiesInput): ContentControlsGetRawPropertiesResult; + patchRawProperties( + input: ContentControlsPatchRawPropertiesInput, + options?: MutationOptions, + ): ContentControlMutationResult; + validateWordCompatibility( + input: ContentControlsValidateWordCompatibilityInput, + ): ContentControlsValidateWordCompatibilityResult; + normalizeWordCompatibility( + input: ContentControlsNormalizeWordCompatibilityInput, + options?: MutationOptions, + ): ContentControlMutationResult; + normalizeTagPayload( + input: ContentControlsNormalizeTagPayloadInput, + options?: MutationOptions, + ): ContentControlMutationResult; + + // C. Typed Controls (nested sub-APIs) + text: ContentControlsTextApi; + date: ContentControlsDateApi; + checkbox: ContentControlsCheckboxApi; + choiceList: ContentControlsChoiceListApi; + + // D. Repeating Section + Group (nested sub-APIs) + repeatingSection: ContentControlsRepeatingSectionApi; + group: ContentControlsGroupApi; +} + +// --------------------------------------------------------------------------- +// Adapter interface โ€” implemented by the engine layer +// --------------------------------------------------------------------------- + +export type ContentControlsAdapter = ContentControlsApi; + +// --------------------------------------------------------------------------- +// Execute functions โ€” thin validation + delegation +// --------------------------------------------------------------------------- + +export function executeContentControlsList( + adapter: ContentControlsAdapter, + query?: ContentControlsListQuery, +): ContentControlsListResult { + return adapter.list(query); +} + +export function executeContentControlsGet( + adapter: ContentControlsAdapter, + input: ContentControlsGetInput, +): ContentControlInfo { + return adapter.get(input); +} + +export function executeContentControlsListInRange( + adapter: ContentControlsAdapter, + input: ContentControlsListInRangeInput, +): ContentControlsListResult { + return adapter.listInRange(input); +} + +export function executeContentControlsSelectByTag( + adapter: ContentControlsAdapter, + input: ContentControlsSelectByTagInput, +): ContentControlsListResult { + return adapter.selectByTag(input); +} + +export function executeContentControlsSelectByTitle( + adapter: ContentControlsAdapter, + input: ContentControlsSelectByTitleInput, +): ContentControlsListResult { + return adapter.selectByTitle(input); +} + +export function executeContentControlsListChildren( + adapter: ContentControlsAdapter, + input: ContentControlsListChildrenInput, +): ContentControlsListResult { + return adapter.listChildren(input); +} + +export function executeContentControlsGetParent( + adapter: ContentControlsAdapter, + input: ContentControlsGetParentInput, +): ContentControlInfo | null { + return adapter.getParent(input); +} + +export function executeContentControlsWrap( + adapter: ContentControlsAdapter, + input: ContentControlsWrapInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.wrap(input, options); +} + +export function executeContentControlsUnwrap( + adapter: ContentControlsAdapter, + input: ContentControlsUnwrapInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.unwrap(input, options); +} + +export function executeContentControlsDelete( + adapter: ContentControlsAdapter, + input: ContentControlsDeleteInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.delete(input, options); +} + +export function executeContentControlsCopy( + adapter: ContentControlsAdapter, + input: ContentControlsCopyInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.copy(input, options); +} + +export function executeContentControlsMove( + adapter: ContentControlsAdapter, + input: ContentControlsMoveInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.move(input, options); +} + +export function executeContentControlsPatch( + adapter: ContentControlsAdapter, + input: ContentControlsPatchInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.patch(input, options); +} + +export function executeContentControlsSetLockMode( + adapter: ContentControlsAdapter, + input: ContentControlsSetLockModeInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.setLockMode(input, options); +} + +export function executeContentControlsSetType( + adapter: ContentControlsAdapter, + input: ContentControlsSetTypeInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.setType(input, options); +} + +export function executeContentControlsGetContent( + adapter: ContentControlsAdapter, + input: ContentControlsGetContentInput, +): ContentControlsGetContentResult { + return adapter.getContent(input); +} + +export function executeContentControlsReplaceContent( + adapter: ContentControlsAdapter, + input: ContentControlsReplaceContentInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.replaceContent(input, options); +} + +export function executeContentControlsClearContent( + adapter: ContentControlsAdapter, + input: ContentControlsClearContentInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.clearContent(input, options); +} + +export function executeContentControlsAppendContent( + adapter: ContentControlsAdapter, + input: ContentControlsAppendContentInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.appendContent(input, options); +} + +export function executeContentControlsPrependContent( + adapter: ContentControlsAdapter, + input: ContentControlsPrependContentInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.prependContent(input, options); +} + +export function executeContentControlsInsertBefore( + adapter: ContentControlsAdapter, + input: ContentControlsInsertBeforeInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.insertBefore(input, options); +} + +export function executeContentControlsInsertAfter( + adapter: ContentControlsAdapter, + input: ContentControlsInsertAfterInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.insertAfter(input, options); +} + +export function executeContentControlsGetBinding( + adapter: ContentControlsAdapter, + input: ContentControlsGetBindingInput, +): ContentControlBinding | null { + return adapter.getBinding(input); +} + +export function executeContentControlsSetBinding( + adapter: ContentControlsAdapter, + input: ContentControlsSetBindingInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.setBinding(input, options); +} + +export function executeContentControlsClearBinding( + adapter: ContentControlsAdapter, + input: ContentControlsClearBindingInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.clearBinding(input, options); +} + +export function executeContentControlsGetRawProperties( + adapter: ContentControlsAdapter, + input: ContentControlsGetRawPropertiesInput, +): ContentControlsGetRawPropertiesResult { + return adapter.getRawProperties(input); +} + +export function executeContentControlsPatchRawProperties( + adapter: ContentControlsAdapter, + input: ContentControlsPatchRawPropertiesInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.patchRawProperties(input, options); +} + +export function executeContentControlsValidateWordCompatibility( + adapter: ContentControlsAdapter, + input: ContentControlsValidateWordCompatibilityInput, +): ContentControlsValidateWordCompatibilityResult { + return adapter.validateWordCompatibility(input); +} + +export function executeContentControlsNormalizeWordCompatibility( + adapter: ContentControlsAdapter, + input: ContentControlsNormalizeWordCompatibilityInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.normalizeWordCompatibility(input, options); +} + +export function executeContentControlsNormalizeTagPayload( + adapter: ContentControlsAdapter, + input: ContentControlsNormalizeTagPayloadInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.normalizeTagPayload(input, options); +} + +// Typed controls +export function executeContentControlsTextSetMultiline( + adapter: ContentControlsAdapter, + input: ContentControlsTextSetMultilineInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.text.setMultiline(input, options); +} + +export function executeContentControlsTextSetValue( + adapter: ContentControlsAdapter, + input: ContentControlsTextSetValueInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.text.setValue(input, options); +} + +export function executeContentControlsTextClearValue( + adapter: ContentControlsAdapter, + input: ContentControlsTextClearValueInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.text.clearValue(input, options); +} + +export function executeContentControlsDateSetValue( + adapter: ContentControlsAdapter, + input: ContentControlsDateSetValueInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.date.setValue(input, options); +} + +export function executeContentControlsDateClearValue( + adapter: ContentControlsAdapter, + input: ContentControlsDateClearValueInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.date.clearValue(input, options); +} + +export function executeContentControlsDateSetDisplayFormat( + adapter: ContentControlsAdapter, + input: ContentControlsDateSetDisplayFormatInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.date.setDisplayFormat(input, options); +} + +export function executeContentControlsDateSetDisplayLocale( + adapter: ContentControlsAdapter, + input: ContentControlsDateSetDisplayLocaleInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.date.setDisplayLocale(input, options); +} + +export function executeContentControlsDateSetStorageFormat( + adapter: ContentControlsAdapter, + input: ContentControlsDateSetStorageFormatInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.date.setStorageFormat(input, options); +} + +export function executeContentControlsDateSetCalendar( + adapter: ContentControlsAdapter, + input: ContentControlsDateSetCalendarInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.date.setCalendar(input, options); +} + +export function executeContentControlsCheckboxGetState( + adapter: ContentControlsAdapter, + input: ContentControlsCheckboxGetStateInput, +): ContentControlsCheckboxGetStateResult { + return adapter.checkbox.getState(input); +} + +export function executeContentControlsCheckboxSetState( + adapter: ContentControlsAdapter, + input: ContentControlsCheckboxSetStateInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.checkbox.setState(input, options); +} + +export function executeContentControlsCheckboxToggle( + adapter: ContentControlsAdapter, + input: ContentControlsCheckboxToggleInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.checkbox.toggle(input, options); +} + +export function executeContentControlsCheckboxSetSymbolPair( + adapter: ContentControlsAdapter, + input: ContentControlsCheckboxSetSymbolPairInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.checkbox.setSymbolPair(input, options); +} + +export function executeContentControlsChoiceListGetItems( + adapter: ContentControlsAdapter, + input: ContentControlsChoiceListGetItemsInput, +): ContentControlsChoiceListGetItemsResult { + return adapter.choiceList.getItems(input); +} + +export function executeContentControlsChoiceListSetItems( + adapter: ContentControlsAdapter, + input: ContentControlsChoiceListSetItemsInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.choiceList.setItems(input, options); +} + +export function executeContentControlsChoiceListSetSelected( + adapter: ContentControlsAdapter, + input: ContentControlsChoiceListSetSelectedInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.choiceList.setSelected(input, options); +} + +export function executeContentControlsRepeatingSectionListItems( + adapter: ContentControlsAdapter, + input: ContentControlsRepeatingSectionListItemsInput, +): ContentControlsRepeatingSectionListItemsResult { + return adapter.repeatingSection.listItems(input); +} + +export function executeContentControlsRepeatingSectionInsertItemBefore( + adapter: ContentControlsAdapter, + input: ContentControlsRepeatingSectionInsertItemBeforeInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.repeatingSection.insertItemBefore(input, options); +} + +export function executeContentControlsRepeatingSectionInsertItemAfter( + adapter: ContentControlsAdapter, + input: ContentControlsRepeatingSectionInsertItemAfterInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.repeatingSection.insertItemAfter(input, options); +} + +export function executeContentControlsRepeatingSectionCloneItem( + adapter: ContentControlsAdapter, + input: ContentControlsRepeatingSectionCloneItemInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.repeatingSection.cloneItem(input, options); +} + +export function executeContentControlsRepeatingSectionDeleteItem( + adapter: ContentControlsAdapter, + input: ContentControlsRepeatingSectionDeleteItemInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.repeatingSection.deleteItem(input, options); +} + +export function executeContentControlsRepeatingSectionSetAllowInsertDelete( + adapter: ContentControlsAdapter, + input: ContentControlsRepeatingSectionSetAllowInsertDeleteInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.repeatingSection.setAllowInsertDelete(input, options); +} + +export function executeContentControlsGroupWrap( + adapter: ContentControlsAdapter, + input: ContentControlsGroupWrapInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.group.wrap(input, options); +} + +export function executeContentControlsGroupUngroup( + adapter: ContentControlsAdapter, + input: ContentControlsGroupUngroupInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.group.ungroup(input, options); +} + +// Create (lives under create.* namespace, not contentControls.*) +export function executeCreateContentControl( + adapter: ContentControlsCreateAdapter, + input: CreateContentControlInput, + options?: MutationOptions, +): ContentControlMutationResult { + return adapter.create(input, options); +} + +/** Adapter extension for create.contentControl. */ +export interface ContentControlsCreateAdapter { + create(input: CreateContentControlInput, options?: MutationOptions): ContentControlMutationResult; +} diff --git a/packages/document-api/src/content-controls/content-controls.types.ts b/packages/document-api/src/content-controls/content-controls.types.ts new file mode 100644 index 0000000000..36a8a14d99 --- /dev/null +++ b/packages/document-api/src/content-controls/content-controls.types.ts @@ -0,0 +1,534 @@ +/** + * Types for the `contentControls` namespace. + * + * Canonical shapes for content control (SDT) discovery, mutation, and + * typed-control operations. All downstream types reference these definitions. + */ + +import type { NodeKind } from '../types/base.js'; +import type { ReceiptFailure } from '../types/receipt.js'; + +// --------------------------------------------------------------------------- +// Enums and constants +// --------------------------------------------------------------------------- + +/** Semantic SDT subtype derived from `w:sdtPr` children. */ +export type ContentControlType = + | 'text' + | 'date' + | 'checkbox' + | 'comboBox' + | 'dropDownList' + | 'repeatingSection' + | 'repeatingSectionItem' + | 'group' + | 'unknown'; + +export const CONTENT_CONTROL_TYPES = [ + 'text', + 'date', + 'checkbox', + 'comboBox', + 'dropDownList', + 'repeatingSection', + 'repeatingSectionItem', + 'group', + 'unknown', +] as const satisfies readonly ContentControlType[]; + +/** ECMA-376 `w:lock` modes. */ +export type LockMode = 'unlocked' | 'sdtLocked' | 'contentLocked' | 'sdtContentLocked'; + +export const LOCK_MODES = [ + 'unlocked', + 'sdtLocked', + 'contentLocked', + 'sdtContentLocked', +] as const satisfies readonly LockMode[]; + +/** Visual appearance of the content control wrapper in Word. */ +export type ContentControlAppearance = 'boundingBox' | 'tags' | 'hidden'; + +export const CONTENT_CONTROL_APPEARANCES = [ + 'boundingBox', + 'tags', + 'hidden', +] as const satisfies readonly ContentControlAppearance[]; + +// --------------------------------------------------------------------------- +// Shared sub-shapes +// --------------------------------------------------------------------------- + +/** Symbol specification for checkbox checked/unchecked glyphs. */ +export interface ContentControlSymbol { + font: string; + char: string; +} + +/** Choice list item (shared by comboBox and dropDownList). */ +export interface ContentControlListItem { + displayText: string; + value: string; +} + +/** Data binding metadata from `w:dataBinding`. */ +export interface ContentControlBinding { + storeItemId: string; + xpath: string; + prefixMappings?: string; +} + +// --------------------------------------------------------------------------- +// Subtype-specific property bags +// --------------------------------------------------------------------------- + +export interface TextControlProperties { + multiline?: boolean; +} + +export interface DateControlProperties { + dateFormat?: string; + dateLocale?: string; + storageFormat?: string; + calendar?: string; +} + +export interface CheckboxControlProperties { + checked?: boolean; + checkedSymbol?: ContentControlSymbol; + uncheckedSymbol?: ContentControlSymbol; +} + +export interface ChoiceControlProperties { + items?: ContentControlListItem[]; + selectedValue?: string; +} + +export interface RepeatingSectionControlProperties { + allowInsertDelete?: boolean; +} + +// --------------------------------------------------------------------------- +// ContentControlProperties โ€” the typed property bag +// --------------------------------------------------------------------------- + +export interface ContentControlProperties { + tag?: string; + alias?: string; + appearance?: ContentControlAppearance; + color?: string; + placeholder?: string; + showingPlaceholder?: boolean; + temporary?: boolean; + tabIndex?: number; + + // Subtype-specific (populated when matching controlType) + multiline?: boolean; + dateFormat?: string; + dateLocale?: string; + storageFormat?: string; + calendar?: string; + checked?: boolean; + checkedSymbol?: ContentControlSymbol; + uncheckedSymbol?: ContentControlSymbol; + items?: ContentControlListItem[]; + selectedValue?: string; + allowInsertDelete?: boolean; +} + +// --------------------------------------------------------------------------- +// ContentControlInfo โ€” the canonical read shape +// --------------------------------------------------------------------------- + +export interface ContentControlTarget { + kind: NodeKind; + nodeType: 'sdt'; + nodeId: string; +} + +/** + * Canonical content control info returned by all read/list operations. + * Replaces the old `SdtNodeInfo`. + */ +export interface ContentControlInfo { + nodeType: 'sdt'; + kind: NodeKind; + id: string; + controlType: ContentControlType; + lockMode: LockMode; + properties: ContentControlProperties; + binding?: ContentControlBinding; + raw?: Record; + target: ContentControlTarget; + text?: string; +} + +// --------------------------------------------------------------------------- +// Mutation result envelope +// --------------------------------------------------------------------------- + +export interface ContentControlMutationSuccess { + success: true; + contentControl: ContentControlTarget; + updatedRef?: ContentControlTarget; +} + +export interface ContentControlMutationFailure { + success: false; + failure: ReceiptFailure; +} + +export type ContentControlMutationResult = ContentControlMutationSuccess | ContentControlMutationFailure; + +// --------------------------------------------------------------------------- +// Discovery list result +// --------------------------------------------------------------------------- + +export interface ContentControlsListResult { + items: ContentControlInfo[]; + total: number; +} + +// --------------------------------------------------------------------------- +// Pagination options (shared across list/select operations) +// --------------------------------------------------------------------------- + +export interface ContentControlsPaginationOptions { + offset?: number; + limit?: number; +} + +// --------------------------------------------------------------------------- +// A. Core CRUD + Discovery โ€” Input types +// --------------------------------------------------------------------------- + +export interface CreateContentControlInput { + kind: NodeKind; + controlType?: ContentControlType; + target?: ContentControlTarget; + tag?: string; + alias?: string; + lockMode?: LockMode; + content?: string; +} + +export interface ContentControlsListQuery extends ContentControlsPaginationOptions { + controlType?: ContentControlType; + tag?: string; +} + +export interface ContentControlsGetInput { + target: ContentControlTarget; +} + +export interface ContentControlsListInRangeInput extends ContentControlsPaginationOptions { + startBlockId: string; + endBlockId: string; +} + +export interface ContentControlsSelectByTagInput extends ContentControlsPaginationOptions { + tag: string; +} + +export interface ContentControlsSelectByTitleInput extends ContentControlsPaginationOptions { + title: string; +} + +export interface ContentControlsListChildrenInput extends ContentControlsPaginationOptions { + target: ContentControlTarget; +} + +export interface ContentControlsGetParentInput { + target: ContentControlTarget; +} + +export interface ContentControlsWrapInput { + kind: NodeKind; + target: ContentControlTarget; + tag?: string; + alias?: string; + lockMode?: LockMode; +} + +export interface ContentControlsUnwrapInput { + target: ContentControlTarget; +} + +export interface ContentControlsDeleteInput { + target: ContentControlTarget; +} + +export interface ContentControlsCopyInput { + target: ContentControlTarget; + destination: ContentControlTarget; +} + +export interface ContentControlsMoveInput { + target: ContentControlTarget; + destination: ContentControlTarget; +} + +export interface ContentControlsPatchInput { + target: ContentControlTarget; + alias?: string | null; + tag?: string | null; + appearance?: ContentControlAppearance | null; + color?: string | null; + placeholder?: string | null; + showingPlaceholder?: boolean; + temporary?: boolean; + tabIndex?: number | null; +} + +export interface ContentControlsSetLockModeInput { + target: ContentControlTarget; + lockMode: LockMode; +} + +export interface ContentControlsSetTypeInput { + target: ContentControlTarget; + controlType: ContentControlType; +} + +export interface ContentControlsGetContentInput { + target: ContentControlTarget; +} + +export interface ContentControlsGetContentResult { + content: string; + format: 'text' | 'html'; +} + +export interface ContentControlsReplaceContentInput { + target: ContentControlTarget; + content: string; + format?: 'text' | 'html'; +} + +export interface ContentControlsClearContentInput { + target: ContentControlTarget; +} + +export interface ContentControlsAppendContentInput { + target: ContentControlTarget; + content: string; + format?: 'text' | 'html'; +} + +export interface ContentControlsPrependContentInput { + target: ContentControlTarget; + content: string; + format?: 'text' | 'html'; +} + +export interface ContentControlsInsertBeforeInput { + target: ContentControlTarget; + content: string; + format?: 'text' | 'html'; +} + +export interface ContentControlsInsertAfterInput { + target: ContentControlTarget; + content: string; + format?: 'text' | 'html'; +} + +// --------------------------------------------------------------------------- +// B. Data Binding + Raw/Compatibility โ€” Input types +// --------------------------------------------------------------------------- + +export interface ContentControlsGetBindingInput { + target: ContentControlTarget; +} + +export interface ContentControlsSetBindingInput { + target: ContentControlTarget; + storeItemId: string; + xpath: string; + prefixMappings?: string; +} + +export interface ContentControlsClearBindingInput { + target: ContentControlTarget; +} + +export interface ContentControlsGetRawPropertiesInput { + target: ContentControlTarget; +} + +export interface ContentControlsGetRawPropertiesResult { + properties: Record; +} + +export type RawPatchOp = + | { op: 'set'; name: string; element: Record } + | { op: 'remove'; name: string } + | { op: 'setAttr'; name: string; attr: string; value: string } + | { op: 'removeAttr'; name: string; attr: string }; + +export interface ContentControlsPatchRawPropertiesInput { + target: ContentControlTarget; + patches: RawPatchOp[]; +} + +export interface ContentControlsValidateWordCompatibilityInput { + target: ContentControlTarget; +} + +export interface WordCompatibilityDiagnostic { + code: string; + severity: 'error' | 'warning'; + message: string; +} + +export interface ContentControlsValidateWordCompatibilityResult { + compatible: boolean; + diagnostics: WordCompatibilityDiagnostic[]; +} + +export interface ContentControlsNormalizeWordCompatibilityInput { + target: ContentControlTarget; +} + +export interface ContentControlsNormalizeTagPayloadInput { + target: ContentControlTarget; +} + +// --------------------------------------------------------------------------- +// C. Typed Controls โ€” Input types +// --------------------------------------------------------------------------- + +// Text +export interface ContentControlsTextSetMultilineInput { + target: ContentControlTarget; + multiline: boolean; +} + +export interface ContentControlsTextSetValueInput { + target: ContentControlTarget; + value: string; +} + +export interface ContentControlsTextClearValueInput { + target: ContentControlTarget; +} + +// Date +export interface ContentControlsDateSetValueInput { + target: ContentControlTarget; + value: string; +} + +export interface ContentControlsDateClearValueInput { + target: ContentControlTarget; +} + +export interface ContentControlsDateSetDisplayFormatInput { + target: ContentControlTarget; + format: string; +} + +export interface ContentControlsDateSetDisplayLocaleInput { + target: ContentControlTarget; + locale: string; +} + +export interface ContentControlsDateSetStorageFormatInput { + target: ContentControlTarget; + format: string; +} + +export interface ContentControlsDateSetCalendarInput { + target: ContentControlTarget; + calendar: string; +} + +// Checkbox +export interface ContentControlsCheckboxGetStateInput { + target: ContentControlTarget; +} + +export interface ContentControlsCheckboxGetStateResult { + checked: boolean; +} + +export interface ContentControlsCheckboxSetStateInput { + target: ContentControlTarget; + checked: boolean; +} + +export interface ContentControlsCheckboxToggleInput { + target: ContentControlTarget; +} + +export interface ContentControlsCheckboxSetSymbolPairInput { + target: ContentControlTarget; + checkedSymbol: ContentControlSymbol; + uncheckedSymbol: ContentControlSymbol; +} + +// Choice list (comboBox + dropDownList) +export interface ContentControlsChoiceListGetItemsInput { + target: ContentControlTarget; +} + +export interface ContentControlsChoiceListGetItemsResult { + items: ContentControlListItem[]; + selectedValue?: string; +} + +export interface ContentControlsChoiceListSetItemsInput { + target: ContentControlTarget; + items: ContentControlListItem[]; +} + +export interface ContentControlsChoiceListSetSelectedInput { + target: ContentControlTarget; + value: string; +} + +// --------------------------------------------------------------------------- +// D. Repeating Section + Group โ€” Input types +// --------------------------------------------------------------------------- + +export interface ContentControlsRepeatingSectionListItemsInput { + target: ContentControlTarget; +} + +export interface ContentControlsRepeatingSectionListItemsResult { + items: ContentControlInfo[]; + total: number; +} + +export interface ContentControlsRepeatingSectionInsertItemBeforeInput { + target: ContentControlTarget; + index: number; +} + +export interface ContentControlsRepeatingSectionInsertItemAfterInput { + target: ContentControlTarget; + index: number; +} + +export interface ContentControlsRepeatingSectionCloneItemInput { + target: ContentControlTarget; + index: number; +} + +export interface ContentControlsRepeatingSectionDeleteItemInput { + target: ContentControlTarget; + index: number; +} + +export interface ContentControlsRepeatingSectionSetAllowInsertDeleteInput { + target: ContentControlTarget; + allow: boolean; +} + +export interface ContentControlsGroupWrapInput { + target: ContentControlTarget; +} + +export interface ContentControlsGroupUngroupInput { + target: ContentControlTarget; +} diff --git a/packages/document-api/src/contract/contract.test.ts b/packages/document-api/src/contract/contract.test.ts index c824542da3..f76d7d799e 100644 --- a/packages/document-api/src/contract/contract.test.ts +++ b/packages/document-api/src/contract/contract.test.ts @@ -175,6 +175,7 @@ describe('document-api contract catalog', () => { 'toc', 'images', 'hyperlinks', + 'contentControls', ]; for (const id of OPERATION_IDS) { expect(validGroups, `${id} has invalid referenceGroup`).toContain(OPERATION_DEFINITIONS[id].referenceGroup); diff --git a/packages/document-api/src/contract/metadata-types.ts b/packages/document-api/src/contract/metadata-types.ts index eabb1deac0..2094cdd742 100644 --- a/packages/document-api/src/contract/metadata-types.ts +++ b/packages/document-api/src/contract/metadata-types.ts @@ -37,6 +37,9 @@ export const PRE_APPLY_THROW_CODES = [ 'RAW_MODE_REQUIRED', 'PRESERVE_ONLY_VIOLATION', 'CAPABILITY_UNSUPPORTED', + // SD-2070 content controls throw codes + 'LOCK_VIOLATION', + 'TYPE_MISMATCH', ] as const; export type PreApplyThrowCode = (typeof PRE_APPLY_THROW_CODES)[number]; diff --git a/packages/document-api/src/contract/operation-definitions.ts b/packages/document-api/src/contract/operation-definitions.ts index feae58d7da..a139b1659a 100644 --- a/packages/document-api/src/contract/operation-definitions.ts +++ b/packages/document-api/src/contract/operation-definitions.ts @@ -51,7 +51,8 @@ export type ReferenceGroupKey = | 'history' | 'toc' | 'images' - | 'hyperlinks'; + | 'hyperlinks' + | 'contentControls'; // --------------------------------------------------------------------------- // Entry shape @@ -159,6 +160,21 @@ const T_NOT_FOUND_COMMAND_TRACKED = [...T_NOT_FOUND_COMMAND] as const; // Image operations can throw AMBIGUOUS_TARGET when multiple images share an sdImageId. const T_IMAGE_COMMAND = ['TARGET_NOT_FOUND', 'AMBIGUOUS_TARGET', 'INVALID_TARGET', 'CAPABILITY_UNAVAILABLE'] as const; +// Content controls throw-code families +const T_CC_READ = ['TARGET_NOT_FOUND', 'INVALID_INPUT', 'CAPABILITY_UNAVAILABLE'] as const; +const T_CC_MUTATION = [ + 'TARGET_NOT_FOUND', + 'INVALID_TARGET', + 'AMBIGUOUS_TARGET', + 'INVALID_INPUT', + 'LOCK_VIOLATION', + 'REVISION_MISMATCH', + 'CAPABILITY_UNAVAILABLE', +] as const; +const T_CC_TYPED = [...T_CC_MUTATION, 'TYPE_MISMATCH'] as const; +const T_CC_TYPED_READ = [...T_CC_READ, 'TYPE_MISMATCH'] as const; +const T_CC_RAW = ['TARGET_NOT_FOUND', 'INVALID_INPUT', 'REVISION_MISMATCH', 'CAPABILITY_UNAVAILABLE'] as const; + const T_QUERY_MATCH = ['MATCH_NOT_FOUND', 'AMBIGUOUS_MATCH', 'INVALID_INPUT', 'INTERNAL_ERROR'] as const; const T_SECTION_CREATE = [ 'TARGET_NOT_FOUND', @@ -3193,6 +3209,815 @@ export const OPERATION_DEFINITIONS = { referenceDocPath: 'hyperlinks/remove.mdx', referenceGroup: 'hyperlinks', }, + + // ========================================================================= + // Content Controls (SD-2070) + // ========================================================================= + + // --- A. Core CRUD + Discovery --- + + 'create.contentControl': { + memberPath: 'create.contentControl', + description: 'Create a new content control (SDT) in the document.', + expectedResult: 'Returns a ContentControlMutationResult with the created content control target.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'non-idempotent', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['INVALID_TARGET', 'NO_OP'], + throws: T_CC_MUTATION, + }), + referenceDocPath: 'content-controls/create.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.list': { + memberPath: 'contentControls.list', + description: 'List all content controls in the document with optional type/tag filtering.', + expectedResult: 'Returns a ContentControlsListResult with items and total count.', + requiresDocumentContext: true, + metadata: readOperation({ throws: T_CC_READ }), + referenceDocPath: 'content-controls/list.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.get': { + memberPath: 'contentControls.get', + description: 'Retrieve a single content control by target.', + expectedResult: 'Returns a ContentControlInfo with full properties.', + requiresDocumentContext: true, + metadata: readOperation({ throws: T_CC_READ }), + referenceDocPath: 'content-controls/get.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.listInRange': { + memberPath: 'contentControls.listInRange', + description: 'List content controls within a block range.', + expectedResult: 'Returns a ContentControlsListResult scoped to the range.', + requiresDocumentContext: true, + metadata: readOperation({ throws: T_CC_READ }), + referenceDocPath: 'content-controls/list-in-range.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.selectByTag': { + memberPath: 'contentControls.selectByTag', + description: 'Select content controls matching a specific tag value.', + expectedResult: 'Returns a ContentControlsListResult with matching items.', + requiresDocumentContext: true, + metadata: readOperation({ throws: T_CC_READ }), + referenceDocPath: 'content-controls/select-by-tag.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.selectByTitle': { + memberPath: 'contentControls.selectByTitle', + description: 'Select content controls matching a specific title (alias) value.', + expectedResult: 'Returns a ContentControlsListResult with matching items.', + requiresDocumentContext: true, + metadata: readOperation({ throws: T_CC_READ }), + referenceDocPath: 'content-controls/select-by-title.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.listChildren': { + memberPath: 'contentControls.listChildren', + description: 'List direct child content controls nested inside the target.', + expectedResult: 'Returns a ContentControlsListResult with child items.', + requiresDocumentContext: true, + metadata: readOperation({ throws: T_CC_READ }), + referenceDocPath: 'content-controls/list-children.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.getParent': { + memberPath: 'contentControls.getParent', + description: 'Get the parent content control of the target, if any.', + expectedResult: 'Returns a ContentControlInfo for the parent, or null if no parent SDT exists.', + requiresDocumentContext: true, + metadata: readOperation({ throws: T_CC_READ }), + referenceDocPath: 'content-controls/get-parent.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.wrap': { + memberPath: 'contentControls.wrap', + description: 'Wrap existing content with a new content control.', + expectedResult: 'Returns a ContentControlMutationResult with the wrapper target.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'conditional', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_MUTATION, + }), + referenceDocPath: 'content-controls/wrap.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.unwrap': { + memberPath: 'contentControls.unwrap', + description: 'Remove the content control wrapper, preserving its content in place.', + expectedResult: 'Returns a ContentControlMutationResult; reports NO_OP if already unwrapped.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'conditional', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_MUTATION, + }), + referenceDocPath: 'content-controls/unwrap.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.delete': { + memberPath: 'contentControls.delete', + description: 'Delete a content control and its content from the document.', + expectedResult: 'Returns a ContentControlMutationResult; reports NO_OP if already removed.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'conditional', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_MUTATION, + }), + referenceDocPath: 'content-controls/delete.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.copy': { + memberPath: 'contentControls.copy', + description: 'Copy a content control to a destination position. Copied SDTs receive new IDs.', + expectedResult: 'Returns a ContentControlMutationResult with the copied content control target.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'non-idempotent', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_MUTATION, + }), + referenceDocPath: 'content-controls/copy.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.move': { + memberPath: 'contentControls.move', + description: 'Move a content control to a new position. Preserves original IDs.', + expectedResult: 'Returns a ContentControlMutationResult with the updated target position.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'conditional', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_MUTATION, + }), + referenceDocPath: 'content-controls/move.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.patch': { + memberPath: 'contentControls.patch', + description: 'Patch metadata properties on a content control (tag, alias, appearance, color, etc.).', + expectedResult: 'Returns a ContentControlMutationResult; reports NO_OP if no fields changed.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'conditional', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_MUTATION, + }), + referenceDocPath: 'content-controls/patch.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.setLockMode': { + memberPath: 'contentControls.setLockMode', + description: 'Set the lock mode on a content control.', + expectedResult: 'Returns a ContentControlMutationResult; reports NO_OP if lock mode unchanged.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'conditional', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_MUTATION, + }), + referenceDocPath: 'content-controls/set-lock-mode.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.setType': { + memberPath: 'contentControls.setType', + description: + 'Transition a content control to a different semantic type. Metadata-only; no implicit content rewrite.', + expectedResult: 'Returns a ContentControlMutationResult; reports NO_OP if type unchanged.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'conditional', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_TYPED, + }), + referenceDocPath: 'content-controls/set-type.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.getContent': { + memberPath: 'contentControls.getContent', + description: 'Get the text content of a content control.', + expectedResult: 'Returns a ContentControlsGetContentResult with the content string and format.', + requiresDocumentContext: true, + metadata: readOperation({ throws: T_CC_READ }), + referenceDocPath: 'content-controls/get-content.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.replaceContent': { + memberPath: 'contentControls.replaceContent', + description: 'Replace the entire content of a content control.', + expectedResult: 'Returns a ContentControlMutationResult with the updated target.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'conditional', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_MUTATION, + }), + referenceDocPath: 'content-controls/replace-content.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.clearContent': { + memberPath: 'contentControls.clearContent', + description: 'Clear all content inside a content control, leaving it empty.', + expectedResult: 'Returns a ContentControlMutationResult; reports NO_OP if already empty.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'conditional', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_MUTATION, + }), + referenceDocPath: 'content-controls/clear-content.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.appendContent': { + memberPath: 'contentControls.appendContent', + description: 'Append content to the end of a content control.', + expectedResult: 'Returns a ContentControlMutationResult with the updated target.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'conditional', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_MUTATION, + }), + referenceDocPath: 'content-controls/append-content.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.prependContent': { + memberPath: 'contentControls.prependContent', + description: 'Prepend content to the beginning of a content control.', + expectedResult: 'Returns a ContentControlMutationResult with the updated target.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'conditional', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_MUTATION, + }), + referenceDocPath: 'content-controls/prepend-content.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.insertBefore': { + memberPath: 'contentControls.insertBefore', + description: 'Insert content immediately before a content control.', + expectedResult: 'Returns a ContentControlMutationResult with the target.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'conditional', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_MUTATION, + }), + referenceDocPath: 'content-controls/insert-before.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.insertAfter': { + memberPath: 'contentControls.insertAfter', + description: 'Insert content immediately after a content control.', + expectedResult: 'Returns a ContentControlMutationResult with the target.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'conditional', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_MUTATION, + }), + referenceDocPath: 'content-controls/insert-after.mdx', + referenceGroup: 'contentControls', + }, + + // --- B. Data Binding + Raw/Compatibility --- + + 'contentControls.getBinding': { + memberPath: 'contentControls.getBinding', + description: 'Get the data binding metadata (w:dataBinding) of a content control.', + expectedResult: 'Returns the ContentControlBinding or null if no binding is set.', + requiresDocumentContext: true, + metadata: readOperation({ throws: T_CC_READ }), + referenceDocPath: 'content-controls/get-binding.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.setBinding': { + memberPath: 'contentControls.setBinding', + description: 'Set data binding metadata on a content control.', + expectedResult: 'Returns a ContentControlMutationResult; reports NO_OP if binding unchanged.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'conditional', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_MUTATION, + }), + referenceDocPath: 'content-controls/set-binding.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.clearBinding': { + memberPath: 'contentControls.clearBinding', + description: 'Remove data binding metadata from a content control.', + expectedResult: 'Returns a ContentControlMutationResult; reports NO_OP if no binding existed.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'conditional', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_MUTATION, + }), + referenceDocPath: 'content-controls/clear-binding.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.getRawProperties': { + memberPath: 'contentControls.getRawProperties', + description: 'Get the raw sdtPr properties of a content control as a passthrough hash.', + expectedResult: 'Returns a ContentControlsGetRawPropertiesResult with the raw properties.', + requiresDocumentContext: true, + metadata: readOperation({ throws: T_CC_READ }), + referenceDocPath: 'content-controls/get-raw-properties.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.patchRawProperties': { + memberPath: 'contentControls.patchRawProperties', + description: 'Apply raw XML-level patches to the sdtPr subtree of a content control.', + expectedResult: 'Returns a ContentControlMutationResult; reports NO_OP if no effective changes.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'conditional', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_RAW, + }), + referenceDocPath: 'content-controls/patch-raw-properties.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.validateWordCompatibility': { + memberPath: 'contentControls.validateWordCompatibility', + description: 'Validate a content control for Word compatibility issues.', + expectedResult: 'Returns a compatibility result with diagnostics.', + requiresDocumentContext: true, + metadata: readOperation({ throws: T_CC_READ }), + referenceDocPath: 'content-controls/validate-word-compatibility.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.normalizeWordCompatibility': { + memberPath: 'contentControls.normalizeWordCompatibility', + description: 'Normalize a content control to resolve Word compatibility issues.', + expectedResult: 'Returns a ContentControlMutationResult; reports NO_OP if already compatible.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'idempotent', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_RAW, + }), + referenceDocPath: 'content-controls/normalize-word-compatibility.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.normalizeTagPayload': { + memberPath: 'contentControls.normalizeTagPayload', + description: 'Normalize a content control tag between plain-string and JSON-encoded formats.', + expectedResult: 'Returns a ContentControlMutationResult; reports NO_OP if already normalized.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'idempotent', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_RAW, + }), + referenceDocPath: 'content-controls/normalize-tag-payload.mdx', + referenceGroup: 'contentControls', + }, + + // --- C. Typed Controls --- + + 'contentControls.text.setMultiline': { + memberPath: 'contentControls.text.setMultiline', + description: 'Set or clear the multiline attribute on a plain-text content control.', + expectedResult: 'Returns a ContentControlMutationResult; reports NO_OP if unchanged.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'conditional', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_TYPED, + }), + referenceDocPath: 'content-controls/text/set-multiline.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.text.setValue': { + memberPath: 'contentControls.text.setValue', + description: 'Set the text value of a plain-text content control.', + expectedResult: 'Returns a ContentControlMutationResult; reports NO_OP if unchanged.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'conditional', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_TYPED, + }), + referenceDocPath: 'content-controls/text/set-value.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.text.clearValue': { + memberPath: 'contentControls.text.clearValue', + description: 'Clear the text value of a plain-text content control.', + expectedResult: 'Returns a ContentControlMutationResult; reports NO_OP if already empty.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'conditional', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_TYPED, + }), + referenceDocPath: 'content-controls/text/clear-value.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.date.setValue': { + memberPath: 'contentControls.date.setValue', + description: 'Set the date value of a date content control.', + expectedResult: 'Returns a ContentControlMutationResult; reports NO_OP if unchanged.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'conditional', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_TYPED, + }), + referenceDocPath: 'content-controls/date/set-value.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.date.clearValue': { + memberPath: 'contentControls.date.clearValue', + description: 'Clear the date value of a date content control.', + expectedResult: 'Returns a ContentControlMutationResult; reports NO_OP if already empty.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'conditional', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_TYPED, + }), + referenceDocPath: 'content-controls/date/clear-value.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.date.setDisplayFormat': { + memberPath: 'contentControls.date.setDisplayFormat', + description: 'Set the display format string for a date content control.', + expectedResult: 'Returns a ContentControlMutationResult; reports NO_OP if unchanged.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'conditional', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_TYPED, + }), + referenceDocPath: 'content-controls/date/set-display-format.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.date.setDisplayLocale': { + memberPath: 'contentControls.date.setDisplayLocale', + description: 'Set the display locale for a date content control.', + expectedResult: 'Returns a ContentControlMutationResult; reports NO_OP if unchanged.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'conditional', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_TYPED, + }), + referenceDocPath: 'content-controls/date/set-display-locale.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.date.setStorageFormat': { + memberPath: 'contentControls.date.setStorageFormat', + description: 'Set the XML storage format for a date content control.', + expectedResult: 'Returns a ContentControlMutationResult; reports NO_OP if unchanged.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'conditional', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_TYPED, + }), + referenceDocPath: 'content-controls/date/set-storage-format.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.date.setCalendar': { + memberPath: 'contentControls.date.setCalendar', + description: 'Set the calendar type for a date content control.', + expectedResult: 'Returns a ContentControlMutationResult; reports NO_OP if unchanged.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'conditional', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_TYPED, + }), + referenceDocPath: 'content-controls/date/set-calendar.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.checkbox.getState': { + memberPath: 'contentControls.checkbox.getState', + description: 'Get the checked state of a checkbox content control.', + expectedResult: 'Returns a CheckboxGetStateResult with the checked boolean.', + requiresDocumentContext: true, + metadata: readOperation({ throws: T_CC_TYPED_READ }), + referenceDocPath: 'content-controls/checkbox/get-state.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.checkbox.setState': { + memberPath: 'contentControls.checkbox.setState', + description: 'Set the checked state of a checkbox content control.', + expectedResult: 'Returns a ContentControlMutationResult; reports NO_OP if unchanged.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'conditional', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_TYPED, + }), + referenceDocPath: 'content-controls/checkbox/set-state.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.checkbox.toggle': { + memberPath: 'contentControls.checkbox.toggle', + description: 'Toggle the checked state of a checkbox content control.', + expectedResult: 'Returns a ContentControlMutationResult with the updated state.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'conditional', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_TYPED, + }), + referenceDocPath: 'content-controls/checkbox/toggle.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.checkbox.setSymbolPair': { + memberPath: 'contentControls.checkbox.setSymbolPair', + description: 'Set the checked and unchecked symbol glyphs for a checkbox content control.', + expectedResult: 'Returns a ContentControlMutationResult; reports NO_OP if symbols unchanged.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'conditional', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_TYPED, + }), + referenceDocPath: 'content-controls/checkbox/set-symbol-pair.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.choiceList.getItems': { + memberPath: 'contentControls.choiceList.getItems', + description: 'Get the list items and selected value of a comboBox or dropDownList content control.', + expectedResult: 'Returns a ChoiceListGetItemsResult with items and selectedValue.', + requiresDocumentContext: true, + metadata: readOperation({ throws: T_CC_TYPED_READ }), + referenceDocPath: 'content-controls/choice-list/get-items.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.choiceList.setItems': { + memberPath: 'contentControls.choiceList.setItems', + description: 'Replace the list items of a comboBox or dropDownList content control.', + expectedResult: 'Returns a ContentControlMutationResult; reports NO_OP if items unchanged.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'conditional', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_TYPED, + }), + referenceDocPath: 'content-controls/choice-list/set-items.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.choiceList.setSelected': { + memberPath: 'contentControls.choiceList.setSelected', + description: 'Set the selected value of a comboBox or dropDownList content control.', + expectedResult: 'Returns a ContentControlMutationResult; reports NO_OP if selection unchanged.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'conditional', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_TYPED, + }), + referenceDocPath: 'content-controls/choice-list/set-selected.mdx', + referenceGroup: 'contentControls', + }, + + // --- D. Repeating Section + Group --- + + 'contentControls.repeatingSection.listItems': { + memberPath: 'contentControls.repeatingSection.listItems', + description: 'List the repeating section items inside a repeating section content control.', + expectedResult: 'Returns a RepeatingSectionListItemsResult with child item info.', + requiresDocumentContext: true, + metadata: readOperation({ throws: T_CC_TYPED_READ }), + referenceDocPath: 'content-controls/repeating-section/list-items.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.repeatingSection.insertItemBefore': { + memberPath: 'contentControls.repeatingSection.insertItemBefore', + description: 'Insert a new item before a specific index in a repeating section.', + expectedResult: 'Returns a ContentControlMutationResult with the new item target.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'non-idempotent', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_TYPED, + }), + referenceDocPath: 'content-controls/repeating-section/insert-item-before.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.repeatingSection.insertItemAfter': { + memberPath: 'contentControls.repeatingSection.insertItemAfter', + description: 'Insert a new item after a specific index in a repeating section.', + expectedResult: 'Returns a ContentControlMutationResult with the new item target.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'non-idempotent', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_TYPED, + }), + referenceDocPath: 'content-controls/repeating-section/insert-item-after.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.repeatingSection.cloneItem': { + memberPath: 'contentControls.repeatingSection.cloneItem', + description: 'Clone a repeating section item at the given index. Cloned SDTs receive new IDs.', + expectedResult: 'Returns a ContentControlMutationResult with the cloned item target.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'non-idempotent', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_TYPED, + }), + referenceDocPath: 'content-controls/repeating-section/clone-item.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.repeatingSection.deleteItem': { + memberPath: 'contentControls.repeatingSection.deleteItem', + description: 'Delete a repeating section item at the given index.', + expectedResult: 'Returns a ContentControlMutationResult; reports NO_OP if item does not exist.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'conditional', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_TYPED, + }), + referenceDocPath: 'content-controls/repeating-section/delete-item.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.repeatingSection.setAllowInsertDelete': { + memberPath: 'contentControls.repeatingSection.setAllowInsertDelete', + description: 'Set the allowInsertDelete flag on a repeating section.', + expectedResult: 'Returns a ContentControlMutationResult; reports NO_OP if unchanged.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'conditional', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_TYPED, + }), + referenceDocPath: 'content-controls/repeating-section/set-allow-insert-delete.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.group.wrap': { + memberPath: 'contentControls.group.wrap', + description: 'Wrap a content control inside a new group content control. Always nests; not idempotent.', + expectedResult: 'Returns a ContentControlMutationResult with the new group wrapper target.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'non-idempotent', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: [], + throws: T_CC_MUTATION, + }), + referenceDocPath: 'content-controls/group/wrap.mdx', + referenceGroup: 'contentControls', + }, + + 'contentControls.group.ungroup': { + memberPath: 'contentControls.group.ungroup', + description: 'Remove the group designation from a group content control.', + expectedResult: 'Returns a ContentControlMutationResult; reports NO_OP if not a group.', + requiresDocumentContext: true, + metadata: mutationOperation({ + idempotency: 'conditional', + supportsDryRun: true, + supportsTrackedMode: false, + possibleFailureCodes: ['NO_OP'], + throws: T_CC_TYPED, + }), + referenceDocPath: 'content-controls/group/ungroup.mdx', + referenceGroup: 'contentControls', + }, } as const satisfies Record; // --------------------------------------------------------------------------- diff --git a/packages/document-api/src/contract/operation-registry.ts b/packages/document-api/src/contract/operation-registry.ts index 2116e104ad..6b73cf7ca6 100644 --- a/packages/document-api/src/contract/operation-registry.ts +++ b/packages/document-api/src/contract/operation-registry.ts @@ -259,6 +259,73 @@ import type { HyperlinksRemoveInput, HyperlinkMutationResult, } from '../hyperlinks/hyperlinks.types.js'; +import type { + ContentControlInfo, + ContentControlMutationResult, + ContentControlsListResult, + ContentControlsListQuery, + ContentControlsGetInput, + ContentControlsListInRangeInput, + ContentControlsSelectByTagInput, + ContentControlsSelectByTitleInput, + ContentControlsListChildrenInput, + ContentControlsGetParentInput, + ContentControlsWrapInput, + ContentControlsUnwrapInput, + ContentControlsDeleteInput, + ContentControlsCopyInput, + ContentControlsMoveInput, + ContentControlsPatchInput, + ContentControlsSetLockModeInput, + ContentControlsSetTypeInput, + ContentControlsGetContentInput, + ContentControlsGetContentResult, + ContentControlsReplaceContentInput, + ContentControlsClearContentInput, + ContentControlsAppendContentInput, + ContentControlsPrependContentInput, + ContentControlsInsertBeforeInput, + ContentControlsInsertAfterInput, + ContentControlsGetBindingInput, + ContentControlBinding, + ContentControlsSetBindingInput, + ContentControlsClearBindingInput, + ContentControlsGetRawPropertiesInput, + ContentControlsGetRawPropertiesResult, + ContentControlsPatchRawPropertiesInput, + ContentControlsValidateWordCompatibilityInput, + ContentControlsValidateWordCompatibilityResult, + ContentControlsNormalizeWordCompatibilityInput, + ContentControlsNormalizeTagPayloadInput, + ContentControlsTextSetMultilineInput, + ContentControlsTextSetValueInput, + ContentControlsTextClearValueInput, + ContentControlsDateSetValueInput, + ContentControlsDateClearValueInput, + ContentControlsDateSetDisplayFormatInput, + ContentControlsDateSetDisplayLocaleInput, + ContentControlsDateSetStorageFormatInput, + ContentControlsDateSetCalendarInput, + ContentControlsCheckboxGetStateInput, + ContentControlsCheckboxGetStateResult, + ContentControlsCheckboxSetStateInput, + ContentControlsCheckboxToggleInput, + ContentControlsCheckboxSetSymbolPairInput, + ContentControlsChoiceListGetItemsInput, + ContentControlsChoiceListGetItemsResult, + ContentControlsChoiceListSetItemsInput, + ContentControlsChoiceListSetSelectedInput, + ContentControlsRepeatingSectionListItemsInput, + ContentControlsRepeatingSectionListItemsResult, + ContentControlsRepeatingSectionInsertItemBeforeInput, + ContentControlsRepeatingSectionInsertItemAfterInput, + ContentControlsRepeatingSectionCloneItemInput, + ContentControlsRepeatingSectionDeleteItemInput, + ContentControlsRepeatingSectionSetAllowInsertDeleteInput, + ContentControlsGroupWrapInput, + ContentControlsGroupUngroupInput, + CreateContentControlInput, +} from '../content-controls/content-controls.types.js'; type FormatInlineAliasOperationRegistry = { [K in InlineRunPatchKey as `format.${K}`]: { @@ -727,6 +794,295 @@ export interface OperationRegistry extends FormatInlineAliasOperationRegistry { 'hyperlinks.insert': { input: HyperlinksInsertInput; options: MutationOptions; output: HyperlinkMutationResult }; 'hyperlinks.patch': { input: HyperlinksPatchInput; options: MutationOptions; output: HyperlinkMutationResult }; 'hyperlinks.remove': { input: HyperlinksRemoveInput; options: MutationOptions; output: HyperlinkMutationResult }; + + // --- create.contentControl --- + 'create.contentControl': { + input: CreateContentControlInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + + // --- contentControls.* core CRUD + discovery --- + 'contentControls.list': { + input: ContentControlsListQuery | undefined; + options: never; + output: ContentControlsListResult; + }; + 'contentControls.get': { input: ContentControlsGetInput; options: never; output: ContentControlInfo }; + 'contentControls.listInRange': { + input: ContentControlsListInRangeInput; + options: never; + output: ContentControlsListResult; + }; + 'contentControls.selectByTag': { + input: ContentControlsSelectByTagInput; + options: never; + output: ContentControlsListResult; + }; + 'contentControls.selectByTitle': { + input: ContentControlsSelectByTitleInput; + options: never; + output: ContentControlsListResult; + }; + 'contentControls.listChildren': { + input: ContentControlsListChildrenInput; + options: never; + output: ContentControlsListResult; + }; + 'contentControls.getParent': { + input: ContentControlsGetParentInput; + options: never; + output: ContentControlInfo | null; + }; + 'contentControls.wrap': { + input: ContentControlsWrapInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + 'contentControls.unwrap': { + input: ContentControlsUnwrapInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + 'contentControls.delete': { + input: ContentControlsDeleteInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + 'contentControls.copy': { + input: ContentControlsCopyInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + 'contentControls.move': { + input: ContentControlsMoveInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + 'contentControls.patch': { + input: ContentControlsPatchInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + 'contentControls.setLockMode': { + input: ContentControlsSetLockModeInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + 'contentControls.setType': { + input: ContentControlsSetTypeInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + 'contentControls.getContent': { + input: ContentControlsGetContentInput; + options: never; + output: ContentControlsGetContentResult; + }; + 'contentControls.replaceContent': { + input: ContentControlsReplaceContentInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + 'contentControls.clearContent': { + input: ContentControlsClearContentInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + 'contentControls.appendContent': { + input: ContentControlsAppendContentInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + 'contentControls.prependContent': { + input: ContentControlsPrependContentInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + 'contentControls.insertBefore': { + input: ContentControlsInsertBeforeInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + 'contentControls.insertAfter': { + input: ContentControlsInsertAfterInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + + // --- contentControls.* data binding + raw --- + 'contentControls.getBinding': { + input: ContentControlsGetBindingInput; + options: never; + output: ContentControlBinding | null; + }; + 'contentControls.setBinding': { + input: ContentControlsSetBindingInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + 'contentControls.clearBinding': { + input: ContentControlsClearBindingInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + 'contentControls.getRawProperties': { + input: ContentControlsGetRawPropertiesInput; + options: never; + output: ContentControlsGetRawPropertiesResult; + }; + 'contentControls.patchRawProperties': { + input: ContentControlsPatchRawPropertiesInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + 'contentControls.validateWordCompatibility': { + input: ContentControlsValidateWordCompatibilityInput; + options: never; + output: ContentControlsValidateWordCompatibilityResult; + }; + 'contentControls.normalizeWordCompatibility': { + input: ContentControlsNormalizeWordCompatibilityInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + 'contentControls.normalizeTagPayload': { + input: ContentControlsNormalizeTagPayloadInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + + // --- contentControls.text.* --- + 'contentControls.text.setMultiline': { + input: ContentControlsTextSetMultilineInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + 'contentControls.text.setValue': { + input: ContentControlsTextSetValueInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + 'contentControls.text.clearValue': { + input: ContentControlsTextClearValueInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + + // --- contentControls.date.* --- + 'contentControls.date.setValue': { + input: ContentControlsDateSetValueInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + 'contentControls.date.clearValue': { + input: ContentControlsDateClearValueInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + 'contentControls.date.setDisplayFormat': { + input: ContentControlsDateSetDisplayFormatInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + 'contentControls.date.setDisplayLocale': { + input: ContentControlsDateSetDisplayLocaleInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + 'contentControls.date.setStorageFormat': { + input: ContentControlsDateSetStorageFormatInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + 'contentControls.date.setCalendar': { + input: ContentControlsDateSetCalendarInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + + // --- contentControls.checkbox.* --- + 'contentControls.checkbox.getState': { + input: ContentControlsCheckboxGetStateInput; + options: never; + output: ContentControlsCheckboxGetStateResult; + }; + 'contentControls.checkbox.setState': { + input: ContentControlsCheckboxSetStateInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + 'contentControls.checkbox.toggle': { + input: ContentControlsCheckboxToggleInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + 'contentControls.checkbox.setSymbolPair': { + input: ContentControlsCheckboxSetSymbolPairInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + + // --- contentControls.choiceList.* --- + 'contentControls.choiceList.getItems': { + input: ContentControlsChoiceListGetItemsInput; + options: never; + output: ContentControlsChoiceListGetItemsResult; + }; + 'contentControls.choiceList.setItems': { + input: ContentControlsChoiceListSetItemsInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + 'contentControls.choiceList.setSelected': { + input: ContentControlsChoiceListSetSelectedInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + + // --- contentControls.repeatingSection.* --- + 'contentControls.repeatingSection.listItems': { + input: ContentControlsRepeatingSectionListItemsInput; + options: never; + output: ContentControlsRepeatingSectionListItemsResult; + }; + 'contentControls.repeatingSection.insertItemBefore': { + input: ContentControlsRepeatingSectionInsertItemBeforeInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + 'contentControls.repeatingSection.insertItemAfter': { + input: ContentControlsRepeatingSectionInsertItemAfterInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + 'contentControls.repeatingSection.cloneItem': { + input: ContentControlsRepeatingSectionCloneItemInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + 'contentControls.repeatingSection.deleteItem': { + input: ContentControlsRepeatingSectionDeleteItemInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + 'contentControls.repeatingSection.setAllowInsertDelete': { + input: ContentControlsRepeatingSectionSetAllowInsertDeleteInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + + // --- contentControls.group.* --- + 'contentControls.group.wrap': { + input: ContentControlsGroupWrapInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; + 'contentControls.group.ungroup': { + input: ContentControlsGroupUngroupInput; + options: MutationOptions; + output: ContentControlMutationResult; + }; } // --- Bidirectional completeness checks --- diff --git a/packages/document-api/src/contract/reference-doc-map.ts b/packages/document-api/src/contract/reference-doc-map.ts index 8a2957b0c6..8e88f748aa 100644 --- a/packages/document-api/src/contract/reference-doc-map.ts +++ b/packages/document-api/src/contract/reference-doc-map.ts @@ -116,6 +116,11 @@ const GROUP_METADATA: Record = { }, ['success'], ), + ReceiptFailure: objectSchema( + { + code: { type: 'string' }, + message: { type: 'string' }, + details: {}, + }, + ['code', 'message'], + ), TextMutationRange: objectSchema( { from: { type: 'integer' }, @@ -642,7 +650,7 @@ function listsMutateItemResultSchemaFor(operationId: OperationId): JsonSchema { }; } -function listsExitResultSchemaFor(operationId: OperationId): JsonSchema { +function _listsExitResultSchemaFor(operationId: OperationId): JsonSchema { return { oneOf: [listsExitSuccessSchema, listsFailureSchemaFor(operationId)], }; @@ -1728,7 +1736,408 @@ const hyperlinkInfoSchema: JsonSchema = objectSchema( ['address', 'properties'], ); -const operationSchemas: Record = { +// --------------------------------------------------------------------------- +// Content Controls shared schemas +// --------------------------------------------------------------------------- + +const contentControlTargetSchema = objectSchema( + { + kind: { enum: ['block', 'inline'] }, + nodeType: { const: 'sdt' }, + nodeId: { type: 'string' }, + }, + ['kind', 'nodeType', 'nodeId'], +); + +const contentControlMutationSuccessSchema = objectSchema( + { + success: { const: true }, + contentControl: contentControlTargetSchema, + updatedRef: contentControlTargetSchema, + }, + ['success', 'contentControl'], +); + +const contentControlMutationFailureSchema = objectSchema( + { + success: { const: false }, + failure: { $ref: '#/$defs/ReceiptFailure' }, + }, + ['success', 'failure'], +); + +function ccMutationResultSchema(): JsonSchema { + return { oneOf: [contentControlMutationSuccessSchema, contentControlMutationFailureSchema] }; +} + +const ccListResultSchema = objectSchema( + { items: { type: 'array', items: { type: 'object' } }, total: { type: 'integer' } }, + ['items', 'total'], +); + +const ccInfoSchema: JsonSchema = { type: 'object', description: 'ContentControlInfo' }; + +function ccTargetInput(): JsonSchema { + return objectSchema({ target: contentControlTargetSchema }, ['target']); +} + +/** Generates all contentControls.* schemas in one helper to keep the main map DRY. */ +function buildContentControlSchemas(): Record { + const targetOnlyMutation: OperationSchemaSet = { + input: ccTargetInput(), + output: ccMutationResultSchema(), + success: contentControlMutationSuccessSchema, + failure: contentControlMutationFailureSchema, + }; + + const targetOnlyRead: OperationSchemaSet = { + input: ccTargetInput(), + output: ccInfoSchema, + }; + + const ccContentMutation: OperationSchemaSet = { + input: objectSchema( + { target: contentControlTargetSchema, content: { type: 'string' }, format: { enum: ['text', 'html'] } }, + ['target', 'content'], + ), + output: ccMutationResultSchema(), + success: contentControlMutationSuccessSchema, + failure: contentControlMutationFailureSchema, + }; + + return { + 'create.contentControl': { + input: objectSchema( + { + kind: { enum: ['block', 'inline'] }, + controlType: { type: 'string' }, + target: contentControlTargetSchema, + tag: { type: 'string' }, + alias: { type: 'string' }, + lockMode: { enum: ['unlocked', 'sdtLocked', 'contentLocked', 'sdtContentLocked'] }, + content: { type: 'string' }, + }, + ['kind'], + ), + output: ccMutationResultSchema(), + success: contentControlMutationSuccessSchema, + failure: contentControlMutationFailureSchema, + }, + 'contentControls.list': { + input: objectSchema({ + controlType: { type: 'string' }, + tag: { type: 'string' }, + offset: { type: 'integer' }, + limit: { type: 'integer' }, + }), + output: ccListResultSchema, + }, + 'contentControls.get': targetOnlyRead, + 'contentControls.listInRange': { + input: objectSchema( + { + startBlockId: { type: 'string' }, + endBlockId: { type: 'string' }, + offset: { type: 'integer' }, + limit: { type: 'integer' }, + }, + ['startBlockId', 'endBlockId'], + ), + output: ccListResultSchema, + }, + 'contentControls.selectByTag': { + input: objectSchema({ tag: { type: 'string' }, offset: { type: 'integer' }, limit: { type: 'integer' } }, [ + 'tag', + ]), + output: ccListResultSchema, + }, + 'contentControls.selectByTitle': { + input: objectSchema({ title: { type: 'string' }, offset: { type: 'integer' }, limit: { type: 'integer' } }, [ + 'title', + ]), + output: ccListResultSchema, + }, + 'contentControls.listChildren': { + input: objectSchema( + { target: contentControlTargetSchema, offset: { type: 'integer' }, limit: { type: 'integer' } }, + ['target'], + ), + output: ccListResultSchema, + }, + 'contentControls.getParent': { input: ccTargetInput(), output: { oneOf: [ccInfoSchema, { type: 'null' }] } }, + 'contentControls.wrap': { + input: objectSchema( + { + kind: { enum: ['block', 'inline'] }, + target: contentControlTargetSchema, + tag: { type: 'string' }, + alias: { type: 'string' }, + lockMode: { enum: ['unlocked', 'sdtLocked', 'contentLocked', 'sdtContentLocked'] }, + }, + ['kind', 'target'], + ), + output: ccMutationResultSchema(), + success: contentControlMutationSuccessSchema, + failure: contentControlMutationFailureSchema, + }, + 'contentControls.unwrap': targetOnlyMutation, + 'contentControls.delete': targetOnlyMutation, + 'contentControls.copy': { + input: objectSchema({ target: contentControlTargetSchema, destination: contentControlTargetSchema }, [ + 'target', + 'destination', + ]), + output: ccMutationResultSchema(), + success: contentControlMutationSuccessSchema, + failure: contentControlMutationFailureSchema, + }, + 'contentControls.move': { + input: objectSchema({ target: contentControlTargetSchema, destination: contentControlTargetSchema }, [ + 'target', + 'destination', + ]), + output: ccMutationResultSchema(), + success: contentControlMutationSuccessSchema, + failure: contentControlMutationFailureSchema, + }, + 'contentControls.patch': { + input: objectSchema( + { + target: contentControlTargetSchema, + alias: {}, + tag: {}, + appearance: { enum: ['boundingBox', 'tags', 'hidden'] }, + color: { type: 'string' }, + placeholder: { type: 'string' }, + showingPlaceholder: { type: 'boolean' }, + temporary: { type: 'boolean' }, + tabIndex: { type: 'integer' }, + }, + ['target'], + ), + output: ccMutationResultSchema(), + success: contentControlMutationSuccessSchema, + failure: contentControlMutationFailureSchema, + }, + 'contentControls.setLockMode': { + input: objectSchema( + { + target: contentControlTargetSchema, + lockMode: { enum: ['unlocked', 'sdtLocked', 'contentLocked', 'sdtContentLocked'] }, + }, + ['target', 'lockMode'], + ), + output: ccMutationResultSchema(), + success: contentControlMutationSuccessSchema, + failure: contentControlMutationFailureSchema, + }, + 'contentControls.setType': { + input: objectSchema({ target: contentControlTargetSchema, controlType: { type: 'string' } }, [ + 'target', + 'controlType', + ]), + output: ccMutationResultSchema(), + success: contentControlMutationSuccessSchema, + failure: contentControlMutationFailureSchema, + }, + 'contentControls.getContent': { + input: ccTargetInput(), + output: objectSchema({ content: { type: 'string' }, format: { enum: ['text', 'html'] } }, ['content', 'format']), + }, + 'contentControls.replaceContent': ccContentMutation, + 'contentControls.clearContent': targetOnlyMutation, + 'contentControls.appendContent': ccContentMutation, + 'contentControls.prependContent': ccContentMutation, + 'contentControls.insertBefore': ccContentMutation, + 'contentControls.insertAfter': ccContentMutation, + + // Binding + 'contentControls.getBinding': { + input: ccTargetInput(), + output: { + oneOf: [ + objectSchema( + { storeItemId: { type: 'string' }, xpath: { type: 'string' }, prefixMappings: { type: 'string' } }, + ['storeItemId', 'xpath'], + ), + { type: 'null' }, + ], + }, + }, + 'contentControls.setBinding': { + input: objectSchema( + { + target: contentControlTargetSchema, + storeItemId: { type: 'string' }, + xpath: { type: 'string' }, + prefixMappings: { type: 'string' }, + }, + ['target', 'storeItemId', 'xpath'], + ), + output: ccMutationResultSchema(), + success: contentControlMutationSuccessSchema, + failure: contentControlMutationFailureSchema, + }, + 'contentControls.clearBinding': targetOnlyMutation, + 'contentControls.getRawProperties': { + input: ccTargetInput(), + output: objectSchema({ properties: { type: 'object' } }, ['properties']), + }, + 'contentControls.patchRawProperties': { + input: objectSchema( + { target: contentControlTargetSchema, patches: { type: 'array', items: { type: 'object' } } }, + ['target', 'patches'], + ), + output: ccMutationResultSchema(), + success: contentControlMutationSuccessSchema, + failure: contentControlMutationFailureSchema, + }, + 'contentControls.validateWordCompatibility': { + input: ccTargetInput(), + output: objectSchema( + { compatible: { type: 'boolean' }, diagnostics: { type: 'array', items: { type: 'object' } } }, + ['compatible', 'diagnostics'], + ), + }, + 'contentControls.normalizeWordCompatibility': targetOnlyMutation, + 'contentControls.normalizeTagPayload': targetOnlyMutation, + + // Text + 'contentControls.text.setMultiline': { + input: objectSchema({ target: contentControlTargetSchema, multiline: { type: 'boolean' } }, [ + 'target', + 'multiline', + ]), + output: ccMutationResultSchema(), + success: contentControlMutationSuccessSchema, + failure: contentControlMutationFailureSchema, + }, + 'contentControls.text.setValue': { + input: objectSchema({ target: contentControlTargetSchema, value: { type: 'string' } }, ['target', 'value']), + output: ccMutationResultSchema(), + success: contentControlMutationSuccessSchema, + failure: contentControlMutationFailureSchema, + }, + 'contentControls.text.clearValue': targetOnlyMutation, + + // Date + 'contentControls.date.setValue': { + input: objectSchema({ target: contentControlTargetSchema, value: { type: 'string' } }, ['target', 'value']), + output: ccMutationResultSchema(), + success: contentControlMutationSuccessSchema, + failure: contentControlMutationFailureSchema, + }, + 'contentControls.date.clearValue': targetOnlyMutation, + 'contentControls.date.setDisplayFormat': { + input: objectSchema({ target: contentControlTargetSchema, format: { type: 'string' } }, ['target', 'format']), + output: ccMutationResultSchema(), + success: contentControlMutationSuccessSchema, + failure: contentControlMutationFailureSchema, + }, + 'contentControls.date.setDisplayLocale': { + input: objectSchema({ target: contentControlTargetSchema, locale: { type: 'string' } }, ['target', 'locale']), + output: ccMutationResultSchema(), + success: contentControlMutationSuccessSchema, + failure: contentControlMutationFailureSchema, + }, + 'contentControls.date.setStorageFormat': { + input: objectSchema({ target: contentControlTargetSchema, format: { type: 'string' } }, ['target', 'format']), + output: ccMutationResultSchema(), + success: contentControlMutationSuccessSchema, + failure: contentControlMutationFailureSchema, + }, + 'contentControls.date.setCalendar': { + input: objectSchema({ target: contentControlTargetSchema, calendar: { type: 'string' } }, ['target', 'calendar']), + output: ccMutationResultSchema(), + success: contentControlMutationSuccessSchema, + failure: contentControlMutationFailureSchema, + }, + + // Checkbox + 'contentControls.checkbox.getState': { + input: ccTargetInput(), + output: objectSchema({ checked: { type: 'boolean' } }, ['checked']), + }, + 'contentControls.checkbox.setState': { + input: objectSchema({ target: contentControlTargetSchema, checked: { type: 'boolean' } }, ['target', 'checked']), + output: ccMutationResultSchema(), + success: contentControlMutationSuccessSchema, + failure: contentControlMutationFailureSchema, + }, + 'contentControls.checkbox.toggle': targetOnlyMutation, + 'contentControls.checkbox.setSymbolPair': { + input: objectSchema( + { target: contentControlTargetSchema, checkedSymbol: { type: 'object' }, uncheckedSymbol: { type: 'object' } }, + ['target', 'checkedSymbol', 'uncheckedSymbol'], + ), + output: ccMutationResultSchema(), + success: contentControlMutationSuccessSchema, + failure: contentControlMutationFailureSchema, + }, + + // Choice list + 'contentControls.choiceList.getItems': { + input: ccTargetInput(), + output: objectSchema({ items: { type: 'array', items: { type: 'object' } }, selectedValue: { type: 'string' } }, [ + 'items', + ]), + }, + 'contentControls.choiceList.setItems': { + input: objectSchema({ target: contentControlTargetSchema, items: { type: 'array', items: { type: 'object' } } }, [ + 'target', + 'items', + ]), + output: ccMutationResultSchema(), + success: contentControlMutationSuccessSchema, + failure: contentControlMutationFailureSchema, + }, + 'contentControls.choiceList.setSelected': { + input: objectSchema({ target: contentControlTargetSchema, value: { type: 'string' } }, ['target', 'value']), + output: ccMutationResultSchema(), + success: contentControlMutationSuccessSchema, + failure: contentControlMutationFailureSchema, + }, + + // Repeating section + 'contentControls.repeatingSection.listItems': { input: ccTargetInput(), output: ccListResultSchema }, + 'contentControls.repeatingSection.insertItemBefore': { + input: objectSchema({ target: contentControlTargetSchema, index: { type: 'integer' } }, ['target', 'index']), + output: ccMutationResultSchema(), + success: contentControlMutationSuccessSchema, + failure: contentControlMutationFailureSchema, + }, + 'contentControls.repeatingSection.insertItemAfter': { + input: objectSchema({ target: contentControlTargetSchema, index: { type: 'integer' } }, ['target', 'index']), + output: ccMutationResultSchema(), + success: contentControlMutationSuccessSchema, + failure: contentControlMutationFailureSchema, + }, + 'contentControls.repeatingSection.cloneItem': { + input: objectSchema({ target: contentControlTargetSchema, index: { type: 'integer' } }, ['target', 'index']), + output: ccMutationResultSchema(), + success: contentControlMutationSuccessSchema, + failure: contentControlMutationFailureSchema, + }, + 'contentControls.repeatingSection.deleteItem': { + input: objectSchema({ target: contentControlTargetSchema, index: { type: 'integer' } }, ['target', 'index']), + output: ccMutationResultSchema(), + success: contentControlMutationSuccessSchema, + failure: contentControlMutationFailureSchema, + }, + 'contentControls.repeatingSection.setAllowInsertDelete': { + input: objectSchema({ target: contentControlTargetSchema, allow: { type: 'boolean' } }, ['target', 'allow']), + output: ccMutationResultSchema(), + success: contentControlMutationSuccessSchema, + failure: contentControlMutationFailureSchema, + }, + + // Group + 'contentControls.group.wrap': targetOnlyMutation, + 'contentControls.group.ungroup': targetOnlyMutation, + }; +} + +const operationSchemas = { get: { input: objectSchema({ options: objectSchema({ @@ -4651,6 +5060,11 @@ const operationSchemas: Record = { success: hyperlinkMutationSuccessSchema, failure: hyperlinkMutationFailureSchema, }, + + // ========================================================================= + // Content Controls (SD-2070) โ€” schemas + // ========================================================================= + ...buildContentControlSchemas(), }; /** @@ -4663,7 +5077,8 @@ const operationSchemas: Record = { * @throws {Error} If any operation is missing a schema or an unknown operation is found. */ export function buildInternalContractSchemas(): InternalContractSchemas { - const operations = { ...operationSchemas }; + // Cast is safe โ€” the runtime loops below verify completeness against OPERATION_IDS. + const operations = { ...operationSchemas } as unknown as Record; for (const operationId of OPERATION_IDS) { if (!operations[operationId]) { diff --git a/packages/document-api/src/create/create.ts b/packages/document-api/src/create/create.ts index 8b938ced83..fcb686f3af 100644 --- a/packages/document-api/src/create/create.ts +++ b/packages/document-api/src/create/create.ts @@ -17,6 +17,10 @@ import type { } from '../sections/sections.types.js'; import type { CreateTableOfContentsInput, CreateTableOfContentsResult, TocCreateLocation } from '../toc/toc.types.js'; import type { CreateImageInput, CreateImageResult } from '../images/images.types.js'; +import type { + CreateContentControlInput, + ContentControlMutationResult, +} from '../content-controls/content-controls.types.js'; import { DocumentApiValidationError } from '../errors.js'; export interface CreateApi { @@ -26,6 +30,7 @@ export interface CreateApi { sectionBreak(input: CreateSectionBreakInput, options?: MutationOptions): CreateSectionBreakResult; tableOfContents(input: CreateTableOfContentsInput, options?: MutationOptions): CreateTableOfContentsResult; image(input: CreateImageInput, options?: MutationOptions): CreateImageResult; + contentControl(input: CreateContentControlInput, options?: MutationOptions): ContentControlMutationResult; } export type CreateAdapter = CreateApi; diff --git a/packages/document-api/src/index.ts b/packages/document-api/src/index.ts index 7001951549..2cdf962b6c 100644 --- a/packages/document-api/src/index.ts +++ b/packages/document-api/src/index.ts @@ -457,6 +457,72 @@ import { executeHyperlinksPatch, executeHyperlinksRemove, } from './hyperlinks/hyperlinks.js'; +import type { + ContentControlsApi, + ContentControlsAdapter, + ContentControlsCreateAdapter, +} from './content-controls/content-controls.js'; +import type { + CreateContentControlInput, + ContentControlMutationResult, +} from './content-controls/content-controls.types.js'; +import { + executeContentControlsList, + executeContentControlsGet, + executeContentControlsListInRange, + executeContentControlsSelectByTag, + executeContentControlsSelectByTitle, + executeContentControlsListChildren, + executeContentControlsGetParent, + executeContentControlsWrap, + executeContentControlsUnwrap, + executeContentControlsDelete, + executeContentControlsCopy, + executeContentControlsMove, + executeContentControlsPatch, + executeContentControlsSetLockMode, + executeContentControlsSetType, + executeContentControlsGetContent, + executeContentControlsReplaceContent, + executeContentControlsClearContent, + executeContentControlsAppendContent, + executeContentControlsPrependContent, + executeContentControlsInsertBefore, + executeContentControlsInsertAfter, + executeContentControlsGetBinding, + executeContentControlsSetBinding, + executeContentControlsClearBinding, + executeContentControlsGetRawProperties, + executeContentControlsPatchRawProperties, + executeContentControlsValidateWordCompatibility, + executeContentControlsNormalizeWordCompatibility, + executeContentControlsNormalizeTagPayload, + executeContentControlsTextSetMultiline, + executeContentControlsTextSetValue, + executeContentControlsTextClearValue, + executeContentControlsDateSetValue, + executeContentControlsDateClearValue, + executeContentControlsDateSetDisplayFormat, + executeContentControlsDateSetDisplayLocale, + executeContentControlsDateSetStorageFormat, + executeContentControlsDateSetCalendar, + executeContentControlsCheckboxGetState, + executeContentControlsCheckboxSetState, + executeContentControlsCheckboxToggle, + executeContentControlsCheckboxSetSymbolPair, + executeContentControlsChoiceListGetItems, + executeContentControlsChoiceListSetItems, + executeContentControlsChoiceListSetSelected, + executeContentControlsRepeatingSectionListItems, + executeContentControlsRepeatingSectionInsertItemBefore, + executeContentControlsRepeatingSectionInsertItemAfter, + executeContentControlsRepeatingSectionCloneItem, + executeContentControlsRepeatingSectionDeleteItem, + executeContentControlsRepeatingSectionSetAllowInsertDelete, + executeContentControlsGroupWrap, + executeContentControlsGroupUngroup, + executeCreateContentControl, +} from './content-controls/content-controls.js'; import type { HyperlinksListQuery, HyperlinksListResult, @@ -641,6 +707,99 @@ export type { TocEntryProperties, } from './toc/toc.types.js'; export type { HyperlinksApi, HyperlinksAdapter } from './hyperlinks/hyperlinks.js'; +export type { + ContentControlsApi, + ContentControlsAdapter, + ContentControlsCreateAdapter, +} from './content-controls/content-controls.js'; +export type { + ContentControlMutationResult, + ContentControlMutationSuccess, + ContentControlMutationFailure, + ContentControlsListResult, + ContentControlsListQuery, + ContentControlTarget, + ContentControlType, + ContentControlProperties, + ContentControlBinding, + ContentControlSymbol, + ContentControlListItem, + ContentControlAppearance, + LockMode, + CreateContentControlInput, + ContentControlsGetInput, + ContentControlsWrapInput, + ContentControlsUnwrapInput, + ContentControlsDeleteInput, + ContentControlsCopyInput, + ContentControlsMoveInput, + ContentControlsPatchInput, + ContentControlsSetLockModeInput, + ContentControlsSetTypeInput, + ContentControlsGetContentInput, + ContentControlsGetContentResult, + ContentControlsReplaceContentInput, + ContentControlsClearContentInput, + ContentControlsAppendContentInput, + ContentControlsPrependContentInput, + ContentControlsInsertBeforeInput, + ContentControlsInsertAfterInput, + ContentControlsGetBindingInput, + ContentControlsSetBindingInput, + ContentControlsClearBindingInput, + ContentControlsGetRawPropertiesInput, + ContentControlsGetRawPropertiesResult, + ContentControlsPatchRawPropertiesInput, + RawPatchOp, + ContentControlsValidateWordCompatibilityInput, + ContentControlsValidateWordCompatibilityResult, + WordCompatibilityDiagnostic, + ContentControlsNormalizeWordCompatibilityInput, + ContentControlsNormalizeTagPayloadInput, + ContentControlsListInRangeInput, + ContentControlsSelectByTagInput, + ContentControlsSelectByTitleInput, + ContentControlsListChildrenInput, + ContentControlsGetParentInput, + ContentControlsTextSetMultilineInput, + ContentControlsTextSetValueInput, + ContentControlsTextClearValueInput, + ContentControlsDateSetValueInput, + ContentControlsDateClearValueInput, + ContentControlsDateSetDisplayFormatInput, + ContentControlsDateSetDisplayLocaleInput, + ContentControlsDateSetStorageFormatInput, + ContentControlsDateSetCalendarInput, + ContentControlsCheckboxGetStateInput, + ContentControlsCheckboxGetStateResult, + ContentControlsCheckboxSetStateInput, + ContentControlsCheckboxToggleInput, + ContentControlsCheckboxSetSymbolPairInput, + ContentControlsChoiceListGetItemsInput, + ContentControlsChoiceListGetItemsResult, + ContentControlsChoiceListSetItemsInput, + ContentControlsChoiceListSetSelectedInput, + ContentControlsRepeatingSectionListItemsInput, + ContentControlsRepeatingSectionListItemsResult, + ContentControlsRepeatingSectionInsertItemBeforeInput, + ContentControlsRepeatingSectionInsertItemAfterInput, + ContentControlsRepeatingSectionCloneItemInput, + ContentControlsRepeatingSectionDeleteItemInput, + ContentControlsRepeatingSectionSetAllowInsertDeleteInput, + ContentControlsGroupWrapInput, + ContentControlsGroupUngroupInput, + ContentControlsPaginationOptions, + TextControlProperties, + DateControlProperties, + CheckboxControlProperties, + ChoiceControlProperties, + RepeatingSectionControlProperties, +} from './content-controls/content-controls.types.js'; +export { + CONTENT_CONTROL_TYPES, + LOCK_MODES, + CONTENT_CONTROL_APPEARANCES, +} from './content-controls/content-controls.types.js'; export type { HyperlinkTarget, HyperlinkDestination, @@ -1034,6 +1193,10 @@ export interface DocumentApi { * Hyperlink discovery, creation, and metadata management. */ hyperlinks: HyperlinksApi; + /** + * Content control (SDT) discovery, mutation, and typed-control operations. + */ + contentControls: ContentControlsApi; /** * Selector-based query with cardinality contracts for mutation targeting. */ @@ -1093,6 +1256,7 @@ export interface DocumentApiAdapters { toc: TocAdapter; images: ImagesAdapter & CreateImageAdapter; hyperlinks: HyperlinksAdapter; + contentControls: ContentControlsAdapter & ContentControlsCreateAdapter; query: QueryAdapter; mutations: MutationsAdapter; history: HistoryAdapter; @@ -1306,6 +1470,9 @@ export function createDocumentApi(adapters: DocumentApiAdapters): DocumentApi { image(input: CreateImageInput, options?: MutationOptions): CreateImageResult { return executeCreateImage(adapters.images, input, options); }, + contentControl(input: CreateContentControlInput, options?: MutationOptions): ContentControlMutationResult { + return executeCreateContentControl(adapters.contentControls, input, options); + }, }, capabilities, images: { @@ -1887,6 +2054,93 @@ export function createDocumentApi(adapters: DocumentApiAdapters): DocumentApi { return executeHyperlinksRemove(adapters.hyperlinks, input, options); }, }, + contentControls: { + list: (query) => executeContentControlsList(adapters.contentControls, query), + get: (input) => executeContentControlsGet(adapters.contentControls, input), + listInRange: (input) => executeContentControlsListInRange(adapters.contentControls, input), + selectByTag: (input) => executeContentControlsSelectByTag(adapters.contentControls, input), + selectByTitle: (input) => executeContentControlsSelectByTitle(adapters.contentControls, input), + listChildren: (input) => executeContentControlsListChildren(adapters.contentControls, input), + getParent: (input) => executeContentControlsGetParent(adapters.contentControls, input), + wrap: (input, options) => executeContentControlsWrap(adapters.contentControls, input, options), + unwrap: (input, options) => executeContentControlsUnwrap(adapters.contentControls, input, options), + delete: (input, options) => executeContentControlsDelete(adapters.contentControls, input, options), + copy: (input, options) => executeContentControlsCopy(adapters.contentControls, input, options), + move: (input, options) => executeContentControlsMove(adapters.contentControls, input, options), + patch: (input, options) => executeContentControlsPatch(adapters.contentControls, input, options), + setLockMode: (input, options) => executeContentControlsSetLockMode(adapters.contentControls, input, options), + setType: (input, options) => executeContentControlsSetType(adapters.contentControls, input, options), + getContent: (input) => executeContentControlsGetContent(adapters.contentControls, input), + replaceContent: (input, options) => + executeContentControlsReplaceContent(adapters.contentControls, input, options), + clearContent: (input, options) => executeContentControlsClearContent(adapters.contentControls, input, options), + appendContent: (input, options) => executeContentControlsAppendContent(adapters.contentControls, input, options), + prependContent: (input, options) => + executeContentControlsPrependContent(adapters.contentControls, input, options), + insertBefore: (input, options) => executeContentControlsInsertBefore(adapters.contentControls, input, options), + insertAfter: (input, options) => executeContentControlsInsertAfter(adapters.contentControls, input, options), + getBinding: (input) => executeContentControlsGetBinding(adapters.contentControls, input), + setBinding: (input, options) => executeContentControlsSetBinding(adapters.contentControls, input, options), + clearBinding: (input, options) => executeContentControlsClearBinding(adapters.contentControls, input, options), + getRawProperties: (input) => executeContentControlsGetRawProperties(adapters.contentControls, input), + patchRawProperties: (input, options) => + executeContentControlsPatchRawProperties(adapters.contentControls, input, options), + validateWordCompatibility: (input) => + executeContentControlsValidateWordCompatibility(adapters.contentControls, input), + normalizeWordCompatibility: (input, options) => + executeContentControlsNormalizeWordCompatibility(adapters.contentControls, input, options), + normalizeTagPayload: (input, options) => + executeContentControlsNormalizeTagPayload(adapters.contentControls, input, options), + text: { + setMultiline: (input, options) => + executeContentControlsTextSetMultiline(adapters.contentControls, input, options), + setValue: (input, options) => executeContentControlsTextSetValue(adapters.contentControls, input, options), + clearValue: (input, options) => executeContentControlsTextClearValue(adapters.contentControls, input, options), + }, + date: { + setValue: (input, options) => executeContentControlsDateSetValue(adapters.contentControls, input, options), + clearValue: (input, options) => executeContentControlsDateClearValue(adapters.contentControls, input, options), + setDisplayFormat: (input, options) => + executeContentControlsDateSetDisplayFormat(adapters.contentControls, input, options), + setDisplayLocale: (input, options) => + executeContentControlsDateSetDisplayLocale(adapters.contentControls, input, options), + setStorageFormat: (input, options) => + executeContentControlsDateSetStorageFormat(adapters.contentControls, input, options), + setCalendar: (input, options) => + executeContentControlsDateSetCalendar(adapters.contentControls, input, options), + }, + checkbox: { + getState: (input) => executeContentControlsCheckboxGetState(adapters.contentControls, input), + setState: (input, options) => executeContentControlsCheckboxSetState(adapters.contentControls, input, options), + toggle: (input, options) => executeContentControlsCheckboxToggle(adapters.contentControls, input, options), + setSymbolPair: (input, options) => + executeContentControlsCheckboxSetSymbolPair(adapters.contentControls, input, options), + }, + choiceList: { + getItems: (input) => executeContentControlsChoiceListGetItems(adapters.contentControls, input), + setItems: (input, options) => + executeContentControlsChoiceListSetItems(adapters.contentControls, input, options), + setSelected: (input, options) => + executeContentControlsChoiceListSetSelected(adapters.contentControls, input, options), + }, + repeatingSection: { + listItems: (input) => executeContentControlsRepeatingSectionListItems(adapters.contentControls, input), + insertItemBefore: (input, options) => + executeContentControlsRepeatingSectionInsertItemBefore(adapters.contentControls, input, options), + insertItemAfter: (input, options) => + executeContentControlsRepeatingSectionInsertItemAfter(adapters.contentControls, input, options), + cloneItem: (input, options) => + executeContentControlsRepeatingSectionCloneItem(adapters.contentControls, input, options), + deleteItem: (input, options) => + executeContentControlsRepeatingSectionDeleteItem(adapters.contentControls, input, options), + setAllowInsertDelete: (input, options) => + executeContentControlsRepeatingSectionSetAllowInsertDelete(adapters.contentControls, input, options), + }, + group: { + wrap: (input, options) => executeContentControlsGroupWrap(adapters.contentControls, input, options), + ungroup: (input, options) => executeContentControlsGroupUngroup(adapters.contentControls, input, options), + }, + }, query: { match(input: QueryMatchInput): QueryMatchOutput { return adapters.query.match(input); diff --git a/packages/document-api/src/invoke/invoke.ts b/packages/document-api/src/invoke/invoke.ts index 945b6e12d5..7010d49b04 100644 --- a/packages/document-api/src/invoke/invoke.ts +++ b/packages/document-api/src/invoke/invoke.ts @@ -301,5 +301,89 @@ export function buildDispatchTable(api: DocumentApi): TypedDispatchTable { 'hyperlinks.insert': (input, options) => api.hyperlinks.insert(input, options), 'hyperlinks.patch': (input, options) => api.hyperlinks.patch(input, options), 'hyperlinks.remove': (input, options) => api.hyperlinks.remove(input, options), + + // --- create.contentControl --- + 'create.contentControl': (input, options) => api.create.contentControl(input, options), + + // --- contentControls.* core CRUD + discovery --- + 'contentControls.list': (input) => api.contentControls.list(input), + 'contentControls.get': (input) => api.contentControls.get(input), + 'contentControls.listInRange': (input) => api.contentControls.listInRange(input), + 'contentControls.selectByTag': (input) => api.contentControls.selectByTag(input), + 'contentControls.selectByTitle': (input) => api.contentControls.selectByTitle(input), + 'contentControls.listChildren': (input) => api.contentControls.listChildren(input), + 'contentControls.getParent': (input) => api.contentControls.getParent(input), + 'contentControls.wrap': (input, options) => api.contentControls.wrap(input, options), + 'contentControls.unwrap': (input, options) => api.contentControls.unwrap(input, options), + 'contentControls.delete': (input, options) => api.contentControls.delete(input, options), + 'contentControls.copy': (input, options) => api.contentControls.copy(input, options), + 'contentControls.move': (input, options) => api.contentControls.move(input, options), + 'contentControls.patch': (input, options) => api.contentControls.patch(input, options), + 'contentControls.setLockMode': (input, options) => api.contentControls.setLockMode(input, options), + 'contentControls.setType': (input, options) => api.contentControls.setType(input, options), + 'contentControls.getContent': (input) => api.contentControls.getContent(input), + 'contentControls.replaceContent': (input, options) => api.contentControls.replaceContent(input, options), + 'contentControls.clearContent': (input, options) => api.contentControls.clearContent(input, options), + 'contentControls.appendContent': (input, options) => api.contentControls.appendContent(input, options), + 'contentControls.prependContent': (input, options) => api.contentControls.prependContent(input, options), + 'contentControls.insertBefore': (input, options) => api.contentControls.insertBefore(input, options), + 'contentControls.insertAfter': (input, options) => api.contentControls.insertAfter(input, options), + + // --- contentControls.* data binding + raw --- + 'contentControls.getBinding': (input) => api.contentControls.getBinding(input), + 'contentControls.setBinding': (input, options) => api.contentControls.setBinding(input, options), + 'contentControls.clearBinding': (input, options) => api.contentControls.clearBinding(input, options), + 'contentControls.getRawProperties': (input) => api.contentControls.getRawProperties(input), + 'contentControls.patchRawProperties': (input, options) => api.contentControls.patchRawProperties(input, options), + 'contentControls.validateWordCompatibility': (input) => api.contentControls.validateWordCompatibility(input), + 'contentControls.normalizeWordCompatibility': (input, options) => + api.contentControls.normalizeWordCompatibility(input, options), + 'contentControls.normalizeTagPayload': (input, options) => api.contentControls.normalizeTagPayload(input, options), + + // --- contentControls.text.* --- + 'contentControls.text.setMultiline': (input, options) => api.contentControls.text.setMultiline(input, options), + 'contentControls.text.setValue': (input, options) => api.contentControls.text.setValue(input, options), + 'contentControls.text.clearValue': (input, options) => api.contentControls.text.clearValue(input, options), + + // --- contentControls.date.* --- + 'contentControls.date.setValue': (input, options) => api.contentControls.date.setValue(input, options), + 'contentControls.date.clearValue': (input, options) => api.contentControls.date.clearValue(input, options), + 'contentControls.date.setDisplayFormat': (input, options) => + api.contentControls.date.setDisplayFormat(input, options), + 'contentControls.date.setDisplayLocale': (input, options) => + api.contentControls.date.setDisplayLocale(input, options), + 'contentControls.date.setStorageFormat': (input, options) => + api.contentControls.date.setStorageFormat(input, options), + 'contentControls.date.setCalendar': (input, options) => api.contentControls.date.setCalendar(input, options), + + // --- contentControls.checkbox.* --- + 'contentControls.checkbox.getState': (input) => api.contentControls.checkbox.getState(input), + 'contentControls.checkbox.setState': (input, options) => api.contentControls.checkbox.setState(input, options), + 'contentControls.checkbox.toggle': (input, options) => api.contentControls.checkbox.toggle(input, options), + 'contentControls.checkbox.setSymbolPair': (input, options) => + api.contentControls.checkbox.setSymbolPair(input, options), + + // --- contentControls.choiceList.* --- + 'contentControls.choiceList.getItems': (input) => api.contentControls.choiceList.getItems(input), + 'contentControls.choiceList.setItems': (input, options) => api.contentControls.choiceList.setItems(input, options), + 'contentControls.choiceList.setSelected': (input, options) => + api.contentControls.choiceList.setSelected(input, options), + + // --- contentControls.repeatingSection.* --- + 'contentControls.repeatingSection.listItems': (input) => api.contentControls.repeatingSection.listItems(input), + 'contentControls.repeatingSection.insertItemBefore': (input, options) => + api.contentControls.repeatingSection.insertItemBefore(input, options), + 'contentControls.repeatingSection.insertItemAfter': (input, options) => + api.contentControls.repeatingSection.insertItemAfter(input, options), + 'contentControls.repeatingSection.cloneItem': (input, options) => + api.contentControls.repeatingSection.cloneItem(input, options), + 'contentControls.repeatingSection.deleteItem': (input, options) => + api.contentControls.repeatingSection.deleteItem(input, options), + 'contentControls.repeatingSection.setAllowInsertDelete': (input, options) => + api.contentControls.repeatingSection.setAllowInsertDelete(input, options), + + // --- contentControls.group.* --- + 'contentControls.group.wrap': (input, options) => api.contentControls.group.wrap(input, options), + 'contentControls.group.ungroup': (input, options) => api.contentControls.group.ungroup(input, options), }; } diff --git a/packages/document-api/src/types/node.ts b/packages/document-api/src/types/node.ts index 7d2bde57ee..a696697302 100644 --- a/packages/document-api/src/types/node.ts +++ b/packages/document-api/src/types/node.ts @@ -7,7 +7,7 @@ import type { HeadingNodeInfo, ListItemNodeInfo, ParagraphNodeInfo } from './par import type { LineBreakNodeInfo, RunNodeInfo, TabNodeInfo } from './inline.types.js'; import type { TableCellNodeInfo, TableNodeInfo, TableRowNodeInfo } from './tables.types.js'; import type { ImageNodeInfo } from './media.types.js'; -import type { BookmarkNodeInfo, HyperlinkNodeInfo, SdtNodeInfo } from './structured.types.js'; +import type { BookmarkNodeInfo, ContentControlInfo, HyperlinkNodeInfo } from './structured.types.js'; import type { CommentNodeInfo } from './comments.types.js'; import type { FootnoteRefNodeInfo } from './references.types.js'; import type { TableOfContentsNodeInfo } from '../toc/toc.types.js'; @@ -21,7 +21,7 @@ export type NodeInfo = | TableCellNodeInfo | TableOfContentsNodeInfo | ImageNodeInfo - | SdtNodeInfo + | ContentControlInfo | RunNodeInfo | BookmarkNodeInfo | CommentNodeInfo diff --git a/packages/document-api/src/types/receipt.ts b/packages/document-api/src/types/receipt.ts index 4eb9259331..6a9998778b 100644 --- a/packages/document-api/src/types/receipt.ts +++ b/packages/document-api/src/types/receipt.ts @@ -47,7 +47,10 @@ export type ReceiptFailureCode = | 'DUPLICATE_ID' | 'INVALID_CONTEXT' | 'RAW_MODE_REQUIRED' - | 'PRESERVE_ONLY_VIOLATION'; + | 'PRESERVE_ONLY_VIOLATION' + // SD-2070 content controls failure codes + | 'LOCK_VIOLATION' + | 'TYPE_MISMATCH'; export type ReceiptFailure = { code: ReceiptFailureCode; diff --git a/packages/document-api/src/types/structured.types.ts b/packages/document-api/src/types/structured.types.ts index 459d493472..c85f182c3e 100644 --- a/packages/document-api/src/types/structured.types.ts +++ b/packages/document-api/src/types/structured.types.ts @@ -1,18 +1,6 @@ -import type { BaseNodeInfo, NodeKind } from './base.js'; +import type { BaseNodeInfo } from './base.js'; -export interface SdtNodeInfo extends BaseNodeInfo { - nodeType: 'sdt'; - kind: NodeKind; - properties: SdtProperties; -} - -export interface SdtProperties { - tag?: string; - alias?: string; - type?: string; - appearance?: string; - placeholder?: string; -} +export type { ContentControlInfo } from '../content-controls/content-controls.types.js'; export interface BookmarkNodeInfo extends BaseNodeInfo { nodeType: 'bookmark'; diff --git a/packages/super-editor/src/core/super-converter/SuperConverter.js b/packages/super-editor/src/core/super-converter/SuperConverter.js index b5b8d5f18f..9da98697ff 100644 --- a/packages/super-editor/src/core/super-converter/SuperConverter.js +++ b/packages/super-editor/src/core/super-converter/SuperConverter.js @@ -1107,6 +1107,7 @@ class SuperConverter { editor, exportJsonOnly = false, fieldsHighlightColor, + preserveSdtWrappers = false, ) { // Reset export warnings for this export cycle this.exportWarnings = []; @@ -1127,6 +1128,7 @@ class SuperConverter { isFinalDoc, editor, fieldsHighlightColor, + preserveSdtWrappers, }); // Keep convertedXml's document part in sync with the current export tree @@ -1231,6 +1233,7 @@ class SuperConverter { editor, isHeaderFooter = false, fieldsHighlightColor = null, + preserveSdtWrappers = false, }) { const bodyNode = this.savedTagsToRestore.find((el) => el.name === 'w:body'); @@ -1250,6 +1253,7 @@ class SuperConverter { editor, isHeaderFooter, fieldsHighlightColor, + preserveSdtWrappers, }); return { result, params }; diff --git a/packages/super-editor/src/core/super-converter/v3/handlers/w/sdt/helpers/handle-structured-content-node.js b/packages/super-editor/src/core/super-converter/v3/handlers/w/sdt/helpers/handle-structured-content-node.js index 1e233dbd64..c186ad4e04 100644 --- a/packages/super-editor/src/core/super-converter/v3/handlers/w/sdt/helpers/handle-structured-content-node.js +++ b/packages/super-editor/src/core/super-converter/v3/handlers/w/sdt/helpers/handle-structured-content-node.js @@ -1,5 +1,50 @@ import { parseAnnotationMarks } from './handle-annotation-node'; +/** + * Detect the semantic control type from sdtPr child elements. + * Returns a canonical controlType string matching the ContentControlType enum. + * @param {Object|null} sdtPr + * @returns {string|null} + */ +function detectControlType(sdtPr) { + if (!sdtPr?.elements) return null; + const names = sdtPr.elements.map((el) => el.name); + + if (names.includes('w:text')) return 'text'; + if (names.includes('w:date')) return 'date'; + if (names.includes('w14:checkbox') || names.includes('w:checkbox')) return 'checkbox'; + if (names.includes('w:comboBox')) return 'comboBox'; + if (names.includes('w:dropDownList')) return 'dropDownList'; + if (names.includes('w15:repeatingSection') || names.includes('w:repeatingSection')) return 'repeatingSection'; + if (names.includes('w15:repeatingSectionItem') || names.includes('w:repeatingSectionItem')) + return 'repeatingSectionItem'; + if (names.includes('w:group')) return 'group'; + return null; +} + +/** + * Extract the appearance value from sdtPr. + * @param {Object|null} sdtPr + * @returns {string|null} + */ +function extractAppearance(sdtPr) { + const el = sdtPr?.elements?.find((e) => e.name === 'w:appearance' || e.name === 'w15:appearance'); + const val = el?.attributes?.['w:val'] ?? el?.attributes?.['w15:val']; + const valid = ['boundingBox', 'tags', 'hidden']; + return valid.includes(val) ? val : null; +} + +/** + * Extract placeholder text from sdtPr. + * @param {Object|null} sdtPr + * @returns {string|null} + */ +function extractPlaceholder(sdtPr) { + const el = sdtPr?.elements?.find((e) => e.name === 'w:placeholder'); + const docPart = el?.elements?.find((e) => e.name === 'w:docPart'); + return docPart?.attributes?.['w:val'] ?? null; +} + /** * @param {Object} params * @returns {Object|null} @@ -19,12 +64,19 @@ export function handleStructuredContentNode(params) { const tag = sdtPr?.elements?.find((el) => el.name === 'w:tag'); const alias = sdtPr?.elements?.find((el) => el.name === 'w:alias'); - // Get the lock tag and value + // Lock mode const lockTag = sdtPr?.elements?.find((el) => el.name === 'w:lock'); const lockValue = lockTag?.attributes?.['w:val']; const validModes = ['unlocked', 'sdtLocked', 'contentLocked', 'sdtContentLocked']; const lockMode = validModes.includes(lockValue) ? lockValue : 'unlocked'; + // Control type detection from sdtPr children + const controlType = detectControlType(sdtPr); + + // Appearance and placeholder + const appearance = extractAppearance(sdtPr); + const placeholder = extractPlaceholder(sdtPr); + if (!sdtContent) { return null; } @@ -50,6 +102,10 @@ export function handleStructuredContentNode(params) { tag: tag?.attributes?.['w:val'] || null, alias: alias?.attributes?.['w:val'] || null, lockMode, + controlType, + type: controlType, + appearance, + placeholder, sdtPr, }, }; diff --git a/packages/super-editor/src/core/super-converter/v3/handlers/w/sdt/helpers/translate-structured-content.js b/packages/super-editor/src/core/super-converter/v3/handlers/w/sdt/helpers/translate-structured-content.js index 0363b91054..60e2795f72 100644 --- a/packages/super-editor/src/core/super-converter/v3/handlers/w/sdt/helpers/translate-structured-content.js +++ b/packages/super-editor/src/core/super-converter/v3/handlers/w/sdt/helpers/translate-structured-content.js @@ -6,21 +6,20 @@ import { convertSdtContentToRuns } from './convert-sdt-content-to-runs.js'; * @returns {Object|Array|Object[]} The XML representation. */ export function translateStructuredContent(params) { - const { node, isFinalDoc } = params; + const { node, isFinalDoc, preserveSdtWrappers } = params; const childContent = translateChildNodes({ ...params, node }); const childElements = Array.isArray(childContent) ? childContent : [childContent]; - if (isFinalDoc) { + // SDT flattening only applies to UI/editor export paths (isFinalDoc without + // preserveSdtWrappers). Document API export paths set preserveSdtWrappers=true + // to maintain full SDT fidelity in the output DOCX. + if (isFinalDoc && !preserveSdtWrappers) { if (node?.type === 'structuredContent') { - // For final docs, inline structured content is flattened to run elements, - // removing the SDT wrapper so content renders as plain text in the output DOCX. return convertSdtContentToRuns(childElements); } if (node?.type === 'structuredContentBlock') { - // For final docs, block-level structured content (tables, paragraphs) is unwrapped - // from the SDT container. Single elements are returned directly, multiple elements as an array. return childElements.length === 1 ? childElements[0] : childElements; } } @@ -38,6 +37,18 @@ export function translateStructuredContent(params) { return result; } +/** Maps control types to their sdtPr element names for OOXML export. */ +const CONTROL_TYPE_ELEMENT_MAP = { + text: 'w:text', + date: 'w:date', + checkbox: 'w14:checkbox', + comboBox: 'w:comboBox', + dropDownList: 'w:dropDownList', + repeatingSection: 'w15:repeatingSection', + repeatingSectionItem: 'w15:repeatingSectionItem', + group: 'w:group', +}; + function generateSdtPrTagForStructuredContent({ node }) { const { attrs = {} } = node; @@ -63,7 +74,7 @@ function generateSdtPrTagForStructuredContent({ node }) { }; const resultElements = []; - if (attrs.id) resultElements.push(id); + if (attrs.id != null) resultElements.push(id); if (attrs.alias) resultElements.push(alias); if (attrs.tag) resultElements.push(tag); if (attrs.lockMode && attrs.lockMode !== 'unlocked') resultElements.push(lock); @@ -80,6 +91,14 @@ function generateSdtPrTagForStructuredContent({ node }) { return result; } + // When sdtPr is absent (newly created controls), emit the type-specific + // element from attrs.controlType so the OOXML is semantically correct. + const controlType = attrs.controlType || attrs.type; + const typeElementName = controlType && CONTROL_TYPE_ELEMENT_MAP[controlType]; + if (typeElementName) { + resultElements.push({ name: typeElementName, type: 'element' }); + } + const result = { name: 'w:sdtPr', type: 'element', diff --git a/packages/super-editor/src/document-api-adapters/__conformance__/contract-conformance.test.ts b/packages/super-editor/src/document-api-adapters/__conformance__/contract-conformance.test.ts index 43695e64a7..1f703fb6c0 100644 --- a/packages/super-editor/src/document-api-adapters/__conformance__/contract-conformance.test.ts +++ b/packages/super-editor/src/document-api-adapters/__conformance__/contract-conformance.test.ts @@ -130,6 +130,7 @@ import { hyperlinksPatchWrapper, hyperlinksRemoveWrapper, } from '../plan-engine/hyperlinks-wrappers.js'; +import { createContentControlsAdapter } from '../plan-engine/content-controls-wrappers.js'; import { listsInsertWrapper, listsIndentWrapper, @@ -1264,6 +1265,33 @@ const STUB_TABLE_OPS: ReadonlySet = new Set([] as OperationId[]); */ const PLAN_ENGINE_META_OPS: ReadonlySet = new Set(['mutations.apply'] as OperationId[]); const NON_RECEIPT_MUTATION_OPS: ReadonlySet = new Set(['history.undo', 'history.redo'] as OperationId[]); + +/** + * Content-control operations whose handlers always return `true` because they + * build and dispatch their own ProseMirror transaction directly (via + * `editor.view!.dispatch(tr)`) rather than delegating to an editor command whose + * boolean result propagates to the domain-command executor. + * + * Because the handler always returns `true`, the `domain.command` executor marks + * the step effect as `'changed'` and `executeSdtMutation` returns success. + * There is no code path that produces the `NO_OP` structured failure for these + * operations, so they are excluded from the failureCase conformance check. + */ +const CC_DIRECT_DISPATCH_OPS: ReadonlySet = new Set([ + 'contentControls.wrap', + 'contentControls.unwrap', + 'contentControls.copy', + 'contentControls.move', + 'contentControls.insertBefore', + 'contentControls.insertAfter', + 'contentControls.group.wrap', + 'contentControls.group.ungroup', + 'contentControls.repeatingSection.insertItemBefore', + 'contentControls.repeatingSection.insertItemAfter', + 'contentControls.repeatingSection.cloneItem', + 'contentControls.repeatingSection.deleteItem', +] as OperationId[]); + const HAS_STRUCTURED_FAILURE_RESULT = (operationId: OperationId): boolean => COMMAND_CATALOG[operationId].possibleFailureCodes.length > 0; @@ -2171,6 +2199,215 @@ function makeHyperlinkEditor( } as unknown as Editor; } +// --------------------------------------------------------------------------- +// Content-controls mock helpers +// --------------------------------------------------------------------------- + +const SDT_TARGET = { kind: 'block' as const, nodeType: 'sdt' as const, nodeId: 'sdt-1' }; +const MISSING_SDT_TARGET = { kind: 'block' as const, nodeType: 'sdt' as const, nodeId: 'nonexistent' }; +const RS_TARGET = { kind: 'block' as const, nodeType: 'sdt' as const, nodeId: 'rs-1' }; + +/** Create an SDT editor whose commands return false โ€” triggers NO_OP failure. */ +function makeNoOpSdtEditor(overrideAttrs: Record = {}): Editor { + const editor = makeSdtEditor(overrideAttrs); + (editor.commands as any).updateStructuredContentById = vi.fn(() => false); + (editor.commands as any).deleteStructuredContentById = vi.fn(() => false); + (editor.commands as any).insertStructuredContentBlock = vi.fn(() => false); + (editor.commands as any).insertStructuredContentInline = vi.fn(() => false); + return editor; +} + +function makeNoOpSdtEditorWithRepeatingSectionItems(): Editor { + const editor = makeSdtEditorWithRepeatingSectionItems(); + (editor.commands as any).updateStructuredContentById = vi.fn(() => false); + (editor.commands as any).deleteStructuredContentById = vi.fn(() => false); + (editor.commands as any).insertStructuredContentBlock = vi.fn(() => false); + (editor.commands as any).insertStructuredContentInline = vi.fn(() => false); + return editor; +} + +function makeSdtEditor(overrideAttrs: Record = {}): Editor { + const sdtAttrs = { + id: 'sdt-1', + tag: 'test-tag', + alias: 'Test Alias', + lockMode: 'unlocked', + controlType: 'text', + type: 'text', + sdtPr: { elements: [] }, + ...overrideAttrs, + }; + + const textNode = createNode('text', [], { text: 'SDT content' }); + const innerParagraph = createNode('paragraph', [textNode], { + attrs: { sdBlockId: 'inner-p' }, + isBlock: true, + inlineContent: true, + }); + const sdtNode = createNode('structuredContentBlock', [innerParagraph], { + attrs: sdtAttrs, + isBlock: true, + }); + const doc = createNode('doc', [sdtNode], { isBlock: false }); + + const tr = { + insertText: vi.fn().mockReturnThis(), + delete: vi.fn().mockReturnThis(), + addMark: vi.fn().mockReturnThis(), + removeMark: vi.fn().mockReturnThis(), + replaceWith: vi.fn().mockReturnThis(), + insert: vi.fn().mockReturnThis(), + setMeta: vi.fn().mockReturnThis(), + mapping: { map: (pos: number) => pos }, + docChanged: true, + doc, + steps: [{ type: 'replaceStep' }], + }; + + const dispatch = vi.fn(); + + const editor = { + state: { + doc, + tr, + schema: { + marks: {}, + text: (t: string) => createNode('text', [], { text: t }), + nodes: { + paragraph: { + create: vi.fn(() => innerParagraph), + createAndFill: vi.fn(() => innerParagraph), + }, + structuredContentBlock: { + create: vi.fn((attrs: unknown, content: unknown) => + createNode('structuredContentBlock', [], { attrs: attrs as Record, isBlock: true }), + ), + }, + }, + }, + selection: { from: 0, to: doc.nodeSize }, + }, + schema: { + marks: {}, + text: (t: string) => createNode('text', [], { text: t }), + nodes: { + paragraph: { + create: vi.fn(() => innerParagraph), + createAndFill: vi.fn(() => innerParagraph), + }, + structuredContentBlock: { + create: vi.fn((attrs: unknown, content: unknown) => + createNode('structuredContentBlock', [], { attrs: attrs as Record, isBlock: true }), + ), + }, + }, + }, + dispatch, + view: { dispatch }, + commands: { + updateStructuredContentById: vi.fn(() => true), + deleteStructuredContentById: vi.fn(() => true), + insertStructuredContentBlock: vi.fn(() => true), + insertStructuredContentInline: vi.fn(() => true), + }, + } as unknown as Editor; + + return editor; +} + +function makeSdtEditorWithRepeatingSectionItems(): Editor { + const textNode = createNode('text', [], { text: 'Item content' }); + const itemParagraph = createNode('paragraph', [textNode], { + attrs: { sdBlockId: 'item-p' }, + isBlock: true, + inlineContent: true, + }); + const rsiNode = createNode('structuredContentBlock', [itemParagraph], { + attrs: { + id: 'rsi-1', + controlType: 'repeatingSectionItem', + type: 'repeatingSectionItem', + lockMode: 'unlocked', + sdtPr: { elements: [] }, + }, + isBlock: true, + }); + const rsNode = createNode('structuredContentBlock', [rsiNode], { + attrs: { + id: 'rs-1', + controlType: 'repeatingSection', + type: 'repeatingSection', + lockMode: 'unlocked', + sdtPr: { elements: [] }, + }, + isBlock: true, + }); + const doc = createNode('doc', [rsNode], { isBlock: false }); + + const tr = { + insertText: vi.fn().mockReturnThis(), + delete: vi.fn().mockReturnThis(), + addMark: vi.fn().mockReturnThis(), + removeMark: vi.fn().mockReturnThis(), + replaceWith: vi.fn().mockReturnThis(), + insert: vi.fn().mockReturnThis(), + setMeta: vi.fn().mockReturnThis(), + mapping: { map: (pos: number) => pos }, + docChanged: true, + doc, + steps: [{ type: 'replaceStep' }], + }; + + const dispatch = vi.fn(); + const itemParagraphNode = createNode('paragraph', [], { isBlock: true, inlineContent: true }); + + return { + state: { + doc, + tr, + schema: { + marks: {}, + text: (t: string) => createNode('text', [], { text: t }), + nodes: { + paragraph: { create: vi.fn(() => itemParagraphNode), createAndFill: vi.fn(() => itemParagraphNode) }, + structuredContentBlock: { + create: vi.fn((attrs: unknown, content: unknown) => + createNode('structuredContentBlock', [content as ProseMirrorNode].flat().filter(Boolean), { + attrs: attrs as Record, + isBlock: true, + }), + ), + }, + }, + }, + selection: { from: 0, to: doc.nodeSize }, + }, + schema: { + marks: {}, + text: (t: string) => createNode('text', [], { text: t }), + nodes: { + paragraph: { create: vi.fn(() => itemParagraphNode), createAndFill: vi.fn(() => itemParagraphNode) }, + structuredContentBlock: { + create: vi.fn((attrs: unknown, content: unknown) => + createNode('structuredContentBlock', [content as ProseMirrorNode].flat().filter(Boolean), { + attrs: attrs as Record, + isBlock: true, + }), + ), + }, + }, + }, + dispatch, + view: { dispatch }, + commands: { + updateStructuredContentById: vi.fn(() => true), + deleteStructuredContentById: vi.fn(() => true), + insertStructuredContentBlock: vi.fn(() => true), + insertStructuredContentInline: vi.fn(() => true), + }, + } as unknown as Editor; +} + /** * Image editor with resolve + schema mocks for caption operations. * @param opts.withCaption Add a `Caption`-styled paragraph after the image paragraph. @@ -5213,106 +5450,823 @@ const mutationVectors: Partial> = { { changeMode: 'direct' }, ), }, - // SD-2100: Image geometry, content, semantic & caption operations // ------------------------------------------------------------------------- - 'images.scale': { - throwCase: () => - imagesScaleWrapper(makeImageEditor(), { imageId: 'missing', factor: 1.5 }, { changeMode: 'direct' }), + // Content control operations + // ------------------------------------------------------------------------- + 'contentControls.appendContent': { + throwCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.appendContent({ target: MISSING_SDT_TARGET, content: 'appended' }, { changeMode: 'direct' }); + }, failureCase: () => { - // factor=1 produces identical dimensions โ†’ explicit NO_OP pre-check - return imagesScaleWrapper(makeImageEditor(), { imageId: 'img-1', factor: 1 }, { changeMode: 'direct' }); + const adapter = createContentControlsAdapter(makeNoOpSdtEditor()); + return adapter.appendContent({ target: SDT_TARGET, content: 'appended' }, { changeMode: 'direct' }); + }, + applyCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.appendContent({ target: SDT_TARGET, content: 'appended' }, { changeMode: 'direct' }); }, - applyCase: () => imagesScaleWrapper(makeImageEditor(), { imageId: 'img-1', factor: 1.5 }, { changeMode: 'direct' }), }, - 'images.setLockAspectRatio': { - throwCase: () => - imagesSetLockAspectRatioWrapper( - makeImageEditor(), - { imageId: 'missing', locked: false }, - { changeMode: 'direct' }, - ), + 'contentControls.checkbox.setState': { + throwCase: () => { + const adapter = createContentControlsAdapter( + makeSdtEditor({ + controlType: 'checkbox', + type: 'checkbox', + sdtPr: { elements: [], 'w14:checkbox': { 'w14:checked': '0' } }, + }), + ); + return adapter.checkbox.setState({ target: MISSING_SDT_TARGET, checked: true }, { changeMode: 'direct' }); + }, failureCase: () => { - // Default lockAspectRatio is true โ†’ NO_OP - return imagesSetLockAspectRatioWrapper( - makeImageEditor(), - { imageId: 'img-1', locked: true }, - { changeMode: 'direct' }, + const adapter = createContentControlsAdapter( + makeNoOpSdtEditor({ + controlType: 'checkbox', + type: 'checkbox', + sdtPr: { elements: [], 'w14:checkbox': { 'w14:checked': '0' } }, + }), ); + return adapter.checkbox.setState({ target: SDT_TARGET, checked: true }, { changeMode: 'direct' }); + }, + applyCase: () => { + const adapter = createContentControlsAdapter( + makeSdtEditor({ + controlType: 'checkbox', + type: 'checkbox', + sdtPr: { elements: [], 'w14:checkbox': { 'w14:checked': '0' } }, + }), + ); + return adapter.checkbox.setState({ target: SDT_TARGET, checked: true }, { changeMode: 'direct' }); }, - applyCase: () => - imagesSetLockAspectRatioWrapper(makeImageEditor(), { imageId: 'img-1', locked: false }, { changeMode: 'direct' }), }, - 'images.rotate': { - throwCase: () => - imagesRotateWrapper(makeImageEditor(), { imageId: 'missing', angle: 90 }, { changeMode: 'direct' }), + 'contentControls.checkbox.toggle': { + throwCase: () => { + const adapter = createContentControlsAdapter( + makeSdtEditor({ + controlType: 'checkbox', + type: 'checkbox', + sdtPr: { elements: [], 'w14:checkbox': { 'w14:checked': '0' } }, + }), + ); + return adapter.checkbox.toggle({ target: MISSING_SDT_TARGET }, { changeMode: 'direct' }); + }, failureCase: () => { - // No rotation set, angle=0 โ†’ NO_OP - return imagesRotateWrapper(makeImageEditor(), { imageId: 'img-1', angle: 0 }, { changeMode: 'direct' }); + const adapter = createContentControlsAdapter( + makeNoOpSdtEditor({ + controlType: 'checkbox', + type: 'checkbox', + sdtPr: { elements: [], 'w14:checkbox': { 'w14:checked': '0' } }, + }), + ); + return adapter.checkbox.toggle({ target: SDT_TARGET }, { changeMode: 'direct' }); + }, + applyCase: () => { + const adapter = createContentControlsAdapter( + makeSdtEditor({ + controlType: 'checkbox', + type: 'checkbox', + sdtPr: { elements: [], 'w14:checkbox': { 'w14:checked': '0' } }, + }), + ); + return adapter.checkbox.toggle({ target: SDT_TARGET }, { changeMode: 'direct' }); }, - applyCase: () => imagesRotateWrapper(makeImageEditor(), { imageId: 'img-1', angle: 90 }, { changeMode: 'direct' }), }, - 'images.flip': { - throwCase: () => - imagesFlipWrapper(makeImageEditor(), { imageId: 'missing', horizontal: true }, { changeMode: 'direct' }), + 'contentControls.checkbox.setSymbolPair': { + throwCase: () => { + const adapter = createContentControlsAdapter( + makeSdtEditor({ + controlType: 'checkbox', + type: 'checkbox', + sdtPr: { elements: [], 'w14:checkbox': { 'w14:checked': '0' } }, + }), + ); + return adapter.checkbox.setSymbolPair( + { + target: MISSING_SDT_TARGET, + checkedSymbol: { font: 'Wingdings', char: '00FE' }, + uncheckedSymbol: { font: 'Wingdings', char: '00A8' }, + }, + { changeMode: 'direct' }, + ); + }, failureCase: () => { - // No transformData, passing false for both axes matches defaults โ†’ NO_OP - return imagesFlipWrapper( - makeImageEditor(), - { imageId: 'img-1', horizontal: false, vertical: false }, + const adapter = createContentControlsAdapter( + makeNoOpSdtEditor({ + controlType: 'checkbox', + type: 'checkbox', + sdtPr: { elements: [], 'w14:checkbox': { 'w14:checked': '0' } }, + }), + ); + return adapter.checkbox.setSymbolPair( + { + target: SDT_TARGET, + checkedSymbol: { font: 'Wingdings', char: '00FE' }, + uncheckedSymbol: { font: 'Wingdings', char: '00A8' }, + }, + { changeMode: 'direct' }, + ); + }, + applyCase: () => { + const adapter = createContentControlsAdapter( + makeSdtEditor({ + controlType: 'checkbox', + type: 'checkbox', + sdtPr: { elements: [], 'w14:checkbox': { 'w14:checked': '0' } }, + }), + ); + return adapter.checkbox.setSymbolPair( + { + target: SDT_TARGET, + checkedSymbol: { font: 'Wingdings', char: '00FE' }, + uncheckedSymbol: { font: 'Wingdings', char: '00A8' }, + }, { changeMode: 'direct' }, ); }, - applyCase: () => - imagesFlipWrapper(makeImageEditor(), { imageId: 'img-1', horizontal: true }, { changeMode: 'direct' }), }, - 'images.crop': { - throwCase: () => - imagesCropWrapper( - makeImageEditor(), - { imageId: 'missing', crop: { left: 10, top: 10, right: 10, bottom: 10 } }, + 'contentControls.choiceList.setItems': { + throwCase: () => { + const adapter = createContentControlsAdapter( + makeSdtEditor({ + controlType: 'comboBox', + type: 'comboBox', + sdtPr: { elements: [], 'w:comboBox': { 'w:listItem': [] } }, + }), + ); + return adapter.choiceList.setItems( + { target: MISSING_SDT_TARGET, items: [{ displayText: 'A', value: 'a' }] }, { changeMode: 'direct' }, - ), + ); + }, failureCase: () => { - // Transaction produces no change โ†’ NO_OP - const editor = makeImageEditor(); - const tr = (editor.state as unknown as { tr: Record }).tr; - tr.docChanged = false; - tr.steps = []; - return imagesCropWrapper( - editor, - { imageId: 'img-1', crop: { left: 10, top: 5, right: 10, bottom: 5 } }, + const adapter = createContentControlsAdapter( + makeNoOpSdtEditor({ + controlType: 'comboBox', + type: 'comboBox', + sdtPr: { elements: [], 'w:comboBox': { 'w:listItem': [] } }, + }), + ); + return adapter.choiceList.setItems( + { target: SDT_TARGET, items: [{ displayText: 'A', value: 'a' }] }, { changeMode: 'direct' }, ); }, - applyCase: () => - imagesCropWrapper( - makeImageEditor(), - { imageId: 'img-1', crop: { left: 10, top: 5, right: 10, bottom: 5 } }, + applyCase: () => { + const adapter = createContentControlsAdapter( + makeSdtEditor({ + controlType: 'comboBox', + type: 'comboBox', + sdtPr: { elements: [], 'w:comboBox': { 'w:listItem': [] } }, + }), + ); + return adapter.choiceList.setItems( + { target: SDT_TARGET, items: [{ displayText: 'A', value: 'a' }] }, { changeMode: 'direct' }, - ), + ); + }, }, - 'images.resetCrop': { - throwCase: () => imagesResetCropWrapper(makeImageEditor(), { imageId: 'missing' }, { changeMode: 'direct' }), + 'contentControls.choiceList.setSelected': { + throwCase: () => { + const adapter = createContentControlsAdapter( + makeSdtEditor({ + controlType: 'comboBox', + type: 'comboBox', + sdtPr: { elements: [], 'w:comboBox': { 'w:listItem': [] } }, + }), + ); + return adapter.choiceList.setSelected({ target: MISSING_SDT_TARGET, value: 'a' }, { changeMode: 'direct' }); + }, failureCase: () => { - // No crop set โ†’ NO_OP - return imagesResetCropWrapper(makeImageEditor(), { imageId: 'img-1' }, { changeMode: 'direct' }); + const adapter = createContentControlsAdapter( + makeNoOpSdtEditor({ + controlType: 'comboBox', + type: 'comboBox', + sdtPr: { elements: [], 'w:comboBox': { 'w:listItem': [] } }, + }), + ); + return adapter.choiceList.setSelected({ target: SDT_TARGET, value: 'a' }, { changeMode: 'direct' }); }, applyCase: () => { - // Image with crop data - const editor = makeCaptionImageEditor({ - imageId: 'img-cropped', - extraAttrs: { - clipPath: 'inset(5% 10% 5% 10%)', - rawSrcRect: { l: '10000', t: '5000', r: '10000', b: '5000' }, - }, - }); - return imagesResetCropWrapper(editor, { imageId: 'img-cropped' }, { changeMode: 'direct' }); + const adapter = createContentControlsAdapter( + makeSdtEditor({ + controlType: 'comboBox', + type: 'comboBox', + sdtPr: { elements: [], 'w:comboBox': { 'w:listItem': [] } }, + }), + ); + return adapter.choiceList.setSelected({ target: SDT_TARGET, value: 'a' }, { changeMode: 'direct' }); }, }, - 'images.replaceSource': { - throwCase: () => - imagesReplaceSourceWrapper( - makeImageEditor(), + 'contentControls.clearBinding': { + throwCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.clearBinding({ target: MISSING_SDT_TARGET }, { changeMode: 'direct' }); + }, + failureCase: () => { + const adapter = createContentControlsAdapter(makeNoOpSdtEditor()); + return adapter.clearBinding({ target: SDT_TARGET }, { changeMode: 'direct' }); + }, + applyCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.clearBinding({ target: SDT_TARGET }, { changeMode: 'direct' }); + }, + }, + 'contentControls.clearContent': { + throwCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.clearContent({ target: MISSING_SDT_TARGET }, { changeMode: 'direct' }); + }, + failureCase: () => { + const adapter = createContentControlsAdapter(makeNoOpSdtEditor()); + return adapter.clearContent({ target: SDT_TARGET }, { changeMode: 'direct' }); + }, + applyCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.clearContent({ target: SDT_TARGET }, { changeMode: 'direct' }); + }, + }, + 'contentControls.copy': { + throwCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.copy({ target: MISSING_SDT_TARGET, destination: SDT_TARGET }, { changeMode: 'direct' }); + }, + // failureCase omitted โ€” CC_DIRECT_DISPATCH_OPS: handler always returns true + applyCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.copy({ target: SDT_TARGET, destination: SDT_TARGET }, { changeMode: 'direct' }); + }, + }, + 'contentControls.date.clearValue': { + throwCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor({ controlType: 'date', type: 'date' })); + return adapter.date.clearValue({ target: MISSING_SDT_TARGET }, { changeMode: 'direct' }); + }, + failureCase: () => { + const adapter = createContentControlsAdapter(makeNoOpSdtEditor({ controlType: 'date', type: 'date' })); + return adapter.date.clearValue({ target: SDT_TARGET }, { changeMode: 'direct' }); + }, + applyCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor({ controlType: 'date', type: 'date' })); + return adapter.date.clearValue({ target: SDT_TARGET }, { changeMode: 'direct' }); + }, + }, + 'contentControls.date.setCalendar': { + throwCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor({ controlType: 'date', type: 'date' })); + return adapter.date.setCalendar({ target: MISSING_SDT_TARGET, calendar: 'gregorian' }, { changeMode: 'direct' }); + }, + failureCase: () => { + const adapter = createContentControlsAdapter(makeNoOpSdtEditor({ controlType: 'date', type: 'date' })); + return adapter.date.setCalendar({ target: SDT_TARGET, calendar: 'gregorian' }, { changeMode: 'direct' }); + }, + applyCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor({ controlType: 'date', type: 'date' })); + return adapter.date.setCalendar({ target: SDT_TARGET, calendar: 'gregorian' }, { changeMode: 'direct' }); + }, + }, + 'contentControls.date.setDisplayFormat': { + throwCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor({ controlType: 'date', type: 'date' })); + return adapter.date.setDisplayFormat( + { target: MISSING_SDT_TARGET, format: 'yyyy-MM-dd' }, + { changeMode: 'direct' }, + ); + }, + failureCase: () => { + const adapter = createContentControlsAdapter(makeNoOpSdtEditor({ controlType: 'date', type: 'date' })); + return adapter.date.setDisplayFormat({ target: SDT_TARGET, format: 'yyyy-MM-dd' }, { changeMode: 'direct' }); + }, + applyCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor({ controlType: 'date', type: 'date' })); + return adapter.date.setDisplayFormat({ target: SDT_TARGET, format: 'yyyy-MM-dd' }, { changeMode: 'direct' }); + }, + }, + 'contentControls.date.setDisplayLocale': { + throwCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor({ controlType: 'date', type: 'date' })); + return adapter.date.setDisplayLocale({ target: MISSING_SDT_TARGET, locale: 'en-US' }, { changeMode: 'direct' }); + }, + failureCase: () => { + const adapter = createContentControlsAdapter(makeNoOpSdtEditor({ controlType: 'date', type: 'date' })); + return adapter.date.setDisplayLocale({ target: SDT_TARGET, locale: 'en-US' }, { changeMode: 'direct' }); + }, + applyCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor({ controlType: 'date', type: 'date' })); + return adapter.date.setDisplayLocale({ target: SDT_TARGET, locale: 'en-US' }, { changeMode: 'direct' }); + }, + }, + 'contentControls.date.setStorageFormat': { + throwCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor({ controlType: 'date', type: 'date' })); + return adapter.date.setStorageFormat( + { target: MISSING_SDT_TARGET, format: 'xsd:dateTime' }, + { changeMode: 'direct' }, + ); + }, + failureCase: () => { + const adapter = createContentControlsAdapter(makeNoOpSdtEditor({ controlType: 'date', type: 'date' })); + return adapter.date.setStorageFormat({ target: SDT_TARGET, format: 'xsd:dateTime' }, { changeMode: 'direct' }); + }, + applyCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor({ controlType: 'date', type: 'date' })); + return adapter.date.setStorageFormat({ target: SDT_TARGET, format: 'xsd:dateTime' }, { changeMode: 'direct' }); + }, + }, + 'contentControls.date.setValue': { + throwCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor({ controlType: 'date', type: 'date' })); + return adapter.date.setValue({ target: MISSING_SDT_TARGET, value: '2024-01-01' }, { changeMode: 'direct' }); + }, + failureCase: () => { + const adapter = createContentControlsAdapter(makeNoOpSdtEditor({ controlType: 'date', type: 'date' })); + return adapter.date.setValue({ target: SDT_TARGET, value: '2024-01-01' }, { changeMode: 'direct' }); + }, + applyCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor({ controlType: 'date', type: 'date' })); + return adapter.date.setValue({ target: SDT_TARGET, value: '2024-01-01' }, { changeMode: 'direct' }); + }, + }, + 'contentControls.delete': { + throwCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.delete({ target: MISSING_SDT_TARGET }, { changeMode: 'direct' }); + }, + failureCase: () => { + const adapter = createContentControlsAdapter(makeNoOpSdtEditor()); + return adapter.delete({ target: SDT_TARGET }, { changeMode: 'direct' }); + }, + applyCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.delete({ target: SDT_TARGET }, { changeMode: 'direct' }); + }, + }, + 'contentControls.group.ungroup': { + throwCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor({ controlType: 'group', type: 'group' })); + return adapter.group.ungroup({ target: MISSING_SDT_TARGET }, { changeMode: 'direct' }); + }, + // failureCase omitted โ€” CC_DIRECT_DISPATCH_OPS: handler always returns true + applyCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor({ controlType: 'group', type: 'group' })); + return adapter.group.ungroup({ target: SDT_TARGET }, { changeMode: 'direct' }); + }, + }, + 'contentControls.group.wrap': { + throwCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.group.wrap({ target: MISSING_SDT_TARGET }, { changeMode: 'direct' }); + }, + // failureCase omitted โ€” CC_DIRECT_DISPATCH_OPS: handler always returns true + applyCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.group.wrap({ target: SDT_TARGET }, { changeMode: 'direct' }); + }, + }, + 'contentControls.insertAfter': { + throwCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.insertAfter({ target: MISSING_SDT_TARGET, content: 'after' }, { changeMode: 'direct' }); + }, + // failureCase omitted โ€” CC_DIRECT_DISPATCH_OPS: handler always returns true + applyCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.insertAfter({ target: SDT_TARGET, content: 'after' }, { changeMode: 'direct' }); + }, + }, + 'contentControls.insertBefore': { + throwCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.insertBefore({ target: MISSING_SDT_TARGET, content: 'before' }, { changeMode: 'direct' }); + }, + // failureCase omitted โ€” CC_DIRECT_DISPATCH_OPS: handler always returns true + applyCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.insertBefore({ target: SDT_TARGET, content: 'before' }, { changeMode: 'direct' }); + }, + }, + 'contentControls.move': { + throwCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.move({ target: MISSING_SDT_TARGET, destination: SDT_TARGET }, { changeMode: 'direct' }); + }, + // failureCase omitted โ€” CC_DIRECT_DISPATCH_OPS: handler always returns true + applyCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.move({ target: SDT_TARGET, destination: SDT_TARGET }, { changeMode: 'direct' }); + }, + }, + 'contentControls.normalizeTagPayload': { + throwCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.normalizeTagPayload({ target: MISSING_SDT_TARGET }, { changeMode: 'direct' }); + }, + failureCase: () => { + // Tag is already valid JSON โ€” returns NO_OP + const adapter = createContentControlsAdapter(makeSdtEditor({ tag: '{"key":"value"}' })); + return adapter.normalizeTagPayload({ target: SDT_TARGET }, { changeMode: 'direct' }); + }, + applyCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.normalizeTagPayload({ target: SDT_TARGET }, { changeMode: 'direct' }); + }, + }, + 'contentControls.normalizeWordCompatibility': { + throwCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.normalizeWordCompatibility({ target: MISSING_SDT_TARGET }, { changeMode: 'direct' }); + }, + failureCase: () => { + // ID is already numeric โ€” returns NO_OP + const numericId = '12345'; + const adapter = createContentControlsAdapter(makeSdtEditor({ id: numericId })); + return adapter.normalizeWordCompatibility( + { target: { kind: 'block' as const, nodeType: 'sdt' as const, nodeId: numericId } }, + { changeMode: 'direct' }, + ); + }, + applyCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor({ id: 'not-a-number-id' })); + return adapter.normalizeWordCompatibility( + { target: { kind: 'block', nodeType: 'sdt', nodeId: 'not-a-number-id' } }, + { changeMode: 'direct' }, + ); + }, + }, + 'contentControls.patch': { + throwCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.patch({ target: MISSING_SDT_TARGET, alias: 'New' }, { changeMode: 'direct' }); + }, + failureCase: () => { + const adapter = createContentControlsAdapter(makeNoOpSdtEditor()); + return adapter.patch({ target: SDT_TARGET, alias: 'New Alias' }, { changeMode: 'direct' }); + }, + applyCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.patch({ target: SDT_TARGET, alias: 'New Alias' }, { changeMode: 'direct' }); + }, + }, + 'contentControls.patchRawProperties': { + throwCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.patchRawProperties( + { target: MISSING_SDT_TARGET, patches: [{ op: 'set', name: 'w:tag', element: { val: 'x' } }] }, + { changeMode: 'direct' }, + ); + }, + failureCase: () => { + const adapter = createContentControlsAdapter(makeNoOpSdtEditor()); + return adapter.patchRawProperties( + { target: SDT_TARGET, patches: [{ op: 'set', name: 'w:tag', element: { val: 'x' } }] }, + { changeMode: 'direct' }, + ); + }, + applyCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.patchRawProperties( + { target: SDT_TARGET, patches: [{ op: 'set', name: 'w:tag', element: { val: 'x' } }] }, + { changeMode: 'direct' }, + ); + }, + }, + 'contentControls.prependContent': { + throwCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.prependContent({ target: MISSING_SDT_TARGET, content: 'prepended' }, { changeMode: 'direct' }); + }, + failureCase: () => { + const adapter = createContentControlsAdapter(makeNoOpSdtEditor()); + return adapter.prependContent({ target: SDT_TARGET, content: 'prepended' }, { changeMode: 'direct' }); + }, + applyCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.prependContent({ target: SDT_TARGET, content: 'prepended' }, { changeMode: 'direct' }); + }, + }, + 'contentControls.repeatingSection.cloneItem': { + throwCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditorWithRepeatingSectionItems()); + return adapter.repeatingSection.cloneItem({ target: MISSING_SDT_TARGET, index: 0 }, { changeMode: 'direct' }); + }, + // failureCase omitted โ€” CC_DIRECT_DISPATCH_OPS: handler always returns true + applyCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditorWithRepeatingSectionItems()); + return adapter.repeatingSection.cloneItem({ target: RS_TARGET, index: 0 }, { changeMode: 'direct' }); + }, + }, + 'contentControls.repeatingSection.deleteItem': { + throwCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditorWithRepeatingSectionItems()); + return adapter.repeatingSection.deleteItem({ target: MISSING_SDT_TARGET, index: 0 }, { changeMode: 'direct' }); + }, + // failureCase omitted โ€” CC_DIRECT_DISPATCH_OPS: handler always returns true + applyCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditorWithRepeatingSectionItems()); + return adapter.repeatingSection.deleteItem({ target: RS_TARGET, index: 0 }, { changeMode: 'direct' }); + }, + }, + 'contentControls.repeatingSection.insertItemAfter': { + throwCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditorWithRepeatingSectionItems()); + return adapter.repeatingSection.insertItemAfter( + { target: MISSING_SDT_TARGET, index: 0 }, + { changeMode: 'direct' }, + ); + }, + // failureCase omitted โ€” CC_DIRECT_DISPATCH_OPS: handler always returns true + applyCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditorWithRepeatingSectionItems()); + return adapter.repeatingSection.insertItemAfter({ target: RS_TARGET, index: 0 }, { changeMode: 'direct' }); + }, + }, + 'contentControls.repeatingSection.insertItemBefore': { + throwCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditorWithRepeatingSectionItems()); + return adapter.repeatingSection.insertItemBefore( + { target: MISSING_SDT_TARGET, index: 0 }, + { changeMode: 'direct' }, + ); + }, + // failureCase omitted โ€” CC_DIRECT_DISPATCH_OPS: handler always returns true + applyCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditorWithRepeatingSectionItems()); + return adapter.repeatingSection.insertItemBefore({ target: RS_TARGET, index: 0 }, { changeMode: 'direct' }); + }, + }, + 'contentControls.repeatingSection.setAllowInsertDelete': { + throwCase: () => { + const adapter = createContentControlsAdapter( + makeSdtEditor({ controlType: 'repeatingSection', type: 'repeatingSection' }), + ); + return adapter.repeatingSection.setAllowInsertDelete( + { target: MISSING_SDT_TARGET, allow: true }, + { changeMode: 'direct' }, + ); + }, + failureCase: () => { + const adapter = createContentControlsAdapter( + makeNoOpSdtEditor({ controlType: 'repeatingSection', type: 'repeatingSection' }), + ); + return adapter.repeatingSection.setAllowInsertDelete( + { target: SDT_TARGET, allow: true }, + { changeMode: 'direct' }, + ); + }, + applyCase: () => { + const adapter = createContentControlsAdapter( + makeSdtEditor({ controlType: 'repeatingSection', type: 'repeatingSection' }), + ); + return adapter.repeatingSection.setAllowInsertDelete( + { target: SDT_TARGET, allow: true }, + { changeMode: 'direct' }, + ); + }, + }, + 'contentControls.replaceContent': { + throwCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.replaceContent({ target: MISSING_SDT_TARGET, content: 'replaced' }, { changeMode: 'direct' }); + }, + failureCase: () => { + const adapter = createContentControlsAdapter(makeNoOpSdtEditor()); + return adapter.replaceContent({ target: SDT_TARGET, content: 'replaced' }, { changeMode: 'direct' }); + }, + applyCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.replaceContent({ target: SDT_TARGET, content: 'replaced' }, { changeMode: 'direct' }); + }, + }, + 'contentControls.setBinding': { + throwCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.setBinding( + { target: MISSING_SDT_TARGET, storeItemId: 'store-1', xpath: '/root' }, + { changeMode: 'direct' }, + ); + }, + failureCase: () => { + const adapter = createContentControlsAdapter(makeNoOpSdtEditor()); + return adapter.setBinding( + { target: SDT_TARGET, storeItemId: 'store-1', xpath: '/root' }, + { changeMode: 'direct' }, + ); + }, + applyCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.setBinding( + { target: SDT_TARGET, storeItemId: 'store-1', xpath: '/root' }, + { changeMode: 'direct' }, + ); + }, + }, + 'contentControls.setLockMode': { + throwCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.setLockMode({ target: MISSING_SDT_TARGET, lockMode: 'locked' }, { changeMode: 'direct' }); + }, + failureCase: () => { + const adapter = createContentControlsAdapter(makeNoOpSdtEditor()); + return adapter.setLockMode({ target: SDT_TARGET, lockMode: 'locked' }, { changeMode: 'direct' }); + }, + applyCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.setLockMode({ target: SDT_TARGET, lockMode: 'locked' }, { changeMode: 'direct' }); + }, + }, + 'contentControls.setType': { + throwCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.setType({ target: MISSING_SDT_TARGET, controlType: 'date' }, { changeMode: 'direct' }); + }, + failureCase: () => { + const adapter = createContentControlsAdapter(makeNoOpSdtEditor()); + return adapter.setType({ target: SDT_TARGET, controlType: 'date' }, { changeMode: 'direct' }); + }, + applyCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.setType({ target: SDT_TARGET, controlType: 'date' }, { changeMode: 'direct' }); + }, + }, + 'contentControls.text.clearValue': { + throwCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor({ controlType: 'text', type: 'text' })); + return adapter.text.clearValue({ target: MISSING_SDT_TARGET }, { changeMode: 'direct' }); + }, + failureCase: () => { + const adapter = createContentControlsAdapter(makeNoOpSdtEditor({ controlType: 'text', type: 'text' })); + return adapter.text.clearValue({ target: SDT_TARGET }, { changeMode: 'direct' }); + }, + applyCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor({ controlType: 'text', type: 'text' })); + return adapter.text.clearValue({ target: SDT_TARGET }, { changeMode: 'direct' }); + }, + }, + 'contentControls.text.setMultiline': { + throwCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor({ controlType: 'text', type: 'text' })); + return adapter.text.setMultiline({ target: MISSING_SDT_TARGET, multiline: true }, { changeMode: 'direct' }); + }, + failureCase: () => { + const adapter = createContentControlsAdapter(makeNoOpSdtEditor({ controlType: 'text', type: 'text' })); + return adapter.text.setMultiline({ target: SDT_TARGET, multiline: true }, { changeMode: 'direct' }); + }, + applyCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor({ controlType: 'text', type: 'text' })); + return adapter.text.setMultiline({ target: SDT_TARGET, multiline: true }, { changeMode: 'direct' }); + }, + }, + 'contentControls.text.setValue': { + throwCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor({ controlType: 'text', type: 'text' })); + return adapter.text.setValue({ target: MISSING_SDT_TARGET, value: 'hello' }, { changeMode: 'direct' }); + }, + failureCase: () => { + const adapter = createContentControlsAdapter(makeNoOpSdtEditor({ controlType: 'text', type: 'text' })); + return adapter.text.setValue({ target: SDT_TARGET, value: 'hello' }, { changeMode: 'direct' }); + }, + applyCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor({ controlType: 'text', type: 'text' })); + return adapter.text.setValue({ target: SDT_TARGET, value: 'hello' }, { changeMode: 'direct' }); + }, + }, + 'contentControls.unwrap': { + throwCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.unwrap({ target: MISSING_SDT_TARGET }, { changeMode: 'direct' }); + }, + // failureCase omitted โ€” CC_DIRECT_DISPATCH_OPS: handler always returns true + applyCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.unwrap({ target: SDT_TARGET }, { changeMode: 'direct' }); + }, + }, + 'contentControls.wrap': { + throwCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.wrap({ target: MISSING_SDT_TARGET, kind: 'block' }, { changeMode: 'direct' }); + }, + // failureCase omitted โ€” CC_DIRECT_DISPATCH_OPS: handler always returns true + applyCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.wrap({ target: SDT_TARGET, kind: 'block' }, { changeMode: 'direct' }); + }, + }, + 'create.contentControl': { + throwCase: () => { + const editor = makeSdtEditor(); + (editor.commands as any).insertStructuredContentBlock = undefined; + const adapter = createContentControlsAdapter(editor); + return adapter.create({ kind: 'block' }, { changeMode: 'direct' }); + }, + failureCase: () => { + const adapter = createContentControlsAdapter(makeNoOpSdtEditor()); + return adapter.create({ kind: 'block' }, { changeMode: 'direct' }); + }, + applyCase: () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.create({ kind: 'block' }, { changeMode: 'direct' }); + }, + }, + // SD-2100: Image geometry, content, semantic & caption operations + // ------------------------------------------------------------------------- + 'images.scale': { + throwCase: () => + imagesScaleWrapper(makeImageEditor(), { imageId: 'missing', factor: 1.5 }, { changeMode: 'direct' }), + failureCase: () => { + // factor=1 produces identical dimensions โ†’ explicit NO_OP pre-check + return imagesScaleWrapper(makeImageEditor(), { imageId: 'img-1', factor: 1 }, { changeMode: 'direct' }); + }, + applyCase: () => imagesScaleWrapper(makeImageEditor(), { imageId: 'img-1', factor: 1.5 }, { changeMode: 'direct' }), + }, + 'images.setLockAspectRatio': { + throwCase: () => + imagesSetLockAspectRatioWrapper( + makeImageEditor(), + { imageId: 'missing', locked: false }, + { changeMode: 'direct' }, + ), + failureCase: () => { + // Default lockAspectRatio is true โ†’ NO_OP + return imagesSetLockAspectRatioWrapper( + makeImageEditor(), + { imageId: 'img-1', locked: true }, + { changeMode: 'direct' }, + ); + }, + applyCase: () => + imagesSetLockAspectRatioWrapper(makeImageEditor(), { imageId: 'img-1', locked: false }, { changeMode: 'direct' }), + }, + 'images.rotate': { + throwCase: () => + imagesRotateWrapper(makeImageEditor(), { imageId: 'missing', angle: 90 }, { changeMode: 'direct' }), + failureCase: () => { + // No rotation set, angle=0 โ†’ NO_OP + return imagesRotateWrapper(makeImageEditor(), { imageId: 'img-1', angle: 0 }, { changeMode: 'direct' }); + }, + applyCase: () => imagesRotateWrapper(makeImageEditor(), { imageId: 'img-1', angle: 90 }, { changeMode: 'direct' }), + }, + 'images.flip': { + throwCase: () => + imagesFlipWrapper(makeImageEditor(), { imageId: 'missing', horizontal: true }, { changeMode: 'direct' }), + failureCase: () => { + // No transformData, passing false for both axes matches defaults โ†’ NO_OP + return imagesFlipWrapper( + makeImageEditor(), + { imageId: 'img-1', horizontal: false, vertical: false }, + { changeMode: 'direct' }, + ); + }, + applyCase: () => + imagesFlipWrapper(makeImageEditor(), { imageId: 'img-1', horizontal: true }, { changeMode: 'direct' }), + }, + 'images.crop': { + throwCase: () => + imagesCropWrapper( + makeImageEditor(), + { imageId: 'missing', crop: { left: 10, top: 10, right: 10, bottom: 10 } }, + { changeMode: 'direct' }, + ), + failureCase: () => { + // Transaction produces no change โ†’ NO_OP + const editor = makeImageEditor(); + const tr = (editor.state as unknown as { tr: Record }).tr; + tr.docChanged = false; + tr.steps = []; + return imagesCropWrapper( + editor, + { imageId: 'img-1', crop: { left: 10, top: 5, right: 10, bottom: 5 } }, + { changeMode: 'direct' }, + ); + }, + applyCase: () => + imagesCropWrapper( + makeImageEditor(), + { imageId: 'img-1', crop: { left: 10, top: 5, right: 10, bottom: 5 } }, + { changeMode: 'direct' }, + ), + }, + 'images.resetCrop': { + throwCase: () => imagesResetCropWrapper(makeImageEditor(), { imageId: 'missing' }, { changeMode: 'direct' }), + failureCase: () => { + // No crop set โ†’ NO_OP + return imagesResetCropWrapper(makeImageEditor(), { imageId: 'img-1' }, { changeMode: 'direct' }); + }, + applyCase: () => { + // Image with crop data + const editor = makeCaptionImageEditor({ + imageId: 'img-cropped', + extraAttrs: { + clipPath: 'inset(5% 10% 5% 10%)', + rawSrcRect: { l: '10000', t: '5000', r: '10000', b: '5000' }, + }, + }); + return imagesResetCropWrapper(editor, { imageId: 'img-cropped' }, { changeMode: 'direct' }); + }, + }, + 'images.replaceSource': { + throwCase: () => + imagesReplaceSourceWrapper( + makeImageEditor(), { imageId: 'missing', src: 'data:image/png;base64,abc' }, { changeMode: 'direct' }, ), @@ -6768,6 +7722,246 @@ const dryRunVectors: Partial unknown>> = { return result; }, + // ------------------------------------------------------------------------- + // Content control operations โ€” dryRun vectors + // ------------------------------------------------------------------------- + 'contentControls.appendContent': () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.appendContent({ target: SDT_TARGET, content: 'appended' }, { changeMode: 'direct', dryRun: true }); + }, + 'contentControls.checkbox.setState': () => { + const adapter = createContentControlsAdapter( + makeSdtEditor({ + controlType: 'checkbox', + type: 'checkbox', + sdtPr: { elements: [], 'w14:checkbox': { 'w14:checked': '0' } }, + }), + ); + return adapter.checkbox.setState({ target: SDT_TARGET, checked: true }, { changeMode: 'direct', dryRun: true }); + }, + 'contentControls.checkbox.toggle': () => { + const adapter = createContentControlsAdapter( + makeSdtEditor({ + controlType: 'checkbox', + type: 'checkbox', + sdtPr: { elements: [], 'w14:checkbox': { 'w14:checked': '0' } }, + }), + ); + return adapter.checkbox.toggle({ target: SDT_TARGET }, { changeMode: 'direct', dryRun: true }); + }, + 'contentControls.checkbox.setSymbolPair': () => { + const adapter = createContentControlsAdapter( + makeSdtEditor({ + controlType: 'checkbox', + type: 'checkbox', + sdtPr: { elements: [], 'w14:checkbox': { 'w14:checked': '0' } }, + }), + ); + return adapter.checkbox.setSymbolPair( + { + target: SDT_TARGET, + checkedSymbol: { font: 'Wingdings', char: '00FE' }, + uncheckedSymbol: { font: 'Wingdings', char: '00A8' }, + }, + { changeMode: 'direct', dryRun: true }, + ); + }, + 'contentControls.choiceList.setItems': () => { + const adapter = createContentControlsAdapter( + makeSdtEditor({ + controlType: 'comboBox', + type: 'comboBox', + sdtPr: { elements: [], 'w:comboBox': { 'w:listItem': [] } }, + }), + ); + return adapter.choiceList.setItems( + { target: SDT_TARGET, items: [{ displayText: 'A', value: 'a' }] }, + { changeMode: 'direct', dryRun: true }, + ); + }, + 'contentControls.choiceList.setSelected': () => { + const adapter = createContentControlsAdapter( + makeSdtEditor({ + controlType: 'comboBox', + type: 'comboBox', + sdtPr: { elements: [], 'w:comboBox': { 'w:listItem': [] } }, + }), + ); + return adapter.choiceList.setSelected({ target: SDT_TARGET, value: 'a' }, { changeMode: 'direct', dryRun: true }); + }, + 'contentControls.clearBinding': () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.clearBinding({ target: SDT_TARGET }, { changeMode: 'direct', dryRun: true }); + }, + 'contentControls.clearContent': () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.clearContent({ target: SDT_TARGET }, { changeMode: 'direct', dryRun: true }); + }, + 'contentControls.copy': () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.copy({ target: SDT_TARGET, destination: SDT_TARGET }, { changeMode: 'direct', dryRun: true }); + }, + 'contentControls.date.clearValue': () => { + const adapter = createContentControlsAdapter(makeSdtEditor({ controlType: 'date', type: 'date' })); + return adapter.date.clearValue({ target: SDT_TARGET }, { changeMode: 'direct', dryRun: true }); + }, + 'contentControls.date.setCalendar': () => { + const adapter = createContentControlsAdapter(makeSdtEditor({ controlType: 'date', type: 'date' })); + return adapter.date.setCalendar( + { target: SDT_TARGET, calendar: 'gregorian' }, + { changeMode: 'direct', dryRun: true }, + ); + }, + 'contentControls.date.setDisplayFormat': () => { + const adapter = createContentControlsAdapter(makeSdtEditor({ controlType: 'date', type: 'date' })); + return adapter.date.setDisplayFormat( + { target: SDT_TARGET, format: 'yyyy-MM-dd' }, + { changeMode: 'direct', dryRun: true }, + ); + }, + 'contentControls.date.setDisplayLocale': () => { + const adapter = createContentControlsAdapter(makeSdtEditor({ controlType: 'date', type: 'date' })); + return adapter.date.setDisplayLocale( + { target: SDT_TARGET, locale: 'en-US' }, + { changeMode: 'direct', dryRun: true }, + ); + }, + 'contentControls.date.setStorageFormat': () => { + const adapter = createContentControlsAdapter(makeSdtEditor({ controlType: 'date', type: 'date' })); + return adapter.date.setStorageFormat( + { target: SDT_TARGET, format: 'xsd:dateTime' }, + { changeMode: 'direct', dryRun: true }, + ); + }, + 'contentControls.date.setValue': () => { + const adapter = createContentControlsAdapter(makeSdtEditor({ controlType: 'date', type: 'date' })); + return adapter.date.setValue({ target: SDT_TARGET, value: '2024-01-01' }, { changeMode: 'direct', dryRun: true }); + }, + 'contentControls.delete': () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.delete({ target: SDT_TARGET }, { changeMode: 'direct', dryRun: true }); + }, + 'contentControls.group.ungroup': () => { + const adapter = createContentControlsAdapter(makeSdtEditor({ controlType: 'group', type: 'group' })); + return adapter.group.ungroup({ target: SDT_TARGET }, { changeMode: 'direct', dryRun: true }); + }, + 'contentControls.group.wrap': () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.group.wrap({ target: SDT_TARGET }, { changeMode: 'direct', dryRun: true }); + }, + 'contentControls.insertAfter': () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.insertAfter({ target: SDT_TARGET, content: 'after' }, { changeMode: 'direct', dryRun: true }); + }, + 'contentControls.insertBefore': () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.insertBefore({ target: SDT_TARGET, content: 'before' }, { changeMode: 'direct', dryRun: true }); + }, + 'contentControls.move': () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.move({ target: SDT_TARGET, destination: SDT_TARGET }, { changeMode: 'direct', dryRun: true }); + }, + 'contentControls.normalizeTagPayload': () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.normalizeTagPayload({ target: SDT_TARGET }, { changeMode: 'direct', dryRun: true }); + }, + 'contentControls.normalizeWordCompatibility': () => { + const adapter = createContentControlsAdapter(makeSdtEditor({ id: 'not-a-number-id' })); + return adapter.normalizeWordCompatibility( + { target: { kind: 'block', nodeType: 'sdt', nodeId: 'not-a-number-id' } }, + { changeMode: 'direct', dryRun: true }, + ); + }, + 'contentControls.patch': () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.patch({ target: SDT_TARGET, alias: 'New Alias' }, { changeMode: 'direct', dryRun: true }); + }, + 'contentControls.patchRawProperties': () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.patchRawProperties( + { target: SDT_TARGET, patches: [{ op: 'set', name: 'w:tag', element: { val: 'x' } }] }, + { changeMode: 'direct', dryRun: true }, + ); + }, + 'contentControls.prependContent': () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.prependContent({ target: SDT_TARGET, content: 'prepended' }, { changeMode: 'direct', dryRun: true }); + }, + 'contentControls.repeatingSection.cloneItem': () => { + const adapter = createContentControlsAdapter(makeSdtEditorWithRepeatingSectionItems()); + return adapter.repeatingSection.cloneItem({ target: RS_TARGET, index: 0 }, { changeMode: 'direct', dryRun: true }); + }, + 'contentControls.repeatingSection.deleteItem': () => { + const adapter = createContentControlsAdapter(makeSdtEditorWithRepeatingSectionItems()); + return adapter.repeatingSection.deleteItem({ target: RS_TARGET, index: 0 }, { changeMode: 'direct', dryRun: true }); + }, + 'contentControls.repeatingSection.insertItemAfter': () => { + const adapter = createContentControlsAdapter(makeSdtEditorWithRepeatingSectionItems()); + return adapter.repeatingSection.insertItemAfter( + { target: RS_TARGET, index: 0 }, + { changeMode: 'direct', dryRun: true }, + ); + }, + 'contentControls.repeatingSection.insertItemBefore': () => { + const adapter = createContentControlsAdapter(makeSdtEditorWithRepeatingSectionItems()); + return adapter.repeatingSection.insertItemBefore( + { target: RS_TARGET, index: 0 }, + { changeMode: 'direct', dryRun: true }, + ); + }, + 'contentControls.repeatingSection.setAllowInsertDelete': () => { + const adapter = createContentControlsAdapter( + makeSdtEditor({ controlType: 'repeatingSection', type: 'repeatingSection' }), + ); + return adapter.repeatingSection.setAllowInsertDelete( + { target: SDT_TARGET, allow: true }, + { changeMode: 'direct', dryRun: true }, + ); + }, + 'contentControls.replaceContent': () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.replaceContent({ target: SDT_TARGET, content: 'replaced' }, { changeMode: 'direct', dryRun: true }); + }, + 'contentControls.setBinding': () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.setBinding( + { target: SDT_TARGET, storeItemId: 'store-1', xpath: '/root' }, + { changeMode: 'direct', dryRun: true }, + ); + }, + 'contentControls.setLockMode': () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.setLockMode({ target: SDT_TARGET, lockMode: 'locked' }, { changeMode: 'direct', dryRun: true }); + }, + 'contentControls.setType': () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.setType({ target: SDT_TARGET, controlType: 'date' }, { changeMode: 'direct', dryRun: true }); + }, + 'contentControls.text.clearValue': () => { + const adapter = createContentControlsAdapter(makeSdtEditor({ controlType: 'text', type: 'text' })); + return adapter.text.clearValue({ target: SDT_TARGET }, { changeMode: 'direct', dryRun: true }); + }, + 'contentControls.text.setMultiline': () => { + const adapter = createContentControlsAdapter(makeSdtEditor({ controlType: 'text', type: 'text' })); + return adapter.text.setMultiline({ target: SDT_TARGET, multiline: true }, { changeMode: 'direct', dryRun: true }); + }, + 'contentControls.text.setValue': () => { + const adapter = createContentControlsAdapter(makeSdtEditor({ controlType: 'text', type: 'text' })); + return adapter.text.setValue({ target: SDT_TARGET, value: 'hello' }, { changeMode: 'direct', dryRun: true }); + }, + 'contentControls.unwrap': () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.unwrap({ target: SDT_TARGET }, { changeMode: 'direct', dryRun: true }); + }, + 'contentControls.wrap': () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.wrap({ target: SDT_TARGET, kind: 'block' }, { changeMode: 'direct', dryRun: true }); + }, + 'create.contentControl': () => { + const adapter = createContentControlsAdapter(makeSdtEditor()); + return adapter.create({ kind: 'block' }, { changeMode: 'direct', dryRun: true }); + }, + // ------------------------------------------------------------------------- // SD-2100: Image geometry, content, semantic & caption โ€” dryRun vectors // ------------------------------------------------------------------------- @@ -6967,7 +8161,7 @@ describe('document-api adapter conformance', () => { const vector = mutationVectors[operationId]; expect(typeof vector?.throwCase, `${operationId} is missing throwCase`).toBe('function'); expect(typeof vector?.applyCase, `${operationId} is missing applyCase`).toBe('function'); - if (HAS_STRUCTURED_FAILURE_RESULT(operationId)) { + if (HAS_STRUCTURED_FAILURE_RESULT(operationId) && !CC_DIRECT_DISPATCH_OPS.has(operationId)) { expect(typeof vector?.failureCase, `${operationId} is missing failureCase`).toBe('function'); } } @@ -7008,6 +8202,7 @@ describe('document-api adapter conformance', () => { !STUB_TABLE_OPS.has(id) && !PLAN_ENGINE_META_OPS.has(id) && !NON_RECEIPT_MUTATION_OPS.has(id) && + !CC_DIRECT_DISPATCH_OPS.has(id) && HAS_STRUCTURED_FAILURE_RESULT(id), ); for (const operationId of implementedMutatingOps) { diff --git a/packages/super-editor/src/document-api-adapters/assemble-adapters.ts b/packages/super-editor/src/document-api-adapters/assemble-adapters.ts index 36086067d7..e8659d6135 100644 --- a/packages/super-editor/src/document-api-adapters/assemble-adapters.ts +++ b/packages/super-editor/src/document-api-adapters/assemble-adapters.ts @@ -209,6 +209,7 @@ import { hyperlinksPatchWrapper, hyperlinksRemoveWrapper, } from './plan-engine/hyperlinks-wrappers.js'; +import { createContentControlsAdapter } from './plan-engine/content-controls-wrappers.js'; /** * Assembles all document-api adapters for the given editor instance. @@ -221,6 +222,8 @@ export function assembleDocumentApiAdapters(editor: Editor): DocumentApiAdapters initRevision(editor); trackRevisions(editor); + const ccAdapter = createContentControlsAdapter(editor); + return { get: { get: (input) => getAdapter(editor, input), @@ -305,6 +308,7 @@ export function assembleDocumentApiAdapters(editor: Editor): DocumentApiAdapters sectionBreak: (input, options) => createSectionBreakAdapter(editor, input, options), tableOfContents: (input, options) => createTableOfContentsWrapper(editor, input, options), image: (input, options) => createImageWrapper(editor, input, options), + contentControl: (input, options) => ccAdapter.create(input, options), }, lists: { list: (query) => listsListWrapper(editor, query), @@ -455,6 +459,7 @@ export function assembleDocumentApiAdapters(editor: Editor): DocumentApiAdapters patch: (input, options) => hyperlinksPatchWrapper(editor, input, options), remove: (input, options) => hyperlinksRemoveWrapper(editor, input, options), }, + contentControls: ccAdapter, query: { match: (input) => queryMatchAdapter(editor, input), }, diff --git a/packages/super-editor/src/document-api-adapters/errors.ts b/packages/super-editor/src/document-api-adapters/errors.ts index 7708952dac..7cca1fca8d 100644 --- a/packages/super-editor/src/document-api-adapters/errors.ts +++ b/packages/super-editor/src/document-api-adapters/errors.ts @@ -15,7 +15,10 @@ export type DocumentApiAdapterErrorCode = | 'DUPLICATE_ID' | 'INVALID_CONTEXT' | 'RAW_MODE_REQUIRED' - | 'PRESERVE_ONLY_VIOLATION'; + | 'PRESERVE_ONLY_VIOLATION' + // SD-2070 content controls codes + | 'LOCK_VIOLATION' + | 'TYPE_MISMATCH'; /** * Structured error thrown by document-api adapter functions. @@ -65,6 +68,8 @@ const ADAPTER_TO_SD_CODE: Record = { INVALID_CONTEXT: 'INVALID_CONTEXT', RAW_MODE_REQUIRED: 'RAW_MODE_REQUIRED', PRESERVE_ONLY_VIOLATION: 'PRESERVE_ONLY_VIOLATION', + LOCK_VIOLATION: 'INVALID_CONTEXT', + TYPE_MISMATCH: 'INVALID_PAYLOAD', }; /** diff --git a/packages/super-editor/src/document-api-adapters/helpers/content-controls/index.ts b/packages/super-editor/src/document-api-adapters/helpers/content-controls/index.ts new file mode 100644 index 0000000000..2a5b915e0c --- /dev/null +++ b/packages/super-editor/src/document-api-adapters/helpers/content-controls/index.ts @@ -0,0 +1,46 @@ +/** + * Content controls shared helper modules. + * + * Re-exports from all sub-modules for convenient single-path imports. + */ + +export { + SDT_NODE_NAMES, + SDT_BLOCK_NAME, + SDT_INLINE_NAME, + isSdtNode, + findAllSdtNodes, + resolveSdtByTarget, + type ResolvedSdt, +} from './target-resolution.js'; + +export { + resolveControlType, + resolveLockMode, + resolveAppearance, + resolveBinding, + buildTarget, + buildContentControlInfoFromNode, + buildContentControlInfoFromAttrs, + readCheckboxChecked, + readChoiceListData, +} from './sdt-info-builder.js'; + +export { assertNotSdtLocked, assertNotContentLocked, assertControlType } from './lock-enforcement.js'; + +export { buildMutationSuccess, buildMutationFailure, applyPagination } from './result-builders.js'; + +export { + applyAttrsUpdate, + updateSdtPrChild, + updateSdtPrChildAttr, + removeSdtPrChildAttr, + updateSdtPrSubElementAttr, + removeSdtPrSubElement, + replaceSdtPrSubElements, + findSdtPrChild, + getSdtPrChildAttrs, + upsertSdtPrChild, + removeSdtPrChild, + type SdtPrElement, +} from './sdt-properties-write.js'; diff --git a/packages/super-editor/src/document-api-adapters/helpers/content-controls/lock-enforcement.ts b/packages/super-editor/src/document-api-adapters/helpers/content-controls/lock-enforcement.ts new file mode 100644 index 0000000000..9c8f251f28 --- /dev/null +++ b/packages/super-editor/src/document-api-adapters/helpers/content-controls/lock-enforcement.ts @@ -0,0 +1,70 @@ +/** + * Lock enforcement and type guards for content control mutations. + * + * Centralized lock-check logic used by all mutation wrappers. + * The plan mandates that lock checks happen pre-apply (before PM dispatch), + * throwing LOCK_VIOLATION for locks and TYPE_MISMATCH for type guards. + */ + +import type { ContentControlType } from '@superdoc/document-api'; +import { DocumentApiAdapterError } from '../../errors.js'; +import type { ResolvedSdt } from './target-resolution.js'; +import { resolveControlType, resolveLockMode } from './sdt-info-builder.js'; + +// --------------------------------------------------------------------------- +// Lock assertions +// --------------------------------------------------------------------------- + +/** + * Assert that the SDT wrapper itself is not locked (sdtLocked / sdtContentLocked). + * Used before operations that modify or remove the wrapper (unwrap, delete, move, patch, etc.). + */ +export function assertNotSdtLocked(sdt: ResolvedSdt, operation: string): void { + const mode = resolveLockMode(sdt.node.attrs as Record); + if (mode === 'sdtLocked' || mode === 'sdtContentLocked') { + throw new DocumentApiAdapterError( + 'LOCK_VIOLATION', + `Content control "${sdt.node.attrs.id}" has lock mode "${mode}" which prevents ${operation}.`, + { lockMode: mode, operation }, + ); + } +} + +/** + * Assert that the SDT content is not locked (contentLocked / sdtContentLocked). + * Used before operations that modify content within the wrapper. + */ +export function assertNotContentLocked(sdt: ResolvedSdt, operation: string): void { + const mode = resolveLockMode(sdt.node.attrs as Record); + if (mode === 'contentLocked' || mode === 'sdtContentLocked') { + throw new DocumentApiAdapterError( + 'LOCK_VIOLATION', + `Content control "${sdt.node.attrs.id}" has lock mode "${mode}" which prevents ${operation}.`, + { lockMode: mode, operation }, + ); + } +} + +// --------------------------------------------------------------------------- +// Type guard +// --------------------------------------------------------------------------- + +/** + * Assert that the SDT has an expected control type. + * Throws TYPE_MISMATCH when the actual type does not match. + */ +export function assertControlType( + sdt: ResolvedSdt, + expected: ContentControlType | ContentControlType[], + operation: string, +): void { + const actual = resolveControlType(sdt.node.attrs as Record); + const allowed = Array.isArray(expected) ? expected : [expected]; + if (!allowed.includes(actual)) { + throw new DocumentApiAdapterError( + 'TYPE_MISMATCH', + `Operation "${operation}" requires control type ${allowed.join(' or ')}, but found "${actual}".`, + { expected: allowed, actual, operation }, + ); + } +} diff --git a/packages/super-editor/src/document-api-adapters/helpers/content-controls/result-builders.ts b/packages/super-editor/src/document-api-adapters/helpers/content-controls/result-builders.ts new file mode 100644 index 0000000000..7fbdf1e629 --- /dev/null +++ b/packages/super-editor/src/document-api-adapters/helpers/content-controls/result-builders.ts @@ -0,0 +1,39 @@ +/** + * Mutation result envelope builders for content control operations. + */ + +import type { + ContentControlTarget, + ContentControlMutationResult, + ContentControlsListResult, + ContentControlInfo, + ReceiptFailureCode, +} from '@superdoc/document-api'; + +/** Build a successful mutation result, optionally with an updated reference. */ +export function buildMutationSuccess( + target: ContentControlTarget, + updatedRef?: ContentControlTarget, +): ContentControlMutationResult { + const result: ContentControlMutationResult = { success: true, contentControl: target }; + if (updatedRef) { + result.updatedRef = updatedRef; + } + return result; +} + +/** Build a failed mutation result. */ +export function buildMutationFailure(code: ReceiptFailureCode, message: string): ContentControlMutationResult { + return { success: false, failure: { code, message } }; +} + +/** Apply offset/limit pagination to a list of ContentControlInfo items. */ +export function applyPagination( + items: ContentControlInfo[], + opts?: { offset?: number; limit?: number }, +): ContentControlsListResult { + const total = items.length; + const offset = opts?.offset ?? 0; + const limit = opts?.limit ?? total; + return { items: items.slice(offset, offset + limit), total }; +} diff --git a/packages/super-editor/src/document-api-adapters/helpers/content-controls/sdt-info-builder.ts b/packages/super-editor/src/document-api-adapters/helpers/content-controls/sdt-info-builder.ts new file mode 100644 index 0000000000..6296cd296f --- /dev/null +++ b/packages/super-editor/src/document-api-adapters/helpers/content-controls/sdt-info-builder.ts @@ -0,0 +1,261 @@ +/** + * SDT info builder โ€” constructs ContentControlInfo from ProseMirror node attributes. + * + * Single source of truth for control-type resolution, lock-mode resolution, + * appearance resolution, and the canonical info shape. Used by both + * node-info-mapper.ts and content-controls-wrappers.ts. + */ + +import type { + ContentControlInfo, + ContentControlType, + ContentControlBinding, + ContentControlTarget, + ContentControlProperties, + LockMode, +} from '@superdoc/document-api'; +import type { ResolvedSdt } from './target-resolution.js'; +import { findSdtPrChild, getSdtPrChildAttrs, type SdtPrElement } from './sdt-properties-write.js'; + +// --------------------------------------------------------------------------- +// Enum resolution +// --------------------------------------------------------------------------- + +const VALID_CONTROL_TYPES: readonly string[] = [ + 'text', + 'date', + 'checkbox', + 'comboBox', + 'dropDownList', + 'repeatingSection', + 'repeatingSectionItem', + 'group', +]; + +const VALID_LOCK_MODES: readonly string[] = ['unlocked', 'sdtLocked', 'contentLocked', 'sdtContentLocked']; + +const VALID_APPEARANCES: readonly string[] = ['boundingBox', 'tags', 'hidden']; + +/** Resolve the SDT control type from node attributes. */ +export function resolveControlType(attrs: Record): ContentControlType { + const rawType = attrs.controlType ?? attrs.type; + if (typeof rawType === 'string' && VALID_CONTROL_TYPES.includes(rawType)) { + return rawType as ContentControlType; + } + return 'unknown'; +} + +/** Resolve the lock mode from node attributes. */ +export function resolveLockMode(attrs: Record): LockMode { + const raw = attrs.lockMode; + if (typeof raw === 'string' && VALID_LOCK_MODES.includes(raw)) { + return raw as LockMode; + } + return 'unlocked'; +} + +/** Resolve the visual appearance from node attributes. */ +export function resolveAppearance(attrs: Record): ContentControlInfo['properties']['appearance'] { + const raw = attrs.appearance; + if (typeof raw === 'string' && VALID_APPEARANCES.includes(raw)) { + return raw as ContentControlInfo['properties']['appearance']; + } + return undefined; +} + +/** Extract data binding metadata from the sdtPr passthrough object (XML element form). */ +export function resolveBinding(attrs: Record): ContentControlBinding | undefined { + const sdtPr = attrs.sdtPr as SdtPrElement | undefined; + if (!sdtPr) return undefined; + const bindingAttrs = getSdtPrChildAttrs(sdtPr, 'w:dataBinding'); + if (!bindingAttrs) return undefined; + const storeItemId = bindingAttrs['w:storeItemID'] as string | undefined; + const xpath = bindingAttrs['w:xpath'] as string | undefined; + if (!storeItemId || !xpath) return undefined; + const prefixMappings = bindingAttrs['w:prefixMappings'] as string | undefined; + return { storeItemId, xpath, prefixMappings }; +} + +// --------------------------------------------------------------------------- +// Target builder +// --------------------------------------------------------------------------- + +/** Build a ContentControlTarget from a resolved SDT node. */ +export function buildTarget(sdt: ResolvedSdt): ContentControlTarget { + return { kind: sdt.kind, nodeType: 'sdt', nodeId: String(sdt.node.attrs.id ?? '') }; +} + +// --------------------------------------------------------------------------- +// Info builder +// --------------------------------------------------------------------------- + +// --------------------------------------------------------------------------- +// Shared subtype readers (used by both info-builder and wrapper getters) +// --------------------------------------------------------------------------- + +/** Read the checked state from a checkbox sdtPr element. */ +export function readCheckboxChecked(sdtPr: SdtPrElement | undefined): boolean { + const cbEl = findSdtPrChild(sdtPr, 'w14:checkbox') ?? findSdtPrChild(sdtPr, 'w:checkbox'); + const checkedEl = cbEl?.elements?.find((e) => e.name === 'w14:checked' || e.name === 'w:checked'); + const checkedVal = checkedEl?.attributes?.['w14:val'] ?? checkedEl?.attributes?.['w:val']; + return checkedVal === '1' || checkedVal === 'true' || checkedVal === true; +} + +/** Read choice-list items and selected value from a comboBox/dropDownList sdtPr element. */ +export function readChoiceListData( + sdtPr: SdtPrElement | undefined, + controlType: 'comboBox' | 'dropDownList', +): { items: Array<{ displayText: string; value: string }>; selectedValue: string | undefined } { + const listEl = findSdtPrChild(sdtPr, `w:${controlType}`); + const itemElements = listEl?.elements?.filter((e) => e.name === 'w:listItem') ?? []; + const items = itemElements.map((item) => ({ + displayText: String(item.attributes?.['w:displayText'] ?? ''), + value: String(item.attributes?.['w:value'] ?? ''), + })); + const selectedValue = listEl?.attributes?.['w:lastValue'] as string | undefined; + return { items, selectedValue }; +} + +// --------------------------------------------------------------------------- +// Subtype-specific property extraction from sdtPr passthrough +// --------------------------------------------------------------------------- + +/** Resolve subtype-specific properties from sdtPr in XML element form. */ +function resolveSubtypeProperties( + controlType: ContentControlType, + sdtPr: SdtPrElement | undefined, +): Partial { + if (!sdtPr) return {}; + + switch (controlType) { + case 'text': { + const textAttrs = getSdtPrChildAttrs(sdtPr, 'w:text'); + if (!textAttrs) return {}; + const multiline = textAttrs['w:multiLine']; + return { multiline: multiline === '1' || multiline === 'true' || multiline === true }; + } + case 'date': { + const dateEl = findSdtPrChild(sdtPr, 'w:date'); + if (!dateEl) return {}; + const fmtEl = findSdtPrChild(dateEl, 'w:dateFormat'); + const lidEl = findSdtPrChild(dateEl, 'w:lid'); + const storageEl = findSdtPrChild(dateEl, 'w:storeMappedDataAs'); + const calendarEl = findSdtPrChild(dateEl, 'w:calendar'); + return { + dateFormat: fmtEl?.attributes?.['w:val'] as string | undefined, + dateLocale: lidEl?.attributes?.['w:val'] as string | undefined, + storageFormat: storageEl?.attributes?.['w:val'] as string | undefined, + calendar: calendarEl?.attributes?.['w:val'] as string | undefined, + }; + } + case 'checkbox': { + const cbEl = findSdtPrChild(sdtPr, 'w14:checkbox') ?? findSdtPrChild(sdtPr, 'w:checkbox'); + if (!cbEl) return {}; + const checkedState = cbEl.elements?.find((e) => e.name === 'w14:checkedState'); + const uncheckedState = cbEl.elements?.find((e) => e.name === 'w14:uncheckedState'); + return { + checked: readCheckboxChecked(sdtPr), + checkedSymbol: checkedState + ? { + font: String(checkedState.attributes?.['w14:font'] ?? ''), + char: String(checkedState.attributes?.['w14:val'] ?? ''), + } + : undefined, + uncheckedSymbol: uncheckedState + ? { + font: String(uncheckedState.attributes?.['w14:font'] ?? ''), + char: String(uncheckedState.attributes?.['w14:val'] ?? ''), + } + : undefined, + }; + } + case 'comboBox': + case 'dropDownList': + return readChoiceListData(sdtPr, controlType); + case 'repeatingSection': { + const rsEl = findSdtPrChild(sdtPr, 'w15:repeatingSection') ?? findSdtPrChild(sdtPr, 'w:repeatingSection'); + if (!rsEl) return {}; + const allowEl = rsEl.elements?.find( + (e) => e.name === 'w15:allowInsertDeleteSection' || e.name === 'w:allowInsertDeleteSection', + ); + const allow = allowEl?.attributes?.['w15:val'] ?? allowEl?.attributes?.['w:val']; + return { allowInsertDelete: allow === '1' || allow === 'true' || allow === true }; + } + default: + return {}; + } +} + +/** Build the full ContentControlInfo shape from a resolved SDT node. */ +export function buildContentControlInfoFromNode(sdt: ResolvedSdt): ContentControlInfo { + const attrs = sdt.node.attrs as Record; + const id = String(attrs.id ?? ''); + const controlType = resolveControlType(attrs); + const lockMode = resolveLockMode(attrs); + const sdtPr = typeof attrs.sdtPr === 'object' && attrs.sdtPr !== null ? (attrs.sdtPr as SdtPrElement) : undefined; + + let text: string | undefined; + try { + text = sdt.node.textContent || undefined; + } catch { + text = undefined; + } + + const subtypeProps = resolveSubtypeProperties(controlType, sdtPr); + + return { + nodeType: 'sdt', + kind: sdt.kind, + id, + controlType, + lockMode, + properties: { + tag: typeof attrs.tag === 'string' ? attrs.tag : undefined, + alias: typeof attrs.alias === 'string' ? attrs.alias : undefined, + appearance: resolveAppearance(attrs), + placeholder: typeof attrs.placeholder === 'string' ? attrs.placeholder : undefined, + color: typeof attrs.color === 'string' ? attrs.color : undefined, + showingPlaceholder: typeof attrs.showingPlaceholder === 'boolean' ? attrs.showingPlaceholder : undefined, + temporary: typeof attrs.temporary === 'boolean' ? attrs.temporary : undefined, + tabIndex: typeof attrs.tabIndex === 'number' ? attrs.tabIndex : undefined, + ...subtypeProps, + }, + binding: resolveBinding(attrs), + raw: sdtPr, + target: { kind: sdt.kind, nodeType: 'sdt', nodeId: id }, + text, + }; +} + +/** + * Build a minimal ContentControlInfo from raw node attributes and kind. + * Used by node-info-mapper.ts when no ResolvedSdt is available. + */ +export function buildContentControlInfoFromAttrs( + attrs: Record | undefined, + kind: 'block' | 'inline', +): ContentControlInfo { + const safeAttrs = attrs ?? {}; + const id = String(safeAttrs.id ?? ''); + const controlType = resolveControlType(safeAttrs); + const lockMode = resolveLockMode(safeAttrs); + + return { + nodeType: 'sdt', + kind, + id, + controlType, + lockMode, + properties: { + tag: typeof safeAttrs.tag === 'string' ? safeAttrs.tag : undefined, + alias: typeof safeAttrs.alias === 'string' ? safeAttrs.alias : undefined, + appearance: resolveAppearance(safeAttrs), + placeholder: typeof safeAttrs.placeholder === 'string' ? safeAttrs.placeholder : undefined, + color: typeof safeAttrs.color === 'string' ? safeAttrs.color : undefined, + showingPlaceholder: typeof safeAttrs.showingPlaceholder === 'boolean' ? safeAttrs.showingPlaceholder : undefined, + temporary: typeof safeAttrs.temporary === 'boolean' ? safeAttrs.temporary : undefined, + tabIndex: typeof safeAttrs.tabIndex === 'number' ? safeAttrs.tabIndex : undefined, + }, + target: { kind, nodeType: 'sdt', nodeId: id }, + }; +} diff --git a/packages/super-editor/src/document-api-adapters/helpers/content-controls/sdt-properties-write.ts b/packages/super-editor/src/document-api-adapters/helpers/content-controls/sdt-properties-write.ts new file mode 100644 index 0000000000..16160b568f --- /dev/null +++ b/packages/super-editor/src/document-api-adapters/helpers/content-controls/sdt-properties-write.ts @@ -0,0 +1,209 @@ +/** + * SDT property mutation helpers โ€” writes to sdtPr children and node attributes. + * + * sdtPr is stored in XML-JSON element form: + * { name: 'w:sdtPr', elements: [ { name: 'w:id', attributes: {...} }, ... ] } + * + * All helpers here operate on the `elements` array to stay consistent with + * the importer (handle-structured-content-node.js) and exporter + * (translate-structured-content.js). + */ + +import type { Editor } from '../../../core/Editor.js'; +import type { ContentControlTarget } from '@superdoc/document-api'; +import { resolveSdtByTarget } from './target-resolution.js'; + +// --------------------------------------------------------------------------- +// XML element helpers for sdtPr.elements +// --------------------------------------------------------------------------- + +interface SdtPrElement { + name: string; + type?: string; + attributes?: Record; + elements?: SdtPrElement[]; + [key: string]: unknown; +} + +/** Find a child element by name within sdtPr.elements. */ +export function findSdtPrChild(sdtPr: SdtPrElement | undefined, childName: string): SdtPrElement | undefined { + return sdtPr?.elements?.find((el) => el.name === childName); +} + +/** Get the attributes object of a named sdtPr child element. */ +export function getSdtPrChildAttrs( + sdtPr: SdtPrElement | undefined, + childName: string, +): Record | undefined { + return findSdtPrChild(sdtPr, childName)?.attributes as Record | undefined; +} + +/** + * Clone sdtPr and upsert a child element by name. + * If a child with the given name exists, it is replaced. Otherwise it is appended. + * Returns the new sdtPr object (immutable update). + */ +function upsertSdtPrChild(sdtPr: SdtPrElement, childName: string, replacement: SdtPrElement): SdtPrElement { + const elements = sdtPr.elements ? [...sdtPr.elements] : []; + const idx = elements.findIndex((el) => el.name === childName); + if (idx >= 0) { + elements[idx] = replacement; + } else { + elements.push(replacement); + } + return { ...sdtPr, elements }; +} + +/** + * Clone sdtPr and remove a child element by name. + * Returns the new sdtPr object (immutable update). + */ +function removeSdtPrChild(sdtPr: SdtPrElement, childName: string): SdtPrElement { + if (!sdtPr.elements) return sdtPr; + return { ...sdtPr, elements: sdtPr.elements.filter((el) => el.name !== childName) }; +} + +// --------------------------------------------------------------------------- +// Attribute update +// --------------------------------------------------------------------------- + +/** + * Apply an attribute patch to an SDT node via updateStructuredContentById. + * Returns true if the command executed successfully. + */ +export function applyAttrsUpdate(editor: Editor, nodeId: string, attrsPatch: Record): boolean { + const updateCmd = editor.commands?.updateStructuredContentById; + if (typeof updateCmd !== 'function') return false; + return Boolean(updateCmd(nodeId, { attrs: attrsPatch })); +} + +// --------------------------------------------------------------------------- +// sdtPr child update (XML-element-form aware) +// --------------------------------------------------------------------------- + +/** + * Resolve the current sdtPr, apply an updater that mutates a named child + * element, then write the entire sdtPr back via attrs update. + * + * The `updater` receives the current child element (or undefined) and returns + * the replacement element. Return `null` to remove the child. + */ +export function updateSdtPrChild( + editor: Editor, + target: ContentControlTarget, + childName: string, + updater: (child: SdtPrElement | undefined) => SdtPrElement | null, +): boolean { + const resolved = resolveSdtByTarget(editor.state.doc, target); + const currentSdtPr = (resolved.node.attrs.sdtPr ?? { name: 'w:sdtPr', elements: [] }) as SdtPrElement; + const existingChild = findSdtPrChild(currentSdtPr, childName); + const replacement = updater(existingChild); + + let newSdtPr: SdtPrElement; + if (replacement === null) { + newSdtPr = removeSdtPrChild(currentSdtPr, childName); + } else { + newSdtPr = upsertSdtPrChild(currentSdtPr, childName, replacement); + } + + return applyAttrsUpdate(editor, target.nodeId, { sdtPr: newSdtPr }); +} + +/** + * Convenience: set an attribute on a named sdtPr child element. + * Creates the child element if it doesn't exist. + */ +export function updateSdtPrChildAttr( + editor: Editor, + target: ContentControlTarget, + childName: string, + attrName: string, + value: string, +): boolean { + return updateSdtPrChild(editor, target, childName, (existing) => ({ + name: childName, + type: 'element', + ...existing, + attributes: { ...(existing?.attributes ?? {}), [attrName]: value }, + })); +} + +/** + * Convenience: remove an attribute from a named sdtPr child element. + */ +export function removeSdtPrChildAttr( + editor: Editor, + target: ContentControlTarget, + childName: string, + attrName: string, +): boolean { + return updateSdtPrChild(editor, target, childName, (existing) => { + if (!existing) return null; + const attrs = { ...(existing.attributes ?? {}) }; + delete attrs[attrName]; + return { ...existing, attributes: attrs }; + }); +} + +/** + * Convenience: find or create a sub-element within a sdtPr child, + * then set an attribute on it. Commonly used for nested structures like + * w:date > w:dateFormat, w14:checkbox > w14:checked, etc. + */ +export function updateSdtPrSubElementAttr( + editor: Editor, + target: ContentControlTarget, + childName: string, + subName: string, + attrName: string, + value: string, +): boolean { + return updateSdtPrChild(editor, target, childName, (existing) => { + const el: SdtPrElement = existing ?? { name: childName, type: 'element', elements: [] }; + const elements = el.elements ? [...el.elements] : []; + const idx = elements.findIndex((e) => e.name === subName); + const subEl: SdtPrElement = { name: subName, type: 'element', attributes: { [attrName]: value } }; + if (idx >= 0) { + elements[idx] = { ...elements[idx], attributes: { ...(elements[idx].attributes ?? {}), [attrName]: value } }; + } else { + elements.push(subEl); + } + return { ...el, elements }; + }); +} + +/** + * Convenience: remove a sub-element from a sdtPr child by name. + */ +export function removeSdtPrSubElement( + editor: Editor, + target: ContentControlTarget, + childName: string, + subName: string, +): boolean { + return updateSdtPrChild(editor, target, childName, (existing) => { + if (!existing?.elements) return existing ?? null; + return { ...existing, elements: existing.elements.filter((e) => e.name !== subName) }; + }); +} + +/** + * Convenience: replace all sub-elements within a sdtPr child. + * Useful for updating choice list items or checkbox symbol pairs. + */ +export function replaceSdtPrSubElements( + editor: Editor, + target: ContentControlTarget, + childName: string, + elements: SdtPrElement[], +): boolean { + return updateSdtPrChild(editor, target, childName, (existing) => ({ + name: childName, + type: 'element', + ...existing, + elements, + })); +} + +// Re-export element helpers for use in patchRawProperties and resolvers +export { upsertSdtPrChild, removeSdtPrChild, type SdtPrElement }; diff --git a/packages/super-editor/src/document-api-adapters/helpers/content-controls/target-resolution.ts b/packages/super-editor/src/document-api-adapters/helpers/content-controls/target-resolution.ts new file mode 100644 index 0000000000..18cb8f72e2 --- /dev/null +++ b/packages/super-editor/src/document-api-adapters/helpers/content-controls/target-resolution.ts @@ -0,0 +1,74 @@ +/** + * SDT target resolution โ€” finding and resolving content control nodes + * in the ProseMirror document tree. + * + * Shared by content-controls-wrappers.ts and node-info-mapper.ts. + */ + +import type { Node as ProseMirrorNode } from 'prosemirror-model'; +import type { ContentControlTarget } from '@superdoc/document-api'; +import { DocumentApiAdapterError } from '../../errors.js'; + +// --------------------------------------------------------------------------- +// Constants +// --------------------------------------------------------------------------- + +export const SDT_NODE_NAMES = ['structuredContent', 'structuredContentBlock'] as const; +export const SDT_BLOCK_NAME = 'structuredContentBlock'; +export const SDT_INLINE_NAME = 'structuredContent'; + +// --------------------------------------------------------------------------- +// Types +// --------------------------------------------------------------------------- + +export interface ResolvedSdt { + node: ProseMirrorNode; + pos: number; + kind: 'block' | 'inline'; +} + +// --------------------------------------------------------------------------- +// Core resolution functions +// --------------------------------------------------------------------------- + +/** Check whether a ProseMirror node is an SDT (structuredContent or structuredContentBlock). */ +export function isSdtNode(node: ProseMirrorNode): boolean { + return SDT_NODE_NAMES.includes(node.type.name as (typeof SDT_NODE_NAMES)[number]); +} + +/** Find all SDT nodes in the document in document order. */ +export function findAllSdtNodes(doc: ProseMirrorNode): ResolvedSdt[] { + const results: ResolvedSdt[] = []; + doc.descendants((node: ProseMirrorNode, pos: number) => { + if (isSdtNode(node)) { + results.push({ + node, + pos, + kind: node.type.name === SDT_BLOCK_NAME ? 'block' : 'inline', + }); + } + return true; + }); + return results; +} + +/** + * Resolve exactly one SDT node by its target. Throws TARGET_NOT_FOUND if + * missing or AMBIGUOUS_TARGET if multiple nodes share the same id. + */ +export function resolveSdtByTarget(doc: ProseMirrorNode, target: ContentControlTarget): ResolvedSdt { + const nodeId = target.nodeId; + const all = findAllSdtNodes(doc); + const matches = all.filter((sdt) => String(sdt.node.attrs.id) === nodeId); + + if (matches.length === 0) { + throw new DocumentApiAdapterError('TARGET_NOT_FOUND', `Content control with id "${nodeId}" not found.`, { target }); + } + if (matches.length > 1) { + throw new DocumentApiAdapterError('AMBIGUOUS_TARGET', `Multiple content controls found with id "${nodeId}".`, { + target, + count: matches.length, + }); + } + return matches[0]; +} diff --git a/packages/super-editor/src/document-api-adapters/helpers/node-info-mapper.ts b/packages/super-editor/src/document-api-adapters/helpers/node-info-mapper.ts index ea857a8656..a93b2c58c7 100644 --- a/packages/super-editor/src/document-api-adapters/helpers/node-info-mapper.ts +++ b/packages/super-editor/src/document-api-adapters/helpers/node-info-mapper.ts @@ -20,7 +20,6 @@ import type { ParagraphNodeInfo, ParagraphProperties, RunNodeInfo, - SdtNodeInfo, TabNodeInfo, TableCellNodeInfo, TableNodeInfo, @@ -36,6 +35,7 @@ import type { TableMeasurement, } from '../../extensions/types/node-attributes.js'; import { parseTocInstruction } from '../../core/super-converter/field-references/shared/toc-switches.js'; +import { buildContentControlInfoFromAttrs } from './content-controls/sdt-info-builder.js'; function resolveMeasurement(value: number | TableMeasurement | null | undefined): number | undefined { if (typeof value === 'number') return value; @@ -324,19 +324,6 @@ function buildImageInfo( }; } -function buildSdtInfo(attrs: StructuredContentBlockAttrs | undefined, kind: 'block' | 'inline'): SdtNodeInfo { - const properties = { - tag: attrs?.tag ?? undefined, - alias: attrs?.alias ?? undefined, - }; - - return { - nodeType: 'sdt', - kind, - properties, - }; -} - function mapHyperlinkNode(candidate: InlineCandidate): HyperlinkNodeInfo { const attrs = (candidate.mark?.attrs ?? candidate.attrs ?? {}) as Record; const properties = { @@ -580,7 +567,7 @@ export function mapNodeInfo( } case 'sdt': { const attrs = candidate.node?.attrs as StructuredContentBlockAttrs | undefined; - return buildSdtInfo(attrs, kind); + return buildContentControlInfoFromAttrs(attrs as Record | undefined, kind); } case 'tableOfContents': if (kind !== 'block') diff --git a/packages/super-editor/src/document-api-adapters/plan-engine/content-controls-wrappers.test.ts b/packages/super-editor/src/document-api-adapters/plan-engine/content-controls-wrappers.test.ts new file mode 100644 index 0000000000..c514cafdff --- /dev/null +++ b/packages/super-editor/src/document-api-adapters/plan-engine/content-controls-wrappers.test.ts @@ -0,0 +1,717 @@ +import type { Node as ProseMirrorNode } from 'prosemirror-model'; +import { describe, expect, it, vi } from 'vitest'; +import type { Editor } from '../../core/Editor.js'; +import { registerBuiltInExecutors } from './register-executors.js'; +import { createContentControlsAdapter } from './content-controls-wrappers.js'; +import { + buildContentControlInfoFromNode, + buildContentControlInfoFromAttrs, +} from '../helpers/content-controls/index.js'; + +registerBuiltInExecutors(); + +// --------------------------------------------------------------------------- +// Mock node builder (mirrors conformance-test pattern) +// --------------------------------------------------------------------------- + +type NodeOptions = { + attrs?: Record; + text?: string; + isInline?: boolean; + isBlock?: boolean; + isLeaf?: boolean; + inlineContent?: boolean; + nodeSize?: number; +}; + +function createNode(typeName: string, children: ProseMirrorNode[] = [], options: NodeOptions = {}): ProseMirrorNode { + const attrs = options.attrs ?? {}; + const text = options.text ?? ''; + const isText = typeName === 'text'; + const isInline = options.isInline ?? isText; + const isBlock = options.isBlock ?? (!isInline && typeName !== 'doc'); + const inlineContent = options.inlineContent ?? isBlock; + const isLeaf = options.isLeaf ?? (isInline && !isText && children.length === 0); + + const contentSize = children.reduce((sum, child) => sum + child.nodeSize, 0); + const nodeSize = isText ? text.length : options.nodeSize != null ? options.nodeSize : isLeaf ? 1 : contentSize + 2; + + const node = { + type: { + name: typeName, + create(newAttrs: Record, newContent: unknown) { + return createNode(typeName, [], { attrs: newAttrs, isBlock, inlineContent }); + }, + createAndFill() { + return createNode(typeName, [], { attrs: {}, isBlock, inlineContent }); + }, + }, + attrs, + text: isText ? text : undefined, + content: { size: contentSize }, + nodeSize, + isText, + isInline, + isBlock, + inlineContent, + isTextblock: inlineContent, + isLeaf, + childCount: children.length, + child(index: number) { + return children[index]!; + }, + forEach(fn: (node: ProseMirrorNode, offset: number, index: number) => void) { + let offset = 0; + children.forEach((child, index) => { + fn(child, offset, index); + offset += child.nodeSize; + }); + }, + nodeAt(pos: number): ProseMirrorNode | null { + let offset = 0; + for (const child of children) { + if (pos === offset) return child; + if (pos < offset + child.nodeSize) { + return (child as unknown as { nodeAt: (p: number) => ProseMirrorNode | null }).nodeAt(pos - offset - 1); + } + offset += child.nodeSize; + } + return null; + }, + copy(_content?: unknown) { + return node; + }, + get textContent(): string { + if (isText) return text; + return children.map((c) => c.textContent).join(''); + }, + _children: children, + descendants(callback: (node: ProseMirrorNode, pos: number) => boolean | void) { + function walk(kids: ProseMirrorNode[], startPos: number) { + let offset = startPos; + for (const child of kids) { + const childStart = offset; + const result = callback(child, childStart); + if (result !== false) { + const innerKids = (child as unknown as { _children?: ProseMirrorNode[] })._children; + if (innerKids && innerKids.length > 0) { + walk(innerKids, childStart + 1); + } + } + offset += child.nodeSize; + } + } + walk(children, 0); + }, + }; + return node as unknown as ProseMirrorNode; +} + +// --------------------------------------------------------------------------- +// Mock editor builders +// --------------------------------------------------------------------------- + +const SDT_TARGET = { kind: 'block' as const, nodeType: 'sdt' as const, nodeId: 'sdt-1' }; + +function makeSdtEditor(overrideAttrs: Record = {}): Editor { + const sdtAttrs = { + id: 'sdt-1', + tag: 'test-tag', + alias: 'Test Alias', + lockMode: 'unlocked', + controlType: 'text', + type: 'text', + sdtPr: { name: 'w:sdtPr', elements: [] }, + ...overrideAttrs, + }; + + const textNode = createNode('text', [], { text: 'SDT content' }); + const innerParagraph = createNode('paragraph', [textNode], { + attrs: { sdBlockId: 'inner-p' }, + isBlock: true, + inlineContent: true, + }); + const sdtNode = createNode('structuredContentBlock', [innerParagraph], { + attrs: sdtAttrs, + isBlock: true, + }); + const doc = createNode('doc', [sdtNode], { isBlock: false }); + + const tr = { + insertText: vi.fn().mockReturnThis(), + delete: vi.fn().mockReturnThis(), + addMark: vi.fn().mockReturnThis(), + removeMark: vi.fn().mockReturnThis(), + replaceWith: vi.fn().mockReturnThis(), + insert: vi.fn().mockReturnThis(), + setMeta: vi.fn().mockReturnThis(), + mapping: { map: (pos: number) => pos }, + docChanged: true, + doc, + steps: [{ type: 'replaceStep' }], + }; + + const dispatch = vi.fn(); + + const editor = { + state: { + doc, + tr, + schema: { + marks: {}, + text: (t: string) => createNode('text', [], { text: t }), + nodes: { + paragraph: { + create: vi.fn(() => innerParagraph), + createAndFill: vi.fn(() => innerParagraph), + }, + structuredContentBlock: { + create: vi.fn((attrs: unknown, content: unknown) => + createNode('structuredContentBlock', [], { attrs: attrs as Record, isBlock: true }), + ), + }, + }, + }, + selection: { from: 0, to: doc.nodeSize }, + }, + schema: { + marks: {}, + text: (t: string) => createNode('text', [], { text: t }), + nodes: { + paragraph: { + create: vi.fn(() => innerParagraph), + createAndFill: vi.fn(() => innerParagraph), + }, + structuredContentBlock: { + create: vi.fn((attrs: unknown, content: unknown) => + createNode('structuredContentBlock', [], { attrs: attrs as Record, isBlock: true }), + ), + }, + }, + }, + dispatch, + view: { dispatch }, + commands: { + updateStructuredContentById: vi.fn(() => true), + deleteStructuredContentById: vi.fn(() => true), + insertStructuredContentBlock: vi.fn(() => true), + insertStructuredContentInline: vi.fn(() => true), + }, + } as unknown as Editor; + + return editor; +} + +/** + * Build a doc with two block-level nodes: a paragraph (block ID = paraId) then an SDT. + * Used for listInRange block-ID resolution tests. + */ +function makeSdtEditorWithBlockRange(): Editor { + const paraText = createNode('text', [], { text: 'Block text' }); + const paragraph = createNode('paragraph', [paraText], { + attrs: { paraId: 'block-p1', sdBlockId: 'block-p1-sd' }, + isBlock: true, + inlineContent: true, + }); + + const sdtText = createNode('text', [], { text: 'SDT content' }); + const innerParagraph = createNode('paragraph', [sdtText], { + attrs: { paraId: 'inner-p', sdBlockId: 'inner-p-sd' }, + isBlock: true, + inlineContent: true, + }); + const sdtNode = createNode('structuredContentBlock', [innerParagraph], { + attrs: { + id: 'sdt-1', + tag: 'test', + lockMode: 'unlocked', + controlType: 'text', + type: 'text', + sdtPr: { name: 'w:sdtPr', elements: [] }, + }, + isBlock: true, + }); + + const para2Text = createNode('text', [], { text: 'After SDT' }); + const paragraph2 = createNode('paragraph', [para2Text], { + attrs: { paraId: 'block-p2', sdBlockId: 'block-p2-sd' }, + isBlock: true, + inlineContent: true, + }); + + const doc = createNode('doc', [paragraph, sdtNode, paragraph2], { isBlock: false }); + + const tr = { + insertText: vi.fn().mockReturnThis(), + delete: vi.fn().mockReturnThis(), + addMark: vi.fn().mockReturnThis(), + removeMark: vi.fn().mockReturnThis(), + replaceWith: vi.fn().mockReturnThis(), + insert: vi.fn().mockReturnThis(), + setMeta: vi.fn().mockReturnThis(), + mapping: { map: (pos: number) => pos }, + docChanged: true, + doc, + steps: [{ type: 'replaceStep' }], + }; + + const dispatch = vi.fn(); + + return { + state: { + doc, + tr, + schema: { marks: {}, text: (t: string) => createNode('text', [], { text: t }), nodes: {} }, + selection: { from: 0, to: doc.nodeSize }, + }, + schema: { marks: {}, text: (t: string) => createNode('text', [], { text: t }), nodes: {} }, + dispatch, + view: { dispatch }, + commands: { + updateStructuredContentById: vi.fn(() => true), + deleteStructuredContentById: vi.fn(() => true), + insertStructuredContentBlock: vi.fn(() => true), + insertStructuredContentInline: vi.fn(() => true), + }, + } as unknown as Editor; +} + +// --------------------------------------------------------------------------- +// Tests +// --------------------------------------------------------------------------- + +describe('contentControls.wrap', () => { + it('calls tr.replaceWith to wrap the resolved target node', () => { + const editor = makeSdtEditor(); + const adapter = createContentControlsAdapter(editor); + + const result = adapter.wrap({ target: SDT_TARGET, kind: 'block' }, { changeMode: 'direct' }); + + expect(result.success).toBe(true); + // tr.replaceWith should have been called (wraps the existing node) + expect((editor.state.tr as any).replaceWith).toHaveBeenCalled(); + }); + + it('returns updatedRef pointing to the new wrapper SDT', () => { + const editor = makeSdtEditor(); + const adapter = createContentControlsAdapter(editor); + + const result = adapter.wrap({ target: SDT_TARGET, kind: 'block' }, { changeMode: 'direct' }); + + expect(result.success).toBe(true); + if (result.success) { + expect(result.updatedRef).toBeDefined(); + expect(result.updatedRef!.nodeType).toBe('sdt'); + expect(result.updatedRef!.kind).toBe('block'); + // The updatedRef nodeId should differ from the original (new wrapper ID) + expect(result.updatedRef!.nodeId).not.toBe('sdt-1'); + } + }); + + it('creates the wrapper node via schema.nodes.structuredContentBlock.create', () => { + const editor = makeSdtEditor(); + const adapter = createContentControlsAdapter(editor); + + adapter.wrap({ target: SDT_TARGET, kind: 'block' }, { changeMode: 'direct' }); + + const createFn = editor.schema.nodes.structuredContentBlock.create as ReturnType; + expect(createFn).toHaveBeenCalledTimes(1); + const [attrs] = createFn.mock.calls[0]; + expect(attrs.tag).toBeUndefined(); + expect(attrs.lockMode).toBe('unlocked'); + expect(typeof attrs.id).toBe('string'); + }); +}); + +describe('contentControls.listInRange', () => { + it('filters SDTs by block-ID positional range, not by SDT ID', () => { + const editor = makeSdtEditorWithBlockRange(); + const adapter = createContentControlsAdapter(editor); + + // Range from start of paragraph block-p1 to end of paragraph block-p2 + // should include the SDT that sits between them + const result = adapter.listInRange({ + startBlockId: 'block-p1', + endBlockId: 'block-p2', + }); + + expect(result.items.length).toBe(1); + expect(result.items[0].id).toBe('sdt-1'); + }); + + it('excludes SDTs outside the block range', () => { + const editor = makeSdtEditorWithBlockRange(); + const adapter = createContentControlsAdapter(editor); + + // Range covering only the second paragraph (after the SDT) + const result = adapter.listInRange({ + startBlockId: 'block-p2', + endBlockId: 'block-p2', + }); + + expect(result.items.length).toBe(0); + }); + + it('throws TARGET_NOT_FOUND for invalid block IDs', () => { + const editor = makeSdtEditorWithBlockRange(); + const adapter = createContentControlsAdapter(editor); + + expect(() => + adapter.listInRange({ + startBlockId: 'nonexistent-block', + endBlockId: 'block-p2', + }), + ).toThrow(/not found/i); + }); +}); + +describe('contentControls.setType OOXML element transitions', () => { + it('calls updateStructuredContentById to persist controlType and type attrs', () => { + const editor = makeSdtEditor({ controlType: 'text', type: 'text' }); + const adapter = createContentControlsAdapter(editor); + + const result = adapter.setType({ target: SDT_TARGET, controlType: 'date' }, { changeMode: 'direct' }); + + expect(result.success).toBe(true); + const updateCmd = editor.commands!.updateStructuredContentById as ReturnType; + // Should be called at least once for the type element transitions + once for attrs + expect(updateCmd).toHaveBeenCalled(); + }); + + it('returns NO_OP when target already has the requested type', () => { + const editor = makeSdtEditor({ controlType: 'text', type: 'text' }); + const adapter = createContentControlsAdapter(editor); + + const result = adapter.setType({ target: SDT_TARGET, controlType: 'text' }, { changeMode: 'direct' }); + + expect(result.success).toBe(false); + if (!result.success) { + expect(result.failure.code).toBe('NO_OP'); + } + }); + + it('removes old type element and adds new type element in sdtPr', () => { + // Start with a 'text' control that has a w:text element in sdtPr + const sdtPr = { + name: 'w:sdtPr', + elements: [{ name: 'w:text', type: 'element' }], + }; + const editor = makeSdtEditor({ controlType: 'text', type: 'text', sdtPr }); + const adapter = createContentControlsAdapter(editor); + + adapter.setType({ target: SDT_TARGET, controlType: 'date' }, { changeMode: 'direct' }); + + // updateStructuredContentById is called 3 times: + // 1) remove old w:text element from sdtPr + // 2) add new w:date element to sdtPr + // 3) update controlType/type attrs + const updateCmd = editor.commands!.updateStructuredContentById as ReturnType; + expect(updateCmd.mock.calls.length).toBeGreaterThanOrEqual(2); + + // Verify the final attrs update includes the new type + const lastCall = updateCmd.mock.calls[updateCmd.mock.calls.length - 1]; + expect(lastCall[1].attrs.controlType).toBe('date'); + expect(lastCall[1].attrs.type).toBe('date'); + }); +}); + +describe('contentControls.patchRawProperties element normalization', () => { + it('normalizes set op elements to include name and type', () => { + const editor = makeSdtEditor(); + const adapter = createContentControlsAdapter(editor); + + adapter.patchRawProperties( + { + target: SDT_TARGET, + patches: [ + { + op: 'set', + name: 'w:custom', + element: { attributes: { 'w:val': 'hello' } } as any, + }, + ], + }, + { changeMode: 'direct' }, + ); + + const updateCmd = editor.commands!.updateStructuredContentById as ReturnType; + expect(updateCmd).toHaveBeenCalled(); + + // The sdtPr written back should contain the normalized element + const call = updateCmd.mock.calls[updateCmd.mock.calls.length - 1]; + const writtenSdtPr = call[1].attrs.sdtPr; + const customEl = writtenSdtPr.elements.find((el: any) => el.name === 'w:custom'); + expect(customEl).toBeDefined(); + expect(customEl.name).toBe('w:custom'); + expect(customEl.type).toBe('element'); + expect(customEl.attributes['w:val']).toBe('hello'); + }); + + it('set op forces name to match patch.name even if element has a different name', () => { + const editor = makeSdtEditor(); + const adapter = createContentControlsAdapter(editor); + + adapter.patchRawProperties( + { + target: SDT_TARGET, + patches: [ + { + op: 'set', + name: 'w:correct', + element: { name: 'w:wrong', type: 'element' } as any, + }, + ], + }, + { changeMode: 'direct' }, + ); + + const updateCmd = editor.commands!.updateStructuredContentById as ReturnType; + const call = updateCmd.mock.calls[updateCmd.mock.calls.length - 1]; + const writtenSdtPr = call[1].attrs.sdtPr; + const el = writtenSdtPr.elements.find((e: any) => e.name === 'w:correct'); + expect(el).toBeDefined(); + // No element with the wrong name should exist + expect(writtenSdtPr.elements.find((e: any) => e.name === 'w:wrong')).toBeUndefined(); + }); + + it('setAttr modifies attributes on an existing sdtPr element', () => { + const sdtPr = { + name: 'w:sdtPr', + elements: [{ name: 'w:custom', type: 'element', attributes: { 'w:val': 'old' } }], + }; + const editor = makeSdtEditor({ sdtPr }); + const adapter = createContentControlsAdapter(editor); + + adapter.patchRawProperties( + { + target: SDT_TARGET, + patches: [{ op: 'setAttr', name: 'w:custom', attr: 'w:val', value: 'new' }], + }, + { changeMode: 'direct' }, + ); + + const updateCmd = editor.commands!.updateStructuredContentById as ReturnType; + const call = updateCmd.mock.calls[updateCmd.mock.calls.length - 1]; + const writtenSdtPr = call[1].attrs.sdtPr; + const el = writtenSdtPr.elements.find((e: any) => e.name === 'w:custom'); + expect(el.attributes['w:val']).toBe('new'); + }); + + it('removeAttr removes an attribute from an existing sdtPr element', () => { + const sdtPr = { + name: 'w:sdtPr', + elements: [{ name: 'w:custom', type: 'element', attributes: { 'w:val': 'x', 'w:other': 'y' } }], + }; + const editor = makeSdtEditor({ sdtPr }); + const adapter = createContentControlsAdapter(editor); + + adapter.patchRawProperties( + { + target: SDT_TARGET, + patches: [{ op: 'removeAttr', name: 'w:custom', attr: 'w:val' }], + }, + { changeMode: 'direct' }, + ); + + const updateCmd = editor.commands!.updateStructuredContentById as ReturnType; + const call = updateCmd.mock.calls[updateCmd.mock.calls.length - 1]; + const writtenSdtPr = call[1].attrs.sdtPr; + const el = writtenSdtPr.elements.find((e: any) => e.name === 'w:custom'); + expect(el.attributes['w:val']).toBeUndefined(); + expect(el.attributes['w:other']).toBe('y'); + }); + + it('remove op deletes an element from sdtPr.elements', () => { + const sdtPr = { + name: 'w:sdtPr', + elements: [ + { name: 'w:custom', type: 'element' }, + { name: 'w:other', type: 'element' }, + ], + }; + const editor = makeSdtEditor({ sdtPr }); + const adapter = createContentControlsAdapter(editor); + + adapter.patchRawProperties( + { + target: SDT_TARGET, + patches: [{ op: 'remove', name: 'w:custom' }], + }, + { changeMode: 'direct' }, + ); + + const updateCmd = editor.commands!.updateStructuredContentById as ReturnType; + const call = updateCmd.mock.calls[updateCmd.mock.calls.length - 1]; + const writtenSdtPr = call[1].attrs.sdtPr; + expect(writtenSdtPr.elements.find((e: any) => e.name === 'w:custom')).toBeUndefined(); + expect(writtenSdtPr.elements.find((e: any) => e.name === 'w:other')).toBeDefined(); + }); +}); + +describe('buildContentControlInfoFromNode sdtPr element-form resolution', () => { + it('resolves binding from sdtPr XML element form', () => { + const sdtPr = { + name: 'w:sdtPr', + elements: [ + { + name: 'w:dataBinding', + type: 'element', + attributes: { + 'w:storeItemID': '{store-123}', + 'w:xpath': '/root/field', + 'w:prefixMappings': 'xmlns:ns="http://example.com"', + }, + }, + ], + }; + const sdtNode = createNode('structuredContentBlock', [], { + attrs: { id: 'sdt-b', controlType: 'text', type: 'text', lockMode: 'unlocked', sdtPr }, + isBlock: true, + }); + + const info = buildContentControlInfoFromNode({ node: sdtNode, pos: 0, kind: 'block' }); + expect(info.binding).toBeDefined(); + expect(info.binding!.storeItemId).toBe('{store-123}'); + expect(info.binding!.xpath).toBe('/root/field'); + expect(info.binding!.prefixMappings).toBe('xmlns:ns="http://example.com"'); + }); + + it('resolves date subtype properties from XML element form', () => { + const sdtPr = { + name: 'w:sdtPr', + elements: [ + { + name: 'w:date', + type: 'element', + attributes: { 'w:fullDate': '2024-01-15' }, + elements: [ + { name: 'w:dateFormat', type: 'element', attributes: { 'w:val': 'M/d/yyyy' } }, + { name: 'w:lid', type: 'element', attributes: { 'w:val': 'en-US' } }, + { name: 'w:storeMappedDataAs', type: 'element', attributes: { 'w:val': 'dateTime' } }, + { name: 'w:calendar', type: 'element', attributes: { 'w:val': 'gregorian' } }, + ], + }, + ], + }; + const sdtNode = createNode('structuredContentBlock', [], { + attrs: { id: 'sdt-d', controlType: 'date', type: 'date', lockMode: 'unlocked', sdtPr }, + isBlock: true, + }); + + const info = buildContentControlInfoFromNode({ node: sdtNode, pos: 0, kind: 'block' }); + expect(info.properties.dateFormat).toBe('M/d/yyyy'); + expect(info.properties.dateLocale).toBe('en-US'); + expect(info.properties.storageFormat).toBe('dateTime'); + expect(info.properties.calendar).toBe('gregorian'); + }); + + it('resolves checkbox subtype properties from XML element form', () => { + const sdtPr = { + name: 'w:sdtPr', + elements: [ + { + name: 'w14:checkbox', + type: 'element', + elements: [ + { name: 'w14:checked', type: 'element', attributes: { 'w14:val': '1' } }, + { name: 'w14:checkedState', type: 'element', attributes: { 'w14:font': 'MS Gothic', 'w14:val': '2612' } }, + { name: 'w14:uncheckedState', type: 'element', attributes: { 'w14:font': 'MS Gothic', 'w14:val': '2610' } }, + ], + }, + ], + }; + const sdtNode = createNode('structuredContentBlock', [], { + attrs: { id: 'sdt-cb', controlType: 'checkbox', type: 'checkbox', lockMode: 'unlocked', sdtPr }, + isBlock: true, + }); + + const info = buildContentControlInfoFromNode({ node: sdtNode, pos: 0, kind: 'block' }); + expect(info.properties.checked).toBe(true); + expect(info.properties.checkedSymbol).toEqual({ font: 'MS Gothic', char: '2612' }); + expect(info.properties.uncheckedSymbol).toEqual({ font: 'MS Gothic', char: '2610' }); + }); + + it('resolves comboBox items from XML element form', () => { + const sdtPr = { + name: 'w:sdtPr', + elements: [ + { + name: 'w:comboBox', + type: 'element', + attributes: { 'w:lastValue': 'b' }, + elements: [ + { name: 'w:listItem', type: 'element', attributes: { 'w:displayText': 'Alpha', 'w:value': 'a' } }, + { name: 'w:listItem', type: 'element', attributes: { 'w:displayText': 'Beta', 'w:value': 'b' } }, + ], + }, + ], + }; + const sdtNode = createNode('structuredContentBlock', [], { + attrs: { id: 'sdt-cb', controlType: 'comboBox', type: 'comboBox', lockMode: 'unlocked', sdtPr }, + isBlock: true, + }); + + const info = buildContentControlInfoFromNode({ node: sdtNode, pos: 0, kind: 'block' }); + expect(info.properties.items).toEqual([ + { displayText: 'Alpha', value: 'a' }, + { displayText: 'Beta', value: 'b' }, + ]); + expect(info.properties.selectedValue).toBe('b'); + }); + + it('includes color, showingPlaceholder, temporary, tabIndex from attrs', () => { + const sdtNode = createNode('structuredContentBlock', [], { + attrs: { + id: 'sdt-meta', + controlType: 'text', + type: 'text', + lockMode: 'unlocked', + sdtPr: { name: 'w:sdtPr', elements: [] }, + color: '#FF0000', + showingPlaceholder: true, + temporary: false, + tabIndex: 3, + }, + isBlock: true, + }); + + const info = buildContentControlInfoFromNode({ node: sdtNode, pos: 0, kind: 'block' }); + expect(info.properties.color).toBe('#FF0000'); + expect(info.properties.showingPlaceholder).toBe(true); + expect(info.properties.temporary).toBe(false); + expect(info.properties.tabIndex).toBe(3); + }); +}); + +describe('buildContentControlInfoFromAttrs completeness', () => { + it('includes color, showingPlaceholder, temporary, tabIndex', () => { + const info = buildContentControlInfoFromAttrs( + { + id: 'sdt-x', + controlType: 'text', + lockMode: 'unlocked', + tag: 'my-tag', + color: 'blue', + showingPlaceholder: true, + temporary: true, + tabIndex: 5, + }, + 'block', + ); + + expect(info.properties.color).toBe('blue'); + expect(info.properties.showingPlaceholder).toBe(true); + expect(info.properties.temporary).toBe(true); + expect(info.properties.tabIndex).toBe(5); + expect(info.properties.tag).toBe('my-tag'); + }); + + it('omits metadata fields when attrs do not include them', () => { + const info = buildContentControlInfoFromAttrs({ id: 'sdt-y', controlType: 'text', lockMode: 'unlocked' }, 'inline'); + + expect(info.properties.color).toBeUndefined(); + expect(info.properties.showingPlaceholder).toBeUndefined(); + expect(info.properties.temporary).toBeUndefined(); + expect(info.properties.tabIndex).toBeUndefined(); + }); +}); diff --git a/packages/super-editor/src/document-api-adapters/plan-engine/content-controls-wrappers.ts b/packages/super-editor/src/document-api-adapters/plan-engine/content-controls-wrappers.ts new file mode 100644 index 0000000000..e8d5370153 --- /dev/null +++ b/packages/super-editor/src/document-api-adapters/plan-engine/content-controls-wrappers.ts @@ -0,0 +1,1514 @@ +/** + * Content Controls plan-engine wrappers โ€” bridge the contentControls namespace + * operations to the underlying ProseMirror editor. + * + * Read operations (list, get, selectByTag, etc.) are pure document queries. + * Mutation operations delegate to editor commands via executeDomainCommand + * with revision-guard support. + * + * All mutations are direct-mode-only (supportsTrackedMode: false). + * + * This file is orchestration-only โ€” all shared logic lives in + * helpers/content-controls/*.ts per the DRY architecture plan. + */ + +import { Fragment, type Node as ProseMirrorNode, type Schema } from 'prosemirror-model'; +import type { Editor } from '../../core/Editor.js'; +import type { + ContentControlInfo, + ContentControlMutationResult, + ContentControlsListResult, + ContentControlsListQuery, + ContentControlsGetInput, + ContentControlsListInRangeInput, + ContentControlsSelectByTagInput, + ContentControlsSelectByTitleInput, + ContentControlsListChildrenInput, + ContentControlsGetParentInput, + ContentControlsWrapInput, + ContentControlsUnwrapInput, + ContentControlsDeleteInput, + ContentControlsCopyInput, + ContentControlsMoveInput, + ContentControlsPatchInput, + ContentControlsSetLockModeInput, + ContentControlsSetTypeInput, + ContentControlsGetContentInput, + ContentControlsGetContentResult, + ContentControlsReplaceContentInput, + ContentControlsClearContentInput, + ContentControlsAppendContentInput, + ContentControlsPrependContentInput, + ContentControlsInsertBeforeInput, + ContentControlsInsertAfterInput, + ContentControlsGetBindingInput, + ContentControlBinding, + ContentControlsSetBindingInput, + ContentControlsClearBindingInput, + ContentControlsGetRawPropertiesInput, + ContentControlsGetRawPropertiesResult, + ContentControlsPatchRawPropertiesInput, + ContentControlsValidateWordCompatibilityInput, + ContentControlsValidateWordCompatibilityResult, + ContentControlsNormalizeWordCompatibilityInput, + ContentControlsNormalizeTagPayloadInput, + ContentControlsTextSetMultilineInput, + ContentControlsTextSetValueInput, + ContentControlsTextClearValueInput, + ContentControlsDateSetValueInput, + ContentControlsDateClearValueInput, + ContentControlsDateSetDisplayFormatInput, + ContentControlsDateSetDisplayLocaleInput, + ContentControlsDateSetStorageFormatInput, + ContentControlsDateSetCalendarInput, + ContentControlsCheckboxGetStateInput, + ContentControlsCheckboxGetStateResult, + ContentControlsCheckboxSetStateInput, + ContentControlsCheckboxToggleInput, + ContentControlsCheckboxSetSymbolPairInput, + ContentControlsChoiceListGetItemsInput, + ContentControlsChoiceListGetItemsResult, + ContentControlsChoiceListSetItemsInput, + ContentControlsChoiceListSetSelectedInput, + ContentControlsRepeatingSectionListItemsInput, + ContentControlsRepeatingSectionListItemsResult, + ContentControlsRepeatingSectionInsertItemBeforeInput, + ContentControlsRepeatingSectionInsertItemAfterInput, + ContentControlsRepeatingSectionCloneItemInput, + ContentControlsRepeatingSectionDeleteItemInput, + ContentControlsRepeatingSectionSetAllowInsertDeleteInput, + ContentControlsGroupWrapInput, + ContentControlsGroupUngroupInput, + ContentControlTarget, + ContentControlsAdapter, + ContentControlsCreateAdapter, + MutationOptions, + CreateContentControlInput, +} from '@superdoc/document-api'; +import { DocumentApiAdapterError } from '../errors.js'; +import { executeDomainCommand } from './plan-wrappers.js'; +import { clearIndexCache } from '../helpers/index-cache.js'; + +// Shared helpers โ€” single source of truth for SDT logic +import { + SDT_BLOCK_NAME, + isSdtNode, + findAllSdtNodes, + resolveSdtByTarget, + resolveControlType, + resolveBinding, + readCheckboxChecked, + readChoiceListData, + buildTarget, + buildContentControlInfoFromNode, + assertNotSdtLocked, + assertNotContentLocked, + assertControlType, + buildMutationSuccess, + buildMutationFailure, + applyPagination, + applyAttrsUpdate, + updateSdtPrChild, + updateSdtPrSubElementAttr, + removeSdtPrSubElement, + replaceSdtPrSubElements, + findSdtPrChild, + upsertSdtPrChild, + removeSdtPrChild, + type SdtPrElement, +} from '../helpers/content-controls/index.js'; +import { buildBlockIndex, findBlockByNodeIdOnly } from '../helpers/node-address-resolver.js'; + +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +function generateSdtId(): string { + return String(Math.floor(Math.random() * 2147483647)); +} + +/** Names that are forbidden from patchRawProperties per ยง10 of the plan. */ +const FORBIDDEN_RAW_PATCH_NAMES = new Set([ + 'w:sdtContent', + 'w:id', + 'w:sdtPr', // cannot replace the entire sdtPr wholesale +]); + +/** + * Recursively regenerate IDs for all descendant SDT nodes within a cloned tree. + * The root node's ID is assumed to already be set by the caller. + */ +function reIdDescendantSdts(node: ProseMirrorNode, schema: Schema): ProseMirrorNode { + if (node.childCount === 0) return node; + + const children: ProseMirrorNode[] = []; + let changed = false; + + node.forEach((child) => { + let result = child; + if (isSdtNode(child)) { + result = child.type.create({ ...child.attrs, id: generateSdtId() }, child.content, child.marks); + changed = true; + } + const recursed = reIdDescendantSdts(result, schema); + if (recursed !== result) changed = true; + children.push(recursed); + }); + + if (!changed) return node; + return node.copy(Fragment.from(children)); +} + +// --------------------------------------------------------------------------- +// Mutation execution helper +// --------------------------------------------------------------------------- + +/** + * Execute an SDT mutation with dryRun / changeMode guards. + * + * The handler returns `boolean` for simple mutations, or a `ContentControlTarget` + * when the mutation produces a new identity (copy, move, group.wrap, etc.), + * which is surfaced as `updatedRef` on the success envelope. + */ +function executeSdtMutation( + editor: Editor, + target: ContentControlTarget, + options: MutationOptions | undefined, + handler: () => boolean | ContentControlTarget, +): ContentControlMutationResult { + const mode = options?.changeMode ?? 'direct'; + if (mode !== 'direct') { + throw new DocumentApiAdapterError( + 'CAPABILITY_UNAVAILABLE', + `Content control mutations only support changeMode "direct", got "${mode}".`, + { changeMode: mode }, + ); + } + + if (options?.dryRun) { + return buildMutationSuccess(target); + } + + let updatedRef: ContentControlTarget | undefined; + + const receipt = executeDomainCommand( + editor, + (): boolean => { + const result = handler(); + if (typeof result === 'boolean') { + return result; + } + updatedRef = result; + return true; + }, + { + expectedRevision: options?.expectedRevision, + }, + ); + + clearIndexCache(editor); + + if (receipt.steps[0]?.effect !== 'changed') { + return buildMutationFailure('NO_OP', 'The mutation had no effect.'); + } + + return buildMutationSuccess(target, updatedRef); +} + +/** + * Dispatch a transaction in both UI-attached and headless adapter contexts. + * Stories and CLI calls can run without a mounted editor view, so fall back to + * the editor-level dispatch when view dispatch is unavailable. + */ +function dispatchTransaction(editor: Editor, tr: Editor['state']['tr']): void { + if (editor.view?.dispatch) { + editor.view.dispatch(tr); + return; + } + + if (typeof editor.dispatch === 'function') { + editor.dispatch(tr); + return; + } + + throw new DocumentApiAdapterError( + 'CAPABILITY_UNAVAILABLE', + 'Content-control mutation requires an editor dispatch function.', + ); +} + +// --------------------------------------------------------------------------- +// A. Core CRUD + Discovery โ€” Read operations +// --------------------------------------------------------------------------- + +function listWrapper(editor: Editor, query?: ContentControlsListQuery): ContentControlsListResult { + const allSdts = findAllSdtNodes(editor.state.doc); + let infos = allSdts.map(buildContentControlInfoFromNode); + + if (query?.controlType) { + infos = infos.filter((info) => info.controlType === query.controlType); + } + if (query?.tag) { + infos = infos.filter((info) => info.properties.tag === query.tag); + } + + return applyPagination(infos, query); +} + +function getWrapper(editor: Editor, input: ContentControlsGetInput): ContentControlInfo { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + return buildContentControlInfoFromNode(sdt); +} + +function listInRangeWrapper(editor: Editor, input: ContentControlsListInRangeInput): ContentControlsListResult { + const doc = editor.state.doc; + const allSdts = findAllSdtNodes(doc); + + // Resolve block range bounds via the block index (block IDs, not SDT IDs). + let rangeStart = 0; + let rangeEnd = doc.content.size; + + if (input.startBlockId || input.endBlockId) { + const blockIndex = buildBlockIndex(editor); + if (input.startBlockId) { + const startBlock = findBlockByNodeIdOnly(blockIndex, input.startBlockId); + rangeStart = startBlock.pos; + } + if (input.endBlockId) { + const endBlock = findBlockByNodeIdOnly(blockIndex, input.endBlockId); + rangeEnd = endBlock.end; + } + } + + const filtered = allSdts.filter((sdt) => { + const sdtEnd = sdt.pos + sdt.node.nodeSize; + return sdt.pos >= rangeStart && sdtEnd <= rangeEnd; + }); + + const infos = filtered.map(buildContentControlInfoFromNode); + return applyPagination(infos, input); +} + +function selectByTagWrapper(editor: Editor, input: ContentControlsSelectByTagInput): ContentControlsListResult { + const allSdts = findAllSdtNodes(editor.state.doc); + const infos = allSdts.map(buildContentControlInfoFromNode).filter((info) => info.properties.tag === input.tag); + return applyPagination(infos, input); +} + +function selectByTitleWrapper(editor: Editor, input: ContentControlsSelectByTitleInput): ContentControlsListResult { + const allSdts = findAllSdtNodes(editor.state.doc); + const infos = allSdts.map(buildContentControlInfoFromNode).filter((info) => info.properties.alias === input.title); + return applyPagination(infos, input); +} + +function listChildrenWrapper(editor: Editor, input: ContentControlsListChildrenInput): ContentControlsListResult { + const parent = resolveSdtByTarget(editor.state.doc, input.target); + const children: { node: typeof parent.node; pos: number; kind: 'block' | 'inline' }[] = []; + + parent.node.forEach((child, offset) => { + if (isSdtNode(child)) { + children.push({ + node: child, + pos: parent.pos + 1 + offset, + kind: child.type.name === SDT_BLOCK_NAME ? 'block' : 'inline', + }); + } + }); + + const infos = children.map(buildContentControlInfoFromNode); + return applyPagination(infos, input); +} + +function getParentWrapper(editor: Editor, input: ContentControlsGetParentInput): ContentControlInfo | null { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + const $pos = editor.state.doc.resolve(sdt.pos); + + for (let depth = $pos.depth - 1; depth >= 0; depth--) { + const ancestor = $pos.node(depth); + if (isSdtNode(ancestor)) { + return buildContentControlInfoFromNode({ + node: ancestor, + pos: $pos.before(depth), + kind: ancestor.type.name === SDT_BLOCK_NAME ? 'block' : 'inline', + }); + } + } + + return null; +} + +// --------------------------------------------------------------------------- +// A. Core CRUD โ€” Mutation operations +// --------------------------------------------------------------------------- + +function wrapWrapper( + editor: Editor, + input: ContentControlsWrapInput, + options?: MutationOptions, +): ContentControlMutationResult { + // Validate the target exists before mutating. + resolveSdtByTarget(editor.state.doc, input.target); + + const id = generateSdtId(); + const wrapperTarget: ContentControlTarget = { kind: input.kind, nodeType: 'sdt', nodeId: id }; + + return executeSdtMutation(editor, input.target, options, () => { + const resolved = resolveSdtByTarget(editor.state.doc, input.target); + const nodeTypeName = input.kind === 'block' ? SDT_BLOCK_NAME : 'structuredContent'; + const nodeType = editor.schema.nodes[nodeTypeName]; + if (!nodeType) return false; + + const wrapperNode = nodeType.create( + { id, tag: input.tag, alias: input.alias, lockMode: input.lockMode ?? 'unlocked' }, + resolved.node, + ); + const { tr } = editor.state; + tr.replaceWith(resolved.pos, resolved.pos + resolved.node.nodeSize, wrapperNode); + dispatchTransaction(editor, tr); + return wrapperTarget; + }); +} + +function unwrapWrapper( + editor: Editor, + input: ContentControlsUnwrapInput, + options?: MutationOptions, +): ContentControlMutationResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + assertNotSdtLocked(sdt, 'unwrap'); + const target = buildTarget(sdt); + + return executeSdtMutation(editor, target, options, () => { + const resolved = resolveSdtByTarget(editor.state.doc, input.target); + const { tr } = editor.state; + tr.replaceWith(resolved.pos, resolved.pos + resolved.node.nodeSize, resolved.node.content); + dispatchTransaction(editor, tr); + return true; + }); +} + +function deleteWrapper( + editor: Editor, + input: ContentControlsDeleteInput, + options?: MutationOptions, +): ContentControlMutationResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + assertNotSdtLocked(sdt, 'delete'); + const target = buildTarget(sdt); + + const deleteCmd = editor.commands?.deleteStructuredContentById; + if (typeof deleteCmd !== 'function') { + throw new DocumentApiAdapterError('CAPABILITY_UNAVAILABLE', 'deleteStructuredContentById command not available.'); + } + + return executeSdtMutation(editor, target, options, () => { + return Boolean(deleteCmd(input.target.nodeId)); + }); +} + +function copyWrapper( + editor: Editor, + input: ContentControlsCopyInput, + options?: MutationOptions, +): ContentControlMutationResult { + resolveSdtByTarget(editor.state.doc, input.target); + const target = input.target; + + return executeSdtMutation(editor, target, options, () => { + const source = resolveSdtByTarget(editor.state.doc, input.target); + const dest = resolveSdtByTarget(editor.state.doc, input.destination); + const newId = generateSdtId(); + const cloned = reIdDescendantSdts( + source.node.type.create({ ...source.node.attrs, id: newId }, source.node.content, source.node.marks), + editor.schema, + ); + const { tr } = editor.state; + const insertPos = dest.pos + dest.node.nodeSize; + tr.insert(insertPos, cloned); + dispatchTransaction(editor, tr); + return { kind: source.kind, nodeType: 'sdt' as const, nodeId: newId }; + }); +} + +function moveWrapper( + editor: Editor, + input: ContentControlsMoveInput, + options?: MutationOptions, +): ContentControlMutationResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + assertNotSdtLocked(sdt, 'move'); + const target = buildTarget(sdt); + + return executeSdtMutation(editor, target, options, () => { + const source = resolveSdtByTarget(editor.state.doc, input.target); + const { tr } = editor.state; + tr.delete(source.pos, source.pos + source.node.nodeSize); + const destAfterDelete = resolveSdtByTarget(tr.doc, input.destination); + const insertPos = destAfterDelete.pos + destAfterDelete.node.nodeSize; + tr.insert(insertPos, source.node); + dispatchTransaction(editor, tr); + return true; + }); +} + +function patchWrapper( + editor: Editor, + input: ContentControlsPatchInput, + options?: MutationOptions, +): ContentControlMutationResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + assertNotSdtLocked(sdt, 'patch'); + const target = buildTarget(sdt); + + const patchFields: Record = {}; + if (input.alias !== undefined) patchFields.alias = input.alias; + if (input.tag !== undefined) patchFields.tag = input.tag; + if (input.appearance !== undefined) patchFields.appearance = input.appearance; + if (input.color !== undefined) patchFields.color = input.color; + if (input.placeholder !== undefined) patchFields.placeholder = input.placeholder; + if (input.showingPlaceholder !== undefined) patchFields.showingPlaceholder = input.showingPlaceholder; + if (input.temporary !== undefined) patchFields.temporary = input.temporary; + if (input.tabIndex !== undefined) patchFields.tabIndex = input.tabIndex; + + return executeSdtMutation(editor, target, options, () => { + return applyAttrsUpdate(editor, input.target.nodeId, patchFields); + }); +} + +function setLockModeWrapper( + editor: Editor, + input: ContentControlsSetLockModeInput, + options?: MutationOptions, +): ContentControlMutationResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + const target = buildTarget(sdt); + + return executeSdtMutation(editor, target, options, () => { + return applyAttrsUpdate(editor, input.target.nodeId, { lockMode: input.lockMode }); + }); +} + +/** Maps control types to their sdtPr element name. Types not listed have no element. */ +const CONTROL_TYPE_SDT_PR_ELEMENTS: Record = { + text: 'w:text', + date: 'w:date', + checkbox: 'w14:checkbox', + comboBox: 'w:comboBox', + dropDownList: 'w:dropDownList', + repeatingSection: 'w15:repeatingSection', + repeatingSectionItem: 'w15:repeatingSectionItem', + group: 'w:group', +}; + +function setTypeWrapper( + editor: Editor, + input: ContentControlsSetTypeInput, + options?: MutationOptions, +): ContentControlMutationResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + assertNotSdtLocked(sdt, 'setType'); + const currentType = resolveControlType(sdt.node.attrs as Record); + + if (currentType === input.controlType) { + return buildMutationFailure('NO_OP', `Control type is already "${currentType}".`); + } + + const target = buildTarget(sdt); + + return executeSdtMutation(editor, target, options, () => { + // Remove the old type-specific element from sdtPr (if any) + const oldElementName = CONTROL_TYPE_SDT_PR_ELEMENTS[currentType]; + if (oldElementName) { + updateSdtPrChild(editor, input.target, oldElementName, () => null); + } + + // Add the new type-specific element to sdtPr (if applicable) + const newElementName = CONTROL_TYPE_SDT_PR_ELEMENTS[input.controlType]; + if (newElementName) { + updateSdtPrChild( + editor, + input.target, + newElementName, + (existing) => existing ?? { name: newElementName, type: 'element' }, + ); + } + + return applyAttrsUpdate(editor, input.target.nodeId, { + controlType: input.controlType, + type: input.controlType, + }); + }); +} + +// --------------------------------------------------------------------------- +// Content IO operations +// --------------------------------------------------------------------------- + +function getContentWrapper(editor: Editor, input: ContentControlsGetContentInput): ContentControlsGetContentResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + return { content: sdt.node.textContent, format: 'text' }; +} + +function replaceContentWrapper( + editor: Editor, + input: ContentControlsReplaceContentInput, + options?: MutationOptions, +): ContentControlMutationResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + assertNotContentLocked(sdt, 'replaceContent'); + const target = buildTarget(sdt); + + return executeSdtMutation(editor, target, options, () => { + return Boolean(editor.commands?.updateStructuredContentById?.(input.target.nodeId, { text: input.content })); + }); +} + +function clearContentWrapper( + editor: Editor, + input: ContentControlsClearContentInput, + options?: MutationOptions, +): ContentControlMutationResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + assertNotContentLocked(sdt, 'clearContent'); + const target = buildTarget(sdt); + + return executeSdtMutation(editor, target, options, () => { + return Boolean(editor.commands?.updateStructuredContentById?.(input.target.nodeId, { text: '' })); + }); +} + +function appendContentWrapper( + editor: Editor, + input: ContentControlsAppendContentInput, + options?: MutationOptions, +): ContentControlMutationResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + assertNotContentLocked(sdt, 'appendContent'); + const target = buildTarget(sdt); + + return executeSdtMutation(editor, target, options, () => { + const resolved = resolveSdtByTarget(editor.state.doc, input.target); + const currentText = resolved.node.textContent; + return Boolean( + editor.commands?.updateStructuredContentById?.(input.target.nodeId, { text: currentText + input.content }), + ); + }); +} + +function prependContentWrapper( + editor: Editor, + input: ContentControlsPrependContentInput, + options?: MutationOptions, +): ContentControlMutationResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + assertNotContentLocked(sdt, 'prependContent'); + const target = buildTarget(sdt); + + return executeSdtMutation(editor, target, options, () => { + const resolved = resolveSdtByTarget(editor.state.doc, input.target); + const currentText = resolved.node.textContent; + return Boolean( + editor.commands?.updateStructuredContentById?.(input.target.nodeId, { text: input.content + currentText }), + ); + }); +} + +function insertBeforeWrapper( + editor: Editor, + input: ContentControlsInsertBeforeInput, + options?: MutationOptions, +): ContentControlMutationResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + const target = buildTarget(sdt); + + return executeSdtMutation(editor, target, options, () => { + const resolved = resolveSdtByTarget(editor.state.doc, input.target); + const textNode = editor.schema.text(input.content); + const { tr } = editor.state; + tr.insert(resolved.pos, textNode); + dispatchTransaction(editor, tr); + return true; + }); +} + +function insertAfterWrapper( + editor: Editor, + input: ContentControlsInsertAfterInput, + options?: MutationOptions, +): ContentControlMutationResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + const target = buildTarget(sdt); + + return executeSdtMutation(editor, target, options, () => { + const resolved = resolveSdtByTarget(editor.state.doc, input.target); + const insertPos = resolved.pos + resolved.node.nodeSize; + const textNode = editor.schema.text(input.content); + const { tr } = editor.state; + tr.insert(insertPos, textNode); + dispatchTransaction(editor, tr); + return true; + }); +} + +// --------------------------------------------------------------------------- +// B. Data Binding + Raw/Compatibility +// --------------------------------------------------------------------------- + +function getBindingWrapper(editor: Editor, input: ContentControlsGetBindingInput): ContentControlBinding | null { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + return resolveBinding(sdt.node.attrs as Record) ?? null; +} + +function setBindingWrapper( + editor: Editor, + input: ContentControlsSetBindingInput, + options?: MutationOptions, +): ContentControlMutationResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + assertNotSdtLocked(sdt, 'setBinding'); + const target = buildTarget(sdt); + + return executeSdtMutation(editor, target, options, () => { + const bindingAttrs: Record = { + 'w:storeItemID': input.storeItemId, + 'w:xpath': input.xpath, + }; + if (input.prefixMappings) bindingAttrs['w:prefixMappings'] = input.prefixMappings; + + return updateSdtPrChild(editor, input.target, 'w:dataBinding', () => ({ + name: 'w:dataBinding', + type: 'element', + attributes: bindingAttrs, + })); + }); +} + +function clearBindingWrapper( + editor: Editor, + input: ContentControlsClearBindingInput, + options?: MutationOptions, +): ContentControlMutationResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + assertNotSdtLocked(sdt, 'clearBinding'); + const target = buildTarget(sdt); + + return executeSdtMutation(editor, target, options, () => { + return updateSdtPrChild(editor, input.target, 'w:dataBinding', () => null); + }); +} + +function getRawPropertiesWrapper( + editor: Editor, + input: ContentControlsGetRawPropertiesInput, +): ContentControlsGetRawPropertiesResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + const sdtPr = sdt.node.attrs.sdtPr; + const properties = typeof sdtPr === 'object' && sdtPr !== null ? ({ ...sdtPr } as Record) : {}; + return { properties }; +} + +function patchRawPropertiesWrapper( + editor: Editor, + input: ContentControlsPatchRawPropertiesInput, + options?: MutationOptions, +): ContentControlMutationResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + const target = buildTarget(sdt); + + // Validate forbidden mutations per ยง10 of the plan + const seenNames = new Set(); + for (const patch of input.patches) { + if (seenNames.has(patch.name)) { + throw new DocumentApiAdapterError( + 'INVALID_INPUT', + `Duplicate patch name "${patch.name}" in the same patch array.`, + ); + } + seenNames.add(patch.name); + + if (FORBIDDEN_RAW_PATCH_NAMES.has(patch.name)) { + throw new DocumentApiAdapterError( + 'INVALID_INPUT', + `Patching "${patch.name}" is not allowed via patchRawProperties.`, + { name: patch.name }, + ); + } + + if (patch.name.startsWith('r:')) { + throw new DocumentApiAdapterError( + 'INVALID_INPUT', + `Injecting relationship references ("${patch.name}") is not allowed.`, + { name: patch.name }, + ); + } + + if (patch.name.startsWith('cp:') || patch.name === 'Types' || patch.name === '[Content_Types]') { + throw new DocumentApiAdapterError( + 'INVALID_INPUT', + `Injecting package-level elements ("${patch.name}") is not allowed.`, + { name: patch.name }, + ); + } + } + + return executeSdtMutation(editor, target, options, () => { + const resolved = resolveSdtByTarget(editor.state.doc, input.target); + let currentSdtPr = (resolved.node.attrs.sdtPr ?? { name: 'w:sdtPr', elements: [] }) as SdtPrElement; + + for (const patch of input.patches) { + switch (patch.op) { + case 'set': { + const el = patch.element as SdtPrElement; + // Normalize to a well-formed XML element: ensure name matches patch.name and type is 'element'. + const normalized: SdtPrElement = { ...el, name: patch.name, type: el.type ?? 'element' }; + currentSdtPr = upsertSdtPrChild(currentSdtPr, patch.name, normalized); + break; + } + case 'remove': + currentSdtPr = removeSdtPrChild(currentSdtPr, patch.name); + break; + case 'setAttr': { + const existing = findSdtPrChild(currentSdtPr, patch.name); + if (!existing) { + throw new DocumentApiAdapterError('INVALID_TARGET', `Element "${patch.name}" does not exist in sdtPr.`); + } + currentSdtPr = upsertSdtPrChild(currentSdtPr, patch.name, { + ...existing, + attributes: { ...(existing.attributes ?? {}), [patch.attr]: patch.value }, + }); + break; + } + case 'removeAttr': { + const existingEl = findSdtPrChild(currentSdtPr, patch.name); + if (!existingEl) { + throw new DocumentApiAdapterError('INVALID_TARGET', `Element "${patch.name}" does not exist in sdtPr.`); + } + const attrs = { ...(existingEl.attributes ?? {}) }; + delete attrs[patch.attr]; + currentSdtPr = upsertSdtPrChild(currentSdtPr, patch.name, { ...existingEl, attributes: attrs }); + break; + } + } + } + + return applyAttrsUpdate(editor, input.target.nodeId, { sdtPr: currentSdtPr }); + }); +} + +function validateWordCompatibilityWrapper( + editor: Editor, + input: ContentControlsValidateWordCompatibilityInput, +): ContentControlsValidateWordCompatibilityResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + const attrs = sdt.node.attrs as Record; + const diagnostics: ContentControlsValidateWordCompatibilityResult['diagnostics'] = []; + const id = String(attrs.id ?? ''); + + if (!/^-?\d+$/.test(id)) { + diagnostics.push({ + code: 'INVALID_ID_FORMAT', + severity: 'error', + message: `Content control id "${id}" is not a valid signed 32-bit integer.`, + }); + } + + return { compatible: diagnostics.length === 0, diagnostics }; +} + +function normalizeWordCompatibilityWrapper( + editor: Editor, + input: ContentControlsNormalizeWordCompatibilityInput, + options?: MutationOptions, +): ContentControlMutationResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + const target = buildTarget(sdt); + const id = String(sdt.node.attrs.id ?? ''); + + if (/^-?\d+$/.test(id)) { + return buildMutationFailure('NO_OP', 'Content control ID is already Word-compatible.'); + } + + return executeSdtMutation(editor, target, options, () => { + const newId = generateSdtId(); + return applyAttrsUpdate(editor, input.target.nodeId, { id: newId }); + }); +} + +function normalizeTagPayloadWrapper( + editor: Editor, + input: ContentControlsNormalizeTagPayloadInput, + options?: MutationOptions, +): ContentControlMutationResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + const target = buildTarget(sdt); + const tag = sdt.node.attrs.tag as string | undefined; + + if (tag) { + try { + JSON.parse(tag); + return buildMutationFailure('NO_OP', 'Tag payload is already valid JSON.'); + } catch { + // Not JSON โ€” will normalize + } + } + + return executeSdtMutation(editor, target, options, () => { + const resolved = resolveSdtByTarget(editor.state.doc, input.target); + const currentTag = resolved.node.attrs.tag ?? ''; + return applyAttrsUpdate(editor, input.target.nodeId, { tag: JSON.stringify({ value: currentTag }) }); + }); +} + +// --------------------------------------------------------------------------- +// C. Typed Controls โ€” Text +// --------------------------------------------------------------------------- + +function textSetMultilineWrapper( + editor: Editor, + input: ContentControlsTextSetMultilineInput, + options?: MutationOptions, +): ContentControlMutationResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + assertControlType(sdt, 'text', 'text.setMultiline'); + assertNotSdtLocked(sdt, 'text.setMultiline'); + const target = buildTarget(sdt); + + return executeSdtMutation(editor, target, options, () => { + return updateSdtPrChild(editor, input.target, 'w:text', (existing) => ({ + name: 'w:text', + type: 'element', + ...existing, + attributes: { ...(existing?.attributes ?? {}), 'w:multiLine': input.multiline ? '1' : '0' }, + })); + }); +} + +function textSetValueWrapper( + editor: Editor, + input: ContentControlsTextSetValueInput, + options?: MutationOptions, +): ContentControlMutationResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + assertControlType(sdt, 'text', 'text.setValue'); + assertNotContentLocked(sdt, 'text.setValue'); + const target = buildTarget(sdt); + + return executeSdtMutation(editor, target, options, () => { + return Boolean(editor.commands?.updateStructuredContentById?.(input.target.nodeId, { text: input.value })); + }); +} + +function textClearValueWrapper( + editor: Editor, + input: ContentControlsTextClearValueInput, + options?: MutationOptions, +): ContentControlMutationResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + assertControlType(sdt, 'text', 'text.clearValue'); + assertNotContentLocked(sdt, 'text.clearValue'); + const target = buildTarget(sdt); + + return executeSdtMutation(editor, target, options, () => { + return Boolean(editor.commands?.updateStructuredContentById?.(input.target.nodeId, { text: '' })); + }); +} + +// --------------------------------------------------------------------------- +// C. Typed Controls โ€” Date +// --------------------------------------------------------------------------- + +/** Set a sub-element with w:val attribute inside the w:date element. */ +function updateDateSubElement( + editor: Editor, + target: ContentControlTarget, + subName: string, + value: string, + operation: string, + options?: MutationOptions, +): ContentControlMutationResult { + const sdt = resolveSdtByTarget(editor.state.doc, target); + assertControlType(sdt, 'date', operation); + assertNotSdtLocked(sdt, operation); + const resolvedTarget = buildTarget(sdt); + + return executeSdtMutation(editor, resolvedTarget, options, () => { + return updateSdtPrSubElementAttr(editor, target, 'w:date', subName, 'w:val', value); + }); +} + +function dateSetValueWrapper( + editor: Editor, + input: ContentControlsDateSetValueInput, + options?: MutationOptions, +): ContentControlMutationResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + assertControlType(sdt, 'date', 'date.setValue'); + assertNotSdtLocked(sdt, 'date.setValue'); + const target = buildTarget(sdt); + + // w:fullDate is an attribute on w:date itself, not a sub-element + return executeSdtMutation(editor, target, options, () => { + return updateSdtPrChild(editor, input.target, 'w:date', (existing) => ({ + name: 'w:date', + type: 'element', + ...existing, + attributes: { ...(existing?.attributes ?? {}), 'w:fullDate': input.value }, + })); + }); +} + +function dateClearValueWrapper( + editor: Editor, + input: ContentControlsDateClearValueInput, + options?: MutationOptions, +): ContentControlMutationResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + assertControlType(sdt, 'date', 'date.clearValue'); + assertNotSdtLocked(sdt, 'date.clearValue'); + const target = buildTarget(sdt); + + return executeSdtMutation(editor, target, options, () => { + return updateSdtPrChild(editor, input.target, 'w:date', (existing) => { + if (!existing) return null; + const attrs = { ...(existing.attributes ?? {}) } as Record; + delete attrs['w:fullDate']; + return { ...existing, attributes: attrs }; + }); + }); +} + +function dateSetDisplayFormatWrapper( + editor: Editor, + input: ContentControlsDateSetDisplayFormatInput, + options?: MutationOptions, +): ContentControlMutationResult { + return updateDateSubElement(editor, input.target, 'w:dateFormat', input.format, 'date.setDisplayFormat', options); +} + +function dateSetDisplayLocaleWrapper( + editor: Editor, + input: ContentControlsDateSetDisplayLocaleInput, + options?: MutationOptions, +): ContentControlMutationResult { + return updateDateSubElement(editor, input.target, 'w:lid', input.locale, 'date.setDisplayLocale', options); +} + +function dateSetStorageFormatWrapper( + editor: Editor, + input: ContentControlsDateSetStorageFormatInput, + options?: MutationOptions, +): ContentControlMutationResult { + return updateDateSubElement( + editor, + input.target, + 'w:storeMappedDataAs', + input.format, + 'date.setStorageFormat', + options, + ); +} + +function dateSetCalendarWrapper( + editor: Editor, + input: ContentControlsDateSetCalendarInput, + options?: MutationOptions, +): ContentControlMutationResult { + return updateDateSubElement(editor, input.target, 'w:calendar', input.calendar, 'date.setCalendar', options); +} + +// --------------------------------------------------------------------------- +// C. Typed Controls โ€” Checkbox +// --------------------------------------------------------------------------- + +function checkboxGetStateWrapper( + editor: Editor, + input: ContentControlsCheckboxGetStateInput, +): ContentControlsCheckboxGetStateResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + assertControlType(sdt, 'checkbox', 'checkbox.getState'); + const sdtPr = sdt.node.attrs.sdtPr as SdtPrElement | undefined; + return { checked: readCheckboxChecked(sdtPr) }; +} + +function checkboxSetStateWrapper( + editor: Editor, + input: ContentControlsCheckboxSetStateInput, + options?: MutationOptions, +): ContentControlMutationResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + assertControlType(sdt, 'checkbox', 'checkbox.setState'); + assertNotSdtLocked(sdt, 'checkbox.setState'); + const target = buildTarget(sdt); + + return executeSdtMutation(editor, target, options, () => { + return updateSdtPrSubElementAttr( + editor, + input.target, + 'w14:checkbox', + 'w14:checked', + 'w14:val', + input.checked ? '1' : '0', + ); + }); +} + +function checkboxToggleWrapper( + editor: Editor, + input: ContentControlsCheckboxToggleInput, + options?: MutationOptions, +): ContentControlMutationResult { + const currentState = checkboxGetStateWrapper(editor, input); + return checkboxSetStateWrapper(editor, { target: input.target, checked: !currentState.checked }, options); +} + +function checkboxSetSymbolPairWrapper( + editor: Editor, + input: ContentControlsCheckboxSetSymbolPairInput, + options?: MutationOptions, +): ContentControlMutationResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + assertControlType(sdt, 'checkbox', 'checkbox.setSymbolPair'); + assertNotSdtLocked(sdt, 'checkbox.setSymbolPair'); + const target = buildTarget(sdt); + + return executeSdtMutation(editor, target, options, () => { + return updateSdtPrChild(editor, input.target, 'w14:checkbox', (existing) => { + const el: SdtPrElement = existing ?? { name: 'w14:checkbox', type: 'element', elements: [] }; + const elements = (el.elements ?? []).filter( + (e) => e.name !== 'w14:checkedState' && e.name !== 'w14:uncheckedState', + ); + elements.push({ + name: 'w14:checkedState', + type: 'element', + attributes: { 'w14:font': input.checkedSymbol.font, 'w14:val': input.checkedSymbol.char }, + }); + elements.push({ + name: 'w14:uncheckedState', + type: 'element', + attributes: { 'w14:font': input.uncheckedSymbol.font, 'w14:val': input.uncheckedSymbol.char }, + }); + return { ...el, elements }; + }); + }); +} + +// --------------------------------------------------------------------------- +// C. Typed Controls โ€” Choice List +// --------------------------------------------------------------------------- + +function choiceListGetItemsWrapper( + editor: Editor, + input: ContentControlsChoiceListGetItemsInput, +): ContentControlsChoiceListGetItemsResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + assertControlType(sdt, ['comboBox', 'dropDownList'], 'choiceList.getItems'); + const sdtPr = sdt.node.attrs.sdtPr as SdtPrElement | undefined; + const controlType = resolveControlType(sdt.node.attrs as Record) as 'comboBox' | 'dropDownList'; + return readChoiceListData(sdtPr, controlType); +} + +function choiceListSetItemsWrapper( + editor: Editor, + input: ContentControlsChoiceListSetItemsInput, + options?: MutationOptions, +): ContentControlMutationResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + assertControlType(sdt, ['comboBox', 'dropDownList'], 'choiceList.setItems'); + assertNotSdtLocked(sdt, 'choiceList.setItems'); + const target = buildTarget(sdt); + const ct = resolveControlType(sdt.node.attrs as Record); + + return executeSdtMutation(editor, target, options, () => { + const childName = `w:${ct}`; + const itemElements: SdtPrElement[] = input.items.map((item) => ({ + name: 'w:listItem', + type: 'element', + attributes: { 'w:displayText': item.displayText, 'w:value': item.value }, + })); + return replaceSdtPrSubElements(editor, input.target, childName, itemElements); + }); +} + +function choiceListSetSelectedWrapper( + editor: Editor, + input: ContentControlsChoiceListSetSelectedInput, + options?: MutationOptions, +): ContentControlMutationResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + assertControlType(sdt, ['comboBox', 'dropDownList'], 'choiceList.setSelected'); + assertNotSdtLocked(sdt, 'choiceList.setSelected'); + const target = buildTarget(sdt); + const ct = resolveControlType(sdt.node.attrs as Record); + + return executeSdtMutation(editor, target, options, () => { + return updateSdtPrChild(editor, input.target, `w:${ct}`, (existing) => ({ + name: `w:${ct}`, + type: 'element', + ...existing, + attributes: { ...(existing?.attributes ?? {}), 'w:lastValue': input.value }, + })); + }); +} + +// --------------------------------------------------------------------------- +// D. Repeating Section +// --------------------------------------------------------------------------- + +function getRepeatingSectionItems(sdt: { + node: import('prosemirror-model').Node; + pos: number; +}): Array<{ node: import('prosemirror-model').Node; pos: number; kind: 'block' | 'inline' }> { + const items: Array<{ node: import('prosemirror-model').Node; pos: number; kind: 'block' | 'inline' }> = []; + sdt.node.forEach((child, offset) => { + if (isSdtNode(child)) { + const ct = resolveControlType(child.attrs as Record); + if (ct === 'repeatingSectionItem') { + items.push({ + node: child, + pos: sdt.pos + 1 + offset, + kind: child.type.name === SDT_BLOCK_NAME ? 'block' : 'inline', + }); + } + } + }); + return items; +} + +function repeatingSectionListItemsWrapper( + editor: Editor, + input: ContentControlsRepeatingSectionListItemsInput, +): ContentControlsRepeatingSectionListItemsResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + assertControlType(sdt, 'repeatingSection', 'repeatingSection.listItems'); + const items = getRepeatingSectionItems(sdt); + const infos = items.map(buildContentControlInfoFromNode); + return { items: infos, total: infos.length }; +} + +function repeatingSectionInsertItemBeforeWrapper( + editor: Editor, + input: ContentControlsRepeatingSectionInsertItemBeforeInput, + options?: MutationOptions, +): ContentControlMutationResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + assertControlType(sdt, 'repeatingSection', 'repeatingSection.insertItemBefore'); + assertNotContentLocked(sdt, 'repeatingSection.insertItemBefore'); + const target = buildTarget(sdt); + + return executeSdtMutation(editor, target, options, () => { + const resolved = resolveSdtByTarget(editor.state.doc, input.target); + const items = getRepeatingSectionItems(resolved); + if (input.index < 0 || input.index > items.length) { + throw new DocumentApiAdapterError('INVALID_INPUT', `Index ${input.index} out of range [0, ${items.length}].`); + } + const insertPos = input.index < items.length ? items[input.index].pos : resolved.pos + resolved.node.nodeSize - 1; + const nodeType = editor.schema.nodes[SDT_BLOCK_NAME]; + const paragraph = editor.schema.nodes.paragraph.create(); + const newItem = nodeType.create( + { id: generateSdtId(), controlType: 'repeatingSectionItem', type: 'repeatingSectionItem' }, + paragraph, + ); + const { tr } = editor.state; + tr.insert(insertPos, newItem); + dispatchTransaction(editor, tr); + return true; + }); +} + +function repeatingSectionInsertItemAfterWrapper( + editor: Editor, + input: ContentControlsRepeatingSectionInsertItemAfterInput, + options?: MutationOptions, +): ContentControlMutationResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + assertControlType(sdt, 'repeatingSection', 'repeatingSection.insertItemAfter'); + assertNotContentLocked(sdt, 'repeatingSection.insertItemAfter'); + const target = buildTarget(sdt); + + return executeSdtMutation(editor, target, options, () => { + const resolved = resolveSdtByTarget(editor.state.doc, input.target); + const items = getRepeatingSectionItems(resolved); + if (input.index < 0 || input.index >= items.length) { + throw new DocumentApiAdapterError('INVALID_INPUT', `Index ${input.index} out of range [0, ${items.length - 1}].`); + } + const afterItem = items[input.index]; + const insertPos = afterItem.pos + afterItem.node.nodeSize; + const nodeType = editor.schema.nodes[SDT_BLOCK_NAME]; + const paragraph = editor.schema.nodes.paragraph.create(); + const newItem = nodeType.create( + { id: generateSdtId(), controlType: 'repeatingSectionItem', type: 'repeatingSectionItem' }, + paragraph, + ); + const { tr } = editor.state; + tr.insert(insertPos, newItem); + dispatchTransaction(editor, tr); + return true; + }); +} + +function repeatingSectionCloneItemWrapper( + editor: Editor, + input: ContentControlsRepeatingSectionCloneItemInput, + options?: MutationOptions, +): ContentControlMutationResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + assertControlType(sdt, 'repeatingSection', 'repeatingSection.cloneItem'); + assertNotContentLocked(sdt, 'repeatingSection.cloneItem'); + const target = buildTarget(sdt); + + return executeSdtMutation(editor, target, options, () => { + const resolved = resolveSdtByTarget(editor.state.doc, input.target); + const items = getRepeatingSectionItems(resolved); + if (input.index < 0 || input.index >= items.length) { + throw new DocumentApiAdapterError('INVALID_INPUT', `Index ${input.index} out of range [0, ${items.length - 1}].`); + } + const sourceItem = items[input.index]; + const cloned = reIdDescendantSdts( + sourceItem.node.type.create( + { ...sourceItem.node.attrs, id: generateSdtId() }, + sourceItem.node.content, + sourceItem.node.marks, + ), + editor.schema, + ); + const insertPos = sourceItem.pos + sourceItem.node.nodeSize; + const { tr } = editor.state; + tr.insert(insertPos, cloned); + dispatchTransaction(editor, tr); + return true; + }); +} + +function repeatingSectionDeleteItemWrapper( + editor: Editor, + input: ContentControlsRepeatingSectionDeleteItemInput, + options?: MutationOptions, +): ContentControlMutationResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + assertControlType(sdt, 'repeatingSection', 'repeatingSection.deleteItem'); + assertNotContentLocked(sdt, 'repeatingSection.deleteItem'); + const target = buildTarget(sdt); + + return executeSdtMutation(editor, target, options, () => { + const resolved = resolveSdtByTarget(editor.state.doc, input.target); + const items = getRepeatingSectionItems(resolved); + if (input.index < 0 || input.index >= items.length) { + throw new DocumentApiAdapterError('INVALID_INPUT', `Index ${input.index} out of range [0, ${items.length - 1}].`); + } + const item = items[input.index]; + const { tr } = editor.state; + tr.delete(item.pos, item.pos + item.node.nodeSize); + dispatchTransaction(editor, tr); + return true; + }); +} + +function repeatingSectionSetAllowInsertDeleteWrapper( + editor: Editor, + input: ContentControlsRepeatingSectionSetAllowInsertDeleteInput, + options?: MutationOptions, +): ContentControlMutationResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + assertControlType(sdt, 'repeatingSection', 'repeatingSection.setAllowInsertDelete'); + assertNotSdtLocked(sdt, 'repeatingSection.setAllowInsertDelete'); + const target = buildTarget(sdt); + + return executeSdtMutation(editor, target, options, () => { + return updateSdtPrSubElementAttr( + editor, + input.target, + 'w15:repeatingSection', + 'w15:allowInsertDeleteSection', + 'w15:val', + input.allow ? '1' : '0', + ); + }); +} + +// --------------------------------------------------------------------------- +// D. Group +// --------------------------------------------------------------------------- + +function groupWrapWrapper( + editor: Editor, + input: ContentControlsGroupWrapInput, + options?: MutationOptions, +): ContentControlMutationResult { + resolveSdtByTarget(editor.state.doc, input.target); + const target = input.target; + + return executeSdtMutation(editor, target, options, () => { + const resolved = resolveSdtByTarget(editor.state.doc, input.target); + const groupNodeType = editor.schema.nodes[SDT_BLOCK_NAME]; + if (!groupNodeType) return false; + + const groupId = generateSdtId(); + const groupNode = groupNodeType.create({ id: groupId, controlType: 'group', type: 'group' }, resolved.node); + + const { tr } = editor.state; + tr.replaceWith(resolved.pos, resolved.pos + resolved.node.nodeSize, groupNode); + dispatchTransaction(editor, tr); + return { kind: 'block' as const, nodeType: 'sdt' as const, nodeId: groupId }; + }); +} + +function groupUngroupWrapper( + editor: Editor, + input: ContentControlsGroupUngroupInput, + options?: MutationOptions, +): ContentControlMutationResult { + const sdt = resolveSdtByTarget(editor.state.doc, input.target); + assertControlType(sdt, 'group', 'group.ungroup'); + assertNotSdtLocked(sdt, 'group.ungroup'); + const target = buildTarget(sdt); + + return executeSdtMutation(editor, target, options, () => { + const resolved = resolveSdtByTarget(editor.state.doc, input.target); + const { tr } = editor.state; + tr.replaceWith(resolved.pos, resolved.pos + resolved.node.nodeSize, resolved.node.content); + dispatchTransaction(editor, tr); + return true; + }); +} + +// --------------------------------------------------------------------------- +// Create content control (create.* namespace) +// --------------------------------------------------------------------------- + +function createWrapper( + editor: Editor, + input: CreateContentControlInput, + options?: MutationOptions, +): ContentControlMutationResult { + const commandName = input.kind === 'block' ? 'insertStructuredContentBlock' : 'insertStructuredContentInline'; + const insertCmd = editor.commands?.[commandName]; + if (typeof insertCmd !== 'function') { + throw new DocumentApiAdapterError('CAPABILITY_UNAVAILABLE', `${commandName} command not available.`); + } + + const id = generateSdtId(); + const target: ContentControlTarget = { kind: input.kind, nodeType: 'sdt', nodeId: id }; + + // When a reference target is provided, validate it before mutating. + if (input.target) { + resolveSdtByTarget(editor.state.doc, input.target); + } + + return executeSdtMutation(editor, target, options, () => { + const attrs: Record = { + id, + tag: input.tag, + alias: input.alias, + lockMode: input.lockMode ?? 'unlocked', + controlType: input.controlType ?? 'unknown', + type: input.controlType ?? 'unknown', + }; + + // When a target is provided, insert adjacent to it for deterministic placement. + if (input.target) { + const ref = resolveSdtByTarget(editor.state.doc, input.target); + const nodeTypeName = input.kind === 'block' ? SDT_BLOCK_NAME : 'structuredContent'; + const nodeType = editor.schema.nodes[nodeTypeName]; + if (!nodeType) return false; + + let content; + if (input.content) { + content = editor.schema.text(input.content); + if (input.kind === 'block') { + content = editor.schema.nodes.paragraph.create(null, content); + } + } else if (input.kind === 'block') { + content = editor.schema.nodes.paragraph.create(); + } + + const newNode = content ? nodeType.create(attrs, content) : nodeType.create(attrs); + const insertPos = ref.pos + ref.node.nodeSize; + const { tr } = editor.state; + tr.insert(insertPos, newNode); + dispatchTransaction(editor, tr); + return true; + } + + // Default: delegate to the editor command (inserts at current selection). + if (input.content) { + return Boolean(insertCmd({ attrs, text: input.content })); + } + return Boolean(insertCmd({ attrs })); + }); +} + +// --------------------------------------------------------------------------- +// Public adapter assembly +// --------------------------------------------------------------------------- + +export function createContentControlsAdapter(editor: Editor): ContentControlsAdapter & ContentControlsCreateAdapter { + return { + list: (query) => listWrapper(editor, query), + get: (input) => getWrapper(editor, input), + listInRange: (input) => listInRangeWrapper(editor, input), + selectByTag: (input) => selectByTagWrapper(editor, input), + selectByTitle: (input) => selectByTitleWrapper(editor, input), + listChildren: (input) => listChildrenWrapper(editor, input), + getParent: (input) => getParentWrapper(editor, input), + wrap: (input, options) => wrapWrapper(editor, input, options), + unwrap: (input, options) => unwrapWrapper(editor, input, options), + delete: (input, options) => deleteWrapper(editor, input, options), + copy: (input, options) => copyWrapper(editor, input, options), + move: (input, options) => moveWrapper(editor, input, options), + patch: (input, options) => patchWrapper(editor, input, options), + setLockMode: (input, options) => setLockModeWrapper(editor, input, options), + setType: (input, options) => setTypeWrapper(editor, input, options), + getContent: (input) => getContentWrapper(editor, input), + replaceContent: (input, options) => replaceContentWrapper(editor, input, options), + clearContent: (input, options) => clearContentWrapper(editor, input, options), + appendContent: (input, options) => appendContentWrapper(editor, input, options), + prependContent: (input, options) => prependContentWrapper(editor, input, options), + insertBefore: (input, options) => insertBeforeWrapper(editor, input, options), + insertAfter: (input, options) => insertAfterWrapper(editor, input, options), + getBinding: (input) => getBindingWrapper(editor, input), + setBinding: (input, options) => setBindingWrapper(editor, input, options), + clearBinding: (input, options) => clearBindingWrapper(editor, input, options), + getRawProperties: (input) => getRawPropertiesWrapper(editor, input), + patchRawProperties: (input, options) => patchRawPropertiesWrapper(editor, input, options), + validateWordCompatibility: (input) => validateWordCompatibilityWrapper(editor, input), + normalizeWordCompatibility: (input, options) => normalizeWordCompatibilityWrapper(editor, input, options), + normalizeTagPayload: (input, options) => normalizeTagPayloadWrapper(editor, input, options), + text: { + setMultiline: (input, options) => textSetMultilineWrapper(editor, input, options), + setValue: (input, options) => textSetValueWrapper(editor, input, options), + clearValue: (input, options) => textClearValueWrapper(editor, input, options), + }, + date: { + setValue: (input, options) => dateSetValueWrapper(editor, input, options), + clearValue: (input, options) => dateClearValueWrapper(editor, input, options), + setDisplayFormat: (input, options) => dateSetDisplayFormatWrapper(editor, input, options), + setDisplayLocale: (input, options) => dateSetDisplayLocaleWrapper(editor, input, options), + setStorageFormat: (input, options) => dateSetStorageFormatWrapper(editor, input, options), + setCalendar: (input, options) => dateSetCalendarWrapper(editor, input, options), + }, + checkbox: { + getState: (input) => checkboxGetStateWrapper(editor, input), + setState: (input, options) => checkboxSetStateWrapper(editor, input, options), + toggle: (input, options) => checkboxToggleWrapper(editor, input, options), + setSymbolPair: (input, options) => checkboxSetSymbolPairWrapper(editor, input, options), + }, + choiceList: { + getItems: (input) => choiceListGetItemsWrapper(editor, input), + setItems: (input, options) => choiceListSetItemsWrapper(editor, input, options), + setSelected: (input, options) => choiceListSetSelectedWrapper(editor, input, options), + }, + repeatingSection: { + listItems: (input) => repeatingSectionListItemsWrapper(editor, input), + insertItemBefore: (input, options) => repeatingSectionInsertItemBeforeWrapper(editor, input, options), + insertItemAfter: (input, options) => repeatingSectionInsertItemAfterWrapper(editor, input, options), + cloneItem: (input, options) => repeatingSectionCloneItemWrapper(editor, input, options), + deleteItem: (input, options) => repeatingSectionDeleteItemWrapper(editor, input, options), + setAllowInsertDelete: (input, options) => repeatingSectionSetAllowInsertDeleteWrapper(editor, input, options), + }, + group: { + wrap: (input, options) => groupWrapWrapper(editor, input, options), + ungroup: (input, options) => groupUngroupWrapper(editor, input, options), + }, + create: (input, options) => createWrapper(editor, input, options), + }; +} diff --git a/packages/super-editor/src/extensions/structured-content/structured-content-block.js b/packages/super-editor/src/extensions/structured-content/structured-content-block.js index 42ee58cbfd..6614491cd1 100644 --- a/packages/super-editor/src/extensions/structured-content/structured-content-block.js +++ b/packages/super-editor/src/extensions/structured-content/structured-content-block.js @@ -86,6 +86,34 @@ export const StructuredContentBlock = Node.create({ }, }, + controlType: { + default: null, + parseDOM: (elem) => elem.getAttribute('data-control-type'), + renderDOM: (attrs) => { + if (!attrs.controlType) return {}; + return { 'data-control-type': attrs.controlType }; + }, + }, + + type: { + default: null, + rendered: false, + }, + + appearance: { + default: null, + parseDOM: (elem) => elem.getAttribute('data-appearance'), + renderDOM: (attrs) => { + if (!attrs.appearance) return {}; + return { 'data-appearance': attrs.appearance }; + }, + }, + + placeholder: { + default: null, + rendered: false, + }, + sdtPr: { rendered: false, }, diff --git a/packages/super-editor/src/extensions/structured-content/structured-content.js b/packages/super-editor/src/extensions/structured-content/structured-content.js index eaa495d6eb..5bf5928b7e 100644 --- a/packages/super-editor/src/extensions/structured-content/structured-content.js +++ b/packages/super-editor/src/extensions/structured-content/structured-content.js @@ -95,6 +95,34 @@ export const StructuredContent = Node.create({ }, }, + controlType: { + default: null, + parseDOM: (elem) => elem.getAttribute('data-control-type'), + renderDOM: (attrs) => { + if (!attrs.controlType) return {}; + return { 'data-control-type': attrs.controlType }; + }, + }, + + type: { + default: null, + rendered: false, + }, + + appearance: { + default: null, + parseDOM: (elem) => elem.getAttribute('data-appearance'), + renderDOM: (attrs) => { + if (!attrs.appearance) return {}; + return { 'data-appearance': attrs.appearance }; + }, + }, + + placeholder: { + default: null, + rendered: false, + }, + sdtPr: { rendered: false, }, diff --git a/packages/super-editor/src/extensions/types/node-attributes.ts b/packages/super-editor/src/extensions/types/node-attributes.ts index 8d9ddeb0d1..a7d4a2a6d1 100644 --- a/packages/super-editor/src/extensions/types/node-attributes.ts +++ b/packages/super-editor/src/extensions/types/node-attributes.ts @@ -1030,6 +1030,16 @@ export interface StructuredContentBlockAttrs extends BlockNodeAttributes { tag?: string | null; /** Display name/alias */ alias?: string | null; + /** Lock mode (ECMA-376 w:lock). */ + lockMode?: string | null; + /** Semantic control type (text, date, checkbox, etc.). */ + controlType?: string | null; + /** Legacy type field (fallback for controlType). */ + type?: string | null; + /** Visual appearance (boundingBox, tags, hidden). */ + appearance?: string | null; + /** Placeholder text. */ + placeholder?: string | null; /** @internal Structured document tag properties */ sdtPr?: unknown; } diff --git a/tests/doc-api-stories/tests/content-controls/all-commands.ts b/tests/doc-api-stories/tests/content-controls/all-commands.ts new file mode 100644 index 0000000000..527fae76a8 --- /dev/null +++ b/tests/doc-api-stories/tests/content-controls/all-commands.ts @@ -0,0 +1,2109 @@ +import { access, writeFile } from 'node:fs/promises'; +import { describe, expect, it } from 'vitest'; +import { corpusDoc, useStoryHarness } from '../harness'; + +type ContentControlTarget = { + kind: 'block' | 'inline'; + nodeType: 'sdt'; + nodeId: string; +}; + +type ChoiceItem = { + displayText: string; + value: string; +}; + +type ScenarioFixture = { + target?: ContentControlTarget; + secondaryTarget?: ContentControlTarget; + parentTarget?: ContentControlTarget; + childTarget?: ContentControlTarget; + tag?: string; + alias?: string; + startBlockId?: string; + endBlockId?: string; + choiceItems?: ChoiceItem[]; +}; + +const BASE_CONTENT_CONTROLS_DOC = corpusDoc('layout/sdts-basic.docx'); + +const ALL_CONTENT_CONTROL_COMMAND_IDS = [ + 'create.contentControl', + 'contentControls.list', + 'contentControls.get', + 'contentControls.listInRange', + 'contentControls.selectByTag', + 'contentControls.selectByTitle', + 'contentControls.listChildren', + 'contentControls.getParent', + 'contentControls.wrap', + 'contentControls.unwrap', + 'contentControls.delete', + 'contentControls.copy', + 'contentControls.move', + 'contentControls.patch', + 'contentControls.setLockMode', + 'contentControls.setType', + 'contentControls.getContent', + 'contentControls.replaceContent', + 'contentControls.clearContent', + 'contentControls.appendContent', + 'contentControls.prependContent', + 'contentControls.insertBefore', + 'contentControls.insertAfter', + 'contentControls.getBinding', + 'contentControls.setBinding', + 'contentControls.clearBinding', + 'contentControls.getRawProperties', + 'contentControls.patchRawProperties', + 'contentControls.validateWordCompatibility', + 'contentControls.normalizeWordCompatibility', + 'contentControls.normalizeTagPayload', + 'contentControls.text.setMultiline', + 'contentControls.text.setValue', + 'contentControls.text.clearValue', + 'contentControls.date.setValue', + 'contentControls.date.clearValue', + 'contentControls.date.setDisplayFormat', + 'contentControls.date.setDisplayLocale', + 'contentControls.date.setStorageFormat', + 'contentControls.date.setCalendar', + 'contentControls.checkbox.getState', + 'contentControls.checkbox.setState', + 'contentControls.checkbox.toggle', + 'contentControls.checkbox.setSymbolPair', + 'contentControls.choiceList.getItems', + 'contentControls.choiceList.setItems', + 'contentControls.choiceList.setSelected', + 'contentControls.repeatingSection.listItems', + 'contentControls.repeatingSection.insertItemBefore', + 'contentControls.repeatingSection.insertItemAfter', + 'contentControls.repeatingSection.cloneItem', + 'contentControls.repeatingSection.deleteItem', + 'contentControls.repeatingSection.setAllowInsertDelete', + 'contentControls.group.wrap', + 'contentControls.group.ungroup', +] as const; + +type ContentControlsCommandId = (typeof ALL_CONTENT_CONTROL_COMMAND_IDS)[number]; + +const READ_OPERATION_IDS = new Set([ + 'contentControls.list', + 'contentControls.get', + 'contentControls.listInRange', + 'contentControls.selectByTag', + 'contentControls.selectByTitle', + 'contentControls.listChildren', + 'contentControls.getParent', + 'contentControls.getContent', + 'contentControls.getBinding', + 'contentControls.getRawProperties', + 'contentControls.validateWordCompatibility', + 'contentControls.checkbox.getState', + 'contentControls.choiceList.getItems', + 'contentControls.repeatingSection.listItems', +]); + +const CHOICE_ITEMS: ChoiceItem[] = [ + { displayText: 'One', value: 'one' }, + { displayText: 'Two', value: 'two' }, + { displayText: 'Three', value: 'three' }, +]; + +type StoryScenario = { + operationId: ContentControlsCommandId; + seedDoc?: string; + allowNoOpFailure?: boolean; + prepare?: (sessionId: string) => Promise; + run: (sessionId: string, fixture: ScenarioFixture | null) => Promise; +}; + +type OperationCallOptions = { + allowCommandFailure?: boolean; +}; + +describe('document-api story: all content-controls commands', () => { + const { outPath, runCli } = useStoryHarness('content-controls/all-commands', { + preserveResults: true, + }); + + function makeSessionId(operationSlug: string): string { + const safeSlug = operationSlug.replace(/[^A-Za-z0-9._-]/g, '-'); + const base = safeSlug.slice(0, 36).replace(/-+$/, '') || 'cc'; + const timestamp = Date.now().toString(36); + const random = Math.random().toString(36).slice(2, 8); + return `${base}-${timestamp}-${random}`.slice(0, 64); + } + + function slug(operationId: ContentControlsCommandId): string { + return operationId.replace(/\./g, '-'); + } + + function sourceDocNameFor(operationId: ContentControlsCommandId): string { + return `${slug(operationId)}-source.docx`; + } + + function resultDocNameFor(operationId: ContentControlsCommandId): string { + return `${slug(operationId)}.docx`; + } + + function readOutputNameFor(operationId: ContentControlsCommandId): string { + return `${slug(operationId)}-read-output.json`; + } + + function fixtureTag(operationId: ContentControlsCommandId, suffix: string): string { + return `${slug(operationId)}-${suffix}`; + } + + function normalizeTargetCandidate(value: unknown): ContentControlTarget | null { + if (typeof value !== 'object' || value === null) { + return null; + } + + const candidate = value as Record; + const kind = candidate.kind; + const nodeType = candidate.nodeType; + const rawNodeId = candidate.nodeId; + + if (kind !== 'block' && kind !== 'inline') { + return null; + } + if (nodeType !== 'sdt' || rawNodeId === undefined || rawNodeId === null) { + return null; + } + + const nodeId = String(rawNodeId); + if (nodeId.length === 0) { + return null; + } + + return { kind, nodeType: 'sdt', nodeId }; + } + + function requireTarget(operationId: ContentControlsCommandId, fixture: ScenarioFixture | null): ContentControlTarget { + const target = normalizeTargetCandidate(fixture?.target); + if (!target) { + throw new Error(`${operationId} requires a target fixture.`); + } + return target; + } + + function requireSecondaryTarget( + operationId: ContentControlsCommandId, + fixture: ScenarioFixture | null, + ): ContentControlTarget { + const target = normalizeTargetCandidate(fixture?.secondaryTarget); + if (!target) { + throw new Error(`${operationId} requires a secondary target fixture.`); + } + return target; + } + + function requireParentTarget( + operationId: ContentControlsCommandId, + fixture: ScenarioFixture | null, + ): ContentControlTarget { + const target = normalizeTargetCandidate(fixture?.parentTarget); + if (!target) { + throw new Error(`${operationId} requires a parent target fixture.`); + } + return target; + } + + function requireChildTarget( + operationId: ContentControlsCommandId, + fixture: ScenarioFixture | null, + ): ContentControlTarget { + const target = normalizeTargetCandidate(fixture?.childTarget); + if (!target) { + throw new Error(`${operationId} requires a child target fixture.`); + } + return target; + } + + function assertMutationSuccess(operationId: ContentControlsCommandId, result: any, allowNoOpFailure = false): void { + if (result?.success === true || result?.receipt?.success === true) return; + + const code = result?.failure?.code ?? result?.receipt?.failure?.code ?? 'UNKNOWN'; + if (allowNoOpFailure && code === 'NO_OP') return; + + throw new Error(`${operationId} did not report success (code: ${code}).`); + } + + function assertReadShape(operationId: ContentControlsCommandId, result: any): void { + if ( + operationId === 'contentControls.list' || + operationId === 'contentControls.listInRange' || + operationId === 'contentControls.selectByTag' || + operationId === 'contentControls.selectByTitle' || + operationId === 'contentControls.listChildren' + ) { + expect(Array.isArray(result?.items)).toBe(true); + expect(typeof result?.total).toBe('number'); + return; + } + + if (operationId === 'contentControls.get') { + expect(result?.nodeType).toBe('sdt'); + expect(typeof result?.id).toBe('string'); + expect(result?.target?.nodeType).toBe('sdt'); + return; + } + + if (operationId === 'contentControls.getParent') { + if (result === null) return; + expect(result?.nodeType).toBe('sdt'); + expect(result?.target?.nodeType).toBe('sdt'); + return; + } + + if (operationId === 'contentControls.getContent') { + expect(typeof result?.content).toBe('string'); + expect(result?.format === 'text' || result?.format === 'html').toBe(true); + return; + } + + if (operationId === 'contentControls.getBinding') { + if (result === null) return; + expect(typeof result?.storeItemId).toBe('string'); + expect(typeof result?.xpath).toBe('string'); + return; + } + + if (operationId === 'contentControls.getRawProperties') { + expect(typeof result?.properties).toBe('object'); + expect(result?.properties).not.toBeNull(); + return; + } + + if (operationId === 'contentControls.validateWordCompatibility') { + expect(typeof result?.compatible).toBe('boolean'); + expect(Array.isArray(result?.diagnostics)).toBe(true); + return; + } + + if (operationId === 'contentControls.checkbox.getState') { + expect(typeof result?.checked).toBe('boolean'); + return; + } + + if (operationId === 'contentControls.choiceList.getItems') { + expect(Array.isArray(result?.items)).toBe(true); + return; + } + + if (operationId === 'contentControls.repeatingSection.listItems') { + expect(Array.isArray(result?.items)).toBe(true); + expect(typeof result?.total).toBe('number'); + return; + } + + throw new Error(`Unexpected read assertion branch for ${operationId}.`); + } + + async function saveReadOutput(operationId: ContentControlsCommandId, result: any): Promise { + await writeFile( + outPath(readOutputNameFor(operationId)), + `${JSON.stringify({ operationId, output: result }, null, 2)}\n`, + 'utf8', + ); + } + + async function saveSource(sessionId: string, operationId: ContentControlsCommandId): Promise { + await callDocOperation('save', { + sessionId, + out: outPath(sourceDocNameFor(operationId)), + force: true, + }); + } + + async function saveResult(sessionId: string, operationId: ContentControlsCommandId): Promise { + await callDocOperation('save', { + sessionId, + out: outPath(resultDocNameFor(operationId)), + force: true, + }); + } + + function hasOwn(record: unknown, key: string): boolean { + return typeof record === 'object' && record !== null && Object.prototype.hasOwnProperty.call(record, key); + } + + function unwrapResultLayer(payload: unknown): unknown { + if (hasOwn(payload, 'result')) { + return (payload as Record).result; + } + if (hasOwn(payload, 'undefined')) { + return (payload as Record).undefined; + } + return payload; + } + + function parseOperationResult(envelope: any): T { + const firstLayer = unwrapResultLayer(envelope?.data); + const secondLayer = unwrapResultLayer(firstLayer); + return secondLayer as T; + } + + async function callDocOperation( + operationId: string, + input: Record, + options: OperationCallOptions = {}, + ): Promise { + const envelope = await runCli(['call', `doc.${operationId}`, '--input-json', JSON.stringify(input)], { + allowError: options.allowCommandFailure === true, + }); + + if (envelope?.ok === false) { + const fallbackFailure = { + code: envelope?.error?.code ?? 'COMMAND_FAILED', + message: envelope?.error?.message ?? 'Operation failed.', + }; + const detailedFailure = hasOwn(envelope?.error?.details, 'failure') + ? (envelope.error.details as Record).failure + : undefined; + + return { + success: false, + failure: (detailedFailure ?? fallbackFailure) as Record, + } as T; + } + + return parseOperationResult(envelope); + } + + async function openSeedDocument(sessionId: string, docPath: string): Promise { + await callDocOperation('open', { sessionId, doc: docPath }); + } + + async function closeSession(sessionId: string): Promise { + await callDocOperation('close', { sessionId, discard: true }); + } + + async function listControls(sessionId: string, query?: Record): Promise { + return callDocOperation('contentControls.list', { + sessionId, + ...(query ?? {}), + }); + } + + async function getControl(sessionId: string, target: ContentControlTarget): Promise { + return callDocOperation('contentControls.get', { sessionId, target }); + } + + async function getControlContent(sessionId: string, target: ContentControlTarget): Promise { + return callDocOperation('contentControls.getContent', { sessionId, target }); + } + + async function createControl( + sessionId: string, + operationId: ContentControlsCommandId, + input: { + kind?: 'block' | 'inline'; + controlType?: string; + tag?: string; + alias?: string; + content?: string; + target?: ContentControlTarget; + } = {}, + ): Promise { + const createResult = await callDocOperation('create.contentControl', { + sessionId, + kind: input.kind ?? 'block', + controlType: input.controlType ?? 'text', + tag: input.tag, + alias: input.alias, + content: input.content, + target: input.target, + }); + + assertMutationSuccess(operationId, createResult); + + const target = normalizeTargetCandidate(createResult?.updatedRef ?? createResult?.contentControl); + if (!target) { + throw new Error(`${operationId}: create.contentControl did not return a target.`); + } + + return target; + } + + async function findParagraphNodeIds(sessionId: string): Promise { + const findResult = await callDocOperation('find', { + sessionId, + type: 'node', + nodeType: 'paragraph', + }); + + const nodeIds = (findResult?.items ?? []) + .map((item: any) => item?.address?.nodeId) + .filter((nodeId: unknown): nodeId is string => typeof nodeId === 'string' && nodeId.length > 0); + + if (nodeIds.length < 2) { + throw new Error('Expected at least two paragraphs to build a range fixture.'); + } + + return nodeIds; + } + + async function createNestedControls( + sessionId: string, + operationId: ContentControlsCommandId, + ): Promise<{ + childTarget: ContentControlTarget; + parentTarget: ContentControlTarget; + }> { + const parentTarget = await seedRepeatingSection(sessionId, operationId, true); + const childList = await callDocOperation('contentControls.repeatingSection.listItems', { + sessionId, + target: parentTarget, + }); + const resolvedChild = normalizeTargetCandidate(childList?.items?.[0]?.target); + if (!resolvedChild) { + throw new Error(`${operationId}: unable to resolve nested child target from repeating section.`); + } + + return { + childTarget: resolvedChild, + parentTarget, + }; + } + + async function findGetParentFixture(sessionId: string): Promise { + const listed = await listControls(sessionId); + const items = listed?.items ?? []; + + for (const item of items) { + const candidate = normalizeTargetCandidate(item?.target); + if (!candidate) continue; + + try { + const parent = await callDocOperation('contentControls.getParent', { + sessionId, + target: candidate, + }); + + const parentTarget = normalizeTargetCandidate(parent?.target); + if (parentTarget) { + return { + childTarget: candidate, + parentTarget, + }; + } + } catch { + // Ignore controls that cannot be resolved in this adapter state. + } + } + + const fallback = normalizeTargetCandidate(items.find((item: any) => item?.target?.nodeId)?.target); + if (!fallback) { + throw new Error('contentControls.getParent requires at least one discoverable content control target.'); + } + + return { + childTarget: fallback, + }; + } + + async function seedChoiceControl( + sessionId: string, + operationId: ContentControlsCommandId, + ): Promise<{ target: ContentControlTarget; items: ChoiceItem[] }> { + const target = await createControl(sessionId, operationId, { + kind: 'block', + controlType: 'comboBox', + tag: fixtureTag(operationId, 'combo'), + alias: `${fixtureTag(operationId, 'combo')}-alias`, + content: 'choice content', + }); + + const setItemsResult = await callDocOperation('contentControls.choiceList.setItems', { + sessionId, + target, + items: CHOICE_ITEMS, + }); + assertMutationSuccess('contentControls.choiceList.setItems', setItemsResult); + + return { target, items: CHOICE_ITEMS }; + } + + async function seedRepeatingSection( + sessionId: string, + operationId: ContentControlsCommandId, + seedWithFirstItem: boolean, + ): Promise { + const target = await createControl(sessionId, operationId, { + kind: 'block', + controlType: 'repeatingSection', + tag: fixtureTag(operationId, 'repeating'), + alias: `${fixtureTag(operationId, 'repeating')}-alias`, + content: 'repeating section host', + }); + + if (seedWithFirstItem) { + const seedResult = await callDocOperation('contentControls.repeatingSection.insertItemBefore', { + sessionId, + target, + index: 0, + }); + assertMutationSuccess('contentControls.repeatingSection.insertItemBefore', seedResult); + } + + return target; + } + + const scenarios: StoryScenario[] = [ + // ----------------------------------------------------------------------- + // A. Core CRUD + Discovery + // ----------------------------------------------------------------------- + { + operationId: 'create.contentControl', + run: async (sessionId) => { + const tag = fixtureTag('create.contentControl', 'created'); + const createResult = await callDocOperation('create.contentControl', { + sessionId, + kind: 'block', + controlType: 'text', + tag, + alias: `${tag}-alias`, + content: 'created by story', + }); + + const listResult = await listControls(sessionId, { tag }); + expect(listResult?.total).toBeGreaterThanOrEqual(1); + + return createResult; + }, + }, + { + operationId: 'contentControls.list', + run: async (sessionId) => { + const listResult = await listControls(sessionId); + expect(listResult?.total).toBeGreaterThanOrEqual(1); + return listResult; + }, + }, + { + operationId: 'contentControls.get', + prepare: async (sessionId) => { + const target = await createControl(sessionId, 'contentControls.get', { + kind: 'block', + controlType: 'text', + tag: fixtureTag('contentControls.get', 'target'), + alias: fixtureTag('contentControls.get', 'alias'), + content: 'get target content', + }); + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.get', fixture); + const info = await getControl(sessionId, target); + expect(info?.target?.nodeId).toBe(target.nodeId); + return info; + }, + }, + { + operationId: 'contentControls.listInRange', + prepare: async (sessionId) => { + const paragraphIds = await findParagraphNodeIds(sessionId); + return { + startBlockId: paragraphIds[0], + endBlockId: paragraphIds[paragraphIds.length - 1], + }; + }, + run: async (sessionId, fixture) => { + const startBlockId = fixture?.startBlockId; + const endBlockId = fixture?.endBlockId; + if (!startBlockId || !endBlockId) { + throw new Error('contentControls.listInRange requires start/end block ids.'); + } + + const listResult = await callDocOperation('contentControls.listInRange', { + sessionId, + startBlockId, + endBlockId, + }); + + expect(listResult?.total).toBeGreaterThanOrEqual(1); + return listResult; + }, + }, + { + operationId: 'contentControls.selectByTag', + prepare: async (sessionId) => { + const tag = fixtureTag('contentControls.selectByTag', 'selected-tag'); + const target = await createControl(sessionId, 'contentControls.selectByTag', { + kind: 'block', + controlType: 'text', + tag, + alias: `${tag}-alias`, + content: 'tag selection text', + }); + return { target, tag }; + }, + run: async (sessionId, fixture) => { + const tag = fixture?.tag; + const target = requireTarget('contentControls.selectByTag', fixture); + if (!tag) throw new Error('contentControls.selectByTag requires a tag fixture.'); + + const result = await callDocOperation('contentControls.selectByTag', { + sessionId, + tag, + }); + + const nodeIds = (result?.items ?? []).map((item: any) => item?.target?.nodeId); + expect(nodeIds).toContain(target.nodeId); + return result; + }, + }, + { + operationId: 'contentControls.selectByTitle', + prepare: async (sessionId) => { + const alias = fixtureTag('contentControls.selectByTitle', 'selected-title'); + const target = await createControl(sessionId, 'contentControls.selectByTitle', { + kind: 'block', + controlType: 'text', + tag: `${alias}-tag`, + alias, + content: 'title selection text', + }); + return { target, alias }; + }, + run: async (sessionId, fixture) => { + const alias = fixture?.alias; + const target = requireTarget('contentControls.selectByTitle', fixture); + if (!alias) throw new Error('contentControls.selectByTitle requires an alias fixture.'); + + const result = await callDocOperation('contentControls.selectByTitle', { + sessionId, + title: alias, + }); + + const nodeIds = (result?.items ?? []).map((item: any) => item?.target?.nodeId); + expect(nodeIds).toContain(target.nodeId); + return result; + }, + }, + { + operationId: 'contentControls.listChildren', + prepare: async (sessionId) => { + const nested = await createNestedControls(sessionId, 'contentControls.listChildren'); + return { + childTarget: nested.childTarget, + parentTarget: nested.parentTarget, + }; + }, + run: async (sessionId, fixture) => { + const parentTarget = requireParentTarget('contentControls.listChildren', fixture); + const childTarget = requireChildTarget('contentControls.listChildren', fixture); + + const result = await callDocOperation('contentControls.listChildren', { + sessionId, + target: parentTarget, + }); + + const nodeIds = (result?.items ?? []).map((item: any) => item?.target?.nodeId); + expect(nodeIds).toContain(childTarget.nodeId); + return result; + }, + }, + { + operationId: 'contentControls.getParent', + prepare: async (sessionId) => { + return findGetParentFixture(sessionId); + }, + run: async (sessionId, fixture) => { + const childTarget = requireChildTarget('contentControls.getParent', fixture); + const expectedParentNodeId = fixture?.parentTarget?.nodeId; + + const result = await callDocOperation('contentControls.getParent', { + sessionId, + target: childTarget, + }); + + if (expectedParentNodeId) { + expect(result?.target?.nodeId ?? result?.id).toBe(expectedParentNodeId); + } else { + expect(result === null || result?.target?.nodeId == null).toBe(true); + } + return result; + }, + }, + { + operationId: 'contentControls.wrap', + prepare: async (sessionId) => { + const target = await createControl(sessionId, 'contentControls.wrap', { + kind: 'block', + controlType: 'text', + tag: fixtureTag('contentControls.wrap', 'target'), + alias: `${fixtureTag('contentControls.wrap', 'target')}-alias`, + content: 'wrap me', + }); + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.wrap', fixture); + const result = await callDocOperation('contentControls.wrap', { + sessionId, + target, + kind: 'block', + tag: fixtureTag('contentControls.wrap', 'wrapper'), + alias: `${fixtureTag('contentControls.wrap', 'wrapper')}-alias`, + }); + + expect(result?.updatedRef?.nodeId).toBeDefined(); + expect(result?.updatedRef?.nodeId).not.toBe(target.nodeId); + return result; + }, + }, + { + operationId: 'contentControls.unwrap', + prepare: async (sessionId) => { + const target = await createControl(sessionId, 'contentControls.unwrap', { + kind: 'block', + controlType: 'text', + tag: fixtureTag('contentControls.unwrap', 'target'), + alias: `${fixtureTag('contentControls.unwrap', 'target')}-alias`, + content: 'unwrap me', + }); + + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.unwrap', fixture); + return callDocOperation('contentControls.unwrap', { sessionId, target }); + }, + }, + { + operationId: 'contentControls.delete', + prepare: async (sessionId) => { + const tag = fixtureTag('contentControls.delete', 'delete-target'); + const target = await createControl(sessionId, 'contentControls.delete', { + kind: 'block', + controlType: 'text', + tag, + alias: `${tag}-alias`, + content: 'delete me', + }); + return { target, tag }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.delete', fixture); + const tag = fixture?.tag; + const result = await callDocOperation('contentControls.delete', { sessionId, target }); + + if (tag) { + const listAfter = await callDocOperation('contentControls.selectByTag', { sessionId, tag }); + expect((listAfter?.items ?? []).some((item: any) => item?.target?.nodeId === target.nodeId)).toBe(false); + } + + return result; + }, + }, + { + operationId: 'contentControls.copy', + prepare: async (sessionId) => { + const source = await createControl(sessionId, 'contentControls.copy', { + kind: 'block', + controlType: 'text', + tag: fixtureTag('contentControls.copy', 'source'), + alias: `${fixtureTag('contentControls.copy', 'source')}-alias`, + content: 'copy source', + }); + + const destination = await createControl(sessionId, 'contentControls.copy', { + kind: 'block', + controlType: 'text', + tag: fixtureTag('contentControls.copy', 'destination'), + alias: `${fixtureTag('contentControls.copy', 'destination')}-alias`, + content: 'copy destination', + }); + + return { target: source, secondaryTarget: destination }; + }, + run: async (sessionId, fixture) => { + const source = requireTarget('contentControls.copy', fixture); + const destination = requireSecondaryTarget('contentControls.copy', fixture); + + const result = await callDocOperation('contentControls.copy', { + sessionId, + target: source, + destination, + }); + + expect(result?.updatedRef?.nodeId).toBeDefined(); + expect(result?.updatedRef?.nodeId).not.toBe(source.nodeId); + return result; + }, + }, + { + operationId: 'contentControls.move', + prepare: async (sessionId) => { + const source = await createControl(sessionId, 'contentControls.move', { + kind: 'block', + controlType: 'text', + tag: fixtureTag('contentControls.move', 'source'), + alias: `${fixtureTag('contentControls.move', 'source')}-alias`, + content: 'move source', + }); + + const destination = await createControl(sessionId, 'contentControls.move', { + kind: 'block', + controlType: 'text', + tag: fixtureTag('contentControls.move', 'destination'), + alias: `${fixtureTag('contentControls.move', 'destination')}-alias`, + content: 'move destination', + }); + + return { target: source, secondaryTarget: destination }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.move', fixture); + const destination = requireSecondaryTarget('contentControls.move', fixture); + + const result = await callDocOperation('contentControls.move', { + sessionId, + target, + destination, + }); + + const movedInfo = await getControl(sessionId, target); + expect(movedInfo?.target?.nodeId).toBe(target.nodeId); + + return result; + }, + }, + { + operationId: 'contentControls.patch', + prepare: async (sessionId) => { + const target = await createControl(sessionId, 'contentControls.patch', { + kind: 'block', + controlType: 'text', + tag: fixtureTag('contentControls.patch', 'target'), + alias: `${fixtureTag('contentControls.patch', 'target')}-alias`, + content: 'patch target', + }); + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.patch', fixture); + + const patchedAlias = `${fixtureTag('contentControls.patch', 'alias')}-updated`; + const patchedTag = `${fixtureTag('contentControls.patch', 'tag')}-updated`; + + const result = await callDocOperation('contentControls.patch', { + sessionId, + target, + alias: patchedAlias, + tag: patchedTag, + color: '#3366FF', + showingPlaceholder: false, + temporary: false, + tabIndex: 7, + }); + + const info = await getControl(sessionId, target); + expect(info?.properties?.alias).toBe(patchedAlias); + expect(info?.properties?.tag).toBe(patchedTag); + + return result; + }, + }, + { + operationId: 'contentControls.setLockMode', + prepare: async (sessionId) => { + const target = await createControl(sessionId, 'contentControls.setLockMode', { + kind: 'block', + controlType: 'text', + tag: fixtureTag('contentControls.setLockMode', 'target'), + alias: `${fixtureTag('contentControls.setLockMode', 'target')}-alias`, + content: 'lock mode target', + }); + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.setLockMode', fixture); + const result = await callDocOperation('contentControls.setLockMode', { + sessionId, + target, + lockMode: 'contentLocked', + }); + + const info = await getControl(sessionId, target); + expect(info?.lockMode).toBe('contentLocked'); + + return result; + }, + }, + { + operationId: 'contentControls.setType', + prepare: async (sessionId) => { + const target = await createControl(sessionId, 'contentControls.setType', { + kind: 'block', + controlType: 'text', + tag: fixtureTag('contentControls.setType', 'target'), + alias: `${fixtureTag('contentControls.setType', 'target')}-alias`, + content: 'set type target', + }); + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.setType', fixture); + const result = await callDocOperation('contentControls.setType', { + sessionId, + target, + controlType: 'date', + }); + + const info = await getControl(sessionId, target); + expect(info?.controlType).toBe('date'); + + return result; + }, + }, + { + operationId: 'contentControls.getContent', + prepare: async (sessionId) => { + const target = await createControl(sessionId, 'contentControls.getContent', { + kind: 'inline', + controlType: 'text', + tag: fixtureTag('contentControls.getContent', 'target'), + alias: `${fixtureTag('contentControls.getContent', 'target')}-alias`, + content: 'content read target', + }); + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.getContent', fixture); + const result = await getControlContent(sessionId, target); + expect(result?.content).toContain('content read target'); + return result; + }, + }, + { + operationId: 'contentControls.replaceContent', + prepare: async (sessionId) => { + const target = await createControl(sessionId, 'contentControls.replaceContent', { + kind: 'inline', + controlType: 'text', + tag: fixtureTag('contentControls.replaceContent', 'target'), + alias: `${fixtureTag('contentControls.replaceContent', 'target')}-alias`, + content: 'replace this text', + }); + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.replaceContent', fixture); + const replacedText = 'replacement text from story'; + + const result = await callDocOperation('contentControls.replaceContent', { + sessionId, + target, + content: replacedText, + format: 'text', + }); + + const contentResult = await getControlContent(sessionId, target); + expect(contentResult?.content).toBe(replacedText); + + return result; + }, + }, + { + operationId: 'contentControls.clearContent', + prepare: async (sessionId) => { + const target = await createControl(sessionId, 'contentControls.clearContent', { + kind: 'block', + controlType: 'text', + tag: fixtureTag('contentControls.clearContent', 'target'), + alias: `${fixtureTag('contentControls.clearContent', 'target')}-alias`, + content: 'clear this text', + }); + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.clearContent', fixture); + const result = await callDocOperation('contentControls.clearContent', { sessionId, target }); + + const contentResult = await getControlContent(sessionId, target); + expect(contentResult?.content).toBe(''); + + return result; + }, + }, + { + operationId: 'contentControls.appendContent', + prepare: async (sessionId) => { + const target = await createControl(sessionId, 'contentControls.appendContent', { + kind: 'inline', + controlType: 'text', + tag: fixtureTag('contentControls.appendContent', 'target'), + alias: `${fixtureTag('contentControls.appendContent', 'target')}-alias`, + content: 'append-start', + }); + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.appendContent', fixture); + const suffix = '-append-end'; + + const result = await callDocOperation('contentControls.appendContent', { + sessionId, + target, + content: suffix, + format: 'text', + }); + + const contentResult = await getControlContent(sessionId, target); + expect(contentResult?.content).toContain('append-start'); + expect(contentResult?.content).toContain(suffix); + + return result; + }, + }, + { + operationId: 'contentControls.prependContent', + prepare: async (sessionId) => { + const target = await createControl(sessionId, 'contentControls.prependContent', { + kind: 'inline', + controlType: 'text', + tag: fixtureTag('contentControls.prependContent', 'target'), + alias: `${fixtureTag('contentControls.prependContent', 'target')}-alias`, + content: 'prepend-tail', + }); + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.prependContent', fixture); + const prefix = 'prepend-head-'; + + const result = await callDocOperation('contentControls.prependContent', { + sessionId, + target, + content: prefix, + format: 'text', + }); + + const contentResult = await getControlContent(sessionId, target); + expect(contentResult?.content.startsWith(prefix)).toBe(true); + + return result; + }, + }, + { + operationId: 'contentControls.insertBefore', + prepare: async (sessionId) => { + const target = await createControl(sessionId, 'contentControls.insertBefore', { + kind: 'block', + controlType: 'text', + tag: fixtureTag('contentControls.insertBefore', 'target'), + alias: `${fixtureTag('contentControls.insertBefore', 'target')}-alias`, + content: 'insert-before-anchor', + }); + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.insertBefore', fixture); + const insertedText = fixtureTag('contentControls.insertBefore', 'inserted-text'); + + const result = await callDocOperation('contentControls.insertBefore', { + sessionId, + target, + content: insertedText, + format: 'text', + }); + + const findResult = await callDocOperation('find', { + sessionId, + type: 'text', + pattern: insertedText, + }); + expect(findResult?.total).toBeGreaterThanOrEqual(1); + + return result; + }, + }, + { + operationId: 'contentControls.insertAfter', + prepare: async (sessionId) => { + const target = await createControl(sessionId, 'contentControls.insertAfter', { + kind: 'block', + controlType: 'text', + tag: fixtureTag('contentControls.insertAfter', 'target'), + alias: `${fixtureTag('contentControls.insertAfter', 'target')}-alias`, + content: 'insert-after-anchor', + }); + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.insertAfter', fixture); + const insertedText = fixtureTag('contentControls.insertAfter', 'inserted-text'); + + const result = await callDocOperation('contentControls.insertAfter', { + sessionId, + target, + content: insertedText, + format: 'text', + }); + + const findResult = await callDocOperation('find', { + sessionId, + type: 'text', + pattern: insertedText, + }); + expect(findResult?.total).toBeGreaterThanOrEqual(1); + + return result; + }, + }, + + // ----------------------------------------------------------------------- + // B. Data Binding + Raw/Compatibility + // ----------------------------------------------------------------------- + { + operationId: 'contentControls.getBinding', + prepare: async (sessionId) => { + const target = await createControl(sessionId, 'contentControls.getBinding', { + kind: 'block', + controlType: 'text', + tag: fixtureTag('contentControls.getBinding', 'target'), + alias: `${fixtureTag('contentControls.getBinding', 'target')}-alias`, + content: 'binding target', + }); + + const setBindingResult = await callDocOperation('contentControls.setBinding', { + sessionId, + target, + storeItemId: '{binding-store-id}', + xpath: '/root/binding/path', + prefixMappings: 'xmlns:ns="http://example.com"', + }); + assertMutationSuccess('contentControls.setBinding', setBindingResult); + + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.getBinding', fixture); + const result = await callDocOperation('contentControls.getBinding', { + sessionId, + target, + }); + + expect(result?.storeItemId).toBe('{binding-store-id}'); + expect(result?.xpath).toBe('/root/binding/path'); + return result; + }, + }, + { + operationId: 'contentControls.setBinding', + prepare: async (sessionId) => { + const target = await createControl(sessionId, 'contentControls.setBinding', { + kind: 'block', + controlType: 'text', + tag: fixtureTag('contentControls.setBinding', 'target'), + alias: `${fixtureTag('contentControls.setBinding', 'target')}-alias`, + content: 'binding mutation target', + }); + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.setBinding', fixture); + + const result = await callDocOperation('contentControls.setBinding', { + sessionId, + target, + storeItemId: '{set-binding-store}', + xpath: '/root/setBinding', + prefixMappings: 'xmlns:abc="http://example.com/abc"', + }); + + const binding = await callDocOperation('contentControls.getBinding', { + sessionId, + target, + }); + expect(binding?.storeItemId).toBe('{set-binding-store}'); + expect(binding?.xpath).toBe('/root/setBinding'); + + return result; + }, + }, + { + operationId: 'contentControls.clearBinding', + prepare: async (sessionId) => { + const target = await createControl(sessionId, 'contentControls.clearBinding', { + kind: 'block', + controlType: 'text', + tag: fixtureTag('contentControls.clearBinding', 'target'), + alias: `${fixtureTag('contentControls.clearBinding', 'target')}-alias`, + content: 'clear binding target', + }); + + const setBindingResult = await callDocOperation('contentControls.setBinding', { + sessionId, + target, + storeItemId: '{clear-binding-store}', + xpath: '/root/clearBinding', + }); + assertMutationSuccess('contentControls.setBinding', setBindingResult); + + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.clearBinding', fixture); + + const result = await callDocOperation('contentControls.clearBinding', { + sessionId, + target, + }); + + const binding = await callDocOperation('contentControls.getBinding', { + sessionId, + target, + }); + expect(binding).toBeNull(); + + return result; + }, + }, + { + operationId: 'contentControls.getRawProperties', + prepare: async (sessionId) => { + const target = await createControl(sessionId, 'contentControls.getRawProperties', { + kind: 'block', + controlType: 'text', + tag: fixtureTag('contentControls.getRawProperties', 'target'), + alias: `${fixtureTag('contentControls.getRawProperties', 'target')}-alias`, + content: 'raw properties target', + }); + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.getRawProperties', fixture); + const result = await callDocOperation('contentControls.getRawProperties', { + sessionId, + target, + }); + expect(typeof result?.properties).toBe('object'); + expect(result?.properties).not.toBeNull(); + return result; + }, + }, + { + operationId: 'contentControls.patchRawProperties', + prepare: async (sessionId) => { + const target = await createControl(sessionId, 'contentControls.patchRawProperties', { + kind: 'block', + controlType: 'text', + tag: fixtureTag('contentControls.patchRawProperties', 'target'), + alias: `${fixtureTag('contentControls.patchRawProperties', 'target')}-alias`, + content: 'patch raw target', + }); + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.patchRawProperties', fixture); + + const result = await callDocOperation('contentControls.patchRawProperties', { + sessionId, + target, + patches: [ + { + op: 'set', + name: 'w:custom', + element: { + attributes: { 'w:val': 'story-custom-value' }, + }, + }, + ], + }); + + const raw = await callDocOperation('contentControls.getRawProperties', { + sessionId, + target, + }); + const rawString = JSON.stringify(raw?.properties ?? {}); + expect(rawString).toContain('w:custom'); + + return result; + }, + }, + { + operationId: 'contentControls.validateWordCompatibility', + prepare: async (sessionId) => { + const target = await createControl(sessionId, 'contentControls.validateWordCompatibility', { + kind: 'block', + controlType: 'text', + tag: fixtureTag('contentControls.validateWordCompatibility', 'target'), + alias: `${fixtureTag('contentControls.validateWordCompatibility', 'target')}-alias`, + content: 'compatibility target', + }); + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.validateWordCompatibility', fixture); + const result = await callDocOperation('contentControls.validateWordCompatibility', { + sessionId, + target, + }); + expect(typeof result?.compatible).toBe('boolean'); + expect(Array.isArray(result?.diagnostics)).toBe(true); + return result; + }, + }, + { + operationId: 'contentControls.normalizeWordCompatibility', + allowNoOpFailure: true, + prepare: async (sessionId) => { + const target = await createControl(sessionId, 'contentControls.normalizeWordCompatibility', { + kind: 'block', + controlType: 'text', + tag: fixtureTag('contentControls.normalizeWordCompatibility', 'target'), + alias: `${fixtureTag('contentControls.normalizeWordCompatibility', 'target')}-alias`, + content: 'normalize compatibility target', + }); + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.normalizeWordCompatibility', fixture); + return callDocOperation( + 'contentControls.normalizeWordCompatibility', + { + sessionId, + target, + }, + { allowCommandFailure: true }, + ); + }, + }, + { + operationId: 'contentControls.normalizeTagPayload', + prepare: async (sessionId) => { + const plainTag = 'not-json-tag'; + const target = await createControl(sessionId, 'contentControls.normalizeTagPayload', { + kind: 'block', + controlType: 'text', + tag: plainTag, + alias: `${fixtureTag('contentControls.normalizeTagPayload', 'target')}-alias`, + content: 'normalize tag target', + }); + return { target, tag: plainTag }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.normalizeTagPayload', fixture); + const originalTag = fixture?.tag; + if (!originalTag) throw new Error('contentControls.normalizeTagPayload requires original tag fixture.'); + + const result = await callDocOperation('contentControls.normalizeTagPayload', { + sessionId, + target, + }); + + const info = await getControl(sessionId, target); + const normalizedTag = info?.properties?.tag; + expect(typeof normalizedTag).toBe('string'); + expect(normalizedTag).toContain('"value"'); + expect(normalizedTag).toContain(originalTag); + + return result; + }, + }, + + // ----------------------------------------------------------------------- + // C. Typed Controls + // ----------------------------------------------------------------------- + { + operationId: 'contentControls.text.setMultiline', + prepare: async (sessionId) => { + const target = await createControl(sessionId, 'contentControls.text.setMultiline', { + kind: 'block', + controlType: 'text', + tag: fixtureTag('contentControls.text.setMultiline', 'target'), + alias: `${fixtureTag('contentControls.text.setMultiline', 'target')}-alias`, + content: 'text multiline target', + }); + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.text.setMultiline', fixture); + const result = await callDocOperation('contentControls.text.setMultiline', { + sessionId, + target, + multiline: true, + }); + + const info = await getControl(sessionId, target); + expect(info?.properties?.multiline).toBe(true); + + return result; + }, + }, + { + operationId: 'contentControls.text.setValue', + prepare: async (sessionId) => { + const target = await createControl(sessionId, 'contentControls.text.setValue', { + kind: 'inline', + controlType: 'text', + tag: fixtureTag('contentControls.text.setValue', 'target'), + alias: `${fixtureTag('contentControls.text.setValue', 'target')}-alias`, + content: 'text set value target', + }); + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.text.setValue', fixture); + const value = 'text value set by story'; + + const result = await callDocOperation('contentControls.text.setValue', { + sessionId, + target, + value, + }); + + const contentResult = await getControlContent(sessionId, target); + expect(contentResult?.content).toBe(value); + + return result; + }, + }, + { + operationId: 'contentControls.text.clearValue', + prepare: async (sessionId) => { + const target = await createControl(sessionId, 'contentControls.text.clearValue', { + kind: 'inline', + controlType: 'text', + tag: fixtureTag('contentControls.text.clearValue', 'target'), + alias: `${fixtureTag('contentControls.text.clearValue', 'target')}-alias`, + content: 'text clear target', + }); + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.text.clearValue', fixture); + const result = await callDocOperation('contentControls.text.clearValue', { + sessionId, + target, + }); + + const contentResult = await getControlContent(sessionId, target); + expect(typeof contentResult?.content).toBe('string'); + expect(contentResult?.content).not.toBeUndefined(); + + return result; + }, + }, + { + operationId: 'contentControls.date.setValue', + prepare: async (sessionId) => { + const target = await createControl(sessionId, 'contentControls.date.setValue', { + kind: 'block', + controlType: 'date', + tag: fixtureTag('contentControls.date.setValue', 'target'), + alias: `${fixtureTag('contentControls.date.setValue', 'target')}-alias`, + content: 'date value target', + }); + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.date.setValue', fixture); + + const result = await callDocOperation('contentControls.date.setValue', { + sessionId, + target, + value: '2026-01-15T00:00:00Z', + }); + + const info = await getControl(sessionId, target); + expect(info?.controlType).toBe('date'); + + return result; + }, + }, + { + operationId: 'contentControls.date.clearValue', + prepare: async (sessionId) => { + const target = await createControl(sessionId, 'contentControls.date.clearValue', { + kind: 'block', + controlType: 'date', + tag: fixtureTag('contentControls.date.clearValue', 'target'), + alias: `${fixtureTag('contentControls.date.clearValue', 'target')}-alias`, + content: 'date clear target', + }); + + const seedValueResult = await callDocOperation('contentControls.date.setValue', { + sessionId, + target, + value: '2026-02-20T00:00:00Z', + }); + assertMutationSuccess('contentControls.date.setValue', seedValueResult); + + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.date.clearValue', fixture); + return callDocOperation('contentControls.date.clearValue', { + sessionId, + target, + }); + }, + }, + { + operationId: 'contentControls.date.setDisplayFormat', + prepare: async (sessionId) => { + const target = await createControl(sessionId, 'contentControls.date.setDisplayFormat', { + kind: 'block', + controlType: 'date', + tag: fixtureTag('contentControls.date.setDisplayFormat', 'target'), + alias: `${fixtureTag('contentControls.date.setDisplayFormat', 'target')}-alias`, + content: 'date format target', + }); + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.date.setDisplayFormat', fixture); + + const result = await callDocOperation('contentControls.date.setDisplayFormat', { + sessionId, + target, + format: 'yyyy-MM-dd', + }); + + const info = await getControl(sessionId, target); + expect(info?.properties?.dateFormat).toBe('yyyy-MM-dd'); + + return result; + }, + }, + { + operationId: 'contentControls.date.setDisplayLocale', + prepare: async (sessionId) => { + const target = await createControl(sessionId, 'contentControls.date.setDisplayLocale', { + kind: 'block', + controlType: 'date', + tag: fixtureTag('contentControls.date.setDisplayLocale', 'target'), + alias: `${fixtureTag('contentControls.date.setDisplayLocale', 'target')}-alias`, + content: 'date locale target', + }); + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.date.setDisplayLocale', fixture); + + const result = await callDocOperation('contentControls.date.setDisplayLocale', { + sessionId, + target, + locale: 'en-US', + }); + + const info = await getControl(sessionId, target); + expect(info?.properties?.dateLocale).toBe('en-US'); + + return result; + }, + }, + { + operationId: 'contentControls.date.setStorageFormat', + prepare: async (sessionId) => { + const target = await createControl(sessionId, 'contentControls.date.setStorageFormat', { + kind: 'block', + controlType: 'date', + tag: fixtureTag('contentControls.date.setStorageFormat', 'target'), + alias: `${fixtureTag('contentControls.date.setStorageFormat', 'target')}-alias`, + content: 'date storage format target', + }); + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.date.setStorageFormat', fixture); + + const result = await callDocOperation('contentControls.date.setStorageFormat', { + sessionId, + target, + format: 'dateTime', + }); + + const info = await getControl(sessionId, target); + expect(info?.properties?.storageFormat).toBe('dateTime'); + + return result; + }, + }, + { + operationId: 'contentControls.date.setCalendar', + prepare: async (sessionId) => { + const target = await createControl(sessionId, 'contentControls.date.setCalendar', { + kind: 'block', + controlType: 'date', + tag: fixtureTag('contentControls.date.setCalendar', 'target'), + alias: `${fixtureTag('contentControls.date.setCalendar', 'target')}-alias`, + content: 'date calendar target', + }); + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.date.setCalendar', fixture); + + const result = await callDocOperation('contentControls.date.setCalendar', { + sessionId, + target, + calendar: 'gregorian', + }); + + const info = await getControl(sessionId, target); + expect(info?.properties?.calendar).toBe('gregorian'); + + return result; + }, + }, + { + operationId: 'contentControls.checkbox.getState', + prepare: async (sessionId) => { + const target = await createControl(sessionId, 'contentControls.checkbox.getState', { + kind: 'block', + controlType: 'checkbox', + tag: fixtureTag('contentControls.checkbox.getState', 'target'), + alias: `${fixtureTag('contentControls.checkbox.getState', 'target')}-alias`, + content: 'checkbox state target', + }); + + const setStateResult = await callDocOperation('contentControls.checkbox.setState', { + sessionId, + target, + checked: true, + }); + assertMutationSuccess('contentControls.checkbox.setState', setStateResult); + + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.checkbox.getState', fixture); + const result = await callDocOperation('contentControls.checkbox.getState', { + sessionId, + target, + }); + expect(result?.checked).toBe(true); + return result; + }, + }, + { + operationId: 'contentControls.checkbox.setState', + prepare: async (sessionId) => { + const target = await createControl(sessionId, 'contentControls.checkbox.setState', { + kind: 'block', + controlType: 'checkbox', + tag: fixtureTag('contentControls.checkbox.setState', 'target'), + alias: `${fixtureTag('contentControls.checkbox.setState', 'target')}-alias`, + content: 'checkbox set state target', + }); + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.checkbox.setState', fixture); + + const result = await callDocOperation('contentControls.checkbox.setState', { + sessionId, + target, + checked: true, + }); + + const state = await callDocOperation('contentControls.checkbox.getState', { + sessionId, + target, + }); + expect(state?.checked).toBe(true); + + return result; + }, + }, + { + operationId: 'contentControls.checkbox.toggle', + prepare: async (sessionId) => { + const target = await createControl(sessionId, 'contentControls.checkbox.toggle', { + kind: 'block', + controlType: 'checkbox', + tag: fixtureTag('contentControls.checkbox.toggle', 'target'), + alias: `${fixtureTag('contentControls.checkbox.toggle', 'target')}-alias`, + content: 'checkbox toggle target', + }); + + const setStateResult = await callDocOperation('contentControls.checkbox.setState', { + sessionId, + target, + checked: false, + }); + assertMutationSuccess('contentControls.checkbox.setState', setStateResult); + + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.checkbox.toggle', fixture); + + const result = await callDocOperation('contentControls.checkbox.toggle', { + sessionId, + target, + }); + + const state = await callDocOperation('contentControls.checkbox.getState', { + sessionId, + target, + }); + expect(state?.checked).toBe(true); + + return result; + }, + }, + { + operationId: 'contentControls.checkbox.setSymbolPair', + prepare: async (sessionId) => { + const target = await createControl(sessionId, 'contentControls.checkbox.setSymbolPair', { + kind: 'block', + controlType: 'checkbox', + tag: fixtureTag('contentControls.checkbox.setSymbolPair', 'target'), + alias: `${fixtureTag('contentControls.checkbox.setSymbolPair', 'target')}-alias`, + content: 'checkbox symbols target', + }); + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.checkbox.setSymbolPair', fixture); + + const result = await callDocOperation('contentControls.checkbox.setSymbolPair', { + sessionId, + target, + checkedSymbol: { font: 'Wingdings', char: '00FE' }, + uncheckedSymbol: { font: 'Wingdings', char: '00A8' }, + }); + + const info = await getControl(sessionId, target); + expect(info?.properties?.checkedSymbol?.font).toBe('Wingdings'); + expect(info?.properties?.uncheckedSymbol?.font).toBe('Wingdings'); + + return result; + }, + }, + { + operationId: 'contentControls.choiceList.getItems', + prepare: async (sessionId) => { + const seeded = await seedChoiceControl(sessionId, 'contentControls.choiceList.getItems'); + + const setSelectedResult = await callDocOperation('contentControls.choiceList.setSelected', { + sessionId, + target: seeded.target, + value: 'two', + }); + assertMutationSuccess('contentControls.choiceList.setSelected', setSelectedResult); + + return { + target: seeded.target, + choiceItems: seeded.items, + }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.choiceList.getItems', fixture); + const result = await callDocOperation('contentControls.choiceList.getItems', { + sessionId, + target, + }); + expect(result?.items?.length).toBeGreaterThanOrEqual(3); + expect(result?.selectedValue).toBe('two'); + return result; + }, + }, + { + operationId: 'contentControls.choiceList.setItems', + prepare: async (sessionId) => { + const seeded = await seedChoiceControl(sessionId, 'contentControls.choiceList.setItems'); + return { + target: seeded.target, + choiceItems: seeded.items, + }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.choiceList.setItems', fixture); + const items: ChoiceItem[] = [ + { displayText: 'Apple', value: 'apple' }, + { displayText: 'Banana', value: 'banana' }, + ]; + + const result = await callDocOperation('contentControls.choiceList.setItems', { + sessionId, + target, + items, + }); + + const info = await callDocOperation('contentControls.choiceList.getItems', { + sessionId, + target, + }); + expect(info?.items).toEqual(items); + + return result; + }, + }, + { + operationId: 'contentControls.choiceList.setSelected', + prepare: async (sessionId) => { + const seeded = await seedChoiceControl(sessionId, 'contentControls.choiceList.setSelected'); + return { + target: seeded.target, + choiceItems: seeded.items, + }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.choiceList.setSelected', fixture); + + const result = await callDocOperation('contentControls.choiceList.setSelected', { + sessionId, + target, + value: 'three', + }); + + const info = await callDocOperation('contentControls.choiceList.getItems', { + sessionId, + target, + }); + expect(info?.selectedValue).toBe('three'); + + return result; + }, + }, + + // ----------------------------------------------------------------------- + // D. Repeating Section + Group + // ----------------------------------------------------------------------- + { + operationId: 'contentControls.repeatingSection.listItems', + prepare: async (sessionId) => { + const target = await seedRepeatingSection(sessionId, 'contentControls.repeatingSection.listItems', true); + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.repeatingSection.listItems', fixture); + const result = await callDocOperation('contentControls.repeatingSection.listItems', { + sessionId, + target, + }); + expect(result?.total).toBeGreaterThanOrEqual(1); + return result; + }, + }, + { + operationId: 'contentControls.repeatingSection.insertItemBefore', + prepare: async (sessionId) => { + const target = await seedRepeatingSection( + sessionId, + 'contentControls.repeatingSection.insertItemBefore', + false, + ); + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.repeatingSection.insertItemBefore', fixture); + + const result = await callDocOperation('contentControls.repeatingSection.insertItemBefore', { + sessionId, + target, + index: 0, + }); + + const list = await callDocOperation('contentControls.repeatingSection.listItems', { + sessionId, + target, + }); + expect(list?.total).toBe(1); + + return result; + }, + }, + { + operationId: 'contentControls.repeatingSection.insertItemAfter', + prepare: async (sessionId) => { + const target = await seedRepeatingSection(sessionId, 'contentControls.repeatingSection.insertItemAfter', true); + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.repeatingSection.insertItemAfter', fixture); + + const result = await callDocOperation('contentControls.repeatingSection.insertItemAfter', { + sessionId, + target, + index: 0, + }); + + const list = await callDocOperation('contentControls.repeatingSection.listItems', { + sessionId, + target, + }); + expect(list?.total).toBeGreaterThanOrEqual(2); + + return result; + }, + }, + { + operationId: 'contentControls.repeatingSection.cloneItem', + prepare: async (sessionId) => { + const target = await seedRepeatingSection(sessionId, 'contentControls.repeatingSection.cloneItem', true); + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.repeatingSection.cloneItem', fixture); + + const result = await callDocOperation('contentControls.repeatingSection.cloneItem', { + sessionId, + target, + index: 0, + }); + + const list = await callDocOperation('contentControls.repeatingSection.listItems', { + sessionId, + target, + }); + expect(list?.total).toBeGreaterThanOrEqual(2); + + return result; + }, + }, + { + operationId: 'contentControls.repeatingSection.deleteItem', + prepare: async (sessionId) => { + const target = await seedRepeatingSection(sessionId, 'contentControls.repeatingSection.deleteItem', true); + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.repeatingSection.deleteItem', fixture); + + const result = await callDocOperation('contentControls.repeatingSection.deleteItem', { + sessionId, + target, + index: 0, + }); + + const list = await callDocOperation('contentControls.repeatingSection.listItems', { + sessionId, + target, + }); + expect(list?.total).toBe(0); + + return result; + }, + }, + { + operationId: 'contentControls.repeatingSection.setAllowInsertDelete', + prepare: async (sessionId) => { + const target = await seedRepeatingSection( + sessionId, + 'contentControls.repeatingSection.setAllowInsertDelete', + false, + ); + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.repeatingSection.setAllowInsertDelete', fixture); + + const result = await callDocOperation('contentControls.repeatingSection.setAllowInsertDelete', { + sessionId, + target, + allow: true, + }); + + const info = await getControl(sessionId, target); + expect(info?.properties?.allowInsertDelete).toBe(true); + + return result; + }, + }, + { + operationId: 'contentControls.group.wrap', + prepare: async (sessionId) => { + const target = await createControl(sessionId, 'contentControls.group.wrap', { + kind: 'block', + controlType: 'text', + tag: fixtureTag('contentControls.group.wrap', 'target'), + alias: `${fixtureTag('contentControls.group.wrap', 'target')}-alias`, + content: 'group wrap target', + }); + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.group.wrap', fixture); + + const result = await callDocOperation('contentControls.group.wrap', { + sessionId, + target, + }); + + const wrappedTarget = normalizeTargetCandidate(result?.updatedRef); + if (!wrappedTarget) { + throw new Error('contentControls.group.wrap did not return updatedRef.'); + } + + expect(wrappedTarget.kind).toBe('block'); + expect(wrappedTarget.nodeType).toBe('sdt'); + + return result; + }, + }, + { + operationId: 'contentControls.group.ungroup', + prepare: async (sessionId) => { + const target = await createControl(sessionId, 'contentControls.group.ungroup', { + kind: 'block', + controlType: 'group', + tag: fixtureTag('contentControls.group.ungroup', 'target'), + alias: `${fixtureTag('contentControls.group.ungroup', 'target')}-alias`, + content: 'group ungroup target', + }); + return { target }; + }, + run: async (sessionId, fixture) => { + const target = requireTarget('contentControls.group.ungroup', fixture); + return callDocOperation('contentControls.group.ungroup', { + sessionId, + target, + }); + }, + }, + ]; + + it('covers every content-controls command currently defined on this branch', () => { + const scenarioIds = scenarios.map((scenario) => scenario.operationId); + expect(new Set(scenarioIds).size).toBe(scenarioIds.length); + expect(new Set(scenarioIds)).toEqual(new Set(ALL_CONTENT_CONTROL_COMMAND_IDS)); + }); + + for (const scenario of scenarios) { + it(`${scenario.operationId}: executes and saves source/result docs`, async () => { + const sessionId = makeSessionId(scenario.operationId.replace(/\./g, '-')); + + try { + await openSeedDocument(sessionId, scenario.seedDoc ?? BASE_CONTENT_CONTROLS_DOC); + + const fixture = scenario.prepare ? await scenario.prepare(sessionId) : null; + + await saveSource(sessionId, scenario.operationId); + + const result = await scenario.run(sessionId, fixture); + + if (READ_OPERATION_IDS.has(scenario.operationId)) { + assertReadShape(scenario.operationId, result); + await saveReadOutput(scenario.operationId, result); + } else { + assertMutationSuccess(scenario.operationId, result, scenario.allowNoOpFailure === true); + } + + await saveResult(sessionId, scenario.operationId); + } finally { + await closeSession(sessionId).catch(() => {}); + } + }); + } + + it('writes source/result artifacts for every content-controls command', async () => { + for (const operationId of ALL_CONTENT_CONTROL_COMMAND_IDS) { + await access(outPath(sourceDocNameFor(operationId))); + await access(outPath(resultDocNameFor(operationId))); + + if (READ_OPERATION_IDS.has(operationId)) { + await access(outPath(readOutputNameFor(operationId))); + } + } + }); +}); diff --git a/tests/doc-api-stories/tests/harness.ts b/tests/doc-api-stories/tests/harness.ts index 577ea19e05..5217022a4f 100644 --- a/tests/doc-api-stories/tests/harness.ts +++ b/tests/doc-api-stories/tests/harness.ts @@ -27,27 +27,29 @@ function resolveInvocation(cliBin: string): CliInvocation { } function parseJsonEnvelope(stdout: string, stderr: string): any { - const source = stdout.trim() || stderr.trim(); - if (!source) { + const sources = [stdout.trim(), stderr.trim()].filter((source) => source.length > 0); + if (sources.length === 0) { throw new Error('No CLI JSON envelope output found.'); } - try { - return JSON.parse(source); - } catch { - const lines = source.split(/\r?\n/); - for (let index = 0; index < lines.length; index += 1) { - const candidate = lines.slice(index).join('\n').trim(); - if (!candidate.startsWith('{')) continue; - try { - return JSON.parse(candidate); - } catch { - // continue scanning + for (const source of sources) { + try { + return JSON.parse(source); + } catch { + const lines = source.split(/\r?\n/); + for (let index = 0; index < lines.length; index += 1) { + const candidate = lines.slice(index).join('\n').trim(); + if (!candidate.startsWith('{')) continue; + try { + return JSON.parse(candidate); + } catch { + // continue scanning + } } } } - throw new Error(`Failed to parse CLI JSON envelope:\n${source}`); + throw new Error(`Failed to parse CLI JSON envelope:\n${sources.join('\n')}`); } /** Resolve a test-corpus relative path to its absolute location. */ @@ -67,7 +69,7 @@ export interface StoryContext { /** Return a path inside the results dir. */ outPath(name: string): string; /** Run a raw CLI command with the story's state dir and parse the JSON envelope. */ - runCli(args: string[]): Promise; + runCli(args: string[], options?: { allowError?: boolean }): Promise; } export interface StoryHarnessOptions { @@ -131,19 +133,30 @@ export function useStoryHarness(storyName: string, options: StoryHarnessOptions return dest; }, outPath: (name) => path.join(resultsDir, name), - runCli: async (args) => { + runCli: async (args, options = {}) => { const invocation = resolveInvocation(cliBin); const argv = [...invocation.prefixArgs, ...args, '--output', 'json']; - const { stdout, stderr } = await execFileAsync(invocation.command, argv, { - cwd: REPO_ROOT, - env: { - ...process.env, - SUPERDOC_CLI_STATE_DIR: stateDir, - }, - }); + let stdout = ''; + let stderr = ''; + + try { + const executed = await execFileAsync(invocation.command, argv, { + cwd: REPO_ROOT, + env: { + ...process.env, + SUPERDOC_CLI_STATE_DIR: stateDir, + }, + }); + stdout = executed.stdout; + stderr = executed.stderr; + } catch (error) { + const failed = error as { stdout?: string; stderr?: string }; + stdout = failed.stdout ?? ''; + stderr = failed.stderr ?? ''; + } const envelope = parseJsonEnvelope(stdout, stderr); - if (envelope?.ok === false) { + if (envelope?.ok === false && options.allowError !== true) { const code = envelope.error?.code ?? 'UNKNOWN'; const message = envelope.error?.message ?? 'Unknown CLI error'; throw new Error(`${code}: ${message}`); @@ -177,7 +190,7 @@ export function useStoryHarness(storyName: string, options: StoryHarnessOptions client: clientProxy, copyDoc: (source: string, name?: string) => requireCtx().copyDoc(source, name), outPath: (name: string) => requireCtx().outPath(name), - runCli: (args: string[]) => requireCtx().runCli(args), + runCli: (args: string[], options?: { allowError?: boolean }) => requireCtx().runCli(args, options), } as StoryContext; Object.defineProperty(api, 'resultsDir', { From 941539bd6c392c314ec67cda3f415ffbebd741e5 Mon Sep 17 00:00:00 2001 From: Nick Bernal Date: Fri, 6 Mar 2026 19:20:41 -0800 Subject: [PATCH 2/3] fix(document-api): content control fixes --- .../v3/handlers/w/r/r-translator.js | 8 + .../v3/handlers/w/r/r-translator.test.js | 25 ++ .../helpers/translate-structured-content.js | 62 +++- .../translate-structured-content.test.js | 43 +++ .../content-controls-wrappers.test.ts | 148 ++++++++ .../plan-engine/content-controls-wrappers.ts | 329 +++++++++++++++++- .../extensions/types/specialized-commands.ts | 1 + 7 files changed, 601 insertions(+), 15 deletions(-) diff --git a/packages/super-editor/src/core/super-converter/v3/handlers/w/r/r-translator.js b/packages/super-editor/src/core/super-converter/v3/handlers/w/r/r-translator.js index 4ffe27c5cc..8cd780a5da 100644 --- a/packages/super-editor/src/core/super-converter/v3/handlers/w/r/r-translator.js +++ b/packages/super-editor/src/core/super-converter/v3/handlers/w/r/r-translator.js @@ -250,6 +250,14 @@ const decode = (params, decodedAttrs = {}) => { return; } + // Run-level SDTs are paragraph siblings in OOXML (not children of w:r). + // Emit them directly so Word does not need to normalize invalid nesting. + if (child.name === 'w:sdt') { + const sdtClone = cloneXmlNode(child); + runs.push(sdtClone); + return; + } + const runWrapper = { name: XML_NODE_NAME, elements: [] }; applyBaseRunProps(runWrapper); if (!Array.isArray(runWrapper.elements)) runWrapper.elements = []; diff --git a/packages/super-editor/src/core/super-converter/v3/handlers/w/r/r-translator.test.js b/packages/super-editor/src/core/super-converter/v3/handlers/w/r/r-translator.test.js index 7e7b5cb5ed..7f83d8cfb4 100644 --- a/packages/super-editor/src/core/super-converter/v3/handlers/w/r/r-translator.test.js +++ b/packages/super-editor/src/core/super-converter/v3/handlers/w/r/r-translator.test.js @@ -298,4 +298,29 @@ describe('w:r r-translator (node)', () => { }), ); }); + + it('emits inline w:sdt as a paragraph-level sibling instead of wrapping it in w:r', () => { + const params = { + node: { + type: 'run', + attrs: { runProperties: [] }, + content: [ + { + type: 'structuredContent', + attrs: { + id: '123', + controlType: 'checkbox', + type: 'checkbox', + }, + content: [{ type: 'text', text: ' ' }], + }, + ], + }, + editor: { extensionService: { extensions: [] } }, + }; + + const result = translator.decode(params); + expect(result).toBeDefined(); + expect(result.name).toBe('w:sdt'); + }); }); diff --git a/packages/super-editor/src/core/super-converter/v3/handlers/w/sdt/helpers/translate-structured-content.js b/packages/super-editor/src/core/super-converter/v3/handlers/w/sdt/helpers/translate-structured-content.js index 60e2795f72..5bac09dda8 100644 --- a/packages/super-editor/src/core/super-converter/v3/handlers/w/sdt/helpers/translate-structured-content.js +++ b/packages/super-editor/src/core/super-converter/v3/handlers/w/sdt/helpers/translate-structured-content.js @@ -49,6 +49,62 @@ const CONTROL_TYPE_ELEMENT_MAP = { group: 'w:group', }; +const DEFAULT_CHECKBOX_SYMBOL_FONT = 'MS Gothic'; +const DEFAULT_CHECKBOX_CHECKED_HEX = '2612'; +const DEFAULT_CHECKBOX_UNCHECKED_HEX = '2610'; + +function buildDefaultTypeElement(controlType) { + const typeElementName = controlType && CONTROL_TYPE_ELEMENT_MAP[controlType]; + if (!typeElementName) return null; + + if (controlType === 'checkbox') { + return { + name: typeElementName, + type: 'element', + elements: [ + { name: 'w14:checked', type: 'element', attributes: { 'w14:val': '0' } }, + { + name: 'w14:checkedState', + type: 'element', + attributes: { 'w14:font': DEFAULT_CHECKBOX_SYMBOL_FONT, 'w14:val': DEFAULT_CHECKBOX_CHECKED_HEX }, + }, + { + name: 'w14:uncheckedState', + type: 'element', + attributes: { 'w14:font': DEFAULT_CHECKBOX_SYMBOL_FONT, 'w14:val': DEFAULT_CHECKBOX_UNCHECKED_HEX }, + }, + ], + }; + } + + if (controlType === 'comboBox' || controlType === 'dropDownList') { + return { name: typeElementName, type: 'element', elements: [] }; + } + + if (controlType === 'repeatingSection') { + return { + name: typeElementName, + type: 'element', + elements: [{ name: 'w15:allowInsertDeleteSection', type: 'element', attributes: { 'w15:val': '1' } }], + }; + } + + if (controlType === 'date') { + return { + name: typeElementName, + type: 'element', + elements: [ + { name: 'w:dateFormat', type: 'element', attributes: { 'w:val': 'M/d/yyyy' } }, + { name: 'w:lid', type: 'element', attributes: { 'w:val': 'en-US' } }, + { name: 'w:storeMappedDataAs', type: 'element', attributes: { 'w:val': 'dateTime' } }, + { name: 'w:calendar', type: 'element', attributes: { 'w:val': 'gregorian' } }, + ], + }; + } + + return { name: typeElementName, type: 'element' }; +} + function generateSdtPrTagForStructuredContent({ node }) { const { attrs = {} } = node; @@ -94,9 +150,9 @@ function generateSdtPrTagForStructuredContent({ node }) { // When sdtPr is absent (newly created controls), emit the type-specific // element from attrs.controlType so the OOXML is semantically correct. const controlType = attrs.controlType || attrs.type; - const typeElementName = controlType && CONTROL_TYPE_ELEMENT_MAP[controlType]; - if (typeElementName) { - resultElements.push({ name: typeElementName, type: 'element' }); + const typeElement = buildDefaultTypeElement(controlType); + if (typeElement) { + resultElements.push(typeElement); } const result = { diff --git a/packages/super-editor/src/core/super-converter/v3/handlers/w/sdt/helpers/translate-structured-content.test.js b/packages/super-editor/src/core/super-converter/v3/handlers/w/sdt/helpers/translate-structured-content.test.js index 5edc18de1a..820eaefa31 100644 --- a/packages/super-editor/src/core/super-converter/v3/handlers/w/sdt/helpers/translate-structured-content.test.js +++ b/packages/super-editor/src/core/super-converter/v3/handlers/w/sdt/helpers/translate-structured-content.test.js @@ -199,4 +199,47 @@ describe('translateStructuredContent', () => { expect(lockElements[0].attributes['w:val']).toBe('sdtContentLocked'); }); }); + + describe('default type element export when sdtPr is absent', () => { + it('exports checkbox defaults with checked + symbol state metadata', () => { + const node = { + type: 'structuredContent', + attrs: { + id: '101', + controlType: 'checkbox', + }, + content: [{ type: 'text', text: ' ' }], + }; + + const result = translateStructuredContent({ node }); + const sdtPr = result.elements.find((el) => el.name === 'w:sdtPr'); + const checkbox = sdtPr.elements.find((el) => el.name === 'w14:checkbox'); + + expect(checkbox).toBeDefined(); + expect(checkbox.elements.some((el) => el.name === 'w14:checked')).toBe(true); + expect(checkbox.elements.some((el) => el.name === 'w14:checkedState')).toBe(true); + expect(checkbox.elements.some((el) => el.name === 'w14:uncheckedState')).toBe(true); + }); + + it('exports date defaults with format/locale/storage/calendar metadata', () => { + const node = { + type: 'structuredContent', + attrs: { + id: '102', + controlType: 'date', + }, + content: [{ type: 'text', text: '3/7/2026' }], + }; + + const result = translateStructuredContent({ node }); + const sdtPr = result.elements.find((el) => el.name === 'w:sdtPr'); + const dateEl = sdtPr.elements.find((el) => el.name === 'w:date'); + + expect(dateEl).toBeDefined(); + expect(dateEl.elements.some((el) => el.name === 'w:dateFormat')).toBe(true); + expect(dateEl.elements.some((el) => el.name === 'w:lid')).toBe(true); + expect(dateEl.elements.some((el) => el.name === 'w:storeMappedDataAs')).toBe(true); + expect(dateEl.elements.some((el) => el.name === 'w:calendar')).toBe(true); + }); + }); }); diff --git a/packages/super-editor/src/document-api-adapters/plan-engine/content-controls-wrappers.test.ts b/packages/super-editor/src/document-api-adapters/plan-engine/content-controls-wrappers.test.ts index c514cafdff..a83e2bb03f 100644 --- a/packages/super-editor/src/document-api-adapters/plan-engine/content-controls-wrappers.test.ts +++ b/packages/super-editor/src/document-api-adapters/plan-engine/content-controls-wrappers.test.ts @@ -715,3 +715,151 @@ describe('buildContentControlInfoFromAttrs completeness', () => { expect(info.properties.tabIndex).toBeUndefined(); }); }); + +describe('choiceList.setSelected visual text sync', () => { + it('updates visible content text to the selected item displayText', () => { + const editor = makeSdtEditor({ + controlType: 'dropDownList', + type: 'dropDownList', + sdtPr: { + name: 'w:sdtPr', + elements: [ + { + name: 'w:dropDownList', + type: 'element', + elements: [ + { name: 'w:listItem', type: 'element', attributes: { 'w:displayText': 'Acme Corp', 'w:value': 'acme' } }, + { name: 'w:listItem', type: 'element', attributes: { 'w:displayText': 'Globex', 'w:value': 'globex' } }, + ], + }, + ], + }, + }); + const adapter = createContentControlsAdapter(editor); + + const result = adapter.choiceList.setSelected({ target: SDT_TARGET, value: 'acme' }, { changeMode: 'direct' }); + expect(result.success).toBe(true); + + const updateCmd = editor.commands!.updateStructuredContentById as ReturnType; + const textCall = updateCmd.mock.calls.find((call) => call[1]?.text === 'Acme Corp'); + expect(textCall).toBeDefined(); + }); + + it('falls back to the selected value when no matching item is found', () => { + const editor = makeSdtEditor({ + controlType: 'dropDownList', + type: 'dropDownList', + sdtPr: { + name: 'w:sdtPr', + elements: [ + { + name: 'w:dropDownList', + type: 'element', + elements: [ + { name: 'w:listItem', type: 'element', attributes: { 'w:displayText': 'Acme Corp', 'w:value': 'acme' } }, + ], + }, + ], + }, + }); + const adapter = createContentControlsAdapter(editor); + + const result = adapter.choiceList.setSelected({ target: SDT_TARGET, value: 'unknown' }, { changeMode: 'direct' }); + expect(result.success).toBe(true); + + const updateCmd = editor.commands!.updateStructuredContentById as ReturnType; + const textCall = updateCmd.mock.calls.find((call) => call[1]?.text === 'unknown'); + expect(textCall).toBeDefined(); + }); +}); + +describe('create.contentControl default sdtPr seeding', () => { + it('seeds checkbox controls with checked state + symbol pair defaults', () => { + const editor = makeSdtEditor(); + const adapter = createContentControlsAdapter(editor); + + const result = adapter.create({ kind: 'inline', controlType: 'checkbox' }, { changeMode: 'direct' }); + expect(result.success).toBe(true); + + const insertInline = editor.commands!.insertStructuredContentInline as ReturnType; + expect(insertInline).toHaveBeenCalledTimes(1); + expect(insertInline.mock.calls[0][0].json?.text).toBe(String.fromCodePoint(0x2610)); + expect(insertInline.mock.calls[0][0].json?.marks?.[0]?.attrs?.fontFamily).toBe('MS Gothic'); + const attrs = insertInline.mock.calls[0][0].attrs as Record; + const sdtPr = attrs.sdtPr as { elements?: Array<{ name: string; elements?: Array<{ name: string }> }> }; + const checkbox = sdtPr.elements?.find((el) => el.name === 'w14:checkbox'); + expect(checkbox).toBeDefined(); + expect(checkbox?.elements?.some((el) => el.name === 'w14:checked')).toBe(true); + expect(checkbox?.elements?.some((el) => el.name === 'w14:checkedState')).toBe(true); + expect(checkbox?.elements?.some((el) => el.name === 'w14:uncheckedState')).toBe(true); + }); + + it.each([ + ['text', 'w:text'], + ['comboBox', 'w:comboBox'], + ['dropDownList', 'w:dropDownList'], + ] as const)('seeds %s controls with %s in sdtPr', (controlType, xmlName) => { + const editor = makeSdtEditor(); + const adapter = createContentControlsAdapter(editor); + + const result = adapter.create({ kind: 'inline', controlType }, { changeMode: 'direct' }); + expect(result.success).toBe(true); + + const insertInline = editor.commands!.insertStructuredContentInline as ReturnType; + const attrs = insertInline.mock.calls[0][0].attrs as Record; + const sdtPr = attrs.sdtPr as { elements?: Array<{ name: string }> }; + expect(sdtPr.elements?.some((el) => el.name === xmlName)).toBe(true); + }); + + it('seeds date controls with today text and Word-compatible date metadata', () => { + const editor = makeSdtEditor(); + const adapter = createContentControlsAdapter(editor); + + const result = adapter.create({ kind: 'inline', controlType: 'date' }, { changeMode: 'direct' }); + expect(result.success).toBe(true); + + const insertInline = editor.commands!.insertStructuredContentInline as ReturnType; + expect(insertInline.mock.calls[0][0].text).toMatch(/^\d{1,2}\/\d{1,2}\/\d{4}$/); + + const attrs = insertInline.mock.calls[0][0].attrs as Record; + const sdtPr = attrs.sdtPr as { + elements?: Array<{ name: string; attributes?: Record; elements?: Array<{ name: string }> }>; + }; + const dateEl = sdtPr.elements?.find((el) => el.name === 'w:date'); + expect(dateEl).toBeDefined(); + expect(String(dateEl?.attributes?.['w:fullDate'] ?? '')).toMatch(/^\d{4}-\d{2}-\d{2}T00:00:00Z$/); + expect(dateEl?.elements?.some((el) => el.name === 'w:dateFormat')).toBe(true); + expect(dateEl?.elements?.some((el) => el.name === 'w:lid')).toBe(true); + expect(dateEl?.elements?.some((el) => el.name === 'w:storeMappedDataAs')).toBe(true); + expect(dateEl?.elements?.some((el) => el.name === 'w:calendar')).toBe(true); + }); +}); + +describe('contentControls.setType default sdtPr seeding', () => { + it('adds Word-visible checkbox defaults when switching to checkbox type', () => { + const editor = makeSdtEditor({ + controlType: 'text', + type: 'text', + sdtPr: { + name: 'w:sdtPr', + elements: [{ name: 'w:text', type: 'element' }], + }, + }); + const adapter = createContentControlsAdapter(editor); + + const result = adapter.setType({ target: SDT_TARGET, controlType: 'checkbox' }, { changeMode: 'direct' }); + expect(result.success).toBe(true); + + const updateCmd = editor.commands!.updateStructuredContentById as ReturnType; + const checkboxWrite = updateCmd.mock.calls.find((call) => + Boolean(call[1]?.attrs?.sdtPr?.elements?.find((el: { name: string }) => el.name === 'w14:checkbox')), + ); + expect(checkboxWrite).toBeDefined(); + const checkbox = checkboxWrite?.[1]?.attrs?.sdtPr?.elements?.find( + (el: { name: string; elements?: Array<{ name: string }> }) => el.name === 'w14:checkbox', + ); + expect(checkbox?.elements?.some((el: { name: string }) => el.name === 'w14:checked')).toBe(true); + expect(checkbox?.elements?.some((el: { name: string }) => el.name === 'w14:checkedState')).toBe(true); + expect(checkbox?.elements?.some((el: { name: string }) => el.name === 'w14:uncheckedState')).toBe(true); + }); +}); diff --git a/packages/super-editor/src/document-api-adapters/plan-engine/content-controls-wrappers.ts b/packages/super-editor/src/document-api-adapters/plan-engine/content-controls-wrappers.ts index e8d5370153..78fbb80303 100644 --- a/packages/super-editor/src/document-api-adapters/plan-engine/content-controls-wrappers.ts +++ b/packages/super-editor/src/document-api-adapters/plan-engine/content-controls-wrappers.ts @@ -14,6 +14,7 @@ import { Fragment, type Node as ProseMirrorNode, type Schema } from 'prosemirror-model'; import type { Editor } from '../../core/Editor.js'; +import type { ProseMirrorJSON } from '../../core/types/EditorTypes.js'; import type { ContentControlInfo, ContentControlMutationResult, @@ -127,6 +128,13 @@ function generateSdtId(): string { return String(Math.floor(Math.random() * 2147483647)); } +/** Check whether an ID string is a valid signed 32-bit integer (Word `w:id` requirement). */ +function isValidWordSdtId(id: string): boolean { + if (!/^-?\d+$/.test(id)) return false; + const n = Number(id); + return n >= -2147483648 && n <= 2147483647; +} + /** Names that are forbidden from patchRawProperties per ยง10 of the plan. */ const FORBIDDEN_RAW_PATCH_NAMES = new Set([ 'w:sdtContent', @@ -500,6 +508,210 @@ const CONTROL_TYPE_SDT_PR_ELEMENTS: Record = { group: 'w:group', }; +const DEFAULT_CHECKBOX_SYMBOL_FONT = 'MS Gothic'; +const DEFAULT_CHECKBOX_CHECKED_HEX = '2612'; +const DEFAULT_CHECKBOX_UNCHECKED_HEX = '2610'; + +type CheckboxVisualSymbol = { + char: string; + font: string; +}; + +type DateControlDefaults = { + displayText: string; + fullDate: string; + dateFormat: string; + locale: string; + storageFormat: string; + calendar: string; +}; + +function pad2(value: number): string { + return String(value).padStart(2, '0'); +} + +function buildDateControlDefaults(now: Date = new Date()): DateControlDefaults { + const year = now.getFullYear(); + const month = now.getMonth() + 1; + const day = now.getDate(); + return { + // Match Word's common default date format for en-US date controls. + displayText: `${month}/${day}/${year}`, + fullDate: `${year}-${pad2(month)}-${pad2(day)}T00:00:00Z`, + dateFormat: 'M/d/yyyy', + locale: 'en-US', + storageFormat: 'dateTime', + calendar: 'gregorian', + }; +} + +function upsertChildElement( + elements: SdtPrElement[], + childName: string, + attrs: Record, +): SdtPrElement[] { + const idx = elements.findIndex((el) => el.name === childName); + const next = [...elements]; + const value = { name: childName, type: 'element', attributes: attrs }; + if (idx >= 0) { + next[idx] = value; + } else { + next.push(value); + } + return next; +} + +function applyDateDefaultsToSdtPr( + sdtPr: SdtPrElement | undefined, + defaults: DateControlDefaults, +): SdtPrElement | undefined { + if (!sdtPr) return sdtPr; + const dateEl = findSdtPrChild(sdtPr, 'w:date'); + if (!dateEl) return sdtPr; + + let dateChildren = [...(dateEl.elements ?? [])]; + dateChildren = upsertChildElement(dateChildren, 'w:dateFormat', { 'w:val': defaults.dateFormat }); + dateChildren = upsertChildElement(dateChildren, 'w:lid', { 'w:val': defaults.locale }); + dateChildren = upsertChildElement(dateChildren, 'w:storeMappedDataAs', { 'w:val': defaults.storageFormat }); + dateChildren = upsertChildElement(dateChildren, 'w:calendar', { 'w:val': defaults.calendar }); + + const dateNext: SdtPrElement = { + ...dateEl, + name: 'w:date', + type: 'element', + attributes: { ...(dateEl.attributes ?? {}), 'w:fullDate': defaults.fullDate }, + elements: dateChildren, + }; + const elements = [...(sdtPr.elements ?? [])]; + const idx = elements.findIndex((el) => el.name === 'w:date'); + if (idx >= 0) { + elements[idx] = dateNext; + } else { + elements.push(dateNext); + } + return { ...sdtPr, elements }; +} + +function parseCheckboxSymbolCodePoint(raw: unknown, fallbackHex: string): number { + const fallback = Number.parseInt(fallbackHex, 16); + const normalized = String(raw ?? '') + .trim() + .replace(/^0x/i, ''); + if (!normalized) return fallback; + + if (/^[0-9A-Fa-f]+$/.test(normalized)) { + const parsedHex = Number.parseInt(normalized, 16); + if (Number.isInteger(parsedHex) && parsedHex >= 0 && parsedHex <= 0x10ffff) { + return parsedHex; + } + } + + if (/^\d+$/.test(normalized)) { + const parsedDec = Number.parseInt(normalized, 10); + if (Number.isInteger(parsedDec) && parsedDec >= 0 && parsedDec <= 0x10ffff) { + return parsedDec; + } + } + + return fallback; +} + +function resolveCheckboxVisualSymbol(sdtPr: SdtPrElement | undefined, checked: boolean): CheckboxVisualSymbol { + const checkboxEl = findSdtPrChild(sdtPr, 'w14:checkbox') ?? findSdtPrChild(sdtPr, 'w:checkbox'); + const stateName = checked ? 'w14:checkedState' : 'w14:uncheckedState'; + const legacyStateName = checked ? 'w:checkedState' : 'w:uncheckedState'; + const stateEl = checkboxEl?.elements?.find((e) => e.name === stateName || e.name === legacyStateName); + const fallbackHex = checked ? DEFAULT_CHECKBOX_CHECKED_HEX : DEFAULT_CHECKBOX_UNCHECKED_HEX; + const codePoint = parseCheckboxSymbolCodePoint( + stateEl?.attributes?.['w14:val'] ?? stateEl?.attributes?.['w:val'], + fallbackHex, + ); + const font = String( + stateEl?.attributes?.['w14:font'] ?? stateEl?.attributes?.['w:font'] ?? DEFAULT_CHECKBOX_SYMBOL_FONT, + ); + return { char: String.fromCodePoint(codePoint), font }; +} + +function buildCheckboxTextJson(symbol: CheckboxVisualSymbol): ProseMirrorJSON { + return { + type: 'text', + text: symbol.char, + marks: [{ type: 'textStyle', attrs: { fontFamily: symbol.font } }], + }; +} + +function createTextWithOptionalFont(editor: Editor, text: string, fontFamily?: string): ProseMirrorNode { + if (fontFamily) { + const textStyleMark = editor.schema.marks?.textStyle; + if (textStyleMark) { + return editor.schema.text(text, [textStyleMark.create({ fontFamily })]); + } + } + return editor.schema.text(text); +} + +/** + * Build a default type-specific sdtPr child element for newly created controls. + * These defaults keep exported OOXML Word-friendly from the first save. + */ +function buildDefaultTypeSdtPrElement(controlType: string | undefined): SdtPrElement | undefined { + switch (controlType) { + case 'text': + return { name: 'w:text', type: 'element' }; + case 'date': + return { + name: 'w:date', + type: 'element', + elements: [ + { name: 'w:dateFormat', type: 'element', attributes: { 'w:val': 'M/d/yyyy' } }, + { name: 'w:lid', type: 'element', attributes: { 'w:val': 'en-US' } }, + { name: 'w:storeMappedDataAs', type: 'element', attributes: { 'w:val': 'dateTime' } }, + { name: 'w:calendar', type: 'element', attributes: { 'w:val': 'gregorian' } }, + ], + }; + case 'checkbox': + return { + name: 'w14:checkbox', + type: 'element', + elements: [ + { name: 'w14:checked', type: 'element', attributes: { 'w14:val': '0' } }, + { + name: 'w14:checkedState', + type: 'element', + attributes: { 'w14:font': DEFAULT_CHECKBOX_SYMBOL_FONT, 'w14:val': DEFAULT_CHECKBOX_CHECKED_HEX }, + }, + { + name: 'w14:uncheckedState', + type: 'element', + attributes: { 'w14:font': DEFAULT_CHECKBOX_SYMBOL_FONT, 'w14:val': DEFAULT_CHECKBOX_UNCHECKED_HEX }, + }, + ], + }; + case 'comboBox': + return { name: 'w:comboBox', type: 'element', elements: [] }; + case 'dropDownList': + return { name: 'w:dropDownList', type: 'element', elements: [] }; + case 'repeatingSection': + return { + name: 'w15:repeatingSection', + type: 'element', + elements: [{ name: 'w15:allowInsertDeleteSection', type: 'element', attributes: { 'w15:val': '1' } }], + }; + case 'repeatingSectionItem': + return { name: 'w15:repeatingSectionItem', type: 'element' }; + case 'group': + return { name: 'w:group', type: 'element' }; + default: + return undefined; + } +} + +function buildDefaultSdtPr(controlType: string | undefined): SdtPrElement | undefined { + const typeElement = buildDefaultTypeSdtPrElement(controlType); + if (!typeElement) return undefined; + return { name: 'w:sdtPr', type: 'element', elements: [typeElement] }; +} + function setTypeWrapper( editor: Editor, input: ContentControlsSetTypeInput, @@ -525,11 +737,12 @@ function setTypeWrapper( // Add the new type-specific element to sdtPr (if applicable) const newElementName = CONTROL_TYPE_SDT_PR_ELEMENTS[input.controlType]; if (newElementName) { + const defaultTypeElement = buildDefaultTypeSdtPrElement(input.controlType); updateSdtPrChild( editor, input.target, newElementName, - (existing) => existing ?? { name: newElementName, type: 'element' }, + (existing) => existing ?? defaultTypeElement ?? { name: newElementName, type: 'element' }, ); } @@ -804,7 +1017,7 @@ function validateWordCompatibilityWrapper( const diagnostics: ContentControlsValidateWordCompatibilityResult['diagnostics'] = []; const id = String(attrs.id ?? ''); - if (!/^-?\d+$/.test(id)) { + if (!isValidWordSdtId(id)) { diagnostics.push({ code: 'INVALID_ID_FORMAT', severity: 'error', @@ -824,7 +1037,7 @@ function normalizeWordCompatibilityWrapper( const target = buildTarget(sdt); const id = String(sdt.node.attrs.id ?? ''); - if (/^-?\d+$/.test(id)) { + if (isValidWordSdtId(id)) { return buildMutationFailure('NO_OP', 'Content control ID is already Word-compatible.'); } @@ -1039,9 +1252,10 @@ function checkboxSetStateWrapper( assertControlType(sdt, 'checkbox', 'checkbox.setState'); assertNotSdtLocked(sdt, 'checkbox.setState'); const target = buildTarget(sdt); + const symbol = resolveCheckboxVisualSymbol(sdt.node.attrs.sdtPr as SdtPrElement | undefined, input.checked); return executeSdtMutation(editor, target, options, () => { - return updateSdtPrSubElementAttr( + const checkboxUpdated = updateSdtPrSubElementAttr( editor, input.target, 'w14:checkbox', @@ -1049,6 +1263,19 @@ function checkboxSetStateWrapper( 'w14:val', input.checked ? '1' : '0', ); + if (!checkboxUpdated) return false; + + if (sdt.kind === 'inline') { + const updateCmd = editor.commands?.updateStructuredContentById; + if (typeof updateCmd === 'function') { + const visualUpdated = + Boolean(updateCmd(input.target.nodeId, { json: buildCheckboxTextJson(symbol) })) || + Boolean(updateCmd(input.target.nodeId, { text: symbol.char, keepTextNodeStyles: true })); + return visualUpdated || checkboxUpdated; + } + } + + return checkboxUpdated; }); } @@ -1070,9 +1297,20 @@ function checkboxSetSymbolPairWrapper( assertControlType(sdt, 'checkbox', 'checkbox.setSymbolPair'); assertNotSdtLocked(sdt, 'checkbox.setSymbolPair'); const target = buildTarget(sdt); + const checked = readCheckboxChecked(sdt.node.attrs.sdtPr as SdtPrElement | undefined); + const symbolFromInput = checked ? input.checkedSymbol : input.uncheckedSymbol; + const symbol: CheckboxVisualSymbol = { + char: String.fromCodePoint( + parseCheckboxSymbolCodePoint( + symbolFromInput.char, + checked ? DEFAULT_CHECKBOX_CHECKED_HEX : DEFAULT_CHECKBOX_UNCHECKED_HEX, + ), + ), + font: symbolFromInput.font || DEFAULT_CHECKBOX_SYMBOL_FONT, + }; return executeSdtMutation(editor, target, options, () => { - return updateSdtPrChild(editor, input.target, 'w14:checkbox', (existing) => { + const pairUpdated = updateSdtPrChild(editor, input.target, 'w14:checkbox', (existing) => { const el: SdtPrElement = existing ?? { name: 'w14:checkbox', type: 'element', elements: [] }; const elements = (el.elements ?? []).filter( (e) => e.name !== 'w14:checkedState' && e.name !== 'w14:uncheckedState', @@ -1089,6 +1327,19 @@ function checkboxSetSymbolPairWrapper( }); return { ...el, elements }; }); + if (!pairUpdated) return false; + + if (sdt.kind === 'inline') { + const updateCmd = editor.commands?.updateStructuredContentById; + if (typeof updateCmd === 'function') { + const visualUpdated = + Boolean(updateCmd(input.target.nodeId, { json: buildCheckboxTextJson(symbol) })) || + Boolean(updateCmd(input.target.nodeId, { text: symbol.char, keepTextNodeStyles: true })); + return visualUpdated || pairUpdated; + } + } + + return pairUpdated; }); } @@ -1138,15 +1389,31 @@ function choiceListSetSelectedWrapper( assertControlType(sdt, ['comboBox', 'dropDownList'], 'choiceList.setSelected'); assertNotSdtLocked(sdt, 'choiceList.setSelected'); const target = buildTarget(sdt); - const ct = resolveControlType(sdt.node.attrs as Record); + const ct = resolveControlType(sdt.node.attrs as Record) as 'comboBox' | 'dropDownList'; + const currentSdtPr = sdt.node.attrs.sdtPr as SdtPrElement | undefined; + const { items } = readChoiceListData(currentSdtPr, ct); + const selectedItem = items.find((item) => item.value === input.value); + const selectedDisplayText = selectedItem?.displayText ?? input.value; return executeSdtMutation(editor, target, options, () => { - return updateSdtPrChild(editor, input.target, `w:${ct}`, (existing) => ({ + const selectedUpdated = updateSdtPrChild(editor, input.target, `w:${ct}`, (existing) => ({ name: `w:${ct}`, type: 'element', ...existing, attributes: { ...(existing?.attributes ?? {}), 'w:lastValue': input.value }, })); + if (!selectedUpdated) return false; + + // Keep the SDT body text in sync so the selected option is visible in-editor and after export. + const updateCmd = editor.commands?.updateStructuredContentById; + if (typeof updateCmd === 'function') { + const visualUpdated = Boolean( + updateCmd(input.target.nodeId, { text: selectedDisplayText, keepTextNodeStyles: true }), + ); + return visualUpdated || selectedUpdated; + } + + return selectedUpdated; }); } @@ -1403,6 +1670,19 @@ function createWrapper( controlType: input.controlType ?? 'unknown', type: input.controlType ?? 'unknown', }; + const defaultSdtPr = buildDefaultSdtPr(input.controlType ?? 'unknown'); + const isDateCreate = input.controlType === 'date' && input.content == null; + const dateDefaults = isDateCreate ? buildDateControlDefaults() : null; + const sdtPrWithDateDefaults = dateDefaults ? applyDateDefaultsToSdtPr(defaultSdtPr, dateDefaults) : defaultSdtPr; + if (sdtPrWithDateDefaults) { + attrs.sdtPr = sdtPrWithDateDefaults; + } + const isCheckboxCreate = input.controlType === 'checkbox' && input.content == null; + const checkboxSymbol = isCheckboxCreate + ? resolveCheckboxVisualSymbol(defaultSdtPr, false) + : ({ char: '', font: DEFAULT_CHECKBOX_SYMBOL_FONT } as CheckboxVisualSymbol); + const contentText = + input.content ?? (isCheckboxCreate ? checkboxSymbol.char : isDateCreate ? dateDefaults?.displayText : undefined); // When a target is provided, insert adjacent to it for deterministic placement. if (input.target) { @@ -1412,10 +1692,16 @@ function createWrapper( if (!nodeType) return false; let content; - if (input.content) { - content = editor.schema.text(input.content); + if (contentText !== undefined) { + const textNode = createTextWithOptionalFont( + editor, + contentText, + isCheckboxCreate ? checkboxSymbol.font : undefined, + ); if (input.kind === 'block') { - content = editor.schema.nodes.paragraph.create(null, content); + content = editor.schema.nodes.paragraph.create(null, textNode); + } else { + content = textNode; } } else if (input.kind === 'block') { content = editor.schema.nodes.paragraph.create(); @@ -1430,8 +1716,27 @@ function createWrapper( } // Default: delegate to the editor command (inserts at current selection). - if (input.content) { - return Boolean(insertCmd({ attrs, text: input.content })); + if (contentText !== undefined) { + if (input.kind === 'block') { + if (isCheckboxCreate) { + return Boolean( + insertCmd({ + attrs, + json: { type: 'paragraph', content: [buildCheckboxTextJson(checkboxSymbol)] }, + }), + ); + } + return Boolean( + insertCmd({ + attrs, + json: { type: 'paragraph', content: [{ type: 'text', text: contentText }] }, + }), + ); + } + if (isCheckboxCreate) { + return Boolean(insertCmd({ attrs, json: buildCheckboxTextJson(checkboxSymbol) })); + } + return Boolean(insertCmd({ attrs, text: contentText })); } return Boolean(insertCmd({ attrs })); }); diff --git a/packages/super-editor/src/extensions/types/specialized-commands.ts b/packages/super-editor/src/extensions/types/specialized-commands.ts index 568946250d..09a0b2992b 100644 --- a/packages/super-editor/src/extensions/types/specialized-commands.ts +++ b/packages/super-editor/src/extensions/types/specialized-commands.ts @@ -53,6 +53,7 @@ type StructuredContentUpdateOptions = { html?: string; json?: ProseMirrorJSON; attrs?: Record; + keepTextNodeStyles?: boolean; }; type StructuredContentTableAppendOptions = { From 91c38759e60060ac925dc153745dfbdd3c8613fc Mon Sep 17 00:00:00 2001 From: Nick Bernal Date: Fri, 6 Mar 2026 19:35:46 -0800 Subject: [PATCH 3/3] chore: fix tests --- tests/behavior/fixtures/superdoc.ts | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/tests/behavior/fixtures/superdoc.ts b/tests/behavior/fixtures/superdoc.ts index 6fb556f472..7e6619dae1 100644 --- a/tests/behavior/fixtures/superdoc.ts +++ b/tests/behavior/fixtures/superdoc.ts @@ -50,7 +50,26 @@ function buildHarnessUrl(config: HarnessConfig = {}): string { } async function waitForReady(page: Page, timeout = 30_000): Promise { - await page.waitForFunction(() => (window as any).superdocReady === true, null, { polling: 100, timeout }); + // Vite may trigger a dep-optimization reload on WebKit after the initial load event, + // which destroys the execution context and resets `superdocReady`. Retry across + // navigations until the flag is set or the overall deadline is reached. + const deadline = Date.now() + timeout; + while (Date.now() < deadline) { + try { + const remaining = Math.max(deadline - Date.now(), 1000); + await page.waitForFunction(() => (window as any).superdocReady === true, null, { + polling: 100, + timeout: remaining, + }); + return; + } catch { + // If the page navigated (context destroyed) and we still have budget, retry + // after the new page finishes loading. + if (Date.now() >= deadline) break; + await page.waitForLoadState('load').catch(() => {}); + } + } + throw new Error(`waitForReady: superdocReady was not set within ${timeout}ms`); } async function waitForStable(page: Page, ms?: number): Promise { @@ -1023,15 +1042,17 @@ export const test = base.extend<{ superdoc: SuperDocFixture } & SuperDocOptions> superdoc: async ({ page, config }, use) => { const modKey = process.platform === 'darwin' ? 'Meta' : 'Control'; - // Navigate to harness + // Navigate to harness โ€” use 'networkidle' so Vite finishes serving all + // assets (and any dep-optimization reloads) before we check app state. + // WebKit is particularly sensitive to mid-load reloads in parallel workers. const url = buildHarnessUrl({ layout: true, ...config }); - await page.goto(url); + await page.goto(url, { waitUntil: 'networkidle' }); await waitForReady(page); // Focus the editor โ€” use .focus() not .click() because in layout mode // the ProseMirror contenteditable is positioned off-screen (DomPainter renders visuals). const editor = page.locator('[contenteditable="true"]').first(); - await editor.waitFor({ state: 'visible', timeout: 10_000 }); + await editor.waitFor({ state: 'visible', timeout: 15_000 }); await editor.focus(); await use(createFixture(page, editor, modKey));