Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
edde1aa
feat(superdoc/ui): createSuperDocUI skeleton + selector substrate (SD…
caio-pizzol Apr 28, 2026
9eefaf5
fix(superdoc/ui): route selection through PresentationEditor + expose…
caio-pizzol Apr 28, 2026
b595c58
fix(superdoc/ui): refcount selector listener so unsubscribed selector…
caio-pizzol Apr 28, 2026
4733adb
feat(superdoc/ui): toolbar domain + per-command observables (SD-2796)…
caio-pizzol Apr 28, 2026
fb69482
feat(superdoc/ui): comments domain (subscribe + actions) (SD-2790) (#…
caio-pizzol Apr 29, 2026
9b382fb
feat(superdoc/ui): review domain (comments + tracked changes feed) (S…
caio-pizzol Apr 29, 2026
0db5bcf
feat(superdoc/ui): viewport domain (rect + scrollIntoView) (SD-2793) …
caio-pizzol Apr 29, 2026
87baae7
chore(document-api): remove UI-shaped surfaces (SD-2795) (#2988)
caio-pizzol Apr 29, 2026
b4f2fc7
feat(superdoc/ui): rich state.selection slice + activeCommentIds/acti…
caio-pizzol Apr 29, 2026
3976e01
fix(superdoc/ui): tsc -b clean + codecov d.ts ignore + consumer-typec…
caio-pizzol Apr 29, 2026
c2fd998
test(behavior): superdoc/ui activeCommentIds + viewport.getRect specs…
caio-pizzol Apr 29, 2026
0f3f9bb
fix(superdoc/ui): viewport.getRect routes through host PresentationEd…
caio-pizzol Apr 29, 2026
9c8fd1f
feat(superdoc/ui): expose toolbar/commands types via superdoc/ui (PR …
caio-pizzol Apr 29, 2026
1f9384c
feat(superdoc/ui): ui.selection handle + narrow viewport target type …
caio-pizzol Apr 29, 2026
20f6c12
feat(superdoc/ui): drop ui.review.setRecording — name lied about beha…
caio-pizzol Apr 29, 2026
71032c8
chore(super-editor): add test:ui / test:ui:watch scripts (PR #2979 re…
caio-pizzol Apr 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,10 @@ coverage:
default:
target: 90%
threshold: 0%

# Type-only declaration files (`.d.ts`) carry no runnable code — they
# describe shapes consumed at compile time. Coverage tools count any
# changed line as "0% covered" because there's nothing to instrument.
# Excluding them keeps the patch coverage signal honest.
ignore:
- '**/*.d.ts'
22 changes: 3 additions & 19 deletions packages/document-api/scripts/check-contract-parity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,26 +22,10 @@ import { OPERATION_REFERENCE_DOC_PATH_MAP } from '../src/contract/reference-doc-
import { buildDispatchTable } from '../src/invoke/invoke.js';

/**
* Meta-methods and helper methods on DocumentApi that are not contract
* operations:
*
* - `ranges.scrollIntoView` is a browser-only UI side-effect (scrolls
* the viewport via the presentation editor). It has no headless
* implementation, so it is intentionally excluded from the RPC
* dispatch surface and the CLI command catalog. Direct calls through
* `editor.doc.ranges.scrollIntoView()` are still supported.
* - `selection.onChange` is a subscription primitive (push-based, no
* request/response shape) rather than a request-response operation,
* so it is not represented in `OPERATION_DEFINITIONS` / schemas /
* dispatch. Direct calls through `editor.doc.selection.onChange()`
* are still supported.
* Meta-methods on DocumentApi that are not contract operations: the
* dispatcher itself plus the documented reference aliases.
*/
const META_MEMBER_PATHS = [
'invoke',
'ranges.scrollIntoView',
'selection.onChange',
...REFERENCE_OPERATION_ALIASES.map((alias) => alias.memberPath),
];
const META_MEMBER_PATHS = ['invoke', ...REFERENCE_OPERATION_ALIASES.map((alias) => alias.memberPath)];

function collectFunctionMemberPaths(value: unknown, prefix = ''): string[] {
if (!value || typeof value !== 'object') return [];
Expand Down
12 changes: 0 additions & 12 deletions packages/document-api/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2173,18 +2173,6 @@ describe('createDocumentApi', () => {
expect(e.code).toBe('SELECTION_ADAPTER_UNAVAILABLE');
}
});

it('throws SELECTION_ADAPTER_UNAVAILABLE when selection.onChange is called without a selection adapter', () => {
const api = makeApiWithoutSelection();
try {
api.selection.onChange(() => {});
expect.fail('expected SELECTION_ADAPTER_UNAVAILABLE to be thrown');
} catch (err: unknown) {
const e = err as { name: string; code: string };
expect(e.name).toBe('DocumentApiValidationError');
expect(e.code).toBe('SELECTION_ADAPTER_UNAVAILABLE');
}
});
});

describe('comments.patch target validation', () => {
Expand Down
50 changes: 4 additions & 46 deletions packages/document-api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,9 @@ export type {
RangeResolverAdapter,
ScrollIntoViewInput,
ScrollIntoViewOutput,
RangeScrollAdapter,
} from './ranges/index.js';
export { executeResolveRange, executeScrollIntoView } from './ranges/index.js';
export type {
SelectionApi,
SelectionAdapter,
SelectionCurrentInput,
SelectionInfo,
SelectionChangeListener,
} from './selection/selection.js';
export { executeResolveRange } from './ranges/index.js';
export type { SelectionApi, SelectionAdapter, SelectionCurrentInput, SelectionInfo } from './selection/selection.js';
export { executeSelectionCurrent } from './selection/selection.js';
export type { HeaderFootersAdapter, HeaderFootersApi } from './header-footers/header-footers.js';
export * from './header-footers/header-footers.types.js';
Expand Down Expand Up @@ -136,22 +129,9 @@ import {
import type { InsertInput } from './insert/insert.js';
import { executeDelete } from './delete/delete.js';
import { executeResolveRange } from './ranges/resolve.js';
import { executeScrollIntoView } from './ranges/scroll-into-view.js';
import type {
RangeResolverAdapter,
ResolveRangeInput,
ResolveRangeOutput,
ScrollIntoViewInput,
ScrollIntoViewOutput,
} from './ranges/ranges.types.js';
import type { RangeResolverAdapter, ResolveRangeInput, ResolveRangeOutput } from './ranges/ranges.types.js';
import { executeSelectionCurrent } from './selection/selection.js';
import type {
SelectionApi,
SelectionAdapter,
SelectionCurrentInput,
SelectionInfo,
SelectionChangeListener,
} from './selection/selection.js';
import type { SelectionApi, SelectionAdapter, SelectionCurrentInput, SelectionInfo } from './selection/selection.js';
import { executeInsert } from './insert/insert.js';
import type { ListsAdapter, ListsApi } from './lists/lists.js';
import type {
Expand Down Expand Up @@ -1508,19 +1488,10 @@ export interface MutationsApi {

export interface RangesApi {
resolve(input: ResolveRangeInput): ResolveRangeOutput;
/**
* Scroll the editor viewport so the target range is visible. Handles
* paginated, virtualized layouts by mounting the target page on demand.
* Async — resolves once the scroll settles (or the page-mount timeout
* expires). Target accepts `TextAddress`, `TextTarget`, or `EntityAddress`
* (comment / tracked change by id).
*/
scrollIntoView(input: ScrollIntoViewInput): Promise<ScrollIntoViewOutput>;
}

export interface RangesAdapter {
resolve(input: ResolveRangeInput): ResolveRangeOutput;
scrollIntoView(input: ScrollIntoViewInput): Promise<ScrollIntoViewOutput>;
}

export interface QueryAdapter {
Expand Down Expand Up @@ -3184,9 +3155,6 @@ export function createDocumentApi(adapters: DocumentApiAdapters): DocumentApi {
resolve(input: ResolveRangeInput): ResolveRangeOutput {
return executeResolveRange(adapters.ranges, input);
},
scrollIntoView(input: ScrollIntoViewInput): Promise<ScrollIntoViewOutput> {
return executeScrollIntoView(adapters.ranges, input);
},
},
selection: {
current(input?: SelectionCurrentInput): SelectionInfo {
Expand All @@ -3199,16 +3167,6 @@ export function createDocumentApi(adapters: DocumentApiAdapters): DocumentApi {
}
return executeSelectionCurrent(adapter, input);
},
onChange(listener: SelectionChangeListener): () => void {
const adapter = adapters.selection;
if (!adapter) {
throw new DocumentApiValidationError(
'SELECTION_ADAPTER_UNAVAILABLE',
'No selection adapter was registered. Pass `selection` in DocumentApiAdapters to call selection.onChange().',
);
}
return adapter.onChange(listener);
},
},
mutations: {
preview(input: MutationsPreviewInput): MutationsPreviewOutput {
Expand Down
2 changes: 0 additions & 2 deletions packages/document-api/src/ranges/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,5 @@ export type {
RangeResolverAdapter,
ScrollIntoViewInput,
ScrollIntoViewOutput,
RangeScrollAdapter,
} from './ranges.types.js';
export { executeResolveRange } from './resolve.js';
export { executeScrollIntoView } from './scroll-into-view.js';
23 changes: 8 additions & 15 deletions packages/document-api/src/ranges/ranges.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,13 +122,14 @@ export interface RangeResolverAdapter {
}

// ---------------------------------------------------------------------------
// scrollIntoView
// scrollIntoView — input/output value types
// ---------------------------------------------------------------------------

/**
* Input for `ranges.scrollIntoView` — scrolls the editor viewport so the
* given text target is visible. Handles paginated, virtualized layouts by
* mounting the target page if it isn't yet in the DOM.
* Input for `ui.viewport.scrollIntoView` — scrolls the editor
* viewport so the given target is visible. Handles paginated,
* virtualized layouts by mounting the target page if it isn't yet in
* the DOM.
*/
export interface ScrollIntoViewInput {
/**
Expand All @@ -146,18 +147,10 @@ export interface ScrollIntoViewInput {
}

/**
* Result of `ranges.scrollIntoView`.
* `success: false` when the target couldn't be resolved or a page failed to
* mount within the navigation timeout.
* Result of `ui.viewport.scrollIntoView`. `success: false` when the
* target couldn't be resolved or a page failed to mount within the
* navigation timeout.
*/
export interface ScrollIntoViewOutput {
success: boolean;
}

/**
* Adapter method for `ranges.scrollIntoView`. Async because virtualized
* pages may need to mount before the scroll completes.
*/
export interface RangeScrollAdapter {
scrollIntoView(input: ScrollIntoViewInput): Promise<ScrollIntoViewOutput>;
}
175 changes: 0 additions & 175 deletions packages/document-api/src/ranges/scroll-into-view.test.ts

This file was deleted.

Loading
Loading