Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
321 commits
Select commit Hold shift + click to select a range
cd178d3
fix(scripts): drop misleading pnpm run type-check hint from audit
caio-pizzol May 27, 2026
df792bf
test(diffing): add cross-editor diff handoff story (SD-3279)
caio-pizzol May 27, 2026
9d64333
test(diffing): assert preview content after cross-editor diff apply
caio-pizzol May 27, 2026
a7db219
fix(diffing): accept legacy fingerprints in compare/apply (SD-3279)
caio-pizzol May 27, 2026
84edf8a
fix(diffing): preserve recipient identity attrs on replay (SD-3279)
caio-pizzol May 27, 2026
f5589ba
Merge pull request #3531 from superdoc-dev/caio-pizzol/SD-docs-snippe…
caio-pizzol May 27, 2026
a75da60
Merge branch 'main' into caio-pizzol/SD-js-contract-owner-audit
caio-pizzol May 27, 2026
9e5dd9f
Merge pull request #3529 from superdoc-dev/caio-pizzol/SD-js-contract…
caio-pizzol May 27, 2026
e00469c
chore: fix tests (#3533)
harbournick May 27, 2026
205449a
Merge remote-tracking branch 'origin/stable' into sync/stable-to-main…
github-actions[bot] May 27, 2026
e2c3522
Merge pull request #3537 from superdoc-dev/sync/stable-to-main-202605…
caio-pizzol May 27, 2026
e174eb2
chore: fix import breaking delinstrtext orphans (#3535)
harbournick May 27, 2026
66f4433
fix(super-converter): preserve cell-level SDT wrapping table cells (S…
caio-pizzol May 27, 2026
8ec205a
test(doc-api-stories): cover cell-level SDT round-trip
caio-pizzol May 27, 2026
49875d2
test(doc-api-stories): assert full cell-level SDT metadata round-trip
caio-pizzol May 27, 2026
760ab39
Merge pull request #3527 from superdoc-dev/caio-pizzol/sd-3279-strip-…
caio-pizzol May 27, 2026
5ab9324
fix(super-converter): handle cell-level SDT in vMerge column lookup (…
caio-pizzol May 27, 2026
8d5c2a1
Merge pull request #3539 from superdoc-dev/caio-pizzol/sd-3289-preser…
caio-pizzol May 27, 2026
f9c72e0
Merge remote-tracking branch 'origin/stable' into sync/stable-to-main…
github-actions[bot] May 27, 2026
7bb8063
Merge pull request #3543 from superdoc-dev/sync/stable-to-main-202605…
caio-pizzol May 27, 2026
576b56e
feat: add modules.contentControls.chrome
May 25, 2026
5cc38eb
fix(sdt): correct cursor placement and label interactions for structu…
luccas-harbour May 22, 2026
ae2fcd4
fix(sdt): focus editor after label selection
luccas-harbour May 22, 2026
8f05663
fix(sdt): select labels with active editor
luccas-harbour May 22, 2026
06a376e
fix(sdt): avoid deferred block label retry
luccas-harbour May 22, 2026
2afe528
fix(dom): share structured content label classes
luccas-harbour May 22, 2026
01b339c
fix(sdt): defer label selection to mouseup so native drag still fires
luccas-harbour May 22, 2026
742b6a9
fix(sdt): scope label clicks to owning editor
luccas-harbour May 22, 2026
f5ab5e5
fix(sdt): clear label gesture state on cancel
luccas-harbour May 22, 2026
eee82d5
fix(sdt): use contract label selectors
luccas-harbour May 22, 2026
ef2c360
fix(sdt): resolve block labels at node boundary
luccas-harbour May 22, 2026
a541322
fix(dom): share structured content chrome label set
luccas-harbour May 22, 2026
45288c9
fix(super-editor): select inline SDT on Backspace at start of followi…
luccas-harbour May 19, 2026
ca78f79
test(super-editor): cover inline SDT selection meta escape
luccas-harbour May 19, 2026
7eeb420
test(super-editor): cover two-step inline SDT Backspace
luccas-harbour May 19, 2026
866c42e
fix(super-editor): select inline SDT content as text on Backspace
luccas-harbour May 25, 2026
bbdee15
fix(super-editor): bump sdBlockRev on ancestors of inline edits
luccas-harbour May 25, 2026
384ac0f
fix(super-editor): delete contentLocked SDT wrapper in one step
luccas-harbour May 25, 2026
dcf2b96
feat(layout-engine): render empty inline SDTs as a visible placeholder
luccas-harbour May 25, 2026
7e61d5d
fix(super-editor): intercept beforeinput insertText at inline SDT bou…
luccas-harbour May 25, 2026
ee91b74
fix(super-editor): select inline SDT content as text on Delete
luccas-harbour May 25, 2026
134811e
test(super-editor): clarify inline SDT boundary lock comment
luccas-harbour May 25, 2026
511acc9
test(super-editor): cover inline SDT content Delete flow
luccas-harbour May 25, 2026
f47c39b
test(super-editor): cover inline SDT Cmd+X selection
luccas-harbour May 25, 2026
abe8a55
test(super-editor): cover locked inline SDT beforeinput
luccas-harbour May 25, 2026
403b406
fix(super-editor): mark block SDT selected when contained image is se…
luccas-harbour May 25, 2026
69ffffa
fix(layout-bridge): detect inline image run changes in paragraph diff
luccas-harbour May 25, 2026
cda05dc
fix(super-editor): disable image resize inside content-locked SDTs
luccas-harbour May 25, 2026
c51fc93
fix(layout-engine): keep block SDT chrome and inline images out of pa…
luccas-harbour May 25, 2026
7e6b8ef
fix(layout-engine): bottom-align text on lines with inline images
luccas-harbour May 25, 2026
a81771c
fix(layout-engine): fit block SDT chrome to actual content width
luccas-harbour May 25, 2026
521a73a
fix(layout-engine): allow top-aligned inline images
luccas-harbour May 25, 2026
ec8fb83
fix(dom): suppress SDT pseudo hover in viewing mode
luccas-harbour May 25, 2026
6ffbd18
fix(dom): offset block SDT chrome for indents
luccas-harbour May 25, 2026
b5a0bc0
fix(dom): preserve SDT chrome continuation offsets
luccas-harbour May 25, 2026
3afd319
fix(dom): align SDT chrome within paragraph width
luccas-harbour May 25, 2026
bb1f67b
fix(layout-bridge): dirty inline image SDT changes
luccas-harbour May 25, 2026
e88b36e
fix(super-editor): honor ancestor image SDT locks
luccas-harbour May 25, 2026
517205e
fix(dom): size SDT chrome for justified lines
luccas-harbour May 25, 2026
cc87b53
fix(dom): align RTL SDT chrome to text
luccas-harbour May 25, 2026
a42ea7e
fix(layout-resolved): version inline image metadata
luccas-harbour May 25, 2026
fb083e4
test(layout-bridge): cover inline image diff fields
luccas-harbour May 25, 2026
d20a114
fix(super-editor): share SDT lock predicates
luccas-harbour May 25, 2026
1e94be8
fix(template-builder): color SDT block label backdrop with field color
luccas-harbour May 25, 2026
d003aa0
fix(super-editor): persist data URI images set via setPresetContent
luccas-harbour May 26, 2026
e72ff81
fix(super-editor): register sized SVG data URI images without canvas …
luccas-harbour May 26, 2026
1ea379b
fix(pm-adapter): scope inline SDT placeholder to structuredContent me…
luccas-harbour May 26, 2026
d05b7b4
fix(super-editor): support non-base64 data URI images in registration
luccas-harbour May 26, 2026
037fb0a
fix(super-editor): reuse data URI image exports
luccas-harbour May 26, 2026
01870f9
fix(super-editor): extract shared hash helpers
luccas-harbour May 26, 2026
88f7c5a
fix(super-editor): decode non-base64 data URI exports
luccas-harbour May 26, 2026
99164a4
fix(super-editor): mirror in-place image media to parent
luccas-harbour May 26, 2026
64395ab
fix(layout-engine): allow non-base64 SVG data URLs in image rendering
luccas-harbour May 26, 2026
5199396
fix(super-editor): export field annotation svgs as svg
luccas-harbour May 26, 2026
53f83ce
fix(super-editor): guard non-base64 data uri exports
luccas-harbour May 26, 2026
bcdba05
fix(super-editor): validate in-place svg image data
luccas-harbour May 26, 2026
eb29066
fix(super-editor): normalize svg data uri filenames
luccas-harbour May 26, 2026
c21ecd3
fix(super-editor): skip invalid data uri image targets
luccas-harbour May 26, 2026
7d3342e
fix(super-editor): share data uri media parsing
luccas-harbour May 26, 2026
8f50d22
test(super-editor): cover structured content image edges
luccas-harbour May 26, 2026
cfab38b
fix(super-editor): read svg data uri dimensions
luccas-harbour May 26, 2026
78a7eea
fix(super-editor): block non-image data uri exports
luccas-harbour May 26, 2026
dba9500
fix(super-editor): reject separatorless data uri files
luccas-harbour May 26, 2026
57ef704
fix(super-editor): warn on skipped image exports
luccas-harbour May 26, 2026
d783d61
fix(super-editor): reject malformed svg data uri payloads
luccas-harbour May 26, 2026
d7259b4
fix(super-editor): block raw raster data uri exports
luccas-harbour May 26, 2026
e47af7b
fix(super-editor): avoid duplicate image rids
luccas-harbour May 26, 2026
f1278e0
docs(super-editor): clarify image registration comments
luccas-harbour May 26, 2026
c0d66de
test(super-editor): repaint saved sdt images through painter
luccas-harbour May 26, 2026
7841c00
fix(super-editor): reject malformed data uri files
luccas-harbour May 26, 2026
e43f231
fix(super-editor): validate field annotation data uri exports
luccas-harbour May 26, 2026
23ee125
fix(super-editor): validate in-place svg payloads
luccas-harbour May 26, 2026
4db9d11
fix(super-editor): reuse target image relationships
luccas-harbour May 26, 2026
898f9ab
fix(shared): centralize image data url policy
luccas-harbour May 26, 2026
00fc164
perf(super-editor): avoid scanning data uri media
luccas-harbour May 26, 2026
601a9f8
fix(super-editor): normalize image data uri extensions
luccas-harbour May 26, 2026
64cbd00
refactor(super-editor): trim data uri metadata fields
luccas-harbour May 26, 2026
cb12bdf
refactor(super-editor): share data uri text decoding
luccas-harbour May 26, 2026
5a10b1d
fix(pm-adapter): narrow sdt metadata overrides
luccas-harbour May 26, 2026
44940db
fix(super-editor): register preset raster data uris in place
luccas-harbour May 26, 2026
0fe04aa
fix(super-editor): validate oversized async svg images
luccas-harbour May 26, 2026
63aa391
fix(super-editor): reject raw raster data uri dimensions
luccas-harbour May 26, 2026
02062d9
fix(super-editor): avoid non-image data uri extensions
luccas-harbour May 26, 2026
0dfaf8f
refactor(shared): centralize image data uri parsing
luccas-harbour May 26, 2026
99bbd36
refactor(super-editor): share image relationship export lookup
luccas-harbour May 26, 2026
9c7636e
fix(super-editor): enforce upload byte cap for data uris
luccas-harbour May 26, 2026
8341906
refactor(super-editor): reuse shared data uri export policy
luccas-harbour May 26, 2026
bc39b27
test(shared): cover image data uri length boundary
luccas-harbour May 26, 2026
928d38a
fix(super-editor): reuse colliding data uri media targets
luccas-harbour May 26, 2026
46524a6
test(super-editor): roundtrip mixed image block sdts
luccas-harbour May 26, 2026
7b3e602
docs(shared): document image data uri helpers
luccas-harbour May 26, 2026
4db7d32
docs(super-editor): clarify data uri buffer conversion
luccas-harbour May 26, 2026
fd1fe88
refactor(super-editor): wrap shared tryDecodeDataUriText re-export
luccas-harbour May 26, 2026
fcb734b
fix(shared): reject malformed base64 image data URIs
luccas-harbour May 26, 2026
e39581c
feat(super-editor): disable mutation toolbar controls inside content-…
luccas-harbour May 26, 2026
c2aff65
fix(super-editor): block locked sdt toolbar execution
luccas-harbour May 26, 2026
05f4ac4
fix(super-editor): guard unlisted locked toolbar commands
luccas-harbour May 26, 2026
f35163f
fix(super-editor): block disabled toolbar execution
luccas-harbour May 27, 2026
1acd342
refactor(super-editor): share structured content predicates
luccas-harbour May 27, 2026
8e879d7
fix(super-editor): keep text-align enabled in locked SDT paragraphs
luccas-harbour May 27, 2026
62e785c
fix(super-editor): exclude sdt chrome labels from caret position lookup
luccas-harbour May 27, 2026
c6dc91c
feat(super-editor): move caret into preceding block sdt on backspace
luccas-harbour May 27, 2026
7323647
feat(super-editor): move caret into following block sdt on delete
luccas-harbour May 27, 2026
855cefd
fix(super-editor): avoid restoring dragged block to its source position
luccas-harbour May 27, 2026
e757a48
refactor(layout-engine): share box model between block and inline sdt…
luccas-harbour May 27, 2026
598fd88
fix(super-editor): handle empty block sdt navigation
luccas-harbour May 27, 2026
80f0089
fix(super-editor): respect inline atoms in sdt navigation
luccas-harbour May 27, 2026
c3bf53d
fix(super-editor): target nearest sdt cursor position
luccas-harbour May 27, 2026
8e6720b
fix(super-editor): skip hidden sdt navigation markers
luccas-harbour May 27, 2026
b09890f
fix(super-editor): keep visible atoms in sdt navigation
luccas-harbour May 27, 2026
f948454
fix(super-editor): handle marker-only sdt paragraphs
luccas-harbour May 27, 2026
9f2f79f
fix(super-editor): skip hidden block sdt markers
luccas-harbour May 27, 2026
cb14217
fix(super-editor): skip hidden metadata sdt markers
luccas-harbour May 27, 2026
630b1e4
fix(super-editor): skip hidden field annotations in sdt navigation
luccas-harbour May 27, 2026
116f5d8
fix(super-editor): handle sdt marker gaps and block atoms
luccas-harbour May 27, 2026
c9db28a
fix(layout-engine): cap block sdt label width
luccas-harbour May 27, 2026
e8cf25d
fix(super-editor): ignore empty block sdt key targets
luccas-harbour May 27, 2026
7a139c9
fix(super-editor): target marker-only textblock end
luccas-harbour May 27, 2026
cd70e14
fix(super-editor): drop unreachable move fallback
luccas-harbour May 27, 2026
98abc60
refactor(super-editor): share block sdt navigation helpers
luccas-harbour May 27, 2026
7d52d43
test(super-editor): cover nested block sdt navigation
luccas-harbour May 27, 2026
4d21cf9
fix(super-editor): allow history transactions through sdt lock
luccas-harbour May 27, 2026
b6d09dc
fix(super-editor): collapse selection on sdtContentLocked delete
luccas-harbour May 27, 2026
4ec031e
feat(super-editor): render placeholder text for empty SDTs
luccas-harbour May 27, 2026
785934a
feat(super-editor): inherit run styles in empty block SDT placeholders
luccas-harbour May 27, 2026
efb881f
fix(layout-engine): size SDT block labels to content width
luccas-harbour May 27, 2026
7109259
fix(layout-engine): use measured width for empty SDT placeholders
luccas-harbour May 27, 2026
073b75d
fix(super-editor): align empty block sdt caret
luccas-harbour May 27, 2026
8179b93
fix(layout-engine): hide empty block sdt placeholder
luccas-harbour May 27, 2026
db03c49
fix(layout-engine): expose block sdt appearance
luccas-harbour May 27, 2026
e08d2f0
fix(super-editor): ignore collapsed inline sdt cut
luccas-harbour May 27, 2026
be4a02b
fix(layout-engine): hide sdt placeholders in viewing
luccas-harbour May 27, 2026
00e424e
fix(layout-engine): hide sdt placeholders in print
luccas-harbour May 27, 2026
8331ae1
fix(layout-engine): remeasure sdt placeholders
luccas-harbour May 27, 2026
cd4f38c
fix(layout-engine): transform sdt placeholder measure
luccas-harbour May 27, 2026
acb3143
fix(layout-engine): keep remeasured sdt placeholder atomic
luccas-harbour May 27, 2026
5764c2b
fix(layout-engine): suppress hidden block sdt chrome
luccas-harbour May 27, 2026
dedd586
fix(pm-adapter): preserve vanished block sdt paragraphs
luccas-harbour May 27, 2026
21d5f2f
fix(pm-adapter): keep vanished sdt paragraph side effects
luccas-harbour May 27, 2026
342d9a7
fix(pm-adapter): trust empty sdt paragraph conversion
luccas-harbour May 27, 2026
76b88ae
fix(layout-engine): keep sdt placeholder pm range atomic
luccas-harbour May 27, 2026
9c597a3
fix(layout-engine): collapse hidden sdt placeholder text
luccas-harbour May 27, 2026
70f8829
fix(pm-adapter): preserve empty sdt bookmark placeholders
luccas-harbour May 27, 2026
a1dd002
fix(pm-adapter): preserve comment-only sdt placeholders
luccas-harbour May 27, 2026
82d4f86
fix(pm-adapter): preserve permission-only sdt placeholders
luccas-harbour May 27, 2026
beef164
fix(super-editor): skip empty sdt scan on arrow right
luccas-harbour May 27, 2026
33a40ca
fix(layout-engine): show empty SDT placeholder text in viewing and pr…
luccas-harbour May 27, 2026
736ab19
refactor(layout-engine): consolidate type imports in dom painter
luccas-harbour May 28, 2026
723f708
test(behavior): correct sdt drag-drop and appearance assertions
luccas-harbour May 28, 2026
f1e6ff6
fix(super-editor): sync block sdt label selection updates
luccas-harbour May 28, 2026
15e7689
fix(super-editor): ignore covered sdt label clicks
luccas-harbour May 28, 2026
57fa19f
fix(super-editor): preserve block ids during metadata updates
luccas-harbour May 28, 2026
d38c4c2
fix(super-editor): decouple base64 image helper imports
luccas-harbour May 28, 2026
7c08072
fix(super-editor): render and round-trip w:smartTag content (SD-2647)
caio-pizzol May 28, 2026
67a55e8
fix(super-editor): keep block SDT chrome at full fragment width
luccas-harbour May 28, 2026
1d1a9c3
fix(layout-engine): include inline SDT chrome width in block SDT bounds
luccas-harbour May 28, 2026
4ed82b4
fix(super-editor): route smartTag through export and preserve smartTa…
caio-pizzol May 28, 2026
0fc5541
test(super-editor): assert smartTag child text survives export round-…
caio-pizzol May 28, 2026
544a8e1
Merge pull request #3444 from superdoc-dev/luccas/sd-3237-bug-sdt-hov…
caio-pizzol May 28, 2026
f808304
test(super-editor): add v2 bridge unit + round-trip behavior coverage…
caio-pizzol May 28, 2026
e771084
fix(layout-engine): anchor inline SDT label to start of chrome
luccas-harbour May 28, 2026
c95c9d9
fix(layout-engine): use logical inset for inline SDT label position
luccas-harbour May 28, 2026
bbd8c0a
Merge pull request #3546 from superdoc-dev/caio/sd-2647-bug-render-an…
caio-pizzol May 28, 2026
d08080e
Merge pull request #3549 from superdoc-dev/luccas/left-align-inline-s…
caio-pizzol May 28, 2026
6797585
fix(layout-engine): paint block SDT background on chrome layer
luccas-harbour May 28, 2026
4a7cdf4
fix(layout-engine): hide block sdt fills in output modes
luccas-harbour May 28, 2026
eb3d737
fix(layout-engine): keep block sdt fill behind content
luccas-harbour May 28, 2026
87fe226
fix(layout-engine): promote image-bearing inline SDT wrappers to inli…
luccas-harbour May 28, 2026
dd11b7e
test(visual): assert inline template field fill
luccas-harbour May 28, 2026
bd163a0
fix(super-editor): allow block SDT wrapper deletion to follow lock rules
luccas-harbour May 28, 2026
8182ff8
SD-2676 - fix: table selection not providing a feedback (#3508)
chittolinag May 28, 2026
dd951d2
test(behavior): search all matching roots in SDT getTextPoint helper
luccas-harbour May 28, 2026
e60387a
Merge branch 'main' into artem/SD-3159
caio-pizzol May 28, 2026
831b887
demo(contract-templates): turn off built-in SDT chrome and paint host…
caio-pizzol May 28, 2026
8cd54f4
Merge pull request #3550 from superdoc-dev/luccas/sd-3302-bug-sdt-in-…
caio-pizzol May 28, 2026
ea387af
test(painter-dom): assert chrome-none hover suppression and cascade o…
caio-pizzol May 28, 2026
435611e
Merge branch 'main' into artem/SD-3159
caio-pizzol May 28, 2026
0d30018
test: cover contentControlsChrome plumbing; clarify chrome-none comment
caio-pizzol May 28, 2026
9a9ccec
fix(types): type modules.contentControls exactly (no pass-through index)
caio-pizzol May 28, 2026
dca5e5e
docs(consumer-typecheck): correct module-config type-policy header
caio-pizzol May 28, 2026
084b543
test(behavior): parity for SDT backspace outside trailing edge (SD-32…
caio-pizzol May 28, 2026
b1431d9
Merge pull request #3486 from superdoc-dev/artem/SD-3159
caio-pizzol May 28, 2026
a35c636
feat(super-editor): select adjacent block SDT content at textblock bo…
luccas-harbour May 28, 2026
b26882c
test(behavior): parity for SDT delete-outside-leading + select-all-in…
caio-pizzol May 28, 2026
0f5c00a
Merge pull request #3552 from superdoc-dev/caio/sdt-word-parity-trans…
caio-pizzol May 28, 2026
b938d5e
Merge pull request #3554 from superdoc-dev/caio/sdt-inline-delete-sel…
caio-pizzol May 28, 2026
acfd612
test(behavior): parity for SDT inside-edge backspace/delete (SD-3237/…
caio-pizzol May 28, 2026
9553ce9
test(behavior): assert inside-edge deletes per press, not just after …
caio-pizzol May 28, 2026
fb01147
test(super-editor): update SDT keymap chain coverage
luccas-harbour May 28, 2026
a752dd2
test(behavior): expect block SDT boundary selection
luccas-harbour May 28, 2026
6761ddb
test(super-editor): cover nested block SDT boundary selection
luccas-harbour May 28, 2026
2013eb2
test(super-editor): cover locked block SDT Delete selection
luccas-harbour May 28, 2026
b9ebc98
test(super-editor): resolve handleBase64 source path from package or …
luccas-harbour May 28, 2026
f4d5b20
fix(super-editor): skip empty block SDT content selection
luccas-harbour May 28, 2026
ebe43cb
Merge pull request #3556 from superdoc-dev/caio/sdt-inline-inside-edg…
caio-pizzol May 28, 2026
f786f5c
test(behavior): add parity axis helpers for SDT contracts (SD-3237/SD…
caio-pizzol May 28, 2026
4b1ffb1
feat: expose public sdt events
May 22, 2026
d7ee037
fix(sdt-events): reset state on unload, dedupe + export payload types…
caio-pizzol May 28, 2026
5ec0173
docs(behavior): clarify bodyMutation excludes wrapper lifecycle, not …
caio-pizzol May 29, 2026
c1c23f2
test(behavior): import test/expect from the behavior fixture, not @pl…
caio-pizzol May 29, 2026
052410b
Merge pull request #3558 from superdoc-dev/caio/sdt-parity-helpers
caio-pizzol May 29, 2026
6168021
test(behavior): parity for SDT right-arrow + shift-right navigation (…
caio-pizzol May 29, 2026
5854a25
fix(sdt-events): wire pointer-source tracking on all init paths; upda…
caio-pizzol May 29, 2026
13d8845
demo(contract-templates): drive the field chip from content-control:a…
caio-pizzol May 29, 2026
1ee44eb
Merge pull request #3561 from superdoc-dev/caio/sdt-navigation-parity
caio-pizzol May 29, 2026
9e0aae1
feat(sdt-events): add activePath (full active stack) to content-contr…
caio-pizzol May 29, 2026
27869f8
Merge pull request #3555 from superdoc-dev/luccas/delete-image-conten…
caio-pizzol May 29, 2026
afc36b0
docs(events): reconcile activePath vs observe guidance for nested con…
caio-pizzol May 29, 2026
5309deb
docs(content-controls): move the custom-UI guide out of the event reg…
caio-pizzol May 29, 2026
915dbbe
Merge pull request #3509 from superdoc-dev/artem/SD-3232
caio-pizzol May 29, 2026
049ec91
feat(ui): add ui.contentControls.scrollIntoView (SD-3310)
caio-pizzol May 29, 2026
f0278ea
test(ui): cover scrollIntoView for imported Word SDTs (SD-3310)
caio-pizzol May 29, 2026
4d85c51
Merge pull request #3562 from superdoc-dev/caio/sd-3310-scrollintoview
caio-pizzol May 29, 2026
6234b10
demo(contract-templates): add "Locate" to scroll to a field/clause co…
caio-pizzol May 29, 2026
3fbfa3f
Merge pull request #3564 from superdoc-dev/caio/contract-templates-lo…
caio-pizzol May 29, 2026
20f6849
feat(ui): add ui.viewport.observe geometry-invalidation signal (SD-3311)
caio-pizzol May 29, 2026
8a2b0d5
fix(ui): report 'zoom' not 'mixed' for a zoom repaint (SD-3311)
caio-pizzol May 29, 2026
3bbdbe5
Merge pull request #3570 from superdoc-dev/caio/sd-3311-viewport-observe
caio-pizzol May 29, 2026
f8f4fdc
feat(ui): add ui.contentControls.focus to place the caret in a contro…
caio-pizzol May 29, 2026
eaa5824
fix(search): don't re-center visible matches on find navigation (SD-3…
caio-pizzol May 29, 2026
998561a
fix(ui): honor the focus() contract + fix dangling docs reference (SD…
caio-pizzol May 29, 2026
a6ceac5
Merge pull request #3571 from superdoc-dev/caio/sd-3312-focus-activate
caio-pizzol May 29, 2026
6e865e4
docs(ui): note content controls in entityAt hit types (SD-3313)
caio-pizzol May 29, 2026
1c70b9b
Merge pull request #3573 from superdoc-dev/caio/sd-3313-hittest-docs
caio-pizzol May 29, 2026
f69fb6b
fix(search): stop find navigation jumping to the reverted caret (SD-3…
caio-pizzol May 29, 2026
a7ccb7b
Merge branch 'main' into caio/sd-3315-find-replace-scroll
caio-pizzol May 29, 2026
0add889
fix(search): center virtualized matches after mount in find nav (SD-3…
caio-pizzol May 29, 2026
633849a
Merge pull request #3572 from superdoc-dev/caio/sd-3315-find-replace-…
caio-pizzol May 29, 2026
7728b1f
fix: copy-pasted text in suggestion mode (#3576)
harbournick May 29, 2026
c429b92
chore: generate all
harbournick May 29, 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
  •  
  •  
  •  
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ Do not hand-edit `COMMAND_CATALOG`, `OPERATION_MEMBER_PATH_MAP`, `OPERATION_REFE
- `pnpm test` - unit tests
- `pnpm dev` - dev server from `examples/`
- `pnpm check:types` - raw TS compile across all referenced projects (`tsc -b tsconfig.references.json`). Does NOT run the public-interface chain. Legacy alias: `pnpm run type-check`.
- `pnpm check:public` - **canonical pre-merge command for typed public surfaces.** Validates both `superdoc` (tier discipline + jsdoc ratchet + public-method fixture coverage + vite build + postbuild chain + consumer typecheck matrix + deep-type audit + package-shape + snapshots + classification closure) and Document API (contract parity + output staleness + examples + overview). ~5 min. Non-mutating. Combines `check:public:superdoc` + `check:public:docapi`.
- `pnpm check:public:superdoc` - SuperDoc public package surface only. Wraps ten stages in cheap-to-expensive order: `contract-tiers-test`, `contract-tiers`, `jsdoc-ratchet`, `public-method-coverage`, `build`, `consumer-typecheck-matrix`, `deep-type-audit-supported-root`, `package-shape`, `export-snapshots`, `root-classification-closure`. Legacy alias: `pnpm run check:public-contract`.
- `pnpm check:public` - **canonical pre-merge command for typed public surfaces.** Validates both `superdoc` (tier discipline + jsdoc ratchet + ts-jsdoc hygiene + public-method fixture coverage + vite build + postbuild chain + consumer typecheck matrix + deep-type audit + package-shape + snapshots + classification closure) and Document API (contract parity + output staleness + examples + overview). ~5 min. Non-mutating. Combines `check:public:superdoc` + `check:public:docapi`.
- `pnpm check:public:superdoc` - SuperDoc public package surface only. Wraps twelve stages in cheap-to-expensive order: `contract-tiers-test`, `contract-tiers`, `jsdoc-ratchet`, `jsdoc-hygiene-ts-test`, `jsdoc-hygiene-ts`, `public-method-coverage`, `build`, `consumer-typecheck-matrix`, `deep-type-audit-supported-root`, `package-shape`, `export-snapshots`, `root-classification-closure`. Legacy alias: `pnpm run check:public-contract`.
- `pnpm check:public:docapi` - Document API public surface only. Wraps four stages: `contract-parity`, `contract-outputs`, `examples`, `overview-alignment`. Clean-checkout safe: gitignored generated artifacts are built in memory; tracked outputs (reference docs, overview block) are compared byte-for-byte. No mutation. Legacy alias: `pnpm run docapi:check`.
- `pnpm generate:docapi` - regenerate Document API outputs after editing the contract (alias of `docapi:sync`). Writes gitignored Document API generated artifacts. Run only when you need the artifacts materialized locally (SDK builds, publishing); `check:public:docapi` does not require it.
- `pnpm generate:all` - regenerate schemas, SDK clients, tool catalogs, reference docs.
Expand Down
33 changes: 33 additions & 0 deletions apps/cli/src/__tests__/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ const ENCRYPTED_FIXTURE_SOURCE = join(
REPO_ROOT,
'packages/super-editor/src/editors/v1/core/ooxml-encryption/fixtures/encrypted-advanced-text.docx',
);
const TRACKED_CHANGE_FIXTURE_SOURCE = join(
REPO_ROOT,
'packages/super-editor/src/editors/v1/tests/data/basic-tracked-change.docx',
);
const execFileAsync = promisify(execFile);
const ZIP_MAX_BUFFER_BYTES = 10 * 1024 * 1024;

Expand Down Expand Up @@ -2334,6 +2338,35 @@ describe('superdoc CLI', () => {
expect(verifyEnvelope.data.result.total).toBeGreaterThan(0);
});

test('save --mode final exports accepted OOXML instead of copying review-preserving revisions', async () => {
const trackedSource = join(TEST_DIR, 'save-final-source.docx');
const savedOut = join(TEST_DIR, 'save-final-output.docx');
await copyFile(TRACKED_CHANGE_FIXTURE_SOURCE, trackedSource);

const openResult = await runCli(['open', trackedSource, '--session', 'final-export']);
expect(openResult.code).toBe(0);

const saveResult = await runCli([
'save',
'--session',
'final-export',
'--mode',
'final',
'--out',
savedOut,
'--force',
]);
expect(saveResult.code).toBe(0);

const saveEnvelope = parseJsonOutput<SuccessEnvelope<{ mode: string; output: { path: string } }>>(saveResult);
expect(saveEnvelope.data.mode).toBe('final');
expect(saveEnvelope.data.output.path).toBe(savedOut);

const documentXml = await readDocxPart(savedOut, 'word/document.xml');
expect(documentXml).not.toContain('<w:ins');
expect(documentXml).not.toContain('<w:del');
});

test('save --in-place detects source drift unless forced', async () => {
const driftSource = join(TEST_DIR, 'drift-source.docx');
await copyFile(SAMPLE_DOC, driftSource);
Expand Down
37 changes: 31 additions & 6 deletions apps/cli/src/__tests__/conformance/scenarios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ function skippedSuccessScenario(operationId: CliOperationId) {
});
}

type SuccessScenarioFactory = (harness: ConformanceHarness) => Promise<ScenarioInvocation>;
type ScenarioFactory = (harness: ConformanceHarness) => Promise<ScenarioInvocation>;

function deferredRuntimeScenario(
operationId: CliOperationId,
Expand Down Expand Up @@ -3334,7 +3334,7 @@ export const SUCCESS_SCENARIOS = {
await harness.openSessionFixture(stateDir, 'doc-history-redo', 'history-redo-session');
return { stateDir, args: ['history', 'redo', '--session', 'history-redo-session'] };
},
} as const satisfies Partial<Record<CliOperationId, SuccessScenarioFactory>>;
} as const satisfies Partial<Record<CliOperationId, ScenarioFactory>>;

const EXPLICIT_RUNTIME_CONFORMANCE_SKIP = new Set<CliOperationId>([
'doc.toc.markEntry',
Expand All @@ -3358,22 +3358,47 @@ const EXPLICIT_RUNTIME_CONFORMANCE_SKIP = new Set<CliOperationId>([
]);

const CANONICAL_OPERATION_IDS = Object.keys(CLI_OPERATION_COMMAND_KEYS) as CliOperationId[];
const SUCCESS_SCENARIOS_BY_OPERATION: Partial<Record<CliOperationId, ScenarioFactory>> = SUCCESS_SCENARIOS;
const AUTO_SKIPPED_OPERATION_IDS = CANONICAL_OPERATION_IDS.filter(
(operationId) => SUCCESS_SCENARIOS[operationId] == null,
(operationId) => SUCCESS_SCENARIOS_BY_OPERATION[operationId] == null,
);

const RUNTIME_CONFORMANCE_SKIP = new Set<CliOperationId>([
...EXPLICIT_RUNTIME_CONFORMANCE_SKIP,
...AUTO_SKIPPED_OPERATION_IDS,
]);

const FAILURE_SCENARIOS: Partial<Record<CliOperationId, ScenarioFactory>> = {
'doc.trackChanges.decide': async (harness: ConformanceHarness): Promise<ScenarioInvocation> => {
const stateDir = await harness.createStateDir('doc-trackChanges-decide-missing-id');
const fixture = await harness.addTrackedChangeFixture(stateDir, 'doc-trackChanges-decide-missing-id');
return {
stateDir,
args: [
...commandTokens('doc.trackChanges.decide'),
fixture.docPath,
'--decision',
'accept',
'--target-json',
JSON.stringify({ id: 'missing-track-change-id' }),
'--out',
harness.createOutputPath('doc-trackChanges-decide-missing-id-output'),
],
};
},
};

const EXPECTED_FAILURE_CODES: Partial<Record<CliOperationId, string[]>> = {
'doc.trackChanges.decide': ['TARGET_NOT_FOUND'],
};

export const OPERATION_SCENARIOS = CANONICAL_OPERATION_IDS.map((operationId) => {
const success = SUCCESS_SCENARIOS[operationId] ?? skippedSuccessScenario(operationId);
const success = SUCCESS_SCENARIOS_BY_OPERATION[operationId] ?? skippedSuccessScenario(operationId);
const scenario: OperationScenario = {
operationId,
success,
failure: genericInvalidArgumentFailure(operationId),
expectedFailureCodes: ['INVALID_ARGUMENT', 'MISSING_REQUIRED'],
failure: FAILURE_SCENARIOS[operationId] ?? genericInvalidArgumentFailure(operationId),
expectedFailureCodes: EXPECTED_FAILURE_CODES[operationId] ?? ['INVALID_ARGUMENT', 'MISSING_REQUIRED'],
...(RUNTIME_CONFORMANCE_SKIP.has(operationId) ? { skipRuntimeConformance: true } : {}),
};
return scenario;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/**
* Subprocess worker for the openDocument track-changes forwarding test.
*/
import { mock } from 'bun:test';

let editorOpenCalled = false;
let capturedTrackChanges: unknown;

const MockEditor = {
open: mock(async (_source: unknown, options: Record<string, unknown> = {}) => {
editorOpenCalled = true;
capturedTrackChanges = (options.modules as { trackChanges?: unknown } | undefined)?.trackChanges;
return {
destroy: () => {},
exportDocument: async () => new Uint8Array(),
};
}),
};

mock.module('superdoc/super-editor', () => ({
Editor: MockEditor,
BLANK_DOCX_BASE64: '',
DocxEncryptionError: class DocxEncryptionError extends Error {
code: string;
constructor(code: string, message: string) {
super(message);
this.code = code;
}
},
getDocumentApiAdapters: () => ({}),
markdownToPmDoc: () => null,
initPartsRuntime: () => ({ dispose: () => {} }),
syncCommentEntitiesFromCollaboration: () => new Set<string>(),
}));

mock.module('@superdoc/document-api', () => ({
createDocumentApi: () => ({}),
}));

mock.module('happy-dom', () => ({
Window: class {
document = {
createElement: () => ({}),
body: { appendChild: () => {}, innerHTML: '' },
};
happyDOM = { abort: () => {} };
close() {}
},
}));

async function main() {
const { openDocument } = await import('../../lib/document');

const io = {
stdout: () => {},
stderr: () => {},
readStdinBytes: async () => new Uint8Array(),
};

let opened: { dispose(): void } | undefined;
try {
opened = await openDocument(undefined, io, {
editorOpenOptions: {
modules: {
trackChanges: {
replacements: 'independent',
},
},
},
});
} finally {
opened?.dispose();
}

console.log(JSON.stringify({ editorOpenCalled, capturedTrackChanges }));
}

main().catch((e) => {
console.error(e);
process.exit(1);
});
44 changes: 44 additions & 0 deletions apps/cli/src/__tests__/lib/error-mapping.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,32 @@ describe('mapInvokeError', () => {
expect(mapped.message).toBe('blocks.delete requires a target.');
expect(mapped.details).toEqual({ operationId: 'blocks.delete', details: { field: 'target' } });
});

test('preserves TARGET_NOT_FOUND for trackChanges.decide stale ids', () => {
const error = Object.assign(new Error('Tracked change "tc-1" was not found.'), {
code: 'TARGET_NOT_FOUND',
details: { id: 'tc-1' },
});

const mapped = mapInvokeError('trackChanges.decide' as any, error);
expect(mapped.code).toBe('TARGET_NOT_FOUND');
expect(mapped.details).toEqual({ operationId: 'trackChanges.decide', details: { id: 'tc-1' } });
});

test('keeps track-changes accept/reject helper missing ids backward compatible', () => {
const error = Object.assign(new Error('Tracked change "tc-1" was not found.'), {
code: 'TARGET_NOT_FOUND',
details: { id: 'tc-1' },
});

const accept = mapInvokeError('trackChanges.decide' as any, error, { commandName: 'track-changes accept' });
const reject = mapInvokeError('trackChanges.decide' as any, error, { commandName: 'track-changes reject' });
const canonical = mapInvokeError('trackChanges.decide' as any, error, { commandName: 'track-changes decide' });

expect(accept.code).toBe('TRACK_CHANGE_NOT_FOUND');
expect(reject.code).toBe('TRACK_CHANGE_NOT_FOUND');
expect(canonical.code).toBe('TARGET_NOT_FOUND');
});
});

// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -205,6 +231,24 @@ describe('mapFailedReceipt: plan-engine code passthrough', () => {
expect(result!.code).toBe('COMMAND_FAILED');
});

test('maps helper trackChanges.decide TARGET_NOT_FOUND receipts to TRACK_CHANGE_NOT_FOUND', () => {
const receipt = {
success: false,
failure: {
code: 'TARGET_NOT_FOUND',
message: 'Tracked change "tc-1" was not found.',
},
};

const helper = mapFailedReceipt('trackChanges.decide' as any, receipt, { commandName: 'track-changes accept' });
const canonical = mapFailedReceipt('trackChanges.decide' as any, receipt, {
commandName: 'track-changes decide',
});

expect(helper?.code).toBe('TRACK_CHANGE_NOT_FOUND');
expect(canonical?.code).toBe('TARGET_NOT_FOUND');
});

test('plan-engine code MATCH_NOT_FOUND passes through with structured details', () => {
const receipt = {
success: false,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* Verifies that `openDocument` forwards `editorOpenOptions.modules.trackChanges`
* through to `Editor.open()`.
*
* This runs in a subprocess so `mock.module` cannot leak into other tests.
*/
import { describe, expect, test } from 'bun:test';
import { join } from 'path';

const WORKER_SCRIPT = join(import.meta.dir, '_open-document-track-changes-worker.ts');

describe('openDocument track changes forwarding', () => {
test('trackChanges replacement mode reaches Editor.open()', async () => {
const proc = Bun.spawn(['bun', 'run', WORKER_SCRIPT], {
cwd: join(import.meta.dir, '../../..'),
stdout: 'pipe',
stderr: 'pipe',
});

const exitCode = await proc.exited;
const stdout = await new Response(proc.stdout).text();
const stderr = await new Response(proc.stderr).text();

if (exitCode !== 0) {
throw new Error(`Worker failed (code ${exitCode}):\n${stderr || stdout}`);
}

const result = JSON.parse(stdout.trim());
expect(result.editorOpenCalled).toBe(true);
expect(result.capturedTrackChanges).toEqual({ replacements: 'independent' });
});
});
28 changes: 28 additions & 0 deletions apps/cli/src/__tests__/lib/operation-runtime-metadata.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,32 @@ describe('operation runtime metadata', () => {
const optionNames = openOptions.map((o) => o.name);
expect(optionNames).toContain('password');
});

test('doc.open metadata includes trackChanges JSON param', () => {
const openMeta = CLI_OPERATION_METADATA['doc.open'];
const trackChangesParam = openMeta.params.find((p) => p.name === 'trackChanges');

expect(trackChangesParam).toBeDefined();
expect(trackChangesParam!.kind).toBe('jsonFlag');
expect(trackChangesParam!.type).toBe('json');
expect(trackChangesParam!.flag).toBe('track-changes-json');
expect(trackChangesParam!.schema).toEqual({
type: 'object',
properties: {
replacements: {
type: 'string',
enum: ['paired', 'independent'],
description: 'Tracked replacement grouping mode.',
},
},
});
});

test('doc.open option specs include track-changes-json flag', () => {
const openOptions = CLI_OPERATION_OPTION_SPECS['doc.open'];
const trackChangesOption = openOptions.find((o) => o.name === 'track-changes-json');

expect(trackChangesOption).toBeDefined();
expect(trackChangesOption!.type).toBe('string');
});
});
Loading
Loading