superdoc 2.20.0-next.9 → 2.20.0

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.
package/README.md CHANGED
@@ -28,3 +28,29 @@ See [package compatibility](https://docs.superdoc.dev/resources/package-compatib
28
28
  ## License
29
29
 
30
30
  AGPL-3.0. Commercial licenses are available from [SuperDoc](https://www.superdoc.dev).
31
+
32
+ ## Navigate to a bookmark
33
+
34
+ After the V2 browser editor is ready, navigate by an exact bookmark name:
35
+
36
+ ```js
37
+ const result = await editor.ui.bookmarks.navigateTo('Terms');
38
+ if (!result.success) console.log(result.reason);
39
+ ```
40
+
41
+ You can also pass a `BookmarkAddress` returned by `editor.activeEditor.doc.bookmarks.get()`
42
+ or `.list()`. The method reveals the bookmark's start and selects its complete range,
43
+ including across paragraphs. A collapsed bookmark places a caret. It supports editing,
44
+ suggesting, and viewing modes. Navigation stays local in a shared room and does not change
45
+ content or another client's selection. It does not take native keyboard focus.
46
+
47
+ Scrolling defaults to instant, centered navigation. Pass `{ block: 'start', behavior: 'auto' }`
48
+ as a second argument to change alignment or scrolling behavior.
49
+
50
+ The first release supports body bookmarks. Non-body and table-column bookmarks, malformed,
51
+ missing, deleted, or ambiguous targets return `{ success: false, ok: false, reason: 'target-unresolved' }`.
52
+ Name-only lookup requires a unique match across the document; an explicit body address scopes
53
+ lookup to the body. Resolution or reveal failure preserves the existing selection. Navigation
54
+ abandons stale work after content changes, document replacement, or a newer request, and times
55
+ out after 15 seconds. Missing editor or host capabilities return their readiness or availability
56
+ reason. The handle and address types are exported from `superdoc/ui`.
@@ -11275,6 +11275,113 @@ function createSuperDocUI(options) {
11275
11275
  };
11276
11276
  }
11277
11277
  };
11278
+ let bookmarkNavigationGeneration = 0;
11279
+ documentResetHooks.push(() => {
11280
+ bookmarkNavigationGeneration += 1;
11281
+ });
11282
+ const bookmarks = { navigateTo: async (input, options) => {
11283
+ const generation = ++bookmarkNavigationGeneration;
11284
+ const unresolved = () => ({
11285
+ success: false,
11286
+ ok: false,
11287
+ reason: SUPERDOC_UI_REASONS.targetUnresolved
11288
+ });
11289
+ const address = typeof input === "string" ? {
11290
+ kind: "entity",
11291
+ entityType: "bookmark",
11292
+ name: input
11293
+ } : input;
11294
+ if (!address || address.kind !== "entity" || address.entityType !== "bookmark" || typeof address.name !== "string" || address.name.length === 0 || address.story !== void 0 && (!address.story || address.story.kind !== "story" || address.story.storyType !== "body")) return unresolved();
11295
+ const editor = getEditor();
11296
+ if (!editor || disposed) return {
11297
+ success: false,
11298
+ ok: false,
11299
+ reason: SUPERDOC_UI_REASONS.notReady
11300
+ };
11301
+ const doc = getDoc();
11302
+ const api = doc?.bookmarks;
11303
+ if (typeof api?.list !== "function" || typeof api?.get !== "function") return {
11304
+ success: false,
11305
+ ok: false,
11306
+ reason: SUPERDOC_UI_REASONS.operationUnavailable
11307
+ };
11308
+ const helper = getSelectionApplyHelper();
11309
+ if (!("helper" in helper)) return {
11310
+ success: false,
11311
+ ok: false,
11312
+ reason: helper.reason
11313
+ };
11314
+ const host = getHost();
11315
+ if (typeof host?.scrollTargetIntoView !== "function") return {
11316
+ success: false,
11317
+ ok: false,
11318
+ reason: SUPERDOC_UI_REASONS.hostCapabilityUnavailable
11319
+ };
11320
+ const token = metadataResolutionToken();
11321
+ let active = true;
11322
+ const current = () => active && !disposed && generation === bookmarkNavigationGeneration && getEditor() === editor && getDoc() === doc && getHost() === host && metadataResolutionToken() === token;
11323
+ const run = async () => {
11324
+ try {
11325
+ const directory = await api.list.call(api, address.story ? { in: address.story } : {});
11326
+ if (!current()) return unresolved();
11327
+ if ((Array.isArray(directory?.items) ? directory.items : []).filter((item) => item?.name === address.name).length !== 1) return unresolved();
11328
+ const info = await api.get.call(api, { target: address });
11329
+ if (!current()) return unresolved();
11330
+ const from = info?.range?.from;
11331
+ const to = info?.range?.to;
11332
+ const pointValid = (point) => typeof point?.blockId === "string" && point.blockId.length > 0 && Number.isInteger(point.offset) && point.offset >= 0;
11333
+ if (info?.name !== address.name || info?.address?.story && info.address.story.storyType !== "body" || info?.tableColumn || !pointValid(from) || !pointValid(to) || from.blockId === to.blockId && from.offset > to.offset) return unresolved();
11334
+ const result = await scrollTargetIntoView({
11335
+ kind: "text",
11336
+ segments: [{
11337
+ blockId: from.blockId,
11338
+ range: {
11339
+ start: from.offset,
11340
+ end: from.offset
11341
+ }
11342
+ }]
11343
+ }, {
11344
+ block: options?.block ?? "center",
11345
+ behavior: options?.behavior ?? "instant",
11346
+ shouldContinue: current
11347
+ });
11348
+ if (!current()) return unresolved();
11349
+ if (!result.ok) return result;
11350
+ const applied = selection.apply({
11351
+ kind: "selection",
11352
+ start: {
11353
+ kind: "text",
11354
+ blockId: from.blockId,
11355
+ offset: from.offset
11356
+ },
11357
+ end: {
11358
+ kind: "text",
11359
+ blockId: to.blockId,
11360
+ offset: to.offset
11361
+ }
11362
+ });
11363
+ return {
11364
+ ...applied,
11365
+ success: applied.ok
11366
+ };
11367
+ } catch {
11368
+ return unresolved();
11369
+ }
11370
+ };
11371
+ let timer;
11372
+ const timeout = new Promise((resolve) => {
11373
+ timer = setTimeout(() => {
11374
+ active = false;
11375
+ resolve(unresolved());
11376
+ }, 15e3);
11377
+ });
11378
+ try {
11379
+ return await Promise.race([run(), timeout]);
11380
+ } finally {
11381
+ active = false;
11382
+ clearTimeout(timer);
11383
+ }
11384
+ } };
11278
11385
  /**
11279
11386
  * Derive the browser-shell's `importedId` carrier-lookup alias from a
11280
11387
  * public list row's `wordRevisionIds` / `sourceIds` provenance fields,
@@ -12508,6 +12615,7 @@ function createSuperDocUI(options) {
12508
12615
  };
12509
12616
  let searchRequestGeneration = 0;
12510
12617
  let pendingFallbackGeneration = null;
12618
+ let completedFallbackSearch = null;
12511
12619
  /**
12512
12620
  * How to tear down the session that is currently open, captured when it was
12513
12621
  * opened.
@@ -12556,6 +12664,7 @@ function createSuperDocUI(options) {
12556
12664
  * is deliberately not made here.
12557
12665
  */
12558
12666
  const resetSearchForActiveEditorChange = () => {
12667
+ completedFallbackSearch = null;
12559
12668
  searchRequestGeneration += 1;
12560
12669
  pendingFallbackGeneration = null;
12561
12670
  shellFallbackOutstanding = false;
@@ -12609,8 +12718,8 @@ function createSuperDocUI(options) {
12609
12718
  const editSearch = getEditCommandSearch();
12610
12719
  if (editSearch && typeof editSearch.query === "function") safeCall(() => editSearch.query({ query: "" }), null);
12611
12720
  };
12612
- const readEditCommandCanReplace = () => readEditCommandStateEntry("find.replace")?.enabled === true;
12613
- const readEditCommandCanReplaceAll = () => readEditCommandStateEntry("find.replaceAll")?.enabled === true;
12721
+ const readEditCommandCanReplace = () => pendingFallbackGeneration !== searchRequestGeneration && readEditCommandStateEntry("find.replace")?.enabled === true;
12722
+ const readEditCommandCanReplaceAll = () => pendingFallbackGeneration !== searchRequestGeneration && readEditCommandStateEntry("find.replaceAll")?.enabled === true;
12614
12723
  const applyHostSearchResult = (result, fallbackCanReplace = false, fallbackCanReplaceAll = fallbackCanReplace) => {
12615
12724
  const snapshot = normalizeHostSearchResult(result, fallbackCanReplace, fallbackCanReplaceAll);
12616
12725
  if (!snapshot) {
@@ -12771,6 +12880,7 @@ function createSuperDocUI(options) {
12771
12880
  return { ok: true };
12772
12881
  },
12773
12882
  close: () => {
12883
+ completedFallbackSearch = null;
12774
12884
  searchRequestGeneration += 1;
12775
12885
  cancelRefreshShellSearch();
12776
12886
  if (releaseActiveSearchSession) releaseSearchSession();
@@ -12795,6 +12905,16 @@ function createSuperDocUI(options) {
12795
12905
  const caseSensitive = Boolean(options?.caseSensitive);
12796
12906
  const includeDeletedText = (options?.includeTrackedDeletions ?? options?.includeDeletedText) === true;
12797
12907
  const regex = options?.regex === true;
12908
+ if ((!host || typeof host.setSession !== "function") && completedFallbackSearch !== null && completedFallbackSearch === getEditCommandSearch() && pendingFallbackGeneration !== searchRequestGeneration && searchRefreshTimer === null && searchState.open && searchState.available && !searchState.reason && searchState.query === query && searchState.caseSensitive === caseSensitive && searchState.includeDeletedText === includeDeletedText && searchState.regex === regex) {
12909
+ syncSearchStateFromHost();
12910
+ emitSearch();
12911
+ return searchState;
12912
+ }
12913
+ completedFallbackSearch = null;
12914
+ if (searchRefreshTimer !== null) {
12915
+ clearTimeout(searchRefreshTimer);
12916
+ searchRefreshTimer = null;
12917
+ }
12798
12918
  const generation = ++searchRequestGeneration;
12799
12919
  cancelRefreshShellSearch();
12800
12920
  cancelOutstandingShellFallback();
@@ -12830,6 +12950,7 @@ function createSuperDocUI(options) {
12830
12950
  if (generation !== searchRequestGeneration) return;
12831
12951
  shellFallbackOutstanding = false;
12832
12952
  pendingFallbackGeneration = null;
12953
+ completedFallbackSearch = editSearch;
12833
12954
  applyHostSearchResult(resolved, readEditCommandCanReplace(), readEditCommandCanReplaceAll());
12834
12955
  }, () => {
12835
12956
  if (generation !== searchRequestGeneration) return;
@@ -12842,6 +12963,7 @@ function createSuperDocUI(options) {
12842
12963
  return searchState;
12843
12964
  }
12844
12965
  pendingFallbackGeneration = null;
12966
+ completedFallbackSearch = editSearch;
12845
12967
  applyHostSearchResult(result, readEditCommandCanReplace(), readEditCommandCanReplaceAll());
12846
12968
  applyHostSearchResult(safeCall(() => editSearch.getState(), null), readEditCommandCanReplace(), readEditCommandCanReplaceAll());
12847
12969
  return searchState;
@@ -12984,6 +13106,7 @@ function createSuperDocUI(options) {
12984
13106
  return { ok: true };
12985
13107
  },
12986
13108
  clear: () => {
13109
+ completedFallbackSearch = null;
12987
13110
  searchRequestGeneration += 1;
12988
13111
  cancelRefreshShellSearch();
12989
13112
  cancelOutstandingShellFallback();
@@ -13099,6 +13222,7 @@ function createSuperDocUI(options) {
13099
13222
  };
13100
13223
  let searchRefreshTimer = null;
13101
13224
  scheduleActiveSearchRefresh = () => {
13225
+ completedFallbackSearch = null;
13102
13226
  if (searchRefreshTimer !== null) clearTimeout(searchRefreshTimer);
13103
13227
  if (!searchState.open || !searchState.query) return;
13104
13228
  const scheduledGeneration = searchRequestGeneration;
@@ -13125,19 +13249,29 @@ function createSuperDocUI(options) {
13125
13249
  const result = typeof editSearch?.refresh === "function" ? safeCall(() => editSearch.refresh(input), null) : safeCall(() => host.refresh(), null);
13126
13250
  if (isPromiseLike(result)) {
13127
13251
  pendingFallbackGeneration = generation;
13252
+ setSearchState({
13253
+ canReplace: false,
13254
+ canReplaceAll: false
13255
+ });
13128
13256
  Promise.resolve(result).then((resolved) => {
13129
13257
  if (generation !== searchRequestGeneration || disposed) return;
13258
+ releaseRefreshShellSearch = null;
13130
13259
  pendingFallbackGeneration = null;
13260
+ completedFallbackSearch = editSearch;
13131
13261
  applyHostSearchResult(resolved, readEditCommandCanReplace(), readEditCommandCanReplaceAll());
13132
13262
  }, () => {
13133
13263
  if (generation !== searchRequestGeneration || disposed) return;
13134
- pendingFallbackGeneration = null;
13264
+ releaseRefreshShellSearch = null;
13135
13265
  setSearchState({
13136
13266
  available: false,
13137
13267
  reason: SUPERDOC_UI_REASONS.searchUnavailable
13138
13268
  });
13139
13269
  });
13140
- } else if (result !== null) applyHostSearchResult(result);
13270
+ } else if (result !== null) {
13271
+ releaseRefreshShellSearch = null;
13272
+ completedFallbackSearch = editSearch;
13273
+ applyHostSearchResult(result);
13274
+ }
13141
13275
  }, 120);
13142
13276
  };
13143
13277
  const stylesSub = sliceHandle((s) => s.styles);
@@ -13187,6 +13321,7 @@ function createSuperDocUI(options) {
13187
13321
  watermark.destroy();
13188
13322
  clearContentControlHighlight();
13189
13323
  disposed = true;
13324
+ completedFallbackSearch = null;
13190
13325
  if (searchRefreshTimer !== null) clearTimeout(searchRefreshTimer);
13191
13326
  searchRefreshTimer = null;
13192
13327
  cancelRefreshShellSearch();
@@ -13262,6 +13397,7 @@ function createSuperDocUI(options) {
13262
13397
  comments,
13263
13398
  trackChanges,
13264
13399
  contentControls,
13400
+ bookmarks,
13265
13401
  fonts,
13266
13402
  zoom,
13267
13403
  document: documentHandle,
@@ -11274,6 +11274,113 @@ function createSuperDocUI(options) {
11274
11274
  };
11275
11275
  }
11276
11276
  };
11277
+ let bookmarkNavigationGeneration = 0;
11278
+ documentResetHooks.push(() => {
11279
+ bookmarkNavigationGeneration += 1;
11280
+ });
11281
+ const bookmarks = { navigateTo: async (input, options) => {
11282
+ const generation = ++bookmarkNavigationGeneration;
11283
+ const unresolved = () => ({
11284
+ success: false,
11285
+ ok: false,
11286
+ reason: SUPERDOC_UI_REASONS.targetUnresolved
11287
+ });
11288
+ const address = typeof input === "string" ? {
11289
+ kind: "entity",
11290
+ entityType: "bookmark",
11291
+ name: input
11292
+ } : input;
11293
+ if (!address || address.kind !== "entity" || address.entityType !== "bookmark" || typeof address.name !== "string" || address.name.length === 0 || address.story !== void 0 && (!address.story || address.story.kind !== "story" || address.story.storyType !== "body")) return unresolved();
11294
+ const editor = getEditor();
11295
+ if (!editor || disposed) return {
11296
+ success: false,
11297
+ ok: false,
11298
+ reason: SUPERDOC_UI_REASONS.notReady
11299
+ };
11300
+ const doc = getDoc();
11301
+ const api = doc?.bookmarks;
11302
+ if (typeof api?.list !== "function" || typeof api?.get !== "function") return {
11303
+ success: false,
11304
+ ok: false,
11305
+ reason: SUPERDOC_UI_REASONS.operationUnavailable
11306
+ };
11307
+ const helper = getSelectionApplyHelper();
11308
+ if (!("helper" in helper)) return {
11309
+ success: false,
11310
+ ok: false,
11311
+ reason: helper.reason
11312
+ };
11313
+ const host = getHost();
11314
+ if (typeof host?.scrollTargetIntoView !== "function") return {
11315
+ success: false,
11316
+ ok: false,
11317
+ reason: SUPERDOC_UI_REASONS.hostCapabilityUnavailable
11318
+ };
11319
+ const token = metadataResolutionToken();
11320
+ let active = true;
11321
+ const current = () => active && !disposed && generation === bookmarkNavigationGeneration && getEditor() === editor && getDoc() === doc && getHost() === host && metadataResolutionToken() === token;
11322
+ const run = async () => {
11323
+ try {
11324
+ const directory = await api.list.call(api, address.story ? { in: address.story } : {});
11325
+ if (!current()) return unresolved();
11326
+ if ((Array.isArray(directory?.items) ? directory.items : []).filter((item) => item?.name === address.name).length !== 1) return unresolved();
11327
+ const info = await api.get.call(api, { target: address });
11328
+ if (!current()) return unresolved();
11329
+ const from = info?.range?.from;
11330
+ const to = info?.range?.to;
11331
+ const pointValid = (point) => typeof point?.blockId === "string" && point.blockId.length > 0 && Number.isInteger(point.offset) && point.offset >= 0;
11332
+ if (info?.name !== address.name || info?.address?.story && info.address.story.storyType !== "body" || info?.tableColumn || !pointValid(from) || !pointValid(to) || from.blockId === to.blockId && from.offset > to.offset) return unresolved();
11333
+ const result = await scrollTargetIntoView({
11334
+ kind: "text",
11335
+ segments: [{
11336
+ blockId: from.blockId,
11337
+ range: {
11338
+ start: from.offset,
11339
+ end: from.offset
11340
+ }
11341
+ }]
11342
+ }, {
11343
+ block: options?.block ?? "center",
11344
+ behavior: options?.behavior ?? "instant",
11345
+ shouldContinue: current
11346
+ });
11347
+ if (!current()) return unresolved();
11348
+ if (!result.ok) return result;
11349
+ const applied = selection.apply({
11350
+ kind: "selection",
11351
+ start: {
11352
+ kind: "text",
11353
+ blockId: from.blockId,
11354
+ offset: from.offset
11355
+ },
11356
+ end: {
11357
+ kind: "text",
11358
+ blockId: to.blockId,
11359
+ offset: to.offset
11360
+ }
11361
+ });
11362
+ return {
11363
+ ...applied,
11364
+ success: applied.ok
11365
+ };
11366
+ } catch {
11367
+ return unresolved();
11368
+ }
11369
+ };
11370
+ let timer;
11371
+ const timeout = new Promise((resolve) => {
11372
+ timer = setTimeout(() => {
11373
+ active = false;
11374
+ resolve(unresolved());
11375
+ }, 15e3);
11376
+ });
11377
+ try {
11378
+ return await Promise.race([run(), timeout]);
11379
+ } finally {
11380
+ active = false;
11381
+ clearTimeout(timer);
11382
+ }
11383
+ } };
11277
11384
  /**
11278
11385
  * Derive the browser-shell's `importedId` carrier-lookup alias from a
11279
11386
  * public list row's `wordRevisionIds` / `sourceIds` provenance fields,
@@ -12507,6 +12614,7 @@ function createSuperDocUI(options) {
12507
12614
  };
12508
12615
  let searchRequestGeneration = 0;
12509
12616
  let pendingFallbackGeneration = null;
12617
+ let completedFallbackSearch = null;
12510
12618
  /**
12511
12619
  * How to tear down the session that is currently open, captured when it was
12512
12620
  * opened.
@@ -12555,6 +12663,7 @@ function createSuperDocUI(options) {
12555
12663
  * is deliberately not made here.
12556
12664
  */
12557
12665
  const resetSearchForActiveEditorChange = () => {
12666
+ completedFallbackSearch = null;
12558
12667
  searchRequestGeneration += 1;
12559
12668
  pendingFallbackGeneration = null;
12560
12669
  shellFallbackOutstanding = false;
@@ -12608,8 +12717,8 @@ function createSuperDocUI(options) {
12608
12717
  const editSearch = getEditCommandSearch();
12609
12718
  if (editSearch && typeof editSearch.query === "function") safeCall(() => editSearch.query({ query: "" }), null);
12610
12719
  };
12611
- const readEditCommandCanReplace = () => readEditCommandStateEntry("find.replace")?.enabled === true;
12612
- const readEditCommandCanReplaceAll = () => readEditCommandStateEntry("find.replaceAll")?.enabled === true;
12720
+ const readEditCommandCanReplace = () => pendingFallbackGeneration !== searchRequestGeneration && readEditCommandStateEntry("find.replace")?.enabled === true;
12721
+ const readEditCommandCanReplaceAll = () => pendingFallbackGeneration !== searchRequestGeneration && readEditCommandStateEntry("find.replaceAll")?.enabled === true;
12613
12722
  const applyHostSearchResult = (result, fallbackCanReplace = false, fallbackCanReplaceAll = fallbackCanReplace) => {
12614
12723
  const snapshot = normalizeHostSearchResult(result, fallbackCanReplace, fallbackCanReplaceAll);
12615
12724
  if (!snapshot) {
@@ -12770,6 +12879,7 @@ function createSuperDocUI(options) {
12770
12879
  return { ok: true };
12771
12880
  },
12772
12881
  close: () => {
12882
+ completedFallbackSearch = null;
12773
12883
  searchRequestGeneration += 1;
12774
12884
  cancelRefreshShellSearch();
12775
12885
  if (releaseActiveSearchSession) releaseSearchSession();
@@ -12794,6 +12904,16 @@ function createSuperDocUI(options) {
12794
12904
  const caseSensitive = Boolean(options?.caseSensitive);
12795
12905
  const includeDeletedText = (options?.includeTrackedDeletions ?? options?.includeDeletedText) === true;
12796
12906
  const regex = options?.regex === true;
12907
+ if ((!host || typeof host.setSession !== "function") && completedFallbackSearch !== null && completedFallbackSearch === getEditCommandSearch() && pendingFallbackGeneration !== searchRequestGeneration && searchRefreshTimer === null && searchState.open && searchState.available && !searchState.reason && searchState.query === query && searchState.caseSensitive === caseSensitive && searchState.includeDeletedText === includeDeletedText && searchState.regex === regex) {
12908
+ syncSearchStateFromHost();
12909
+ emitSearch();
12910
+ return searchState;
12911
+ }
12912
+ completedFallbackSearch = null;
12913
+ if (searchRefreshTimer !== null) {
12914
+ clearTimeout(searchRefreshTimer);
12915
+ searchRefreshTimer = null;
12916
+ }
12797
12917
  const generation = ++searchRequestGeneration;
12798
12918
  cancelRefreshShellSearch();
12799
12919
  cancelOutstandingShellFallback();
@@ -12829,6 +12949,7 @@ function createSuperDocUI(options) {
12829
12949
  if (generation !== searchRequestGeneration) return;
12830
12950
  shellFallbackOutstanding = false;
12831
12951
  pendingFallbackGeneration = null;
12952
+ completedFallbackSearch = editSearch;
12832
12953
  applyHostSearchResult(resolved, readEditCommandCanReplace(), readEditCommandCanReplaceAll());
12833
12954
  }, () => {
12834
12955
  if (generation !== searchRequestGeneration) return;
@@ -12841,6 +12962,7 @@ function createSuperDocUI(options) {
12841
12962
  return searchState;
12842
12963
  }
12843
12964
  pendingFallbackGeneration = null;
12965
+ completedFallbackSearch = editSearch;
12844
12966
  applyHostSearchResult(result, readEditCommandCanReplace(), readEditCommandCanReplaceAll());
12845
12967
  applyHostSearchResult(safeCall(() => editSearch.getState(), null), readEditCommandCanReplace(), readEditCommandCanReplaceAll());
12846
12968
  return searchState;
@@ -12983,6 +13105,7 @@ function createSuperDocUI(options) {
12983
13105
  return { ok: true };
12984
13106
  },
12985
13107
  clear: () => {
13108
+ completedFallbackSearch = null;
12986
13109
  searchRequestGeneration += 1;
12987
13110
  cancelRefreshShellSearch();
12988
13111
  cancelOutstandingShellFallback();
@@ -13098,6 +13221,7 @@ function createSuperDocUI(options) {
13098
13221
  };
13099
13222
  let searchRefreshTimer = null;
13100
13223
  scheduleActiveSearchRefresh = () => {
13224
+ completedFallbackSearch = null;
13101
13225
  if (searchRefreshTimer !== null) clearTimeout(searchRefreshTimer);
13102
13226
  if (!searchState.open || !searchState.query) return;
13103
13227
  const scheduledGeneration = searchRequestGeneration;
@@ -13124,19 +13248,29 @@ function createSuperDocUI(options) {
13124
13248
  const result = typeof editSearch?.refresh === "function" ? safeCall(() => editSearch.refresh(input), null) : safeCall(() => host.refresh(), null);
13125
13249
  if (isPromiseLike(result)) {
13126
13250
  pendingFallbackGeneration = generation;
13251
+ setSearchState({
13252
+ canReplace: false,
13253
+ canReplaceAll: false
13254
+ });
13127
13255
  Promise.resolve(result).then((resolved) => {
13128
13256
  if (generation !== searchRequestGeneration || disposed) return;
13257
+ releaseRefreshShellSearch = null;
13129
13258
  pendingFallbackGeneration = null;
13259
+ completedFallbackSearch = editSearch;
13130
13260
  applyHostSearchResult(resolved, readEditCommandCanReplace(), readEditCommandCanReplaceAll());
13131
13261
  }, () => {
13132
13262
  if (generation !== searchRequestGeneration || disposed) return;
13133
- pendingFallbackGeneration = null;
13263
+ releaseRefreshShellSearch = null;
13134
13264
  setSearchState({
13135
13265
  available: false,
13136
13266
  reason: SUPERDOC_UI_REASONS.searchUnavailable
13137
13267
  });
13138
13268
  });
13139
- } else if (result !== null) applyHostSearchResult(result);
13269
+ } else if (result !== null) {
13270
+ releaseRefreshShellSearch = null;
13271
+ completedFallbackSearch = editSearch;
13272
+ applyHostSearchResult(result);
13273
+ }
13140
13274
  }, 120);
13141
13275
  };
13142
13276
  const stylesSub = sliceHandle((s) => s.styles);
@@ -13186,6 +13320,7 @@ function createSuperDocUI(options) {
13186
13320
  watermark.destroy();
13187
13321
  clearContentControlHighlight();
13188
13322
  disposed = true;
13323
+ completedFallbackSearch = null;
13189
13324
  if (searchRefreshTimer !== null) clearTimeout(searchRefreshTimer);
13190
13325
  searchRefreshTimer = null;
13191
13326
  cancelRefreshShellSearch();
@@ -13261,6 +13396,7 @@ function createSuperDocUI(options) {
13261
13396
  comments,
13262
13397
  trackChanges,
13263
13398
  contentControls,
13399
+ bookmarks,
13264
13400
  fonts,
13265
13401
  zoom,
13266
13402
  document: documentHandle,
@@ -19,7 +19,7 @@ var COLLABORATION_UPGRADE_ENGINE_MINIMUM_NODE_MAJOR = 20;
19
19
  var PRIVATE_ENGINE_INFO = (0, _superdoc_docx_engine_collaboration_upgrade_engine.getCollaborationUpgradeEngineInfo)();
20
20
  var ENGINE_INFO = Object.freeze({
21
21
  ...PRIVATE_ENGINE_INFO,
22
- superdocVersion: "2.20.0-next.9",
22
+ superdocVersion: "2.20.0",
23
23
  roomSchemaVersion: Object.freeze({ ...PRIVATE_ENGINE_INFO.roomSchemaVersion }),
24
24
  supportedBundleVersions: SUPPORTED_COLLABORATION_UPGRADE_BUNDLE_VERSIONS,
25
25
  supportedV1ReaderContractVersions: SUPPORTED_V1_READER_CONTRACT_VERSIONS
@@ -18,7 +18,7 @@ var COLLABORATION_UPGRADE_ENGINE_MINIMUM_NODE_MAJOR = 20;
18
18
  var PRIVATE_ENGINE_INFO = getCollaborationUpgradeEngineInfo$1();
19
19
  var ENGINE_INFO = Object.freeze({
20
20
  ...PRIVATE_ENGINE_INFO,
21
- superdocVersion: "2.20.0-next.9",
21
+ superdocVersion: "2.20.0",
22
22
  roomSchemaVersion: Object.freeze({ ...PRIVATE_ENGINE_INFO.roomSchemaVersion }),
23
23
  supportedBundleVersions: SUPPORTED_COLLABORATION_UPGRADE_BUNDLE_VERSIONS,
24
24
  supportedV1ReaderContractVersions: SUPPORTED_V1_READER_CONTRACT_VERSIONS
@@ -3649,7 +3649,7 @@ export declare const OPERATION_DEFINITIONS: {
3649
3649
  readonly find: {
3650
3650
  readonly memberPath: "find";
3651
3651
  readonly description: "Search the document for text or node matches using SDM/1 selectors. Returns discovery-grade results: for mutation targeting, use query.match instead.";
3652
- readonly expectedResult: "Returns an SDFindResult envelope ({ total, limit, offset, items }). Each item is an SDNodeResult ({ node, address }).";
3652
+ readonly expectedResult: "Returns an SDFindResult envelope ({ total, limit, offset, items }). Each item is an SDNodeResult ({ node, address }). Text hits contain only the matched text; use getNode or getNodeById for the full containing node. Node selectors return full node projections.";
3653
3653
  readonly requiresDocumentContext: true;
3654
3654
  readonly metadata: CommandStaticMetadata;
3655
3655
  readonly referenceDocPath: "find.mdx";
@@ -452,6 +452,7 @@ export type TabRun = RunMarks & {
452
452
  };
453
453
  export type LineBreakRun = {
454
454
  kind: 'lineBreak';
455
+ trackedChange?: TrackedChangeMeta;
455
456
  /**
456
457
  * Optional attributes carried through from the source document.
457
458
  * Mirrors OOXML <w:br> attributes (type/clear) to preserve fidelity.
@@ -13,7 +13,6 @@ export type { BoundaryRange } from './text-boundaries.js';
13
13
  export { buildSectionAwareHeaderFooterLayoutKey, buildSectionContentWidth, buildEffectiveHeaderFooterRefsBySection, collectReferencedHeaderFooterRIds, buildSectionAwareHeaderFooterMeasurementGroups, } from './sectionAwareHeaderFooter.js';
14
14
  export type { HeaderFooterSectionKind, HeaderFooterRefs, SectionAwareHeaderFooterMeasurementGroup, } from './sectionAwareHeaderFooter.js';
15
15
  export { clearIncrementalModuleState, incrementalLayout, measureCache, normalizeMargin } from './incrementalLayout.js';
16
- export { hydrateTableTextboxMeasures } from './hydrateTableTextboxMeasures.js';
17
16
  export { hashMeasureContent } from './cache.js';
18
17
  export type { HeaderFooterLayoutResult, HeaderFooterGeometrySeed, IncrementalLayoutBridgeTiming, IncrementalLayoutResult, IncrementalLayoutExecutionControl, IncrementalMeasureReuseProof, IncrementalLayoutReuseOptions, IncrementalLayoutReuseSummary, IncrementalSectionPageNumberTransform, IncrementalLayoutTailAdoption, IncrementalLayoutTailDisposition, IncrementalPaginationProof, LayoutPositionTransform, FootnoteReserveSeed, } from './incrementalLayout.js';
19
18
  export { collectFootnoteLedgers, getPreferredReserveCandidates, getPreferredReserveTrialTargets, isMandatoryOnlyFootnotePage, scoreFootnoteWindow, summarizeFootnoteWindow, } from './footnote-scorer.js';
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_create_super_doc_ui = require("../chunks/create-super-doc-ui-Dc2QEczb.cjs");
2
+ const require_create_super_doc_ui = require("../chunks/create-super-doc-ui-BQBQsjoe.cjs");
3
3
  const require_slice_source = require("../chunks/slice-source-CFaqq0Vo.cjs");
4
4
  let react = require("react");
5
5
  //#region src/public/ui/react.ts
@@ -1,4 +1,4 @@
1
- import { t as createSuperDocUI } from "../chunks/create-super-doc-ui-B8olYVBF.es.js";
1
+ import { t as createSuperDocUI } from "../chunks/create-super-doc-ui-D5VrP4Dx.es.js";
2
2
  import { t as toSliceSource } from "../chunks/slice-source-gfOhG2MW.es.js";
3
3
  import { createContext, createElement, useCallback, useContext, useEffect, useRef, useState } from "react";
4
4
  //#region src/public/ui/react.ts
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_create_super_doc_ui = require("../chunks/create-super-doc-ui-Dc2QEczb.cjs");
2
+ const require_create_super_doc_ui = require("../chunks/create-super-doc-ui-BQBQsjoe.cjs");
3
3
  const require_slice_source = require("../chunks/slice-source-CFaqq0Vo.cjs");
4
4
  let superdoc_entry_vue = require("vue");
5
5
  //#region src/public/ui/vue.ts
@@ -1,4 +1,4 @@
1
- import { t as createSuperDocUI } from "../chunks/create-super-doc-ui-B8olYVBF.es.js";
1
+ import { t as createSuperDocUI } from "../chunks/create-super-doc-ui-D5VrP4Dx.es.js";
2
2
  import { t as toSliceSource } from "../chunks/slice-source-gfOhG2MW.es.js";
3
3
  import { computed, inject, onScopeDispose, provide, shallowRef, toRaw, toValue, watch } from "vue";
4
4
  //#region src/public/ui/vue.ts
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_create_super_doc_ui = require("../chunks/create-super-doc-ui-Dc2QEczb.cjs");
2
+ const require_create_super_doc_ui = require("../chunks/create-super-doc-ui-BQBQsjoe.cjs");
3
3
  exports.BUILT_IN_COMMAND_IDS = require_create_super_doc_ui.BUILT_IN_COMMAND_IDS;
4
4
  exports.createSuperDocUI = require_create_super_doc_ui.createSuperDocUI;
5
5
  exports.shallowEqual = require_create_super_doc_ui.shallowEqual;
@@ -1,2 +1,2 @@
1
- import { a as BUILT_IN_COMMAND_IDS, t as createSuperDocUI, u as shallowEqual } from "../chunks/create-super-doc-ui-B8olYVBF.es.js";
1
+ import { a as BUILT_IN_COMMAND_IDS, t as createSuperDocUI, u as shallowEqual } from "../chunks/create-super-doc-ui-D5VrP4Dx.es.js";
2
2
  export { BUILT_IN_COMMAND_IDS, createSuperDocUI, shallowEqual };