diff --git a/AGENTS.md b/AGENTS.md index 8db586c013..bf219229e6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -72,8 +72,8 @@ Do not hand-edit `COMMAND_CATALOG`, `OPERATION_MEMBER_PATH_MAP`, `OPERATION_REFE - `pnpm test` - unit tests - `pnpm dev` - dev server from `examples/` - `pnpm check:types` - raw TS compile across all referenced projects (`tsc -b tsconfig.references.json`). Does NOT run the public-interface chain. Legacy alias: `pnpm run type-check`. -- `pnpm check:public` - **canonical pre-merge command for typed public surfaces.** Validates both `superdoc` (tier discipline + jsdoc ratchet + public-method fixture coverage + vite build + postbuild chain + consumer typecheck matrix + deep-type audit + package-shape + snapshots + classification closure) and Document API (contract parity + output staleness + examples + overview). ~5 min. Non-mutating. Combines `check:public:superdoc` + `check:public:docapi`. -- `pnpm check:public:superdoc` - SuperDoc public package surface only. Wraps ten stages in cheap-to-expensive order: `contract-tiers-test`, `contract-tiers`, `jsdoc-ratchet`, `public-method-coverage`, `build`, `consumer-typecheck-matrix`, `deep-type-audit-supported-root`, `package-shape`, `export-snapshots`, `root-classification-closure`. Legacy alias: `pnpm run check:public-contract`. +- `pnpm check:public` - **canonical pre-merge command for typed public surfaces.** Validates both `superdoc` (tier discipline + jsdoc ratchet + ts-jsdoc hygiene + public-method fixture coverage + vite build + postbuild chain + consumer typecheck matrix + deep-type audit + package-shape + snapshots + classification closure) and Document API (contract parity + output staleness + examples + overview). ~5 min. Non-mutating. Combines `check:public:superdoc` + `check:public:docapi`. +- `pnpm check:public:superdoc` - SuperDoc public package surface only. Wraps twelve stages in cheap-to-expensive order: `contract-tiers-test`, `contract-tiers`, `jsdoc-ratchet`, `jsdoc-hygiene-ts-test`, `jsdoc-hygiene-ts`, `public-method-coverage`, `build`, `consumer-typecheck-matrix`, `deep-type-audit-supported-root`, `package-shape`, `export-snapshots`, `root-classification-closure`. Legacy alias: `pnpm run check:public-contract`. - `pnpm check:public:docapi` - Document API public surface only. Wraps four stages: `contract-parity`, `contract-outputs`, `examples`, `overview-alignment`. Clean-checkout safe: gitignored generated artifacts are built in memory; tracked outputs (reference docs, overview block) are compared byte-for-byte. No mutation. Legacy alias: `pnpm run docapi:check`. - `pnpm generate:docapi` - regenerate Document API outputs after editing the contract (alias of `docapi:sync`). Writes gitignored Document API generated artifacts. Run only when you need the artifacts materialized locally (SDK builds, publishing); `check:public:docapi` does not require it. - `pnpm generate:all` - regenerate schemas, SDK clients, tool catalogs, reference docs. diff --git a/apps/cli/src/__tests__/cli.test.ts b/apps/cli/src/__tests__/cli.test.ts index c62df7233f..1fbda397fa 100644 --- a/apps/cli/src/__tests__/cli.test.ts +++ b/apps/cli/src/__tests__/cli.test.ts @@ -65,6 +65,10 @@ const ENCRYPTED_FIXTURE_SOURCE = join( REPO_ROOT, 'packages/super-editor/src/editors/v1/core/ooxml-encryption/fixtures/encrypted-advanced-text.docx', ); +const TRACKED_CHANGE_FIXTURE_SOURCE = join( + REPO_ROOT, + 'packages/super-editor/src/editors/v1/tests/data/basic-tracked-change.docx', +); const execFileAsync = promisify(execFile); const ZIP_MAX_BUFFER_BYTES = 10 * 1024 * 1024; @@ -2334,6 +2338,35 @@ describe('superdoc CLI', () => { expect(verifyEnvelope.data.result.total).toBeGreaterThan(0); }); + test('save --mode final exports accepted OOXML instead of copying review-preserving revisions', async () => { + const trackedSource = join(TEST_DIR, 'save-final-source.docx'); + const savedOut = join(TEST_DIR, 'save-final-output.docx'); + await copyFile(TRACKED_CHANGE_FIXTURE_SOURCE, trackedSource); + + const openResult = await runCli(['open', trackedSource, '--session', 'final-export']); + expect(openResult.code).toBe(0); + + const saveResult = await runCli([ + 'save', + '--session', + 'final-export', + '--mode', + 'final', + '--out', + savedOut, + '--force', + ]); + expect(saveResult.code).toBe(0); + + const saveEnvelope = parseJsonOutput>(saveResult); + expect(saveEnvelope.data.mode).toBe('final'); + expect(saveEnvelope.data.output.path).toBe(savedOut); + + const documentXml = await readDocxPart(savedOut, 'word/document.xml'); + expect(documentXml).not.toContain(' { const driftSource = join(TEST_DIR, 'drift-source.docx'); await copyFile(SAMPLE_DOC, driftSource); diff --git a/apps/cli/src/__tests__/conformance/scenarios.ts b/apps/cli/src/__tests__/conformance/scenarios.ts index d49ba0bff6..5cf9a1a8de 100644 --- a/apps/cli/src/__tests__/conformance/scenarios.ts +++ b/apps/cli/src/__tests__/conformance/scenarios.ts @@ -36,7 +36,7 @@ function skippedSuccessScenario(operationId: CliOperationId) { }); } -type SuccessScenarioFactory = (harness: ConformanceHarness) => Promise; +type ScenarioFactory = (harness: ConformanceHarness) => Promise; function deferredRuntimeScenario( operationId: CliOperationId, @@ -3334,7 +3334,7 @@ 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 Partial>; +} as const satisfies Partial>; const EXPLICIT_RUNTIME_CONFORMANCE_SKIP = new Set([ 'doc.toc.markEntry', @@ -3358,8 +3358,9 @@ const EXPLICIT_RUNTIME_CONFORMANCE_SKIP = new Set([ ]); const CANONICAL_OPERATION_IDS = Object.keys(CLI_OPERATION_COMMAND_KEYS) as CliOperationId[]; +const SUCCESS_SCENARIOS_BY_OPERATION: Partial> = SUCCESS_SCENARIOS; const AUTO_SKIPPED_OPERATION_IDS = CANONICAL_OPERATION_IDS.filter( - (operationId) => SUCCESS_SCENARIOS[operationId] == null, + (operationId) => SUCCESS_SCENARIOS_BY_OPERATION[operationId] == null, ); const RUNTIME_CONFORMANCE_SKIP = new Set([ @@ -3367,13 +3368,37 @@ const RUNTIME_CONFORMANCE_SKIP = new Set([ ...AUTO_SKIPPED_OPERATION_IDS, ]); +const FAILURE_SCENARIOS: Partial> = { + 'doc.trackChanges.decide': async (harness: ConformanceHarness): Promise => { + const stateDir = await harness.createStateDir('doc-trackChanges-decide-missing-id'); + const fixture = await harness.addTrackedChangeFixture(stateDir, 'doc-trackChanges-decide-missing-id'); + return { + stateDir, + args: [ + ...commandTokens('doc.trackChanges.decide'), + fixture.docPath, + '--decision', + 'accept', + '--target-json', + JSON.stringify({ id: 'missing-track-change-id' }), + '--out', + harness.createOutputPath('doc-trackChanges-decide-missing-id-output'), + ], + }; + }, +}; + +const EXPECTED_FAILURE_CODES: Partial> = { + 'doc.trackChanges.decide': ['TARGET_NOT_FOUND'], +}; + export const OPERATION_SCENARIOS = CANONICAL_OPERATION_IDS.map((operationId) => { - const success = SUCCESS_SCENARIOS[operationId] ?? skippedSuccessScenario(operationId); + const success = SUCCESS_SCENARIOS_BY_OPERATION[operationId] ?? skippedSuccessScenario(operationId); const scenario: OperationScenario = { operationId, success, - failure: genericInvalidArgumentFailure(operationId), - expectedFailureCodes: ['INVALID_ARGUMENT', 'MISSING_REQUIRED'], + failure: FAILURE_SCENARIOS[operationId] ?? genericInvalidArgumentFailure(operationId), + expectedFailureCodes: EXPECTED_FAILURE_CODES[operationId] ?? ['INVALID_ARGUMENT', 'MISSING_REQUIRED'], ...(RUNTIME_CONFORMANCE_SKIP.has(operationId) ? { skipRuntimeConformance: true } : {}), }; return scenario; diff --git a/apps/cli/src/__tests__/lib/_open-document-track-changes-worker.ts b/apps/cli/src/__tests__/lib/_open-document-track-changes-worker.ts new file mode 100644 index 0000000000..ad5d410c7c --- /dev/null +++ b/apps/cli/src/__tests__/lib/_open-document-track-changes-worker.ts @@ -0,0 +1,81 @@ +/** + * Subprocess worker for the openDocument track-changes forwarding test. + */ +import { mock } from 'bun:test'; + +let editorOpenCalled = false; +let capturedTrackChanges: unknown; + +const MockEditor = { + open: mock(async (_source: unknown, options: Record = {}) => { + editorOpenCalled = true; + capturedTrackChanges = (options.modules as { trackChanges?: unknown } | undefined)?.trackChanges; + return { + destroy: () => {}, + exportDocument: async () => new Uint8Array(), + }; + }), +}; + +mock.module('superdoc/super-editor', () => ({ + Editor: MockEditor, + BLANK_DOCX_BASE64: '', + DocxEncryptionError: class DocxEncryptionError extends Error { + code: string; + constructor(code: string, message: string) { + super(message); + this.code = code; + } + }, + getDocumentApiAdapters: () => ({}), + markdownToPmDoc: () => null, + initPartsRuntime: () => ({ dispose: () => {} }), + syncCommentEntitiesFromCollaboration: () => new Set(), +})); + +mock.module('@superdoc/document-api', () => ({ + createDocumentApi: () => ({}), +})); + +mock.module('happy-dom', () => ({ + Window: class { + document = { + createElement: () => ({}), + body: { appendChild: () => {}, innerHTML: '' }, + }; + happyDOM = { abort: () => {} }; + close() {} + }, +})); + +async function main() { + const { openDocument } = await import('../../lib/document'); + + const io = { + stdout: () => {}, + stderr: () => {}, + readStdinBytes: async () => new Uint8Array(), + }; + + let opened: { dispose(): void } | undefined; + try { + opened = await openDocument(undefined, io, { + editorOpenOptions: { + modules: { + trackChanges: { + replacements: 'independent', + }, + }, + }, + }); + } finally { + opened?.dispose(); + } + + console.log(JSON.stringify({ editorOpenCalled, capturedTrackChanges })); +} + +main().catch((e) => { + console.error(e); + process.exit(1); +}); diff --git a/apps/cli/src/__tests__/lib/error-mapping.test.ts b/apps/cli/src/__tests__/lib/error-mapping.test.ts index cbb0f165da..88b3303fd9 100644 --- a/apps/cli/src/__tests__/lib/error-mapping.test.ts +++ b/apps/cli/src/__tests__/lib/error-mapping.test.ts @@ -14,6 +14,32 @@ describe('mapInvokeError', () => { expect(mapped.message).toBe('blocks.delete requires a target.'); expect(mapped.details).toEqual({ operationId: 'blocks.delete', details: { field: 'target' } }); }); + + test('preserves TARGET_NOT_FOUND for trackChanges.decide stale ids', () => { + const error = Object.assign(new Error('Tracked change "tc-1" was not found.'), { + code: 'TARGET_NOT_FOUND', + details: { id: 'tc-1' }, + }); + + const mapped = mapInvokeError('trackChanges.decide' as any, error); + expect(mapped.code).toBe('TARGET_NOT_FOUND'); + expect(mapped.details).toEqual({ operationId: 'trackChanges.decide', details: { id: 'tc-1' } }); + }); + + test('keeps track-changes accept/reject helper missing ids backward compatible', () => { + const error = Object.assign(new Error('Tracked change "tc-1" was not found.'), { + code: 'TARGET_NOT_FOUND', + details: { id: 'tc-1' }, + }); + + const accept = mapInvokeError('trackChanges.decide' as any, error, { commandName: 'track-changes accept' }); + const reject = mapInvokeError('trackChanges.decide' as any, error, { commandName: 'track-changes reject' }); + const canonical = mapInvokeError('trackChanges.decide' as any, error, { commandName: 'track-changes decide' }); + + expect(accept.code).toBe('TRACK_CHANGE_NOT_FOUND'); + expect(reject.code).toBe('TRACK_CHANGE_NOT_FOUND'); + expect(canonical.code).toBe('TARGET_NOT_FOUND'); + }); }); // --------------------------------------------------------------------------- @@ -205,6 +231,24 @@ describe('mapFailedReceipt: plan-engine code passthrough', () => { expect(result!.code).toBe('COMMAND_FAILED'); }); + test('maps helper trackChanges.decide TARGET_NOT_FOUND receipts to TRACK_CHANGE_NOT_FOUND', () => { + const receipt = { + success: false, + failure: { + code: 'TARGET_NOT_FOUND', + message: 'Tracked change "tc-1" was not found.', + }, + }; + + const helper = mapFailedReceipt('trackChanges.decide' as any, receipt, { commandName: 'track-changes accept' }); + const canonical = mapFailedReceipt('trackChanges.decide' as any, receipt, { + commandName: 'track-changes decide', + }); + + expect(helper?.code).toBe('TRACK_CHANGE_NOT_FOUND'); + expect(canonical?.code).toBe('TARGET_NOT_FOUND'); + }); + test('plan-engine code MATCH_NOT_FOUND passes through with structured details', () => { const receipt = { success: false, diff --git a/apps/cli/src/__tests__/lib/open-document-track-changes-forwarding.test.ts b/apps/cli/src/__tests__/lib/open-document-track-changes-forwarding.test.ts new file mode 100644 index 0000000000..6df793eb59 --- /dev/null +++ b/apps/cli/src/__tests__/lib/open-document-track-changes-forwarding.test.ts @@ -0,0 +1,32 @@ +/** + * Verifies that `openDocument` forwards `editorOpenOptions.modules.trackChanges` + * through to `Editor.open()`. + * + * This runs in a subprocess so `mock.module` cannot leak into other tests. + */ +import { describe, expect, test } from 'bun:test'; +import { join } from 'path'; + +const WORKER_SCRIPT = join(import.meta.dir, '_open-document-track-changes-worker.ts'); + +describe('openDocument track changes forwarding', () => { + test('trackChanges replacement mode reaches Editor.open()', async () => { + const proc = Bun.spawn(['bun', 'run', WORKER_SCRIPT], { + cwd: join(import.meta.dir, '../../..'), + stdout: 'pipe', + stderr: 'pipe', + }); + + const exitCode = await proc.exited; + const stdout = await new Response(proc.stdout).text(); + const stderr = await new Response(proc.stderr).text(); + + if (exitCode !== 0) { + throw new Error(`Worker failed (code ${exitCode}):\n${stderr || stdout}`); + } + + const result = JSON.parse(stdout.trim()); + expect(result.editorOpenCalled).toBe(true); + expect(result.capturedTrackChanges).toEqual({ replacements: 'independent' }); + }); +}); diff --git a/apps/cli/src/__tests__/lib/operation-runtime-metadata.test.ts b/apps/cli/src/__tests__/lib/operation-runtime-metadata.test.ts index c1a4cada5c..364883d0b6 100644 --- a/apps/cli/src/__tests__/lib/operation-runtime-metadata.test.ts +++ b/apps/cli/src/__tests__/lib/operation-runtime-metadata.test.ts @@ -75,4 +75,32 @@ describe('operation runtime metadata', () => { const optionNames = openOptions.map((o) => o.name); expect(optionNames).toContain('password'); }); + + test('doc.open metadata includes trackChanges JSON param', () => { + const openMeta = CLI_OPERATION_METADATA['doc.open']; + const trackChangesParam = openMeta.params.find((p) => p.name === 'trackChanges'); + + expect(trackChangesParam).toBeDefined(); + expect(trackChangesParam!.kind).toBe('jsonFlag'); + expect(trackChangesParam!.type).toBe('json'); + expect(trackChangesParam!.flag).toBe('track-changes-json'); + expect(trackChangesParam!.schema).toEqual({ + type: 'object', + properties: { + replacements: { + type: 'string', + enum: ['paired', 'independent'], + description: 'Tracked replacement grouping mode.', + }, + }, + }); + }); + + test('doc.open option specs include track-changes-json flag', () => { + const openOptions = CLI_OPERATION_OPTION_SPECS['doc.open']; + const trackChangesOption = openOptions.find((o) => o.name === 'track-changes-json'); + + expect(trackChangesOption).toBeDefined(); + expect(trackChangesOption!.type).toBe('string'); + }); }); diff --git a/apps/cli/src/__tests__/lib/special-handlers.test.ts b/apps/cli/src/__tests__/lib/special-handlers.test.ts new file mode 100644 index 0000000000..e6095a16ff --- /dev/null +++ b/apps/cli/src/__tests__/lib/special-handlers.test.ts @@ -0,0 +1,144 @@ +import { describe, expect, test } from 'bun:test'; +import { POST_INVOKE_HOOKS, PRE_INVOKE_HOOKS } from '../../lib/special-handlers'; + +const rawTrackChangesList = { + evaluatedRevision: '0', + total: 2, + items: [ + { + id: 'raw-parent', + handle: { ref: 'tc::body::raw-parent', refStability: 'stable', targetKind: 'trackedChange' }, + address: { kind: 'entity', entityType: 'trackedChange', entityId: 'raw-parent' }, + type: 'insert', + author: 'Missy Fox', + date: '2026-05-20T14:08:00Z', + excerpt: 'ABCXYZ', + overlap: { + visualLayers: [ + { id: 'raw-parent', type: 'insert', relationship: 'parent' }, + { id: 'raw-child', type: 'delete', relationship: 'child' }, + ], + preferredContextTargetId: 'raw-child', + preferredContextTarget: { id: 'raw-child', type: 'delete', relationship: 'child' }, + }, + }, + { + id: 'raw-child', + handle: { ref: 'tc::body::raw-child', refStability: 'stable', targetKind: 'trackedChange' }, + address: { kind: 'entity', entityType: 'trackedChange', entityId: 'raw-child' }, + type: 'delete', + author: 'Vivienne Salisbury', + date: '2026-05-20T14:08:00Z', + excerpt: 'HELLO', + }, + ], +}; + +type Overlap = { + visualLayers: Array<{ id: string }>; + preferredContextTargetId: string; + preferredContextTarget: { id: string }; +}; + +type TrackChangeItem = { + id: string; + overlap?: Overlap; +}; + +describe('special track-changes handlers', () => { + test('normalizes overlap IDs to the same stable IDs as trackChanges.list items', () => { + const hook = POST_INVOKE_HOOKS['trackChanges.list']; + if (!hook) throw new Error('trackChanges.list post hook must be registered'); + + const result = hook(rawTrackChangesList, { editor: {} as never }) as { items: TrackChangeItem[] }; + const parent = result.items[0] as TrackChangeItem & { overlap: Overlap }; + const child = result.items[1] as TrackChangeItem; + + expect(parent.id).toBe('raw-parent'); + expect(child.id).toBe('raw-child'); + expect(parent.overlap.visualLayers[0].id).toBe(parent.id); + expect(parent.overlap.visualLayers[1].id).toBe(child.id); + expect(parent.overlap.preferredContextTargetId).toBe(child.id); + expect(parent.overlap.preferredContextTarget.id).toBe(child.id); + }); + + test('normalizes overlap IDs on trackChanges.get output', () => { + const hook = POST_INVOKE_HOOKS['trackChanges.get']; + const listHook = POST_INVOKE_HOOKS['trackChanges.list']; + if (!hook) throw new Error('trackChanges.get post hook must be registered'); + if (!listHook) throw new Error('trackChanges.list post hook must be registered'); + + const context = { + editor: { + doc: { + invoke: () => rawTrackChangesList, + }, + }, + }; + const result = hook(rawTrackChangesList.items[0], context as never) as TrackChangeItem & { overlap: Overlap }; + const normalizedList = listHook(rawTrackChangesList, { editor: {} as never }) as { items: TrackChangeItem[] }; + const parentId = normalizedList.items[0]?.id; + const childId = normalizedList.items[1]?.id; + if (!parentId || !childId) throw new Error('expected normalized list to contain parent and child ids'); + + expect(result.id).toBe(parentId); + expect(result.overlap.visualLayers[1].id).toBe(childId); + expect(result.overlap.preferredContextTargetId).toBe(childId); + expect(result.overlap.preferredContextTarget.id).toBe(childId); + }); + + test('flattens formatRange receipts for CLI response validation', () => { + const hook = POST_INVOKE_HOOKS.formatRange; + if (!hook) throw new Error('formatRange post hook must be registered'); + + const result = hook( + { + resolution: { + target: { kind: 'text', blockId: 'p1', range: { start: 1, end: 4 } }, + range: { kind: 'text', segments: [{ blockId: 'p1', range: { start: 1, end: 4 } }] }, + }, + applied: true, + }, + { editor: {} as never }, + ) as { + target: unknown; + resolvedRange: unknown; + receipt: { applied: boolean }; + }; + + expect(result.target).toEqual({ kind: 'text', blockId: 'p1', range: { start: 1, end: 4 } }); + expect(result.resolvedRange).toEqual({ + kind: 'text', + segments: [{ blockId: 'p1', range: { start: 1, end: 4 } }], + }); + expect(result.receipt.applied).toBe(true); + }); + + test('translates stable trackedChangeId comment targets back to raw ids before invoke', () => { + const hook = PRE_INVOKE_HOOKS['comments.create']; + if (!hook) throw new Error('comments.create pre hook must be registered'); + + const normalizedList = POST_INVOKE_HOOKS['trackChanges.list']?.(rawTrackChangesList, { + editor: {} as never, + }) as { items: TrackChangeItem[] }; + const stableId = normalizedList.items[0]?.id; + if (!stableId) throw new Error('expected normalized list to contain a stable id'); + + const result = hook( + { + target: { trackedChangeId: stableId }, + text: 'comment', + }, + { + editor: { + doc: { + invoke: () => rawTrackChangesList, + }, + }, + } as never, + ) as { target: { trackedChangeId: string }; text: string }; + + expect(result.target.trackedChangeId).toBe('raw-parent'); + expect(result.text).toBe('comment'); + }); +}); diff --git a/apps/cli/src/__tests__/lib/validate-type-spec.test.ts b/apps/cli/src/__tests__/lib/validate-type-spec.test.ts index 415ecc8266..621b16cb7c 100644 --- a/apps/cli/src/__tests__/lib/validate-type-spec.test.ts +++ b/apps/cli/src/__tests__/lib/validate-type-spec.test.ts @@ -179,6 +179,39 @@ describe('doc.find select schema — accepts canonical and shorthand forms', () }); }); +describe('comments target schema — accepts selection and tracked-change targets', () => { + const createMetadata = CLI_OPERATION_METADATA['doc.comments.create']; + const patchMetadata = CLI_OPERATION_METADATA['doc.comments.patch']; + const createTargetSchema = createMetadata.params.find((p) => p.name === 'target')?.schema; + const patchTargetSchema = patchMetadata.params.find((p) => p.name === 'target')?.schema; + + if (!createTargetSchema || !patchTargetSchema) { + throw new Error('comments metadata missing target schema'); + } + + const selectionTarget = { + kind: 'selection', + start: { kind: 'text', blockId: '36D666B6', offset: 10 }, + end: { kind: 'text', blockId: '36D666B6', offset: 18 }, + }; + + test('accepts SelectionTarget for comments.create', () => { + expect(() => validateValueAgainstTypeSpec(selectionTarget, createTargetSchema, 'target')).not.toThrow(); + }); + + test('accepts SelectionTarget for comments.patch', () => { + expect(() => validateValueAgainstTypeSpec(selectionTarget, patchTargetSchema, 'target')).not.toThrow(); + }); + + test('accepts tracked-change target without explicit kind for comments.create', () => { + expect(() => validateValueAgainstTypeSpec({ trackedChangeId: 'tc-1' }, createTargetSchema, 'target')).not.toThrow(); + }); + + test('accepts tracked-change target without explicit kind for comments.patch', () => { + expect(() => validateValueAgainstTypeSpec({ trackedChangeId: 'tc-1' }, patchTargetSchema, 'target')).not.toThrow(); + }); +}); + describe('validateValueAgainstTypeSpec – object without explicit properties', () => { // type: 'object' schemas that use additionalProperties (or nothing at all) // must not crash the validator when `properties` is absent. diff --git a/apps/cli/src/cli/cli-only-operation-definitions.ts b/apps/cli/src/cli/cli-only-operation-definitions.ts index 8707f70ef3..4e40695649 100644 --- a/apps/cli/src/cli/cli-only-operation-definitions.ts +++ b/apps/cli/src/cli/cli-only-operation-definitions.ts @@ -87,7 +87,8 @@ export const CLI_ONLY_OPERATION_DEFINITIONS: Record = { 'blocks.list': 'listed blocks', 'blocks.delete': 'deleted block', 'blocks.deleteRange': 'deleted block range', + formatRange: 'applied style', 'format.apply': 'applied style', ...buildFormatInlineAliasRecord('applied style'), ...buildParagraphRecord('updated paragraph formatting'), @@ -473,6 +474,7 @@ export const OUTPUT_FORMAT: Record = { 'blocks.list': 'plain', 'blocks.delete': 'plain', 'blocks.deleteRange': 'plain', + formatRange: 'mutationReceipt', 'format.apply': 'mutationReceipt', ...buildFormatInlineAliasRecord('mutationReceipt'), ...buildParagraphRecord('plain'), @@ -840,6 +842,7 @@ export const RESPONSE_ENVELOPE_KEY: Record 'blocks.list': 'result', 'blocks.delete': 'result', 'blocks.deleteRange': 'result', + formatRange: null, 'format.apply': null, ...buildFormatInlineAliasRecord(null), ...buildParagraphRecord('result'), @@ -1194,6 +1197,7 @@ export const RESPONSE_VALIDATION_KEY: Partial = 'blocks.list': 'blocks', 'blocks.delete': 'blocks', 'blocks.deleteRange': 'blocks', + formatRange: 'textMutation', 'format.apply': 'textMutation', ...buildFormatInlineAliasRecord('textMutation'), ...buildParagraphRecord('textMutation'), diff --git a/apps/cli/src/cli/operation-params.ts b/apps/cli/src/cli/operation-params.ts index 30e19c65a1..923228da75 100644 --- a/apps/cli/src/cli/operation-params.ts +++ b/apps/cli/src/cli/operation-params.ts @@ -56,6 +56,13 @@ const FORCE_PARAM: CliOperationParamSpec = { type: 'boolean', description: 'Bypass confirmation checks.', }; +const SAVE_MODE_PARAM: CliOperationParamSpec = { + name: 'mode', + kind: 'flag', + type: 'string', + schema: { type: 'string', enum: ['review-preserving', 'final'] } as CliTypeSpec, + description: 'Save mode: review-preserving keeps revision markup; final exports accepted content.', +}; const DRY_RUN_PARAM: CliOperationParamSpec = { name: 'dryRun', kind: 'flag', @@ -68,7 +75,7 @@ const CHANGE_MODE_PARAM: CliOperationParamSpec = { kind: 'flag', flag: 'change-mode', type: 'string', - schema: { enum: ['direct', 'tracked'] } as CliTypeSpec, + schema: { type: 'string', enum: ['direct', 'tracked'] } as CliTypeSpec, description: 'Edit mode: "direct" applies changes immediately, "tracked" records as suggestions.', }; const EXPECTED_REVISION_PARAM: CliOperationParamSpec = { @@ -1059,6 +1066,23 @@ const CLI_ONLY_METADATA: Record = { { name: 'overrideType', kind: 'flag', flag: 'override-type', type: 'string' }, { name: 'onMissing', kind: 'flag', flag: 'on-missing', type: 'string' }, { name: 'bootstrapSettlingMs', kind: 'flag', flag: 'bootstrap-settling-ms', type: 'number' }, + { + name: 'trackChanges', + kind: 'jsonFlag', + flag: 'track-changes-json', + type: 'json', + description: 'Track-changes open configuration.', + schema: { + type: 'object', + properties: { + replacements: { + type: 'string', + enum: ['paired', 'independent'], + description: 'Tracked replacement grouping mode.', + }, + }, + } as CliTypeSpec, + }, USER_NAME_PARAM, USER_EMAIL_PARAM, PASSWORD_PARAM, @@ -1071,6 +1095,7 @@ const CLI_ONLY_METADATA: Record = { docRequirement: 'none', params: [ SESSION_PARAM, + SAVE_MODE_PARAM, OUT_PARAM, FORCE_PARAM, { name: 'inPlace', kind: 'flag', flag: 'in-place', type: 'boolean' }, diff --git a/apps/cli/src/cli/types.ts b/apps/cli/src/cli/types.ts index 39384c68bb..93a485fc91 100644 --- a/apps/cli/src/cli/types.ts +++ b/apps/cli/src/cli/types.ts @@ -11,6 +11,7 @@ type TypeSpecBase = { description?: string; + enum?: readonly unknown[]; }; export type CliTypeSpec = @@ -25,6 +26,7 @@ export type CliTypeSpec = type: 'object'; properties: Record; required?: readonly string[]; + additionalProperties?: boolean | CliTypeSpec; } & TypeSpecBase); // --------------------------------------------------------------------------- diff --git a/apps/cli/src/commands/open.ts b/apps/cli/src/commands/open.ts index e7b5d50845..9519d76e66 100644 --- a/apps/cli/src/commands/open.ts +++ b/apps/cli/src/commands/open.ts @@ -27,7 +27,7 @@ const VALID_OVERRIDE_TYPES = new Set(['markdown', 'html', 'text']); const VALID_ON_MISSING = new Set(['seedFromDoc', 'blank', 'error']); export async function runOpen(tokens: string[], context: CommandContext): Promise { - const { parsed, help } = parseOperationArgs('doc.open', tokens, { + const { parsed, args, help } = parseOperationArgs('doc.open', tokens, { commandName: 'open', extraOptionSpecs: [{ name: 'collaboration-file', type: 'string' }], }); @@ -67,6 +67,7 @@ export async function runOpen(tokens: string[], context: CommandContext): Promis const bootstrapSettlingMs = getNumberOption(parsed, 'bootstrap-settling-ms'); const userName = getStringOption(parsed, 'user-name'); const userEmail = getStringOption(parsed, 'user-email'); + const trackChanges = args.trackChanges as { replacements?: 'paired' | 'independent' } | undefined; const allowEnvFallback = context.executionMode !== 'host'; const password = resolvePassword(getStringOption(parsed, 'password'), allowEnvFallback); @@ -142,7 +143,7 @@ export async function runOpen(tokens: string[], context: CommandContext): Promis const user = userName != null || userEmail != null ? { name: userName ?? 'CLI', email: userEmail ?? '' } : undefined; // Build editor open options from override params and password. - const editorOpenOptions: EditorPassThroughOptions & Record = {}; + const editorOpenOptions: EditorPassThroughOptions & { markdown?: string; html?: string; plainText?: string } = {}; if (contentOverride != null && overrideType) { if (overrideType === 'markdown') { editorOpenOptions.markdown = contentOverride; @@ -157,6 +158,13 @@ export async function runOpen(tokens: string[], context: CommandContext): Promis if (password != null) { editorOpenOptions.password = password; } + if (trackChanges?.replacements != null) { + editorOpenOptions.modules = { + trackChanges: { + replacements: trackChanges.replacements, + }, + }; + } return withContextLock( context.io, diff --git a/apps/cli/src/commands/save.ts b/apps/cli/src/commands/save.ts index ea9c4c2f1e..7e7c256ecb 100644 --- a/apps/cli/src/commands/save.ts +++ b/apps/cli/src/commands/save.ts @@ -10,26 +10,34 @@ import { withActiveContext, writeContextMetadata, } from '../lib/context'; -import { openSessionDocument } from '../lib/document'; +import { exportToPath, openSessionDocument } from '../lib/document'; import { syncCollaborativeSessionSnapshot } from '../lib/session-collab'; import type { CommandContext, CommandExecution } from '../lib/types'; + +type SaveMode = 'review-preserving' | 'final'; + function validateSaveMode( inPlace: boolean, outPath: string | undefined, force: boolean, + mode: string | undefined, ): { inPlace: boolean; outPath?: string; force: boolean; + mode: SaveMode; } { if (inPlace && outPath) { throw new CliError('INVALID_ARGUMENT', 'save: use either --in-place or --out, not both.'); } + const resolvedMode = mode === 'final' ? 'final' : 'review-preserving'; + return { inPlace, outPath, force, + mode: resolvedMode, }; } @@ -40,9 +48,12 @@ export async function runSave(tokens: string[], context: CommandContext): Promis return { command: 'save', data: { - usage: ['superdoc save [--in-place] [--out ] [--force]'], + usage: ['superdoc save [--mode ] [--in-place] [--out ] [--force]'], }, - pretty: ['Usage:', ' superdoc save [--in-place] [--out ] [--force]'].join('\n'), + pretty: [ + 'Usage:', + ' superdoc save [--mode ] [--in-place] [--out ] [--force]', + ].join('\n'), }; } @@ -50,6 +61,7 @@ export async function runSave(tokens: string[], context: CommandContext): Promis getBooleanOption(parsed, 'in-place'), getStringOption(parsed, 'out'), getBooleanOption(parsed, 'force'), + getStringOption(parsed, 'mode'), ); return withActiveContext( @@ -87,9 +99,15 @@ export async function runSave(tokens: string[], context: CommandContext): Promis if (isInPlace && !sourcePath) { throw new CliError('MISSING_REQUIRED', 'save: --in-place requires a source path; use --out .'); } + if (mode.mode !== 'review-preserving' && isInPlace) { + throw new CliError( + 'INVALID_ARGUMENT', + 'save: final-mode export requires --out ; in-place final export would desynchronize the live session.', + ); + } let output: { path: string; byteLength: number }; - if (isInPlace) { + if (mode.mode === 'review-preserving' && isInPlace) { const drift = await detectSourceDrift(effectiveMetadata); if (drift.drifted && !mode.force) { throw new CliError('SOURCE_DRIFT_DETECTED', 'Source document changed since open. Refusing to overwrite.', { @@ -102,8 +120,41 @@ export async function runSave(tokens: string[], context: CommandContext): Promis } output = await copyWorkingDocumentToPath(paths, sourcePath!, true); - } else { + } else if (mode.mode === 'review-preserving') { output = await copyWorkingDocumentToPath(paths, targetPath, mode.force); + } else { + const opened = await openSessionDocument(paths.workingDocPath, context.io, effectiveMetadata, { + sessionId: context.sessionId ?? effectiveMetadata.contextId, + executionMode: context.executionMode, + sessionPool: context.sessionPool, + }); + try { + output = await exportToPath(opened.editor, targetPath, mode.force, { isFinalDoc: true }); + } finally { + opened.dispose(); + } + + return { + command: 'save', + data: { + contextId: effectiveMetadata.contextId, + saved: true, + inPlace: false, + mode: mode.mode, + document: { + path: effectiveMetadata.sourcePath, + source: effectiveMetadata.source, + revision: effectiveMetadata.revision, + }, + context: { + dirty: effectiveMetadata.dirty, + revision: effectiveMetadata.revision, + lastSavedAt: effectiveMetadata.lastSavedAt, + }, + output, + }, + pretty: `Exported final document to ${output.path}`, + }; } const nextSourcePath = isInPlace ? sourcePath! : targetPath; @@ -124,6 +175,7 @@ export async function runSave(tokens: string[], context: CommandContext): Promis contextId: updatedMetadata.contextId, saved: true, inPlace: isInPlace, + mode: mode.mode, document: { path: updatedMetadata.sourcePath, source: updatedMetadata.source, diff --git a/apps/cli/src/commands/session-save.ts b/apps/cli/src/commands/session-save.ts index efbf422d63..581f540075 100644 --- a/apps/cli/src/commands/session-save.ts +++ b/apps/cli/src/commands/session-save.ts @@ -37,14 +37,14 @@ export async function runSessionSave(tokens: string[], context: CommandContext): command: 'session save', data: { usage: [ - 'superdoc session save [--in-place] [--out ] [--force]', - 'superdoc session save --session [--in-place] [--out ] [--force]', + 'superdoc session save [--mode ] [--in-place] [--out ] [--force]', + 'superdoc session save --session [--mode ] [--in-place] [--out ] [--force]', ], }, pretty: [ 'Usage:', - ' superdoc session save [--in-place] [--out ] [--force]', - ' superdoc session save --session [--in-place] [--out ] [--force]', + ' superdoc session save [--mode ] [--in-place] [--out ] [--force]', + ' superdoc session save --session [--mode ] [--in-place] [--out ] [--force]', ].join('\n'), }; } diff --git a/apps/cli/src/lib/document.ts b/apps/cli/src/lib/document.ts index bd01d7347d..cd2d193d70 100644 --- a/apps/cli/src/lib/document.ts +++ b/apps/cli/src/lib/document.ts @@ -50,9 +50,16 @@ interface ContentOverrideOptions { plainText?: string; } +type TrackChangesReplacementMode = 'paired' | 'independent'; + /** Options passed through to Editor.open() alongside content overrides. */ export interface EditorPassThroughOptions { password?: string; + modules?: { + trackChanges?: { + replacements?: TrackChangesReplacementMode; + }; + }; } interface OpenDocumentOptions { @@ -487,7 +494,12 @@ export async function exportOptionalSessionOutput( } } -export async function exportToPath(editor: Editor, outputPath: string, force = false): Promise { +export async function exportToPath( + editor: Editor, + outputPath: string, + force = false, + options: { isFinalDoc?: boolean } = {}, +): Promise { const exists = await pathExists(outputPath); if (exists && !force) { throw new CliError('OUTPUT_EXISTS', `Output path already exists: ${outputPath}`, { @@ -498,7 +510,7 @@ export async function exportToPath(editor: Editor, outputPath: string, force = f let exported: unknown; try { - exported = await editor.exportDocument(); + exported = await editor.exportDocument({ isFinalDoc: options.isFinalDoc }); } catch (error) { const message = error instanceof Error ? error.message : String(error); throw new CliError('DOCUMENT_EXPORT_FAILED', 'Failed to export document.', { diff --git a/apps/cli/src/lib/error-mapping.ts b/apps/cli/src/lib/error-mapping.ts index 4a24bc3a75..177f06ab11 100644 --- a/apps/cli/src/lib/error-mapping.ts +++ b/apps/cli/src/lib/error-mapping.ts @@ -13,6 +13,16 @@ import type { CliExposedOperationId } from '../cli/operation-set.js'; import { OPERATION_FAMILY, type OperationFamily } from '../cli/operation-hints.js'; import { CliError, type AdapterLikeError, type CliErrorCode } from './errors.js'; +type ErrorMappingContext = { + commandName?: string; +}; + +const TRACK_CHANGES_REVIEW_HELPER_COMMANDS = new Set(['track-changes accept', 'track-changes reject']); + +function isTrackChangesReviewHelper(operationId: CliExposedOperationId, context?: ErrorMappingContext): boolean { + return operationId === 'trackChanges.decide' && TRACK_CHANGES_REVIEW_HELPER_COMMANDS.has(context?.commandName ?? ''); +} + // --------------------------------------------------------------------------- // Error code extraction // --------------------------------------------------------------------------- @@ -37,10 +47,22 @@ function extractErrorDetails(error: unknown): unknown { // Per-family error mappers (thrown errors) // --------------------------------------------------------------------------- -function mapTrackChangesError(operationId: CliExposedOperationId, error: unknown, code: string | undefined): CliError { +function mapTrackChangesError( + operationId: CliExposedOperationId, + error: unknown, + code: string | undefined, + context?: ErrorMappingContext, +): CliError { const message = extractErrorMessage(error); const details = extractErrorDetails(error); + if (operationId === 'trackChanges.decide' && code === 'TARGET_NOT_FOUND') { + if (isTrackChangesReviewHelper(operationId, context)) { + return new CliError('TRACK_CHANGE_NOT_FOUND', message, { operationId, details }); + } + return new CliError('TARGET_NOT_FOUND', message, { operationId, details }); + } + if (code === 'TARGET_NOT_FOUND' || (typeof message === 'string' && message.includes('was not found'))) { return new CliError('TRACK_CHANGE_NOT_FOUND', message, { operationId, details }); } @@ -354,7 +376,12 @@ function mapDiffError(operationId: CliExposedOperationId, error: unknown, code: const FAMILY_MAPPERS: Record< OperationFamily, - (operationId: CliExposedOperationId, error: unknown, code: string | undefined) => CliError + ( + operationId: CliExposedOperationId, + error: unknown, + code: string | undefined, + context?: ErrorMappingContext, + ) => CliError > = { trackChanges: mapTrackChangesError, comments: mapCommentsError, @@ -385,11 +412,15 @@ function resolveOperationFamily(operationId: CliExposedOperationId): OperationFa * Maps an invoke() exception to a CLI error with the appropriate error code. * Called by the generic dispatch path after every invoke() failure. */ -export function mapInvokeError(operationId: CliExposedOperationId, error: unknown): CliError { +export function mapInvokeError( + operationId: CliExposedOperationId, + error: unknown, + context?: ErrorMappingContext, +): CliError { if (error instanceof CliError) return error; const code = extractErrorCode(error); const family = resolveOperationFamily(operationId); - return FAMILY_MAPPERS[family](operationId, error, code); + return FAMILY_MAPPERS[family](operationId, error, code, context); } // --------------------------------------------------------------------------- @@ -418,7 +449,11 @@ function isReceiptLike(value: unknown): value is ReceiptLike { * Returns null if the result is not a failed receipt (either successful or * not receipt-shaped at all). */ -export function mapFailedReceipt(operationId: CliExposedOperationId, result: unknown): CliError | null { +export function mapFailedReceipt( + operationId: CliExposedOperationId, + result: unknown, + context?: ErrorMappingContext, +): CliError | null { if (!isReceiptLike(result)) return null; if (result.success) return null; @@ -439,6 +474,12 @@ export function mapFailedReceipt(operationId: CliExposedOperationId, result: unk // Track-changes family if (family === 'trackChanges') { + if (operationId === 'trackChanges.decide' && failureCode === 'TARGET_NOT_FOUND') { + if (isTrackChangesReviewHelper(operationId, context)) { + return new CliError('TRACK_CHANGE_NOT_FOUND', failureMessage, { operationId, failure }); + } + return new CliError('TARGET_NOT_FOUND', failureMessage, { operationId, failure }); + } if (failureCode === 'TRACK_CHANGE_COMMAND_UNAVAILABLE') { return new CliError('TRACK_CHANGE_COMMAND_UNAVAILABLE', failureMessage, { operationId, failure }); } diff --git a/apps/cli/src/lib/generic-dispatch.ts b/apps/cli/src/lib/generic-dispatch.ts index 205f38d7dc..b39c1cf0b1 100644 --- a/apps/cli/src/lib/generic-dispatch.ts +++ b/apps/cli/src/lib/generic-dispatch.ts @@ -15,6 +15,7 @@ export type DocOperationRequest = { operationId: CliExposedOperationId; input: Record; context: CommandContext; + commandName?: string; }; /** diff --git a/apps/cli/src/lib/mutation-orchestrator.ts b/apps/cli/src/lib/mutation-orchestrator.ts index c140fa1af9..8db24c01f0 100644 --- a/apps/cli/src/lib/mutation-orchestrator.ts +++ b/apps/cli/src/lib/mutation-orchestrator.ts @@ -54,6 +54,7 @@ function invokeOperation( operationId: CliExposedOperationId, input: Record, options?: Record, + commandName?: string, ): unknown { const apiInput = extractInvokeInput(operationId, input); const preHook = PRE_INVOKE_HOOKS[operationId]; @@ -67,11 +68,11 @@ function invokeOperation( options, }); } catch (error) { - throw mapInvokeError(operationId, error); + throw mapInvokeError(operationId, error, { commandName }); } // Check for failed receipts (non-throwing failure path) - const failedReceiptError = mapFailedReceipt(operationId, result); + const failedReceiptError = mapFailedReceipt(operationId, result, { commandName }); if (failedReceiptError) throw failedReceiptError; const postHook = POST_INVOKE_HOOKS[operationId]; @@ -120,7 +121,7 @@ export async function executeMutationOperation(request: DocOperationRequest): Pr const changeMode = readChangeMode(input); const force = readBoolean(input, 'force'); const expectedRevision = readOptionalNumber(input, 'expectedRevision'); - const commandName = deriveCommandName(operationId); + const commandName = request.commandName ?? deriveCommandName(operationId); const catalog = COMMAND_CATALOG[operationId]; const invokeOptions: Record = {}; @@ -152,7 +153,7 @@ export async function executeMutationOperation(request: DocOperationRequest): Pr const source = doc === '-' ? 'stdin' : 'path'; const opened = await openDocument(doc, context.io); try { - const result = invokeOperation(opened.editor, operationId, input, invokeOptions); + const result = invokeOperation(opened.editor, operationId, input, invokeOptions, commandName); const document: DocumentPayload = { path: source === 'path' ? doc : undefined, source, @@ -204,7 +205,7 @@ export async function executeMutationOperation(request: DocOperationRequest): Pr }); try { - const result = invokeOperation(opened.editor, operationId, input, invokeOptions); + const result = invokeOperation(opened.editor, operationId, input, invokeOptions, commandName); if (dryRun) { const document: DocumentPayload = { diff --git a/apps/cli/src/lib/operation-executor.ts b/apps/cli/src/lib/operation-executor.ts index 0ea224ac6c..76bda7bcf6 100644 --- a/apps/cli/src/lib/operation-executor.ts +++ b/apps/cli/src/lib/operation-executor.ts @@ -238,6 +238,7 @@ export async function executeOperation(request: ExecuteOperationRequest): Promis operationId: docApiId as CliExposedOperationId, input, context: effectiveContext, + commandName, }); } diff --git a/apps/cli/src/lib/read-orchestrator.ts b/apps/cli/src/lib/read-orchestrator.ts index cdc3efc429..51cfa4355d 100644 --- a/apps/cli/src/lib/read-orchestrator.ts +++ b/apps/cli/src/lib/read-orchestrator.ts @@ -35,6 +35,7 @@ function invokeOperation( editor: EditorWithDoc, operationId: CliExposedOperationId, input: Record, + commandName?: string, ): unknown { const apiInput = extractInvokeInput(operationId, input); const preHook = PRE_INVOKE_HOOKS[operationId]; @@ -47,7 +48,7 @@ function invokeOperation( input: transformedInput, }); } catch (error) { - throw mapInvokeError(operationId, error); + throw mapInvokeError(operationId, error, { commandName }); } const postHook = POST_INVOKE_HOOKS[operationId]; @@ -102,13 +103,13 @@ export async function executeReadOperation(request: DocOperationRequest): Promis // snapshot sync) from running past a drift failure. const envelopeKey = resolveResponseEnvelopeKey(operationId); const doc = readOptionalString(input, 'doc'); - const commandName = deriveCommandName(operationId); + const commandName = request.commandName ?? deriveCommandName(operationId); if (doc) { const source = doc === '-' ? 'stdin' : 'path'; const opened = await openDocument(doc, context.io); try { - const result = invokeOperation(opened.editor, operationId, input); + const result = invokeOperation(opened.editor, operationId, input, commandName); const document: DocumentPayload = { path: source === 'path' ? doc : undefined, source, @@ -140,7 +141,7 @@ export async function executeReadOperation(request: DocOperationRequest): Promis }); try { - const result = invokeOperation(opened.editor, operationId, input); + const result = invokeOperation(opened.editor, operationId, input, commandName); // For oneshot collab reads, sync snapshot to keep working.docx current const isHostMode = context.executionMode === 'host' && context.sessionPool != null; diff --git a/apps/cli/src/lib/special-handlers.ts b/apps/cli/src/lib/special-handlers.ts index 24029e20f6..46b3792303 100644 --- a/apps/cli/src/lib/special-handlers.ts +++ b/apps/cli/src/lib/special-handlers.ts @@ -8,7 +8,6 @@ * capability should move into document-api. */ -import { createHash } from 'node:crypto'; import { INLINE_PROPERTY_REGISTRY } from '@superdoc/document-api'; import type { CliExposedOperationId } from '../cli/operation-set.js'; import type { EditorWithDoc } from './document.js'; @@ -27,6 +26,7 @@ type PreInvokeHook = (input: unknown, context: HookContext) => unknown; type PostInvokeHook = (result: unknown, context: HookContext) => unknown; const FORMAT_RECEIPT_OPERATION_IDS: readonly CliExposedOperationId[] = [ + 'formatRange', 'format.apply', ...INLINE_PROPERTY_REGISTRY.map((entry) => `format.${entry.key}` as CliExposedOperationId), ]; @@ -58,18 +58,45 @@ function asTrackChangeAddress(value: unknown): { kind: string; entityType: strin }; } -function stableTrackChangeSignature(change: TrackChangeLike): string { - const type = typeof change.type === 'string' ? change.type : ''; - const author = typeof change.author === 'string' ? change.author : ''; - const authorEmail = typeof change.authorEmail === 'string' ? change.authorEmail : ''; - const date = typeof change.date === 'string' ? change.date : ''; - const excerpt = typeof change.excerpt === 'string' ? change.excerpt : ''; - return `${type}|${author}|${authorEmail}|${date}|${excerpt}`; +function normalizeStableTrackChangeId(value: unknown, rawToStableId: ReadonlyMap): unknown { + if (typeof value !== 'string' || value.length === 0) return value; + return rawToStableId.get(value) ?? value; +} + +function normalizeOverlapLayer(value: unknown, rawToStableId: ReadonlyMap): unknown { + const record = asRecord(value); + if (!record) return value; + return { + ...record, + id: normalizeStableTrackChangeId(record.id, rawToStableId), + }; +} + +function normalizeTrackChangeOverlap(value: unknown, rawToStableId: ReadonlyMap): unknown { + const record = asRecord(value); + if (!record) return value; + + const visualLayers = Array.isArray(record.visualLayers) + ? record.visualLayers.map((layer) => normalizeOverlapLayer(layer, rawToStableId)) + : record.visualLayers; + const preferredContextTarget = record.preferredContextTarget + ? normalizeOverlapLayer(record.preferredContextTarget, rawToStableId) + : record.preferredContextTarget; + + return { + ...record, + ...(Array.isArray(record.visualLayers) ? { visualLayers } : {}), + preferredContextTargetId: normalizeStableTrackChangeId(record.preferredContextTargetId, rawToStableId), + ...(record.preferredContextTarget ? { preferredContextTarget } : {}), + }; } /** * Builds stable-ID ↔ raw-ID mappings from a track-changes list result. - * The CLI uses SHA-1-based stable IDs instead of adapter raw IDs. + * The CLI preserves the adapter's logical ids when they are present. + * The mapping is still retained so pre-invoke hooks can translate ids from + * list/get payloads back into the exact raw/runtime ids that mutating + * adapters expect. */ function buildStableIdMappings(rawListResult: unknown): { normalizedResult: unknown; @@ -83,36 +110,38 @@ function buildStableIdMappings(rawListResult: unknown): { const stableToRawId = new Map(); const rawToStableId = new Map(); - const signatureCounts = new Map(); - const normalizedItems = asArray(record.items) + const entries = asArray(record.items) .map((entry) => asRecord(entry)) .filter((entry): entry is Record => Boolean(entry)) .map((entry) => { const rawId = (typeof entry.id === 'string' && entry.id.length > 0 ? entry.id : undefined) ?? asTrackChangeAddress(entry.address)?.entityId; - if (!rawId) return entry; + if (!rawId) return { entry }; - const signature = stableTrackChangeSignature(entry); - const hash = createHash('sha1').update(signature).digest('hex').slice(0, 24); - const nextCount = (signatureCounts.get(hash) ?? 0) + 1; - signatureCounts.set(hash, nextCount); - const stableId = nextCount === 1 ? hash : `${hash}-${nextCount}`; + const stableId = rawId; stableToRawId.set(stableId, rawId); rawToStableId.set(rawId, stableId); - const normalizedAddress = asTrackChangeAddress(entry.address); - const handleRecord = asRecord(entry.handle); - return { - ...entry, - id: stableId, - address: normalizedAddress ? { ...normalizedAddress, entityId: stableId } : entry.address, - handle: handleRecord ? { ...handleRecord, ref: `tc:${stableId}` } : entry.handle, - }; + return { entry, rawId, stableId }; }); + const normalizedItems = entries.map(({ entry, rawId, stableId }) => { + if (!rawId || !stableId) return entry; + + const normalizedAddress = asTrackChangeAddress(entry.address); + const handleRecord = asRecord(entry.handle); + return { + ...entry, + id: stableId, + address: normalizedAddress ? { ...normalizedAddress, entityId: stableId } : entry.address, + handle: handleRecord ? { ...handleRecord, ref: `tc:${stableId}` } : entry.handle, + ...(entry.overlap !== undefined ? { overlap: normalizeTrackChangeOverlap(entry.overlap, rawToStableId) } : {}), + }; + }); + return { normalizedResult: { ...record, @@ -172,6 +201,31 @@ const resolveReviewDecideId: PreInvokeHook = (input, context) => { return { ...record, target: { ...target, id: rawId } }; }; +/** + * Comment target shapes can carry trackedChangeId values copied from + * `trackChanges.list`, which the CLI normalizes to stable SHA-1 IDs. The + * adapter expects raw/runtime ids, so translate the target id before invoke. + */ +const resolveCommentTrackedChangeTargetId: PreInvokeHook = (input, context) => { + const record = asRecord(input); + if (!record) return input; + + const target = asRecord(record.target); + if (!target) return input; + + const stableId = typeof target.trackedChangeId === 'string' ? target.trackedChangeId : undefined; + if (!stableId) return input; + + const listResult = context.editor.doc.invoke({ + operationId: 'trackChanges.list' as const, + input: {}, + }); + const { stableToRawId } = buildStableIdMappings(listResult); + const rawId = stableToRawId.get(stableId) ?? stableId; + + return { ...record, target: { ...target, trackedChangeId: rawId } }; +}; + // --------------------------------------------------------------------------- // Post-invoke hooks // --------------------------------------------------------------------------- @@ -207,6 +261,7 @@ const normalizeTrackChangeGetId: PostInvokeHook = (result, context) => { ...record, id: stableId, address: normalizedAddress ? { ...normalizedAddress, entityId: stableId } : record.address, + ...(record.overlap !== undefined ? { overlap: normalizeTrackChangeOverlap(record.overlap, rawToStableId) } : {}), }; }; @@ -242,6 +297,9 @@ export const PRE_INVOKE_HOOKS: Partial superdoc.export(...)` — + * the callback param is `{ superdoc }`, not the instance). + * + * Scope (v1): + * - apps/docs/editor/superdoc/** + * - pattern === 'superdoc' (snippets importing `superdoc` or + * instantiating `new SuperDoc`) + * - "Full Example" fenced blocks only (the canonical copy-pasteable + * form; "Usage" snippets are intentional fragments) + * + * Fences supported: + * - javascript / js → `.js` + `// @ts-check` + `allowJs` + `checkJs` + * - typescript / ts / tsx → `.ts` + strict + * + * Dist resolution: `superdoc` is resolved via tsconfig `paths` pointing at + * `packages/superdoc/dist/superdoc/src/public/index.d.ts`. Docs CI builds + * dist before this gate runs; local dev requires `pnpm --filter superdoc + * build` first. + * + * Why dist, not source: docs examples are what consumers copy-paste. Source + * types include internal helpers that aren't exported; checking against + * dist matches what a real consumer would see. + * + * Why dist, not packed tarball: the packed-tarball matrix already covers + * package-shape correctness (tests/consumer-typecheck). Adding a pack step + * here would duplicate work and slow the gate; dist is a sufficient proxy + * for consumer-facing types. + */ + +import { mkdirSync, mkdtempSync, rmSync, writeFileSync, readFileSync } from 'node:fs'; +import { join, dirname, relative, resolve } from 'node:path'; +import { tmpdir } from 'node:os'; +import { fileURLToPath } from 'node:url'; +import { spawnSync } from 'node:child_process'; + +import { extractExamples, type CodeExample } from './lib/extract.ts'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const docsRoot = resolve(__dirname, '..'); +const repoRoot = resolve(__dirname, '..', '..', '..'); +const distTypesEntry = resolve(repoRoot, 'packages/superdoc/dist/superdoc/src/public/index.d.ts'); + +const SCOPE_PREFIX = 'editor/superdoc/'; + +/** + * Ambient stubs for placeholder identifiers that appear in docs examples + * (`yourFile`, `doc1`, etc.). Written to a shared `.d.ts` in the temp + * project rather than prepended into each snippet, so it works for both + * `.ts` and `.js` files (TS rejects `declare` inside `.js`). + * + * Kept intentionally tiny — when docs reference a new placeholder, prefer + * fixing the doc rather than expanding this list. + */ +const PLACEHOLDERS_DTS = ` +// Document/file placeholders used in docs examples. +declare const yourFile: File; +declare const file: File; +declare const doc1: File; +declare const doc2: File; +declare const content: string; + +// Helper-function placeholders. Docs examples reference these to keep +// the focus on SuperDoc usage; the typecheck shouldn't require docs to +// inline a complete app. Signatures intentionally permissive (unknown +// args) so the doc reads naturally without leaking type assertions. +declare function cleanup(): void; +declare function autoSave(...args: unknown[]): void; +declare function adjustLayout(...args: unknown[]): void; +declare function showOnlineUsers(...args: unknown[]): void; +declare function updateUserCursors(...args: unknown[]): void; +declare function showLockBanner(...args: unknown[]): void; +`.trimStart(); + +function langKind(lang: string): 'js' | 'ts' | null { + const l = lang.toLowerCase(); + if (l === 'js' || l === 'javascript') return 'js'; + if (l === 'ts' || l === 'typescript' || l === 'tsx') return 'ts'; + return null; +} + +function inScope(example: CodeExample): boolean { + if (example.pattern !== 'superdoc') return false; + if (!example.file.startsWith(SCOPE_PREFIX)) return false; + return langKind(example.lang) !== null; +} + +interface PreparedExample { + index: number; + example: CodeExample; + kind: 'js' | 'ts'; + tempFile: string; +} + +function prepareTempProject(examples: CodeExample[]): { tempDir: string; prepared: PreparedExample[] } { + const tempDir = mkdtempSync(join(tmpdir(), 'superdoc-doctest-types-')); + const srcDir = join(tempDir, 'src'); + mkdirSync(srcDir, { recursive: true }); + + // Shared ambient declarations file — picked up by tsconfig `include` + // and visible to both .ts and .js example files in the same project. + writeFileSync(join(srcDir, 'placeholders.d.ts'), PLACEHOLDERS_DTS); + + const prepared: PreparedExample[] = []; + for (let i = 0; i < examples.length; i++) { + const example = examples[i]; + const kind = langKind(example.lang)!; + const ext = kind === 'ts' ? 'ts' : 'js'; + const header = kind === 'js' ? '// @ts-check\n' : ''; + const tempFile = join(srcDir, `example-${i}.${ext}`); + writeFileSync(tempFile, `${header}${example.code}\n`); + prepared.push({ index: i, example, kind, tempFile }); + } + + // tsconfig: strict + allowJs/checkJs (only JS fences need them, but + // enabling globally is simpler than per-file projects and TS only + // checks .js when checkJs is on AND the file has // @ts-check or is + // included by allowJs+checkJs). + const tsconfig = { + compilerOptions: { + target: 'ESNext', + module: 'ESNext', + moduleResolution: 'bundler', + strict: true, + noEmit: true, + allowJs: true, + checkJs: true, + skipLibCheck: true, + esModuleInterop: true, + resolveJsonModule: true, + isolatedModules: true, + types: [], + paths: { + superdoc: [distTypesEntry], + }, + }, + include: ['src/**/*.ts', 'src/**/*.js', 'src/**/*.d.ts'], + }; + writeFileSync(join(tempDir, 'tsconfig.json'), JSON.stringify(tsconfig, null, 2)); + + return { tempDir, prepared }; +} + +interface TscError { + file: string; + line: number; + col: number; + message: string; +} + +function parseTscErrors(stdout: string, tempDir: string): TscError[] { + const errors: TscError[] = []; + // tsc format: "src/example-0.ts(12,5): error TS2339: Property ..." + const re = /^(.+?)\((\d+),(\d+)\): error TS\d+: (.+)$/; + for (const line of stdout.split('\n')) { + const m = line.match(re); + if (!m) continue; + errors.push({ + file: m[1].startsWith('/') ? m[1] : join(tempDir, m[1]), + line: Number(m[2]), + col: Number(m[3]), + message: m[4], + }); + } + return errors; +} + +function main(): void { + if (!existsLocal(distTypesEntry)) { + console.error(`[doctest-types] missing dist types entry: ${distTypesEntry}`); + console.error( + '[doctest-types] run `pnpm --filter superdoc build` first, or this script in a CI step ' + + 'that builds dist beforehand.', + ); + process.exit(2); + } + + const all = extractExamples(docsRoot); + const inScopeExamples = all.filter(inScope); + + console.log('[doctest-types] SuperDoc docs snippet type-check (SD-673)'); + console.log('='.repeat(72)); + console.log( + `Examples discovered (Full Example, superdoc-pattern): ${all.filter((e) => e.pattern === 'superdoc').length}`, + ); + console.log(`In scope (${SCOPE_PREFIX}**, JS/TS fences): ${inScopeExamples.length}`); + console.log(''); + + if (inScopeExamples.length === 0) { + console.log('OK no in-scope examples; nothing to check.'); + return; + } + + const { tempDir, prepared } = prepareTempProject(inScopeExamples); + let exitCode = 0; + try { + // tsc is resolved from the docs workspace (apps/docs/node_modules/.bin). + const tscBin = resolve(docsRoot, '..', '..', 'node_modules', '.bin', 'tsc'); + const result = spawnSync(tscBin, ['-p', join(tempDir, 'tsconfig.json'), '--pretty', 'false'], { + cwd: tempDir, + encoding: 'utf8', + }); + const stdout = result.stdout ?? ''; + const stderr = result.stderr ?? ''; + const errors = parseTscErrors(stdout + stderr, tempDir); + + if (result.status === 0) { + console.log(`OK ${inScopeExamples.length} example(s) typechecked clean.`); + return; + } + + // Group errors by source example. + const errorsByIndex = new Map(); + for (const e of errors) { + const m = e.file.match(/example-(\d+)\.(?:ts|js)/); + if (!m) continue; + const idx = Number(m[1]); + const list = errorsByIndex.get(idx) ?? []; + list.push(e); + errorsByIndex.set(idx, list); + } + + console.log(`FAIL ${errorsByIndex.size} example(s) failed typecheck:`); + console.log(''); + for (const [idx, errs] of [...errorsByIndex.entries()].sort((a, b) => a[0] - b[0])) { + const p = prepared[idx]; + console.log(` ${p.example.file}:${p.example.line} (${p.example.section}) [${p.kind}]`); + // Only the `// @ts-check` header is prepended (JS files only); the + // shared placeholders.d.ts is a sibling file. Subtract that header + // count to map tsc-reported lines back to docs source lines. The + // example.line in the docs file is the fence-open line; the snippet + // body starts at example.line + 1. + const headerLines = p.kind === 'js' ? 1 : 0; + for (const e of errs) { + const snippetLine = e.line - headerLines; + const sourceLine = snippetLine > 0 ? p.example.line + snippetLine : p.example.line; + console.log(` L${sourceLine}: ${e.message}`); + } + console.log(''); + } + console.log( + 'Each example was extracted from a "Full Example" code block, given an\n' + + 'ambient prelude for placeholders (`yourFile`, `doc1`, `doc2`), and\n' + + 'typechecked against packages/superdoc/dist via `superdoc` module\n' + + 'resolution. Fix the example so it matches the typed public surface;\n' + + 'if the type itself is wrong, fix the type first.', + ); + exitCode = 1; + } finally { + // Keep the temp dir on failure so devs can poke at the files. + if (exitCode === 0) { + rmSync(tempDir, { recursive: true, force: true }); + } else { + console.log(''); + console.log(`(temp project preserved at ${tempDir} for inspection)`); + } + } + process.exit(exitCode); +} + +function existsLocal(path: string): boolean { + try { + readFileSync(path); + return true; + } catch { + return false; + } +} + +main(); diff --git a/apps/docs/__tests__/lib/extract.ts b/apps/docs/__tests__/lib/extract.ts index 3aa127c6c7..e6216b6555 100644 --- a/apps/docs/__tests__/lib/extract.ts +++ b/apps/docs/__tests__/lib/extract.ts @@ -12,6 +12,12 @@ export interface CodeExample { code: string; pattern: 'superdoc' | 'editor' | 'headless' | 'unknown'; line: number; + /** + * Fence language as written in the .mdx (`javascript`, `typescript`, + * `js`, `ts`, `tsx`, or empty for unfenced). Used by the type-check + * gate to pick `.js + // @ts-check + allowJs` vs `.ts + strict`. + */ + lang: string; } const SKIP_FILE_PATTERNS = [ @@ -39,6 +45,8 @@ const SKIP_IMPORTS = [ 'react-dom', 'vue', '@angular/', + 'yjs', + 'y-websocket', ]; const parser = unified().use(remarkParse).use(remarkMdx); @@ -124,7 +132,7 @@ export function extractExamples(docsRoot: string): CodeExample[] { } } - examples.push({ file: relPath, section, code, pattern, line: codeLine }); + examples.push({ file: relPath, section, code, pattern, line: codeLine, lang: node.lang ?? '' }); }); } diff --git a/apps/docs/docs.json b/apps/docs/docs.json index d67934546d..c81cdc87ee 100644 --- a/apps/docs/docs.json +++ b/apps/docs/docs.json @@ -104,6 +104,7 @@ "editor/custom-ui/custom-commands", "editor/custom-ui/comments", "editor/custom-ui/track-changes", + "editor/custom-ui/content-controls", "editor/custom-ui/context-menu", "editor/custom-ui/selection-and-viewport", "editor/custom-ui/document-control", diff --git a/apps/docs/document-api/available-operations.mdx b/apps/docs/document-api/available-operations.mdx index 61e9ce9491..fc2370f907 100644 --- a/apps/docs/document-api/available-operations.mdx +++ b/apps/docs/document-api/available-operations.mdx @@ -28,7 +28,7 @@ Use the tables below to see what operations are available and where each one is | Diff | 3 | 0 | 3 | [Reference](/document-api/reference/diff/index) | | Fields | 5 | 0 | 5 | [Reference](/document-api/reference/fields/index) | | Footnotes | 6 | 0 | 6 | [Reference](/document-api/reference/footnotes/index) | -| Format | 44 | 1 | 45 | [Reference](/document-api/reference/format/index) | +| Format | 45 | 1 | 46 | [Reference](/document-api/reference/format/index) | | Headers & Footers | 9 | 0 | 9 | [Reference](/document-api/reference/header-footers/index) | | History | 3 | 0 | 3 | [Reference](/document-api/reference/history/index) | | Hyperlinks | 6 | 0 | 6 | [Reference](/document-api/reference/hyperlinks/index) | @@ -192,6 +192,7 @@ Use the tables below to see what operations are available and where each one is | editor.doc.footnotes.update(...) | [`footnotes.update`](/document-api/reference/footnotes/update) | | editor.doc.footnotes.remove(...) | [`footnotes.remove`](/document-api/reference/footnotes/remove) | | editor.doc.footnotes.configure(...) | [`footnotes.configure`](/document-api/reference/footnotes/configure) | +| editor.doc.formatRange(...) | [`formatRange`](/document-api/reference/format/apply) | | editor.doc.format.apply(...) | [`format.apply`](/document-api/reference/format/apply) | | editor.doc.format.bold(...) | [`format.bold`](/document-api/reference/format/bold) | | editor.doc.format.italic(...) | [`format.italic`](/document-api/reference/format/italic) | diff --git a/apps/docs/document-api/migration.mdx b/apps/docs/document-api/migration.mdx index ee609c342f..daaae7b46d 100644 --- a/apps/docs/document-api/migration.mdx +++ b/apps/docs/document-api/migration.mdx @@ -1,7 +1,7 @@ --- title: Migrate to the Document API sidebarTitle: Migrate to Document API -keywords: "migrate commands, document api migration, editor commands deprecated, prosemirror deprecated, editor.doc" +keywords: 'migrate commands, document api migration, editor commands deprecated, prosemirror deprecated, editor.doc' --- `editor.commands`, `editor.state`, `editor.view`, and direct ProseMirror access are deprecated and will be removed in a future version. The [Document API](/document-api/overview) (`editor.doc`) is the replacement for all programmatic document operations. @@ -101,8 +101,8 @@ editor.commands.rejectTrackedChange(changeId); // After editor.doc.trackChanges.list(); -editor.doc.trackChanges.decide({ id: changeId, decision: 'accept' }); -editor.doc.trackChanges.decide({ id: changeId, decision: 'reject' }); +editor.doc.trackChanges.decide({ decision: 'accept', target: { id: changeId } }); +editor.doc.trackChanges.decide({ decision: 'reject', target: { id: changeId } }); ``` ### Tables diff --git a/apps/docs/document-api/reference/_generated-manifest.json b/apps/docs/document-api/reference/_generated-manifest.json index 4efc168b52..73084acd22 100644 --- a/apps/docs/document-api/reference/_generated-manifest.json +++ b/apps/docs/document-api/reference/_generated-manifest.json @@ -524,6 +524,7 @@ "aliasMemberPaths": ["format.strikethrough"], "key": "format", "operationIds": [ + "formatRange", "format.apply", "format.bold", "format.italic", @@ -1077,5 +1078,5 @@ } ], "marker": "{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */}", - "sourceHash": "266b6bb8fa042ebd94c3da6e11652471f49c40a061960f3df9055cf64c4bcbbe" + "sourceHash": "5f439c4117bbb2e55f227e7711df415c1173f8c476954c6e412a4b8b45edd1a3" } diff --git a/apps/docs/document-api/reference/authorities/entries-insert.mdx b/apps/docs/document-api/reference/authorities/entries-insert.mdx index 1549427879..f6379b3631 100644 --- a/apps/docs/document-api/reference/authorities/entries-insert.mdx +++ b/apps/docs/document-api/reference/authorities/entries-insert.mdx @@ -32,7 +32,7 @@ Returns an AuthorityEntryMutationResult indicating success with the entry addres | `at.story` | StoryLocator | no | StoryLocator | | `entry` | object | yes | | | `entry.bold` | boolean | no | | -| `entry.category` | string \\| integer | yes | One of: string, integer | +| `entry.category` | string \| integer | yes | One of: string, integer | | `entry.italic` | boolean | no | | | `entry.longCitation` | string | yes | | | `entry.shortCitation` | string | no | | diff --git a/apps/docs/document-api/reference/authorities/entries-list.mdx b/apps/docs/document-api/reference/authorities/entries-list.mdx index c6580e9f67..ff8ef8163b 100644 --- a/apps/docs/document-api/reference/authorities/entries-list.mdx +++ b/apps/docs/document-api/reference/authorities/entries-list.mdx @@ -26,7 +26,7 @@ Returns an AuthorityEntryListResult containing discovered entries with address a | Field | Type | Required | Description | | --- | --- | --- | --- | -| `category` | string \\| integer | no | One of: string, integer | +| `category` | string \| integer | no | One of: string, integer | | `limit` | integer | no | | | `offset` | integer | no | | diff --git a/apps/docs/document-api/reference/authorities/entries-update.mdx b/apps/docs/document-api/reference/authorities/entries-update.mdx index 1e8ecf1666..9280e15ae9 100644 --- a/apps/docs/document-api/reference/authorities/entries-update.mdx +++ b/apps/docs/document-api/reference/authorities/entries-update.mdx @@ -28,7 +28,7 @@ Returns an AuthorityEntryMutationResult indicating success or a failure. | --- | --- | --- | --- | | `patch` | object | yes | | | `patch.bold` | boolean | no | | -| `patch.category` | string \\| integer | no | One of: string, integer | +| `patch.category` | string \| integer | no | One of: string, integer | | `patch.italic` | boolean | no | | | `patch.longCitation` | string | no | | | `patch.shortCitation` | string | no | | diff --git a/apps/docs/document-api/reference/authorities/insert.mdx b/apps/docs/document-api/reference/authorities/insert.mdx index b24f3e80b3..976551468e 100644 --- a/apps/docs/document-api/reference/authorities/insert.mdx +++ b/apps/docs/document-api/reference/authorities/insert.mdx @@ -26,7 +26,7 @@ Returns an AuthoritiesMutationResult indicating success with the TOA address or | Field | Type | Required | Description | | --- | --- | --- | --- | -| `at` | object(kind="documentStart") \\| object(kind="documentEnd") \\| object(kind="before") \\| object(kind="after") | yes | One of: object(kind="documentStart"), object(kind="documentEnd"), object(kind="before"), object(kind="after") | +| `at` | object(kind="documentStart") \| object(kind="documentEnd") \| object(kind="before") \| object(kind="after") | yes | One of: object(kind="documentStart"), object(kind="documentEnd"), object(kind="before"), object(kind="after") | | `config` | object | no | | | `config.category` | integer | no | | | `config.entryPageSeparator` | string | no | | diff --git a/apps/docs/document-api/reference/blocks/delete.mdx b/apps/docs/document-api/reference/blocks/delete.mdx index 421ced1695..c156286cc3 100644 --- a/apps/docs/document-api/reference/blocks/delete.mdx +++ b/apps/docs/document-api/reference/blocks/delete.mdx @@ -55,7 +55,7 @@ Returns a BlocksDeleteResult receipt confirming the block was removed, including | `deletedBlock.nodeId` | string | no | | | `deletedBlock.nodeType` | string | no | | | `deletedBlock.ordinal` | number | no | | -| `deletedBlock.textPreview` | string \\| null | no | One of: string, null | +| `deletedBlock.textPreview` | string \| null | no | One of: string, null | | `success` | `true` | yes | Constant: `true` | ### Example response diff --git a/apps/docs/document-api/reference/capabilities/get.mdx b/apps/docs/document-api/reference/capabilities/get.mdx index 9746e87c3a..28b5000970 100644 --- a/apps/docs/document-api/reference/capabilities/get.mdx +++ b/apps/docs/document-api/reference/capabilities/get.mdx @@ -1260,6 +1260,11 @@ _No fields._ | `operations.format.webHidden.dryRun` | boolean | yes | | | `operations.format.webHidden.reasons` | enum[] | no | | | `operations.format.webHidden.tracked` | boolean | yes | | +| `operations.formatRange` | object | yes | | +| `operations.formatRange.available` | boolean | yes | | +| `operations.formatRange.dryRun` | boolean | yes | | +| `operations.formatRange.reasons` | enum[] | no | | +| `operations.formatRange.tracked` | boolean | yes | | | `operations.get` | object | yes | | | `operations.get.available` | boolean | yes | | | `operations.get.dryRun` | boolean | yes | | @@ -3566,6 +3571,11 @@ _No fields._ "dryRun": true, "tracked": false }, + "formatRange": { + "available": true, + "dryRun": true, + "tracked": true + }, "get": { "available": true, "dryRun": false, @@ -13194,6 +13204,41 @@ _No fields._ ], "type": "object" }, + "formatRange": { + "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" + }, "get": { "additionalProperties": false, "properties": { @@ -20385,6 +20430,7 @@ _No fields._ "insert", "replace", "delete", + "formatRange", "blocks.list", "blocks.delete", "blocks.deleteRange", diff --git a/apps/docs/document-api/reference/citations/bibliography-insert.mdx b/apps/docs/document-api/reference/citations/bibliography-insert.mdx index 993b5fed34..212d564db9 100644 --- a/apps/docs/document-api/reference/citations/bibliography-insert.mdx +++ b/apps/docs/document-api/reference/citations/bibliography-insert.mdx @@ -26,7 +26,7 @@ Returns a BibliographyMutationResult indicating success with the bibliography ad | Field | Type | Required | Description | | --- | --- | --- | --- | -| `at` | object(kind="documentStart") \\| object(kind="documentEnd") \\| object(kind="before") \\| object(kind="after") | yes | One of: object(kind="documentStart"), object(kind="documentEnd"), object(kind="before"), object(kind="after") | +| `at` | object(kind="documentStart") \| object(kind="documentEnd") \| object(kind="before") \| object(kind="after") | yes | One of: object(kind="documentStart"), object(kind="documentEnd"), object(kind="before"), object(kind="after") | | `style` | string | no | | ### Example request diff --git a/apps/docs/document-api/reference/comments/create.mdx b/apps/docs/document-api/reference/comments/create.mdx index b805fbb0b1..89532d34f1 100644 --- a/apps/docs/document-api/reference/comments/create.mdx +++ b/apps/docs/document-api/reference/comments/create.mdx @@ -20,14 +20,14 @@ Create a new comment thread (or reply when parentCommentId is given). ## Expected result -Returns a Receipt confirming the comment was created; reports NO_OP if the anchor target is invalid. +Returns a Receipt confirming the comment was created, including the new comment id; reports NO_OP if the anchor target is invalid. ## Input fields | Field | Type | Required | Description | | --- | --- | --- | --- | | `parentCommentId` | string | no | | -| `target` | TextAddress \\| TextTarget | no | One of: TextAddress, TextTarget | +| `target` | TextAddress \| TextTarget \| SelectionTarget \| CommentTrackedChangeTarget | no | One of: TextAddress, TextTarget, SelectionTarget, CommentTrackedChangeTarget | | `text` | string | yes | | ### Example request @@ -53,6 +53,7 @@ Returns a Receipt confirming the comment was created; reports NO_OP if the ancho | Field | Type | Required | Description | | --- | --- | --- | --- | +| `id` | string | yes | | | `inserted` | EntityAddress[] | no | | | `removed` | EntityAddress[] | no | | | `success` | `true` | yes | Constant: `true` | @@ -72,6 +73,7 @@ Returns a Receipt confirming the comment was created; reports NO_OP if the ancho ```json { + "id": "id-001", "inserted": [ { "entityId": "entity-789", @@ -113,13 +115,19 @@ Returns a Receipt confirming the comment was created; reports NO_OP if the ancho "type": "string" }, "target": { - "description": "Text range to anchor the comment. Accepts either a single-block TextAddress {kind:'text', blockId, range} or a multi-segment TextTarget {kind:'text', segments:[{blockId, range}, ...]} for selections that span blocks.", + "description": "Comment target. Accepts a TextAddress, TextTarget, SelectionTarget, or {trackedChangeId, kind?:'trackedChange'} to anchor directly on tracked content.", "oneOf": [ { "$ref": "#/$defs/TextAddress" }, { "$ref": "#/$defs/TextTarget" + }, + { + "$ref": "#/$defs/SelectionTarget" + }, + { + "$ref": "#/$defs/CommentTrackedChangeTarget" } ] }, @@ -141,7 +149,7 @@ Returns a Receipt confirming the comment was created; reports NO_OP if the ancho { "oneOf": [ { - "$ref": "#/$defs/ReceiptSuccess" + "$ref": "#/$defs/CommentsCreateSuccess" }, { "additionalProperties": false, @@ -184,7 +192,7 @@ Returns a Receipt confirming the comment was created; reports NO_OP if the ancho ```json { - "$ref": "#/$defs/ReceiptSuccess" + "$ref": "#/$defs/CommentsCreateSuccess" } ``` diff --git a/apps/docs/document-api/reference/comments/get.mdx b/apps/docs/document-api/reference/comments/get.mdx index cdf60532ee..8224e77ea9 100644 --- a/apps/docs/document-api/reference/comments/get.mdx +++ b/apps/docs/document-api/reference/comments/get.mdx @@ -49,6 +49,7 @@ Returns a CommentInfo object with the comment text, author, date, and thread met | `createdTime` | number | no | | | `creatorEmail` | string | no | | | `creatorName` | string | no | | +| `deletedText` | string \| null | no | | | `importedId` | string | no | | | `isInternal` | boolean | no | | | `parentCommentId` | string | no | | @@ -58,6 +59,13 @@ Returns a CommentInfo object with the comment text, author, date, and thread met | `target.segments` | TextSegment[] | no | | | `target.story` | StoryLocator | no | StoryLocator | | `text` | string | no | | +| `trackedChange` | boolean | no | | +| `trackedChangeAnchorKey` | string \| null | no | | +| `trackedChangeDisplayType` | string \| null | no | | +| `trackedChangeLink` | CommentTrackedChangeLink \| null | no | One of: CommentTrackedChangeLink, null | +| `trackedChangeStory` | StoryLocator \| null | no | One of: StoryLocator, null | +| `trackedChangeText` | string \| null | no | | +| `trackedChangeType` | enum | no | `"insert"`, `"delete"`, `"replacement"`, `"format"` | ### Example response @@ -125,6 +133,12 @@ Returns a CommentInfo object with the comment text, author, date, and thread met "creatorName": { "type": "string" }, + "deletedText": { + "type": [ + "string", + "null" + ] + }, "importedId": { "type": "string" }, @@ -145,6 +159,55 @@ Returns a CommentInfo object with the comment text, author, date, and thread met }, "text": { "type": "string" + }, + "trackedChange": { + "type": "boolean" + }, + "trackedChangeAnchorKey": { + "type": [ + "string", + "null" + ] + }, + "trackedChangeDisplayType": { + "type": [ + "string", + "null" + ] + }, + "trackedChangeLink": { + "oneOf": [ + { + "$ref": "#/$defs/CommentTrackedChangeLink" + }, + { + "type": "null" + } + ] + }, + "trackedChangeStory": { + "oneOf": [ + { + "$ref": "#/$defs/StoryLocator" + }, + { + "type": "null" + } + ] + }, + "trackedChangeText": { + "type": [ + "string", + "null" + ] + }, + "trackedChangeType": { + "enum": [ + "insert", + "delete", + "replacement", + "format" + ] } }, "required": [ diff --git a/apps/docs/document-api/reference/comments/list.mdx b/apps/docs/document-api/reference/comments/list.mdx index 96e21e03ed..baf327be4a 100644 --- a/apps/docs/document-api/reference/comments/list.mdx +++ b/apps/docs/document-api/reference/comments/list.mdx @@ -143,6 +143,12 @@ Returns a CommentsListResult with an array of comment threads and total count. "creatorName": { "type": "string" }, + "deletedText": { + "type": [ + "string", + "null" + ] + }, "handle": { "$ref": "#/$defs/ResolvedHandle" }, @@ -169,6 +175,55 @@ Returns a CommentsListResult with an array of comment threads and total count. }, "text": { "type": "string" + }, + "trackedChange": { + "type": "boolean" + }, + "trackedChangeAnchorKey": { + "type": [ + "string", + "null" + ] + }, + "trackedChangeDisplayType": { + "type": [ + "string", + "null" + ] + }, + "trackedChangeLink": { + "oneOf": [ + { + "$ref": "#/$defs/CommentTrackedChangeLink" + }, + { + "type": "null" + } + ] + }, + "trackedChangeStory": { + "oneOf": [ + { + "$ref": "#/$defs/StoryLocator" + }, + { + "type": "null" + } + ] + }, + "trackedChangeText": { + "type": [ + "string", + "null" + ] + }, + "trackedChangeType": { + "enum": [ + "insert", + "delete", + "replacement", + "format" + ] } }, "required": [ diff --git a/apps/docs/document-api/reference/comments/patch.mdx b/apps/docs/document-api/reference/comments/patch.mdx index 60204130ca..375a1cb9de 100644 --- a/apps/docs/document-api/reference/comments/patch.mdx +++ b/apps/docs/document-api/reference/comments/patch.mdx @@ -29,12 +29,7 @@ Returns a Receipt confirming the comment was updated; reports NO_OP if no fields | `commentId` | string | yes | | | `isInternal` | boolean | no | | | `status` | enum | no | `"resolved"`, `"active"` | -| `target` | TextAddress | no | TextAddress | -| `target.blockId` | string | no | | -| `target.kind` | `"text"` | no | Constant: `"text"` | -| `target.range` | Range | no | Range | -| `target.range.end` | integer | no | | -| `target.range.start` | integer | no | | +| `target` | TextAddress \| TextTarget \| SelectionTarget \| CommentTrackedChangeTarget | no | One of: TextAddress, TextTarget, SelectionTarget, CommentTrackedChangeTarget | | `text` | string | no | | ### Example request @@ -131,7 +126,20 @@ Returns a Receipt confirming the comment was updated; reports NO_OP if no fields ] }, "target": { - "$ref": "#/$defs/TextAddress" + "oneOf": [ + { + "$ref": "#/$defs/TextAddress" + }, + { + "$ref": "#/$defs/TextTarget" + }, + { + "$ref": "#/$defs/SelectionTarget" + }, + { + "$ref": "#/$defs/CommentTrackedChangeTarget" + } + ] }, "text": { "description": "Updated comment text.", diff --git a/apps/docs/document-api/reference/create/heading.mdx b/apps/docs/document-api/reference/create/heading.mdx index 06bbbdc051..5f96c10022 100644 --- a/apps/docs/document-api/reference/create/heading.mdx +++ b/apps/docs/document-api/reference/create/heading.mdx @@ -26,7 +26,7 @@ Returns a CreateHeadingResult with the new heading block ID and address. | Field | Type | Required | Description | | --- | --- | --- | --- | -| `at` | object(kind="documentStart") \\| object(kind="documentEnd") \\| object(kind="before") \\| object(kind="after") | no | One of: object(kind="documentStart"), object(kind="documentEnd"), object(kind="before"), object(kind="after") | +| `at` | object(kind="documentStart") \| object(kind="documentEnd") \| object(kind="before") \| object(kind="after") | no | One of: object(kind="documentStart"), object(kind="documentEnd"), object(kind="before"), object(kind="after") | | `in` | StoryLocator | no | StoryLocator | | `level` | integer | yes | | | `text` | string | no | | diff --git a/apps/docs/document-api/reference/create/image.mdx b/apps/docs/document-api/reference/create/image.mdx index 2bfad8561b..058683138b 100644 --- a/apps/docs/document-api/reference/create/image.mdx +++ b/apps/docs/document-api/reference/create/image.mdx @@ -27,7 +27,7 @@ Returns a CreateImageResult with the new image address. | Field | Type | Required | Description | | --- | --- | --- | --- | | `alt` | string | no | | -| `at` | object(kind="documentStart") \\| object(kind="documentEnd") \\| object(kind="before") \\| object(kind="after") \\| object(kind="inParagraph") | no | One of: object(kind="documentStart"), object(kind="documentEnd"), object(kind="before"), object(kind="after"), object(kind="inParagraph") | +| `at` | object(kind="documentStart") \| object(kind="documentEnd") \| object(kind="before") \| object(kind="after") \| object(kind="inParagraph") | no | One of: object(kind="documentStart"), object(kind="documentEnd"), object(kind="before"), object(kind="after"), object(kind="inParagraph") | | `in` | StoryLocator | no | StoryLocator | | `size` | object | no | | | `size.height` | number | no | | diff --git a/apps/docs/document-api/reference/create/paragraph.mdx b/apps/docs/document-api/reference/create/paragraph.mdx index a50a5990f5..4b9cfdbaa6 100644 --- a/apps/docs/document-api/reference/create/paragraph.mdx +++ b/apps/docs/document-api/reference/create/paragraph.mdx @@ -26,7 +26,7 @@ Returns a CreateParagraphResult with the new paragraph block ID and address. | Field | Type | Required | Description | | --- | --- | --- | --- | -| `at` | object(kind="documentStart") \\| object(kind="documentEnd") \\| object(kind="before") \\| object(kind="after") | no | One of: object(kind="documentStart"), object(kind="documentEnd"), object(kind="before"), object(kind="after") | +| `at` | object(kind="documentStart") \| object(kind="documentEnd") \| object(kind="before") \| object(kind="after") | no | One of: object(kind="documentStart"), object(kind="documentEnd"), object(kind="before"), object(kind="after") | | `in` | StoryLocator | no | StoryLocator | | `text` | string | no | | diff --git a/apps/docs/document-api/reference/create/section-break.mdx b/apps/docs/document-api/reference/create/section-break.mdx index ecf3a7911b..3e0ea33a32 100644 --- a/apps/docs/document-api/reference/create/section-break.mdx +++ b/apps/docs/document-api/reference/create/section-break.mdx @@ -26,7 +26,7 @@ Returns a CreateSectionBreakResult with the new section break position and secti | Field | Type | Required | Description | | --- | --- | --- | --- | -| `at` | object(kind="documentStart") \\| object(kind="documentEnd") \\| object(kind="before") \\| object(kind="after") | no | One of: object(kind="documentStart"), object(kind="documentEnd"), object(kind="before"), object(kind="after") | +| `at` | object(kind="documentStart") \| object(kind="documentEnd") \| object(kind="before") \| object(kind="after") | no | One of: object(kind="documentStart"), object(kind="documentEnd"), object(kind="before"), object(kind="after") | | `breakType` | enum | no | `"continuous"`, `"nextPage"`, `"evenPage"`, `"oddPage"` | | `headerFooterMargins` | object | no | | | `headerFooterMargins.footer` | number | no | | diff --git a/apps/docs/document-api/reference/create/table-of-contents.mdx b/apps/docs/document-api/reference/create/table-of-contents.mdx index d6d19b7787..3eeaf62bf7 100644 --- a/apps/docs/document-api/reference/create/table-of-contents.mdx +++ b/apps/docs/document-api/reference/create/table-of-contents.mdx @@ -26,7 +26,7 @@ Returns a CreateTableOfContentsResult with the new TOC block address. | Field | Type | Required | Description | | --- | --- | --- | --- | -| `at` | object(kind="documentStart") \\| object(kind="documentEnd") \\| object(kind="before") \\| object(kind="after") | no | One of: object(kind="documentStart"), object(kind="documentEnd"), object(kind="before"), object(kind="after") | +| `at` | object(kind="documentStart") \| object(kind="documentEnd") \| object(kind="before") \| object(kind="after") | no | One of: object(kind="documentStart"), object(kind="documentEnd"), object(kind="before"), object(kind="after") | | `config` | object | no | | | `config.hideInWebView` | boolean | no | | | `config.hyperlinks` | boolean | no | | diff --git a/apps/docs/document-api/reference/create/table.mdx b/apps/docs/document-api/reference/create/table.mdx index 95baf6d48f..082ca428f9 100644 --- a/apps/docs/document-api/reference/create/table.mdx +++ b/apps/docs/document-api/reference/create/table.mdx @@ -26,7 +26,7 @@ Returns a CreateTableResult with the new table block ID and address. | Field | Type | Required | Description | | --- | --- | --- | --- | -| `at` | object(kind="documentStart") \\| object(kind="documentEnd") \\| object(kind="before") \\| object(kind="after") \\| object(kind="before") \\| object(kind="after") | no | One of: object(kind="documentStart"), object(kind="documentEnd"), object(kind="before"), object(kind="after"), object(kind="before"), object(kind="after") | +| `at` | object(kind="documentStart") \| object(kind="documentEnd") \| object(kind="before") \| object(kind="after") \| object(kind="before") \| object(kind="after") | no | One of: object(kind="documentStart"), object(kind="documentEnd"), object(kind="before"), object(kind="after"), object(kind="before"), object(kind="after") | | `columns` | integer | yes | | | `rows` | integer | yes | | diff --git a/apps/docs/document-api/reference/cross-refs/insert.mdx b/apps/docs/document-api/reference/cross-refs/insert.mdx index 04e9cdc54e..13cc8a301e 100644 --- a/apps/docs/document-api/reference/cross-refs/insert.mdx +++ b/apps/docs/document-api/reference/cross-refs/insert.mdx @@ -31,7 +31,7 @@ Returns a CrossRefMutationResult indicating success with the cross-reference add | `at.segments` | TextSegment[] | yes | | | `at.story` | StoryLocator | no | StoryLocator | | `display` | enum | yes | `"content"`, `"pageNumber"`, `"noteNumber"`, `"labelAndNumber"`, `"aboveBelow"`, `"numberOnly"`, `"numberFullContext"`, `"styledContent"`, `"styledPageNumber"` | -| `target` | object(kind="bookmark") \\| object(kind="heading") \\| object(kind="note") \\| object(kind="caption") \\| object(kind="numberedItem") \\| object(kind="styledParagraph") | yes | One of: object(kind="bookmark"), object(kind="heading"), object(kind="note"), object(kind="caption"), object(kind="numberedItem"), object(kind="styledParagraph") | +| `target` | object(kind="bookmark") \| object(kind="heading") \| object(kind="note") \| object(kind="caption") \| object(kind="numberedItem") \| object(kind="styledParagraph") | yes | One of: object(kind="bookmark"), object(kind="heading"), object(kind="note"), object(kind="caption"), object(kind="numberedItem"), object(kind="styledParagraph") | ### Example request diff --git a/apps/docs/document-api/reference/custom-xml/parts/get.mdx b/apps/docs/document-api/reference/custom-xml/parts/get.mdx index 8f47051e2a..a7fc0404be 100644 --- a/apps/docs/document-api/reference/custom-xml/parts/get.mdx +++ b/apps/docs/document-api/reference/custom-xml/parts/get.mdx @@ -26,7 +26,7 @@ Returns a CustomXmlPartInfo with id, partName, namespaces, schemaRefs, and conte | Field | Type | Required | Description | | --- | --- | --- | --- | -| `target` | object \\| object | yes | One of: object, object | +| `target` | object \| object | yes | One of: object, object | ### Example request diff --git a/apps/docs/document-api/reference/custom-xml/parts/patch.mdx b/apps/docs/document-api/reference/custom-xml/parts/patch.mdx index 8fdde7ce28..ac9bd1635e 100644 --- a/apps/docs/document-api/reference/custom-xml/parts/patch.mdx +++ b/apps/docs/document-api/reference/custom-xml/parts/patch.mdx @@ -29,14 +29,14 @@ Returns a CustomXmlPartsMutationResult indicating success with the resolved targ | Field | Type | Required | Description | | --- | --- | --- | --- | | `content` | string | yes | | -| `target` | object \\| object | yes | One of: object, object | +| `target` | object \| object | yes | One of: object, object | ### Variant 2 (required: schemaRefs) | Field | Type | Required | Description | | --- | --- | --- | --- | | `schemaRefs` | string[] | yes | | -| `target` | object \\| object | yes | One of: object, object | +| `target` | object \| object | yes | One of: object, object | ### Example request @@ -57,7 +57,7 @@ Returns a CustomXmlPartsMutationResult indicating success with the resolved targ | --- | --- | --- | --- | | `id` | string | no | | | `success` | `true` | yes | Constant: `true` | -| `target` | object \\| object | yes | One of: object, object | +| `target` | object \| object | yes | One of: object, object | ### Variant 2 (success=false) diff --git a/apps/docs/document-api/reference/custom-xml/parts/remove.mdx b/apps/docs/document-api/reference/custom-xml/parts/remove.mdx index 1b723f4ce8..2c19eedd2d 100644 --- a/apps/docs/document-api/reference/custom-xml/parts/remove.mdx +++ b/apps/docs/document-api/reference/custom-xml/parts/remove.mdx @@ -26,7 +26,7 @@ Returns a CustomXmlPartsMutationResult indicating success or a failure. | Field | Type | Required | Description | | --- | --- | --- | --- | -| `target` | object \\| object | yes | One of: object, object | +| `target` | object \| object | yes | One of: object, object | ### Example request @@ -46,7 +46,7 @@ Returns a CustomXmlPartsMutationResult indicating success or a failure. | --- | --- | --- | --- | | `id` | string | no | | | `success` | `true` | yes | Constant: `true` | -| `target` | object \\| object | yes | One of: object, object | +| `target` | object \| object | yes | One of: object, object | ### Variant 2 (success=false) diff --git a/apps/docs/document-api/reference/extract.mdx b/apps/docs/document-api/reference/extract.mdx index 00057d1914..1f39be85d6 100644 --- a/apps/docs/document-api/reference/extract.mdx +++ b/apps/docs/document-api/reference/extract.mdx @@ -304,10 +304,11 @@ _No fields._ "type": "string" }, "type": { - "description": "Aggregate type at the entity level. In paired replacement mode, a delete+insert pair shares one entity and this collapses to 'insert'; per-half type lives on block.textSpans[].trackedChanges[].", + "description": "Entity-level type. In paired replacement mode, a delete+insert pair shares one entity with type 'replacement'; per-half type lives on block.textSpans[].trackedChanges[].", "enum": [ "insert", "delete", + "replacement", "format" ], "type": "string" diff --git a/apps/docs/document-api/reference/find.mdx b/apps/docs/document-api/reference/find.mdx index 4b939f036b..733f2fafa8 100644 --- a/apps/docs/document-api/reference/find.mdx +++ b/apps/docs/document-api/reference/find.mdx @@ -33,7 +33,7 @@ Returns an SDFindResult envelope (\{ total, limit, offset, items \}). Each item | `options.includeContext` | boolean | no | | | `options.includeProvenance` | boolean | no | | | `options.includeResolved` | boolean | no | | -| `select` | object(type="text") \\| object(type="node") | yes | One of: object(type="text"), object(type="node") | +| `select` | object(type="text") \| object(type="node") | yes | One of: object(type="text"), object(type="node") | | `within` | BlockNodeAddress | no | BlockNodeAddress | | `within.kind` | `"block"` | no | Constant: `"block"` | | `within.nodeId` | string | no | | diff --git a/apps/docs/document-api/reference/footnotes/configure.mdx b/apps/docs/document-api/reference/footnotes/configure.mdx index 55698c5647..8bea5c1875 100644 --- a/apps/docs/document-api/reference/footnotes/configure.mdx +++ b/apps/docs/document-api/reference/footnotes/configure.mdx @@ -31,7 +31,7 @@ Returns a FootnoteConfigResult indicating success or a failure. | `numbering.position` | enum | no | `"pageBottom"`, `"beneathText"`, `"sectionEnd"`, `"documentEnd"` | | `numbering.restartPolicy` | enum | no | `"continuous"`, `"eachSection"`, `"eachPage"` | | `numbering.start` | integer | no | | -| `scope` | object(kind="document") \\| object(kind="section") | yes | One of: object(kind="document"), object(kind="section") | +| `scope` | object(kind="document") \| object(kind="section") | yes | One of: object(kind="document"), object(kind="section") | | `type` | enum | yes | `"footnote"`, `"endnote"` | ### Example request diff --git a/apps/docs/document-api/reference/format/apply.mdx b/apps/docs/document-api/reference/format/apply.mdx index 465ba0192b..661b004e93 100644 --- a/apps/docs/document-api/reference/format/apply.mdx +++ b/apps/docs/document-api/reference/format/apply.mdx @@ -30,49 +30,49 @@ Returns a TextMutationReceipt confirming inline styles were applied to the targe | --- | --- | --- | --- | | `in` | StoryLocator | no | StoryLocator | | `inline` | object | yes | | -| `inline.bCs` | boolean \\| null | no | One of: boolean, null | -| `inline.bold` | boolean \\| null | no | One of: boolean, null | -| `inline.border` | object \\| null | no | One of: object, null | -| `inline.caps` | boolean \\| null | no | One of: boolean, null | -| `inline.charScale` | number \\| null | no | One of: number, null | -| `inline.color` | string \\| null | no | One of: string, null | -| `inline.contextualAlternates` | boolean \\| null | no | One of: boolean, null | -| `inline.cs` | boolean \\| null | no | One of: boolean, null | -| `inline.dstrike` | boolean \\| null | no | One of: boolean, null | -| `inline.eastAsianLayout` | object \\| null | no | One of: object, null | -| `inline.em` | string \\| null | no | One of: string, null | -| `inline.emboss` | boolean \\| null | no | One of: boolean, null | -| `inline.fitText` | object \\| null | no | One of: object, null | -| `inline.fontFamily` | string \\| null | no | One of: string, null | -| `inline.fontSize` | number \\| null | no | One of: number, null | -| `inline.fontSizeCs` | number \\| null | no | One of: number, null | -| `inline.highlight` | string \\| null | no | One of: string, null | -| `inline.iCs` | boolean \\| null | no | One of: boolean, null | -| `inline.imprint` | boolean \\| null | no | One of: boolean, null | -| `inline.italic` | boolean \\| null | no | One of: boolean, null | -| `inline.kerning` | number \\| null | no | One of: number, null | -| `inline.lang` | object \\| null | no | One of: object, null | -| `inline.letterSpacing` | number \\| null | no | One of: number, null | -| `inline.ligatures` | string \\| null | no | One of: string, null | -| `inline.numForm` | string \\| null | no | One of: string, null | -| `inline.numSpacing` | string \\| null | no | One of: string, null | -| `inline.oMath` | boolean \\| null | no | One of: boolean, null | -| `inline.outline` | boolean \\| null | no | One of: boolean, null | -| `inline.position` | number \\| null | no | One of: number, null | -| `inline.rFonts` | object \\| null | no | One of: object, null | -| `inline.rStyle` | string \\| null | no | One of: string, null | -| `inline.rtl` | boolean \\| null | no | One of: boolean, null | -| `inline.shading` | object \\| null | no | One of: object, null | -| `inline.shadow` | boolean \\| null | no | One of: boolean, null | -| `inline.smallCaps` | boolean \\| null | no | One of: boolean, null | -| `inline.snapToGrid` | boolean \\| null | no | One of: boolean, null | -| `inline.specVanish` | boolean \\| null | no | One of: boolean, null | -| `inline.strike` | boolean \\| null | no | One of: boolean, null | -| `inline.stylisticSets` | object[] \\| null | no | One of: object[], null | -| `inline.underline` | boolean \\| null \\| object | no | One of: boolean, null, object | -| `inline.vanish` | boolean \\| null | no | One of: boolean, null | -| `inline.vertAlign` | enum \\| null | no | One of: enum, null | -| `inline.webHidden` | boolean \\| null | no | One of: boolean, null | +| `inline.bCs` | boolean \| null | no | One of: boolean, null | +| `inline.bold` | boolean \| null | no | One of: boolean, null | +| `inline.border` | object \| null | no | One of: object, null | +| `inline.caps` | boolean \| null | no | One of: boolean, null | +| `inline.charScale` | number \| null | no | One of: number, null | +| `inline.color` | string \| null | no | One of: string, null | +| `inline.contextualAlternates` | boolean \| null | no | One of: boolean, null | +| `inline.cs` | boolean \| null | no | One of: boolean, null | +| `inline.dstrike` | boolean \| null | no | One of: boolean, null | +| `inline.eastAsianLayout` | object \| null | no | One of: object, null | +| `inline.em` | string \| null | no | One of: string, null | +| `inline.emboss` | boolean \| null | no | One of: boolean, null | +| `inline.fitText` | object \| null | no | One of: object, null | +| `inline.fontFamily` | string \| null | no | One of: string, null | +| `inline.fontSize` | number \| null | no | One of: number, null | +| `inline.fontSizeCs` | number \| null | no | One of: number, null | +| `inline.highlight` | string \| null | no | One of: string, null | +| `inline.iCs` | boolean \| null | no | One of: boolean, null | +| `inline.imprint` | boolean \| null | no | One of: boolean, null | +| `inline.italic` | boolean \| null | no | One of: boolean, null | +| `inline.kerning` | number \| null | no | One of: number, null | +| `inline.lang` | object \| null | no | One of: object, null | +| `inline.letterSpacing` | number \| null | no | One of: number, null | +| `inline.ligatures` | string \| null | no | One of: string, null | +| `inline.numForm` | string \| null | no | One of: string, null | +| `inline.numSpacing` | string \| null | no | One of: string, null | +| `inline.oMath` | boolean \| null | no | One of: boolean, null | +| `inline.outline` | boolean \| null | no | One of: boolean, null | +| `inline.position` | number \| null | no | One of: number, null | +| `inline.rFonts` | object \| null | no | One of: object, null | +| `inline.rStyle` | string \| null | no | One of: string, null | +| `inline.rtl` | boolean \| null | no | One of: boolean, null | +| `inline.shading` | object \| null | no | One of: object, null | +| `inline.shadow` | boolean \| null | no | One of: boolean, null | +| `inline.smallCaps` | boolean \| null | no | One of: boolean, null | +| `inline.snapToGrid` | boolean \| null | no | One of: boolean, null | +| `inline.specVanish` | boolean \| null | no | One of: boolean, null | +| `inline.strike` | boolean \| null | no | One of: boolean, null | +| `inline.stylisticSets` | object[] \| null | no | One of: object[], null | +| `inline.underline` | boolean \| null \| object | no | One of: boolean, null, object | +| `inline.vanish` | boolean \| null | no | One of: boolean, null | +| `inline.vertAlign` | enum \| null | no | One of: enum, null | +| `inline.webHidden` | boolean \| null | no | One of: boolean, null | | `target` | SelectionTarget | yes | SelectionTarget | | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | @@ -84,49 +84,49 @@ Returns a TextMutationReceipt confirming inline styles were applied to the targe | --- | --- | --- | --- | | `in` | StoryLocator | no | StoryLocator | | `inline` | object | yes | | -| `inline.bCs` | boolean \\| null | no | One of: boolean, null | -| `inline.bold` | boolean \\| null | no | One of: boolean, null | -| `inline.border` | object \\| null | no | One of: object, null | -| `inline.caps` | boolean \\| null | no | One of: boolean, null | -| `inline.charScale` | number \\| null | no | One of: number, null | -| `inline.color` | string \\| null | no | One of: string, null | -| `inline.contextualAlternates` | boolean \\| null | no | One of: boolean, null | -| `inline.cs` | boolean \\| null | no | One of: boolean, null | -| `inline.dstrike` | boolean \\| null | no | One of: boolean, null | -| `inline.eastAsianLayout` | object \\| null | no | One of: object, null | -| `inline.em` | string \\| null | no | One of: string, null | -| `inline.emboss` | boolean \\| null | no | One of: boolean, null | -| `inline.fitText` | object \\| null | no | One of: object, null | -| `inline.fontFamily` | string \\| null | no | One of: string, null | -| `inline.fontSize` | number \\| null | no | One of: number, null | -| `inline.fontSizeCs` | number \\| null | no | One of: number, null | -| `inline.highlight` | string \\| null | no | One of: string, null | -| `inline.iCs` | boolean \\| null | no | One of: boolean, null | -| `inline.imprint` | boolean \\| null | no | One of: boolean, null | -| `inline.italic` | boolean \\| null | no | One of: boolean, null | -| `inline.kerning` | number \\| null | no | One of: number, null | -| `inline.lang` | object \\| null | no | One of: object, null | -| `inline.letterSpacing` | number \\| null | no | One of: number, null | -| `inline.ligatures` | string \\| null | no | One of: string, null | -| `inline.numForm` | string \\| null | no | One of: string, null | -| `inline.numSpacing` | string \\| null | no | One of: string, null | -| `inline.oMath` | boolean \\| null | no | One of: boolean, null | -| `inline.outline` | boolean \\| null | no | One of: boolean, null | -| `inline.position` | number \\| null | no | One of: number, null | -| `inline.rFonts` | object \\| null | no | One of: object, null | -| `inline.rStyle` | string \\| null | no | One of: string, null | -| `inline.rtl` | boolean \\| null | no | One of: boolean, null | -| `inline.shading` | object \\| null | no | One of: object, null | -| `inline.shadow` | boolean \\| null | no | One of: boolean, null | -| `inline.smallCaps` | boolean \\| null | no | One of: boolean, null | -| `inline.snapToGrid` | boolean \\| null | no | One of: boolean, null | -| `inline.specVanish` | boolean \\| null | no | One of: boolean, null | -| `inline.strike` | boolean \\| null | no | One of: boolean, null | -| `inline.stylisticSets` | object[] \\| null | no | One of: object[], null | -| `inline.underline` | boolean \\| null \\| object | no | One of: boolean, null, object | -| `inline.vanish` | boolean \\| null | no | One of: boolean, null | -| `inline.vertAlign` | enum \\| null | no | One of: enum, null | -| `inline.webHidden` | boolean \\| null | no | One of: boolean, null | +| `inline.bCs` | boolean \| null | no | One of: boolean, null | +| `inline.bold` | boolean \| null | no | One of: boolean, null | +| `inline.border` | object \| null | no | One of: object, null | +| `inline.caps` | boolean \| null | no | One of: boolean, null | +| `inline.charScale` | number \| null | no | One of: number, null | +| `inline.color` | string \| null | no | One of: string, null | +| `inline.contextualAlternates` | boolean \| null | no | One of: boolean, null | +| `inline.cs` | boolean \| null | no | One of: boolean, null | +| `inline.dstrike` | boolean \| null | no | One of: boolean, null | +| `inline.eastAsianLayout` | object \| null | no | One of: object, null | +| `inline.em` | string \| null | no | One of: string, null | +| `inline.emboss` | boolean \| null | no | One of: boolean, null | +| `inline.fitText` | object \| null | no | One of: object, null | +| `inline.fontFamily` | string \| null | no | One of: string, null | +| `inline.fontSize` | number \| null | no | One of: number, null | +| `inline.fontSizeCs` | number \| null | no | One of: number, null | +| `inline.highlight` | string \| null | no | One of: string, null | +| `inline.iCs` | boolean \| null | no | One of: boolean, null | +| `inline.imprint` | boolean \| null | no | One of: boolean, null | +| `inline.italic` | boolean \| null | no | One of: boolean, null | +| `inline.kerning` | number \| null | no | One of: number, null | +| `inline.lang` | object \| null | no | One of: object, null | +| `inline.letterSpacing` | number \| null | no | One of: number, null | +| `inline.ligatures` | string \| null | no | One of: string, null | +| `inline.numForm` | string \| null | no | One of: string, null | +| `inline.numSpacing` | string \| null | no | One of: string, null | +| `inline.oMath` | boolean \| null | no | One of: boolean, null | +| `inline.outline` | boolean \| null | no | One of: boolean, null | +| `inline.position` | number \| null | no | One of: number, null | +| `inline.rFonts` | object \| null | no | One of: object, null | +| `inline.rStyle` | string \| null | no | One of: string, null | +| `inline.rtl` | boolean \| null | no | One of: boolean, null | +| `inline.shading` | object \| null | no | One of: object, null | +| `inline.shadow` | boolean \| null | no | One of: boolean, null | +| `inline.smallCaps` | boolean \| null | no | One of: boolean, null | +| `inline.snapToGrid` | boolean \| null | no | One of: boolean, null | +| `inline.specVanish` | boolean \| null | no | One of: boolean, null | +| `inline.strike` | boolean \| null | no | One of: boolean, null | +| `inline.stylisticSets` | object[] \| null | no | One of: object[], null | +| `inline.underline` | boolean \| null \| object | no | One of: boolean, null, object | +| `inline.vanish` | boolean \| null | no | One of: boolean, null | +| `inline.vertAlign` | enum \| null | no | One of: enum, null | +| `inline.webHidden` | boolean \| null | no | One of: boolean, null | | `ref` | string | yes | | ### Example request @@ -194,7 +194,7 @@ Returns a TextMutationReceipt confirming inline styles were applied to the targe | Field | Type | Required | Description | | --- | --- | --- | --- | | `failure` | object | yes | | -| `failure.code` | enum | yes | `"INVALID_TARGET"` | +| `failure.code` | enum | yes | `"INVALID_TARGET"`, `"NO_OP"` | | `failure.details` | any | no | | | `failure.message` | string | yes | | | `resolution` | TextMutationResolution | yes | TextMutationResolution | @@ -293,6 +293,7 @@ Returns a TextMutationReceipt confirming inline styles were applied to the targe ## Non-applied failure codes - `INVALID_TARGET` +- `NO_OP` ## Raw schemas @@ -1974,7 +1975,8 @@ Returns a TextMutationReceipt confirming inline styles were applied to the targe "properties": { "code": { "enum": [ - "INVALID_TARGET" + "INVALID_TARGET", + "NO_OP" ] }, "details": {}, @@ -2025,7 +2027,8 @@ Returns a TextMutationReceipt confirming inline styles were applied to the targe "properties": { "code": { "enum": [ - "INVALID_TARGET" + "INVALID_TARGET", + "NO_OP" ] }, "details": {}, diff --git a/apps/docs/document-api/reference/format/b-cs.mdx b/apps/docs/document-api/reference/format/b-cs.mdx index 525a817907..683e9e4671 100644 --- a/apps/docs/document-api/reference/format/b-cs.mdx +++ b/apps/docs/document-api/reference/format/b-cs.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Variant 2 (required: ref) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Example request diff --git a/apps/docs/document-api/reference/format/bold.mdx b/apps/docs/document-api/reference/format/bold.mdx index 68233ad335..e0d085546b 100644 --- a/apps/docs/document-api/reference/format/bold.mdx +++ b/apps/docs/document-api/reference/format/bold.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Variant 2 (required: ref) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Example request diff --git a/apps/docs/document-api/reference/format/border.mdx b/apps/docs/document-api/reference/format/border.mdx index 6aed19ffee..5b6cd31fad 100644 --- a/apps/docs/document-api/reference/format/border.mdx +++ b/apps/docs/document-api/reference/format/border.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | object \\| null | yes | One of: object, null | +| `value` | object \| null | yes | One of: object, null | ### Variant 2 (required: ref, value) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | object \\| null | yes | One of: object, null | +| `value` | object \| null | yes | One of: object, null | ### Example request diff --git a/apps/docs/document-api/reference/format/caps.mdx b/apps/docs/document-api/reference/format/caps.mdx index 7270336de5..390e8db00b 100644 --- a/apps/docs/document-api/reference/format/caps.mdx +++ b/apps/docs/document-api/reference/format/caps.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Variant 2 (required: ref) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Example request diff --git a/apps/docs/document-api/reference/format/char-scale.mdx b/apps/docs/document-api/reference/format/char-scale.mdx index 1b3a2f2f27..54be8eb00f 100644 --- a/apps/docs/document-api/reference/format/char-scale.mdx +++ b/apps/docs/document-api/reference/format/char-scale.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | number \\| null | yes | One of: number, null | +| `value` | number \| null | yes | One of: number, null | ### Variant 2 (required: ref, value) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | number \\| null | yes | One of: number, null | +| `value` | number \| null | yes | One of: number, null | ### Example request diff --git a/apps/docs/document-api/reference/format/color.mdx b/apps/docs/document-api/reference/format/color.mdx index 08fb49df38..5a4d95c476 100644 --- a/apps/docs/document-api/reference/format/color.mdx +++ b/apps/docs/document-api/reference/format/color.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | string \\| null | yes | One of: string, null | +| `value` | string \| null | yes | One of: string, null | ### Variant 2 (required: ref, value) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | string \\| null | yes | One of: string, null | +| `value` | string \| null | yes | One of: string, null | ### Example request diff --git a/apps/docs/document-api/reference/format/contextual-alternates.mdx b/apps/docs/document-api/reference/format/contextual-alternates.mdx index 40e99851f0..3bad3cb710 100644 --- a/apps/docs/document-api/reference/format/contextual-alternates.mdx +++ b/apps/docs/document-api/reference/format/contextual-alternates.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Variant 2 (required: ref) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Example request diff --git a/apps/docs/document-api/reference/format/cs.mdx b/apps/docs/document-api/reference/format/cs.mdx index 47c7b40bc2..dea5d2a9fe 100644 --- a/apps/docs/document-api/reference/format/cs.mdx +++ b/apps/docs/document-api/reference/format/cs.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Variant 2 (required: ref) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Example request diff --git a/apps/docs/document-api/reference/format/dstrike.mdx b/apps/docs/document-api/reference/format/dstrike.mdx index 75f7e578ba..4c1e106f08 100644 --- a/apps/docs/document-api/reference/format/dstrike.mdx +++ b/apps/docs/document-api/reference/format/dstrike.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Variant 2 (required: ref) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Example request diff --git a/apps/docs/document-api/reference/format/east-asian-layout.mdx b/apps/docs/document-api/reference/format/east-asian-layout.mdx index 3272975ada..d8f4192336 100644 --- a/apps/docs/document-api/reference/format/east-asian-layout.mdx +++ b/apps/docs/document-api/reference/format/east-asian-layout.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | object \\| null | yes | One of: object, null | +| `value` | object \| null | yes | One of: object, null | ### Variant 2 (required: ref, value) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | object \\| null | yes | One of: object, null | +| `value` | object \| null | yes | One of: object, null | ### Example request diff --git a/apps/docs/document-api/reference/format/em.mdx b/apps/docs/document-api/reference/format/em.mdx index 2bff8f3293..6fd59e41d0 100644 --- a/apps/docs/document-api/reference/format/em.mdx +++ b/apps/docs/document-api/reference/format/em.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | string \\| null | yes | One of: string, null | +| `value` | string \| null | yes | One of: string, null | ### Variant 2 (required: ref, value) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | string \\| null | yes | One of: string, null | +| `value` | string \| null | yes | One of: string, null | ### Example request diff --git a/apps/docs/document-api/reference/format/emboss.mdx b/apps/docs/document-api/reference/format/emboss.mdx index 5d06da187f..462ba225e8 100644 --- a/apps/docs/document-api/reference/format/emboss.mdx +++ b/apps/docs/document-api/reference/format/emboss.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Variant 2 (required: ref) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Example request diff --git a/apps/docs/document-api/reference/format/fit-text.mdx b/apps/docs/document-api/reference/format/fit-text.mdx index 51e1ba6483..4e055b7f8d 100644 --- a/apps/docs/document-api/reference/format/fit-text.mdx +++ b/apps/docs/document-api/reference/format/fit-text.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | object \\| null | yes | One of: object, null | +| `value` | object \| null | yes | One of: object, null | ### Variant 2 (required: ref, value) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | object \\| null | yes | One of: object, null | +| `value` | object \| null | yes | One of: object, null | ### Example request diff --git a/apps/docs/document-api/reference/format/font-family.mdx b/apps/docs/document-api/reference/format/font-family.mdx index b890b0a9b1..feec7d0695 100644 --- a/apps/docs/document-api/reference/format/font-family.mdx +++ b/apps/docs/document-api/reference/format/font-family.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | string \\| null | yes | One of: string, null | +| `value` | string \| null | yes | One of: string, null | ### Variant 2 (required: ref, value) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | string \\| null | yes | One of: string, null | +| `value` | string \| null | yes | One of: string, null | ### Example request diff --git a/apps/docs/document-api/reference/format/font-size-cs.mdx b/apps/docs/document-api/reference/format/font-size-cs.mdx index 63acc1a1df..43a79b9063 100644 --- a/apps/docs/document-api/reference/format/font-size-cs.mdx +++ b/apps/docs/document-api/reference/format/font-size-cs.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | number \\| null | yes | One of: number, null | +| `value` | number \| null | yes | One of: number, null | ### Variant 2 (required: ref, value) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | number \\| null | yes | One of: number, null | +| `value` | number \| null | yes | One of: number, null | ### Example request diff --git a/apps/docs/document-api/reference/format/font-size.mdx b/apps/docs/document-api/reference/format/font-size.mdx index be9d5c09c5..4e2811ffaf 100644 --- a/apps/docs/document-api/reference/format/font-size.mdx +++ b/apps/docs/document-api/reference/format/font-size.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | number \\| null | yes | One of: number, null | +| `value` | number \| null | yes | One of: number, null | ### Variant 2 (required: ref, value) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | number \\| null | yes | One of: number, null | +| `value` | number \| null | yes | One of: number, null | ### Example request diff --git a/apps/docs/document-api/reference/format/highlight.mdx b/apps/docs/document-api/reference/format/highlight.mdx index d4d29f4a46..49617fe9ec 100644 --- a/apps/docs/document-api/reference/format/highlight.mdx +++ b/apps/docs/document-api/reference/format/highlight.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | string \\| null | yes | One of: string, null | +| `value` | string \| null | yes | One of: string, null | ### Variant 2 (required: ref, value) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | string \\| null | yes | One of: string, null | +| `value` | string \| null | yes | One of: string, null | ### Example request diff --git a/apps/docs/document-api/reference/format/i-cs.mdx b/apps/docs/document-api/reference/format/i-cs.mdx index 46323eab3c..ef200d3015 100644 --- a/apps/docs/document-api/reference/format/i-cs.mdx +++ b/apps/docs/document-api/reference/format/i-cs.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Variant 2 (required: ref) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Example request diff --git a/apps/docs/document-api/reference/format/imprint.mdx b/apps/docs/document-api/reference/format/imprint.mdx index 368ee601e2..154312de2b 100644 --- a/apps/docs/document-api/reference/format/imprint.mdx +++ b/apps/docs/document-api/reference/format/imprint.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Variant 2 (required: ref) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Example request diff --git a/apps/docs/document-api/reference/format/index.mdx b/apps/docs/document-api/reference/format/index.mdx index df4abd5fde..feefdc85a8 100644 --- a/apps/docs/document-api/reference/format/index.mdx +++ b/apps/docs/document-api/reference/format/index.mdx @@ -12,6 +12,7 @@ Canonical formatting mutation with directive semantics ('on', 'off', 'clear'). | Operation | Member path | Mutates | Idempotency | Tracked | Dry run | | --- | --- | --- | --- | --- | --- | +| formatRange | `formatRange` | Yes | `conditional` | Yes | Yes | | format.apply | `format.apply` | Yes | `conditional` | Yes | Yes | | format.bold | `format.bold` | Yes | `conditional` | Yes | Yes | | format.italic | `format.italic` | Yes | `conditional` | Yes | Yes | diff --git a/apps/docs/document-api/reference/format/italic.mdx b/apps/docs/document-api/reference/format/italic.mdx index 8006aa0900..c84e9a14ed 100644 --- a/apps/docs/document-api/reference/format/italic.mdx +++ b/apps/docs/document-api/reference/format/italic.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Variant 2 (required: ref) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Example request diff --git a/apps/docs/document-api/reference/format/kerning.mdx b/apps/docs/document-api/reference/format/kerning.mdx index 256c210660..727bd0b09f 100644 --- a/apps/docs/document-api/reference/format/kerning.mdx +++ b/apps/docs/document-api/reference/format/kerning.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | number \\| null | yes | One of: number, null | +| `value` | number \| null | yes | One of: number, null | ### Variant 2 (required: ref, value) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | number \\| null | yes | One of: number, null | +| `value` | number \| null | yes | One of: number, null | ### Example request diff --git a/apps/docs/document-api/reference/format/lang.mdx b/apps/docs/document-api/reference/format/lang.mdx index b8fb75db95..b500fcccc8 100644 --- a/apps/docs/document-api/reference/format/lang.mdx +++ b/apps/docs/document-api/reference/format/lang.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | object \\| null | yes | One of: object, null | +| `value` | object \| null | yes | One of: object, null | ### Variant 2 (required: ref, value) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | object \\| null | yes | One of: object, null | +| `value` | object \| null | yes | One of: object, null | ### Example request diff --git a/apps/docs/document-api/reference/format/letter-spacing.mdx b/apps/docs/document-api/reference/format/letter-spacing.mdx index 590ebab57a..5851a698f9 100644 --- a/apps/docs/document-api/reference/format/letter-spacing.mdx +++ b/apps/docs/document-api/reference/format/letter-spacing.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | number \\| null | yes | One of: number, null | +| `value` | number \| null | yes | One of: number, null | ### Variant 2 (required: ref, value) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | number \\| null | yes | One of: number, null | +| `value` | number \| null | yes | One of: number, null | ### Example request diff --git a/apps/docs/document-api/reference/format/ligatures.mdx b/apps/docs/document-api/reference/format/ligatures.mdx index c38a7719b7..040e0134f4 100644 --- a/apps/docs/document-api/reference/format/ligatures.mdx +++ b/apps/docs/document-api/reference/format/ligatures.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | string \\| null | yes | One of: string, null | +| `value` | string \| null | yes | One of: string, null | ### Variant 2 (required: ref, value) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | string \\| null | yes | One of: string, null | +| `value` | string \| null | yes | One of: string, null | ### Example request diff --git a/apps/docs/document-api/reference/format/num-form.mdx b/apps/docs/document-api/reference/format/num-form.mdx index f4c76c5015..278daf5d83 100644 --- a/apps/docs/document-api/reference/format/num-form.mdx +++ b/apps/docs/document-api/reference/format/num-form.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | string \\| null | yes | One of: string, null | +| `value` | string \| null | yes | One of: string, null | ### Variant 2 (required: ref, value) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | string \\| null | yes | One of: string, null | +| `value` | string \| null | yes | One of: string, null | ### Example request diff --git a/apps/docs/document-api/reference/format/num-spacing.mdx b/apps/docs/document-api/reference/format/num-spacing.mdx index e023787d37..fa90536af1 100644 --- a/apps/docs/document-api/reference/format/num-spacing.mdx +++ b/apps/docs/document-api/reference/format/num-spacing.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | string \\| null | yes | One of: string, null | +| `value` | string \| null | yes | One of: string, null | ### Variant 2 (required: ref, value) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | string \\| null | yes | One of: string, null | +| `value` | string \| null | yes | One of: string, null | ### Example request diff --git a/apps/docs/document-api/reference/format/o-math.mdx b/apps/docs/document-api/reference/format/o-math.mdx index 1760cbc4e8..a11cd4e4ac 100644 --- a/apps/docs/document-api/reference/format/o-math.mdx +++ b/apps/docs/document-api/reference/format/o-math.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Variant 2 (required: ref) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Example request diff --git a/apps/docs/document-api/reference/format/outline.mdx b/apps/docs/document-api/reference/format/outline.mdx index e2edb904cc..2cc8476ba2 100644 --- a/apps/docs/document-api/reference/format/outline.mdx +++ b/apps/docs/document-api/reference/format/outline.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Variant 2 (required: ref) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Example request diff --git a/apps/docs/document-api/reference/format/paragraph/clear-alignment.mdx b/apps/docs/document-api/reference/format/paragraph/clear-alignment.mdx index 44fc665b23..228ed9796b 100644 --- a/apps/docs/document-api/reference/format/paragraph/clear-alignment.mdx +++ b/apps/docs/document-api/reference/format/paragraph/clear-alignment.mdx @@ -26,7 +26,7 @@ Returns a ParagraphMutationResult; reports NO_OP if no direct alignment is set. | Field | Type | Required | Description | | --- | --- | --- | --- | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Example request @@ -47,9 +47,9 @@ Returns a ParagraphMutationResult; reports NO_OP if no direct alignment is set. | Field | Type | Required | Description | | --- | --- | --- | --- | | `resolution` | object | yes | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `true` | yes | Constant: `true` | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Variant 2 (success=false) @@ -60,7 +60,7 @@ Returns a ParagraphMutationResult; reports NO_OP if no direct alignment is set. | `failure.details` | any | no | | | `failure.message` | string | yes | | | `resolution` | object | no | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `false` | yes | Constant: `false` | ### Example response diff --git a/apps/docs/document-api/reference/format/paragraph/clear-all-tab-stops.mdx b/apps/docs/document-api/reference/format/paragraph/clear-all-tab-stops.mdx index 3652ddfcac..6d8ee1ddeb 100644 --- a/apps/docs/document-api/reference/format/paragraph/clear-all-tab-stops.mdx +++ b/apps/docs/document-api/reference/format/paragraph/clear-all-tab-stops.mdx @@ -26,7 +26,7 @@ Returns a ParagraphMutationResult; reports NO_OP if no tab stops exist. | Field | Type | Required | Description | | --- | --- | --- | --- | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Example request @@ -47,9 +47,9 @@ Returns a ParagraphMutationResult; reports NO_OP if no tab stops exist. | Field | Type | Required | Description | | --- | --- | --- | --- | | `resolution` | object | yes | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `true` | yes | Constant: `true` | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Variant 2 (success=false) @@ -60,7 +60,7 @@ Returns a ParagraphMutationResult; reports NO_OP if no tab stops exist. | `failure.details` | any | no | | | `failure.message` | string | yes | | | `resolution` | object | no | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `false` | yes | Constant: `false` | ### Example response diff --git a/apps/docs/document-api/reference/format/paragraph/clear-border.mdx b/apps/docs/document-api/reference/format/paragraph/clear-border.mdx index f51f90ab0f..57e18ded7a 100644 --- a/apps/docs/document-api/reference/format/paragraph/clear-border.mdx +++ b/apps/docs/document-api/reference/format/paragraph/clear-border.mdx @@ -27,7 +27,7 @@ Returns a ParagraphMutationResult; reports NO_OP if the border is already absent | Field | Type | Required | Description | | --- | --- | --- | --- | | `side` | enum | yes | `"top"`, `"bottom"`, `"left"`, `"right"`, `"between"`, `"bar"`, `"all"` | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Example request @@ -49,9 +49,9 @@ Returns a ParagraphMutationResult; reports NO_OP if the border is already absent | Field | Type | Required | Description | | --- | --- | --- | --- | | `resolution` | object | yes | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `true` | yes | Constant: `true` | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Variant 2 (success=false) @@ -62,7 +62,7 @@ Returns a ParagraphMutationResult; reports NO_OP if the border is already absent | `failure.details` | any | no | | | `failure.message` | string | yes | | | `resolution` | object | no | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `false` | yes | Constant: `false` | ### Example response diff --git a/apps/docs/document-api/reference/format/paragraph/clear-direction.mdx b/apps/docs/document-api/reference/format/paragraph/clear-direction.mdx index 30f326db8b..adf2cb9fd2 100644 --- a/apps/docs/document-api/reference/format/paragraph/clear-direction.mdx +++ b/apps/docs/document-api/reference/format/paragraph/clear-direction.mdx @@ -26,7 +26,7 @@ Returns a ParagraphMutationResult; reports NO_OP if no direction is set. | Field | Type | Required | Description | | --- | --- | --- | --- | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Example request @@ -47,9 +47,9 @@ Returns a ParagraphMutationResult; reports NO_OP if no direction is set. | Field | Type | Required | Description | | --- | --- | --- | --- | | `resolution` | object | yes | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `true` | yes | Constant: `true` | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Variant 2 (success=false) @@ -60,7 +60,7 @@ Returns a ParagraphMutationResult; reports NO_OP if no direction is set. | `failure.details` | any | no | | | `failure.message` | string | yes | | | `resolution` | object | no | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `false` | yes | Constant: `false` | ### Example response diff --git a/apps/docs/document-api/reference/format/paragraph/clear-indentation.mdx b/apps/docs/document-api/reference/format/paragraph/clear-indentation.mdx index 3ecfd8c7a0..3852c38d38 100644 --- a/apps/docs/document-api/reference/format/paragraph/clear-indentation.mdx +++ b/apps/docs/document-api/reference/format/paragraph/clear-indentation.mdx @@ -26,7 +26,7 @@ Returns a ParagraphMutationResult; reports NO_OP if no direct indentation is set | Field | Type | Required | Description | | --- | --- | --- | --- | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Example request @@ -47,9 +47,9 @@ Returns a ParagraphMutationResult; reports NO_OP if no direct indentation is set | Field | Type | Required | Description | | --- | --- | --- | --- | | `resolution` | object | yes | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `true` | yes | Constant: `true` | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Variant 2 (success=false) @@ -60,7 +60,7 @@ Returns a ParagraphMutationResult; reports NO_OP if no direct indentation is set | `failure.details` | any | no | | | `failure.message` | string | yes | | | `resolution` | object | no | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `false` | yes | Constant: `false` | ### Example response diff --git a/apps/docs/document-api/reference/format/paragraph/clear-shading.mdx b/apps/docs/document-api/reference/format/paragraph/clear-shading.mdx index f8e2431711..c7f8a23ca1 100644 --- a/apps/docs/document-api/reference/format/paragraph/clear-shading.mdx +++ b/apps/docs/document-api/reference/format/paragraph/clear-shading.mdx @@ -26,7 +26,7 @@ Returns a ParagraphMutationResult; reports NO_OP if no shading is set. | Field | Type | Required | Description | | --- | --- | --- | --- | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Example request @@ -47,9 +47,9 @@ Returns a ParagraphMutationResult; reports NO_OP if no shading is set. | Field | Type | Required | Description | | --- | --- | --- | --- | | `resolution` | object | yes | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `true` | yes | Constant: `true` | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Variant 2 (success=false) @@ -60,7 +60,7 @@ Returns a ParagraphMutationResult; reports NO_OP if no shading is set. | `failure.details` | any | no | | | `failure.message` | string | yes | | | `resolution` | object | no | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `false` | yes | Constant: `false` | ### Example response diff --git a/apps/docs/document-api/reference/format/paragraph/clear-spacing.mdx b/apps/docs/document-api/reference/format/paragraph/clear-spacing.mdx index e9db4b454a..e6bf7a46da 100644 --- a/apps/docs/document-api/reference/format/paragraph/clear-spacing.mdx +++ b/apps/docs/document-api/reference/format/paragraph/clear-spacing.mdx @@ -26,7 +26,7 @@ Returns a ParagraphMutationResult; reports NO_OP if no direct spacing is set. | Field | Type | Required | Description | | --- | --- | --- | --- | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Example request @@ -47,9 +47,9 @@ Returns a ParagraphMutationResult; reports NO_OP if no direct spacing is set. | Field | Type | Required | Description | | --- | --- | --- | --- | | `resolution` | object | yes | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `true` | yes | Constant: `true` | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Variant 2 (success=false) @@ -60,7 +60,7 @@ Returns a ParagraphMutationResult; reports NO_OP if no direct spacing is set. | `failure.details` | any | no | | | `failure.message` | string | yes | | | `resolution` | object | no | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `false` | yes | Constant: `false` | ### Example response diff --git a/apps/docs/document-api/reference/format/paragraph/clear-tab-stop.mdx b/apps/docs/document-api/reference/format/paragraph/clear-tab-stop.mdx index edda7f202f..6ace2aad9a 100644 --- a/apps/docs/document-api/reference/format/paragraph/clear-tab-stop.mdx +++ b/apps/docs/document-api/reference/format/paragraph/clear-tab-stop.mdx @@ -27,7 +27,7 @@ Returns a ParagraphMutationResult; reports NO_OP if no tab stop exists at that p | Field | Type | Required | Description | | --- | --- | --- | --- | | `position` | integer | yes | | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Example request @@ -49,9 +49,9 @@ Returns a ParagraphMutationResult; reports NO_OP if no tab stop exists at that p | Field | Type | Required | Description | | --- | --- | --- | --- | | `resolution` | object | yes | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `true` | yes | Constant: `true` | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Variant 2 (success=false) @@ -62,7 +62,7 @@ Returns a ParagraphMutationResult; reports NO_OP if no tab stop exists at that p | `failure.details` | any | no | | | `failure.message` | string | yes | | | `resolution` | object | no | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `false` | yes | Constant: `false` | ### Example response diff --git a/apps/docs/document-api/reference/format/paragraph/reset-direct-formatting.mdx b/apps/docs/document-api/reference/format/paragraph/reset-direct-formatting.mdx index a1bd86e328..8538af93bc 100644 --- a/apps/docs/document-api/reference/format/paragraph/reset-direct-formatting.mdx +++ b/apps/docs/document-api/reference/format/paragraph/reset-direct-formatting.mdx @@ -26,7 +26,7 @@ Returns a ParagraphMutationResult; reports NO_OP if no direct formatting is pres | Field | Type | Required | Description | | --- | --- | --- | --- | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Example request @@ -47,9 +47,9 @@ Returns a ParagraphMutationResult; reports NO_OP if no direct formatting is pres | Field | Type | Required | Description | | --- | --- | --- | --- | | `resolution` | object | yes | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `true` | yes | Constant: `true` | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Variant 2 (success=false) @@ -60,7 +60,7 @@ Returns a ParagraphMutationResult; reports NO_OP if no direct formatting is pres | `failure.details` | any | no | | | `failure.message` | string | yes | | | `resolution` | object | no | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `false` | yes | Constant: `false` | ### Example response diff --git a/apps/docs/document-api/reference/format/paragraph/set-alignment.mdx b/apps/docs/document-api/reference/format/paragraph/set-alignment.mdx index 7730402c6b..399602fd3c 100644 --- a/apps/docs/document-api/reference/format/paragraph/set-alignment.mdx +++ b/apps/docs/document-api/reference/format/paragraph/set-alignment.mdx @@ -27,7 +27,7 @@ Returns a ParagraphMutationResult; reports NO_OP if the alignment already matche | Field | Type | Required | Description | | --- | --- | --- | --- | | `alignment` | enum | yes | `"left"`, `"center"`, `"right"`, `"justify"` | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Example request @@ -49,9 +49,9 @@ Returns a ParagraphMutationResult; reports NO_OP if the alignment already matche | Field | Type | Required | Description | | --- | --- | --- | --- | | `resolution` | object | yes | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `true` | yes | Constant: `true` | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Variant 2 (success=false) @@ -62,7 +62,7 @@ Returns a ParagraphMutationResult; reports NO_OP if the alignment already matche | `failure.details` | any | no | | | `failure.message` | string | yes | | | `resolution` | object | no | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `false` | yes | Constant: `false` | ### Example response diff --git a/apps/docs/document-api/reference/format/paragraph/set-border.mdx b/apps/docs/document-api/reference/format/paragraph/set-border.mdx index e1abf4812b..f7df79cf6b 100644 --- a/apps/docs/document-api/reference/format/paragraph/set-border.mdx +++ b/apps/docs/document-api/reference/format/paragraph/set-border.mdx @@ -31,7 +31,7 @@ Returns a ParagraphMutationResult; reports NO_OP if the border already matches. | `size` | integer | no | | | `space` | integer | no | | | `style` | string | yes | | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Example request @@ -56,9 +56,9 @@ Returns a ParagraphMutationResult; reports NO_OP if the border already matches. | Field | Type | Required | Description | | --- | --- | --- | --- | | `resolution` | object | yes | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `true` | yes | Constant: `true` | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Variant 2 (success=false) @@ -69,7 +69,7 @@ Returns a ParagraphMutationResult; reports NO_OP if the border already matches. | `failure.details` | any | no | | | `failure.message` | string | yes | | | `resolution` | object | no | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `false` | yes | Constant: `false` | ### Example response diff --git a/apps/docs/document-api/reference/format/paragraph/set-direction.mdx b/apps/docs/document-api/reference/format/paragraph/set-direction.mdx index 23732c28d4..f6cb6bb3bd 100644 --- a/apps/docs/document-api/reference/format/paragraph/set-direction.mdx +++ b/apps/docs/document-api/reference/format/paragraph/set-direction.mdx @@ -28,7 +28,7 @@ Returns a ParagraphMutationResult; reports NO_OP if the direction already matche | --- | --- | --- | --- | | `alignmentPolicy` | enum | no | `"preserve"`, `"matchDirection"` | | `direction` | enum | yes | `"ltr"`, `"rtl"` | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Example request @@ -51,9 +51,9 @@ Returns a ParagraphMutationResult; reports NO_OP if the direction already matche | Field | Type | Required | Description | | --- | --- | --- | --- | | `resolution` | object | yes | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `true` | yes | Constant: `true` | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Variant 2 (success=false) @@ -64,7 +64,7 @@ Returns a ParagraphMutationResult; reports NO_OP if the direction already matche | `failure.details` | any | no | | | `failure.message` | string | yes | | | `resolution` | object | no | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `false` | yes | Constant: `false` | ### Example response diff --git a/apps/docs/document-api/reference/format/paragraph/set-flow-options.mdx b/apps/docs/document-api/reference/format/paragraph/set-flow-options.mdx index d282d931ff..f077998021 100644 --- a/apps/docs/document-api/reference/format/paragraph/set-flow-options.mdx +++ b/apps/docs/document-api/reference/format/paragraph/set-flow-options.mdx @@ -29,21 +29,21 @@ Returns a ParagraphMutationResult; reports NO_OP if all flags already match. | Field | Type | Required | Description | | --- | --- | --- | --- | | `contextualSpacing` | boolean | yes | | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Variant 2 (required: pageBreakBefore) | Field | Type | Required | Description | | --- | --- | --- | --- | | `pageBreakBefore` | boolean | yes | | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Variant 3 (required: suppressAutoHyphens) | Field | Type | Required | Description | | --- | --- | --- | --- | | `suppressAutoHyphens` | boolean | yes | | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Example request @@ -65,9 +65,9 @@ Returns a ParagraphMutationResult; reports NO_OP if all flags already match. | Field | Type | Required | Description | | --- | --- | --- | --- | | `resolution` | object | yes | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `true` | yes | Constant: `true` | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Variant 2 (success=false) @@ -78,7 +78,7 @@ Returns a ParagraphMutationResult; reports NO_OP if all flags already match. | `failure.details` | any | no | | | `failure.message` | string | yes | | | `resolution` | object | no | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `false` | yes | Constant: `false` | ### Example response diff --git a/apps/docs/document-api/reference/format/paragraph/set-indentation.mdx b/apps/docs/document-api/reference/format/paragraph/set-indentation.mdx index bc9c117a69..a037ef0e72 100644 --- a/apps/docs/document-api/reference/format/paragraph/set-indentation.mdx +++ b/apps/docs/document-api/reference/format/paragraph/set-indentation.mdx @@ -29,28 +29,28 @@ Returns a ParagraphMutationResult; reports NO_OP if indentation already matches. | Field | Type | Required | Description | | --- | --- | --- | --- | | `left` | integer | yes | | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Variant 2 (required: right) | Field | Type | Required | Description | | --- | --- | --- | --- | | `right` | integer | yes | | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Variant 3 (required: firstLine) | Field | Type | Required | Description | | --- | --- | --- | --- | | `firstLine` | integer | yes | | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Variant 4 (required: hanging) | Field | Type | Required | Description | | --- | --- | --- | --- | | `hanging` | integer | yes | | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Example request @@ -72,9 +72,9 @@ Returns a ParagraphMutationResult; reports NO_OP if indentation already matches. | Field | Type | Required | Description | | --- | --- | --- | --- | | `resolution` | object | yes | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `true` | yes | Constant: `true` | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Variant 2 (success=false) @@ -85,7 +85,7 @@ Returns a ParagraphMutationResult; reports NO_OP if indentation already matches. | `failure.details` | any | no | | | `failure.message` | string | yes | | | `resolution` | object | no | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `false` | yes | Constant: `false` | ### Example response diff --git a/apps/docs/document-api/reference/format/paragraph/set-keep-options.mdx b/apps/docs/document-api/reference/format/paragraph/set-keep-options.mdx index 7c56034b2b..574a597d05 100644 --- a/apps/docs/document-api/reference/format/paragraph/set-keep-options.mdx +++ b/apps/docs/document-api/reference/format/paragraph/set-keep-options.mdx @@ -29,20 +29,20 @@ Returns a ParagraphMutationResult; reports NO_OP if all flags already match. | Field | Type | Required | Description | | --- | --- | --- | --- | | `keepNext` | boolean | yes | | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Variant 2 (required: keepLines) | Field | Type | Required | Description | | --- | --- | --- | --- | | `keepLines` | boolean | yes | | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Variant 3 (required: widowControl) | Field | Type | Required | Description | | --- | --- | --- | --- | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `widowControl` | boolean | yes | | ### Example request @@ -65,9 +65,9 @@ Returns a ParagraphMutationResult; reports NO_OP if all flags already match. | Field | Type | Required | Description | | --- | --- | --- | --- | | `resolution` | object | yes | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `true` | yes | Constant: `true` | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Variant 2 (success=false) @@ -78,7 +78,7 @@ Returns a ParagraphMutationResult; reports NO_OP if all flags already match. | `failure.details` | any | no | | | `failure.message` | string | yes | | | `resolution` | object | no | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `false` | yes | Constant: `false` | ### Example response diff --git a/apps/docs/document-api/reference/format/paragraph/set-outline-level.mdx b/apps/docs/document-api/reference/format/paragraph/set-outline-level.mdx index 4b01ce6d62..99597b460b 100644 --- a/apps/docs/document-api/reference/format/paragraph/set-outline-level.mdx +++ b/apps/docs/document-api/reference/format/paragraph/set-outline-level.mdx @@ -26,8 +26,8 @@ Returns a ParagraphMutationResult; reports NO_OP if outline level already matche | Field | Type | Required | Description | | --- | --- | --- | --- | -| `outlineLevel` | integer \\| null | yes | One of: integer, null | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `outlineLevel` | integer \| null | yes | One of: integer, null | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Example request @@ -49,9 +49,9 @@ Returns a ParagraphMutationResult; reports NO_OP if outline level already matche | Field | Type | Required | Description | | --- | --- | --- | --- | | `resolution` | object | yes | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `true` | yes | Constant: `true` | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Variant 2 (success=false) @@ -62,7 +62,7 @@ Returns a ParagraphMutationResult; reports NO_OP if outline level already matche | `failure.details` | any | no | | | `failure.message` | string | yes | | | `resolution` | object | no | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `false` | yes | Constant: `false` | ### Example response diff --git a/apps/docs/document-api/reference/format/paragraph/set-shading.mdx b/apps/docs/document-api/reference/format/paragraph/set-shading.mdx index 3e113ed8eb..f97c1537fc 100644 --- a/apps/docs/document-api/reference/format/paragraph/set-shading.mdx +++ b/apps/docs/document-api/reference/format/paragraph/set-shading.mdx @@ -29,21 +29,21 @@ Returns a ParagraphMutationResult; reports NO_OP if the shading already matches. | Field | Type | Required | Description | | --- | --- | --- | --- | | `fill` | string | yes | | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Variant 2 (required: color) | Field | Type | Required | Description | | --- | --- | --- | --- | | `color` | string | yes | | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Variant 3 (required: pattern) | Field | Type | Required | Description | | --- | --- | --- | --- | | `pattern` | string | yes | | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Example request @@ -65,9 +65,9 @@ Returns a ParagraphMutationResult; reports NO_OP if the shading already matches. | Field | Type | Required | Description | | --- | --- | --- | --- | | `resolution` | object | yes | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `true` | yes | Constant: `true` | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Variant 2 (success=false) @@ -78,7 +78,7 @@ Returns a ParagraphMutationResult; reports NO_OP if the shading already matches. | `failure.details` | any | no | | | `failure.message` | string | yes | | | `resolution` | object | no | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `false` | yes | Constant: `false` | ### Example response diff --git a/apps/docs/document-api/reference/format/paragraph/set-spacing.mdx b/apps/docs/document-api/reference/format/paragraph/set-spacing.mdx index 31cb6812dc..ebb1545a98 100644 --- a/apps/docs/document-api/reference/format/paragraph/set-spacing.mdx +++ b/apps/docs/document-api/reference/format/paragraph/set-spacing.mdx @@ -29,28 +29,28 @@ Returns a ParagraphMutationResult; reports NO_OP if spacing already matches. | Field | Type | Required | Description | | --- | --- | --- | --- | | `before` | integer | yes | | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Variant 2 (required: after) | Field | Type | Required | Description | | --- | --- | --- | --- | | `after` | integer | yes | | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Variant 3 (required: line) | Field | Type | Required | Description | | --- | --- | --- | --- | | `line` | integer | yes | | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Variant 4 (required: lineRule) | Field | Type | Required | Description | | --- | --- | --- | --- | | `lineRule` | enum | yes | `"auto"`, `"exact"`, `"atLeast"` | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Example request @@ -72,9 +72,9 @@ Returns a ParagraphMutationResult; reports NO_OP if spacing already matches. | Field | Type | Required | Description | | --- | --- | --- | --- | | `resolution` | object | yes | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `true` | yes | Constant: `true` | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Variant 2 (success=false) @@ -85,7 +85,7 @@ Returns a ParagraphMutationResult; reports NO_OP if spacing already matches. | `failure.details` | any | no | | | `failure.message` | string | yes | | | `resolution` | object | no | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `false` | yes | Constant: `false` | ### Example response diff --git a/apps/docs/document-api/reference/format/paragraph/set-tab-stop.mdx b/apps/docs/document-api/reference/format/paragraph/set-tab-stop.mdx index 16db8393c7..292e5ce29b 100644 --- a/apps/docs/document-api/reference/format/paragraph/set-tab-stop.mdx +++ b/apps/docs/document-api/reference/format/paragraph/set-tab-stop.mdx @@ -29,7 +29,7 @@ Returns a ParagraphMutationResult; reports NO_OP if an identical tab stop alread | `alignment` | enum | yes | `"left"`, `"center"`, `"right"`, `"decimal"`, `"bar"` | | `leader` | enum | no | `"none"`, `"dot"`, `"hyphen"`, `"underscore"`, `"heavy"`, `"middleDot"` | | `position` | integer | yes | | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Example request @@ -53,9 +53,9 @@ Returns a ParagraphMutationResult; reports NO_OP if an identical tab stop alread | Field | Type | Required | Description | | --- | --- | --- | --- | | `resolution` | object | yes | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `true` | yes | Constant: `true` | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Variant 2 (success=false) @@ -66,7 +66,7 @@ Returns a ParagraphMutationResult; reports NO_OP if an identical tab stop alread | `failure.details` | any | no | | | `failure.message` | string | yes | | | `resolution` | object | no | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `false` | yes | Constant: `false` | ### Example response diff --git a/apps/docs/document-api/reference/format/position.mdx b/apps/docs/document-api/reference/format/position.mdx index 901b2c3edc..d7fd88bac4 100644 --- a/apps/docs/document-api/reference/format/position.mdx +++ b/apps/docs/document-api/reference/format/position.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | number \\| null | yes | One of: number, null | +| `value` | number \| null | yes | One of: number, null | ### Variant 2 (required: ref, value) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | number \\| null | yes | One of: number, null | +| `value` | number \| null | yes | One of: number, null | ### Example request diff --git a/apps/docs/document-api/reference/format/r-fonts.mdx b/apps/docs/document-api/reference/format/r-fonts.mdx index c88eb13d72..e3e53e0785 100644 --- a/apps/docs/document-api/reference/format/r-fonts.mdx +++ b/apps/docs/document-api/reference/format/r-fonts.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | object \\| null | yes | One of: object, null | +| `value` | object \| null | yes | One of: object, null | ### Variant 2 (required: ref, value) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | object \\| null | yes | One of: object, null | +| `value` | object \| null | yes | One of: object, null | ### Example request diff --git a/apps/docs/document-api/reference/format/r-style.mdx b/apps/docs/document-api/reference/format/r-style.mdx index b1f5d3d2e5..43fa56d4a3 100644 --- a/apps/docs/document-api/reference/format/r-style.mdx +++ b/apps/docs/document-api/reference/format/r-style.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | string \\| null | yes | One of: string, null | +| `value` | string \| null | yes | One of: string, null | ### Variant 2 (required: ref, value) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | string \\| null | yes | One of: string, null | +| `value` | string \| null | yes | One of: string, null | ### Example request diff --git a/apps/docs/document-api/reference/format/rtl.mdx b/apps/docs/document-api/reference/format/rtl.mdx index 6ea1a06590..c41f383def 100644 --- a/apps/docs/document-api/reference/format/rtl.mdx +++ b/apps/docs/document-api/reference/format/rtl.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming only the inline run property patch was | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Variant 2 (required: ref) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Example request diff --git a/apps/docs/document-api/reference/format/shading.mdx b/apps/docs/document-api/reference/format/shading.mdx index 15f63eae56..429a44105f 100644 --- a/apps/docs/document-api/reference/format/shading.mdx +++ b/apps/docs/document-api/reference/format/shading.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | object \\| null | yes | One of: object, null | +| `value` | object \| null | yes | One of: object, null | ### Variant 2 (required: ref, value) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | object \\| null | yes | One of: object, null | +| `value` | object \| null | yes | One of: object, null | ### Example request diff --git a/apps/docs/document-api/reference/format/shadow.mdx b/apps/docs/document-api/reference/format/shadow.mdx index bc39e333d8..eacd97b135 100644 --- a/apps/docs/document-api/reference/format/shadow.mdx +++ b/apps/docs/document-api/reference/format/shadow.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Variant 2 (required: ref) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Example request diff --git a/apps/docs/document-api/reference/format/small-caps.mdx b/apps/docs/document-api/reference/format/small-caps.mdx index 8fb0db43e1..a22d96ef83 100644 --- a/apps/docs/document-api/reference/format/small-caps.mdx +++ b/apps/docs/document-api/reference/format/small-caps.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Variant 2 (required: ref) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Example request diff --git a/apps/docs/document-api/reference/format/snap-to-grid.mdx b/apps/docs/document-api/reference/format/snap-to-grid.mdx index 75d98d2f73..fa33253a48 100644 --- a/apps/docs/document-api/reference/format/snap-to-grid.mdx +++ b/apps/docs/document-api/reference/format/snap-to-grid.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Variant 2 (required: ref) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Example request diff --git a/apps/docs/document-api/reference/format/spec-vanish.mdx b/apps/docs/document-api/reference/format/spec-vanish.mdx index 42f51debe3..05eb650992 100644 --- a/apps/docs/document-api/reference/format/spec-vanish.mdx +++ b/apps/docs/document-api/reference/format/spec-vanish.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Variant 2 (required: ref) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Example request diff --git a/apps/docs/document-api/reference/format/strike.mdx b/apps/docs/document-api/reference/format/strike.mdx index d80b195d50..710c566f0d 100644 --- a/apps/docs/document-api/reference/format/strike.mdx +++ b/apps/docs/document-api/reference/format/strike.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Variant 2 (required: ref) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Example request diff --git a/apps/docs/document-api/reference/format/stylistic-sets.mdx b/apps/docs/document-api/reference/format/stylistic-sets.mdx index c3b20172bb..a950ff435f 100644 --- a/apps/docs/document-api/reference/format/stylistic-sets.mdx +++ b/apps/docs/document-api/reference/format/stylistic-sets.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | object[] \\| null | yes | One of: object[], null | +| `value` | object[] \| null | yes | One of: object[], null | ### Variant 2 (required: ref, value) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | object[] \\| null | yes | One of: object[], null | +| `value` | object[] \| null | yes | One of: object[], null | ### Example request diff --git a/apps/docs/document-api/reference/format/underline.mdx b/apps/docs/document-api/reference/format/underline.mdx index 253427ece2..313a28c602 100644 --- a/apps/docs/document-api/reference/format/underline.mdx +++ b/apps/docs/document-api/reference/format/underline.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | boolean \\| null \\| object | no | One of: boolean, null, object | +| `value` | boolean \| null \| object | no | One of: boolean, null, object | ### Variant 2 (required: ref) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | boolean \\| null \\| object | no | One of: boolean, null, object | +| `value` | boolean \| null \| object | no | One of: boolean, null, object | ### Example request diff --git a/apps/docs/document-api/reference/format/vanish.mdx b/apps/docs/document-api/reference/format/vanish.mdx index 0566cd541e..c29ade86fc 100644 --- a/apps/docs/document-api/reference/format/vanish.mdx +++ b/apps/docs/document-api/reference/format/vanish.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Variant 2 (required: ref) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Example request diff --git a/apps/docs/document-api/reference/format/vert-align.mdx b/apps/docs/document-api/reference/format/vert-align.mdx index db5ffe9c30..693884cfcf 100644 --- a/apps/docs/document-api/reference/format/vert-align.mdx +++ b/apps/docs/document-api/reference/format/vert-align.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | enum \\| null | yes | One of: enum, null | +| `value` | enum \| null | yes | One of: enum, null | ### Variant 2 (required: ref, value) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | enum \\| null | yes | One of: enum, null | +| `value` | enum \| null | yes | One of: enum, null | ### Example request diff --git a/apps/docs/document-api/reference/format/web-hidden.mdx b/apps/docs/document-api/reference/format/web-hidden.mdx index 4460c64535..c9ebe4f069 100644 --- a/apps/docs/document-api/reference/format/web-hidden.mdx +++ b/apps/docs/document-api/reference/format/web-hidden.mdx @@ -32,14 +32,14 @@ Returns a TextMutationReceipt confirming the inline run property patch was appli | `target.end` | SelectionPoint | yes | SelectionPoint | | `target.kind` | `"selection"` | yes | Constant: `"selection"` | | `target.start` | SelectionPoint | yes | SelectionPoint | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Variant 2 (required: ref) | Field | Type | Required | Description | | --- | --- | --- | --- | | `ref` | string | yes | | -| `value` | boolean \\| null | no | One of: boolean, null | +| `value` | boolean \| null | no | One of: boolean, null | ### Example request diff --git a/apps/docs/document-api/reference/header-footers/get.mdx b/apps/docs/document-api/reference/header-footers/get.mdx index b1f8015839..c3f6b7a526 100644 --- a/apps/docs/document-api/reference/header-footers/get.mdx +++ b/apps/docs/document-api/reference/header-footers/get.mdx @@ -56,7 +56,7 @@ Returns a HeaderFooterSlotEntry for the targeted section slot. | --- | --- | --- | --- | | `isExplicit` | boolean | yes | | | `kind` | enum | yes | `"header"`, `"footer"` | -| `refId` | any | no | | +| `refId` | string \| null | no | | | `section` | SectionAddress | yes | SectionAddress | | `section.kind` | `"section"` | yes | Constant: `"section"` | | `section.sectionId` | string | yes | | @@ -69,7 +69,7 @@ Returns a HeaderFooterSlotEntry for the targeted section slot. { "isExplicit": true, "kind": "header", - "refId": {}, + "refId": null, "section": { "kind": "section", "sectionId": "example" diff --git a/apps/docs/document-api/reference/header-footers/list.mdx b/apps/docs/document-api/reference/header-footers/list.mdx index b7d71e2ad6..3d21b7c7e5 100644 --- a/apps/docs/document-api/reference/header-footers/list.mdx +++ b/apps/docs/document-api/reference/header-footers/list.mdx @@ -72,7 +72,7 @@ Returns a paginated DiscoveryOutput of HeaderFooterSlotEntry items. "id": "id-001", "isExplicit": true, "kind": "header", - "refId": {}, + "refId": null, "section": { "kind": "section", "sectionId": "example" diff --git a/apps/docs/document-api/reference/hyperlinks/patch.mdx b/apps/docs/document-api/reference/hyperlinks/patch.mdx index e2e88cddf9..3bc0534cbe 100644 --- a/apps/docs/document-api/reference/hyperlinks/patch.mdx +++ b/apps/docs/document-api/reference/hyperlinks/patch.mdx @@ -27,12 +27,12 @@ Returns a HyperlinkMutationResult with the updated hyperlink address on success, | Field | Type | Required | Description | | --- | --- | --- | --- | | `patch` | object | yes | | -| `patch.anchor` | string \\| null | no | One of: string, null | -| `patch.docLocation` | string \\| null | no | One of: string, null | -| `patch.href` | string \\| null | no | One of: string, null | -| `patch.rel` | string \\| null | no | One of: string, null | -| `patch.target` | string \\| null | no | One of: string, null | -| `patch.tooltip` | string \\| null | no | One of: string, null | +| `patch.anchor` | string \| null | no | One of: string, null | +| `patch.docLocation` | string \| null | no | One of: string, null | +| `patch.href` | string \| null | no | One of: string, null | +| `patch.rel` | string \| null | no | One of: string, null | +| `patch.target` | string \| null | no | One of: string, null | +| `patch.tooltip` | string \| null | no | One of: string, null | | `target` | object(kind="inline") | yes | | | `target.anchor` | InlineAnchor | yes | InlineAnchor | | `target.anchor.end` | Position | yes | Position | diff --git a/apps/docs/document-api/reference/images/move.mdx b/apps/docs/document-api/reference/images/move.mdx index 3651f7e91c..8071762611 100644 --- a/apps/docs/document-api/reference/images/move.mdx +++ b/apps/docs/document-api/reference/images/move.mdx @@ -27,7 +27,7 @@ Returns an ImagesMutationResult indicating success or failure. | Field | Type | Required | Description | | --- | --- | --- | --- | | `imageId` | string | yes | | -| `to` | object(kind="documentStart") \\| object(kind="documentEnd") \\| object(kind="before") \\| object(kind="after") \\| object(kind="inParagraph") | yes | One of: object(kind="documentStart"), object(kind="documentEnd"), object(kind="before"), object(kind="after"), object(kind="inParagraph") | +| `to` | object(kind="documentStart") \| object(kind="documentEnd") \| object(kind="before") \| object(kind="after") \| object(kind="inParagraph") | yes | One of: object(kind="documentStart"), object(kind="documentEnd"), object(kind="before"), object(kind="after"), object(kind="inParagraph") | ### Example request diff --git a/apps/docs/document-api/reference/images/set-hyperlink.mdx b/apps/docs/document-api/reference/images/set-hyperlink.mdx index fc6502fc41..0f384b709a 100644 --- a/apps/docs/document-api/reference/images/set-hyperlink.mdx +++ b/apps/docs/document-api/reference/images/set-hyperlink.mdx @@ -28,7 +28,7 @@ Returns an ImagesMutationResult; reports NO_OP if unchanged. | --- | --- | --- | --- | | `imageId` | string | yes | | | `tooltip` | string | no | | -| `url` | any | yes | | +| `url` | string \| null | yes | | ### Example request @@ -36,7 +36,7 @@ Returns an ImagesMutationResult; reports NO_OP if unchanged. { "imageId": "example", "tooltip": "example", - "url": {} + "url": "example" } ``` diff --git a/apps/docs/document-api/reference/index.mdx b/apps/docs/document-api/reference/index.mdx index 49a58b3833..e590891c7c 100644 --- a/apps/docs/document-api/reference/index.mdx +++ b/apps/docs/document-api/reference/index.mdx @@ -24,7 +24,7 @@ This reference is sourced from `packages/document-api/src/contract/*`. | Capabilities | 1 | 0 | 1 | [Open](/document-api/reference/capabilities/index) | | Create | 6 | 0 | 6 | [Open](/document-api/reference/create/index) | | Sections | 18 | 0 | 18 | [Open](/document-api/reference/sections/index) | -| Format | 44 | 1 | 45 | [Open](/document-api/reference/format/index) | +| Format | 45 | 1 | 46 | [Open](/document-api/reference/format/index) | | Styles | 1 | 0 | 1 | [Open](/document-api/reference/styles/index) | | Lists | 39 | 0 | 39 | [Open](/document-api/reference/lists/index) | | Comments | 5 | 0 | 5 | [Open](/document-api/reference/comments/index) | @@ -131,6 +131,7 @@ The tables below are grouped by namespace. | Operation | API member path | Description | | --- | --- | --- | +| formatRange | editor.doc.formatRange(...) | Legacy root-level alias for inline range formatting. Routes to `format.apply` for compatibility with older callers. | | format.apply | editor.doc.format.apply(...) | Apply inline run-property patch changes to the target range with explicit set/clear semantics. | | format.bold | editor.doc.format.bold(...) | Set or clear the `bold` inline run property on the target text range. | | format.italic | editor.doc.format.italic(...) | Set or clear the `italic` inline run property on the target text range. | @@ -243,7 +244,7 @@ The tables below are grouped by namespace. | --- | --- | --- | | trackChanges.list | editor.doc.trackChanges.list(...) | List all tracked changes in the document. | | trackChanges.get | editor.doc.trackChanges.get(...) | Retrieve a single tracked change by ID. | -| trackChanges.decide | editor.doc.trackChanges.decide(...) | Accept or reject a tracked change (by ID or scope: all). | +| trackChanges.decide | editor.doc.trackChanges.decide(...) | Accept or reject tracked changes by ID, range, or scope: all (optionally filtered by story). | #### Query diff --git a/apps/docs/document-api/reference/index/insert.mdx b/apps/docs/document-api/reference/index/insert.mdx index 864ad1a029..24cc07ddc7 100644 --- a/apps/docs/document-api/reference/index/insert.mdx +++ b/apps/docs/document-api/reference/index/insert.mdx @@ -26,7 +26,7 @@ Returns an IndexMutationResult indicating success with the index address or a fa | Field | Type | Required | Description | | --- | --- | --- | --- | -| `at` | object(kind="documentStart") \\| object(kind="documentEnd") \\| object(kind="before") \\| object(kind="after") | yes | One of: object(kind="documentStart"), object(kind="documentEnd"), object(kind="before"), object(kind="after") | +| `at` | object(kind="documentStart") \| object(kind="documentEnd") \| object(kind="before") \| object(kind="after") | yes | One of: object(kind="documentStart"), object(kind="documentEnd"), object(kind="before"), object(kind="after") | | `config` | object | no | | | `config.accentedSorting` | boolean | no | | | `config.columns` | integer | no | | diff --git a/apps/docs/document-api/reference/insert.mdx b/apps/docs/document-api/reference/insert.mdx index 3f0d0fc77e..d44754769d 100644 --- a/apps/docs/document-api/reference/insert.mdx +++ b/apps/docs/document-api/reference/insert.mdx @@ -57,7 +57,7 @@ Returns an SDMutationReceipt with applied status; resolution reports the inserte | Field | Type | Required | Description | | --- | --- | --- | --- | -| `content` | object \\| object[] | yes | One of: object, object[] | +| `content` | object \| object[] | yes | One of: object, object[] | | `in` | StoryLocator | no | StoryLocator | | `nestingPolicy` | object | no | | | `nestingPolicy.tables` | enum | no | `"forbid"`, `"allow"` | @@ -106,7 +106,7 @@ Returns an SDMutationReceipt with applied status; resolution reports the inserte | `resolution.selectionTarget.end` | SelectionPoint | no | SelectionPoint | | `resolution.selectionTarget.kind` | `"selection"` | no | Constant: `"selection"` | | `resolution.selectionTarget.start` | SelectionPoint | no | SelectionPoint | -| `resolution.target` | TextAddress \\| BlockNodeAddress | no | One of: TextAddress, BlockNodeAddress | +| `resolution.target` | TextAddress \| BlockNodeAddress | no | One of: TextAddress, BlockNodeAddress | | `success` | `true` | yes | Constant: `true` | ### Variant 2 (success=false) @@ -128,7 +128,7 @@ Returns an SDMutationReceipt with applied status; resolution reports the inserte | `resolution.selectionTarget.end` | SelectionPoint | no | SelectionPoint | | `resolution.selectionTarget.kind` | `"selection"` | no | Constant: `"selection"` | | `resolution.selectionTarget.start` | SelectionPoint | no | SelectionPoint | -| `resolution.target` | TextAddress \\| BlockNodeAddress | no | One of: TextAddress, BlockNodeAddress | +| `resolution.target` | TextAddress \| BlockNodeAddress | no | One of: TextAddress, BlockNodeAddress | | `success` | `false` | yes | Constant: `false` | ### Example response diff --git a/apps/docs/document-api/reference/lists/set-level-layout.mdx b/apps/docs/document-api/reference/lists/set-level-layout.mdx index 1a944edf04..a1a563126c 100644 --- a/apps/docs/document-api/reference/lists/set-level-layout.mdx +++ b/apps/docs/document-api/reference/lists/set-level-layout.mdx @@ -30,7 +30,7 @@ Returns a ListsMutateItemResult receipt; reports NO_OP if all values already mat | `layout.alignedAt` | integer | no | | | `layout.alignment` | enum | no | `"left"`, `"center"`, `"right"` | | `layout.followCharacter` | enum | no | `"tab"`, `"space"`, `"nothing"` | -| `layout.tabStopAt` | any | no | | +| `layout.tabStopAt` | integer \| null | no | | | `layout.textIndentAt` | integer | no | | | `level` | integer | yes | | | `target` | ListItemAddress | yes | ListItemAddress | diff --git a/apps/docs/document-api/reference/lists/set-level-restart.mdx b/apps/docs/document-api/reference/lists/set-level-restart.mdx index 0dc738f657..a8c59a8c6c 100644 --- a/apps/docs/document-api/reference/lists/set-level-restart.mdx +++ b/apps/docs/document-api/reference/lists/set-level-restart.mdx @@ -27,7 +27,7 @@ Returns a ListsMutateItemResult receipt. | Field | Type | Required | Description | | --- | --- | --- | --- | | `level` | integer | yes | | -| `restartAfterLevel` | any | yes | | +| `restartAfterLevel` | integer \| null | yes | | | `scope` | enum | no | `"definition"`, `"instance"` | | `target` | ListItemAddress | yes | ListItemAddress | | `target.kind` | `"block"` | yes | Constant: `"block"` | @@ -39,7 +39,7 @@ Returns a ListsMutateItemResult receipt. ```json { "level": 1, - "restartAfterLevel": {}, + "restartAfterLevel": 1, "scope": "definition", "target": { "kind": "block", diff --git a/apps/docs/document-api/reference/lists/set-value.mdx b/apps/docs/document-api/reference/lists/set-value.mdx index d6aaab7836..1ccd2795f4 100644 --- a/apps/docs/document-api/reference/lists/set-value.mdx +++ b/apps/docs/document-api/reference/lists/set-value.mdx @@ -30,7 +30,7 @@ Returns a ListsMutateItemResult receipt. | `target.kind` | `"block"` | yes | Constant: `"block"` | | `target.nodeId` | string | yes | | | `target.nodeType` | `"listItem"` | yes | Constant: `"listItem"` | -| `value` | any | yes | | +| `value` | integer \| null | yes | | ### Example request @@ -41,7 +41,7 @@ Returns a ListsMutateItemResult receipt. "nodeId": "node-def456", "nodeType": "listItem" }, - "value": {} + "value": 1 } ``` diff --git a/apps/docs/document-api/reference/lists/split.mdx b/apps/docs/document-api/reference/lists/split.mdx index 9b3b534228..91390f7158 100644 --- a/apps/docs/document-api/reference/lists/split.mdx +++ b/apps/docs/document-api/reference/lists/split.mdx @@ -53,7 +53,7 @@ Returns a ListsSplitResult with the new listId, numId, and the restart value app | --- | --- | --- | --- | | `listId` | string | yes | | | `numId` | integer | yes | | -| `restartedAt` | any | yes | | +| `restartedAt` | integer \| null | yes | | | `success` | `true` | yes | Constant: `true` | ### Variant 2 (success=false) @@ -72,7 +72,7 @@ Returns a ListsSplitResult with the new listId, numId, and the restart value app { "listId": "example", "numId": 1, - "restartedAt": {}, + "restartedAt": 1, "success": true } ``` diff --git a/apps/docs/document-api/reference/mutations/apply.mdx b/apps/docs/document-api/reference/mutations/apply.mdx index 75d5b836a2..4708aa2a0e 100644 --- a/apps/docs/document-api/reference/mutations/apply.mdx +++ b/apps/docs/document-api/reference/mutations/apply.mdx @@ -105,7 +105,7 @@ The runtime capability snapshot also exposes this allowlist at `planEngine.suppo | `changeMode` | enum | yes | `"direct"`, `"tracked"` | | `expectedRevision` | string | no | | | `in` | StoryLocator | no | StoryLocator | -| `steps` | object(op="text.rewrite") \\| object(op="text.insert") \\| object(op="text.delete") \\| object(op="format.apply") \\| object(op="assert")[] | yes | | +| `steps` | object(op="text.rewrite") \| object(op="text.insert") \| object(op="text.delete") \| object(op="format.apply") \| object(op="assert")[] | yes | | ### Example request diff --git a/apps/docs/document-api/reference/mutations/preview.mdx b/apps/docs/document-api/reference/mutations/preview.mdx index 774acdad91..03dce35e61 100644 --- a/apps/docs/document-api/reference/mutations/preview.mdx +++ b/apps/docs/document-api/reference/mutations/preview.mdx @@ -105,7 +105,7 @@ The runtime capability snapshot also exposes this allowlist at `planEngine.suppo | `changeMode` | enum | yes | `"direct"`, `"tracked"` | | `expectedRevision` | string | no | | | `in` | StoryLocator | no | StoryLocator | -| `steps` | object(op="text.rewrite") \\| object(op="text.insert") \\| object(op="text.delete") \\| object(op="format.apply") \\| object(op="assert")[] | yes | | +| `steps` | object(op="text.rewrite") \| object(op="text.insert") \| object(op="text.delete") \| object(op="format.apply") \| object(op="assert")[] | yes | | ### Example request diff --git a/apps/docs/document-api/reference/query/match.mdx b/apps/docs/document-api/reference/query/match.mdx index a0723c244b..365d3c9e3c 100644 --- a/apps/docs/document-api/reference/query/match.mdx +++ b/apps/docs/document-api/reference/query/match.mdx @@ -32,7 +32,7 @@ Returns a QueryMatchOutput with the resolved target address and cardinality meta | `mode` | enum | no | `"strict"`, `"candidates"` | | `offset` | integer | no | | | `require` | enum | no | `"any"`, `"first"`, `"exactlyOne"`, `"all"` | -| `select` | object(type="text") \\| object(type="node") | yes | One of: object(type="text"), object(type="node") | +| `select` | object(type="text") \| object(type="node") | yes | One of: object(type="text"), object(type="node") | | `within` | BlockNodeAddress | no | BlockNodeAddress | | `within.kind` | `"block"` | no | Constant: `"block"` | | `within.nodeId` | string | no | | @@ -65,7 +65,7 @@ Returns a QueryMatchOutput with the resolved target address and cardinality meta | Field | Type | Required | Description | | --- | --- | --- | --- | | `evaluatedRevision` | string | yes | | -| `items` | object(matchKind="text") \\| object(matchKind="node")[] | yes | | +| `items` | object(matchKind="text") \| object(matchKind="node")[] | yes | | | `meta` | object | yes | | | `meta.effectiveResolved` | boolean | yes | | | `page` | PageInfo | yes | PageInfo | diff --git a/apps/docs/document-api/reference/ranges/resolve.mdx b/apps/docs/document-api/reference/ranges/resolve.mdx index 35513aaa32..66a3d6c31f 100644 --- a/apps/docs/document-api/reference/ranges/resolve.mdx +++ b/apps/docs/document-api/reference/ranges/resolve.mdx @@ -26,9 +26,9 @@ Returns a ResolveRangeOutput with evaluatedRevision, handle.ref, target (Selecti | Field | Type | Required | Description | | --- | --- | --- | --- | -| `end` | object(kind="document") \\| object(kind="point") \\| object(kind="ref") | yes | One of: object(kind="document"), object(kind="point"), object(kind="ref") | +| `end` | object(kind="document") \| object(kind="point") \| object(kind="ref") | yes | One of: object(kind="document"), object(kind="point"), object(kind="ref") | | `expectedRevision` | string | no | | -| `start` | object(kind="document") \\| object(kind="point") \\| object(kind="ref") | yes | One of: object(kind="document"), object(kind="point"), object(kind="ref") | +| `start` | object(kind="document") \| object(kind="point") \| object(kind="ref") | yes | One of: object(kind="document"), object(kind="point"), object(kind="ref") | ### Example request @@ -53,7 +53,7 @@ Returns a ResolveRangeOutput with evaluatedRevision, handle.ref, target (Selecti | `evaluatedRevision` | string | yes | | | `handle` | object(refStability="ephemeral") | yes | | | `handle.coversFullTarget` | boolean | yes | | -| `handle.ref` | string \\| null | yes | One of: string, null | +| `handle.ref` | string \| null | yes | One of: string, null | | `handle.refStability` | `"ephemeral"` | yes | Constant: `"ephemeral"` | | `preview` | object | yes | | | `preview.blocks` | object[] | yes | | diff --git a/apps/docs/document-api/reference/replace.mdx b/apps/docs/document-api/reference/replace.mdx index de53ace0bb..ce17ecd20a 100644 --- a/apps/docs/document-api/reference/replace.mdx +++ b/apps/docs/document-api/reference/replace.mdx @@ -47,17 +47,17 @@ Returns an SDMutationReceipt with applied status; receipt reports NO_OP if the t | Field | Type | Required | Description | | --- | --- | --- | --- | -| `content` | object \\| object[] | yes | One of: object, object[] | +| `content` | object \| object[] | yes | One of: object, object[] | | `in` | StoryLocator | no | StoryLocator | | `nestingPolicy` | object | no | | | `nestingPolicy.tables` | enum | no | `"forbid"`, `"allow"` | -| `target` | BlockNodeAddress \\| SelectionTarget | yes | One of: BlockNodeAddress, SelectionTarget | +| `target` | BlockNodeAddress \| SelectionTarget | yes | One of: BlockNodeAddress, SelectionTarget | ### Variant 2.2 (required: ref, content) | Field | Type | Required | Description | | --- | --- | --- | --- | -| `content` | object \\| object[] | yes | One of: object, object[] | +| `content` | object \| object[] | yes | One of: object, object[] | | `in` | StoryLocator | no | StoryLocator | | `nestingPolicy` | object | no | | | `nestingPolicy.tables` | enum | no | `"forbid"`, `"allow"` | @@ -101,7 +101,7 @@ Returns an SDMutationReceipt with applied status; receipt reports NO_OP if the t | `resolution.selectionTarget.end` | SelectionPoint | no | SelectionPoint | | `resolution.selectionTarget.kind` | `"selection"` | no | Constant: `"selection"` | | `resolution.selectionTarget.start` | SelectionPoint | no | SelectionPoint | -| `resolution.target` | TextAddress \\| BlockNodeAddress | no | One of: TextAddress, BlockNodeAddress | +| `resolution.target` | TextAddress \| BlockNodeAddress | no | One of: TextAddress, BlockNodeAddress | | `success` | `true` | yes | Constant: `true` | ### Variant 2 (success=false) @@ -123,7 +123,7 @@ Returns an SDMutationReceipt with applied status; receipt reports NO_OP if the t | `resolution.selectionTarget.end` | SelectionPoint | no | SelectionPoint | | `resolution.selectionTarget.kind` | `"selection"` | no | Constant: `"selection"` | | `resolution.selectionTarget.start` | SelectionPoint | no | SelectionPoint | -| `resolution.target` | TextAddress \\| BlockNodeAddress | no | One of: TextAddress, BlockNodeAddress | +| `resolution.target` | TextAddress \| BlockNodeAddress | no | One of: TextAddress, BlockNodeAddress | | `success` | `false` | yes | Constant: `false` | ### Example response diff --git a/apps/docs/document-api/reference/sections/get.mdx b/apps/docs/document-api/reference/sections/get.mdx index a436a7e8ac..fba6042315 100644 --- a/apps/docs/document-api/reference/sections/get.mdx +++ b/apps/docs/document-api/reference/sections/get.mdx @@ -78,8 +78,8 @@ Returns a SectionInfo object with full section properties including margins, col | `margins.right` | number | no | | | `margins.top` | number | no | | | `oddEvenHeadersFooters` | boolean | no | | -| `pageBorders` | any \\| any \\| any \\| any \\| any \\| any \\| any | no | One of: any, any, any, any, any, any, any | -| `pageBorders.bottom` | any \\| any \\| any \\| any \\| any \\| any | no | One of: any, any, any, any, any, any | +| `pageBorders` | any \| any \| any \| any \| any \| any \| any | no | One of: any, any, any, any, any, any, any | +| `pageBorders.bottom` | any \| any \| any \| any \| any \| any | no | One of: any, any, any, any, any, any | | `pageBorders.bottom.color` | string | no | | | `pageBorders.bottom.frame` | boolean | no | | | `pageBorders.bottom.shadow` | boolean | no | | @@ -87,7 +87,7 @@ Returns a SectionInfo object with full section properties including margins, col | `pageBorders.bottom.space` | number | no | | | `pageBorders.bottom.style` | string | no | | | `pageBorders.display` | enum | no | `"allPages"`, `"firstPage"`, `"notFirstPage"` | -| `pageBorders.left` | any \\| any \\| any \\| any \\| any \\| any | no | One of: any, any, any, any, any, any | +| `pageBorders.left` | any \| any \| any \| any \| any \| any | no | One of: any, any, any, any, any, any | | `pageBorders.left.color` | string | no | | | `pageBorders.left.frame` | boolean | no | | | `pageBorders.left.shadow` | boolean | no | | @@ -95,14 +95,14 @@ Returns a SectionInfo object with full section properties including margins, col | `pageBorders.left.space` | number | no | | | `pageBorders.left.style` | string | no | | | `pageBorders.offsetFrom` | enum | no | `"page"`, `"text"` | -| `pageBorders.right` | any \\| any \\| any \\| any \\| any \\| any | no | One of: any, any, any, any, any, any | +| `pageBorders.right` | any \| any \| any \| any \| any \| any | no | One of: any, any, any, any, any, any | | `pageBorders.right.color` | string | no | | | `pageBorders.right.frame` | boolean | no | | | `pageBorders.right.shadow` | boolean | no | | | `pageBorders.right.size` | number | no | | | `pageBorders.right.space` | number | no | | | `pageBorders.right.style` | string | no | | -| `pageBorders.top` | any \\| any \\| any \\| any \\| any \\| any | no | One of: any, any, any, any, any, any | +| `pageBorders.top` | any \| any \| any \| any \| any \| any | no | One of: any, any, any, any, any, any | | `pageBorders.top.color` | string | no | | | `pageBorders.top.frame` | boolean | no | | | `pageBorders.top.shadow` | boolean | no | | diff --git a/apps/docs/document-api/reference/sections/set-page-borders.mdx b/apps/docs/document-api/reference/sections/set-page-borders.mdx index 1648bacdaa..b2400f62f2 100644 --- a/apps/docs/document-api/reference/sections/set-page-borders.mdx +++ b/apps/docs/document-api/reference/sections/set-page-borders.mdx @@ -26,8 +26,8 @@ Returns a SectionMutationResult receipt; reports NO_OP if page border configurat | Field | Type | Required | Description | | --- | --- | --- | --- | -| `borders` | any \\| any \\| any \\| any \\| any \\| any \\| any | yes | One of: any, any, any, any, any, any, any | -| `borders.bottom` | any \\| any \\| any \\| any \\| any \\| any | no | One of: any, any, any, any, any, any | +| `borders` | any \| any \| any \| any \| any \| any \| any | yes | One of: any, any, any, any, any, any, any | +| `borders.bottom` | any \| any \| any \| any \| any \| any | no | One of: any, any, any, any, any, any | | `borders.bottom.color` | string | no | | | `borders.bottom.frame` | boolean | no | | | `borders.bottom.shadow` | boolean | no | | @@ -35,7 +35,7 @@ Returns a SectionMutationResult receipt; reports NO_OP if page border configurat | `borders.bottom.space` | number | no | | | `borders.bottom.style` | string | no | | | `borders.display` | enum | no | `"allPages"`, `"firstPage"`, `"notFirstPage"` | -| `borders.left` | any \\| any \\| any \\| any \\| any \\| any | no | One of: any, any, any, any, any, any | +| `borders.left` | any \| any \| any \| any \| any \| any | no | One of: any, any, any, any, any, any | | `borders.left.color` | string | no | | | `borders.left.frame` | boolean | no | | | `borders.left.shadow` | boolean | no | | @@ -43,14 +43,14 @@ Returns a SectionMutationResult receipt; reports NO_OP if page border configurat | `borders.left.space` | number | no | | | `borders.left.style` | string | no | | | `borders.offsetFrom` | enum | no | `"page"`, `"text"` | -| `borders.right` | any \\| any \\| any \\| any \\| any \\| any | no | One of: any, any, any, any, any, any | +| `borders.right` | any \| any \| any \| any \| any \| any | no | One of: any, any, any, any, any, any | | `borders.right.color` | string | no | | | `borders.right.frame` | boolean | no | | | `borders.right.shadow` | boolean | no | | | `borders.right.size` | number | no | | | `borders.right.space` | number | no | | | `borders.right.style` | string | no | | -| `borders.top` | any \\| any \\| any \\| any \\| any \\| any | no | One of: any, any, any, any, any, any | +| `borders.top` | any \| any \| any \| any \| any \| any | no | One of: any, any, any, any, any, any | | `borders.top.color` | string | no | | | `borders.top.frame` | boolean | no | | | `borders.top.shadow` | boolean | no | | diff --git a/apps/docs/document-api/reference/selection/current.mdx b/apps/docs/document-api/reference/selection/current.mdx index 0ed358f60a..8ff7442d59 100644 --- a/apps/docs/document-api/reference/selection/current.mdx +++ b/apps/docs/document-api/reference/selection/current.mdx @@ -44,7 +44,7 @@ Returns a SelectionInfo with `empty`, `target` (TextTarget or null), `activeMark | `activeCommentIds` | string[] | yes | | | `activeMarks` | string[] | yes | | | `empty` | boolean | yes | | -| `target` | TextTarget \\| null | yes | One of: TextTarget, null | +| `target` | TextTarget \| null | yes | One of: TextTarget, null | | `text` | string | no | | ### Example response diff --git a/apps/docs/document-api/reference/styles/apply.mdx b/apps/docs/document-api/reference/styles/apply.mdx index 3feb22777f..6e03d01f36 100644 --- a/apps/docs/document-api/reference/styles/apply.mdx +++ b/apps/docs/document-api/reference/styles/apply.mdx @@ -286,59 +286,59 @@ Returns a StylesApplyReceipt with per-channel success/failure details for each p | `after.autoSpaceDN` | enum | no | `"on"`, `"off"`, `"inherit"` | | `after.bold` | enum | no | `"on"`, `"off"`, `"inherit"` | | `after.boldCs` | enum | no | `"on"`, `"off"`, `"inherit"` | -| `after.borders` | object \\| `"inherit"` | no | One of: object, `"inherit"` | -| `after.color` | object \\| `"inherit"` | no | One of: object, `"inherit"` | +| `after.borders` | object \| `"inherit"` | no | One of: object, `"inherit"` | +| `after.color` | object \| `"inherit"` | no | One of: object, `"inherit"` | | `after.contextualSpacing` | enum | no | `"on"`, `"off"`, `"inherit"` | | `after.dstrike` | enum | no | `"on"`, `"off"`, `"inherit"` | -| `after.eastAsianLayout` | object \\| `"inherit"` | no | One of: object, `"inherit"` | -| `after.effect` | string \\| `"inherit"` | no | One of: string, `"inherit"` | -| `after.em` | string \\| `"inherit"` | no | One of: string, `"inherit"` | +| `after.eastAsianLayout` | object \| `"inherit"` | no | One of: object, `"inherit"` | +| `after.effect` | string \| `"inherit"` | no | One of: string, `"inherit"` | +| `after.em` | string \| `"inherit"` | no | One of: string, `"inherit"` | | `after.emboss` | enum | no | `"on"`, `"off"`, `"inherit"` | -| `after.fitText` | object \\| `"inherit"` | no | One of: object, `"inherit"` | -| `after.fontFamily` | object \\| `"inherit"` | no | One of: object, `"inherit"` | -| `after.fontSize` | number \\| `"inherit"` | no | One of: number, `"inherit"` | -| `after.fontSizeCs` | number \\| `"inherit"` | no | One of: number, `"inherit"` | -| `after.framePr` | object \\| `"inherit"` | no | One of: object, `"inherit"` | +| `after.fitText` | object \| `"inherit"` | no | One of: object, `"inherit"` | +| `after.fontFamily` | object \| `"inherit"` | no | One of: object, `"inherit"` | +| `after.fontSize` | number \| `"inherit"` | no | One of: number, `"inherit"` | +| `after.fontSizeCs` | number \| `"inherit"` | no | One of: number, `"inherit"` | +| `after.framePr` | object \| `"inherit"` | no | One of: object, `"inherit"` | | `after.iCs` | enum | no | `"on"`, `"off"`, `"inherit"` | | `after.imprint` | enum | no | `"on"`, `"off"`, `"inherit"` | -| `after.indent` | object \\| `"inherit"` | no | One of: object, `"inherit"` | +| `after.indent` | object \| `"inherit"` | no | One of: object, `"inherit"` | | `after.italic` | enum | no | `"on"`, `"off"`, `"inherit"` | -| `after.justification` | string \\| `"inherit"` | no | One of: string, `"inherit"` | +| `after.justification` | string \| `"inherit"` | no | One of: string, `"inherit"` | | `after.keepLines` | enum | no | `"on"`, `"off"`, `"inherit"` | | `after.keepNext` | enum | no | `"on"`, `"off"`, `"inherit"` | -| `after.kern` | number \\| `"inherit"` | no | One of: number, `"inherit"` | +| `after.kern` | number \| `"inherit"` | no | One of: number, `"inherit"` | | `after.kinsoku` | enum | no | `"on"`, `"off"`, `"inherit"` | -| `after.lang` | object \\| `"inherit"` | no | One of: object, `"inherit"` | -| `after.letterSpacing` | number \\| `"inherit"` | no | One of: number, `"inherit"` | +| `after.lang` | object \| `"inherit"` | no | One of: object, `"inherit"` | +| `after.letterSpacing` | number \| `"inherit"` | no | One of: number, `"inherit"` | | `after.mirrorIndents` | enum | no | `"on"`, `"off"`, `"inherit"` | | `after.noProof` | enum | no | `"on"`, `"off"`, `"inherit"` | -| `after.numberingProperties` | object \\| `"inherit"` | no | One of: object, `"inherit"` | +| `after.numberingProperties` | object \| `"inherit"` | no | One of: object, `"inherit"` | | `after.outline` | enum | no | `"on"`, `"off"`, `"inherit"` | -| `after.outlineLvl` | number \\| `"inherit"` | no | One of: number, `"inherit"` | +| `after.outlineLvl` | number \| `"inherit"` | no | One of: number, `"inherit"` | | `after.overflowPunct` | enum | no | `"on"`, `"off"`, `"inherit"` | | `after.pageBreakBefore` | enum | no | `"on"`, `"off"`, `"inherit"` | -| `after.position` | number \\| `"inherit"` | no | One of: number, `"inherit"` | +| `after.position` | number \| `"inherit"` | no | One of: number, `"inherit"` | | `after.rightToLeft` | enum | no | `"on"`, `"off"`, `"inherit"` | -| `after.shading` | object \\| `"inherit"` | no | One of: object, `"inherit"` | +| `after.shading` | object \| `"inherit"` | no | One of: object, `"inherit"` | | `after.shadow` | enum | no | `"on"`, `"off"`, `"inherit"` | | `after.smallCaps` | enum | no | `"on"`, `"off"`, `"inherit"` | | `after.snapToGrid` | enum | no | `"on"`, `"off"`, `"inherit"` | -| `after.spacing` | object \\| `"inherit"` | no | One of: object, `"inherit"` | +| `after.spacing` | object \| `"inherit"` | no | One of: object, `"inherit"` | | `after.specVanish` | enum | no | `"on"`, `"off"`, `"inherit"` | | `after.strike` | enum | no | `"on"`, `"off"`, `"inherit"` | | `after.suppressAutoHyphens` | enum | no | `"on"`, `"off"`, `"inherit"` | | `after.suppressLineNumbers` | enum | no | `"on"`, `"off"`, `"inherit"` | | `after.suppressOverlap` | enum | no | `"on"`, `"off"`, `"inherit"` | -| `after.tabStops` | array \\| `"inherit"` | no | One of: array, `"inherit"` | -| `after.textAlignment` | string \\| `"inherit"` | no | One of: string, `"inherit"` | -| `after.textDirection` | string \\| `"inherit"` | no | One of: string, `"inherit"` | -| `after.textTransform` | string \\| `"inherit"` | no | One of: string, `"inherit"` | -| `after.textboxTightWrap` | string \\| `"inherit"` | no | One of: string, `"inherit"` | +| `after.tabStops` | array \| `"inherit"` | no | One of: array, `"inherit"` | +| `after.textAlignment` | string \| `"inherit"` | no | One of: string, `"inherit"` | +| `after.textDirection` | string \| `"inherit"` | no | One of: string, `"inherit"` | +| `after.textTransform` | string \| `"inherit"` | no | One of: string, `"inherit"` | +| `after.textboxTightWrap` | string \| `"inherit"` | no | One of: string, `"inherit"` | | `after.topLinePunct` | enum | no | `"on"`, `"off"`, `"inherit"` | -| `after.underline` | object \\| `"inherit"` | no | One of: object, `"inherit"` | +| `after.underline` | object \| `"inherit"` | no | One of: object, `"inherit"` | | `after.vanish` | enum | no | `"on"`, `"off"`, `"inherit"` | -| `after.vertAlign` | string \\| `"inherit"` | no | One of: string, `"inherit"` | -| `after.w` | number \\| `"inherit"` | no | One of: number, `"inherit"` | +| `after.vertAlign` | string \| `"inherit"` | no | One of: string, `"inherit"` | +| `after.w` | number \| `"inherit"` | no | One of: number, `"inherit"` | | `after.webHidden` | enum | no | `"on"`, `"off"`, `"inherit"` | | `after.widowControl` | enum | no | `"on"`, `"off"`, `"inherit"` | | `after.wordWrap` | enum | no | `"on"`, `"off"`, `"inherit"` | @@ -348,59 +348,59 @@ Returns a StylesApplyReceipt with per-channel success/failure details for each p | `before.autoSpaceDN` | enum | no | `"on"`, `"off"`, `"inherit"` | | `before.bold` | enum | no | `"on"`, `"off"`, `"inherit"` | | `before.boldCs` | enum | no | `"on"`, `"off"`, `"inherit"` | -| `before.borders` | object \\| `"inherit"` | no | One of: object, `"inherit"` | -| `before.color` | object \\| `"inherit"` | no | One of: object, `"inherit"` | +| `before.borders` | object \| `"inherit"` | no | One of: object, `"inherit"` | +| `before.color` | object \| `"inherit"` | no | One of: object, `"inherit"` | | `before.contextualSpacing` | enum | no | `"on"`, `"off"`, `"inherit"` | | `before.dstrike` | enum | no | `"on"`, `"off"`, `"inherit"` | -| `before.eastAsianLayout` | object \\| `"inherit"` | no | One of: object, `"inherit"` | -| `before.effect` | string \\| `"inherit"` | no | One of: string, `"inherit"` | -| `before.em` | string \\| `"inherit"` | no | One of: string, `"inherit"` | +| `before.eastAsianLayout` | object \| `"inherit"` | no | One of: object, `"inherit"` | +| `before.effect` | string \| `"inherit"` | no | One of: string, `"inherit"` | +| `before.em` | string \| `"inherit"` | no | One of: string, `"inherit"` | | `before.emboss` | enum | no | `"on"`, `"off"`, `"inherit"` | -| `before.fitText` | object \\| `"inherit"` | no | One of: object, `"inherit"` | -| `before.fontFamily` | object \\| `"inherit"` | no | One of: object, `"inherit"` | -| `before.fontSize` | number \\| `"inherit"` | no | One of: number, `"inherit"` | -| `before.fontSizeCs` | number \\| `"inherit"` | no | One of: number, `"inherit"` | -| `before.framePr` | object \\| `"inherit"` | no | One of: object, `"inherit"` | +| `before.fitText` | object \| `"inherit"` | no | One of: object, `"inherit"` | +| `before.fontFamily` | object \| `"inherit"` | no | One of: object, `"inherit"` | +| `before.fontSize` | number \| `"inherit"` | no | One of: number, `"inherit"` | +| `before.fontSizeCs` | number \| `"inherit"` | no | One of: number, `"inherit"` | +| `before.framePr` | object \| `"inherit"` | no | One of: object, `"inherit"` | | `before.iCs` | enum | no | `"on"`, `"off"`, `"inherit"` | | `before.imprint` | enum | no | `"on"`, `"off"`, `"inherit"` | -| `before.indent` | object \\| `"inherit"` | no | One of: object, `"inherit"` | +| `before.indent` | object \| `"inherit"` | no | One of: object, `"inherit"` | | `before.italic` | enum | no | `"on"`, `"off"`, `"inherit"` | -| `before.justification` | string \\| `"inherit"` | no | One of: string, `"inherit"` | +| `before.justification` | string \| `"inherit"` | no | One of: string, `"inherit"` | | `before.keepLines` | enum | no | `"on"`, `"off"`, `"inherit"` | | `before.keepNext` | enum | no | `"on"`, `"off"`, `"inherit"` | -| `before.kern` | number \\| `"inherit"` | no | One of: number, `"inherit"` | +| `before.kern` | number \| `"inherit"` | no | One of: number, `"inherit"` | | `before.kinsoku` | enum | no | `"on"`, `"off"`, `"inherit"` | -| `before.lang` | object \\| `"inherit"` | no | One of: object, `"inherit"` | -| `before.letterSpacing` | number \\| `"inherit"` | no | One of: number, `"inherit"` | +| `before.lang` | object \| `"inherit"` | no | One of: object, `"inherit"` | +| `before.letterSpacing` | number \| `"inherit"` | no | One of: number, `"inherit"` | | `before.mirrorIndents` | enum | no | `"on"`, `"off"`, `"inherit"` | | `before.noProof` | enum | no | `"on"`, `"off"`, `"inherit"` | -| `before.numberingProperties` | object \\| `"inherit"` | no | One of: object, `"inherit"` | +| `before.numberingProperties` | object \| `"inherit"` | no | One of: object, `"inherit"` | | `before.outline` | enum | no | `"on"`, `"off"`, `"inherit"` | -| `before.outlineLvl` | number \\| `"inherit"` | no | One of: number, `"inherit"` | +| `before.outlineLvl` | number \| `"inherit"` | no | One of: number, `"inherit"` | | `before.overflowPunct` | enum | no | `"on"`, `"off"`, `"inherit"` | | `before.pageBreakBefore` | enum | no | `"on"`, `"off"`, `"inherit"` | -| `before.position` | number \\| `"inherit"` | no | One of: number, `"inherit"` | +| `before.position` | number \| `"inherit"` | no | One of: number, `"inherit"` | | `before.rightToLeft` | enum | no | `"on"`, `"off"`, `"inherit"` | -| `before.shading` | object \\| `"inherit"` | no | One of: object, `"inherit"` | +| `before.shading` | object \| `"inherit"` | no | One of: object, `"inherit"` | | `before.shadow` | enum | no | `"on"`, `"off"`, `"inherit"` | | `before.smallCaps` | enum | no | `"on"`, `"off"`, `"inherit"` | | `before.snapToGrid` | enum | no | `"on"`, `"off"`, `"inherit"` | -| `before.spacing` | object \\| `"inherit"` | no | One of: object, `"inherit"` | +| `before.spacing` | object \| `"inherit"` | no | One of: object, `"inherit"` | | `before.specVanish` | enum | no | `"on"`, `"off"`, `"inherit"` | | `before.strike` | enum | no | `"on"`, `"off"`, `"inherit"` | | `before.suppressAutoHyphens` | enum | no | `"on"`, `"off"`, `"inherit"` | | `before.suppressLineNumbers` | enum | no | `"on"`, `"off"`, `"inherit"` | | `before.suppressOverlap` | enum | no | `"on"`, `"off"`, `"inherit"` | -| `before.tabStops` | array \\| `"inherit"` | no | One of: array, `"inherit"` | -| `before.textAlignment` | string \\| `"inherit"` | no | One of: string, `"inherit"` | -| `before.textDirection` | string \\| `"inherit"` | no | One of: string, `"inherit"` | -| `before.textTransform` | string \\| `"inherit"` | no | One of: string, `"inherit"` | -| `before.textboxTightWrap` | string \\| `"inherit"` | no | One of: string, `"inherit"` | +| `before.tabStops` | array \| `"inherit"` | no | One of: array, `"inherit"` | +| `before.textAlignment` | string \| `"inherit"` | no | One of: string, `"inherit"` | +| `before.textDirection` | string \| `"inherit"` | no | One of: string, `"inherit"` | +| `before.textTransform` | string \| `"inherit"` | no | One of: string, `"inherit"` | +| `before.textboxTightWrap` | string \| `"inherit"` | no | One of: string, `"inherit"` | | `before.topLinePunct` | enum | no | `"on"`, `"off"`, `"inherit"` | -| `before.underline` | object \\| `"inherit"` | no | One of: object, `"inherit"` | +| `before.underline` | object \| `"inherit"` | no | One of: object, `"inherit"` | | `before.vanish` | enum | no | `"on"`, `"off"`, `"inherit"` | -| `before.vertAlign` | string \\| `"inherit"` | no | One of: string, `"inherit"` | -| `before.w` | number \\| `"inherit"` | no | One of: number, `"inherit"` | +| `before.vertAlign` | string \| `"inherit"` | no | One of: string, `"inherit"` | +| `before.w` | number \| `"inherit"` | no | One of: number, `"inherit"` | | `before.webHidden` | enum | no | `"on"`, `"off"`, `"inherit"` | | `before.widowControl` | enum | no | `"on"`, `"off"`, `"inherit"` | | `before.wordWrap` | enum | no | `"on"`, `"off"`, `"inherit"` | diff --git a/apps/docs/document-api/reference/styles/paragraph/clear-style.mdx b/apps/docs/document-api/reference/styles/paragraph/clear-style.mdx index 68acc8e661..e571bd9526 100644 --- a/apps/docs/document-api/reference/styles/paragraph/clear-style.mdx +++ b/apps/docs/document-api/reference/styles/paragraph/clear-style.mdx @@ -26,7 +26,7 @@ Returns a ParagraphMutationResult; reports NO_OP if no style is set. | Field | Type | Required | Description | | --- | --- | --- | --- | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Example request @@ -47,9 +47,9 @@ Returns a ParagraphMutationResult; reports NO_OP if no style is set. | Field | Type | Required | Description | | --- | --- | --- | --- | | `resolution` | object | yes | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `true` | yes | Constant: `true` | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Variant 2 (success=false) @@ -60,7 +60,7 @@ Returns a ParagraphMutationResult; reports NO_OP if no style is set. | `failure.details` | any | no | | | `failure.message` | string | yes | | | `resolution` | object | no | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `false` | yes | Constant: `false` | ### Example response diff --git a/apps/docs/document-api/reference/styles/paragraph/set-style.mdx b/apps/docs/document-api/reference/styles/paragraph/set-style.mdx index a80b4c15ec..6c65c1c104 100644 --- a/apps/docs/document-api/reference/styles/paragraph/set-style.mdx +++ b/apps/docs/document-api/reference/styles/paragraph/set-style.mdx @@ -27,7 +27,7 @@ Returns a ParagraphMutationResult; reports NO_OP if the style already matches. W | Field | Type | Required | Description | | --- | --- | --- | --- | | `styleId` | string | yes | | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Example request @@ -49,9 +49,9 @@ Returns a ParagraphMutationResult; reports NO_OP if the style already matches. W | Field | Type | Required | Description | | --- | --- | --- | --- | | `resolution` | object | yes | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `true` | yes | Constant: `true` | -| `target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | yes | One of: ParagraphAddress, HeadingAddress, ListItemAddress | ### Variant 2 (success=false) @@ -62,7 +62,7 @@ Returns a ParagraphMutationResult; reports NO_OP if the style already matches. W | `failure.details` | any | no | | | `failure.message` | string | yes | | | `resolution` | object | no | | -| `resolution.target` | ParagraphAddress \\| HeadingAddress \\| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | +| `resolution.target` | ParagraphAddress \| HeadingAddress \| ListItemAddress | no | One of: ParagraphAddress, HeadingAddress, ListItemAddress | | `success` | `false` | yes | Constant: `false` | ### Example response diff --git a/apps/docs/document-api/reference/tables/convert-from-text.mdx b/apps/docs/document-api/reference/tables/convert-from-text.mdx index a8a2dca7bd..547579fd77 100644 --- a/apps/docs/document-api/reference/tables/convert-from-text.mdx +++ b/apps/docs/document-api/reference/tables/convert-from-text.mdx @@ -29,7 +29,7 @@ Returns a TableMutationResult receipt confirming text was converted into a table | Field | Type | Required | Description | | --- | --- | --- | --- | | `columns` | integer | no | | -| `delimiter` | enum \\| object | no | One of: enum, object | +| `delimiter` | enum \| object | no | One of: enum, object | | `inferColumns` | boolean | no | | | `target` | BlockNodeAddress | yes | BlockNodeAddress | | `target.kind` | `"block"` | yes | Constant: `"block"` | @@ -41,7 +41,7 @@ Returns a TableMutationResult receipt confirming text was converted into a table | Field | Type | Required | Description | | --- | --- | --- | --- | | `columns` | integer | no | | -| `delimiter` | enum \\| object | no | One of: enum, object | +| `delimiter` | enum \| object | no | One of: enum, object | | `inferColumns` | boolean | no | | | `nodeId` | string | yes | | diff --git a/apps/docs/document-api/reference/tables/get-properties.mdx b/apps/docs/document-api/reference/tables/get-properties.mdx index 1c05263439..c377dc227c 100644 --- a/apps/docs/document-api/reference/tables/get-properties.mdx +++ b/apps/docs/document-api/reference/tables/get-properties.mdx @@ -62,12 +62,12 @@ Returns a TablesGetPropertiesOutput with direct table layout and style state, in | `alignment` | enum | no | `"left"`, `"center"`, `"right"` | | `autoFitMode` | enum | no | `"fixedWidth"`, `"fitContents"`, `"fitWindow"` | | `borders` | object | no | | -| `borders.bottom` | object \\| null | no | One of: object, null | -| `borders.insideH` | object \\| null | no | One of: object, null | -| `borders.insideV` | object \\| null | no | One of: object, null | -| `borders.left` | object \\| null | no | One of: object, null | -| `borders.right` | object \\| null | no | One of: object, null | -| `borders.top` | object \\| null | no | One of: object, null | +| `borders.bottom` | object \| null | no | One of: object, null | +| `borders.insideH` | object \| null | no | One of: object, null | +| `borders.insideV` | object \| null | no | One of: object, null | +| `borders.left` | object \| null | no | One of: object, null | +| `borders.right` | object \| null | no | One of: object, null | +| `borders.top` | object \| null | no | One of: object, null | | `cellSpacingPt` | number | no | | | `defaultCellMargins` | object | no | | | `defaultCellMargins.bottomPt` | number | no | | diff --git a/apps/docs/document-api/reference/tables/get-styles.mdx b/apps/docs/document-api/reference/tables/get-styles.mdx index 4804efe649..94e636b5f1 100644 --- a/apps/docs/document-api/reference/tables/get-styles.mdx +++ b/apps/docs/document-api/reference/tables/get-styles.mdx @@ -37,8 +37,8 @@ _No fields._ | Field | Type | Required | Description | | --- | --- | --- | --- | | `effectiveDefaultSource` | string | yes | | -| `effectiveDefaultStyleId` | any | yes | | -| `explicitDefaultStyleId` | any | yes | | +| `effectiveDefaultStyleId` | string \| null | yes | | +| `explicitDefaultStyleId` | string \| null | yes | | | `styles` | object[] | yes | | ### Example response @@ -46,11 +46,11 @@ _No fields._ ```json { "effectiveDefaultSource": "example", - "effectiveDefaultStyleId": {}, - "explicitDefaultStyleId": {}, + "effectiveDefaultStyleId": "example", + "explicitDefaultStyleId": "example", "styles": [ { - "basedOn": {}, + "basedOn": "example", "conditionalRegions": [ "example" ], @@ -58,9 +58,9 @@ _No fields._ "id": "id-001", "isCustom": true, "isDefault": true, - "name": {}, + "name": "example", "quickFormat": true, - "uiPriority": {} + "uiPriority": 1 } ] } diff --git a/apps/docs/document-api/reference/tables/move.mdx b/apps/docs/document-api/reference/tables/move.mdx index fe7e1ebba7..4991c33f61 100644 --- a/apps/docs/document-api/reference/tables/move.mdx +++ b/apps/docs/document-api/reference/tables/move.mdx @@ -28,7 +28,7 @@ Returns a TableMutationResult receipt; reports NO_OP if the table is already at | Field | Type | Required | Description | | --- | --- | --- | --- | -| `destination` | object(kind="documentStart") \\| object(kind="documentEnd") \\| object(kind="before") \\| object(kind="after") \\| object(kind="before") \\| object(kind="after") | yes | One of: object(kind="documentStart"), object(kind="documentEnd"), object(kind="before"), object(kind="after"), object(kind="before"), object(kind="after") | +| `destination` | object(kind="documentStart") \| object(kind="documentEnd") \| object(kind="before") \| object(kind="after") \| object(kind="before") \| object(kind="after") | yes | One of: object(kind="documentStart"), object(kind="documentEnd"), object(kind="before"), object(kind="after"), object(kind="before"), object(kind="after") | | `target` | TableAddress | yes | TableAddress | | `target.kind` | `"block"` | yes | Constant: `"block"` | | `target.nodeId` | string | yes | | @@ -38,7 +38,7 @@ Returns a TableMutationResult receipt; reports NO_OP if the table is already at | Field | Type | Required | Description | | --- | --- | --- | --- | -| `destination` | object(kind="documentStart") \\| object(kind="documentEnd") \\| object(kind="before") \\| object(kind="after") \\| object(kind="before") \\| object(kind="after") | yes | One of: object(kind="documentStart"), object(kind="documentEnd"), object(kind="before"), object(kind="after"), object(kind="before"), object(kind="after") | +| `destination` | object(kind="documentStart") \| object(kind="documentEnd") \| object(kind="before") \| object(kind="after") \| object(kind="before") \| object(kind="after") | yes | One of: object(kind="documentStart"), object(kind="documentEnd"), object(kind="before"), object(kind="after"), object(kind="before"), object(kind="after") | | `nodeId` | string | yes | | ### Example request diff --git a/apps/docs/document-api/reference/tables/set-borders.mdx b/apps/docs/document-api/reference/tables/set-borders.mdx index 2ea83a268b..7270f68829 100644 --- a/apps/docs/document-api/reference/tables/set-borders.mdx +++ b/apps/docs/document-api/reference/tables/set-borders.mdx @@ -29,7 +29,7 @@ Returns a TableMutationResult receipt. Does not perform NO_OP detection. | Field | Type | Required | Description | | --- | --- | --- | --- | | `applyTo` | enum | yes | `"all"`, `"outside"`, `"inside"`, `"top"`, `"bottom"`, `"left"`, `"right"`, `"insideH"`, `"insideV"` | -| `border` | object \\| null | yes | One of: object, null | +| `border` | object \| null | yes | One of: object, null | | `mode` | `"applyTo"` | yes | Constant: `"applyTo"` | | `target` | BlockNodeAddress | yes | BlockNodeAddress | | `target.kind` | `"block"` | yes | Constant: `"block"` | @@ -41,7 +41,7 @@ Returns a TableMutationResult receipt. Does not perform NO_OP detection. | Field | Type | Required | Description | | --- | --- | --- | --- | | `applyTo` | enum | yes | `"all"`, `"outside"`, `"inside"`, `"top"`, `"bottom"`, `"left"`, `"right"`, `"insideH"`, `"insideV"` | -| `border` | object \\| null | yes | One of: object, null | +| `border` | object \| null | yes | One of: object, null | | `mode` | `"applyTo"` | yes | Constant: `"applyTo"` | | `nodeId` | string | yes | | @@ -50,12 +50,12 @@ Returns a TableMutationResult receipt. Does not perform NO_OP detection. | Field | Type | Required | Description | | --- | --- | --- | --- | | `edges` | object | yes | | -| `edges.bottom` | object \\| null | no | One of: object, null | -| `edges.insideH` | object \\| null | no | One of: object, null | -| `edges.insideV` | object \\| null | no | One of: object, null | -| `edges.left` | object \\| null | no | One of: object, null | -| `edges.right` | object \\| null | no | One of: object, null | -| `edges.top` | object \\| null | no | One of: object, null | +| `edges.bottom` | object \| null | no | One of: object, null | +| `edges.insideH` | object \| null | no | One of: object, null | +| `edges.insideV` | object \| null | no | One of: object, null | +| `edges.left` | object \| null | no | One of: object, null | +| `edges.right` | object \| null | no | One of: object, null | +| `edges.top` | object \| null | no | One of: object, null | | `mode` | `"edges"` | yes | Constant: `"edges"` | | `target` | BlockNodeAddress | yes | BlockNodeAddress | | `target.kind` | `"block"` | yes | Constant: `"block"` | @@ -67,12 +67,12 @@ Returns a TableMutationResult receipt. Does not perform NO_OP detection. | Field | Type | Required | Description | | --- | --- | --- | --- | | `edges` | object | yes | | -| `edges.bottom` | object \\| null | no | One of: object, null | -| `edges.insideH` | object \\| null | no | One of: object, null | -| `edges.insideV` | object \\| null | no | One of: object, null | -| `edges.left` | object \\| null | no | One of: object, null | -| `edges.right` | object \\| null | no | One of: object, null | -| `edges.top` | object \\| null | no | One of: object, null | +| `edges.bottom` | object \| null | no | One of: object, null | +| `edges.insideH` | object \| null | no | One of: object, null | +| `edges.insideV` | object \| null | no | One of: object, null | +| `edges.left` | object \| null | no | One of: object, null | +| `edges.right` | object \| null | no | One of: object, null | +| `edges.top` | object \| null | no | One of: object, null | | `mode` | `"edges"` | yes | Constant: `"edges"` | | `nodeId` | string | yes | | diff --git a/apps/docs/document-api/reference/tables/set-shading.mdx b/apps/docs/document-api/reference/tables/set-shading.mdx index 587a3a1b97..ea15f7c243 100644 --- a/apps/docs/document-api/reference/tables/set-shading.mdx +++ b/apps/docs/document-api/reference/tables/set-shading.mdx @@ -28,7 +28,7 @@ Returns a TableMutationResult receipt; reports NO_OP if shading already matches. | Field | Type | Required | Description | | --- | --- | --- | --- | -| `color` | string \\| null | yes | One of: string, null | +| `color` | string \| null | yes | One of: string, null | | `target` | TableOrCellAddress | yes | TableOrCellAddress | | `target.kind` | `"block"` | yes | Constant: `"block"` | | `target.nodeId` | string | yes | | @@ -38,7 +38,7 @@ Returns a TableMutationResult receipt; reports NO_OP if shading already matches. | Field | Type | Required | Description | | --- | --- | --- | --- | -| `color` | string \\| null | yes | One of: string, null | +| `color` | string \| null | yes | One of: string, null | | `nodeId` | string | yes | | ### Example request diff --git a/apps/docs/document-api/reference/tables/set-table-options.mdx b/apps/docs/document-api/reference/tables/set-table-options.mdx index 620802a82a..79cae4a8be 100644 --- a/apps/docs/document-api/reference/tables/set-table-options.mdx +++ b/apps/docs/document-api/reference/tables/set-table-options.mdx @@ -28,7 +28,7 @@ Returns a TableMutationResult receipt; reports NO_OP if the provided values alre | Field | Type | Required | Description | | --- | --- | --- | --- | -| `cellSpacingPt` | number \\| null | no | One of: number, null | +| `cellSpacingPt` | number \| null | no | One of: number, null | | `defaultCellMargins` | object | no | | | `defaultCellMargins.bottomPt` | number | no | | | `defaultCellMargins.leftPt` | number | no | | @@ -43,7 +43,7 @@ Returns a TableMutationResult receipt; reports NO_OP if the provided values alre | Field | Type | Required | Description | | --- | --- | --- | --- | -| `cellSpacingPt` | number \\| null | no | One of: number, null | +| `cellSpacingPt` | number \| null | no | One of: number, null | | `defaultCellMargins` | object | no | | | `defaultCellMargins.bottomPt` | number | no | | | `defaultCellMargins.leftPt` | number | no | | diff --git a/apps/docs/document-api/reference/track-changes/decide.mdx b/apps/docs/document-api/reference/track-changes/decide.mdx index cfd98e37fb..d44816985b 100644 --- a/apps/docs/document-api/reference/track-changes/decide.mdx +++ b/apps/docs/document-api/reference/track-changes/decide.mdx @@ -1,14 +1,14 @@ --- title: trackChanges.decide sidebarTitle: trackChanges.decide -description: "Accept or reject a tracked change (by ID or scope: all)." +description: "Accept or reject tracked changes by ID, range, or scope: all (optionally filtered by story)." --- {/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */} ## Summary -Accept or reject a tracked change (by ID or scope: all). +Accept or reject tracked changes by ID, range, or scope: all (optionally filtered by story). - Operation ID: `trackChanges.decide` - API member path: `editor.doc.trackChanges.decide(...)` @@ -20,14 +20,14 @@ Accept or reject a tracked change (by ID or scope: all). ## Expected result -Returns a Receipt confirming the decision was applied; reports NO_OP if the change was already resolved. +Returns a Receipt confirming the decision was applied; reports NO_OP if the change was already resolved and typed failures for unsupported or denied tracked-change decisions. ## Input fields | Field | Type | Required | Description | | --- | --- | --- | --- | | `decision` | enum | yes | `"accept"`, `"reject"` | -| `target` | object \\| object | yes | One of: object, object | +| `target` | object \| object(kind="range") \| object | yes | One of: object, object(kind="range"), object | ### Example request @@ -60,7 +60,7 @@ Returns a Receipt confirming the decision was applied; reports NO_OP if the chan | Field | Type | Required | Description | | --- | --- | --- | --- | | `failure` | object | yes | | -| `failure.code` | enum | yes | `"NO_OP"` | +| `failure.code` | enum | yes | `"NO_OP"`, `"INVALID_TARGET"`, `"TARGET_NOT_FOUND"`, `"CAPABILITY_UNAVAILABLE"`, `"PERMISSION_DENIED"`, `"PRECONDITION_FAILED"`, `"COMMENT_CASCADE_PARTIAL"` | | `failure.details` | any | no | | | `failure.message` | string | yes | | | `success` | `false` | yes | Constant: `false` | @@ -97,6 +97,12 @@ Returns a Receipt confirming the decision was applied; reports NO_OP if the chan ## Non-applied failure codes - `NO_OP` +- `INVALID_TARGET` +- `TARGET_NOT_FOUND` +- `CAPABILITY_UNAVAILABLE` +- `PERMISSION_DENIED` +- `PRECONDITION_FAILED` +- `COMMENT_CASCADE_PARTIAL` ## Raw schemas @@ -128,6 +134,29 @@ Returns a Receipt confirming the decision was applied; reports NO_OP if the chan ], "type": "object" }, + { + "additionalProperties": false, + "properties": { + "kind": { + "const": "range" + }, + "part": { + "description": "Optional part discriminator for the range target.", + "type": "string" + }, + "range": { + "$ref": "#/$defs/TextTarget" + }, + "story": { + "$ref": "#/$defs/StoryLocator" + } + }, + "required": [ + "kind", + "range" + ], + "type": "object" + }, { "additionalProperties": false, "properties": { @@ -135,6 +164,17 @@ Returns a Receipt confirming the decision was applied; reports NO_OP if the chan "enum": [ "all" ] + }, + "story": { + "description": "Optional explicit bulk filter. Omit or pass 'all' to target every revision-capable story, or pass a StoryLocator to scope the decision to one story.", + "oneOf": [ + { + "$ref": "#/$defs/StoryLocator" + }, + { + "const": "all" + } + ] } }, "required": [ @@ -169,7 +209,13 @@ Returns a Receipt confirming the decision was applied; reports NO_OP if the chan "properties": { "code": { "enum": [ - "NO_OP" + "NO_OP", + "INVALID_TARGET", + "TARGET_NOT_FOUND", + "CAPABILITY_UNAVAILABLE", + "PERMISSION_DENIED", + "PRECONDITION_FAILED", + "COMMENT_CASCADE_PARTIAL" ] }, "details": {}, @@ -216,7 +262,13 @@ Returns a Receipt confirming the decision was applied; reports NO_OP if the chan "properties": { "code": { "enum": [ - "NO_OP" + "NO_OP", + "INVALID_TARGET", + "TARGET_NOT_FOUND", + "CAPABILITY_UNAVAILABLE", + "PERMISSION_DENIED", + "PRECONDITION_FAILED", + "COMMENT_CASCADE_PARTIAL" ] }, "details": {}, diff --git a/apps/docs/document-api/reference/track-changes/get.mdx b/apps/docs/document-api/reference/track-changes/get.mdx index f3d9ab8a54..6ed1052cbb 100644 --- a/apps/docs/document-api/reference/track-changes/get.mdx +++ b/apps/docs/document-api/reference/track-changes/get.mdx @@ -20,7 +20,7 @@ Retrieve a single tracked change by ID. ## Expected result -Returns a TrackChangeInfo object with the change type, author, date, affected content, and raw imported Word OOXML revision IDs (`w:id`) when available. +Returns a TrackChangeInfo object with the change type (`insert`, `delete`, `replacement`, `format`), author, date, affected content, and raw imported Word OOXML revision IDs (`w:id`) when available. ## Input fields @@ -54,9 +54,13 @@ Returns a TrackChangeInfo object with the change type, author, date, affected co | `authorEmail` | string | no | | | `authorImage` | string | no | | | `date` | string | no | | +| `deletedText` | string | no | | | `excerpt` | string | no | | +| `grouping` | enum | no | `"standalone"`, `"replacement-pair"`, `"unknown"` | | `id` | string | yes | | -| `type` | enum | yes | `"insert"`, `"delete"`, `"format"` | +| `insertedText` | string | no | | +| `pairedWithChangeId` | string \| null | no | | +| `type` | enum | yes | `"insert"`, `"delete"`, `"replacement"`, `"format"` | | `wordRevisionIds` | object | no | | | `wordRevisionIds.delete` | string | no | | | `wordRevisionIds.format` | string | no | | @@ -75,13 +79,10 @@ Returns a TrackChangeInfo object with the change type, author, date, affected co "storyType": "body" } }, - "author": "Jane Doe", + "grouping": "standalone", "id": "id-001", - "type": "insert", - "wordRevisionIds": { - "delete": "example", - "insert": "example" - } + "pairedWithChangeId": null, + "type": "insert" } ``` @@ -135,16 +136,36 @@ Returns a TrackChangeInfo object with the change type, author, date, affected co "date": { "type": "string" }, + "deletedText": { + "type": "string" + }, "excerpt": { "type": "string" }, + "grouping": { + "enum": [ + "standalone", + "replacement-pair", + "unknown" + ] + }, "id": { "type": "string" }, + "insertedText": { + "type": "string" + }, + "pairedWithChangeId": { + "type": [ + "string", + "null" + ] + }, "type": { "enum": [ "insert", "delete", + "replacement", "format" ] }, diff --git a/apps/docs/document-api/reference/track-changes/list.mdx b/apps/docs/document-api/reference/track-changes/list.mdx index 6411a19b16..2725a23a83 100644 --- a/apps/docs/document-api/reference/track-changes/list.mdx +++ b/apps/docs/document-api/reference/track-changes/list.mdx @@ -20,16 +20,16 @@ List all tracked changes in the document. ## Expected result -Returns a TrackChangesListResult with tracked change entries, total count, and raw imported Word OOXML revision IDs (`w:id`) when available. +Returns a TrackChangesListResult with tracked change entries (`insert`, `delete`, `replacement`, `format`), total count, and raw imported Word OOXML revision IDs (`w:id`) when available. ## Input fields | Field | Type | Required | Description | | --- | --- | --- | --- | -| `in` | StoryLocator \\| `"all"` | no | One of: StoryLocator, `"all"` | +| `in` | StoryLocator \| `"all"` | no | One of: StoryLocator, `"all"` | | `limit` | integer | no | | | `offset` | integer | no | | -| `type` | enum | no | `"insert"`, `"delete"`, `"format"` | +| `type` | enum | no | `"insert"`, `"delete"`, `"replacement"`, `"format"` | ### Example request @@ -68,18 +68,15 @@ Returns a TrackChangesListResult with tracked change entries, total count, and r "storyType": "body" } }, - "author": "Jane Doe", + "grouping": "standalone", "handle": { "ref": "handle:abc123", "refStability": "stable", "targetKind": "text" }, "id": "id-001", - "type": "insert", - "wordRevisionIds": { - "delete": "example", - "insert": "example" - } + "pairedWithChangeId": null, + "type": "insert" } ], "page": { @@ -126,10 +123,11 @@ Returns a TrackChangesListResult with tracked change entries, total count, and r "type": "integer" }, "type": { - "description": "Filter by change type: 'insert', 'delete', or 'format'.", + "description": "Filter by change type: 'insert', 'delete', 'replacement', or 'format'.", "enum": [ "insert", "delete", + "replacement", "format" ] } @@ -166,19 +164,39 @@ Returns a TrackChangesListResult with tracked change entries, total count, and r "date": { "type": "string" }, + "deletedText": { + "type": "string" + }, "excerpt": { "type": "string" }, + "grouping": { + "enum": [ + "standalone", + "replacement-pair", + "unknown" + ] + }, "handle": { "$ref": "#/$defs/ResolvedHandle" }, "id": { "type": "string" }, + "insertedText": { + "type": "string" + }, + "pairedWithChangeId": { + "type": [ + "string", + "null" + ] + }, "type": { "enum": [ "insert", "delete", + "replacement", "format" ] }, diff --git a/apps/docs/document-engine/sdks.mdx b/apps/docs/document-engine/sdks.mdx index c886ed7c82..979ff60b7c 100644 --- a/apps/docs/document-engine/sdks.mdx +++ b/apps/docs/document-engine/sdks.mdx @@ -746,6 +746,7 @@ The SDKs expose all operations from the [Document API](/document-api/overview) p | Operation | CLI command | Description | | --- | --- | --- | +| `doc.formatRange` | `format-range` | Legacy root-level alias for inline range formatting. Routes to `format.apply` for compatibility with older callers. | | `doc.format.apply` | `format apply` | Apply inline run-property patch changes to the target range with explicit set/clear semantics. | | `doc.format.bold` | `format bold` | Set or clear the `bold` inline run property on the target text range. | | `doc.format.italic` | `format italic` | Set or clear the `italic` inline run property on the target text range. | @@ -1006,7 +1007,7 @@ The SDKs expose all operations from the [Document API](/document-api/overview) p | --- | --- | --- | | `doc.trackChanges.list` | `track-changes list` | List all tracked changes in the document. | | `doc.trackChanges.get` | `track-changes get` | Retrieve a single tracked change by ID. | -| `doc.trackChanges.decide` | `track-changes decide` | Accept or reject a tracked change (by ID or scope: all). | +| `doc.trackChanges.decide` | `track-changes decide` | Accept or reject tracked changes by ID, range, or scope: all (optionally filtered by story). | #### History @@ -1021,7 +1022,7 @@ The SDKs expose all operations from the [Document API](/document-api/overview) p | Operation | CLI command | Description | | --- | --- | --- | | `client.open` | `open` | Open a document and create a persistent editing session. Optionally override the document body with contentOverride + overrideType (markdown, html, or text). | -| `doc.save` | `save` | Save the current session to the original file or a new path. | +| `doc.save` | `save` | Save the current session to the original file or a new path. Supports explicit review-preserving and final export modes. | | `doc.close` | `close` | Close the active editing session and clean up resources. | #### Client @@ -1224,6 +1225,7 @@ The SDKs expose all operations from the [Document API](/document-api/overview) p | Operation | CLI command | Description | | --- | --- | --- | +| `doc.format_range` | `format-range` | Legacy root-level alias for inline range formatting. Routes to `format.apply` for compatibility with older callers. | | `doc.format.apply` | `format apply` | Apply inline run-property patch changes to the target range with explicit set/clear semantics. | | `doc.format.bold` | `format bold` | Set or clear the `bold` inline run property on the target text range. | | `doc.format.italic` | `format italic` | Set or clear the `italic` inline run property on the target text range. | @@ -1484,7 +1486,7 @@ The SDKs expose all operations from the [Document API](/document-api/overview) p | --- | --- | --- | | `doc.track_changes.list` | `track-changes list` | List all tracked changes in the document. | | `doc.track_changes.get` | `track-changes get` | Retrieve a single tracked change by ID. | -| `doc.track_changes.decide` | `track-changes decide` | Accept or reject a tracked change (by ID or scope: all). | +| `doc.track_changes.decide` | `track-changes decide` | Accept or reject tracked changes by ID, range, or scope: all (optionally filtered by story). | #### History @@ -1499,7 +1501,7 @@ The SDKs expose all operations from the [Document API](/document-api/overview) p | Operation | CLI command | Description | | --- | --- | --- | | `client.open` | `open` | Open a document and create a persistent editing session. Optionally override the document body with contentOverride + overrideType (markdown, html, or text). | -| `doc.save` | `save` | Save the current session to the original file or a new path. | +| `doc.save` | `save` | Save the current session to the original file or a new path. Supports explicit review-preserving and final export modes. | | `doc.close` | `close` | Close the active editing session and clean up resources. | #### Client diff --git a/apps/docs/editor/collaboration/configuration.mdx b/apps/docs/editor/collaboration/configuration.mdx index 60b2baea22..e9661ebbe7 100644 --- a/apps/docs/editor/collaboration/configuration.mdx +++ b/apps/docs/editor/collaboration/configuration.mdx @@ -152,7 +152,7 @@ onLocked: ({ isLocked, lockedBy }) => { | Property | Type | Description | | ---------- | --------- | ------------------------------ | | `isLocked` | `boolean` | Whether the document is locked | -| `lockedBy` | `Object` | User who locked the document | +| `lockedBy` | `User \| null` | User who locked the document. `null` when unlocked or when the lock was set without an attributed user; always null-check before reading `lockedBy.name`. | ## Provider events diff --git a/apps/docs/editor/custom-ui/content-controls.mdx b/apps/docs/editor/custom-ui/content-controls.mdx new file mode 100644 index 0000000000..c0a94bcce8 --- /dev/null +++ b/apps/docs/editor/custom-ui/content-controls.mdx @@ -0,0 +1,62 @@ +--- +title: 'Content controls' +description: 'Build your own UI for Word content controls (SDT fields): turn off the built-in chrome, react to the active control, and anchor your UI with getRect().' +--- + +Turn off SuperDoc's built-in chrome, listen for the active control, and anchor your own UI over it. The control wrappers and `data-sdt-*` attributes stay in the DOM, so your UI has something to attach to. + +## A minimal field chip + +```ts +import { SuperDoc } from 'superdoc'; +import { createSuperDocUI } from 'superdoc/ui'; + +new SuperDoc({ + selector: '#editor', + document: '/contract.docx', + // Turn off the built-in labels, borders, and hover/selection chrome. + modules: { contentControls: { chrome: 'none' } }, + onReady: ({ superdoc }) => { + const ui = createSuperDocUI({ superdoc }); + + superdoc.on('content-control:active-change', ({ active }) => { + if (!active) return chip.hide(); // `chip` is your own element + const rect = ui.contentControls.getRect({ id: active.id }); + if (rect.success) chip.showAt(rect.rect, active.alias ?? active.tag); + }); + }, +}); +``` + +The event tells you *what* is active; `getRect` tells you *where* to draw. `active` is an `SdtRef` with `id`, `tag`, `alias`, `controlType`, and `scope`. + +## Pick the right surface + +| Goal | API | +| --- | --- | +| Active control (enter, switch, leave) | `superdoc.on('content-control:active-change')` | +| Click inside a control | `superdoc.on('content-control:click')` | +| Full live list and active stack | `ui.contentControls.observe()` / `getSnapshot()` | +| Read one control | `ui.contentControls.get({ id })` | +| Position your UI | `ui.contentControls.getRect({ id })` | +| Scroll a control into view | `ui.contentControls.scrollIntoView({ id })` | +| Scroll to it and put the cursor in | `ui.contentControls.focus({ id })` | +| Re-anchor your UI when the page moves | `ui.viewport.observe(() => ...)` | +| Hover and right-click hit-testing | `ui.viewport.entityAt()` / `contextAt()` | +| Change content, tags, or locks | `editor.doc.contentControls.*` | + +`active` is the innermost control. For nested controls (an inline field inside a block clause), `activePath` carries the full stack, innermost first, so you don't also need `observe()` just to read the nesting. + +`scrollIntoView` resolves the control's position from the document, so it works even when the control is on a page that hasn't rendered yet (the page mounts, then scrolls). It scrolls only - it does not move the cursor into the control. `focus` does both: scrolls to the control and places the caret inside so the user can start typing. `focus` is selection, not editing - it does not bypass lock or document-mode rules, so a locked or read-only control can be focused for inspection but edits are still blocked. + +`ui.viewport.observe` is the single signal for "your `getRect()` coordinates may be stale, re-query": it fires (coalesced, once per frame) on scroll, resize, zoom, and layout reflow, so an overlay anchored with `getRect` stays glued without hand-wiring those events yourself. + +## How the model works + +You build your UI *over* the control, not inside it. SuperDoc owns how the control's content is painted in the document; you turn off its built-in chrome and draw your own (chips, badges, panels) anchored with `getRect`, react with the events, and change content through `editor.doc.contentControls.*`. Custom field types are expressed as a `tag` - for example `{ kind: 'smartField', key: 'party_name' }`, interpreted by your own UI - the underlying control stays a standard Word SDT so it round-trips to `.docx`. + +## See also + +- [Contract templates demo](https://github.com/superdoc-dev/superdoc/tree/main/demos/contract-templates) - a working field chip built on these APIs. +- [Configuration](/editor/superdoc/configuration) - the `modules.contentControls.chrome` option. +- [Document API: content controls](/document-api/features/content-controls) - read and change controls. diff --git a/apps/docs/editor/superdoc/configuration.mdx b/apps/docs/editor/superdoc/configuration.mdx index d0dfdfb729..48a70ffd1a 100644 --- a/apps/docs/editor/superdoc/configuration.mdx +++ b/apps/docs/editor/superdoc/configuration.mdx @@ -357,6 +357,34 @@ See [Surfaces](/editor/superdoc/surfaces) for the full API and examples. **Deprecated**: Use `modules.contextMenu` instead. See the [Context Menu module](/editor/built-in-ui/context-menu) for configuration options. +### Content controls module + + + Content-control rendering configuration. + + + + Built-in content-control chrome mode. + - `'default'`: Word-like built-in chrome (labels, borders, hover, selected outline) + - `'none'`: disables built-in chrome while keeping SDT data/geometry and `data-sdt-*` wrappers + + + + +Use this when you want to keep content controls in the document but disable SuperDoc’s built-in field visuals. + +```javascript +new SuperDoc({ + selector: '#editor', + document: 'contract.docx', + modules: { + contentControls: { + chrome: 'none', + }, + }, +}); +``` + ## Spell check Provider-based spell check for the layout-engine editor surface. The configuration key is `proofing` because the provider contract is designed to support spelling, grammar, and style. The current UI renders spelling only. diff --git a/apps/docs/editor/superdoc/events.mdx b/apps/docs/editor/superdoc/events.mdx index f1dc662e83..3cffc4de66 100644 --- a/apps/docs/editor/superdoc/events.mdx +++ b/apps/docs/editor/superdoc/events.mdx @@ -29,6 +29,7 @@ const superdoc = new SuperDoc({ document: yourFile, }); +/** @param {import('superdoc').SuperDocReadyPayload} payload */ const handler = ({ superdoc }) => { console.log('Handler called'); }; @@ -154,6 +155,7 @@ When editor content changes. Use this to refresh live UI state like word counts ```javascript Usage superdoc.on('editor-update', ({ editor }) => { + if (!editor) return; autoSave(editor.getJSON()); }); ``` @@ -168,6 +170,7 @@ const superdoc = new SuperDoc({ }); superdoc.on('editor-update', ({ editor }) => { + if (!editor) return; autoSave(editor.getJSON()); }); ``` @@ -190,7 +193,7 @@ When content processing fails. ```javascript Usage -superdoc.on('content-error', ({ error, editor, documentId }) => { +superdoc.on('content-error', ({ error, editor }) => { console.error('Content error:', error); }); ``` @@ -204,7 +207,7 @@ const superdoc = new SuperDoc({ document: yourFile, }); -superdoc.on('content-error', ({ error, editor, documentId }) => { +superdoc.on('content-error', ({ error, editor }) => { console.error('Content error:', error); }); ``` @@ -240,6 +243,55 @@ superdoc.on('fonts-resolved', ({ documentFonts, unsupportedFonts }) => { ``` +## Content controls (SDT fields) + +### `content-control:active-change` + +Fires when selection enters a content control, switches between controls, or leaves all controls. + +```javascript +superdoc.on('content-control:active-change', ({ active, previous, activePath, source }) => { + // source: 'keyboard' | 'pointer' + // active / previous: SdtRef | null + // activePath: SdtRef[] - full active stack, innermost first ([] when none) +}); +``` + +`SdtRef` shape: + +```ts +type SdtRef = { + id: string; + tag?: string; + alias?: string; + controlType: string; + scope: 'inline' | 'block'; +}; +``` + +`active` is the deepest control (`activePath[0]`); `activePath` holds the full stack, innermost first. Controls without an `id` do not emit these events. + +How to interpret: + +1. Focus (`null -> A`): `previous === null && active !== null` +2. Switch (`A -> B`): `previous !== null && active !== null && previous.id !== active.id` +3. Blur (`A -> null`): `previous !== null && active === null` + +### `content-control:click` + +Fires on pointer click inside a content control. + +```javascript +superdoc.on('content-control:click', ({ target, source }) => { + // source is always 'pointer' + // target: SdtRef +}); +``` + +Both are also available as config callbacks: `onContentControlActiveChange` and `onContentControlClick`. + +To build your own content-control UI on these events, see [Custom UI > Content controls](/editor/custom-ui/content-controls). + ## Comments events ### `comments-update` @@ -248,8 +300,10 @@ When comments are modified. ```javascript Usage -superdoc.on('comments-update', ({ type, data }) => { +superdoc.on('comments-update', ({ type, comment, changes }) => { // type: 'add', 'update', 'deleted', 'resolved' + // comment: the comment object (when applicable) + // changes: per-field change set (when the update is a mutation) }); ``` @@ -262,8 +316,10 @@ const superdoc = new SuperDoc({ document: yourFile, }); -superdoc.on('comments-update', ({ type, data }) => { +superdoc.on('comments-update', ({ type, comment, changes }) => { // type: 'add', 'update', 'deleted', 'resolved' + // comment: the comment object (when applicable) + // changes: per-field change set (when the update is a mutation) }); ``` @@ -302,9 +358,8 @@ When user presence changes. ```javascript Usage -superdoc.on('awareness-update', ({ context, states }) => { - const users = Array.from(states.values()); - updateUserCursors(users); +superdoc.on('awareness-update', ({ states, added, removed }) => { + updateUserCursors(states); }); ``` @@ -317,9 +372,8 @@ const superdoc = new SuperDoc({ document: yourFile, }); -superdoc.on('awareness-update', ({ context, states }) => { - const users = Array.from(states.values()); - updateUserCursors(users); +superdoc.on('awareness-update', ({ states, added, removed }) => { + updateUserCursors(states); }); ``` @@ -331,7 +385,7 @@ When document lock state changes. ```javascript Usage superdoc.on('locked', ({ isLocked, lockedBy }) => { - if (isLocked) { + if (isLocked && lockedBy) { showLockBanner(`Locked by ${lockedBy.name}`); } }); @@ -347,7 +401,7 @@ const superdoc = new SuperDoc({ }); superdoc.on('locked', ({ isLocked, lockedBy }) => { - if (isLocked) { + if (isLocked && lockedBy) { showLockBanner(`Locked by ${lockedBy.name}`); } }); @@ -374,7 +428,6 @@ import 'superdoc/style.css'; const superdoc = new SuperDoc({ selector: '#editor', document: yourFile, - pagination: true, }); superdoc.on('pagination-update', ({ totalPages, superdoc }) => { @@ -445,8 +498,10 @@ When an error occurs during document processing or runtime. ```javascript Usage -superdoc.on('exception', ({ error, document, editor }) => { - console.error('SuperDoc error:', error); +superdoc.on('exception', (payload) => { + // `payload` is a discriminated union; narrow before reading variant-specific fields. + // `code` is only on the editor-lifecycle variant; `stage` is only on document-init. + console.error('SuperDoc error:', payload.error); }); ``` @@ -459,8 +514,10 @@ const superdoc = new SuperDoc({ document: yourFile, }); -superdoc.on('exception', ({ error, document, editor }) => { - console.error('SuperDoc error:', error); +superdoc.on('exception', (payload) => { + // `payload` is a discriminated union; narrow before reading variant-specific fields. + // `code` is only on the editor-lifecycle variant; `stage` is only on document-init. + console.error('SuperDoc error:', payload.error); }); ``` diff --git a/apps/docs/editor/superdoc/import-export.mdx b/apps/docs/editor/superdoc/import-export.mdx index d85b2eac89..9904bed99c 100644 --- a/apps/docs/editor/superdoc/import-export.mdx +++ b/apps/docs/editor/superdoc/import-export.mdx @@ -95,7 +95,8 @@ import 'superdoc/style.css'; const superdoc = new SuperDoc({ selector: '#editor', document: yourFile, - onReady: (superdoc) => { + onReady: ({ superdoc }) => { + if (!superdoc.activeEditor) return; superdoc.activeEditor.commands.insertContent(content, { contentType: 'html' // 'html' | 'markdown' | 'text' | 'schema' }); @@ -145,7 +146,7 @@ import 'superdoc/style.css'; const superdoc = new SuperDoc({ selector: '#editor', document: yourFile, - onReady: async (superdoc) => { + onReady: async ({ superdoc }) => { // Download as .docx file await superdoc.export(); @@ -215,11 +216,12 @@ import 'superdoc/style.css'; const superdoc = new SuperDoc({ selector: '#editor', document: yourFile, - onReady: (superdoc) => { + onReady: ({ superdoc }) => { // Returns array of HTML strings (one per document) const htmlArray = superdoc.getHTML(); // From the active editor (single string) + if (!superdoc.activeEditor) return; const html = superdoc.activeEditor.getHTML(); }, }); @@ -235,7 +237,7 @@ HTML export is structure-only. Custom CSS styling and Word-specific formatting a ```javascript Usage // From the active editor -const json = superdoc.activeEditor.getJSON(); +const json = superdoc.activeEditor?.getJSON(); ``` ```javascript Full Example @@ -245,7 +247,8 @@ import 'superdoc/style.css'; const superdoc = new SuperDoc({ selector: '#editor', document: yourFile, - onReady: (superdoc) => { + onReady: ({ superdoc }) => { + if (!superdoc.activeEditor) return; const json = superdoc.activeEditor.getJSON(); }, }); @@ -259,7 +262,7 @@ JSON export preserves the full document structure and can be re-imported with `j ```javascript Usage // From the active editor -const markdown = await superdoc.activeEditor.getMarkdown(); +const markdown = await superdoc.activeEditor?.getMarkdown(); ``` ```javascript Full Example @@ -269,7 +272,8 @@ import 'superdoc/style.css'; const superdoc = new SuperDoc({ selector: '#editor', document: yourFile, - onReady: async (superdoc) => { + onReady: async ({ superdoc }) => { + if (!superdoc.activeEditor) return; const markdown = await superdoc.activeEditor.getMarkdown(); }, }); diff --git a/apps/docs/editor/superdoc/methods.mdx b/apps/docs/editor/superdoc/methods.mdx index 367ccd2006..3e807a5000 100644 --- a/apps/docs/editor/superdoc/methods.mdx +++ b/apps/docs/editor/superdoc/methods.mdx @@ -54,7 +54,7 @@ import 'superdoc/style.css'; const superdoc = new SuperDoc({ selector: '#editor', document: yourFile, - onReady: async (superdoc) => { + onReady: async ({ superdoc }) => { const blob = await superdoc.export({ isFinalDoc: true, commentsType: 'clean', @@ -84,7 +84,7 @@ import 'superdoc/style.css'; const superdoc = new SuperDoc({ selector: '#editor', document: yourFile, - onReady: async (superdoc) => { + onReady: async ({ superdoc }) => { await superdoc.save(); }, }); @@ -121,7 +121,7 @@ import 'superdoc/style.css'; const superdoc = new SuperDoc({ selector: '#editor', document: yourFile, - onReady: (superdoc) => { + onReady: ({ superdoc }) => { const htmlArray = superdoc.getHTML(); console.log(htmlArray); }, @@ -230,7 +230,7 @@ import 'superdoc/style.css'; const superdoc = new SuperDoc({ selector: '#editor', document: yourFile, - onReady: (superdoc) => { + onReady: ({ superdoc }) => { superdoc.setDocumentMode('suggesting'); }, }); @@ -263,7 +263,7 @@ import 'superdoc/style.css'; const superdoc = new SuperDoc({ selector: '#editor', document: yourFile, - onReady: (superdoc) => { + onReady: ({ superdoc }) => { superdoc.lockSuperdoc(true, { name: 'Jane Smith', email: 'jane@example.com', @@ -295,7 +295,7 @@ import 'superdoc/style.css'; const superdoc = new SuperDoc({ selector: '#editor', document: yourFile, - onReady: (superdoc) => { + onReady: ({ superdoc }) => { superdoc.setHighContrastMode(true); }, }); @@ -323,13 +323,22 @@ superdoc.setActiveEditor(editor); import { SuperDoc } from 'superdoc'; import 'superdoc/style.css'; +/** @type {Array} */ +const createdEditors = []; + const superdoc = new SuperDoc({ selector: '#editor', documents: [doc1, doc2], - onReady: (superdoc) => { - // Switch to the second editor - const editors = superdoc.state.documents; - superdoc.setActiveEditor(editors[1].editor); + onEditorCreate: ({ editor }) => { + // Each per-document editor reaches `onEditorCreate` in `documents` order. + createdEditors.push(editor); + + // Switch to the second editor as soon as it exists. + if (createdEditors.length === 2) { + queueMicrotask(() => { + superdoc.setActiveEditor(editor); + }); + } }, }); ``` @@ -357,7 +366,7 @@ import 'superdoc/style.css'; const superdoc = new SuperDoc({ selector: '#editor', document: yourFile, - onReady: (superdoc) => { + onReady: ({ superdoc }) => { superdoc.setDisableContextMenu(true); }, }); @@ -460,7 +469,7 @@ import 'superdoc/style.css'; const superdoc = new SuperDoc({ selector: '#editor', document: yourFile, - onReady: (superdoc) => { + onReady: ({ superdoc }) => { superdoc.setTrackedChangesPreferences({ mode: 'final' }); }, }); @@ -485,7 +494,7 @@ import 'superdoc/style.css'; const superdoc = new SuperDoc({ selector: '#editor', document: yourFile, - onReady: (superdoc) => { + onReady: ({ superdoc }) => { superdoc.toggleRuler(); }, }); @@ -518,7 +527,7 @@ import 'superdoc/style.css'; const superdoc = new SuperDoc({ selector: '#editor', document: yourFile, - onReady: (superdoc) => { + onReady: ({ superdoc }) => { const zoom = superdoc.getZoom(); console.log(`Current zoom: ${zoom}%`); }, @@ -548,7 +557,7 @@ import 'superdoc/style.css'; const superdoc = new SuperDoc({ selector: '#editor', document: yourFile, - onReady: (superdoc) => { + onReady: ({ superdoc }) => { superdoc.setZoom(150); superdoc.on('zoomChange', ({ zoom }) => { @@ -577,7 +586,7 @@ import 'superdoc/style.css'; const superdoc = new SuperDoc({ selector: '#editor', document: yourFile, - onReady: (superdoc) => { + onReady: ({ superdoc }) => { superdoc.focus(); }, }); @@ -611,9 +620,11 @@ import 'superdoc/style.css'; const superdoc = new SuperDoc({ selector: '#editor', document: yourFile, - onReady: (superdoc) => { + onReady: ({ superdoc }) => { const results = superdoc.search('contract'); - console.log(`Found ${results.length} matches`); + if (results) { + console.log(`Found ${results.length} matches`); + } const regexResults = superdoc.search(/section \d+/gi); }, @@ -643,9 +654,9 @@ import 'superdoc/style.css'; const superdoc = new SuperDoc({ selector: '#editor', document: yourFile, - onReady: (superdoc) => { + onReady: ({ superdoc }) => { const results = superdoc.search('contract'); - if (results.length) { + if (results && results.length) { superdoc.goToSearchResult(results[0]); } }, @@ -664,14 +675,15 @@ const superdoc = new SuperDoc({ Add a comments list to the specified element. - - Container for comments list + + Container element for the comments list ```javascript Usage -superdoc.addCommentsList('#comments-sidebar'); +const sidebar = document.getElementById('comments-sidebar'); +if (sidebar) superdoc.addCommentsList(sidebar); ``` ```javascript Full Example @@ -681,8 +693,9 @@ import 'superdoc/style.css'; const superdoc = new SuperDoc({ selector: '#editor', document: yourFile, - onReady: (superdoc) => { - superdoc.addCommentsList('#comments-sidebar'); + onReady: ({ superdoc }) => { + const sidebar = document.getElementById('comments-sidebar'); + if (sidebar) superdoc.addCommentsList(sidebar); }, }); ``` @@ -706,7 +719,7 @@ import 'superdoc/style.css'; const superdoc = new SuperDoc({ selector: '#editor', document: yourFile, - onReady: (superdoc) => { + onReady: ({ superdoc }) => { superdoc.removeCommentsList(); }, }); @@ -740,11 +753,11 @@ Scroll to a comment in the document and set it as active. ```javascript Usage // Get a comment ID from the document API -const { items } = superdoc.editor.doc.comments.list(); -const commentId = items[0].id; - -// Scroll to it -superdoc.scrollToComment(commentId); +const editor = superdoc.activeEditor; +if (editor) { + const { items } = editor.doc.comments.list(); + if (items.length > 0) superdoc.scrollToComment(items[0].id); +} ``` ```javascript Full Example @@ -755,8 +768,10 @@ const superdoc = new SuperDoc({ selector: '#editor', document: yourFile, modules: { comments: {} }, - onReady: (superdoc) => { - const { items } = superdoc.editor.doc.comments.list(); + onReady: ({ superdoc }) => { + const editor = superdoc.activeEditor; + if (!editor) return; + const { items } = editor.doc.comments.list(); if (items.length > 0) { superdoc.scrollToComment(items[0].id, { behavior: 'smooth', @@ -787,10 +802,10 @@ const result = editor.doc.query.match({ select: { type: 'text', pattern: 'Introduction', mode: 'contains' }, require: 'first', }); -const nodeId = result.items[0].address.nodeId; - -// Scroll to it: one call, any element type -await superdoc.scrollToElement(nodeId); +const first = result.items[0]; +if (first && first.address.kind === 'block') { + await superdoc.scrollToElement(first.address.nodeId); +} ``` ```javascript Full Example @@ -801,26 +816,30 @@ const superdoc = new SuperDoc({ selector: '#editor', document: yourFile, modules: { comments: {} }, - onReady: async (superdoc) => { - const editor = superdoc.editor; + onReady: async ({ superdoc }) => { + const editor = superdoc.activeEditor; + if (!editor) return; - // Navigate to a paragraph + // Navigate to a paragraph (block addresses carry `nodeId`; narrow first) const match = editor.doc.query.match({ select: { type: 'text', pattern: 'Summary', mode: 'contains' }, require: 'first', }); - await superdoc.scrollToElement(match.items[0].address.nodeId); + const first = match.items[0]; + if (first && first.address.kind === 'block') { + await superdoc.scrollToElement(first.address.nodeId); + } // Navigate to a comment const comments = editor.doc.comments.list(); if (comments.items.length > 0) { - await superdoc.scrollToElement(comments.items[0].entityId); + await superdoc.scrollToElement(comments.items[0].id); } // Navigate to a tracked change const changes = editor.doc.trackChanges.list(); if (changes.items.length > 0) { - await superdoc.scrollToElement(changes.items[0].entityId); + await superdoc.scrollToElement(changes.items[0].id); } }, }); @@ -867,7 +886,7 @@ import 'superdoc/style.css'; const superdoc = new SuperDoc({ selector: '#editor', document: yourFile, - onReady: (superdoc) => { + onReady: ({ superdoc }) => { superdoc.addSharedUser({ name: 'Jane Smith', email: 'jane@example.com', @@ -899,7 +918,7 @@ import 'superdoc/style.css'; const superdoc = new SuperDoc({ selector: '#editor', document: yourFile, - onReady: (superdoc) => { + onReady: ({ superdoc }) => { superdoc.removeSharedUser('jane@example.com'); }, }); @@ -1013,6 +1032,7 @@ const superdoc = new SuperDoc({ document: yourFile, }); +/** @param {import('superdoc').SuperDocReadyPayload} payload */ const handler = ({ superdoc }) => { console.log('SuperDoc ready'); }; @@ -1086,7 +1106,7 @@ import 'superdoc/style.css'; const superdoc = new SuperDoc({ selector: '#editor', document: yourFile, - onReady: (superdoc) => { + onReady: ({ superdoc }) => { if (superdoc.activeEditor) { superdoc.activeEditor.commands.toggleBold(); } diff --git a/apps/docs/package.json b/apps/docs/package.json index c35dae7ea7..571ab1150b 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -11,6 +11,7 @@ "check:imports": "bun scripts/validate-code-imports.ts", "check:icons": "bun scripts/validate-icons.ts", "check:em-dashes": "bun scripts/validate-em-dashes.ts", + "check:types": "tsx __tests__/doctest-types.ts", "test:examples": "bun test __tests__/doctest.test.ts" }, "devDependencies": { diff --git a/apps/mcp/src/generated/catalog.ts b/apps/mcp/src/generated/catalog.ts index 4cd0a69ba6..7b994255c4 100644 --- a/apps/mcp/src/generated/catalog.ts +++ b/apps/mcp/src/generated/catalog.ts @@ -2327,16 +2327,35 @@ export const MCP_TOOL_CATALOG = { { $ref: '#/$defs/TextTarget', }, + { + $ref: '#/$defs/SelectionTarget', + }, + { + $ref: '#/$defs/CommentTrackedChangeTarget', + }, ], description: - "Text range to anchor the comment. Accepts either a single-block TextAddress {kind:'text', blockId, range} or a multi-segment TextTarget {kind:'text', segments:[{blockId, range}, ...]} for selections that span blocks.", + "Comment target. Accepts a TextAddress, TextTarget, SelectionTarget, or {trackedChangeId, kind?:'trackedChange'} to anchor directly on tracked content.", }, { - $ref: '#/$defs/TextAddress', + oneOf: [ + { + $ref: '#/$defs/TextAddress', + }, + { + $ref: '#/$defs/TextTarget', + }, + { + $ref: '#/$defs/SelectionTarget', + }, + { + $ref: '#/$defs/CommentTrackedChangeTarget', + }, + ], }, ], description: - "Text range to anchor the comment. Accepts either a single-block TextAddress {kind:'text', blockId, range} or a multi-segment TextTarget {kind:'text', segments:[{blockId, range}, ...]} for selections that span blocks. Only for actions 'create', 'update'. Omit for other actions.", + "Comment target. Accepts a TextAddress, TextTarget, SelectionTarget, or {trackedChangeId, kind?:'trackedChange'} to anchor directly on tracked content. Only for actions 'create', 'update'. Omit for other actions.", }, parentId: { type: 'string', @@ -2404,7 +2423,7 @@ export const MCP_TOOL_CATALOG = { { toolName: 'superdoc_track_changes', description: - 'Review and resolve tracked changes (insertions, deletions, format changes) in the document. Action "list" returns all tracked changes with optional filtering by type (insert, delete, format) and pagination (limit, offset). Each change includes an ID, type, author, timestamp, and content preview. Action "decide" accepts or rejects changes. Pass decision:"accept" to apply the change permanently, or decision:"reject" to discard it. Target a single change with {id:""} or all changes at once with {scope:"all"}. Do NOT use this tool unless the document has tracked changes. Use superdoc_get_content info to check the tracked change count first.\n\nEXAMPLES:\n 1. {"action":"list"}\n 2. {"action":"list","type":"insert","limit":10}\n 3. {"action":"decide","decision":"accept","target":{"id":""}}\n 4. {"action":"decide","decision":"reject","target":{"scope":"all"}}', + 'Review and resolve tracked changes (insertions, deletions, replacements, format changes) in the document. Action "list" returns all tracked changes with optional filtering by type (insert, delete, replacement, format) and pagination (limit, offset). Each change includes an ID, type, author, timestamp, and content preview. Action "decide" accepts or rejects changes. Pass decision:"accept" to apply the change permanently, or decision:"reject" to discard it. Target a single change with {id:""}, a partial selection with {kind:"range", range:{...}}, or all changes at once with {scope:"all"} (optionally plus story). Do NOT use this tool unless the document has tracked changes. Use superdoc_get_content info to check the tracked change count first.\n\nEXAMPLES:\n 1. {"action":"list"}\n 2. {"action":"list","type":"replacement","limit":10}\n 3. {"action":"decide","decision":"accept","target":{"id":""}}\n 4. {"action":"decide","decision":"reject","target":{"kind":"range","range":{"kind":"text","segments":[{"blockId":"","range":{"start":0,"end":5}}]}}}\n 5. {"action":"decide","decision":"reject","target":{"scope":"all"}}', inputSchema: { type: 'object', properties: { @@ -2423,9 +2442,9 @@ export const MCP_TOOL_CATALOG = { "Number of tracked changes to skip for pagination. Only for action 'list'. Omit for other actions.", }, type: { - enum: ['insert', 'delete', 'format'], + enum: ['insert', 'delete', 'replacement', 'format'], description: - "Filter by change type: 'insert', 'delete', or 'format'. Only for action 'list'. Omit for other actions.", + "Filter by change type: 'insert', 'delete', 'replacement', or 'format'. Only for action 'list'. Omit for other actions.", }, force: { type: 'boolean', @@ -2456,12 +2475,46 @@ export const MCP_TOOL_CATALOG = { additionalProperties: false, required: ['id'], }, + { + type: 'object', + properties: { + kind: { + const: 'range', + type: 'string', + }, + range: { + $ref: '#/$defs/TextTarget', + }, + story: { + $ref: '#/$defs/StoryLocator', + }, + part: { + type: 'string', + description: 'Optional part discriminator for the range target.', + }, + }, + additionalProperties: false, + required: ['kind', 'range'], + }, { type: 'object', properties: { scope: { enum: ['all'], }, + story: { + oneOf: [ + { + $ref: '#/$defs/StoryLocator', + }, + { + const: 'all', + type: 'string', + }, + ], + description: + "Optional explicit bulk filter. Omit or pass 'all' to target every revision-capable story, or pass a StoryLocator to scope the decision to one story.", + }, }, additionalProperties: false, required: ['scope'], diff --git a/demos/__tests__/contract-templates-chip-anchor.spec.ts b/demos/__tests__/contract-templates-chip-anchor.spec.ts new file mode 100644 index 0000000000..cf16ec6558 --- /dev/null +++ b/demos/__tests__/contract-templates-chip-anchor.spec.ts @@ -0,0 +1,73 @@ +import { test, expect } from '@playwright/test'; + +/** + * SD-3311 regression: the field chip must stay anchored to its active control + * after a geometry change that fires NO scroll event (zoom). The chip is a + * fixed-position overlay positioned from `ui.contentControls.getRect()`. Today + * field-chip only re-anchors on active-change / scroll / resize, so a zoom + * leaves it stranded (verified: ~230px drift). This is RED until + * `ui.viewport.observe()` lands and field-chip re-queries on it. + * + * Runs only for the contract-templates demo (the shared suite runs once per DEMO). + */ + +test.use({ viewport: { width: 1280, height: 800 } }); + +test('field chip stays anchored to its control after a zoom (no-scroll geometry change)', async ({ page }) => { + test.skip(process.env.DEMO !== 'contract-templates', 'contract-templates demo only'); + + await page.route('**/ingest.superdoc.dev/**', (r) => + r.fulfill({ status: 204, contentType: 'application/json', body: '{}' }), + ); + await page.goto('/'); + await page.waitForFunction( + () => { + const ui = (window as any).__demo?.state?.ui; + return !!ui && ui.contentControls.getSnapshot().items.length > 0; + }, + null, + { timeout: 30_000 }, + ); + + // Activate the first inline smart field so the chip appears and anchors. + await page.waitForSelector('.superdoc-structured-content-inline[data-sdt-id]'); + await page.locator('.superdoc-structured-content-inline[data-sdt-id]').first().click(); + await page.locator('.sd-field-chip').waitFor({ state: 'visible', timeout: 10_000 }); + + // Horizontal gap between the chip's left edge and its active control's left + // edge. positionChip sets chip.left = control.left, so this is ~0 when anchored. + const probe = () => + page.evaluate(() => { + const ui = (window as any).__demo.state.ui; + const activeId = ui.contentControls.getSnapshot().activeId as string | null; + const chip = document.querySelector('.sd-field-chip'); + const ctrl = activeId ? document.querySelector(`[data-sdt-id="${activeId}"]`) : null; + if (!chip || !ctrl) return null; + const c = chip.getBoundingClientRect(); + const k = ctrl.getBoundingClientRect(); + return { dxLeft: Math.abs(c.left - k.left), ctrlLeft: Math.round(k.left) }; + }); + + const before = await probe(); + expect(before, 'chip + active control both resolve').not.toBeNull(); + expect(before!.dxLeft, 'chip starts anchored to the control').toBeLessThanOrEqual(2); + + // Zoom: a geometry change with no scroll event. + await page.evaluate(() => (window as any).__demo.superdoc.setZoom(150)); + + // Poll for the settled state: the control has moved (zoom applied) AND the + // chip has re-anchored to it. Polling absorbs the rAF/repaint delay between + // the geometry change and the viewport.observe -> positionChip re-query. + // Without the SD-3311 fix this stays "drift:~230" and times out. + await expect + .poll( + async () => { + const p = await probe(); + if (!p) return 'no-probe'; + if (p.ctrlLeft === before!.ctrlLeft) return 'control-not-moved'; + return p.dxLeft <= 2 ? 'anchored' : `drift:${Math.round(p.dxLeft)}`; + }, + { timeout: 6_000 }, + ) + .toBe('anchored'); +}); diff --git a/demos/__tests__/contract-templates-focus.spec.ts b/demos/__tests__/contract-templates-focus.spec.ts new file mode 100644 index 0000000000..8520649355 --- /dev/null +++ b/demos/__tests__/contract-templates-focus.spec.ts @@ -0,0 +1,141 @@ +import { test, expect } from '@playwright/test'; + +/** + * SD-3312 acceptance: clicking a field's "Focus" button in the contract-templates + * sidebar places the editor caret INSIDE that control (the "scroll there and let + * me edit" step, vs "Locate" which only scrolls). Dogfoods ui.contentControls.focus. + * + * Runs only for the contract-templates demo (the shared suite runs once per DEMO). + */ + +// Short viewport so the bottom clause reliably starts below the fold for the +// off-screen focus case (the field case works at any height). +test.use({ viewport: { width: 1100, height: 520 } }); + +test('clicking a field Focus places the caret inside that control', async ({ page }) => { + test.skip(process.env.DEMO !== 'contract-templates', 'contract-templates demo only'); + + await page.route('**/ingest.superdoc.dev/**', (r) => + r.fulfill({ status: 204, contentType: 'application/json', body: '{}' }), + ); + await page.goto('/'); + await page.waitForFunction( + () => (window as any).__demo?.state?.ui?.contentControls?.getSnapshot()?.items?.length > 0, + null, + { timeout: 30_000 }, + ); + + // Fields tab is the default; the Focus buttons live on field rows. + await page.waitForSelector('[data-focus-field]'); + const key = await page.getAttribute('[data-focus-field]', 'data-focus-field'); + expect(key).toBeTruthy(); + + // Resolve which structuredContent control (by id) the caret currently sits in. + const controlKeyAtSelection = () => + page.evaluate(() => { + const ed = (window as any).__demo.superdoc.activeEditor; + const from = ed?.state?.selection?.from; + if (typeof from !== 'number') return null; + let hit: string | null = null; + ed.state.doc.descendants((node: any, pos: number) => { + if ( + (node.type.name === 'structuredContent' || node.type.name === 'structuredContentBlock') && + from > pos && + from < pos + node.nodeSize + ) { + try { + hit = JSON.parse(node.attrs.tag ?? '{}').key ?? null; + } catch { + hit = null; + } + } + return true; + }); + return hit; + }); + + // Caret should not already be in this field's control. + expect(await controlKeyAtSelection()).not.toBe(key); + + await page.click(`[data-focus-field="${key}"]`); + + // After focus, the caret lands inside a control whose tag carries this key. + await expect.poll(controlKeyAtSelection, { timeout: 5_000 }).toBe(key); +}); + +test('focusing an off-screen clause scrolls it in AND lands the caret inside it', async ({ page }) => { + test.skip(process.env.DEMO !== 'contract-templates', 'contract-templates demo only'); + + await page.route('**/ingest.superdoc.dev/**', (r) => + r.fulfill({ status: 204, contentType: 'application/json', body: '{}' }), + ); + await page.goto('/'); + await page.waitForFunction( + () => (window as any).__demo?.state?.ui?.contentControls?.getSnapshot()?.items?.length > 0, + null, + { timeout: 30_000 }, + ); + + // Clause Focus buttons live in the (initially hidden) clauses panel. + await page.click('.tab[data-tab="clauses"]'); + await page.waitForSelector('[data-focus-clause]'); + + // Bottom-most block clause: its painted id + sectionId (= the button's data attr). + const target = await page.evaluate(() => { + const ui = (window as any).__demo.state.ui; + const blocks = ui.contentControls.getSnapshot().items.filter((i: any) => i.kind === 'block'); + const last = blocks[blocks.length - 1]; + let sectionId: string | null = null; + try { + sectionId = JSON.parse(last?.properties?.tag ?? '{}').sectionId ?? null; + } catch { + sectionId = null; + } + return { id: last?.id ?? null, sectionId }; + }); + expect(target.id).toBeTruthy(); + expect(target.sectionId).toBeTruthy(); + + // Scroll to the top so the bottom clause starts off-screen. + await page.evaluate(() => { + let node: HTMLElement | null = document.querySelector('.presentation-editor__pages'); + while (node && !(node.scrollHeight > node.clientHeight + 4)) node = node.parentElement; + if (node) node.scrollTop = 0; + else window.scrollTo(0, 0); + }); + + const state = () => + page.evaluate((id) => { + // caret's containing control id + const ed = (window as any).__demo.superdoc.activeEditor; + const from = ed?.state?.selection?.from; + let caretIn: string | null = null; + if (typeof from === 'number') { + ed.state.doc.descendants((node: any, pos: number) => { + if ( + (node.type.name === 'structuredContent' || node.type.name === 'structuredContentBlock') && + from > pos && + from < pos + node.nodeSize + ) { + caretIn = String(node.attrs.id); + } + return true; + }); + } + // is the control's painted element in the viewport? + const el = document.querySelector(`[data-sdt-id="${id}"]`); + const r = el?.getBoundingClientRect(); + const inViewport = r ? r.top >= 0 && r.top <= window.innerHeight : false; + return { caretIn, inViewport }; + }, target.id); + + // Before focus: caret is not in the bottom clause and it's off-screen. + const before = await state(); + expect(before.caretIn).not.toBe(target.id); + expect(before.inViewport).toBe(false); + + await page.click(`[data-focus-clause="${target.sectionId}"]`); + + // After focus: the control is scrolled into view AND the caret is inside it. + await expect.poll(state, { timeout: 6_000 }).toEqual({ caretIn: target.id, inViewport: true }); +}); diff --git a/demos/__tests__/contract-templates-locate.spec.ts b/demos/__tests__/contract-templates-locate.spec.ts new file mode 100644 index 0000000000..40d9e76016 --- /dev/null +++ b/demos/__tests__/contract-templates-locate.spec.ts @@ -0,0 +1,79 @@ +import { test, expect } from '@playwright/test'; + +/** + * Demo smoke for the contract-templates "Locate" affordance (dogfoods + * `ui.contentControls.scrollIntoView`): clicking a lower clause's Locate + * button scrolls that control's painted element into view. + * + * The shared suite runs once per DEMO, so this skips for every other demo. + */ + +// A short viewport so the bottom clause starts below the fold. +test.use({ viewport: { width: 1100, height: 520 } }); + +test('clicking a lower clause Locate scrolls its content control into view', async ({ page }) => { + test.skip(process.env.DEMO !== 'contract-templates', 'contract-templates demo only'); + + const errors: string[] = []; + page.on('pageerror', (e) => errors.push(e.message)); + page.on('console', (m) => { + if (m.type() === 'error') errors.push(m.text()); + }); + await page.route('**/ingest.superdoc.dev/**', (r) => + r.fulfill({ status: 204, contentType: 'application/json', body: '{}' }), + ); + + await page.goto('/'); + await expect(page.locator('body')).toBeVisible({ timeout: 30_000 }); + + // Wait until SuperDoc has imported the fixture and the UI handle sees controls. + await page.waitForFunction( + () => { + const ui = (window as unknown as { __demo?: { state?: { ui?: unknown } } }).__demo?.state?.ui as + | { contentControls: { getSnapshot(): { items: unknown[] } } } + | undefined; + return !!ui && ui.contentControls.getSnapshot().items.length > 0; + }, + null, + { timeout: 30_000 }, + ); + + // Locate buttons on clause cards live in the (initially hidden) clauses panel. + await page.click('.tab[data-tab="clauses"]'); + await page.waitForSelector('[data-locate-clause]'); + + // Resolve the bottom-most block clause: its painted id (data-sdt-id) and its + // sectionId (= the Locate button's data-locate-clause). + const target = await page.evaluate(() => { + const ui = (window as unknown as { __demo: { state: { ui: { contentControls: { getSnapshot(): { items: Array<{ id: string; kind: string; properties?: { tag?: string } }> } } } } } }).__demo.state.ui; + const items = ui.contentControls.getSnapshot().items; + const blocks = items.filter((i) => i.kind === 'block'); + const last = blocks[blocks.length - 1]; + let sectionId: string | null = null; + try { + sectionId = JSON.parse(last?.properties?.tag ?? '{}').sectionId ?? null; + } catch { + sectionId = null; + } + return { id: last?.id ?? null, sectionId }; + }); + expect(target.id).toBeTruthy(); + expect(target.sectionId).toBeTruthy(); + + const inViewport = () => + page.evaluate((id) => { + const el = document.querySelector(`[data-sdt-id="${id}"]`); + if (!el) return false; + const r = el.getBoundingClientRect(); + return r.top >= 0 && r.top <= window.innerHeight; + }, target.id); + + // The bottom clause starts off-screen. + expect(await inViewport()).toBe(false); + + // Click its Locate button; the document should scroll it into view. + await page.click(`[data-locate-clause="${target.sectionId}"]`); + await expect.poll(inViewport, { timeout: 5_000 }).toBe(true); + + expect(errors).toEqual([]); +}); diff --git a/demos/contract-templates/src/field-chip.ts b/demos/contract-templates/src/field-chip.ts index a272d083a4..a6311ce52e 100644 --- a/demos/contract-templates/src/field-chip.ts +++ b/demos/contract-templates/src/field-chip.ts @@ -1,25 +1,30 @@ /** - * Contextual smart-field chip — SD-3157 demo. + * Contextual smart-field chip — SD-3157 / SD-3232 demo. * * Shows a small chip anchored above the active smart-field content - * control with the field's label and current value. Wired against the - * public `superdoc/ui` controller (no framework — this demo is plain - * TypeScript), using: + * control with the field's label and current value. Plain TypeScript + * (no framework), wired against two public SuperDoc APIs: * - * - `ui.contentControls.observe(...)` to react to the active control - * - `ui.contentControls.getRect({ id })` to anchor the chip + * - `superdoc.on('content-control:active-change', ...)` to know *which* + * control is active (SD-3232 events). The payload's `SdtRef` carries + * the tag/alias/scope directly, so no extra lookup is needed. + * - `ui.contentControls.getRect({ id })` to know *where* to draw the chip. + * + * That pairing is the intended model: events tell you what is active; + * `getRect()` tells you where to place your own UI. * * Narrow on purpose: only renders for `kind: 'smartField'` controls so - * the chip doesn't collide with the existing block-clause review UI in - * the Clauses tab. Linked-occurrence highlights, field-details popovers, - * and clause badges are deliberate follow-ups (SD-3155 umbrella). + * the chip doesn't collide with the block-clause review UI in the Clauses + * tab. Linked-occurrence highlights, field-details popovers, and clause + * badges are deliberate follow-ups (SD-3155 umbrella). * - * The chip renders ALONGSIDE SuperDoc's built-in SDT chrome (blue - * label, border, hover background) by design: this demo demonstrates - * the API's ability to add contextual UI on top of the document, not - * to replace the editor's default visuals. Suppressing the built-in - * chrome is filed as SD-3159 (`modules.contentControls.chrome: 'default' | 'none'`). + * The demo runs with SuperDoc's built-in SDT chrome turned off + * (`modules.contentControls.chrome: 'none'`, SD-3159), so the chip is the + * smart field's active-state UI rather than an addition on top of the + * built-in blue label/border. The wrappers and data-sdt-* datasets are + * still emitted, which is what `getRect` relies on. */ +import type { SuperDoc, ContentControlActiveChangePayload } from 'superdoc'; import type { SuperDocUI } from 'superdoc/ui'; export type SmartFieldLookup = { @@ -33,11 +38,12 @@ const CHIP_CLASS = 'sd-field-chip'; const CHIP_OFFSET_PX = 6; /** - * Wire the chip to the controller. Returns a teardown function that + * Wire the chip. `superdoc` supplies the active-change events; `ui` + * supplies `getRect` for positioning. Returns a teardown function that * detaches listeners and removes the chip element. Safe to call after * `initialize()` has populated the field-value cache. */ -export function attachFieldChip(ui: SuperDocUI, lookup: SmartFieldLookup): () => void { +export function attachFieldChip(superdoc: SuperDoc, ui: SuperDocUI, lookup: SmartFieldLookup): () => void { const chipEl = document.createElement('div'); chipEl.className = CHIP_CLASS; chipEl.style.position = 'fixed'; @@ -50,12 +56,11 @@ export function attachFieldChip(ui: SuperDocUI, lookup: SmartFieldLookup): () => let currentKey: string | null = null; /** - * Clear the active control entirely. Use ONLY when the controller - * tells us "no active SDT" — i.e. the observe callback fires with - * `activeId: null` or the active control isn't a smart field. Do - * NOT call this from the positioning loop on a transient rect miss - * (a reflow can drop the rect for one tick; clearing here would - * leave the chip hidden until the user clicks away and back). + * Clear the active control entirely. Use ONLY when active-change tells + * us "no active smart field" (active is null, or not a smart field). Do + * NOT call this from the positioning loop on a transient rect miss (a + * reflow can drop the rect for one tick; clearing here would leave the + * chip hidden until the user clicks away and back). */ const clearActive = () => { chipEl.style.visibility = 'hidden'; @@ -72,9 +77,8 @@ export function attachFieldChip(ui: SuperDocUI, lookup: SmartFieldLookup): () => if (!currentId) return; const rect = ui.contentControls.getRect({ id: currentId }); if (!rect.success) { - // Transient miss — keep the active state so the next scroll / - // resize / observe tick can re-anchor without requiring the - // user to click away. + // Transient miss — keep the active state so the next scroll / resize + // tick can re-anchor without requiring the user to click away. hideVisually(); return; } @@ -111,19 +115,24 @@ export function attachFieldChip(ui: SuperDocUI, lookup: SmartFieldLookup): () => positionChip(); }; - const onScrollOrResize = () => positionChip(); + // Re-anchor whenever the viewport geometry changes. ui.viewport.observe is + // the single signal for this - it fires on scroll, resize, zoom, and + // layout/pagination reflow, so we catch the zoom / reflow cases that + // hand-wired window scroll + resize listeners miss (SD-3311). + const onViewportChange = () => positionChip(); - const unsubscribe = ui.contentControls.observe((snapshot) => { - // Narrow to smart-field SDTs only. Block-level reusable clauses - // have their own review surface in the Clauses tab; rendering a - // chip on them would compete with that flow. - const activeId = snapshot.activeId; - if (!activeId) { + // SD-3232: the active control comes from the public SuperDoc event. The + // payload includes the SdtRef (id + tag), so we can narrow to smart + // fields and anchor by id without a separate lookup. + const onActiveChange = ({ active }: ContentControlActiveChangePayload) => { + if (!active) { clearActive(); return; } - const info = ui.contentControls.get({ id: activeId }); - const tagStr = info?.properties?.tag; + // Narrow to smart-field SDTs only. Block-level reusable clauses have + // their own review surface in the Clauses tab; a chip on them would + // compete with that flow. + const tagStr = active.tag; if (!tagStr) { clearActive(); return; @@ -139,18 +148,17 @@ export function attachFieldChip(ui: SuperDocUI, lookup: SmartFieldLookup): () => clearActive(); return; } - currentId = activeId; + currentId = active.id; currentKey = parsed.key; update(); - }); + }; - window.addEventListener('scroll', onScrollOrResize, true); - window.addEventListener('resize', onScrollOrResize); + superdoc.on('content-control:active-change', onActiveChange); + const unobserveViewport = ui.viewport.observe(onViewportChange); return () => { - unsubscribe(); - window.removeEventListener('scroll', onScrollOrResize, true); - window.removeEventListener('resize', onScrollOrResize); + superdoc.off('content-control:active-change', onActiveChange); + unobserveViewport(); chipEl.remove(); }; } diff --git a/demos/contract-templates/src/main.ts b/demos/contract-templates/src/main.ts index 5842f201f4..e104ff8987 100644 --- a/demos/contract-templates/src/main.ts +++ b/demos/contract-templates/src/main.ts @@ -216,7 +216,11 @@ const superdoc = new SuperDoc({ selector: '#editor', documentMode: 'editing', document: '/nda-template.docx', - modules: { comments: false }, + // Disable SuperDoc's built-in SDT chrome (border, label, hover/selection + // highlight). The wrappers and data-sdt-* datasets are preserved, so the + // contextual field chip (field-chip.ts) and the document API still work; + // this demo paints its own SDT visuals in style.css instead. + modules: { comments: false, contentControls: { chrome: 'none' } }, telemetry: { enabled: false }, onReady: ({ superdoc: sd }) => void initialize(sd as DemoSuperDoc), }); @@ -275,7 +279,9 @@ async function initialize(instance: DemoSuperDoc): Promise { // leave the previous controller's scroll/resize listeners attached // to `window` and the previous chip element in the DOM. state.ui = createSuperDocUI({ superdoc: instance }); - state.fieldChipTeardown = attachFieldChip(state.ui, { + // Active control comes from the SuperDoc event (SD-3232); placement from + // the UI controller's getRect (SD-3157). + state.fieldChipTeardown = attachFieldChip(instance, state.ui, { labelFor: (key) => FIELDS.find((f) => f.key === (key as FieldKey))?.label ?? key, valueFor: (key) => state.values[key as FieldKey], }); @@ -352,6 +358,39 @@ async function exportDocument(mode: 'raw' | 'clean'): Promise { // Rendering // --------------------------------------------------------------------------- +/** + * Scroll the first control carrying `tag` into view. Dogfoods the shipped + * public API: resolve the control id by tag (`selectByTag`), then + * `ui.contentControls.scrollIntoView`. Scroll-only - it does not move the + * cursor into the control (focus/activate is a separate concern). + */ +function locateByTag(tag: string): void { + const ui = state.ui; + const editor = state.editor; + if (!ui || !editor?.doc) return; + const { items } = editor.doc.contentControls.selectByTag({ tag }); + const first = items[0]; + if (!first) return; + // `target.nodeId` is the SDT node id (= the painted `data-sdt-id`), which is + // what scrollIntoView matches on. + void ui.contentControls.scrollIntoView({ id: first.target.nodeId, block: 'center' }); +} + +/** + * Focus the first control carrying `tag`: scroll to it AND put the caret + * inside (ui.contentControls.focus), so the user can start editing. The + * counterpart to locateByTag (scroll only). + */ +function focusByTag(tag: string): void { + const ui = state.ui; + const editor = state.editor; + if (!ui || !editor?.doc) return; + const { items } = editor.doc.contentControls.selectByTag({ tag }); + const first = items[0]; + if (!first) return; + void ui.contentControls.focus({ id: first.target.nodeId, block: 'center' }); +} + function renderPanels(): void { renderFieldsPanel(); renderClausesPanel(); @@ -360,13 +399,29 @@ function renderPanels(): void { function renderFieldsPanel(): void { fieldsPanelEl.innerHTML = ''; for (const field of FIELDS) { - const row = document.createElement('label'); + // A
wrapper (not
+ `; fieldsPanelEl.appendChild(row); + row.querySelector('.locate')?.addEventListener('click', () => { + locateByTag(fieldTag(field.key)); + }); + row.querySelector('.focus')?.addEventListener('click', () => { + focusByTag(fieldTag(field.key)); + }); const input = row.querySelector('input'); if (!input) continue; // Reactive: each keystroke debounces ~250ms and fans the value to every @@ -398,7 +453,11 @@ function renderClausesPanel(): void { card.innerHTML = `

${escapeHtml(clause.label)}

- Update available +
+ Update available + + +

${escapeHtml(upgrade.summary)}

Document ${escapeHtml(inDoc)} \u00b7 Library ${escapeHtml(upgrade.version)}

@@ -429,12 +488,22 @@ function renderClausesPanel(): void { card.innerHTML = `

${escapeHtml(clause.label)}

- Current +
+ Current + + +

Document ${escapeHtml(inDoc)}

`; } + card.querySelector('.locate')?.addEventListener('click', () => { + locateByTag(clauseTag(clause.id, inDoc)); + }); + card.querySelector('.focus')?.addEventListener('click', () => { + focusByTag(clauseTag(clause.id, inDoc)); + }); clausesPanelEl.appendChild(card); } } diff --git a/demos/contract-templates/src/style.css b/demos/contract-templates/src/style.css index f47b9ce68e..9d7213e80b 100644 --- a/demos/contract-templates/src/style.css +++ b/demos/contract-templates/src/style.css @@ -97,7 +97,10 @@ button { cursor: pointer; } .row { display: block; margin-bottom: 10px; } .row-label { - display: block; + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; font-size: var(--sd-font-size-200, 12px); color: var(--demo-text-muted); margin-bottom: 4px; @@ -133,6 +136,28 @@ input:focus { } .btn:disabled { color: var(--demo-text-muted); cursor: not-allowed; opacity: 0.55; } +/* "Locate" — scroll a control into view (ui.contentControls.scrollIntoView); + "Focus" — scroll AND place the caret inside it (ui.contentControls.focus). */ +.clause-actions { display: flex; align-items: center; gap: 8px; } +.row-actions { display: flex; align-items: center; gap: 6px; } +.locate, +.focus { + font: inherit; + font-size: var(--sd-font-size-100, 11px); + line-height: 1; + padding: 3px 8px; + border: 1px solid var(--demo-border); + border-radius: var(--sd-radius-50, 4px); + background: transparent; + color: var(--demo-text-muted); + cursor: pointer; + white-space: nowrap; +} +.locate:hover, +.focus:hover { color: var(--demo-text); border-color: var(--demo-accent); } +.locate:focus-visible, +.focus:focus-visible { outline: 1px solid var(--demo-accent); outline-offset: 1px; } + #fields-panel .btn { width: 100%; margin-top: 12px; } /* ----------------------------------------------------------------------- @@ -238,42 +263,38 @@ input:focus { } /* ----------------------------------------------------------------------- - In-editor SDT chrome (visible DomPainter classes only) - The painter renders block SDTs with `position: absolute` and explicit - coordinates, so the rules below color the existing 1px border slot and - add background only. They never change padding, margin, or box size. + Host-owned SDT styling. + This demo turns off SuperDoc's built-in content-control chrome + (`modules.contentControls.chrome: 'none'` in main.ts) and paints its + own. The painter adds `.superdoc-cc-chrome-none` to the mount and resets + border/padding/radius/background on the SDT wrappers; scoping under that + class keeps these rules above the reset in specificity and cascade, and + restores the box properties the reset strips. No painter label element + exists under chrome-none, so there is nothing to style for it. ----------------------------------------------------------------------- */ /* Inline smart fields: blue pill */ -.superdoc-structured-content-inline[data-sdt-tag*='smartField'] { - border-color: var(--demo-accent); +.superdoc-cc-chrome-none .superdoc-structured-content-inline[data-sdt-tag*='smartField'] { + padding: 1px 4px; + border: 1px solid var(--demo-accent); + border-radius: 4px; background-color: var(--demo-accent-soft); } -/* Block clauses: visible bordered card with soft background */ -.superdoc-structured-content-block[data-sdt-tag*='reusableSection'] { - border-color: var(--demo-border); - background-color: var(--demo-bg); -} -.superdoc-structured-content-block[data-sdt-tag*='reusableSection'][data-lock-mode='unlocked'] { +/* Block clauses: bordered card with soft background */ +.superdoc-cc-chrome-none .superdoc-structured-content-block[data-sdt-tag*='reusableSection'] { + border: 1px solid var(--demo-border); + border-radius: 4px; background-color: var(--demo-bg); } -/* Existing painter-provided label gets a typographic tune */ -.superdoc-structured-content-block__label { - font-size: var(--sd-font-size-200, 11px); - text-transform: uppercase; - letter-spacing: 0.05em; - color: var(--demo-text-muted); - font-weight: 600; -} - /* - * Contextual smart-field chip (SD-3157). Floats over the active smart- - * field SDT showing field label + live value. Wired in field-chip.ts - * against `ui.contentControls.observe` + `getRect`. Renders alongside - * SuperDoc's built-in SDT chrome by design — the chip demonstrates - * additive contextual UI, not a chrome replacement (that's SD-3159). + * Contextual smart-field chip (SD-3157 / SD-3232). Floats over the active + * smart-field SDT showing field label + live value. Wired in field-chip.ts + * against the public `content-control:active-change` event (what's active) + * + `ui.contentControls.getRect` (where to draw). With built-in chrome off + * (SD-3159), the chip is the smart field's active-state affordance: custom + * UI anchored to the SDT via public events and the geometry API. */ .sd-field-chip { display: inline-flex; diff --git a/examples/README.md b/examples/README.md index d5fef41cf0..525e241510 100644 --- a/examples/README.md +++ b/examples/README.md @@ -119,6 +119,7 @@ Document editing through models and agents. | [bedrock](./ai/bedrock) | AWS Bedrock Converse API with tool use | | [streaming](./ai/streaming) | Stream model output into a visible editor | | [redlining](./ai/redlining) | LLM-driven tracked-change review (browser) | +| [footnote-tool-agent](./ai/footnote-tool-agent) | Real LLM tool-use loop: model picks `addFootnoteCitation`, browser executes against `editor.doc` | ## Advanced diff --git a/examples/ai/footnote-tool-agent/.env.example b/examples/ai/footnote-tool-agent/.env.example new file mode 100644 index 0000000000..fc9ce1aa30 --- /dev/null +++ b/examples/ai/footnote-tool-agent/.env.example @@ -0,0 +1,5 @@ +OPENAI_API_KEY=sk-... +# Optional. Defaults to gpt-4o-mini. +# OPENAI_MODEL=gpt-4o-mini +# Optional. Defaults to 8093. +# PORT=8093 diff --git a/examples/ai/footnote-tool-agent/.gitignore b/examples/ai/footnote-tool-agent/.gitignore new file mode 100644 index 0000000000..9c97bbd46d --- /dev/null +++ b/examples/ai/footnote-tool-agent/.gitignore @@ -0,0 +1,3 @@ +node_modules +dist +.env diff --git a/examples/ai/footnote-tool-agent/README.md b/examples/ai/footnote-tool-agent/README.md new file mode 100644 index 0000000000..4b9eb8daff --- /dev/null +++ b/examples/ai/footnote-tool-agent/README.md @@ -0,0 +1,61 @@ +# SuperDoc - Footnote tool agent + +Real LLM tool-calling against a live SuperDoc document. The user types a natural-language request; the model picks a tool; the browser executes it against the Document API; the document updates. The `OPENAI_API_KEY` never leaves the server. + +## Files, in the order you'd read them + +1. **`src/tool.ts`** — the Document API wrapper. One function: `addFootnoteCitation(api, { sourceText })`. Wraps `selection.current` + `footnotes.insert` + `footnotes.list` and returns a typed receipt. +2. **`src/agent.ts`** — the tool-use loop. `runAgentTurn` posts the user message, dispatches any `tool_calls` the model returns to local handlers, sends results back, and emits events to the UI. SDK-agnostic — speaks the Chat Completions message shape but doesn't import any provider SDK. +3. **`src/App.tsx`** — the UI. Mounts SuperDoc, captures the user prompt, binds `addFootnoteCitation` to the live `editor.doc` as a handler, calls `runAgentTurn`, renders chat rows. +4. **`server.mjs`** — the proxy. Declares the tool schema (`strict: true`, `parallel_tool_calls: false`), forwards turn requests to `openai.chat.completions.create`, returns the assistant message untouched. + +## Architecture + +``` +Browser ──POST /api/turn──▶ Node proxy ──▶ OpenAI + ▲ │ + │ message or ◀┘ + │ tool_calls + ▼ +editor.doc.* (tool execution lives here) + │ + └── POST /api/turn with the tool result, loop until the model returns text +``` + +| Surface | Owns | +| -------- | -------------------------------------- | +| Server | API key, model client, tool **schema** | +| Browser | Editor, Document API, tool **impl** | + +The browser owns tool execution because `editor.doc` lives there. The server has no editor. So the server runs the model conversation; the browser runs the document. + +## Adding more tools + +1. Add a handler in `src/App.tsx`'s `handlers` map. +2. Mirror the JSON schema in `server.mjs`'s `TOOLS` array. + +That's it. The loop and dispatch in `src/agent.ts` are tool-agnostic. + +## Run + +```bash +cp .env.example .env # then add your OPENAI_API_KEY +pnpm install +pnpm dev # Node proxy + Vite, run together +``` + +Open http://localhost:5181. Click into the paragraph, then send a message like: + +> Add a footnote citing Doe's 2024 cloud reliability paper. + +The chat shows: user → `used addFootnoteCitation · ok` → one-line assistant confirmation. The doc shows the superscript marker. + +## Notes + +- Non-streaming: each `/api/turn` call is request/response. For a streaming-token UX layered on top of tool calls, swap to the Responses API or SSE per-event delivery. +- Each `send` starts a fresh tool loop — prior turns are not preserved. For multi-turn conversations, lift `messages` into app state and append rather than replace. +- For production, add auth, rate limiting, a stricter iteration cap, and reject tool calls that aren't in the registry. + +## See also + +- [examples/ai/streaming](../streaming) — SSE token streaming into a document (no tool use). diff --git a/examples/ai/footnote-tool-agent/index.html b/examples/ai/footnote-tool-agent/index.html new file mode 100644 index 0000000000..8c5d071276 --- /dev/null +++ b/examples/ai/footnote-tool-agent/index.html @@ -0,0 +1,16 @@ + + + + + + SuperDoc - Footnote tool agent + + + +
+ + + diff --git a/examples/ai/footnote-tool-agent/package.json b/examples/ai/footnote-tool-agent/package.json new file mode 100644 index 0000000000..4436cf2af8 --- /dev/null +++ b/examples/ai/footnote-tool-agent/package.json @@ -0,0 +1,27 @@ +{ + "name": "@superdoc-examples/ai-footnote-tool-agent", + "private": true, + "type": "module", + "scripts": { + "predev": "pnpm --filter superdoc build", + "dev": "concurrently -k -n API,WEB -c blue,green \"node server.mjs\" \"vite --host 127.0.0.1 --port 5181 --strictPort\"", + "dev:server": "node server.mjs", + "dev:web": "vite", + "build": "tsc --noEmit && vite build" + }, + "dependencies": { + "dotenv": "^17.4.2", + "openai": "^6.33.0", + "react": "^19.2.5", + "react-dom": "^19.2.5", + "superdoc": "workspace:*" + }, + "devDependencies": { + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "concurrently": "^9.2.1", + "typescript": "catalog:", + "vite": "catalog:" + } +} diff --git a/examples/ai/footnote-tool-agent/server.mjs b/examples/ai/footnote-tool-agent/server.mjs new file mode 100644 index 0000000000..36fd38122a --- /dev/null +++ b/examples/ai/footnote-tool-agent/server.mjs @@ -0,0 +1,120 @@ +import 'dotenv/config'; +import http from 'node:http'; +import OpenAI from 'openai'; + +const PORT = Number(process.env.PORT || 8093); +const MODEL = process.env.OPENAI_MODEL || 'gpt-4o-mini'; + +// Tool schema the model is allowed to call. Mirror any change here in the +// browser's `handlers` map in src/App.tsx so the model's request can +// actually be executed. +const TOOLS = [ + { + type: 'function', + function: { + name: 'addFootnoteCitation', + description: + 'Insert a footnote at the current cursor position in the document. The footnote body is the provided source text. Word renders the superscript number.', + strict: true, + parameters: { + type: 'object', + properties: { + sourceText: { + type: 'string', + description: 'The full source citation text, e.g. "Doe, Cloud Reliability Patterns, 2024".', + }, + }, + required: ['sourceText'], + additionalProperties: false, + }, + }, + }, +]; + +// Lazy so the server can still boot and serve a friendly 500 from +// `handleTurn` when OPENAI_API_KEY is missing. Constructing at module +// scope would throw before the friendly path runs. +let _openai; +function getOpenAI() { + return _openai ?? (_openai = new OpenAI()); +} + +const server = http.createServer(async (req, res) => { + setCors(res); + if (req.method === 'OPTIONS') return res.writeHead(204).end(); + + const url = new URL(req.url || '/', `http://${req.headers.host}`); + if (req.method === 'GET' && url.pathname === '/api/health') return sendJson(res, 200, { ok: true, model: MODEL }); + if (req.method === 'POST' && url.pathname === '/api/turn') return handleTurn(req, res); + sendJson(res, 404, { error: 'Not found' }); +}); + +server.listen(PORT, () => console.log(`[api] footnote tool agent on http://localhost:${PORT}`)); + +async function handleTurn(req, res) { + if (!process.env.OPENAI_API_KEY) { + return sendJson(res, 500, { error: 'OPENAI_API_KEY is not set. Copy .env.example to .env first.' }); + } + + let body; + try { body = await readJson(req); } + catch (err) { return sendJson(res, 400, { error: err.message }); } + + const messages = Array.isArray(body.messages) ? body.messages : null; + const system = typeof body.system === 'string' ? body.system : ''; + if (!messages) return sendJson(res, 400, { error: 'messages array is required' }); + + // Abort upstream if the client disconnects mid-call so we don't burn tokens. + const upstream = new AbortController(); + res.on('close', () => upstream.abort()); + + try { + const completion = await getOpenAI().chat.completions.create( + { + model: MODEL, + messages: system ? [{ role: 'system', content: system }, ...messages] : messages, + tools: TOOLS, + tool_choice: 'auto', + // One tool call per assistant turn keeps the demo lesson clean. + parallel_tool_calls: false, + }, + { signal: upstream.signal }, + ); + + const choice = completion.choices[0]; + sendJson(res, 200, { + type: 'message', + message: { + content: choice?.message?.content ?? null, + tool_calls: choice?.message?.tool_calls, + }, + }); + } catch (err) { + if (upstream.signal.aborted) return; + sendJson(res, 502, { error: err instanceof Error ? err.message : String(err) }); + } +} + +function setCors(res) { + res.setHeader('Access-Control-Allow-Origin', '*'); + res.setHeader('Access-Control-Allow-Methods', 'GET,POST,OPTIONS'); + res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); +} + +async function readJson(req) { + const MAX = 256 * 1024; + const chunks = []; + let size = 0; + for await (const chunk of req) { + size += chunk.length; + if (size > MAX) throw new Error('Request body too large'); + chunks.push(chunk); + } + if (!chunks.length) return {}; + return JSON.parse(Buffer.concat(chunks).toString('utf8')); +} + +function sendJson(res, status, payload) { + res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' }); + res.end(JSON.stringify(payload)); +} diff --git a/examples/ai/footnote-tool-agent/src/App.tsx b/examples/ai/footnote-tool-agent/src/App.tsx new file mode 100644 index 0000000000..2c5eb31617 --- /dev/null +++ b/examples/ai/footnote-tool-agent/src/App.tsx @@ -0,0 +1,231 @@ +import { useEffect, useRef, useState } from 'react'; +import { SuperDoc } from 'superdoc'; +import type { DocumentApi } from 'superdoc'; +import { addFootnoteCitation } from './tool'; +import { runAgentTurn, type AssistantReply, type ChatMessage, type ToolHandler } from './agent'; + +const SYSTEM_PROMPT = `You are a writing assistant for a document the user is editing. +You have one tool, addFootnoteCitation, that inserts a footnote at the user's cursor. +When the user asks to cite a source or add a footnote, extract the source text from the request and call the tool. +Use only source details present in the user request. Do not invent authors, titles, publishers, or years. If the request is underspecified, pass the user's wording verbatim as sourceText. +If the tool returns ok: false, briefly tell the user the reason. If ok: true, confirm in one short sentence. +Do not narrate what you're about to do. Just call the tool, then summarize the result.`; + +const SEED = + '# Reliability brief\n\n' + + 'Cloud-native teams converged on a small set of reliability patterns over the past decade. ' + + 'Click into this paragraph to position the cursor, then ask the assistant to add a footnote citation.'; + +const EMPTY_DOC = { type: 'doc', content: [{ type: 'paragraph' }] }; + +type ChatRow = + | { kind: 'user'; text: string } + | { kind: 'assistant'; text: string } + | { kind: 'tool'; name: string; ok: boolean; reason?: string }; + +export default function App() { + const [prompt, setPrompt] = useState('Add a footnote citing Doe, "Cloud Reliability Patterns," 2024.'); + const [running, setRunning] = useState(false); + const [editorReady, setEditorReady] = useState(false); + const [error, setError] = useState(null); + const [chat, setChat] = useState([]); + + const containerRef = useRef(null); + const superdocRef = useRef(null); + const abortRef = useRef(null); + + useEffect(() => { + if (!containerRef.current) return; + const sd = new SuperDoc({ + selector: containerRef.current, + documentMode: 'editing', + jsonOverride: EMPTY_DOC, + modules: { comments: false }, + telemetry: { enabled: false }, + onReady: ({ superdoc }) => { + const api = (superdoc as SuperDoc).activeEditor?.doc; + if (!api) return; + const cleared = api.clearContent({}); + if (!cleared.success && cleared.failure?.code !== 'NO_OP') return; + api.insert({ value: SEED, type: 'markdown' }); + setEditorReady(true); + }, + }); + superdocRef.current = sd; + return () => { + abortRef.current?.abort(); + sd.destroy(); + superdocRef.current = null; + }; + }, []); + + const send = async () => { + // `abortRef.current` is the synchronous in-flight guard. `running` is + // React state and is batched, so a fast double-trigger (double-click, + // Cmd+Enter twice within one frame) can pass a state-based guard before + // setRunning(true) takes effect and start two loops that overwrite each + // other's abort controller. The ref check holds because refs update + // immediately, not on the next render. + const userText = prompt.trim(); + const api = superdocRef.current?.activeEditor?.doc; + if (!api || abortRef.current || !userText) return; + + const controller = new AbortController(); + abortRef.current = controller; + setPrompt(''); + setError(null); + setChat((c) => [...c, { kind: 'user', text: userText }]); + setRunning(true); + + // Bind editor.doc once so handlers are plain (args) → result functions. + // Add more tools by appending entries to this object and mirroring + // their JSON schema in server.mjs TOOLS. + const handlers: Record = { + addFootnoteCitation: (args) => addFootnoteCitation(api, args as { sourceText: string }), + }; + + try { + await runAgentTurn({ + userText, + handlers, + postTurn: (messages, signal) => postTurn(messages, SYSTEM_PROMPT, signal), + onEvent: (event) => { + setChat((c) => + event.kind === 'assistant' + ? [...c, { kind: 'assistant', text: event.text }] + : [...c, { kind: 'tool', name: event.name, ok: event.result.ok, reason: event.result.ok ? undefined : event.result.reason }], + ); + }, + signal: controller.signal, + }); + } catch (err) { + if ((err as Error).name === 'AbortError') return; + setError((err as Error).message || String(err)); + } finally { + // Clear only if it's still our controller — guards against accidental + // clearing if a future edit allows overlapping turns. + if (abortRef.current === controller) { + abortRef.current = null; + } + setRunning(false); + } + }; + + const stop = () => abortRef.current?.abort(); + + return ( +
+