Skip to content

Commit f3bfc71

Browse files
authored
refactor(config): consolidate track-changes config under modules.trackChanges (SD-2608) (#2847)
* refactor(config): consolidate track-changes config under modules.trackChanges (SD-2608) track-changes config lived in two places (config.trackChanges for visibility, config.layoutEngineOptions.trackedChanges for mode/enabled), unlike every other module which sits under config.modules.*. consumers had to learn three keys for one module. this adds modules.trackChanges as the canonical path and keeps both legacy keys working as deprecated aliases with one-time warnings. - new normalizer in core/helpers resolves canonical + legacy with precedence new > legacy > derived default, and mirrors values back to legacy paths so the ~14 internal reads keep working untouched - accepts all four TrackedChangesMode values (review | original | final | off) to preserve existing pass-through of layoutEngineOptions.trackedChanges - suppresses re-warning on a second normalization of the same config object so write-through values don't look like new legacy usage - JSDoc, Mintlify docs, and the one SuperDoc.test.js legacy use-site updated * feat(track-changes): add pairReplacements mode matching Word/ECMA-376 (SD-2607) (#2849) * feat(track-changes): add pairReplacements mode matching Word/ECMA-376 (SD-2607) when track changes are on and a user replaces text, SuperDoc groups the insertion and deletion under one shared id so accepting or rejecting takes one click (Google-Docs-like). Microsoft Word and ECMA-376 §17.13.5 treat every <w:ins> and <w:del> as an independent revision with its own required w:id. a consumer wants their UI to match Word. this adds modules.trackChanges.pairReplacements (default true preserves current behaviour). when false, each insertion and each deletion is an independent change, addressable and resolvable on its own. - importer: buildTrackedChangeIdMap accepts { pairReplacements }; when false, skips the adjacent w:del+w:ins pairing so each Word w:id maps to its own UUID - insertTrackedChange: no shared id on replacements in unpaired mode - getChangesByIdToResolve: returns just the single matching mark in unpaired mode (no neighbor walk) - wiring: SuperDoc.vue -> editor.options.trackedChanges -> Editor.ts -> SuperConverter.trackedChangesOptions -> docxImporter no exporter change needed — <w:ins>/<w:del> are already written per-mark with their own w:id in both modes. no public API shape change. * fix(track-changes): address review findings on pairReplacements (SD-2607) - always walk adjacent same-id marks in getChangesByIdToResolve so a single logical revision split across multiple segments resolves as one unit; the unpaired case is handled implicitly because ins/del now have distinct ids - align changeId with the insertion mark's id so comment threads and the optional comment reply attach to the same thread in unpaired mode - simplify id-minting: one primary id anchors the operation; the deletion mints its own fresh id only when unpaired AND it's a replacement. the Document API write adapter now gets unpaired revisions when the flag is off without any adapter-level change - add trackedChanges?: {...} to EditorOptions so consumers don't need casts - add an unpaired-mode example snippet to the docs - extension test now covers the headline guarantee: in unpaired mode, acceptTrackedChangeById(insertionId) resolves only the insertion, and the deletion is still independently rejectable by its own id * fix(types): tighten JSDoc signatures so type-check passes (SD-2608) the normalizer and tracked-change mapper emit strict typedefs but several helper signatures were relying on implicit any, and the NORMALIZED_MARKER symbol indexed an under-typed config object. CI type-check rejected these once @ts-check saw the full types. - normalize-track-changes-config.js: explicit JSDoc param types on helpers, Record<string, any> on config so module/track-chains resolve, symbol cast for the NORMALIZED_MARKER access - trackedChangeIdMapper.js: nested walk now propagates pairReplacements instead of emitting an under-typed WalkContext * refactor(track-changes): rename pairReplacements → replacements enum (SD-2608) pairReplacements leaked editor-speak and forced consumers to mentally invert a boolean to get the Word-style model. rename to a self-documenting enum that sits alongside future per-behavior siblings under modules.trackChanges. - public surface: `modules.trackChanges.replacements: 'paired' | 'independent'` default `'paired'` (no behavior change for existing consumers) - plumbing: Editor.options.trackedChanges.replacements → SuperConverter trackedChangesOptions → buildTrackedChangeIdMap; parallel path through trackedTransaction → replaceStep so user-driven replacements (the Firefox coalescing path) also honor the flag - replaceStep now passes id: undefined to markDeletion in 'independent' mode so the deletion mints its own id — closing the UI-typing gap - docs: configuration.mdx ParamField + example use the enum; track-changes extension doc gets a new "Revision model" section explaining paired vs independent with snippets - tests: normalizer, mapper, extension, and a new behavior spec (tracked-change-independent-replacement.spec.ts) exercise the enum end-to-end across chromium/firefox/webkit; harness + fixture accept a `replacements` URL param so tests can flip the flag * docs(track-changes): migrate from extensions to modules (SD-2608) with SuperDoc moving away from ProseMirror and extensions no longer appearing in the public nav, track changes reads better as a first-class module page alongside comments. the new modules/track-changes.mdx consolidates what consumers actually need: configuration, revision model (paired vs independent), the Document API (editor.doc.trackChanges.*), editor commands, events, permissions, and DOCX import/export. - new: apps/docs/modules/track-changes.mdx modeled on modules/comments.mdx - nav: docs.json lists modules/track-changes after modules/comments - redirect: /extensions/track-changes → /modules/track-changes so bookmarked URLs and external links keep working - deleted: apps/docs/extensions/track-changes.mdx (was hidden anyway) - updated: modules/overview, extensions/overview, and superdoc/configuration cross-links point at the new page * docs(track-changes): correct docs against codebase + lead with Document API (SD-2608) audited every claim on modules/track-changes.mdx against packages/ and fixed the drift. the Document API is now the primary surface; legacy editor commands are grouped under a single deprecated section. corrections: - event payload is flat ({ type: 'trackedChange', event, changeId, ... }), not nested under { type, comment }. fields trackedChangeType, trackedChangeText, deletedText, trackedChangeDisplayType, author, authorEmail, date, importedAuthor come straight from comments-plugin.js:1134 - event values pulled from shared/common/event-types.ts: add, update, deleted, resolved, selected, change-accepted, change-rejected - permissions include RESOLVE_OWN / RESOLVE_OTHER (accept) alongside REJECT_OWN / REJECT_OTHER (permission-helpers.js:3-12) - trackChanges.list() returns .items (not .changes) per DiscoveryOutput - removed disableTrackChangesShowFinal() — not implemented - mark names confirmed: trackInsert / trackDelete / trackFormat restructuring: - Document API section moved up and gets the full TrackChangeInfo shape - editor.commands usage moved to a "Legacy editor commands" section at the bottom, wrapped in a Warning banner pointing at the Document API - voice aligned with brand.md: shorter sentences, concrete examples, "Import it. Edit it. Export it. Nothing lost." over abstract claims no code changes. * docs(track-changes): use Lucide play icon for the example card (SD-2608) * docs(track-changes): use git-compare icon to match module theming (SD-2608) * feat(dev-app): add tracked-replacements mode toggle (SD-2608) expose modules.trackChanges.replacements in the SuperDoc dev app so developers can test both 'paired' (Google Docs) and 'independent' (Word) revision models without editing code. - reads the initial value from ?replacements=independent in the URL so test links can pin a specific mode - switching via the header <select> updates the URL param and reloads, same pattern used by the Layout Engine and View Layout toggles - moved the legacy top-level trackChanges: { visible: true } config to modules.trackChanges.{visible, replacements} so the dev app stops triggering its own deprecation warning
1 parent f778c4a commit f3bfc71

27 files changed

Lines changed: 1496 additions & 375 deletions

apps/docs/core/superdoc/configuration.mdx

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ new SuperDoc({
112112
- `viewing` - Read-only display
113113
- `suggesting` - Track changes enabled
114114
</Expandable>
115-
<Info>See the [Track Changes extension](/extensions/track-changes) for accept/reject commands, and the [runnable example](https://github.com/superdoc-dev/superdoc/tree/main/examples/features/track-changes) for a complete workflow.</Info>
115+
<Info>See the [Track Changes module](/modules/track-changes) for accept/reject commands, the Document API, and configuration. The [runnable example](https://github.com/superdoc-dev/superdoc/tree/main/examples/features/track-changes) shows a complete workflow.</Info>
116116
</ParamField>
117117

118118
<ParamField path="comments" type="Object">
@@ -124,11 +124,11 @@ new SuperDoc({
124124
</Expandable>
125125
</ParamField>
126126

127-
<ParamField path="trackChanges" type="Object">
128-
Viewing-mode visibility controls for tracked changes
127+
<ParamField path="trackChanges" type="Object" deprecated>
128+
<Warning>**Deprecated** — Use [`modules.trackChanges`](#track-changes-module) instead. This top-level key remains supported as an alias and will emit a one-time console warning.</Warning>
129129
<Expandable title="properties" defaultOpen>
130130
<ParamField path="trackChanges.visible" type="boolean" default="false">
131-
Show tracked-change markup and threads when `documentMode` is `viewing`
131+
Show tracked-change markup and threads when `documentMode` is `viewing`.
132132
</ParamField>
133133
</Expandable>
134134
</ParamField>
@@ -196,6 +196,56 @@ new SuperDoc({
196196
</Expandable>
197197
</ParamField>
198198

199+
### Track changes module
200+
201+
<ParamField path="modules.trackChanges" type="Object">
202+
Track changes configuration. Supersedes the top-level `trackChanges` and `layoutEngineOptions.trackedChanges` keys, which remain supported as deprecated aliases.
203+
204+
<Expandable title="properties" defaultOpen>
205+
<ParamField path="modules.trackChanges.visible" type="boolean" default="false">
206+
Show tracked-change markup and threads when `documentMode` is `viewing`.
207+
</ParamField>
208+
<ParamField path="modules.trackChanges.mode" type="'review' | 'original' | 'final' | 'off'">
209+
Rendering mode for tracked changes.
210+
- `'review'`: show insertions and deletions inline (default for editing/suggesting).
211+
- `'original'`: show the document as it existed before tracked changes (default for viewing when `visible` is `false`).
212+
- `'final'`: show the document with changes applied.
213+
- `'off'`: disable tracked-change rendering.
214+
</ParamField>
215+
<ParamField path="modules.trackChanges.enabled" type="boolean" default="true">
216+
Whether the layout engine treats tracked changes as active.
217+
</ParamField>
218+
<ParamField path="modules.trackChanges.replacements" type="'paired' | 'independent'" default="'paired'">
219+
How a tracked replacement (adjacent insertion + deletion created by typing over selected text) surfaces in the UI and API.
220+
- `'paired'` (default, Google Docs model): the two halves share one id and resolve together with a single accept/reject click.
221+
- `'independent'` (Microsoft Word / ECMA-376 §17.13.5 model): each insertion and each deletion has its own id, is addressable on its own, and resolves independently.
222+
</ParamField>
223+
</Expandable>
224+
</ParamField>
225+
226+
```javascript
227+
new SuperDoc({
228+
selector: '#editor',
229+
document: 'contract.docx',
230+
documentMode: 'viewing',
231+
modules: {
232+
trackChanges: { visible: true, mode: 'review' },
233+
},
234+
});
235+
```
236+
237+
Opt into Microsoft Word / ECMA-376-style independent revisions, where each insertion and each deletion has its own id and resolves on its own:
238+
239+
```javascript
240+
new SuperDoc({
241+
selector: '#editor',
242+
document: 'contract.docx',
243+
modules: {
244+
trackChanges: { replacements: 'independent' },
245+
},
246+
});
247+
```
248+
199249
### Toolbar module
200250

201251
<ParamField path="modules.toolbar" type="Object">

apps/docs/docs.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@
154154
]
155155
},
156156
"modules/comments",
157+
"modules/track-changes",
157158
{
158159
"group": "Toolbar",
159160
"tag": "NEW",
@@ -455,6 +456,10 @@
455456
"source": "/extensions/slash-menu",
456457
"destination": "/extensions/context-menu"
457458
},
459+
{
460+
"source": "/extensions/track-changes",
461+
"destination": "/modules/track-changes"
462+
},
458463
{
459464
"source": "/guides/breaking-changes-v1",
460465
"destination": "/guides/migration/breaking-changes-v1"

apps/docs/extensions/overview.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ Basic document capabilities:
5353

5454
### Advanced features
5555
Complex functionality:
56-
- **[Track Changes](/extensions/track-changes)** - Revision tracking
56+
- **[Track Changes](/modules/track-changes)** - Revision tracking
5757
- **[Comments](/extensions/comments)** - Discussions
5858
- **[Field Annotation](/extensions/field-annotation)** - Form fields
5959
- **[Document Section](/extensions/document-section)** - Locked sections

0 commit comments

Comments
 (0)