Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
90ed709
🔄 Sync stable → main
superdoc-bot[bot] Jun 20, 2026
251a1fd
Merge pull request #160 from superdoc/luccas/sd-3478-bug-atomic-track…
chittolinag Jun 23, 2026
6184e25
Merge pull request #162 from superdoc/artem/doc-diffing-v5
luccas-harbour Jun 23, 2026
b55220f
Merge pull request #148 from superdoc/colep/sd-3403-bug-localized-wor…
luccas-harbour Jun 23, 2026
73f7514
Merge pull request #89 from superdoc/artem/SD-3318
luccas-harbour Jun 23, 2026
d5175a6
Merge pull request #159 from superdoc/colep/sd-3483-collab-mergefield…
luccas-harbour Jun 23, 2026
33172f4
Merge pull request #128 from superdoc/sd-3453_font-picker-dropdown
luccas-harbour Jun 23, 2026
873dd25
chore: reversions
harbournick Jun 23, 2026
71fb047
Merge pull request #180 from superdoc/caio-pizzol/editing-mode-direct…
caio-pizzol Jun 24, 2026
6ea2c20
Merge pull request #158 from superdoc/sd-3465_nested-sdt
chittolinag Jun 24, 2026
05918fe
chore: merge stable into main
caio-pizzol Jun 24, 2026
bd5002f
fix(plan-engine): use coarse tracked rewrite for large single changes…
luccas-harbour Jun 24, 2026
75297f6
Merge pull request #194 from superdoc/caio-pizzol/document-host-runtime
caio-pizzol Jun 25, 2026
7c1a897
fix(toolbar): toolbar should be blank with multiple fonts selected
palmer-cl Jun 17, 2026
46ae454
fix: image rendering inside textbox
VladaHarbour Jun 15, 2026
2b50f53
fix: table rendering fidelity round 2 (SD-3028: borders, styles, widt…
tupizz Jun 5, 2026
bbcc04f
Merge remote-tracking branch 'origin/main' into merge/main-into-stabl…
github-actions[bot] Jun 26, 2026
5e4fd0e
fix(painter-dom): align thick-border tests with authored-width render…
caio-pizzol Jun 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
26 changes: 1 addition & 25 deletions apps/cli/src/__tests__/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ describe('superdoc CLI', () => {
});

test('describe command paragraph format ops do not advertise text-range shortcuts', async () => {
const result = await runCli(['describe', 'command', 'doc.format.paragraph.setMarkRunProps', '--output', 'pretty']);
const result = await runCli(['describe', 'command', 'doc.format.paragraph.setAlignment', '--output', 'pretty']);
expect(result.code).toBe(0);
expect(result.stdout).toContain('--block-id');
expect(result.stdout).not.toContain('--start');
Expand Down Expand Up @@ -1696,30 +1696,6 @@ describe('superdoc CLI', () => {
expect(await readDocxPart(footnotesOut, 'word/document.xml')).toContain('footnoteReference');
});

test('paragraph mark run props surface CAPABILITY_UNAVAILABLE on the v1 runtime', async () => {
const formatSource = join(TEST_DIR, 'paragraph-mark-run-props-source.docx');
const formatOut = join(TEST_DIR, 'paragraph-mark-run-props-out.docx');
await copyFile(SAMPLE_DOC, formatSource);

const target = await firstTextRange(['find', formatSource, '--type', 'text', '--pattern', 'Wilde']);
const result = await runCli([
'format',
'paragraph',
'set-mark-run-props',
formatSource,
'--block-id',
target.blockId,
'--mark-run-props-json',
'{"bold":true}',
'--out',
formatOut,
]);

expect(result.code).toBe(1);
const envelope = parseJsonOutput<ErrorEnvelope>(result);
expect(envelope.error.code).toBe('CAPABILITY_UNAVAILABLE');
});

test('track-changes list is capability-aware', async () => {
const result = await runCli(['track-changes', 'list', SAMPLE_DOC]);
if (result.code === 0) {
Expand Down
22 changes: 22 additions & 0 deletions apps/cli/src/__tests__/lib/context.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,28 @@ describe('normalizeContextMetadata', () => {
});
});

describe('runtime normalization', () => {
test('preserves the supported v1 runtime', () => {
const result = normalizeContextMetadata(makeMetadata({ runtime: 'v1' }));
expect(result.runtime).toBe('v1');
});

test('defaults an absent runtime to v1', () => {
const result = normalizeContextMetadata(makeMetadata({ runtime: undefined as any }));
expect(result.runtime).toBe('v1');
});

test('normalizes stale unsupported runtime metadata to v1', () => {
const metadata = makeMetadata({ runtime: 'legacy-runtime' as any });
expect(normalizeContextMetadata(metadata).runtime).toBe('v1');
});

test('normalizes any unknown runtime value to v1', () => {
const metadata = makeMetadata({ runtime: 'v3' as any });
expect(normalizeContextMetadata(metadata).runtime).toBe('v1');
});
});

describe('session type normalization', () => {
test('normalizes unknown session type to local', () => {
const metadata = makeMetadata({ sessionType: 'unknown' as any });
Expand Down
56 changes: 25 additions & 31 deletions apps/cli/src/__tests__/lib/error-mapping.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,20 +135,17 @@ describe('mapInvokeError', () => {
expect(canonical.code).toBe('TARGET_NOT_FOUND');
});

test.each([
'blocks.split',
'lists.apply',
'format.paragraph.setMarkRunProps',
'tables.moveRow',
'fields.insert',
] as const)('%s preserves CAPABILITY_UNAVAILABLE for unsupported v1/v2-gated paths', (operationId) => {
const error = Object.assign(new Error(`${operationId} is only available on v2-backed sessions.`), {
code: 'CAPABILITY_UNAVAILABLE',
});
test.each(['fields.insert', 'footnotes.insert'] as const)(
'%s preserves CAPABILITY_UNAVAILABLE for adapter-gated paths',
(operationId) => {
const error = Object.assign(new Error(`${operationId} is not available on this adapter.`), {
code: 'CAPABILITY_UNAVAILABLE',
});

const mapped = mapInvokeError(operationId as any, error);
expect(mapped.code).toBe('CAPABILITY_UNAVAILABLE');
});
const mapped = mapInvokeError(operationId as any, error);
expect(mapped.code).toBe('CAPABILITY_UNAVAILABLE');
},
);
});

// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -523,25 +520,22 @@ describe('mapFailedReceipt: plan-engine code passthrough', () => {
},
);

test.each([
'blocks.split',
'lists.apply',
'format.paragraph.setMarkRunProps',
'tables.moveRow',
'footnotes.insert',
] as const)('%s failed receipts preserve CAPABILITY_UNAVAILABLE', (operationId) => {
const receipt = {
success: false,
failure: {
code: 'CAPABILITY_UNAVAILABLE',
message: `${operationId} is only available on v2-backed sessions.`,
},
};
test.each(['fields.insert', 'footnotes.insert'] as const)(
'%s failed receipts preserve CAPABILITY_UNAVAILABLE',
(operationId) => {
const receipt = {
success: false,
failure: {
code: 'CAPABILITY_UNAVAILABLE',
message: `${operationId} is not available on this adapter.`,
},
};

const result = mapFailedReceipt(operationId as any, receipt);
expect(result).toBeInstanceOf(CliError);
expect(result!.code).toBe('CAPABILITY_UNAVAILABLE');
});
const result = mapFailedReceipt(operationId as any, receipt);
expect(result).toBeInstanceOf(CliError);
expect(result!.code).toBe('CAPABILITY_UNAVAILABLE');
},
);
});

// ---------------------------------------------------------------------------
Expand Down
18 changes: 5 additions & 13 deletions apps/cli/src/__tests__/lib/invoke-input.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,9 @@ describe('extractInvokeInput', () => {
});

test('converts paragraph format --block-id shortcuts into paragraph block targets', () => {
const input = extractInvokeInput('format.paragraph.setMarkRunProps', {
const input = extractInvokeInput('format.paragraph.setAlignment', {
blockId: 'p1',
markRunProps: {
bold: true,
color: { model: 'rgb', value: 'FF0000' },
},
alignment: 'center',
}) as Record<string, unknown>;

expect(input).toEqual({
Expand All @@ -123,22 +120,17 @@ describe('extractInvokeInput', () => {
nodeType: 'paragraph',
nodeId: 'p1',
},
markRunProps: {
bold: true,
color: { model: 'rgb', value: 'FF0000' },
},
alignment: 'center',
});
});

test('rejects text-range shortcuts for paragraph format operations', () => {
expect(() =>
extractInvokeInput('format.paragraph.setMarkRunProps', {
extractInvokeInput('format.paragraph.setAlignment', {
blockId: 'p1',
start: 0,
end: 5,
markRunProps: {
bold: true,
},
alignment: 'center',
}),
).toThrow(CliError);
});
Expand Down
6 changes: 3 additions & 3 deletions apps/cli/src/__tests__/lib/operation-args.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import { parseOperationArgs } from '../../lib/operation-args';
describe('parseOperationArgs target validation', () => {
test('rejects legacy text-address target-json payloads for paragraph format operations', () => {
expect(() =>
parseOperationArgs('doc.format.paragraph.setMarkRunProps', [
parseOperationArgs('doc.format.paragraph.setAlignment', [
'--target-json',
'{"kind":"text","blockId":"p1","range":{"start":0,"end":4}}',
'--mark-run-props-json',
'{"bold":true}',
'--alignment',
'center',
]),
).toThrow(CliError);
});
Expand Down
14 changes: 11 additions & 3 deletions apps/cli/src/__tests__/lib/operation-runtime-metadata.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,14 @@ describe('operation runtime metadata', () => {
expect(trackChangesOption!.type).toBe('string');
});

test('doc.open metadata does not expose a runtime selector', () => {
const openMeta = CLI_OPERATION_METADATA['doc.open'];
const openOptions = CLI_OPERATION_OPTION_SPECS['doc.open'];

expect(openMeta.params.map((p) => p.name)).not.toContain('runtime');
expect(openOptions.map((o) => o.name)).not.toContain('runtime');
});

test('final recipe-provider parity operations expose their promoted CLI params', () => {
const blocksListMeta = CLI_OPERATION_METADATA['doc.blocks.list'];
expect(blocksListMeta.params.find((p) => p.name === 'in')?.flag).toBe('in-json');
Expand All @@ -118,12 +126,12 @@ describe('operation runtime metadata', () => {
expect(fieldsInsertMeta.params.find((p) => p.name === 'cachedResultText')?.flag).toBe('cached-result-text');
expect(fieldsInsertMeta.params.find((p) => p.name === 'updatePolicy')?.flag).toBe('update-policy');

const markRunMeta = CLI_OPERATION_METADATA['doc.format.paragraph.setMarkRunProps'];
expect(markRunMeta.params.find((p) => p.name === 'markRunProps')?.flag).toBe('mark-run-props-json');
const alignmentMeta = CLI_OPERATION_METADATA['doc.format.paragraph.setAlignment'];
expect(alignmentMeta.params.find((p) => p.name === 'alignment')?.flag).toBe('alignment');
});

test('paragraph format operations expose block shortcuts without text-range flags', () => {
const paragraphFormatMeta = CLI_OPERATION_METADATA['doc.format.paragraph.setMarkRunProps'];
const paragraphFormatMeta = CLI_OPERATION_METADATA['doc.format.paragraph.setAlignment'];
const paragraphParamNames = paragraphFormatMeta.params.map((p) => p.name);

expect(paragraphParamNames).toContain('blockId');
Expand Down

This file was deleted.

16 changes: 0 additions & 16 deletions apps/cli/src/__tests__/lib/v2-runtime-import-boundaries.test.ts

This file was deleted.

40 changes: 0 additions & 40 deletions apps/cli/src/__tests__/runtime-v2-open.test.ts

This file was deleted.

2 changes: 1 addition & 1 deletion apps/cli/src/cli/__tests__/schema-ref-resolution.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ describe('operation-params deriveParamsFromInputSchema with $ref', () => {
});

describe('operation-params production metadata', () => {
test('trackChanges.decide exposes nested logical range targets used by v2 requirements', () => {
test('trackChanges.decide exposes nested logical range targets', () => {
const targetParam = CLI_OPERATION_METADATA['doc.trackChanges.decide'].params.find(
(param) => param.name === 'target',
);
Expand Down
12 changes: 6 additions & 6 deletions apps/cli/src/cli/cli-only-operation-definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,15 @@ export const CLI_ONLY_OPERATION_DEFINITIONS: Record<CliOnlyOperation, CliOnlyOpe
open: {
category: 'session',
description:
"Open a document and create a persistent editing session. Pass `runtime: 'v1'` (default) or `runtime: 'v2'` to select the underlying engine. V2 single-socket collaboration supports only `y-websocket`; `hocuspocus` and `liveblocks` remain legacy v1-only collaboration providers. Optionally override the document body with contentOverride + overrideType (markdown, html, or text).",
'Open a document and create a persistent editing session. Collaboration supports the `y-websocket`, `hocuspocus`, and `liveblocks` providers. Optionally override the document body with contentOverride + overrideType (markdown, html, or text).',
requiresDocumentContext: false,
sdkMetadata: { mutates: true, idempotency: 'non-idempotent', supportsTrackedMode: false, supportsDryRun: false },
outputSchema: {
type: 'object',
properties: {
active: { type: 'boolean' },
contextId: { type: 'string' },
runtime: { type: 'string', enum: ['v1', 'v2'] },
runtime: { type: 'string', enum: ['v1'] },
sessionType: { type: 'string' },
document: {
type: 'object',
Expand Down Expand Up @@ -96,7 +96,7 @@ export const CLI_ONLY_OPERATION_DEFINITIONS: Record<CliOnlyOperation, CliOnlyOpe
type: 'object',
properties: {
contextId: { type: 'string' },
runtime: { type: 'string', enum: ['v1', 'v2'] },
runtime: { type: 'string', enum: ['v1'] },
saved: { type: 'boolean' },
inPlace: { type: 'boolean' },
document: {
Expand Down Expand Up @@ -152,7 +152,7 @@ export const CLI_ONLY_OPERATION_DEFINITIONS: Record<CliOnlyOperation, CliOnlyOpe
type: 'object',
properties: {
contextId: { type: 'string' },
runtime: { type: 'string', enum: ['v1', 'v2'] },
runtime: { type: 'string', enum: ['v1'] },
closed: { type: 'boolean' },
saved: { type: 'boolean' },
discarded: { type: 'boolean' },
Expand Down Expand Up @@ -218,7 +218,7 @@ export const CLI_ONLY_OPERATION_DEFINITIONS: Record<CliOnlyOperation, CliOnlyOpe
properties: {
active: { type: 'boolean' },
contextId: { type: 'string' },
runtime: { type: 'string', enum: ['v1', 'v2'] },
runtime: { type: 'string', enum: ['v1'] },
activeSessionId: { type: 'string' },
requestedSessionId: { type: 'string' },
projectRoot: { type: 'string' },
Expand Down Expand Up @@ -313,7 +313,7 @@ export const CLI_ONLY_OPERATION_DEFINITIONS: Record<CliOnlyOperation, CliOnlyOpe
properties: {
sessionId: { type: 'string' },
sessionType: { type: 'string' },
runtime: { type: 'string', enum: ['v1', 'v2'] },
runtime: { type: 'string', enum: ['v1'] },
dirty: { type: 'boolean' },
revision: { type: 'number' },
collaboration: {
Expand Down
4 changes: 1 addition & 3 deletions apps/cli/src/cli/command-examples.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,5 @@ export const DOC_COMMAND_EXAMPLES: Readonly<Record<string, readonly string[]>> =
],

// ── Paragraph Format ────────────────────────────────────────────────
'format.paragraph.setMarkRunProps': [
'superdoc format paragraph set-mark-run-props --block-id abc123 --mark-run-props-json \'{"bold":true,"italic":true,"color":{"model":"rgb","value":"FF0000"}}\'',
],
'format.paragraph.setAlignment': ['superdoc format paragraph set-alignment --block-id abc123 --alignment center'],
};
Loading
Loading