superdoc 2.7.0-next.2 → 2.7.0-next.4

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.
@@ -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.7.0-next.2",
22
+ superdocVersion: "2.7.0-next.4",
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.7.0-next.2",
21
+ superdocVersion: "2.7.0-next.4",
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
@@ -108,8 +108,11 @@ export interface ScrollIntoViewInput {
108
108
  target: TextAddress | TextTarget | EntityAddress;
109
109
  /** Alignment within the viewport. Defaults to `'center'`. */
110
110
  block?: 'start' | 'center' | 'end' | 'nearest';
111
- /** Scroll behavior. Defaults to `'smooth'`. */
112
- behavior?: 'auto' | 'smooth';
111
+ /**
112
+ * Scroll behavior. Defaults to `'smooth'`. `'auto'` follows the computed
113
+ * CSS `scroll-behavior`; `'instant'` always moves without animation.
114
+ */
115
+ behavior?: 'auto' | 'instant' | 'smooth';
113
116
  }
114
117
  /**
115
118
  * Result of `ui.viewport.scrollIntoView`. `success: false` when the
@@ -127,10 +127,9 @@ export type HyperlinkGetResult = {
127
127
  };
128
128
  /**
129
129
  * Result of `doc.bookmarks.get()` as read here (Document API `BookmarkInfo`).
130
- * `range.from.blockId` is the bookmark's `w14:paraId`, used to find/reveal
131
- * the target paragraph. `address.story` is populated for non-body
132
- * bookmarks (omitted for body) used to scope this fix to body targets,
133
- * since `revealBodyTarget`/`pageIndexForBodyTarget` are body-only.
130
+ * `range.from.blockId` identifies the model paragraph sent to the viewport.
131
+ * `address.story` is populated for non-body bookmarks (omitted for body),
132
+ * which keeps anchor navigation scoped to body targets.
134
133
  */
135
134
  export type BookmarkGetResult = {
136
135
  address?: {
@@ -184,16 +183,6 @@ export type LinkPopoverDocumentApi = {
184
183
  }) => BookmarkGetResult | Promise<BookmarkGetResult | null | undefined> | null | undefined;
185
184
  } | null | undefined;
186
185
  };
187
- /**
188
- * Result of `activeEditor.pageMetrics.revealBodyTarget()` as read here
189
- * (`V2EditorHostRevealBodyTargetResult`). Only available in editing/
190
- * suggesting mode — the host rejects with `editing-not-mounted` in
191
- * viewing mode.
192
- */
193
- export type RevealBodyTargetResult = {
194
- status?: "rejected" | "revealed" | undefined;
195
- reason?: string | undefined;
196
- };
197
186
  /**
198
187
  * The `v2-link-click` payload the shell relays from the v2 host's
199
188
  * `onLinkClick` callback.
package/dist/superdoc.cjs CHANGED
@@ -18195,10 +18195,9 @@ function scrollToElement(targetElement, options = {
18195
18195
  */
18196
18196
  /**
18197
18197
  * Result of `doc.bookmarks.get()` as read here (Document API `BookmarkInfo`).
18198
- * `range.from.blockId` is the bookmark's `w14:paraId`, used to find/reveal
18199
- * the target paragraph. `address.story` is populated for non-body
18200
- * bookmarks (omitted for body) used to scope this fix to body targets,
18201
- * since `revealBodyTarget`/`pageIndexForBodyTarget` are body-only.
18198
+ * `range.from.blockId` identifies the model paragraph sent to the viewport.
18199
+ * `address.story` is populated for non-body bookmarks (omitted for body),
18200
+ * which keeps anchor navigation scoped to body targets.
18202
18201
  * @typedef {Object} BookmarkGetResult
18203
18202
  * @property {{ story?: { kind?: string, storyType?: string } }} [address]
18204
18203
  * @property {{ from?: { blockId?: string } }} [range]
@@ -18217,15 +18216,6 @@ function scrollToElement(targetElement, options = {
18217
18216
  * @property {{ get?: (input: { target: { kind: 'entity', entityType: 'bookmark', name: string } }) => BookmarkGetResult | Promise<BookmarkGetResult | null | undefined> | null | undefined } | null} [bookmarks]
18218
18217
  */
18219
18218
  /**
18220
- * Result of `activeEditor.pageMetrics.revealBodyTarget()` as read here
18221
- * (`V2EditorHostRevealBodyTargetResult`). Only available in editing/
18222
- * suggesting mode — the host rejects with `editing-not-mounted` in
18223
- * viewing mode.
18224
- * @typedef {Object} RevealBodyTargetResult
18225
- * @property {'revealed' | 'rejected'} [status]
18226
- * @property {string} [reason]
18227
- */
18228
- /**
18229
18219
  * The `v2-link-click` payload the shell relays from the v2 host's
18230
18220
  * `onLinkClick` callback.
18231
18221
  * @typedef {Object} LinkClickPayload
@@ -18567,20 +18557,8 @@ function useLinkPopover({ getSurfaceManager, getActiveEditor, getUi, getResolver
18567
18557
  handle.result?.then?.((outcome) => clearCurrentPopover(handle, outcome), () => clearCurrentPopover(handle));
18568
18558
  }
18569
18559
  /**
18570
- * Look up a real `<a name>` anchor, or a bookmark-marker span, in the
18571
- * currently mounted DOM. The marker span
18572
- * (`data-bookmark-marker`/`data-bookmark-name`) is a best-effort fast
18573
- * path only: layout-engine measuring drops those zero-width marker runs
18574
- * from paint whenever the wrapping paragraph has other visible content
18575
- * (the standard shape for a TOC bookmark wrapping heading text,
18576
- * `<w:bookmarkStart .../><w:r>Heading</w:r><w:bookmarkEnd/>`) — but it
18577
- * DOES survive for a standalone/empty bookmark paragraph (a supported v2
18578
- * shape, see `v2-layout-adapter/src/empty-paragraph.test.ts` and
18579
- * `isSyntheticStandaloneBookmarkParagraph` in
18580
- * `v2-host/src/compose-exact-complete-render.ts`), where it lets a
18581
- * mounted bookmark scroll immediately without a Document API round trip.
18582
- * `findPaintedParagraphByParaId` remains the reliable fallback for the
18583
- * common (marker-not-painted) case.
18560
+ * Find a mounted named anchor for runtimes that do not expose the bookmark
18561
+ * model. Model-backed runtimes never use DOM identity for navigation.
18584
18562
  * @param {HTMLElement | null} container
18585
18563
  * @param {string} escapedAnchorName
18586
18564
  * @returns {HTMLElement | null}
@@ -18589,44 +18567,6 @@ function useLinkPopover({ getSurfaceManager, getActiveEditor, getUi, getResolver
18589
18567
  return container?.querySelector?.(`a[name='${escapedAnchorName}'], [data-bookmark-marker='start'][data-bookmark-name='${escapedAnchorName}']`) ?? null;
18590
18568
  }
18591
18569
  /**
18592
- * Find the painted paragraph element for a `w14:paraId`, mirroring the
18593
- * attribute chain `paintedParaIdElement` uses in v2-host
18594
- * (`create-v2-editor-host.ts`) to confirm a paragraph is painted.
18595
- * @param {HTMLElement | null} container
18596
- * @param {string} paraId
18597
- * @returns {HTMLElement | null}
18598
- */
18599
- function findPaintedParagraphByParaId(container, paraId) {
18600
- if (!container) return null;
18601
- const escaped = cssEscape(paraId);
18602
- return container.querySelector(`[data-source-node-id='${escaped}']`) ?? container.querySelector(`[data-layout-paraid='${escaped}']`) ?? container.querySelector(`[data-block-id$='/${escaped}']`) ?? container.querySelector(`[data-layout-block-ref$='/${escaped}']`) ?? null;
18603
- }
18604
- /**
18605
- * Poll for an element across a bounded number of animation frames. Used
18606
- * after `scrollToPage()` in review/viewing mode, where — unlike
18607
- * `revealBodyTarget` in editing mode — there is no promise that resolves
18608
- * once the deep-jumped page's geometry reconciles and paints.
18609
- * @param {() => HTMLElement | null} lookup
18610
- * @param {number} maxTicks
18611
- * @returns {Promise<HTMLElement | null>}
18612
- */
18613
- function pollForElement(lookup, maxTicks = 30) {
18614
- return new Promise((resolve) => {
18615
- const raf = typeof window !== "undefined" ? window.requestAnimationFrame : null;
18616
- let ticks = 0;
18617
- const tick = () => {
18618
- const found = lookup();
18619
- if (found || ticks >= maxTicks || typeof raf !== "function") {
18620
- resolve(found ?? null);
18621
- return;
18622
- }
18623
- ticks += 1;
18624
- raf(tick);
18625
- };
18626
- tick();
18627
- });
18628
- }
18629
- /**
18630
18570
  * Move the editable caret to the start of a resolved target paragraph after
18631
18571
  * navigating to it. Mirrors V1's `goToAnchor`, whose final step places the
18632
18572
  * caret at the bookmark position: without it a V2 TOC click only scrolls, so
@@ -18666,87 +18606,59 @@ function useLinkPopover({ getSurfaceManager, getActiveEditor, getUi, getResolver
18666
18606
  } catch {}
18667
18607
  }
18668
18608
  /**
18669
- * Navigate to an internal `#anchor` link (TOC entries, bookmark links).
18670
- * Tries a real `<a name>` anchor in the currently mounted DOM first, then
18671
- * resolves the bookmark's target paragraph through the Document API and
18672
- * scrolls to that paragraph by its `paraId` — not to a bookmark-marker
18673
- * element, which layout-engine measuring may have excluded from paint.
18674
- *
18675
- * In editing/suggesting mode (`placeCaret`), also moves the caret to the
18676
- * start of the resolved target paragraph, matching V1's `goToAnchor`.
18677
- *
18678
- * If the paragraph isn't mounted (the v2 layout engine only mounts a
18679
- * sliding window of pages), asks the host to reveal it:
18680
- * - In editing/suggesting mode, `pageMetrics.revealBodyTarget` deep-jumps
18681
- * into unmounted pages and resolves once the target is painted. Any
18682
- * rejection here OTHER than `editing-not-mounted` is a real failure of
18683
- * this strong, paint-confirmed path and is left as a no-op rather than
18684
- * silently degrading to the weaker path below.
18685
- * - In viewing mode the host is mounted in "review" mode, where
18686
- * `revealBodyTarget` always rejects with reason `editing-not-mounted`
18687
- * (or is simply unpublished); only then falls back to
18688
- * `pageIndexForBodyTarget` + `scrollToPage` (neither gated on mount
18689
- * mode) and polls for paint, since neither returns a paint-complete
18690
- * promise.
18691
- *
18692
- * Out of scope: bookmarks outside the body story (headers/footers/
18693
- * footnotes) — `revealBodyTarget`/`pageIndexForBodyTarget` are body-only.
18609
+ * Navigate to a body bookmark through its model address. A mounted-anchor
18610
+ * fallback is reserved for runtimes without a callable bookmark model.
18694
18611
  * @param {string | null | undefined} anchorUrl
18695
18612
  * @param {{ placeCaret?: boolean }} [options]
18696
18613
  */
18697
18614
  async function navigateToAnchor(anchorUrl, { placeCaret = false } = {}) {
18698
18615
  if (typeof anchorUrl !== "string" || !anchorUrl.startsWith("#")) return;
18699
18616
  const anchorName = anchorUrl.slice(1);
18700
- const escapedAnchorName = cssEscape(anchorName);
18701
- const mountedAnchor = findMountedAnchor(getUi()?.viewport?.getHost?.() ?? null, escapedAnchorName);
18702
- if (mountedAnchor) {
18703
- scrollToElement(mountedAnchor);
18704
- return;
18705
- }
18706
18617
  /** @type {LinkPopoverDocumentApi | null | undefined} */
18707
18618
  const doc = getActiveEditor()?.doc;
18619
+ const bookmarkGet = doc?.bookmarks?.get;
18620
+ if (typeof bookmarkGet !== "function") {
18621
+ const mountedAnchor = findMountedAnchor(getUi()?.viewport?.getHost?.() ?? null, cssEscape(anchorName));
18622
+ if (mountedAnchor) scrollToElement(mountedAnchor, {
18623
+ block: "start",
18624
+ behavior: "instant"
18625
+ });
18626
+ return;
18627
+ }
18708
18628
  /** @type {BookmarkGetResult | null} */
18709
18629
  let bookmark = null;
18710
18630
  try {
18711
- bookmark = await Promise.resolve(doc?.bookmarks?.get?.({ target: {
18631
+ bookmark = await Promise.resolve(bookmarkGet.call(doc.bookmarks, { target: {
18712
18632
  kind: "entity",
18713
18633
  entityType: "bookmark",
18714
18634
  name: anchorName
18715
18635
  } }));
18716
18636
  bookmark ??= null;
18717
18637
  } catch {
18718
- bookmark = null;
18638
+ return;
18719
18639
  }
18720
18640
  const story = bookmark?.address?.story;
18721
18641
  if (story && story.storyType !== "body") return;
18722
18642
  const blockId = bookmark?.range?.from?.blockId;
18723
18643
  if (typeof blockId !== "string") return;
18724
18644
  if (placeCaret) await placeCaretAtHeadingStart(blockId);
18725
- const mountedParagraph = findPaintedParagraphByParaId(getUi()?.viewport?.getHost?.() ?? null, blockId);
18726
- if (mountedParagraph) {
18727
- scrollToElement(mountedParagraph);
18728
- return;
18729
- }
18730
- const pageMetrics = getActiveEditor()?.pageMetrics;
18731
- const revealBodyTarget = pageMetrics?.revealBodyTarget;
18732
- let revealResult;
18645
+ const viewport = getUi()?.viewport;
18646
+ const scrollIntoView = viewport?.scrollIntoView;
18647
+ if (typeof scrollIntoView !== "function") return;
18733
18648
  try {
18734
- revealResult = await revealBodyTarget?.({ paraId: blockId });
18735
- } catch {
18736
- revealResult = void 0;
18737
- }
18738
- if (revealResult?.status === "revealed") {
18739
- const revealedParagraph = findPaintedParagraphByParaId(getUi()?.viewport?.getHost?.() ?? null, blockId);
18740
- if (revealedParagraph) scrollToElement(revealedParagraph);
18741
- return;
18742
- }
18743
- if (revealBodyTarget && revealResult?.reason !== "editing-not-mounted") return;
18744
- const pageIndexForBodyTarget = pageMetrics?.pageIndexForBodyTarget;
18745
- const scrollToPage = pageMetrics?.scrollToPage;
18746
- const pageIndex = pageIndexForBodyTarget?.({ paraId: blockId });
18747
- if (typeof pageIndex !== "number" || !scrollToPage?.(pageIndex)) return;
18748
- const revealedByScroll = await pollForElement(() => findPaintedParagraphByParaId(getUi()?.viewport?.getHost?.() ?? null, blockId));
18749
- if (revealedByScroll) scrollToElement(revealedByScroll);
18649
+ await Promise.resolve(scrollIntoView.call(viewport, {
18650
+ target: {
18651
+ kind: "text",
18652
+ blockId,
18653
+ range: {
18654
+ start: 0,
18655
+ end: 0
18656
+ }
18657
+ },
18658
+ block: "start",
18659
+ behavior: "instant"
18660
+ }));
18661
+ } catch {}
18750
18662
  }
18751
18663
  /**
18752
18664
  * @param {LinkPopoverContext} ctx
@@ -43606,7 +43518,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
43606
43518
  this.config.colors = shuffleArray(this.config.colors);
43607
43519
  this.userColorMap = /* @__PURE__ */ new Map();
43608
43520
  this.colorIndex = 0;
43609
- this.version = "2.7.0-next.2";
43521
+ this.version = "2.7.0-next.4";
43610
43522
  this.#log("🦋 [superdoc] Using SuperDoc version:", this.version);
43611
43523
  this.superdocId = config.superdocId || require_uuid.v4();
43612
43524
  this.colors = this.config.colors ?? [];
@@ -18163,10 +18163,9 @@ function scrollToElement(targetElement, options = {
18163
18163
  */
18164
18164
  /**
18165
18165
  * Result of `doc.bookmarks.get()` as read here (Document API `BookmarkInfo`).
18166
- * `range.from.blockId` is the bookmark's `w14:paraId`, used to find/reveal
18167
- * the target paragraph. `address.story` is populated for non-body
18168
- * bookmarks (omitted for body) used to scope this fix to body targets,
18169
- * since `revealBodyTarget`/`pageIndexForBodyTarget` are body-only.
18166
+ * `range.from.blockId` identifies the model paragraph sent to the viewport.
18167
+ * `address.story` is populated for non-body bookmarks (omitted for body),
18168
+ * which keeps anchor navigation scoped to body targets.
18170
18169
  * @typedef {Object} BookmarkGetResult
18171
18170
  * @property {{ story?: { kind?: string, storyType?: string } }} [address]
18172
18171
  * @property {{ from?: { blockId?: string } }} [range]
@@ -18185,15 +18184,6 @@ function scrollToElement(targetElement, options = {
18185
18184
  * @property {{ get?: (input: { target: { kind: 'entity', entityType: 'bookmark', name: string } }) => BookmarkGetResult | Promise<BookmarkGetResult | null | undefined> | null | undefined } | null} [bookmarks]
18186
18185
  */
18187
18186
  /**
18188
- * Result of `activeEditor.pageMetrics.revealBodyTarget()` as read here
18189
- * (`V2EditorHostRevealBodyTargetResult`). Only available in editing/
18190
- * suggesting mode — the host rejects with `editing-not-mounted` in
18191
- * viewing mode.
18192
- * @typedef {Object} RevealBodyTargetResult
18193
- * @property {'revealed' | 'rejected'} [status]
18194
- * @property {string} [reason]
18195
- */
18196
- /**
18197
18187
  * The `v2-link-click` payload the shell relays from the v2 host's
18198
18188
  * `onLinkClick` callback.
18199
18189
  * @typedef {Object} LinkClickPayload
@@ -18535,20 +18525,8 @@ function useLinkPopover({ getSurfaceManager, getActiveEditor, getUi, getResolver
18535
18525
  handle.result?.then?.((outcome) => clearCurrentPopover(handle, outcome), () => clearCurrentPopover(handle));
18536
18526
  }
18537
18527
  /**
18538
- * Look up a real `<a name>` anchor, or a bookmark-marker span, in the
18539
- * currently mounted DOM. The marker span
18540
- * (`data-bookmark-marker`/`data-bookmark-name`) is a best-effort fast
18541
- * path only: layout-engine measuring drops those zero-width marker runs
18542
- * from paint whenever the wrapping paragraph has other visible content
18543
- * (the standard shape for a TOC bookmark wrapping heading text,
18544
- * `<w:bookmarkStart .../><w:r>Heading</w:r><w:bookmarkEnd/>`) — but it
18545
- * DOES survive for a standalone/empty bookmark paragraph (a supported v2
18546
- * shape, see `v2-layout-adapter/src/empty-paragraph.test.ts` and
18547
- * `isSyntheticStandaloneBookmarkParagraph` in
18548
- * `v2-host/src/compose-exact-complete-render.ts`), where it lets a
18549
- * mounted bookmark scroll immediately without a Document API round trip.
18550
- * `findPaintedParagraphByParaId` remains the reliable fallback for the
18551
- * common (marker-not-painted) case.
18528
+ * Find a mounted named anchor for runtimes that do not expose the bookmark
18529
+ * model. Model-backed runtimes never use DOM identity for navigation.
18552
18530
  * @param {HTMLElement | null} container
18553
18531
  * @param {string} escapedAnchorName
18554
18532
  * @returns {HTMLElement | null}
@@ -18557,44 +18535,6 @@ function useLinkPopover({ getSurfaceManager, getActiveEditor, getUi, getResolver
18557
18535
  return container?.querySelector?.(`a[name='${escapedAnchorName}'], [data-bookmark-marker='start'][data-bookmark-name='${escapedAnchorName}']`) ?? null;
18558
18536
  }
18559
18537
  /**
18560
- * Find the painted paragraph element for a `w14:paraId`, mirroring the
18561
- * attribute chain `paintedParaIdElement` uses in v2-host
18562
- * (`create-v2-editor-host.ts`) to confirm a paragraph is painted.
18563
- * @param {HTMLElement | null} container
18564
- * @param {string} paraId
18565
- * @returns {HTMLElement | null}
18566
- */
18567
- function findPaintedParagraphByParaId(container, paraId) {
18568
- if (!container) return null;
18569
- const escaped = cssEscape(paraId);
18570
- return container.querySelector(`[data-source-node-id='${escaped}']`) ?? container.querySelector(`[data-layout-paraid='${escaped}']`) ?? container.querySelector(`[data-block-id$='/${escaped}']`) ?? container.querySelector(`[data-layout-block-ref$='/${escaped}']`) ?? null;
18571
- }
18572
- /**
18573
- * Poll for an element across a bounded number of animation frames. Used
18574
- * after `scrollToPage()` in review/viewing mode, where — unlike
18575
- * `revealBodyTarget` in editing mode — there is no promise that resolves
18576
- * once the deep-jumped page's geometry reconciles and paints.
18577
- * @param {() => HTMLElement | null} lookup
18578
- * @param {number} maxTicks
18579
- * @returns {Promise<HTMLElement | null>}
18580
- */
18581
- function pollForElement(lookup, maxTicks = 30) {
18582
- return new Promise((resolve) => {
18583
- const raf = typeof window !== "undefined" ? window.requestAnimationFrame : null;
18584
- let ticks = 0;
18585
- const tick = () => {
18586
- const found = lookup();
18587
- if (found || ticks >= maxTicks || typeof raf !== "function") {
18588
- resolve(found ?? null);
18589
- return;
18590
- }
18591
- ticks += 1;
18592
- raf(tick);
18593
- };
18594
- tick();
18595
- });
18596
- }
18597
- /**
18598
18538
  * Move the editable caret to the start of a resolved target paragraph after
18599
18539
  * navigating to it. Mirrors V1's `goToAnchor`, whose final step places the
18600
18540
  * caret at the bookmark position: without it a V2 TOC click only scrolls, so
@@ -18634,87 +18574,59 @@ function useLinkPopover({ getSurfaceManager, getActiveEditor, getUi, getResolver
18634
18574
  } catch {}
18635
18575
  }
18636
18576
  /**
18637
- * Navigate to an internal `#anchor` link (TOC entries, bookmark links).
18638
- * Tries a real `<a name>` anchor in the currently mounted DOM first, then
18639
- * resolves the bookmark's target paragraph through the Document API and
18640
- * scrolls to that paragraph by its `paraId` — not to a bookmark-marker
18641
- * element, which layout-engine measuring may have excluded from paint.
18642
- *
18643
- * In editing/suggesting mode (`placeCaret`), also moves the caret to the
18644
- * start of the resolved target paragraph, matching V1's `goToAnchor`.
18645
- *
18646
- * If the paragraph isn't mounted (the v2 layout engine only mounts a
18647
- * sliding window of pages), asks the host to reveal it:
18648
- * - In editing/suggesting mode, `pageMetrics.revealBodyTarget` deep-jumps
18649
- * into unmounted pages and resolves once the target is painted. Any
18650
- * rejection here OTHER than `editing-not-mounted` is a real failure of
18651
- * this strong, paint-confirmed path and is left as a no-op rather than
18652
- * silently degrading to the weaker path below.
18653
- * - In viewing mode the host is mounted in "review" mode, where
18654
- * `revealBodyTarget` always rejects with reason `editing-not-mounted`
18655
- * (or is simply unpublished); only then falls back to
18656
- * `pageIndexForBodyTarget` + `scrollToPage` (neither gated on mount
18657
- * mode) and polls for paint, since neither returns a paint-complete
18658
- * promise.
18659
- *
18660
- * Out of scope: bookmarks outside the body story (headers/footers/
18661
- * footnotes) — `revealBodyTarget`/`pageIndexForBodyTarget` are body-only.
18577
+ * Navigate to a body bookmark through its model address. A mounted-anchor
18578
+ * fallback is reserved for runtimes without a callable bookmark model.
18662
18579
  * @param {string | null | undefined} anchorUrl
18663
18580
  * @param {{ placeCaret?: boolean }} [options]
18664
18581
  */
18665
18582
  async function navigateToAnchor(anchorUrl, { placeCaret = false } = {}) {
18666
18583
  if (typeof anchorUrl !== "string" || !anchorUrl.startsWith("#")) return;
18667
18584
  const anchorName = anchorUrl.slice(1);
18668
- const escapedAnchorName = cssEscape(anchorName);
18669
- const mountedAnchor = findMountedAnchor(getUi()?.viewport?.getHost?.() ?? null, escapedAnchorName);
18670
- if (mountedAnchor) {
18671
- scrollToElement(mountedAnchor);
18672
- return;
18673
- }
18674
18585
  /** @type {LinkPopoverDocumentApi | null | undefined} */
18675
18586
  const doc = getActiveEditor()?.doc;
18587
+ const bookmarkGet = doc?.bookmarks?.get;
18588
+ if (typeof bookmarkGet !== "function") {
18589
+ const mountedAnchor = findMountedAnchor(getUi()?.viewport?.getHost?.() ?? null, cssEscape(anchorName));
18590
+ if (mountedAnchor) scrollToElement(mountedAnchor, {
18591
+ block: "start",
18592
+ behavior: "instant"
18593
+ });
18594
+ return;
18595
+ }
18676
18596
  /** @type {BookmarkGetResult | null} */
18677
18597
  let bookmark = null;
18678
18598
  try {
18679
- bookmark = await Promise.resolve(doc?.bookmarks?.get?.({ target: {
18599
+ bookmark = await Promise.resolve(bookmarkGet.call(doc.bookmarks, { target: {
18680
18600
  kind: "entity",
18681
18601
  entityType: "bookmark",
18682
18602
  name: anchorName
18683
18603
  } }));
18684
18604
  bookmark ??= null;
18685
18605
  } catch {
18686
- bookmark = null;
18606
+ return;
18687
18607
  }
18688
18608
  const story = bookmark?.address?.story;
18689
18609
  if (story && story.storyType !== "body") return;
18690
18610
  const blockId = bookmark?.range?.from?.blockId;
18691
18611
  if (typeof blockId !== "string") return;
18692
18612
  if (placeCaret) await placeCaretAtHeadingStart(blockId);
18693
- const mountedParagraph = findPaintedParagraphByParaId(getUi()?.viewport?.getHost?.() ?? null, blockId);
18694
- if (mountedParagraph) {
18695
- scrollToElement(mountedParagraph);
18696
- return;
18697
- }
18698
- const pageMetrics = getActiveEditor()?.pageMetrics;
18699
- const revealBodyTarget = pageMetrics?.revealBodyTarget;
18700
- let revealResult;
18613
+ const viewport = getUi()?.viewport;
18614
+ const scrollIntoView = viewport?.scrollIntoView;
18615
+ if (typeof scrollIntoView !== "function") return;
18701
18616
  try {
18702
- revealResult = await revealBodyTarget?.({ paraId: blockId });
18703
- } catch {
18704
- revealResult = void 0;
18705
- }
18706
- if (revealResult?.status === "revealed") {
18707
- const revealedParagraph = findPaintedParagraphByParaId(getUi()?.viewport?.getHost?.() ?? null, blockId);
18708
- if (revealedParagraph) scrollToElement(revealedParagraph);
18709
- return;
18710
- }
18711
- if (revealBodyTarget && revealResult?.reason !== "editing-not-mounted") return;
18712
- const pageIndexForBodyTarget = pageMetrics?.pageIndexForBodyTarget;
18713
- const scrollToPage = pageMetrics?.scrollToPage;
18714
- const pageIndex = pageIndexForBodyTarget?.({ paraId: blockId });
18715
- if (typeof pageIndex !== "number" || !scrollToPage?.(pageIndex)) return;
18716
- const revealedByScroll = await pollForElement(() => findPaintedParagraphByParaId(getUi()?.viewport?.getHost?.() ?? null, blockId));
18717
- if (revealedByScroll) scrollToElement(revealedByScroll);
18617
+ await Promise.resolve(scrollIntoView.call(viewport, {
18618
+ target: {
18619
+ kind: "text",
18620
+ blockId,
18621
+ range: {
18622
+ start: 0,
18623
+ end: 0
18624
+ }
18625
+ },
18626
+ block: "start",
18627
+ behavior: "instant"
18628
+ }));
18629
+ } catch {}
18718
18630
  }
18719
18631
  /**
18720
18632
  * @param {LinkPopoverContext} ctx
@@ -43539,7 +43451,7 @@ var SuperDoc = class extends import_eventemitter3.default {
43539
43451
  this.config.colors = shuffleArray(this.config.colors);
43540
43452
  this.userColorMap = /* @__PURE__ */ new Map();
43541
43453
  this.colorIndex = 0;
43542
- this.version = "2.7.0-next.2";
43454
+ this.version = "2.7.0-next.4";
43543
43455
  this.#log("🦋 [superdoc] Using SuperDoc version:", this.version);
43544
43456
  this.superdocId = config.superdocId || v4();
43545
43457
  this.colors = this.config.colors ?? [];