superdoc 2.0.0-next.45 → 2.0.0-next.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/AGENTS.md +15 -8
  2. package/dist/chunks/{create-super-doc-ui-BTOlZen9.es.js → create-super-doc-ui-CKyCKKdl.es.js} +244 -143
  3. package/dist/chunks/{create-super-doc-ui-DDxbK_M7.cjs → create-super-doc-ui-Dk6T01KE.cjs} +243 -142
  4. package/dist/collaboration-upgrade-engine.cjs +1 -1
  5. package/dist/collaboration-upgrade-engine.es.js +1 -1
  6. package/dist/document-api/src/contract/command-catalog.d.ts +1 -1
  7. package/dist/document-api/src/contract/operation-definitions.d.ts +1 -1
  8. package/dist/public/ui-react.cjs +17 -14
  9. package/dist/public/ui-react.es.js +17 -14
  10. package/dist/public/ui.cjs +1 -1
  11. package/dist/public/ui.es.js +1 -1
  12. package/dist/style.css +23 -23
  13. package/dist/style.layered.css +23 -23
  14. package/dist/superdoc/src/components/surfaces/FindReplaceSurface.vue.d.ts +2 -2
  15. package/dist/superdoc/src/components/surfaces/PasswordPromptSurface.vue.d.ts +2 -2
  16. package/dist/superdoc/src/components/surfaces/SurfaceExternalMount.vue.d.ts +2 -2
  17. package/dist/superdoc/src/composables/replace-continuation.d.ts +37 -0
  18. package/dist/superdoc/src/composables/use-find-replace.d.ts +2 -2
  19. package/dist/superdoc/src/core/SuperDoc.d.ts +41 -0
  20. package/dist/superdoc/src/core/types/index.d.ts +2 -14
  21. package/dist/superdoc/src/internal/toolbar/built-in/default-items.d.ts +11 -0
  22. package/dist/superdoc/src/internal/toolbar/built-in-toolbar.d.ts +5 -5
  23. package/dist/superdoc/src/public/browser-document-api.d.ts +53 -0
  24. package/dist/superdoc/src/public/index.d.cts +4 -0
  25. package/dist/superdoc/src/public/index.d.ts +2 -0
  26. package/dist/superdoc/src/public/ui/react.d.ts +18 -7
  27. package/dist/superdoc/src/public/ui/types.d.ts +81 -12
  28. package/dist/superdoc/src/public/ui-react.d.ts +4 -3
  29. package/dist/superdoc/src/public/ui.d.cts +8 -0
  30. package/dist/superdoc/src/public/ui.d.ts +1 -1
  31. package/dist/superdoc.cjs +84 -59
  32. package/dist/superdoc.es.js +84 -59
  33. package/dist-cdn/style.layered.css +1 -1
  34. package/dist-cdn/superdoc.min.css +1 -1
  35. package/dist-cdn/superdoc.min.js +35 -35
  36. package/package.json +1 -1
package/AGENTS.md CHANGED
@@ -179,7 +179,14 @@ For building your own browser UI (toolbars, selection popovers, comment panels,
179
179
  citation cards) around a live SuperDoc editor, use the v2-native public custom-UI
180
180
  entrypoints. They are the supported subpath exports for custom UI:
181
181
 
182
- - `superdoc/ui` — framework-agnostic controller: `createSuperDocUI({ superdoc })`.
182
+ - `superdoc.ui` — the framework-agnostic controller every instance owns. Read it;
183
+ do not build one. `superdoc.destroy()` tears it down. The property is typed
184
+ `BorrowedSuperDocUI`, which omits `destroy()`, so you cannot tear down state
185
+ the built-in toolbar and other consumers of that instance are still reading.
186
+ Only a controller you created yourself is yours to destroy.
187
+ - `superdoc/ui` — the controller's types, plus `createSuperDocUI({ superdoc })`
188
+ for the rare case you want a **second, independently owned** controller that
189
+ you create and destroy yourself.
183
190
  - `superdoc/ui/react` — React bindings: `SuperDocUIProvider`, `useSetSuperDoc`,
184
191
  `useSuperDocUI`, `useSuperDocHost`, and slice hooks.
185
192
 
@@ -207,14 +214,14 @@ painted output or reach into editor internals for coordinates.
207
214
  **Supported / unsupported.**
208
215
 
209
216
  - There is **no public headless path.** Backend / external Document API access uses
210
- the SDK (`@superdoc-dev/sdk`) — see *Document Engine* above. Browser custom UI uses
211
- `superdoc/ui`.
217
+ the SDK (`@superdoc-dev/sdk`) — see *Document Engine* above. Browser custom UI
218
+ reads `superdoc.ui`, typed by `superdoc/ui`.
212
219
  - **Migrating from v1 `superdoc/headless-toolbar*`?** Those package subpaths are
213
220
  intentionally removed in v2 (migration-only posture). Move v1 custom toolbar /
214
- headless-toolbar code to `superdoc/ui` (`createSuperDocUI`) and
215
- `superdoc/ui/react`. Unsupported or not-yet-migrated commands stay visible in
216
- the toolbar snapshot as disabled with a stable `reason` (the `SuperDocUIReason`
217
- taxonomy on `CommandState`) instead of silently disappearing.
221
+ headless-toolbar code to `superdoc.ui` and `superdoc/ui/react`. Unsupported or
222
+ not-yet-migrated commands stay visible in the toolbar snapshot as disabled with
223
+ a stable `reason` (the `SuperDocUIReason` taxonomy on `CommandState`) instead
224
+ of silently disappearing.
218
225
  - The SuperDoc shell may import only the supported `@superdoc/docx-engine`
219
226
  package entries. Do **not** import internal implementation packages such as
220
227
  `@superdoc/v2-host`, `@superdoc/document-api-v2-adapter`, or
@@ -241,7 +248,7 @@ painted output or reach into editor internals for coordinates.
241
248
  | Format text (programmatic) | Use Document API: `editor.doc.format.bold(...)`, `.italic(...)`, etc. |
242
249
  | Real-time collab | Configure `modules.collaboration` with a Yjs provider |
243
250
  | Custom built-in toolbar | Use `modules.toolbar.customButtons` array |
244
- | Build your own UI | Use `superdoc/ui` / `superdoc/ui/react` (`createSuperDocUI`) — see *Custom UI* above |
251
+ | Build your own UI | Read `superdoc.ui`, or use `superdoc/ui/react` — see *Custom UI* above |
245
252
  | Anchor citations to inserted text | Read the Document API receipt's `effects.insertedText[]` |
246
253
  | Listen to events | `superdoc.on('ready', ({ superdoc }) => { ... })` |
247
254
 
@@ -320,118 +320,6 @@ const isV2EditableTextMutationEvent = (event) => {
320
320
  if (event?.type !== "mutation:committed" || event.origin !== "command") return false;
321
321
  return V2_TYPING_COMMAND_KINDS.has(event.editableCommandKind);
322
322
  };
323
- function shallowEqual(a, b) {
324
- if (Object.is(a, b)) return true;
325
- if (typeof a !== "object" || a === null || typeof b !== "object" || b === null) return false;
326
- if (Array.isArray(a) !== Array.isArray(b)) return false;
327
- const aKeys = Object.keys(a);
328
- const bKeys = Object.keys(b);
329
- if (aKeys.length !== bKeys.length) return false;
330
- for (const key of aKeys) {
331
- if (!Object.prototype.hasOwnProperty.call(b, key)) return false;
332
- if (!Object.is(a[key], b[key])) return false;
333
- }
334
- return true;
335
- }
336
- function sdRunPropsToInlineRunPatch(props) {
337
- if (!props || typeof props !== "object") return {};
338
- const patch = {};
339
- if (props.fonts) patch.rFonts = {
340
- ascii: props.fonts.ascii,
341
- hAnsi: props.fonts.hAnsi,
342
- eastAsia: props.fonts.eastAsia,
343
- cs: props.fonts.cs,
344
- asciiTheme: props.fonts.asciiTheme,
345
- hAnsiTheme: props.fonts.hAnsiTheme,
346
- eastAsiaTheme: props.fonts.eastAsiaTheme,
347
- csTheme: props.fonts.csTheme,
348
- hint: props.fonts.hint
349
- };
350
- if (props.fontFamily != null) patch.fontFamily = props.fontFamily;
351
- if (props.lang) patch.lang = {
352
- val: props.lang.val,
353
- eastAsia: props.lang.eastAsia,
354
- bidi: props.lang.bidi
355
- };
356
- if (props.fontSize != null) patch.fontSize = props.fontSize;
357
- if (props.fontSizeCs != null) patch.fontSizeCs = props.fontSizeCs;
358
- const color = projectColorRefToInlineColor(props.color);
359
- if (color != null) patch.color = color;
360
- if (props.highlight != null) patch.highlight = props.highlight;
361
- const shading = projectShadingToInlinePatch(props.shading);
362
- if (shading) patch.shading = shading;
363
- if (props.cs != null) patch.cs = props.cs;
364
- if (props.rtl != null) patch.rtl = props.rtl;
365
- if (props.bold != null) patch.bold = props.bold;
366
- if (props.boldCs != null) patch.bCs = props.boldCs;
367
- if (props.italic != null) patch.italic = props.italic;
368
- if (props.italicCs != null) patch.iCs = props.italicCs;
369
- const underline = projectUnderlineToInlinePatch(props.underline);
370
- if (underline !== void 0) patch.underline = underline;
371
- if (props.strikethrough != null) patch.strike = props.strikethrough;
372
- if (props.doubleStrikethrough != null) patch.dstrike = props.doubleStrikethrough;
373
- if (props.caps != null) patch.caps = props.caps;
374
- if (props.smallCaps != null) patch.smallCaps = props.smallCaps;
375
- if (props.outline != null) patch.outline = props.outline;
376
- if (props.shadow != null) patch.shadow = props.shadow;
377
- if (props.emboss != null) patch.emboss = props.emboss;
378
- if (props.imprint != null) patch.imprint = props.imprint;
379
- if (props.verticalAlign != null) patch.vertAlign = props.verticalAlign;
380
- if (props.characterSpacing != null) patch.letterSpacing = props.characterSpacing;
381
- if (props.characterScale != null) patch.charScale = props.characterScale;
382
- if (props.kern != null) patch.kerning = props.kern;
383
- if (props.baselineShift != null) patch.position = props.baselineShift;
384
- if (props.fitTextWidth != null) patch.fitText = { val: props.fitTextWidth };
385
- if (props.vanish != null) patch.vanish = props.vanish;
386
- if (props.webHidden != null) patch.webHidden = props.webHidden;
387
- if (props.specVanish != null) patch.specVanish = props.specVanish;
388
- const border = projectBorderToInlinePatch(props.border);
389
- if (border) patch.border = border;
390
- return patch;
391
- }
392
- function projectColorRefToInlineColor(color) {
393
- if (!color) return void 0;
394
- if (typeof color === "string") return color;
395
- if (color.model === "rgb") return color.value;
396
- if (color.model === "auto") return "auto";
397
- }
398
- function projectUnderlineToInlinePatch(underline) {
399
- if (underline == null) return void 0;
400
- const patch = {};
401
- if (underline.style != null) patch.style = underline.style;
402
- if (underline.color?.model === "rgb") patch.color = underline.color.value;
403
- if (underline.color?.model === "theme") patch.themeColor = underline.color.theme;
404
- return Object.keys(patch).length > 0 ? patch : true;
405
- }
406
- function projectShadingToInlinePatch(shading) {
407
- if (!shading) return void 0;
408
- const patch = {};
409
- const fill = projectColorRefToInlineColor(shading.fill);
410
- if (fill != null) patch.fill = fill;
411
- const color = projectColorRefToInlineColor(shading.color);
412
- if (color != null) patch.color = color;
413
- if (shading.pattern != null) patch.val = shading.pattern;
414
- return Object.keys(patch).length > 0 ? patch : void 0;
415
- }
416
- function projectBorderToInlinePatch(border) {
417
- if (!border) return void 0;
418
- const patch = {};
419
- if (border.style != null) patch.val = border.style;
420
- if (border.width != null) patch.sz = border.width;
421
- if (border.space != null) patch.space = border.space;
422
- const color = projectColorRefToInlineColor(border.color);
423
- if (color != null) patch.color = color;
424
- return Object.keys(patch).length > 0 ? patch : void 0;
425
- }
426
- function selectionKey(selection) {
427
- const target = selection?.selectionTarget ?? selection?.target;
428
- if (!selection || !target) return "";
429
- try {
430
- return JSON.stringify(target);
431
- } catch {
432
- return String(target);
433
- }
434
- }
435
323
  const SUPERDOC_UI_REASONS = {
436
324
  notReady: "not-ready",
437
325
  documentApiUnavailable: "document-api-unavailable",
@@ -1094,6 +982,118 @@ const ALL_BUILT_IN_COMMAND_IDS = COMMAND_CATALOG.map((d) => d.id);
1094
982
  function getCommandDescriptor(id) {
1095
983
  return CATALOG_BY_ID.get(id) ?? null;
1096
984
  }
985
+ function shallowEqual(a, b) {
986
+ if (Object.is(a, b)) return true;
987
+ if (typeof a !== "object" || a === null || typeof b !== "object" || b === null) return false;
988
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
989
+ const aKeys = Object.keys(a);
990
+ const bKeys = Object.keys(b);
991
+ if (aKeys.length !== bKeys.length) return false;
992
+ for (const key of aKeys) {
993
+ if (!Object.prototype.hasOwnProperty.call(b, key)) return false;
994
+ if (!Object.is(a[key], b[key])) return false;
995
+ }
996
+ return true;
997
+ }
998
+ function sdRunPropsToInlineRunPatch(props) {
999
+ if (!props || typeof props !== "object") return {};
1000
+ const patch = {};
1001
+ if (props.fonts) patch.rFonts = {
1002
+ ascii: props.fonts.ascii,
1003
+ hAnsi: props.fonts.hAnsi,
1004
+ eastAsia: props.fonts.eastAsia,
1005
+ cs: props.fonts.cs,
1006
+ asciiTheme: props.fonts.asciiTheme,
1007
+ hAnsiTheme: props.fonts.hAnsiTheme,
1008
+ eastAsiaTheme: props.fonts.eastAsiaTheme,
1009
+ csTheme: props.fonts.csTheme,
1010
+ hint: props.fonts.hint
1011
+ };
1012
+ if (props.fontFamily != null) patch.fontFamily = props.fontFamily;
1013
+ if (props.lang) patch.lang = {
1014
+ val: props.lang.val,
1015
+ eastAsia: props.lang.eastAsia,
1016
+ bidi: props.lang.bidi
1017
+ };
1018
+ if (props.fontSize != null) patch.fontSize = props.fontSize;
1019
+ if (props.fontSizeCs != null) patch.fontSizeCs = props.fontSizeCs;
1020
+ const color = projectColorRefToInlineColor(props.color);
1021
+ if (color != null) patch.color = color;
1022
+ if (props.highlight != null) patch.highlight = props.highlight;
1023
+ const shading = projectShadingToInlinePatch(props.shading);
1024
+ if (shading) patch.shading = shading;
1025
+ if (props.cs != null) patch.cs = props.cs;
1026
+ if (props.rtl != null) patch.rtl = props.rtl;
1027
+ if (props.bold != null) patch.bold = props.bold;
1028
+ if (props.boldCs != null) patch.bCs = props.boldCs;
1029
+ if (props.italic != null) patch.italic = props.italic;
1030
+ if (props.italicCs != null) patch.iCs = props.italicCs;
1031
+ const underline = projectUnderlineToInlinePatch(props.underline);
1032
+ if (underline !== void 0) patch.underline = underline;
1033
+ if (props.strikethrough != null) patch.strike = props.strikethrough;
1034
+ if (props.doubleStrikethrough != null) patch.dstrike = props.doubleStrikethrough;
1035
+ if (props.caps != null) patch.caps = props.caps;
1036
+ if (props.smallCaps != null) patch.smallCaps = props.smallCaps;
1037
+ if (props.outline != null) patch.outline = props.outline;
1038
+ if (props.shadow != null) patch.shadow = props.shadow;
1039
+ if (props.emboss != null) patch.emboss = props.emboss;
1040
+ if (props.imprint != null) patch.imprint = props.imprint;
1041
+ if (props.verticalAlign != null) patch.vertAlign = props.verticalAlign;
1042
+ if (props.characterSpacing != null) patch.letterSpacing = props.characterSpacing;
1043
+ if (props.characterScale != null) patch.charScale = props.characterScale;
1044
+ if (props.kern != null) patch.kerning = props.kern;
1045
+ if (props.baselineShift != null) patch.position = props.baselineShift;
1046
+ if (props.fitTextWidth != null) patch.fitText = { val: props.fitTextWidth };
1047
+ if (props.vanish != null) patch.vanish = props.vanish;
1048
+ if (props.webHidden != null) patch.webHidden = props.webHidden;
1049
+ if (props.specVanish != null) patch.specVanish = props.specVanish;
1050
+ const border = projectBorderToInlinePatch(props.border);
1051
+ if (border) patch.border = border;
1052
+ return patch;
1053
+ }
1054
+ function projectColorRefToInlineColor(color) {
1055
+ if (!color) return void 0;
1056
+ if (typeof color === "string") return color;
1057
+ if (color.model === "rgb") return color.value;
1058
+ if (color.model === "auto") return "auto";
1059
+ }
1060
+ function projectUnderlineToInlinePatch(underline) {
1061
+ if (underline == null) return void 0;
1062
+ const patch = {};
1063
+ if (underline.style != null) patch.style = underline.style;
1064
+ if (underline.color?.model === "rgb") patch.color = underline.color.value;
1065
+ if (underline.color?.model === "theme") patch.themeColor = underline.color.theme;
1066
+ return Object.keys(patch).length > 0 ? patch : true;
1067
+ }
1068
+ function projectShadingToInlinePatch(shading) {
1069
+ if (!shading) return void 0;
1070
+ const patch = {};
1071
+ const fill = projectColorRefToInlineColor(shading.fill);
1072
+ if (fill != null) patch.fill = fill;
1073
+ const color = projectColorRefToInlineColor(shading.color);
1074
+ if (color != null) patch.color = color;
1075
+ if (shading.pattern != null) patch.val = shading.pattern;
1076
+ return Object.keys(patch).length > 0 ? patch : void 0;
1077
+ }
1078
+ function projectBorderToInlinePatch(border) {
1079
+ if (!border) return void 0;
1080
+ const patch = {};
1081
+ if (border.style != null) patch.val = border.style;
1082
+ if (border.width != null) patch.sz = border.width;
1083
+ if (border.space != null) patch.space = border.space;
1084
+ const color = projectColorRefToInlineColor(border.color);
1085
+ if (color != null) patch.color = color;
1086
+ return Object.keys(patch).length > 0 ? patch : void 0;
1087
+ }
1088
+ function selectionKey(selection) {
1089
+ const target = selection?.selectionTarget ?? selection?.target;
1090
+ if (!selection || !target) return "";
1091
+ try {
1092
+ return JSON.stringify(target);
1093
+ } catch {
1094
+ return String(target);
1095
+ }
1096
+ }
1097
1097
  function parseCommaSeparatedIds(value) {
1098
1098
  return value.split(",").map((id) => id.trim()).filter(Boolean);
1099
1099
  }
@@ -1503,6 +1503,7 @@ function normalizeHostSelectionApplyResult(result) {
1503
1503
  }
1504
1504
  var HOST_EVENTS = [
1505
1505
  "editorCreate",
1506
+ "active-editor-change",
1506
1507
  "document-mode-change",
1507
1508
  "zoomChange",
1508
1509
  "measurement-unit-change",
@@ -5728,11 +5729,20 @@ function createSuperDocUI(options) {
5728
5729
  state = nextState;
5729
5730
  for (const listener of [...listeners]) listener(state);
5730
5731
  };
5732
+ const activeEditorResetHooks = [];
5733
+ const runActiveEditorResetHooks = () => {
5734
+ for (const reset of activeEditorResetHooks) try {
5735
+ reset();
5736
+ } catch {}
5737
+ };
5731
5738
  const detachers = [];
5732
5739
  const attach = (source, events) => {
5733
5740
  if (!source || typeof source.on !== "function") return;
5734
5741
  for (const event of events) {
5735
- const handler = () => recompute();
5742
+ const handler = event === "active-editor-change" ? () => {
5743
+ runActiveEditorResetHooks();
5744
+ recompute();
5745
+ } : () => recompute();
5736
5746
  try {
5737
5747
  source.on(event, handler);
5738
5748
  detachers.push(() => {
@@ -8036,6 +8046,28 @@ function createSuperDocUI(options) {
8036
8046
  if (typeof xOrInput === "object") return pointEntityHits(xOrInput?.x, xOrInput?.y);
8037
8047
  return null;
8038
8048
  }
8049
+ const geometryObservers = /* @__PURE__ */ new Set();
8050
+ const bindGeometryObserver = (entry) => {
8051
+ const host = getHost();
8052
+ if (typeof host?.observeGeometry !== "function") return;
8053
+ const off = safeCall(() => host.observeGeometry(() => entry.schedule()), null);
8054
+ entry.detach = typeof off === "function" ? off : null;
8055
+ };
8056
+ const detachGeometryObserver = (entry) => {
8057
+ if (!entry.detach) return;
8058
+ try {
8059
+ entry.detach();
8060
+ } catch {}
8061
+ entry.detach = null;
8062
+ };
8063
+ const rebindGeometryObservers = () => {
8064
+ for (const entry of geometryObservers) {
8065
+ detachGeometryObserver(entry);
8066
+ bindGeometryObserver(entry);
8067
+ entry.schedule();
8068
+ }
8069
+ };
8070
+ activeEditorResetHooks.push(rebindGeometryObservers);
8039
8071
  const viewport = {
8040
8072
  getRect: (input) => {
8041
8073
  const host = getHost();
@@ -8071,12 +8103,15 @@ function createSuperDocUI(options) {
8071
8103
  zoom: s.zoom
8072
8104
  }));
8073
8105
  unsubs.push(sub.subscribe(() => schedule()));
8074
- const host = getHost();
8075
- if (typeof host?.observeGeometry === "function") {
8076
- const off = safeCall(() => host.observeGeometry(() => schedule()), null);
8077
- if (typeof off === "function") unsubs.push(off);
8078
- }
8106
+ const entry = {
8107
+ schedule,
8108
+ detach: null
8109
+ };
8110
+ bindGeometryObserver(entry);
8111
+ geometryObservers.add(entry);
8079
8112
  return () => {
8113
+ geometryObservers.delete(entry);
8114
+ detachGeometryObserver(entry);
8080
8115
  if (frame && typeof cancelAnimationFrame === "function") cancelAnimationFrame(frame);
8081
8116
  for (const unsub of unsubs.splice(0)) try {
8082
8117
  unsub();
@@ -8250,6 +8285,28 @@ function createSuperDocUI(options) {
8250
8285
  return searchState;
8251
8286
  };
8252
8287
  let searchRequestGeneration = 0;
8288
+ let releaseActiveSearchSession = null;
8289
+ const releaseSearchSession = () => {
8290
+ const release = releaseActiveSearchSession;
8291
+ releaseActiveSearchSession = null;
8292
+ if (!release) return;
8293
+ safeCall(() => {
8294
+ release();
8295
+ return null;
8296
+ }, null);
8297
+ };
8298
+ const resetSearchForActiveEditorChange = () => {
8299
+ searchRequestGeneration += 1;
8300
+ releaseSearchSession();
8301
+ const available = searchIsAvailable();
8302
+ searchState = {
8303
+ ...SEARCH_UNAVAILABLE_SLICE,
8304
+ available,
8305
+ reason: available ? void 0 : SUPERDOC_UI_REASONS.searchUnavailable
8306
+ };
8307
+ emitSearch();
8308
+ };
8309
+ activeEditorResetHooks.push(resetSearchForActiveEditorChange);
8253
8310
  const readSearchQueryError = (record) => {
8254
8311
  if (!record) return false;
8255
8312
  const queryError = record.queryError;
@@ -8401,11 +8458,14 @@ function createSuperDocUI(options) {
8401
8458
  },
8402
8459
  close: () => {
8403
8460
  searchRequestGeneration += 1;
8404
- const host = getHostSearch();
8405
- if (host && typeof host.clear === "function") safeCall(() => host.clear(), null);
8461
+ if (releaseActiveSearchSession) releaseSearchSession();
8406
8462
  else {
8407
- const editSearch = getEditCommandSearch();
8408
- if (editSearch && typeof editSearch.query === "function") safeCall(() => editSearch.query({ query: "" }), null);
8463
+ const host = getHostSearch();
8464
+ if (host && typeof host.clear === "function") safeCall(() => host.clear(), null);
8465
+ else {
8466
+ const editSearch = getEditCommandSearch();
8467
+ if (editSearch && typeof editSearch.query === "function") safeCall(() => editSearch.query({ query: "" }), null);
8468
+ }
8409
8469
  }
8410
8470
  searchState = {
8411
8471
  ...SEARCH_UNAVAILABLE_SLICE,
@@ -8433,6 +8493,9 @@ function createSuperDocUI(options) {
8433
8493
  open: true,
8434
8494
  reason: void 0
8435
8495
  });
8496
+ releaseActiveSearchSession = () => {
8497
+ editSearch.query({ query: "" });
8498
+ };
8436
8499
  const result = safeCall(() => editSearch.query({
8437
8500
  query,
8438
8501
  caseSensitive,
@@ -8473,6 +8536,9 @@ function createSuperDocUI(options) {
8473
8536
  open: true,
8474
8537
  reason: void 0
8475
8538
  });
8539
+ releaseActiveSearchSession = () => {
8540
+ if (typeof host.clear === "function") host.clear();
8541
+ };
8476
8542
  applyHostSearchResult(safeCall(() => host.setSession(query, {
8477
8543
  caseSensitive,
8478
8544
  includeDeletedText,
@@ -8482,6 +8548,11 @@ function createSuperDocUI(options) {
8482
8548
  if (query.length > 0 && searchState.total === 0 && searchState.reason !== SUPERDOC_UI_REASONS.searchInvalidPattern) {
8483
8549
  const editSearch = getEditCommandSearch();
8484
8550
  if (editSearch && typeof editSearch.query === "function" && typeof editSearch.getState === "function") {
8551
+ const releaseHostSession = releaseActiveSearchSession;
8552
+ releaseActiveSearchSession = () => {
8553
+ releaseHostSession?.();
8554
+ editSearch.query({ query: "" });
8555
+ };
8485
8556
  const commandResult = safeCall(() => editSearch.query({
8486
8557
  query,
8487
8558
  caseSensitive,
@@ -8517,13 +8588,19 @@ function createSuperDocUI(options) {
8517
8588
  reason: SUPERDOC_UI_REASONS.operationUnavailable
8518
8589
  };
8519
8590
  const result = safeCall(() => editSearch.next(), null);
8520
- if (isPromiseLike(result)) Promise.resolve(result).then((resolved) => applyHostSearchResult(resolved, readEditCommandCanReplace())).catch(() => {
8521
- setSearchState({
8522
- available: false,
8523
- reason: SUPERDOC_UI_REASONS.searchUnavailable
8591
+ if (isPromiseLike(result)) {
8592
+ const generation = searchRequestGeneration;
8593
+ Promise.resolve(result).then((resolved) => {
8594
+ if (generation !== searchRequestGeneration) return;
8595
+ applyHostSearchResult(resolved, readEditCommandCanReplace());
8596
+ }).catch(() => {
8597
+ if (generation !== searchRequestGeneration) return;
8598
+ setSearchState({
8599
+ available: false,
8600
+ reason: SUPERDOC_UI_REASONS.searchUnavailable
8601
+ });
8524
8602
  });
8525
- });
8526
- else applyHostSearchResult(result, readEditCommandCanReplace());
8603
+ } else applyHostSearchResult(result, readEditCommandCanReplace());
8527
8604
  applyHostSearchResult(safeCall(() => editSearch.getState(), null), readEditCommandCanReplace());
8528
8605
  return { ok: true };
8529
8606
  }
@@ -8547,13 +8624,19 @@ function createSuperDocUI(options) {
8547
8624
  reason: SUPERDOC_UI_REASONS.operationUnavailable
8548
8625
  };
8549
8626
  const result = safeCall(() => editSearch.previous(), null);
8550
- if (isPromiseLike(result)) Promise.resolve(result).then((resolved) => applyHostSearchResult(resolved, readEditCommandCanReplace())).catch(() => {
8551
- setSearchState({
8552
- available: false,
8553
- reason: SUPERDOC_UI_REASONS.searchUnavailable
8627
+ if (isPromiseLike(result)) {
8628
+ const generation = searchRequestGeneration;
8629
+ Promise.resolve(result).then((resolved) => {
8630
+ if (generation !== searchRequestGeneration) return;
8631
+ applyHostSearchResult(resolved, readEditCommandCanReplace());
8632
+ }).catch(() => {
8633
+ if (generation !== searchRequestGeneration) return;
8634
+ setSearchState({
8635
+ available: false,
8636
+ reason: SUPERDOC_UI_REASONS.searchUnavailable
8637
+ });
8554
8638
  });
8555
- });
8556
- else applyHostSearchResult(result, readEditCommandCanReplace());
8639
+ } else applyHostSearchResult(result, readEditCommandCanReplace());
8557
8640
  applyHostSearchResult(safeCall(() => editSearch.getState(), null), readEditCommandCanReplace());
8558
8641
  return { ok: true };
8559
8642
  }
@@ -8565,6 +8648,7 @@ function createSuperDocUI(options) {
8565
8648
  return { ok: true };
8566
8649
  },
8567
8650
  clear: () => {
8651
+ searchRequestGeneration += 1;
8568
8652
  const host = getHostSearch();
8569
8653
  if (host && typeof host.clear === "function") safeCall(() => host.clear(), null);
8570
8654
  if (!host || typeof host.clear !== "function") {
@@ -8592,15 +8676,19 @@ function createSuperDocUI(options) {
8592
8676
  };
8593
8677
  const result$1 = safeCall(() => editSearch.replace({ replacement: typeof replacement === "string" ? replacement : "" }), null);
8594
8678
  if (isPromiseLike(result$1)) {
8679
+ const generation = searchRequestGeneration;
8595
8680
  syncSearchStateFromHost();
8596
8681
  emitSearch();
8682
+ const isCurrent = () => generation === searchRequestGeneration;
8597
8683
  return Promise.resolve(result$1).then((resolved) => {
8598
- applyHostSearchResult(resolved, readEditCommandCanReplace());
8599
- syncSearchStateFromHost();
8600
- emitSearch();
8684
+ if (isCurrent()) {
8685
+ applyHostSearchResult(resolved, readEditCommandCanReplace());
8686
+ syncSearchStateFromHost();
8687
+ emitSearch();
8688
+ }
8601
8689
  return mapHostReplaceResult(resolved);
8602
8690
  }, () => {
8603
- setSearchState({
8691
+ if (isCurrent()) setSearchState({
8604
8692
  available: false,
8605
8693
  reason: SUPERDOC_UI_REASONS.searchUnavailable
8606
8694
  });
@@ -8637,15 +8725,19 @@ function createSuperDocUI(options) {
8637
8725
  };
8638
8726
  const result$1 = safeCall(() => editSearch.replaceAll({ replacement: typeof replacement === "string" ? replacement : "" }), null);
8639
8727
  if (isPromiseLike(result$1)) {
8728
+ const generation = searchRequestGeneration;
8640
8729
  syncSearchStateFromHost();
8641
8730
  emitSearch();
8731
+ const isCurrent = () => generation === searchRequestGeneration;
8642
8732
  return Promise.resolve(result$1).then((resolved) => {
8643
- applyHostSearchResult(resolved, readEditCommandCanReplace());
8644
- syncSearchStateFromHost();
8645
- emitSearch();
8733
+ if (isCurrent()) {
8734
+ applyHostSearchResult(resolved, readEditCommandCanReplace());
8735
+ syncSearchStateFromHost();
8736
+ emitSearch();
8737
+ }
8646
8738
  return mapHostReplaceResult(resolved);
8647
8739
  }, () => {
8648
- setSearchState({
8740
+ if (isCurrent()) setSearchState({
8649
8741
  available: false,
8650
8742
  reason: SUPERDOC_UI_REASONS.searchUnavailable
8651
8743
  });
@@ -8746,6 +8838,9 @@ function createSuperDocUI(options) {
8746
8838
  currentDocumentSelectionSource = null;
8747
8839
  listeners.clear();
8748
8840
  searchListeners.clear();
8841
+ painterCaptureEpoch += 1;
8842
+ painterModeListeners.clear();
8843
+ searchRequestGeneration += 1;
8749
8844
  customCommands.clear();
8750
8845
  };
8751
8846
  const controller = {
@@ -8769,12 +8864,14 @@ function createSuperDocUI(options) {
8769
8864
  styles,
8770
8865
  formatPainter: {
8771
8866
  setPointerSelecting(flag) {
8867
+ if (disposed) return;
8772
8868
  painter = {
8773
8869
  ...painter,
8774
8870
  pointerSelecting: flag
8775
8871
  };
8776
8872
  },
8777
8873
  notifyPointerUp() {
8874
+ if (disposed) return;
8778
8875
  painter = {
8779
8876
  ...painter,
8780
8877
  pointerSelecting: false
@@ -8782,12 +8879,14 @@ function createSuperDocUI(options) {
8782
8879
  maybeApply();
8783
8880
  },
8784
8881
  setKeyboardSelecting(flag) {
8882
+ if (disposed) return;
8785
8883
  painter = {
8786
8884
  ...painter,
8787
8885
  keyboardSelecting: flag
8788
8886
  };
8789
8887
  },
8790
8888
  notifyKeyUp() {
8889
+ if (disposed) return;
8791
8890
  painter = {
8792
8891
  ...painter,
8793
8892
  keyboardSelecting: false
@@ -8795,9 +8894,11 @@ function createSuperDocUI(options) {
8795
8894
  maybeApply();
8796
8895
  },
8797
8896
  cancel() {
8897
+ if (disposed) return;
8798
8898
  exitFormatPainter();
8799
8899
  },
8800
8900
  onModeChange(cb) {
8901
+ if (disposed) return () => {};
8801
8902
  painterModeListeners.add(cb);
8802
8903
  return () => painterModeListeners.delete(cb);
8803
8904
  }
@@ -8944,4 +9045,4 @@ var DEFAULT_FONT_SIZE_OPTIONS = [
8944
9045
  function commandIsActive(descriptor, selection) {
8945
9046
  return descriptor.activeMark ? selection.activeMarks.includes(descriptor.activeMark) : false;
8946
9047
  }
8947
- export { createV2ReviewMutationReconciler as a, isV2EditableTextMutationEvent as i, BUILT_IN_COMMAND_IDS as n, getV2TrackedChangeMutationImpact as o, shallowEqual as r, composeAuthorColorResolver as s, createSuperDocUI as t };
9048
+ export { createV2ReviewMutationReconciler as a, isV2EditableTextMutationEvent as i, shallowEqual as n, getV2TrackedChangeMutationImpact as o, BUILT_IN_COMMAND_IDS as r, composeAuthorColorResolver as s, createSuperDocUI as t };