diff --git a/codecov.yml b/codecov.yml index da07cff790..4cd290c72e 100644 --- a/codecov.yml +++ b/codecov.yml @@ -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' diff --git a/packages/super-editor/src/ui/create-super-doc-ui.ts b/packages/super-editor/src/ui/create-super-doc-ui.ts index 959dc305c7..ee702dbb9e 100644 --- a/packages/super-editor/src/ui/create-super-doc-ui.ts +++ b/packages/super-editor/src/ui/create-super-doc-ui.ts @@ -843,16 +843,13 @@ export function createSuperDocUI(options: SuperDocUIOptions): SuperDocUI { return receipt; }, async scrollTo(commentId) { - // Preserve `address.story` for non-body comments so navigation - // resolves to the right header / footer / note rather than - // defaulting to body. - const story = lookupItemStory(commentId); - const target = - story != null - ? { kind: 'entity' as const, entityType: 'comment' as const, entityId: commentId, story } - : { kind: 'entity' as const, entityType: 'comment' as const, entityId: commentId }; + // `CommentAddress` is body-scoped in the contract — it has no + // `story` field today. Story-aware comment navigation lands as + // a separate doc-API extension; until then, just route the id + // and let `presentation.navigateTo` resolve through the comment + // entity store. return runScrollIntoView({ - target, + target: { kind: 'entity', entityType: 'comment', entityId: commentId }, block: 'center', behavior: 'smooth', }); @@ -990,14 +987,22 @@ export function createSuperDocUI(options: SuperDocUIOptions): SuperDocUI { }, async scrollTo(id) { const kind = entityKindForId(id); - const entityType = kind === 'change' ? 'trackedChange' : 'comment'; activeReviewId = id; scheduleNotify(); - const story = lookupItemStory(id); - const target = - story != null - ? { kind: 'entity' as const, entityType, entityId: id, story } - : { kind: 'entity' as const, entityType, entityId: id }; + // `EntityAddress` is a discriminated union: `CommentAddress` + // doesn't carry a `story` field, only `TrackedChangeAddress` + // does. Branch on `kind` so the constructed target matches the + // right union member exactly. + let target: import('@superdoc/document-api').EntityAddress; + if (kind === 'change') { + const story = lookupItemStory(id) as import('@superdoc/document-api').TrackedChangeAddress['story']; + target = + story != null + ? { kind: 'entity', entityType: 'trackedChange', entityId: id, story } + : { kind: 'entity', entityType: 'trackedChange', entityId: id }; + } else { + target = { kind: 'entity', entityType: 'comment', entityId: id }; + } return runScrollIntoView({ target, block: 'center', diff --git a/packages/super-editor/src/ui/types.ts b/packages/super-editor/src/ui/types.ts index ff97a9393c..41281056d6 100644 --- a/packages/super-editor/src/ui/types.ts +++ b/packages/super-editor/src/ui/types.ts @@ -62,6 +62,8 @@ export interface SuperDocEditorLike { empty: boolean; text?: string; target?: unknown; + /** Active mark names at the caret / across the selection. */ + activeMarks?: string[]; /** Present after SD-2792; absent on older builds — controller falls back to []. */ activeCommentIds?: string[]; activeChangeIds?: string[]; diff --git a/tests/consumer-typecheck/src/customer-scenario.ts b/tests/consumer-typecheck/src/customer-scenario.ts index 3a4b443a16..3e2006e5a6 100644 --- a/tests/consumer-typecheck/src/customer-scenario.ts +++ b/tests/consumer-typecheck/src/customer-scenario.ts @@ -834,6 +834,88 @@ function testVueComponents() { const slashMenu = SlashMenu; } +// ============================================ +// SECTION 18: superdoc/ui sub-entry — `createSuperDocUI({ superdoc })` +// ============================================ + +/** + * Type-level smoke test for the published `superdoc/ui` sub-entry. + * + * Mirrors the `superdoc/headless-toolbar` shim pattern: this module + * is a thin re-export of the browser-only UI controller from + * `@superdoc/super-editor`. Without a consumer-perspective import, + * the published sub-entry would only be type-checked from inside the + * monorepo and a broken re-export could ship undetected. + */ +import { + createSuperDocUI, + shallowEqual, + type CommentsHandle, + type CommentsSlice, + type EqualityFn, + type ReviewHandle, + type ReviewItem, + type ReviewSlice, + type SelectionSlice, + type SelectorFn, + type Subscribable, + type SuperDocEditorLike, + type SuperDocLike, + type SuperDocUI, + type SuperDocUIOptions, + type SuperDocUIState, + type ViewportGetRectInput, + type ViewportHandle, + type ViewportRect, + type ViewportRectResult, +} from 'superdoc/ui'; + +function testSuperDocUISubEntry() { + // Runtime exports compile and have callable shapes. + const factory: (options: SuperDocUIOptions) => SuperDocUI = createSuperDocUI; + const eq: EqualityFn = shallowEqual; + void factory; + void eq; + + // Public handle / slice types resolve through the sub-entry. + type AssertHandles = { + toolbar: SuperDocUI['toolbar']; + commands: SuperDocUI['commands']; + comments: CommentsHandle; + review: ReviewHandle; + viewport: ViewportHandle; + state: SuperDocUIState; + }; + type AssertSlices = { + selection: SelectionSlice; + comments: CommentsSlice; + review: ReviewSlice; + reviewItem: ReviewItem; + }; + type AssertViewportShapes = { + input: ViewportGetRectInput; + rect: ViewportRect; + result: ViewportRectResult; + }; + type AssertSubstrate = { + selector: SelectorFn; + sub: Subscribable; + }; + type AssertHostShapes = { + superdoc: SuperDocLike; + editor: SuperDocEditorLike; + }; + + // `void` the type aliases so the file stays a smoke test, not a + // sample. Touching each at value level via `null as never` keeps + // the typechecker honest without runtime work. + void (null as never as AssertHandles); + void (null as never as AssertSlices); + void (null as never as AssertViewportShapes); + void (null as never as AssertSubstrate); + void (null as never as AssertHostShapes); +} + export { testTypeShapes, testEditorOptions, @@ -860,4 +942,5 @@ export { testAdditionalFunctions, testAdditionalClasses, testVueComponents, + testSuperDocUISubEntry, };