Merge main into stable#3580
Conversation
The remediation message suggested 'pnpm build (or pnpm run type-check
to emit declarations only).' That alternative is wrong:
- packages/superdoc/tsconfig.types.json sets outDir to dist-types,
not dist.
- The audit walks packages/superdoc/dist (the consumer-visible
tree via package.json exports).
- So pnpm run type-check alone leaves packages/superdoc/dist empty
and the audit keeps failing with the same structural error.
Now recommends pnpm build only, in three places:
- script header doc
- per-package missing-target error
- final structural-failure hint
README row updated to match.
The script behavior is unchanged; only the user-facing remediation
hint was misleading.
Locks in the public Document API surface for the customer's preview-pane pattern (IT-1116). Three sessions, base and preview sharing identical content; target produces the snapshot; main session computes the diff; the diff is applied to the *separately-opened* preview session as tracked changes. Pre-SD-3279 this throws PRECONDITION_FAILED because the two sessions' canonical fingerprints diverge on session-local sdBlockId values. The service-level integration test in diff-service.test.ts already covers this; the story sits at the SDK/CLI dispatch tier that customers actually use, so a regression in the public path would be caught even if the internal helper kept passing. Verified end-to-end via vitest run against the rebuilt CLI + SDK on the fix branch.
The earlier story commit verified apply result + tracked changes total. Adds the customer-visible side: the preview session's body text now contains the target paragraph after the diff is applied. Same single test, one extra assertion. Catches a class of regression where the apply succeeds in tracking-changes accounting but never lands the underlying content.
The previous SD-3279 commit changed the canonical normalization (stripped sdBlockId / sdBlockRev) without bumping the snapshot/payload version. Any DiffSnapshot persisted by a customer under the pre-fix algorithm would now be rejected as tampered when re-derivation runs through the new normalizer. Same risk for DiffPayloads in same-session reapply. Add a parallel legacy normalization path that reproduces the pre-fix behavior exactly (paragraph-only attribute stripping over the original 7-element set, no stripping on structural containers, image originalAttributes stripping unchanged). compareToSnapshot and applyDiffPayload now accept either the new or the legacy fingerprint: re-derive with the current normalizer first; on mismatch, retry with the legacy normalizer. Genuinely tampered artifacts fail both checks and still throw. Scope of recovery: - DiffSnapshot persisted under old algorithm: fully recovered. This is the customer's revision-history workflow (capture and store). - DiffPayload persisted under old algorithm: recovered only for same-editor reapply where the editor still holds the original sdBlockIds. Cross-session reapply of old payloads was already broken by per-session sdBlockId divergence — that's the exact bug SD-3279 fixes for new snapshots; the legacy fallback can't retroactively rescue it. Tests added: - diff-service.test.ts: legacy snapshot accepted by compareToSnapshot; legacy same-editor payload accepted by applyDiffPayload; genuinely tampered snapshot still rejected. - fingerprint.test.ts: legacy normalizer produces a different fingerprint than the current normalizer for docs with sdBlockId (otherwise the fallback would be dead code). - diff-service.test.ts: SD-3282 ticket reference added to the existing export → reimport regression marker comment. Verified: 257/257 diffing tests pass; pnpm check:types clean.
Replay previously called setNodeMarkup with the diff's full source attrs, overwriting the recipient editor's session-local sdBlockId / sdBlockRev with the originator's values. Merge the AttributesDiff onto the recipient node's existing attrs so any path the diff does not touch is preserved verbatim. Also pass NON_SEMANTIC_BLOCK_ATTRS as ignoreKeys for non-paragraph block attr diffs; paragraphs were already covered via normalizeParagraphAttrs.
…t-typecheck-2 feat(docs): docs snippet typecheck for editor/superdoc/** + fix stale examples (SD-673)
…-owner-audit feat(scripts): js contract-owner audit, report-only (SD-673)
…27-171318 🔄 Sync stable → main
…D-3289) Word date pickers and similar content controls that wrap a single table cell are stored in DOCX as `<w:tr><w:sdt><w:sdtContent><w:tc> ...</w:tc></w:sdtContent></w:sdt></w:tr>` (ECMA-376 Part 1 §17.5.2.32, CT_SdtCell). The row importer previously filtered to direct `w:tc` children only, silently dropping the wrapped cell on import and from any subsequent export. The row encoder now recognizes both direct `<w:tc>` and cell-level `<w:sdt>`, routes the inner cell through the existing tc-translator, and preserves `w:sdtPr`/`w:sdtEndPr` on a new `cellSdt` attr (rendered: false, declared on `tableCell` and `tableHeader`). The row decoder re-wraps the exported `<w:tc>` in `<w:sdt>` when the attr is present, so date metadata (fullDate, dateFormat, lid, calendar) round-trips. - Cell-level controls remain outside the content-controls Document API surface for v1. - Multi-cell `CT_SdtContentCell` wrappers import all cells defensively but drop wrapper metadata; exact multi-cell grouping is out of scope. - Adds 11 unit tests plus a real table import/export round-trip integration test covering the IT-1119 OOXML shape.
…sdblockid-from-diff-fingerprint fix(diffing): strip session-local sdBlockId from diff fingerprint (SD-3279)
…SD-3289) The row encoder unwraps `<w:sdt><w:sdtContent><w:tc/></w:sdtContent></w:sdt>` into real cells, but `findTableCellAtColumn` (called from `handleTableCellNode` to mark vMerge continuations) still filtered to direct `<w:tc>` children of `<w:tr>`. As a result, a vertical merge whose continuation cell lived inside a cell-level SDT was never marked `_vMergeConsumed`; the row encoder would then re-emit it as a real cell and break the merge round-trip. Extract the row-child normalization to a shared helper (`row-cell-children.js`) and route both the row encoder and the vMerge column lookup through it. The inner `<w:tc>` is now reachable from either side; vMerge marking propagates naturally because both sides see the same node reference. Caught by automated review on PR #3539. Adds a unit test covering a vMerge column with an SDT-wrapped continuation cell.
…ve-cell-level-sdt fix(super-converter): preserve cell-level SDT wrapping table cells (SD-3289)
…27-230540 🔄 Sync stable → main
…red content - Restore normal caret placement inside block and inline SDTs; remove SD-1584 whole-node selection on body click and the first-click select-all behavior on inline SDT content. - Rewrite label click handling in EditorInputManager: move pointerdown to the visible host with capture, fall back to elementsFromPoint to resolve hover affordances, and apply NodeSelection (with pm-start retry) on label hits. - Exclude `superdoc-structured-content__label` and inline-label chrome from layout/dom pointer mapping so label rects don't bleed into body caret resolution. - Drop sdt-group-hover style that revealed the block label without selection. - Add SD-3237 behavior spec and fixture covering hover, click-to-place-cursor, and label-selection across nested SDTs; refresh related unit tests.
Selecting a structured content label on pointerdown was swallowing the drag handle's native dragstart, so labels could be clicked but not dragged. Move the click-to-select handling to pointerup/click with a movement threshold, and listen on the visible host, viewport host, and owner document so the event is caught regardless of which surface ProseMirror reattaches the label to. Also stop the structured-content select plugin from collapsing a parent block selection that merely contains a nested inline SDT, and update the cursor-placement tests to match the new flow.
layoutUpdated and paginationUpdate are emitted back-to-back with the same payload for the same paint (PresentationEditor.ts:6491-6492), so subscribing geometry invalidation to both double-counted one repaint: a zoom coalesced to 'mixed' and the 'zoom' reason was unreachable. Subscribe to layoutUpdated only - it covers every repaint. Adds regression coverage for the zoom and plain layout reasons.
feat(ui): viewport geometry-invalidation hook ui.viewport.observe (SD-3311)
…l (SD-3312)
focus({ id, block?, behavior? }) scrolls a content control into view and places
the caret inside it - the "take me there and let me edit" counterpart to the
scroll-only scrollIntoView. Caret-inside (both SDT node types are atom:false, so
a TextSelection inside is the meaningful selection). Selection, not mutation: it
does not bypass lock or document-mode rules, so a locked / read-only control can
be focused for inspection but edits stay blocked. Returns { success } or
{ success: false, reason } only for real navigation problems (invalid-id /
not-ready / not-found); v1 is body-only.
Reuses the scroll method's node resolution. contract-templates dogfoods it with
a Focus button beside Locate on field rows and clause cards; adds a demo
acceptance test (Focus lands the caret inside the control) and handle unit tests.
Documents it and drops the now-resolved "no focus-by-id" limit.
…315)
Search next/previous re-centered the active match on every move via
scrollToPosition({ block: 'center' }), jumping the document ~50px even when
the match was already on screen. Add an opt-in `ifNeeded` mode that downgrades
to 'nearest' (a no-op) when the match is fully visible, keeping center for
off-screen matches. goToFirstMatch still centers the initial result.
…-3312)
focus now fails with not-ready when setTextSelection is unavailable and
not-reachable when it doesn't place the caret, so { success: true } means the
caret was actually placed (was reported regardless via optional chaining).
Docs: introduce the smartField tag convention inline instead of referencing a
"convention above" that wasn't introduced earlier on the page.
feat(ui): focus a content control to place the caret inside it (SD-3312)
The entityAt JSDoc said the supported hit types are comment and trackedChange,
but content controls are returned too: ViewportEntityHit includes
{ type: 'contentControl', scope?, tag? }, emitted from data-sdt-id (entity-at.ts).
Correct the supported-types list and add data-sdt-id to the painter
data-attribute examples.
docs(ui): note content controls in entityAt hit types (SD-3313)
…315) The ifNeeded scroll removed the per-match re-center, but exposed a second scroll: navigating via the find UI restores focus to the find input, which fires a selectionUpdate that reverts the editor selection to its pre-search caret; selection-sync then scrolled the viewport to that stale caret and the RAF re-assert yanked it back — a jump/flash on every navigation. Add an opt-in `suppressSelectionSyncScroll` to scrollToPosition: while a search-owned scroll is in flight (set before its scroll, cleared in its RAF re-assert), #scrollActiveEndIntoView is skipped so it can't fight the search scroll. The selection overlay still renders; normal keyboard/pointer selection scroll resumes the next frame. Active-match tracking and replace are storage-owned, so the selection revert does not affect them.
…315) scrollToPositionAsync forwarded ifNeeded:true into its post-mount retry. A match that was off-screen at call time (hence the async/mount path) could end up edge-visible after the page scrolls into view, then downgrade to 'nearest' and skip the centering it should get. Force ifNeeded:false on the retry (keeping suppressSelectionSyncScroll); the fast path keeps ifNeeded for already-mounted matches. Flagged in review by Codex and cubic.
…scroll fix(search): don't re-center visible matches on find navigation (SD-3315)
* fix: track SuperDoc slice paste in suggestion mode
|
📖 Docs preview: https://superdoc-nick-merge-main-stable-1-38-0.mintlify.app |
There was a problem hiding this comment.
1 issue found across 650 files
Partial review: This PR has more than 50 files, so cubic reviewed the highest-priority files first. During the trial, paid plans get a higher file limit.
You can try an ultrareview to bypass the file limit, comment @cubic-dev-ai ultrareview. Learn more.
Fix all with cubic | Re-trigger cubic
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c429b92e8d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
|
🎉 This PR is included in superdoc-cli v0.15.0 The release is available on GitHub release |
|
🎉 This PR is included in superdoc-sdk v1.14.0 |
|
🎉 This PR is included in @superdoc-dev/mcp v0.10.0 The release is available on GitHub release |
|
🎉 This PR is included in superdoc v1.38.0 The release is available on GitHub release |
|
🎉 This PR is included in @superdoc-dev/react v1.9.0 The release is available on GitHub release |
|
🎉 This PR is included in vscode-ext v2.10.0 |
No description provided.