Skip to content

Merge main into stable#3580

Merged
harbournick merged 321 commits into
stablefrom
nick/merge-main-stable-1.38.0
May 29, 2026
Merged

Merge main into stable#3580
harbournick merged 321 commits into
stablefrom
nick/merge-main-stable-1.38.0

Conversation

@harbournick
Copy link
Copy Markdown
Collaborator

No description provided.

caio-pizzol and others added 30 commits May 27, 2026 10:15
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)
…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)
…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.
caio-pizzol and others added 14 commits May 29, 2026 13:26
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
@harbournick harbournick self-assigned this May 29, 2026
@harbournick harbournick requested a review from a team as a code owner May 29, 2026 20:45
@github-actions
Copy link
Copy Markdown
Contributor

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread apps/cli/src/commands/save.ts
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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-commenter
Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@harbournick harbournick merged commit 1b85001 into stable May 29, 2026
77 checks passed
@harbournick harbournick deleted the nick/merge-main-stable-1.38.0 branch May 29, 2026 21:12
@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented May 29, 2026

🎉 This PR is included in superdoc-cli v0.15.0

The release is available on GitHub release

@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented May 29, 2026

🎉 This PR is included in superdoc-sdk v1.14.0

@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented May 29, 2026

🎉 This PR is included in @superdoc-dev/mcp v0.10.0

The release is available on GitHub release

@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented May 29, 2026

🎉 This PR is included in superdoc v1.38.0

The release is available on GitHub release

@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented May 29, 2026

🎉 This PR is included in @superdoc-dev/react v1.9.0

The release is available on GitHub release

@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented May 29, 2026

🎉 This PR is included in vscode-ext v2.10.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants