superdoc 2.27.0 → 2.28.0-next.10

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 (149) hide show
  1. package/dist/chunks/{create-super-doc-ui-DpVh2nQH.cjs → create-super-doc-ui-B4c55pFy.cjs} +575 -86
  2. package/dist/chunks/{create-super-doc-ui-cHUUipYM.es.js → create-super-doc-ui-C4Ka5Ph3.es.js} +570 -87
  3. package/dist/collaboration-upgrade-engine.cjs +1 -1
  4. package/dist/collaboration-upgrade-engine.es.js +1 -1
  5. package/dist/document-api/src/content-controls/content-controls.types.d.ts +11 -0
  6. package/dist/document-api/src/contract/command-catalog.d.ts +1 -1
  7. package/dist/document-api/src/contract/operation-definitions.d.ts +46 -46
  8. package/dist/document-api/src/contract/operation-registry.d.ts +4 -0
  9. package/dist/document-api/src/export/export.d.ts +3 -1
  10. package/dist/document-api/src/export/export.types.d.ts +8 -0
  11. package/dist/document-api/src/get-node/get-node.d.ts +10 -4
  12. package/dist/document-api/src/hyperlinks/hyperlinks.types.d.ts +2 -0
  13. package/dist/document-api/src/index.d.ts +5 -4
  14. package/dist/document-api/src/replace/replace.d.ts +4 -1
  15. package/dist/document-api/src/styles/apply.d.ts +26 -2
  16. package/dist/document-api/src/styles/index.d.ts +3 -3
  17. package/dist/document-api/src/styles/schema.d.ts +3 -1
  18. package/dist/document-api/src/styles/validation.d.ts +10 -9
  19. package/dist/document-api/src/types/mutation-plan.types.d.ts +13 -4
  20. package/dist/document-api/src/types/temporary-mutation.d.ts +5 -1
  21. package/dist/document-api/src/types/track-changes.types.d.ts +10 -2
  22. package/dist/layout-engine/contracts/src/index.d.ts +3 -1
  23. package/dist/layout-engine/contracts/src/resolved-layout.d.ts +1 -0
  24. package/dist/layout-engine/contracts/src/run-helpers.d.ts +7 -0
  25. package/dist/layout-engine/painters/dom/src/page-content.d.ts +1 -0
  26. package/dist/layout-engine/painters/dom/src/review-markers.d.ts +3 -0
  27. package/dist/layout-engine/painters/dom/src/table/renderTableCell.d.ts +1 -0
  28. package/dist/public/ui-react.cjs +1 -1
  29. package/dist/public/ui-react.es.js +1 -1
  30. package/dist/public/ui-vue.cjs +1 -1
  31. package/dist/public/ui-vue.es.js +1 -1
  32. package/dist/public/ui.cjs +1 -1
  33. package/dist/public/ui.es.js +1 -1
  34. package/dist/shared/common/list-marker-utils.d.ts +3 -0
  35. package/dist/style.css +1 -1
  36. package/dist/style.layered.css +1 -1
  37. package/dist/superdoc/src/core/SuperDoc.d.ts +2 -6
  38. package/dist/superdoc/src/core/collaboration/resolve-v2-collaboration-target.d.ts +2 -0
  39. package/dist/superdoc/src/core/config/normalize-ui-config.d.ts +1 -0
  40. package/dist/superdoc/src/core/editor-runtime/types.d.ts +1 -0
  41. package/dist/superdoc/src/core/editor-runtime/v2/v2-editor-runtime-adapter.d.ts +2 -2
  42. package/dist/superdoc/src/core/extensions/types.d.ts +14 -3
  43. package/dist/superdoc/src/core/helpers/resolve-docx-export.d.ts +6 -0
  44. package/dist/superdoc/src/core/types/index.d.ts +35 -1
  45. package/dist/superdoc/src/core/v2-integration/v2-integration.d.ts +0 -8
  46. package/dist/superdoc/src/internal/toolbar/built-in/LinkedStyle.vue.d.ts +3 -1
  47. package/dist/superdoc/src/internal/toolbar/built-in/color-dropdown-helpers.d.ts +0 -1
  48. package/dist/superdoc/src/internal/toolbar/built-in/constants.d.ts +0 -2
  49. package/dist/superdoc/src/internal/toolbar/built-in/toolbarTexts.d.ts +1 -0
  50. package/dist/superdoc/src/internal/toolbar/built-in-toolbar.d.ts +1 -0
  51. package/dist/superdoc/src/public/export-types.d.ts +3 -0
  52. package/dist/superdoc/src/public/ui/entity-at.d.ts +1 -0
  53. package/dist/superdoc/src/public/ui/format-painter-helpers.d.ts +0 -13
  54. package/dist/superdoc/src/public/ui/style-match-patch.d.ts +7 -0
  55. package/dist/superdoc/src/public/ui/types.d.ts +20 -2
  56. package/dist/superdoc/src/public/ui.d.cts +18 -0
  57. package/dist/superdoc/src/public/ui.d.ts +1 -1
  58. package/dist/superdoc/src/stores/comments-store.d.ts +3 -57
  59. package/dist/superdoc/src/stores/superdoc-store.d.ts +9 -171
  60. package/dist/superdoc.cjs +476 -216
  61. package/dist/superdoc.es.js +473 -215
  62. package/dist/word-layout/src/types.d.ts +1 -0
  63. package/dist-cdn/style.layered.css +1 -1
  64. package/dist-cdn/superdoc.min.css +1 -1
  65. package/dist-cdn/superdoc.min.js +42 -42
  66. package/package.json +2 -2
  67. package/dist/footnote-table-order.docx +0 -0
  68. package/dist/images/altText_add.svg +0 -3
  69. package/dist/images/altText_disclaimer.svg +0 -3
  70. package/dist/images/altText_done.svg +0 -3
  71. package/dist/images/altText_spinner.svg +0 -30
  72. package/dist/images/altText_warning.svg +0 -3
  73. package/dist/images/annotation-check.svg +0 -11
  74. package/dist/images/annotation-comment.svg +0 -16
  75. package/dist/images/annotation-help.svg +0 -26
  76. package/dist/images/annotation-insert.svg +0 -10
  77. package/dist/images/annotation-key.svg +0 -11
  78. package/dist/images/annotation-newparagraph.svg +0 -11
  79. package/dist/images/annotation-noicon.svg +0 -7
  80. package/dist/images/annotation-note.svg +0 -42
  81. package/dist/images/annotation-paperclip.svg +0 -6
  82. package/dist/images/annotation-paragraph.svg +0 -16
  83. package/dist/images/annotation-pushpin.svg +0 -7
  84. package/dist/images/checkmark.svg +0 -5
  85. package/dist/images/comment-actionsButton.svg +0 -3
  86. package/dist/images/comment-closeButton.svg +0 -3
  87. package/dist/images/comment-editButton.svg +0 -8
  88. package/dist/images/comment-popup-editButton.svg +0 -5
  89. package/dist/images/cursor-editorFreeHighlight.svg +0 -6
  90. package/dist/images/cursor-editorFreeText.svg +0 -3
  91. package/dist/images/cursor-editorInk.svg +0 -4
  92. package/dist/images/cursor-editorTextHighlight.svg +0 -8
  93. package/dist/images/editor-toolbar-delete.svg +0 -5
  94. package/dist/images/editor-toolbar-edit.svg +0 -3
  95. package/dist/images/findbarButton-next.svg +0 -3
  96. package/dist/images/findbarButton-previous.svg +0 -3
  97. package/dist/images/gv-toolbarButton-download.svg +0 -3
  98. package/dist/images/loading-icon.gif +0 -0
  99. package/dist/images/loading.svg +0 -1
  100. package/dist/images/messageBar_closingButton.svg +0 -3
  101. package/dist/images/messageBar_info.svg +0 -3
  102. package/dist/images/messageBar_warning.svg +0 -3
  103. package/dist/images/pages_closeButton.svg +0 -3
  104. package/dist/images/pages_selected.svg +0 -7
  105. package/dist/images/pages_viewArrow.svg +0 -3
  106. package/dist/images/pages_viewButton.svg +0 -3
  107. package/dist/images/secondaryToolbarButton-documentProperties.svg +0 -3
  108. package/dist/images/secondaryToolbarButton-firstPage.svg +0 -3
  109. package/dist/images/secondaryToolbarButton-handTool.svg +0 -3
  110. package/dist/images/secondaryToolbarButton-lastPage.svg +0 -3
  111. package/dist/images/secondaryToolbarButton-rotateCcw.svg +0 -3
  112. package/dist/images/secondaryToolbarButton-rotateCw.svg +0 -3
  113. package/dist/images/secondaryToolbarButton-scrollHorizontal.svg +0 -3
  114. package/dist/images/secondaryToolbarButton-scrollPage.svg +0 -3
  115. package/dist/images/secondaryToolbarButton-scrollVertical.svg +0 -3
  116. package/dist/images/secondaryToolbarButton-scrollWrapped.svg +0 -3
  117. package/dist/images/secondaryToolbarButton-selectTool.svg +0 -3
  118. package/dist/images/secondaryToolbarButton-spreadEven.svg +0 -3
  119. package/dist/images/secondaryToolbarButton-spreadNone.svg +0 -3
  120. package/dist/images/secondaryToolbarButton-spreadOdd.svg +0 -3
  121. package/dist/images/toolbarButton-bookmark.svg +0 -3
  122. package/dist/images/toolbarButton-currentOutlineItem.svg +0 -3
  123. package/dist/images/toolbarButton-download.svg +0 -4
  124. package/dist/images/toolbarButton-editorFreeText.svg +0 -5
  125. package/dist/images/toolbarButton-editorHighlight.svg +0 -6
  126. package/dist/images/toolbarButton-editorInk.svg +0 -4
  127. package/dist/images/toolbarButton-editorSignature.svg +0 -6
  128. package/dist/images/toolbarButton-editorStamp.svg +0 -8
  129. package/dist/images/toolbarButton-menuArrow.svg +0 -3
  130. package/dist/images/toolbarButton-openFile.svg +0 -3
  131. package/dist/images/toolbarButton-pageDown.svg +0 -3
  132. package/dist/images/toolbarButton-pageUp.svg +0 -3
  133. package/dist/images/toolbarButton-presentationMode.svg +0 -3
  134. package/dist/images/toolbarButton-print.svg +0 -3
  135. package/dist/images/toolbarButton-search.svg +0 -3
  136. package/dist/images/toolbarButton-secondaryToolbarToggle.svg +0 -3
  137. package/dist/images/toolbarButton-viewAttachments.svg +0 -3
  138. package/dist/images/toolbarButton-viewLayers.svg +0 -3
  139. package/dist/images/toolbarButton-viewOutline.svg +0 -3
  140. package/dist/images/toolbarButton-viewThumbnail.svg +0 -3
  141. package/dist/images/toolbarButton-viewsManagerToggle.svg +0 -3
  142. package/dist/images/toolbarButton-zoomIn.svg +0 -3
  143. package/dist/images/toolbarButton-zoomOut.svg +0 -3
  144. package/dist/images/treeitem-collapsed.svg +0 -1
  145. package/dist/images/treeitem-expanded.svg +0 -1
  146. package/dist/superdoc/src/helpers/floor.d.ts +0 -1
  147. package/dist/superdoc/src/index.d.cts +0 -18
  148. package/dist/superdoc/src/index.d.ts +0 -14
  149. package/dist-cdn/footnote-table-order.docx +0 -0
@@ -2459,6 +2459,69 @@ function createDialogWorkflow(editor, options, onSuccess) {
2459
2459
  };
2460
2460
  }
2461
2461
  //#endregion
2462
+ //#region src/public/ui/style-match-patch.ts
2463
+ var STYLE_MATCH_KEYS = [
2464
+ ["bold", "bold"],
2465
+ ["italic", "italic"],
2466
+ ["underline", "underline"],
2467
+ ["color", "color"],
2468
+ ["fontSize", "fontSize"],
2469
+ ["rFonts", "fontFamily"]
2470
+ ];
2471
+ var STYLE_MATCH_READ_KEYS = [...STYLE_MATCH_KEYS.map(([readKey]) => readKey), "rStyle"];
2472
+ function buildStyleMatchPatch(effective, direct, options) {
2473
+ const patch = {};
2474
+ const characterStyle = direct.properties.rStyle;
2475
+ const hasCharacterStyle = characterStyle != null && characterStyle.state !== "unset";
2476
+ for (const [readKey, styleKey] of STYLE_MATCH_KEYS) {
2477
+ const read = effective.properties[readKey];
2478
+ if (read?.state !== "uniform") continue;
2479
+ if (options.selectionUsesStyle && !hasCharacterStyle && direct.properties[readKey]?.state === "unset") continue;
2480
+ patch[styleKey] = toStyleValue(readKey, read.value);
2481
+ }
2482
+ return patch;
2483
+ }
2484
+ function toStyleValue(key, value) {
2485
+ switch (key) {
2486
+ case "bold":
2487
+ case "italic": return value === true;
2488
+ case "underline": return underlineValue(value);
2489
+ case "color": return typeof value === "string" && value.length > 0 ? { val: value.replace(/^#/, "") } : null;
2490
+ case "fontSize": return typeof value === "number" && Number.isFinite(value) ? Math.round(value * 2) : null;
2491
+ case "rFonts": return fontsValue(value);
2492
+ default: return null;
2493
+ }
2494
+ }
2495
+ function underlineValue(value) {
2496
+ if (value === true) return { val: "single" };
2497
+ if (!value || typeof value !== "object") return { val: "none" };
2498
+ const underline = value;
2499
+ const result = { val: typeof underline.style === "string" && underline.style ? underline.style : "single" };
2500
+ if (typeof underline.color === "string") result.color = underline.color;
2501
+ if (typeof underline.themeColor === "string") result.themeColor = underline.themeColor;
2502
+ return result;
2503
+ }
2504
+ var FONT_SLOTS = {
2505
+ ascii: "ascii",
2506
+ hAnsi: "hAnsi",
2507
+ eastAsia: "eastAsia",
2508
+ cs: "cs",
2509
+ asciiTheme: "asciiTheme",
2510
+ hAnsiTheme: "hAnsiTheme",
2511
+ eastAsiaTheme: "eastAsiaTheme",
2512
+ csTheme: "cstheme",
2513
+ hint: "hint"
2514
+ };
2515
+ function fontsValue(value) {
2516
+ if (!value || typeof value !== "object") return null;
2517
+ const result = {};
2518
+ for (const [slot, styleSlot] of Object.entries(FONT_SLOTS)) {
2519
+ const font = value[slot];
2520
+ if (typeof font === "string" && font.length > 0) result[styleSlot] = font;
2521
+ }
2522
+ return Object.keys(result).length > 0 ? result : null;
2523
+ }
2524
+ //#endregion
2462
2525
  //#region src/public/ui/format-painter-helpers.js
2463
2526
  /**
2464
2527
  * Maps SDRunProps field names to InlineRunPatch field names.
@@ -3629,6 +3692,25 @@ function getCommandDescriptor(id) {
3629
3692
  }
3630
3693
  //#endregion
3631
3694
  //#region src/public/ui/entity-at.ts
3695
+ function readDecorationProviders(node) {
3696
+ const encoded = node.getAttribute("data-superdoc-extension-decorations");
3697
+ if (encoded) try {
3698
+ const parsed = JSON.parse(encoded);
3699
+ if (Array.isArray(parsed)) return parsed.filter((id) => typeof id === "string");
3700
+ } catch {
3701
+ return [];
3702
+ }
3703
+ const single = node.getAttribute("data-superdoc-extension-decoration");
3704
+ return single ? [single] : [];
3705
+ }
3706
+ function collectDecorationProvidersFromChain(start, stopAt) {
3707
+ const providers = /* @__PURE__ */ new Set();
3708
+ for (let element = start; element; element = element.parentElement) {
3709
+ for (const provider of readDecorationProviders(element)) providers.add(provider);
3710
+ if (element === stopAt) break;
3711
+ }
3712
+ return [...providers];
3713
+ }
3632
3714
  function parseCommaSeparatedIds(value) {
3633
3715
  return value.split(",").map((id) => id.trim()).filter(Boolean);
3634
3716
  }
@@ -3700,6 +3782,16 @@ function collectEntityHitsFromChain(start, stopAt) {
3700
3782
  for (const hit of pendingStory) hit.story = layoutStory;
3701
3783
  pendingStory = [];
3702
3784
  }
3785
+ for (const decorationProvider of readDecorationProviders(node)) {
3786
+ const key = `decoration:${decorationProvider}`;
3787
+ if (!seen.has(key)) {
3788
+ seen.add(key);
3789
+ hits.push({
3790
+ type: "decoration",
3791
+ id: decorationProvider
3792
+ });
3793
+ }
3794
+ }
3703
3795
  const citationId = node.getAttribute("data-citation-id");
3704
3796
  if (citationId) {
3705
3797
  const key = `citation:${citationId}`;
@@ -6090,6 +6182,7 @@ function createSuperDocUI(options) {
6090
6182
  pointerSelecting: false,
6091
6183
  keyboardSelecting: false
6092
6184
  };
6185
+ const paintedInlineTargets = /* @__PURE__ */ new WeakSet();
6093
6186
  let painterCaptureEpoch = 0;
6094
6187
  let painterCommandDoc = null;
6095
6188
  const painterModeListeners = /* @__PURE__ */ new Set();
@@ -6186,7 +6279,7 @@ function createSuperDocUI(options) {
6186
6279
  const readHostSelectionSnapshot = () => {
6187
6280
  const handles = safeCall(() => hostMethod(getHost(), "getHandles")?.() ?? null, null);
6188
6281
  const editing = isPlainObject(handles) ? handles.editing : void 0;
6189
- const getSnapshot = hostMethod(isPlainObject(editing) ? editing.selection : void 0, "getSnapshot");
6282
+ const getSnapshot = hostMethod(isPlainObject(editing) ? editing.selection : void 0, "getSnapshot") ?? hostMethod(isPlainObject(handles) ? handles.selection : void 0, "getSnapshot");
6190
6283
  if (!getSnapshot) return null;
6191
6284
  const snapshot = safeCall(() => getSnapshot(), null);
6192
6285
  return snapshot && typeof snapshot === "object" ? snapshot : null;
@@ -6400,7 +6493,7 @@ function createSuperDocUI(options) {
6400
6493
  return left.id === right.id && left.paintedEntityId === right.paintedEntityId && storyLocatorSignature(left.story) === storyLocatorSignature(right.story);
6401
6494
  };
6402
6495
  const hasPendingTrackChangeRevealFocus = (focus) => Array.from(pendingTrackChangeRevealFocuses).some((pendingFocus) => explicitActiveChangesEqual(pendingFocus, focus));
6403
- const mirrorTrackedChangeFocusToHost = (next) => {
6496
+ const mirrorReviewFocusToHost = (entityType, next) => {
6404
6497
  const readHandles = hostMethod(getHost(), "getHandles");
6405
6498
  if (!readHandles) return;
6406
6499
  const handles = safeCall(() => readHandles(), null);
@@ -6411,15 +6504,15 @@ function createSuperDocUI(options) {
6411
6504
  if (!setActiveReviewTarget) return;
6412
6505
  const layout = isPlainObject(handles.layout) ? handles.layout : void 0;
6413
6506
  safeCall(() => setActiveReviewTarget({
6414
- entityType: "trackedChange",
6507
+ entityType,
6415
6508
  entityId: next.id,
6416
6509
  ...next.paintedEntityId ? { paintedEntityId: next.paintedEntityId } : {},
6417
6510
  origin: "panel",
6418
6511
  layoutEpoch: typeof layout?.generation === "number" ? layout.generation : 0,
6419
- story: next.story ?? {
6512
+ ...entityType === "trackedChange" ? { story: next.story ?? {
6420
6513
  kind: "story",
6421
6514
  storyType: "body"
6422
- }
6515
+ } } : {}
6423
6516
  }), null);
6424
6517
  return;
6425
6518
  }
@@ -6427,10 +6520,22 @@ function createSuperDocUI(options) {
6427
6520
  const clearActiveReviewTarget = hostMethod(review, "clearActiveReviewTarget");
6428
6521
  if (!getActiveReviewTarget || !clearActiveReviewTarget) return;
6429
6522
  const activeTarget = safeCall(() => getActiveReviewTarget(), null);
6430
- if (isPlainObject(activeTarget) && activeTarget.entityType === "trackedChange") safeCall(() => clearActiveReviewTarget(), void 0);
6523
+ if (isPlainObject(activeTarget) && activeTarget.entityType === entityType) safeCall(() => clearActiveReviewTarget(), void 0);
6524
+ };
6525
+ const mirrorTrackedChangeFocusToHost = (next) => mirrorReviewFocusToHost("trackedChange", next);
6526
+ const readReviewFocus = () => ({
6527
+ change: explicitActiveChange,
6528
+ commentId: explicitActiveCommentId
6529
+ });
6530
+ const restoreReviewFocus = (previous) => {
6531
+ setExplicitActiveChange(previous.change, { invalidateQueuedNavigation: false });
6532
+ explicitActiveCommentId = previous.commentId;
6533
+ if (previous.commentId) mirrorReviewFocusToHost("comment", { id: previous.commentId });
6534
+ else mirrorTrackedChangeFocusToHost(previous.change);
6431
6535
  };
6432
6536
  const setExplicitActiveChange = (next, options) => {
6433
6537
  explicitActiveChange = next;
6538
+ if (next) explicitActiveCommentId = null;
6434
6539
  explicitActiveChangeRevision += 1;
6435
6540
  trackChangeRevealInvalidation += 1;
6436
6541
  if (options?.invalidateQueuedNavigation !== false) queuedTrackChangeNavigationInvalidation += 1;
@@ -7100,7 +7205,7 @@ function createSuperDocUI(options) {
7100
7205
  failureCount: retryingSameToken ? prev.failureCount ?? 0 : 0,
7101
7206
  retryAtMs: 0
7102
7207
  });
7103
- Promise.resolve(raw).then((resolved) => {
7208
+ const settled = Promise.resolve(raw).then((resolved) => {
7104
7209
  const entry = asyncReads.get(key);
7105
7210
  if (!entry || entry.inflightToken !== token) return;
7106
7211
  asyncReads.set(key, {
@@ -7139,6 +7244,7 @@ function createSuperDocUI(options) {
7139
7244
  const retryAtMs = Date.now() + retryDelayMs;
7140
7245
  asyncReads.set(key, {
7141
7246
  ...entry,
7247
+ settled: void 0,
7142
7248
  inflightToken: null,
7143
7249
  failedToken: token,
7144
7250
  failureCount,
@@ -7146,6 +7252,8 @@ function createSuperDocUI(options) {
7146
7252
  });
7147
7253
  scheduleAsyncReadFailureRetry(retryAtMs);
7148
7254
  });
7255
+ const entry = asyncReads.get(key);
7256
+ if (entry?.inflightToken === token) entry.settled = settled;
7149
7257
  return null;
7150
7258
  };
7151
7259
  /**
@@ -7487,15 +7595,53 @@ function createSuperDocUI(options) {
7487
7595
  * Explicit whole-table changes have a complete local source range and also
7488
7596
  * seed clears, so a delayed worker read cannot retain a cleared table target.
7489
7597
  */
7490
- const refreshSelectionFromHost = (hostSelectionSnapshot, afterTypingPaint = false, wholeTableSelectionChanged = false) => {
7598
+ function captureWholeTableParagraphSeed(host, seed, nativeSelection) {
7599
+ const editor = getEditor();
7600
+ const rawLifetime = safeCall(() => hostMethod(host, "getForegroundInteractionLifetime")?.() ?? null, null);
7601
+ const lifetime = hasAbortedFlag(rawLifetime) ? rawLifetime : null;
7602
+ const mount = safeCall(() => hostMethod(host, "getMountedHandle")?.() ?? null, null);
7603
+ const mode = readDocumentMode();
7604
+ if (!host || !editor || !mount || !lifetime || lifetime.aborted || mode === null || seed.empty || seed.tableSelection?.kind !== "tableCells" || seed.tableSelection.type !== "table" || seed.target?.kind !== "text" || !seed.target.segments.length) return false;
7605
+ if (nativeSelection) {
7606
+ const presentation = safeCall(() => hostMethod(host, "readAppliedSelectionPresentation")?.(), null);
7607
+ const applied = normalizeSelectionInfo(isPlainObject(presentation) ? presentation.selection : null);
7608
+ if (readHostSelectionSnapshot() !== nativeSelection || !isNonCollapsedHostSelection(nativeSelection) || !applied || selectionSignature(selectionSliceFromInfo(applied, "ready")) !== selectionSignature(selectionSliceFromInfo(seed, "ready")) || JSON.stringify(applied.tableSelection) !== JSON.stringify(seed.tableSelection)) return false;
7609
+ const snapshot = nativeSelection;
7610
+ const first = seed.target.segments[0]?.blockId, last = seed.target.segments.at(-1)?.blockId;
7611
+ const matches = (point, blockId) => !!blockId && isPlainObject(point) && point.blockId === blockId && storyLocatorSignature(point.story ?? snapshot.story) === storyLocatorSignature(seed.target?.story);
7612
+ if (!(matches(snapshot.anchor, first) && matches(snapshot.focus, last) || matches(snapshot.focus, first) && matches(snapshot.anchor, last))) return false;
7613
+ }
7614
+ wholeTableParagraphSeed = {
7615
+ editor,
7616
+ host,
7617
+ doc: editor.doc,
7618
+ mount,
7619
+ lifetime,
7620
+ sourceToken: contentToken(),
7621
+ mode,
7622
+ info: seed,
7623
+ nativeSelection
7624
+ };
7625
+ return true;
7626
+ }
7627
+ const refreshSelectionFromHost = (hostSelectionSnapshot, afterTypingPaint = false, wholeTableSelectionChanged = false, initialTableSelection = null) => {
7628
+ const host = getHost();
7491
7629
  const foreground = foregroundMutationState();
7630
+ if (isNonCollapsedHostSelection(hostSelectionSnapshot) && readHostSelectionSnapshot() === hostSelectionSnapshot) {
7631
+ const seed = normalizeSelectionInfo(safeCall(() => hostMethod(host, "readLiveSelectionSyncSnapshot")?.(), null));
7632
+ if (seed && !seed.selectionTarget && captureWholeTableParagraphSeed(host, seed, hostSelectionSnapshot)) asyncReads.set("selection", {
7633
+ token: asyncReads.get("selection")?.token ?? null,
7634
+ value: seed,
7635
+ hasSettled: true,
7636
+ inflightToken: null
7637
+ });
7638
+ }
7492
7639
  if (isNonCollapsedHostSelection(hostSelectionSnapshot) && !(foreground && foreground.active > 0)) {
7493
7640
  typedCaretValidationHeld = false;
7494
7641
  pendingSelectionSeedValidationToken = null;
7495
7642
  issueAsyncRead("selection", selectionReadToken(), selectionCurrentRun, normalizeSelectionInfo);
7496
7643
  return;
7497
7644
  }
7498
- const host = getHost();
7499
7645
  const read = host?.readLiveSelectionSyncSnapshot;
7500
7646
  if (typeof read !== "function") return;
7501
7647
  const seed = normalizeSelectionInfo(safeCall(() => read.call(host), null));
@@ -7504,21 +7650,7 @@ function createSuperDocUI(options) {
7504
7650
  typedCaretValidationHeld = false;
7505
7651
  pendingSelectionSeedValidationToken = null;
7506
7652
  wholeTableParagraphSeed = null;
7507
- const editor = getEditor();
7508
- const rawLifetime = safeCall(() => hostMethod(host, "getForegroundInteractionLifetime")?.() ?? null, null);
7509
- const lifetime = hasAbortedFlag(rawLifetime) ? rawLifetime : null;
7510
- const mount = safeCall(() => hostMethod(host, "getMountedHandle")?.() ?? null, null);
7511
- const mode = readDocumentMode();
7512
- if (host && editor && mount && lifetime && !lifetime.aborted && mode !== null && !seed.empty && seed.tableSelection?.kind === "tableCells" && seed.tableSelection.type === "table" && seed.target?.segments.length && seed.selectionTarget) wholeTableParagraphSeed = {
7513
- editor,
7514
- host,
7515
- doc: editor.doc,
7516
- mount,
7517
- lifetime,
7518
- sourceToken: contentToken(),
7519
- mode,
7520
- info: seed
7521
- };
7653
+ captureWholeTableParagraphSeed(host, seed, initialTableSelection);
7522
7654
  const token = selectionReadToken();
7523
7655
  asyncReads.set("selection", {
7524
7656
  token,
@@ -7676,7 +7808,7 @@ function createSuperDocUI(options) {
7676
7808
  const directory = asyncReads.get("comments");
7677
7809
  const directoryItems = directory?.token === contentToken() && directory.hasSettled && Array.isArray(directory.value) ? directory.value : null;
7678
7810
  const activeValidationItems = directoryLeaseCounts.comments > 0 ? directoryItems : items;
7679
- if (explicitActiveCommentId && activeValidationItems && !activeValidationItems.some((item) => readEntityId(item) === explicitActiveCommentId)) explicitActiveCommentId = null;
7811
+ if (explicitActiveCommentId && activeValidationItems && (activeValidationItems !== items || listStatus === "ready")) explicitActiveCommentId = resolveActiveCommentId(activeValidationItems, explicitActiveCommentId);
7680
7812
  const activeId = explicitActiveCommentId ?? activeIds[0] ?? null;
7681
7813
  return {
7682
7814
  status: combineStatus(listStatus, selection.status),
@@ -8321,12 +8453,13 @@ function createSuperDocUI(options) {
8321
8453
  ];
8322
8454
  function effectiveInlineUniformityInput(selection) {
8323
8455
  const cellTarget = currentCellSelectionTarget();
8324
- const target = cellTarget ?? selection.selectionTarget;
8456
+ const paintedTarget = cellTarget ? null : currentUnrepresentableSelectionTarget(selection);
8457
+ const target = cellTarget ?? paintedTarget ?? selection.selectionTarget;
8325
8458
  if (!target) return null;
8326
8459
  const tableCellSelection = cellTarget ? tableCellSelectionForTarget(cellTarget) : null;
8327
8460
  return {
8328
8461
  target,
8329
- offsetSpace: "selection",
8462
+ offsetSpace: cellTarget || paintedTarget ? "selection" : "kernel",
8330
8463
  ...tableCellSelection ? { tableCellSelection } : {}
8331
8464
  };
8332
8465
  }
@@ -8464,7 +8597,11 @@ function createSuperDocUI(options) {
8464
8597
  const getNodeById = hostMethod(doc, "getNodeById");
8465
8598
  const raw = await Promise.resolve(getNode ? getNode(address) : getNodeById?.({
8466
8599
  nodeId: address["nodeId"],
8467
- nodeType: address["nodeType"]
8600
+ nodeType: address["nodeType"],
8601
+ story: address["story"] ?? {
8602
+ kind: "story",
8603
+ storyType: "body"
8604
+ }
8468
8605
  }));
8469
8606
  return isPlainObject(raw) ? raw : void 0;
8470
8607
  };
@@ -9010,7 +9147,8 @@ function createSuperDocUI(options) {
9010
9147
  const token = contentToken();
9011
9148
  const read = readAsync(key, token, () => isBodyStory ? doc.getNodeById({
9012
9149
  nodeId: blockId,
9013
- nodeType: "paragraph"
9150
+ nodeType: "paragraph",
9151
+ story
9014
9152
  }) : doc.getNode(paragraphTarget(blockId, story)), (raw) => raw && typeof raw === "object" ? raw : null);
9015
9153
  const entry = asyncReads.get(key);
9016
9154
  return {
@@ -9219,7 +9357,11 @@ function createSuperDocUI(options) {
9219
9357
  const isBodyStory = !story || story.storyType === "body";
9220
9358
  const raw = story && typeof doc.getNode === "function" ? await doc.getNode(paragraphTarget(blockId, story)) : isBodyStory && typeof doc.getNodeById === "function" ? await doc.getNodeById({
9221
9359
  nodeId: blockId,
9222
- nodeType: "paragraph"
9360
+ nodeType: "paragraph",
9361
+ story: {
9362
+ kind: "story",
9363
+ storyType: "body"
9364
+ }
9223
9365
  }) : null;
9224
9366
  return readParagraphIndentationFromResult(raw && typeof raw === "object" ? raw : null);
9225
9367
  } catch {
@@ -9896,14 +10038,15 @@ function createSuperDocUI(options) {
9896
10038
  const tableSelection = isPlainObject(handles) ? handles.selection : void 0;
9897
10039
  const subscribeWholeTable = hostMethod(tableSelection, "subscribeWholeTable");
9898
10040
  if (subscribeWholeTable) {
9899
- const refreshWholeTable = () => {
10041
+ const refreshWholeTable = (initial = false) => {
10042
+ const native = initial ? readHostSelectionSnapshot() : null;
9900
10043
  selectionEpoch += 1;
9901
- refreshSelectionFromHost(void 0, false, true);
10044
+ refreshSelectionFromHost(void 0, false, true, native);
9902
10045
  recompute("host-selection");
9903
10046
  };
9904
- const detach = subscribeWholeTable(refreshWholeTable);
10047
+ const detach = subscribeWholeTable(() => refreshWholeTable());
9905
10048
  if (typeof detach === "function") unsubscribeCallbacks.push(() => detach());
9906
- if (hostMethod(tableSelection, "getWholeTableNodeId")?.()) refreshWholeTable();
10049
+ if (hostMethod(tableSelection, "getWholeTableNodeId")?.()) refreshWholeTable(true);
9907
10050
  }
9908
10051
  } catch {
9909
10052
  currentHostSelectionSource = null;
@@ -9924,10 +10067,35 @@ function createSuperDocUI(options) {
9924
10067
  };
9925
10068
  let trackedChangeNavigationInFlight = 0;
9926
10069
  let lastPublishedHostReviewTarget;
10070
+ const isRepublishedHostReviewTarget = (left, right) => left === right || isPlainObject(left) && isPlainObject(right) && left.layoutEpoch !== right.layoutEpoch && left.origin === right.origin && left.entityType === right.entityType && left.entityId === right.entityId && left.paintedEntityId === right.paintedEntityId && storyLocatorSignature(left.story) === storyLocatorSignature(right.story);
9927
10071
  const syncTrackedChangeFocusFromHostReviewTarget = (rawTarget, rawSnapshot) => {
9928
10072
  const rejection = (rawSnapshot && typeof rawSnapshot === "object" ? rawSnapshot : null)?.lastInteractionRejection;
9929
- if (rawTarget == null && rejection && typeof rejection === "object" && rejection.code === "review-target-invalidated" && rejection.detail === "not-painted" && explicitActiveChange) return false;
10073
+ if (rawTarget == null && rejection && typeof rejection === "object" && rejection.code === "review-target-invalidated" && rejection.detail === "not-painted" && (explicitActiveChange || explicitActiveCommentId)) return false;
9930
10074
  const target = rawTarget && typeof rawTarget === "object" ? rawTarget : null;
10075
+ if (target?.entityType === "comment" && typeof target.entityId === "string" && target.entityId.length > 0) {
10076
+ const commentId = resolveActiveCommentId(state?.comments.items ?? [], target.entityId) ?? target.entityId;
10077
+ const republished = isRepublishedHostReviewTarget(rawTarget, lastPublishedHostReviewTarget?.raw);
10078
+ lastPublishedHostReviewTarget = {
10079
+ raw: rawTarget,
10080
+ focus: null,
10081
+ origin: target.origin
10082
+ };
10083
+ if (republished || explicitActiveCommentId === commentId && !explicitActiveChange) return false;
10084
+ setExplicitActiveChange(null);
10085
+ explicitActiveCommentId = commentId;
10086
+ return true;
10087
+ }
10088
+ const previousTarget = lastPublishedHostReviewTarget?.raw;
10089
+ if (!target && isPlainObject(previousTarget) && previousTarget.entityType === "comment") {
10090
+ lastPublishedHostReviewTarget = {
10091
+ raw: rawTarget,
10092
+ focus: null,
10093
+ origin: void 0
10094
+ };
10095
+ if (!explicitActiveCommentId) return false;
10096
+ explicitActiveCommentId = null;
10097
+ return true;
10098
+ }
9931
10099
  let next = null;
9932
10100
  if (target?.entityType === "trackedChange" && typeof target.entityId === "string" && target.entityId.length > 0) {
9933
10101
  const targetStory = target.story && typeof target.story === "object" ? target.story : void 0;
@@ -9952,7 +10120,7 @@ function createSuperDocUI(options) {
9952
10120
  }
9953
10121
  if (rawTarget == null && next == null && explicitActiveChange != null && hasPendingTrackChangeRevealFocus(explicitActiveChange)) return false;
9954
10122
  const previousPublication = lastPublishedHostReviewTarget;
9955
- if (previousPublication && (rawTarget === previousPublication.raw || target?.origin === "panel" && previousPublication.origin === "panel" && next && previousPublication.focus && explicitActiveChangesEqual(previousPublication.focus, next))) return false;
10123
+ if (previousPublication && (isRepublishedHostReviewTarget(rawTarget, previousPublication.raw) || target?.origin === "panel" && previousPublication.origin === "panel" && next && previousPublication.focus && explicitActiveChangesEqual(previousPublication.focus, next))) return false;
9956
10124
  lastPublishedHostReviewTarget = {
9957
10125
  raw: rawTarget,
9958
10126
  focus: next,
@@ -10330,11 +10498,16 @@ function createSuperDocUI(options) {
10330
10498
  * ordinary/insertion selections are visible-space, while a selection that
10331
10499
  * reaches painted deleted text is projected in tracked space by the host.
10332
10500
  *
10333
- * Cross-block coverage must come from the model-backed tracked target;
10334
- * intermediate block lengths cannot be inferred from the endpoints.
10501
+ * Cross-block and object-containing coverage use the model-backed tracked
10502
+ * target when the explicit text selection cannot represent the range.
10335
10503
  */
10336
10504
  function trackDecisionRangeFromSelection(selection) {
10337
10505
  if (selection.empty) return null;
10506
+ if (!selection.selectionTarget && selection.trackedTarget) return {
10507
+ kind: "range",
10508
+ coordinateSpace: "tracked",
10509
+ range: selection.trackedTarget
10510
+ };
10338
10511
  const explicit = selection.selectionTarget;
10339
10512
  const start = explicit?.start;
10340
10513
  const end = explicit?.end;
@@ -10488,7 +10661,7 @@ function createSuperDocUI(options) {
10488
10661
  kind: "blocked",
10489
10662
  reason: SUPERDOC_UI_REASONS.operationUnavailable
10490
10663
  };
10491
- if (!selection.selectionTarget) return null;
10664
+ if (!selection.selectionTarget && !selection.trackedTarget && selection.target?.kind !== "text") return null;
10492
10665
  const target = trackDecisionRangeFromSelection(selection);
10493
10666
  if (!target && route() !== "id") return {
10494
10667
  kind: "blocked",
@@ -10828,25 +11001,35 @@ function createSuperDocUI(options) {
10828
11001
  pendingCommandSettlement = settledResults.length ? Promise.all(settledResults).then((results) => combineCommandResults(results)) : null;
10829
11002
  return combineCommandResults(immediateResults);
10830
11003
  };
10831
- /**
10832
- * Call an inline `format.*` operation against the live selection. Mirrors
10833
- * `callEditorMutation` (suggesting mode adds `changeMode: 'tracked'`), and
10834
- * when the target is the host's own editable `selectionTarget` it also
10835
- * carries the PRIVATE V2 option `offsetSpace: 'selection'` (browser
10836
- * selection offsets count an inline object as one caret position). The
10837
- * option is not part of public `MutationOptions`; this is the same cast
10838
- * pattern the delete/replace callers use.
10839
- */
10840
11004
  function currentCellSelectionTarget() {
11005
+ return captureInputSelectionTarget(true);
11006
+ }
11007
+ function captureInputSelectionTarget(requireTableCells = false) {
10841
11008
  const handles = safeCall(() => hostMethod(getHost(), "getHandles")?.(), null);
10842
11009
  const selection = isPlainObject(handles) ? handles.selection : void 0;
10843
11010
  const held = hostMethod(selection, "getSnapshot")?.();
10844
- if (!isPlainObject(held) || !held.tableCells) return null;
11011
+ if (requireTableCells && (!isPlainObject(held) || !held.tableCells)) return null;
10845
11012
  const resolved = hostMethod(selection, "toSelectionTarget")?.();
10846
11013
  return isPlainObject(resolved) && resolved.kind === "ok" && resolved.mode === "range" ? capturedTableCellTarget(resolved.target) : null;
10847
11014
  }
11015
+ function currentUnrepresentableSelectionTarget(selection) {
11016
+ if (selection.empty || selection.selectionTarget || selection.target?.kind !== "text") return null;
11017
+ return heldPaintedTextSelectionTarget();
11018
+ }
11019
+ function heldPaintedTextSelectionTarget() {
11020
+ const target = captureInputSelectionTarget();
11021
+ if (target) paintedInlineTargets.add(target);
11022
+ return target;
11023
+ }
10848
11024
  function resolveInlineCommandTarget(selection) {
10849
- return currentCellSelectionTarget() ?? resolveInlineSelectionTarget(selection);
11025
+ const cells = currentCellSelectionTarget();
11026
+ if (cells) return cells;
11027
+ if (!selection.empty && !selection.selectionTarget && selection.target?.kind === "text") {
11028
+ const snapshot = readHostSelectionSnapshot();
11029
+ const painted = currentUnrepresentableSelectionTarget(selection);
11030
+ if (painted) return snapshot && appliedSelectionPresentation(selection) && readHostSelectionSnapshot() === snapshot ? painted : null;
11031
+ }
11032
+ return resolveInlineSelectionTarget(selection);
10850
11033
  }
10851
11034
  function tableCellSelectionForTarget(target) {
10852
11035
  const cellTarget = currentCellSelectionTarget();
@@ -10866,10 +11049,15 @@ function createSuperDocUI(options) {
10866
11049
  offsetSpace: "selection",
10867
11050
  tableCellSelection
10868
11051
  });
11052
+ const paintedTarget = input?.target;
11053
+ if (paintedTarget !== null && typeof paintedTarget === "object" && (paintedInlineTargets.has(paintedTarget) || paintedTarget.coordinateSpace === "tracked")) return op(input, {
11054
+ ...options,
11055
+ offsetSpace: "selection"
11056
+ });
10869
11057
  if (!selection.selectionTarget) return options ? op(input, options) : op(input);
10870
11058
  return op(input, {
10871
11059
  ...options,
10872
- offsetSpace: "selection"
11060
+ offsetSpace: "kernel"
10873
11061
  });
10874
11062
  };
10875
11063
  /** Build the `format.paragraph.*` / `styles.paragraph.*` input for a block. */
@@ -11098,13 +11286,16 @@ function createSuperDocUI(options) {
11098
11286
  };
11099
11287
  pendingCommandSettlement = (async () => {
11100
11288
  const plans = await Promise.all(blockIds.map((blockId) => planBlock(blockId).catch(() => null)));
11101
- if (plans.length === blockIds.length && plans.every((plan) => plan?.kind === "list") && readDocumentMode() !== "suggesting") {
11289
+ const allListItems = plans.length === blockIds.length && plans.every((plan) => plan?.kind === "list");
11290
+ const preserveFirstItemLevel = blockIds.length === 1 && story.storyType === "body";
11291
+ if (allListItems && (readDocumentMode() !== "suggesting" || preserveFirstItemLevel)) {
11102
11292
  const rangeRoute = mode === "indent" ? "lists.indentRange" : "lists.outdentRange";
11103
11293
  const rangeOp = resolveDocOperation(doc, rangeRoute);
11104
11294
  if (rangeOp) try {
11105
11295
  return await settleOperationResult(callEditorMutation(rangeRoute, rangeOp, {
11106
11296
  paraIds: blockIds,
11107
- story
11297
+ story,
11298
+ ...preserveFirstItemLevel ? { preserveFirstItemLevel: true } : {}
11108
11299
  })).settled;
11109
11300
  } catch {
11110
11301
  return false;
@@ -11644,23 +11835,32 @@ function createSuperDocUI(options) {
11644
11835
  const record = readLinkPayloadRecord(payload);
11645
11836
  return Boolean(readLinkPayloadTarget(payload) || isPlainObject(record.hyperlinkTarget) || isPlainObject(record.textTarget));
11646
11837
  };
11647
- const currentWholeTableParagraphSelection = () => {
11838
+ const currentWholeTableParagraphOwner = () => {
11648
11839
  const owner = wholeTableParagraphSeed, host = getHost();
11649
- if (!owner || disposed || owner.lifetime.aborted || owner.host !== host || owner.editor !== getEditor() || owner.editor.doc !== owner.doc || owner.sourceToken !== contentToken() || owner.mode !== readDocumentMode() || safeCall(() => hostMethod(host, "getMountedHandle")?.(), null) !== owner.mount) return false;
11840
+ if (!owner || disposed || owner.lifetime.aborted || owner.host !== host || owner.editor !== getEditor() || owner.editor.doc !== owner.doc || owner.sourceToken !== contentToken() || owner.mode !== readDocumentMode() || safeCall(() => hostMethod(host, "getMountedHandle")?.(), null) !== owner.mount) return null;
11650
11841
  const tableId = owner.info.tableSelection?.tableId;
11651
11842
  const activeTable = safeCall(() => {
11652
11843
  const handles = hostMethod(host, "getHandles")?.();
11653
11844
  return hostMethod(isPlainObject(handles) ? handles.selection : void 0, "getWholeTableNodeId")?.();
11654
11845
  }, null);
11655
- if (!tableId || activeTable !== tableId || state.selection.tableSelection?.type !== "table" || state.selection.tableSelection.tableId !== tableId || selectionSignature(state.selection) !== selectionSignature(selectionSliceFromInfo(owner.info, "ready"))) return false;
11846
+ if (!tableId || activeTable !== tableId || state.selection.tableSelection?.type !== "table" || state.selection.tableSelection.tableId !== tableId || selectionSignature(state.selection) !== selectionSignature(selectionSliceFromInfo(owner.info, "ready"))) return null;
11656
11847
  const applied = appliedSelectionPresentation(state.selection);
11657
11848
  const info = normalizeSelectionInfo(applied?.selection);
11658
- return !!info && selectionInfoMatchesHost(info) && JSON.stringify(info.tableSelection) === JSON.stringify(owner.info.tableSelection);
11849
+ return info && JSON.stringify(info.tableSelection) === JSON.stringify(owner.info.tableSelection) ? {
11850
+ owner,
11851
+ info
11852
+ } : null;
11853
+ };
11854
+ const currentWholeTableParagraphSelection = () => {
11855
+ const current = currentWholeTableParagraphOwner();
11856
+ if (!current) return false;
11857
+ return current.owner.info.selectionTarget ? selectionInfoMatchesHost(current.info) : current.owner.nativeSelection !== null && readHostSelectionSnapshot() === current.owner.nativeSelection;
11659
11858
  };
11660
11859
  const commandSelectionIsReady = (id, descriptor, payload) => {
11661
11860
  const foreground = foregroundMutationState();
11662
11861
  if (foreground && (foreground.active > 0 || foreground.pending > 0)) return false;
11663
11862
  if (descriptor?.blockParagraph && currentWholeTableParagraphSelection()) return true;
11863
+ if (descriptor?.blockParagraph && state.selection.tableSelection?.type === "table" && !state.selection.selectionTarget) return false;
11664
11864
  if (state.selection.status !== "ready") return false;
11665
11865
  if (trackDecisionCommand(id)?.scope === "id") return true;
11666
11866
  if (!descriptor || descriptor.disposition !== "routed") return true;
@@ -12081,10 +12281,23 @@ function createSuperDocUI(options) {
12081
12281
  const cellTarget = currentCellSelectionTarget();
12082
12282
  const tableCellSelection = tableCellSelectionForTarget(cellTarget);
12083
12283
  const rawSelection = typeof selectionApi?.["current"] === "function" ? await Promise.resolve(selectionApi["current"]({ includeText: true })) : readSelectionInfoLive().value;
12284
+ const selection = normalizeSelectionInfo(rawSelection);
12285
+ let paintedTarget = null;
12286
+ if (selection && !selection.empty && !selection.selectionTarget && selection.target?.kind === "text" && !cellTarget) {
12287
+ const nativeSelection = readHostSelectionSnapshot();
12288
+ paintedTarget = heldPaintedTextSelectionTarget();
12289
+ if (paintedTarget && (!nativeSelection || !appliedSelectionPresentation(selectionSliceFromInfo(selection, "ready")) || readHostSelectionSnapshot() !== nativeSelection)) return {
12290
+ selection: null,
12291
+ cellTarget: null,
12292
+ tableCellSelection: null,
12293
+ paintedTarget: null
12294
+ };
12295
+ }
12084
12296
  return {
12085
- selection: normalizeSelectionInfo(rawSelection),
12297
+ selection,
12086
12298
  cellTarget,
12087
- tableCellSelection
12299
+ tableCellSelection,
12300
+ paintedTarget
12088
12301
  };
12089
12302
  };
12090
12303
  const waitForSelectionSettle = () => new Promise((resolve) => setTimeout(resolve, 16));
@@ -12102,7 +12315,7 @@ function createSuperDocUI(options) {
12102
12315
  break;
12103
12316
  }
12104
12317
  if (selectionRead?.selection && !selectionRead.selection.empty && key !== painter.sourceSelectionKey) {
12105
- await applyFormatPainter(selectionRead.selection, selectionRead.cellTarget, selectionRead.tableCellSelection);
12318
+ await applyFormatPainter(selectionRead.selection, selectionRead.cellTarget, selectionRead.tableCellSelection, selectionRead.paintedTarget);
12106
12319
  return;
12107
12320
  }
12108
12321
  if (painter.snapshot?.paragraph) {
@@ -12110,10 +12323,10 @@ function createSuperDocUI(options) {
12110
12323
  const caretSel = caretRead.selection;
12111
12324
  if (!caretSel) return;
12112
12325
  if (selectionKey(caretSel) === painter.sourceSelectionKey) return;
12113
- await applyFormatPainter(caretSel, caretRead.cellTarget, caretRead.tableCellSelection);
12326
+ await applyFormatPainter(caretSel, caretRead.cellTarget, caretRead.tableCellSelection, caretRead.paintedTarget);
12114
12327
  }
12115
12328
  };
12116
- const applyFormatPainter = async (selection, cellTarget, tableCellSelection) => {
12329
+ const applyFormatPainter = async (selection, cellTarget, tableCellSelection, paintedTarget) => {
12117
12330
  const snap = painter.snapshot;
12118
12331
  if (!snap) {
12119
12332
  if (painter.mode === "armed") exitFormatPainter();
@@ -12131,7 +12344,7 @@ function createSuperDocUI(options) {
12131
12344
  return;
12132
12345
  }
12133
12346
  const selTarget = selection.target;
12134
- const selectionTarget = cellTarget ?? selection.selectionTarget;
12347
+ const selectionTarget = cellTarget ?? paintedTarget ?? selection.selectionTarget;
12135
12348
  const selectionIsEmpty = selection.empty === true;
12136
12349
  const segments = Array.isArray(selTarget?.segments) ? selTarget.segments : [];
12137
12350
  const targetStory = selTarget?.story;
@@ -12369,13 +12582,16 @@ function createSuperDocUI(options) {
12369
12582
  const snapshot = selectionSub.get();
12370
12583
  if (snapshot.empty) return null;
12371
12584
  const cells = currentCellSelectionTarget();
12585
+ const painted = cells ? null : currentUnrepresentableSelectionTarget(snapshot);
12586
+ const hostSelection = selectionHostSnapshots.get(snapshot);
12587
+ if ((cells || painted || hostSelection && !snapshot.selectionTarget) && (!hostSelection || readHostSelectionSnapshot() !== hostSelection)) return null;
12372
12588
  if (!snapshot.target && !snapshot.selectionTarget && !cells) return null;
12373
12589
  const capture = {
12374
12590
  ...snapshot,
12375
12591
  capturedAt: Date.now(),
12376
- ...cells ? { _tableCellSelectionTarget: cells } : {}
12592
+ ...cells ? { _tableCellSelectionTarget: cells } : {},
12593
+ ...painted ? { _paintedSelectionTarget: painted } : {}
12377
12594
  };
12378
- const hostSelection = selectionHostSnapshots.get(snapshot);
12379
12595
  if (hostSelection) selectionHostSnapshots.set(capture, hostSelection);
12380
12596
  return capture;
12381
12597
  },
@@ -12401,7 +12617,13 @@ function createSuperDocUI(options) {
12401
12617
  ok: false,
12402
12618
  reason: SUPERDOC_UI_REASONS.targetUnresolved
12403
12619
  });
12404
- const target = cellTarget ?? selectionTargetForRestore(capture);
12620
+ const hasPaintedTarget = "_paintedSelectionTarget" in capture;
12621
+ const paintedTarget = hasPaintedTarget ? capturedTableCellTarget(capture._paintedSelectionTarget) : null;
12622
+ if (hasPaintedTarget && !paintedTarget) return done({
12623
+ ok: false,
12624
+ reason: SUPERDOC_UI_REASONS.targetUnresolved
12625
+ });
12626
+ const target = cellTarget ?? paintedTarget ?? selectionTargetForRestore(capture);
12405
12627
  if (!target) return done({
12406
12628
  ok: false,
12407
12629
  reason: SUPERDOC_UI_REASONS.targetUnresolved
@@ -12414,7 +12636,7 @@ function createSuperDocUI(options) {
12414
12636
  reason: resolved.reason
12415
12637
  });
12416
12638
  try {
12417
- return done(normalizeHostSelectionApplyResult(hostMethod(resolved.helper, "apply")?.(target, ...cellTarget ? [{ tableCells: true }] : [])));
12639
+ return done(normalizeHostSelectionApplyResult(hostMethod(resolved.helper, "apply")?.(target, ...cellTarget ? [{ tableCells: true }] : paintedTarget ? [{ offsetSpace: "selection" }] : [])));
12418
12640
  } catch {
12419
12641
  return done({
12420
12642
  ok: false,
@@ -12833,6 +13055,10 @@ function createSuperDocUI(options) {
12833
13055
  };
12834
13056
  const commentsSub = sliceHandle((s) => s.comments);
12835
13057
  const commentsSnap = directorySnapshotHandle(commentsSub, select((s) => computeCommentsDirectorySnapshot(s.selection, s.comments)), "comments");
13058
+ documentResetHooks.push(() => {
13059
+ setExplicitActiveChange(null);
13060
+ explicitActiveCommentId = null;
13061
+ });
12836
13062
  const comments = {
12837
13063
  get: commentsSnap.get,
12838
13064
  getSnapshot: commentsSnap.getSnapshot,
@@ -12926,6 +13152,7 @@ function createSuperDocUI(options) {
12926
13152
  setActive: (commentId) => {
12927
13153
  if (commentId == null) {
12928
13154
  explicitActiveCommentId = null;
13155
+ mirrorReviewFocusToHost("comment", null);
12929
13156
  recompute();
12930
13157
  return true;
12931
13158
  }
@@ -12938,7 +13165,9 @@ function createSuperDocUI(options) {
12938
13165
  const resolvedId = resolveActiveCommentId(items, commentId);
12939
13166
  if (!resolvedId) return false;
12940
13167
  if (!items.some((entry) => readEntityId(entry) === resolvedId)) return false;
13168
+ setExplicitActiveChange(null);
12941
13169
  explicitActiveCommentId = resolvedId;
13170
+ mirrorReviewFocusToHost("comment", { id: resolvedId });
12942
13171
  recompute();
12943
13172
  return true;
12944
13173
  },
@@ -12998,7 +13227,7 @@ function createSuperDocUI(options) {
12998
13227
  };
12999
13228
  const navigateAndScrollCurrentRequest = async (step, requestedAtInvalidation) => {
13000
13229
  if (queuedTrackChangeNavigationInvalidation !== requestedAtInvalidation) return { success: false };
13001
- const previousActiveChange = explicitActiveChange;
13230
+ const previousFocus = readReviewFocus();
13002
13231
  const activeId = navigateTrackChange(step, { invalidateQueuedNavigation: false });
13003
13232
  if (activeId == null) return { success: false };
13004
13233
  const optimisticActiveChange = explicitActiveChange;
@@ -13006,7 +13235,7 @@ function createSuperDocUI(options) {
13006
13235
  const isCurrent = () => queuedTrackChangeNavigationInvalidation === requestedAtInvalidation && explicitActiveChange === optimisticActiveChange && explicitActiveChangeRevision === optimisticRevision;
13007
13236
  const rollback = () => {
13008
13237
  if (!isCurrent()) return;
13009
- setExplicitActiveChange(previousActiveChange, { invalidateQueuedNavigation: false });
13238
+ restoreReviewFocus(previousFocus);
13010
13239
  recompute();
13011
13240
  };
13012
13241
  if (optimisticActiveChange) pendingTrackChangeRevealFocuses.add(optimisticActiveChange);
@@ -13026,10 +13255,8 @@ function createSuperDocUI(options) {
13026
13255
  shouldContinue: isCurrent
13027
13256
  });
13028
13257
  if (!isCurrent()) return { success: false };
13029
- if (!result.ok && result.reason !== SUPERDOC_UI_REASONS.targetNotVisible) {
13030
- rollback();
13031
- mirrorTrackedChangeFocusToHost(previousActiveChange);
13032
- } else {
13258
+ if (!result.ok && result.reason !== SUPERDOC_UI_REASONS.targetNotVisible) rollback();
13259
+ else {
13033
13260
  if (result.ok) await restampVisibleTrackedChangeCarrierAliases(activeId);
13034
13261
  if (!isCurrent()) return { success: false };
13035
13262
  mirrorTrackedChangeFocusToHost(optimisticActiveChange);
@@ -13160,7 +13387,7 @@ function createSuperDocUI(options) {
13160
13387
  const matchingRow = matchStory ? loadedItems.find((item) => entityRowMatchesRequest(item, publicId, matchStory)) : loadedItems.find((item) => readEntityId(item) === publicId);
13161
13388
  const story = matchStory ? requestedStory : readEntityRequestStory(matchingRow);
13162
13389
  const importedId = deriveTrackedChangeImportedId(matchingRow);
13163
- const previousActiveChange = explicitActiveChange;
13390
+ const previousFocus = readReviewFocus();
13164
13391
  const requestedActiveChange = {
13165
13392
  id: publicId,
13166
13393
  ...story ? { story } : {},
@@ -13188,7 +13415,7 @@ function createSuperDocUI(options) {
13188
13415
  const isCurrent = () => trackChangeRevealInvalidation === revealInvalidation && explicitActiveChange === optimisticActiveChange && explicitActiveChangeRevision === optimisticRevision;
13189
13416
  const rollback = () => {
13190
13417
  if (!isCurrent()) return;
13191
- setExplicitActiveChange(previousActiveChange, { invalidateQueuedNavigation: false });
13418
+ restoreReviewFocus(previousFocus);
13192
13419
  recompute();
13193
13420
  };
13194
13421
  recompute();
@@ -13202,10 +13429,8 @@ function createSuperDocUI(options) {
13202
13429
  success: false,
13203
13430
  ok: false
13204
13431
  };
13205
- if (!result.ok && result.reason !== SUPERDOC_UI_REASONS.targetNotVisible) {
13206
- rollback();
13207
- mirrorTrackedChangeFocusToHost(previousActiveChange);
13208
- } else {
13432
+ if (!result.ok && result.reason !== SUPERDOC_UI_REASONS.targetNotVisible) rollback();
13433
+ else {
13209
13434
  if (result.ok) await restampVisibleTrackedChangeCarrierAliases(publicId);
13210
13435
  if (!isCurrent()) return {
13211
13436
  success: false,
@@ -13540,14 +13765,14 @@ function createSuperDocUI(options) {
13540
13765
  if (root instanceof HTMLElement) return root;
13541
13766
  return null;
13542
13767
  };
13543
- const pointEntityHits = (x, y) => {
13544
- if (typeof x !== "number" || typeof y !== "number" || !Number.isFinite(x) || !Number.isFinite(y)) return [];
13768
+ const pointHitElements = (x, y) => {
13769
+ if (typeof x !== "number" || typeof y !== "number" || !Number.isFinite(x) || !Number.isFinite(y)) return null;
13545
13770
  const dom = globalThis.document;
13546
- if (!dom || typeof dom.elementFromPoint !== "function") return [];
13771
+ if (!dom || typeof dom.elementFromPoint !== "function") return null;
13547
13772
  const container = resolveVisibleHost();
13548
- if (!container) return [];
13773
+ if (!container) return null;
13549
13774
  const startEl = dom.elementFromPoint(x, y);
13550
- if (!startEl || !container.contains(startEl)) return [];
13775
+ if (!startEl || !container.contains(startEl)) return null;
13551
13776
  const primaryEls = [startEl];
13552
13777
  const addPrimaryEl = (el) => {
13553
13778
  if (el && container.contains(el) && !primaryEls.includes(el)) primaryEls.push(el);
@@ -13562,6 +13787,17 @@ function createSuperDocUI(options) {
13562
13787
  }
13563
13788
  for (const el of stacked) addPrimaryEl(el);
13564
13789
  }
13790
+ return {
13791
+ container,
13792
+ elements: primaryEls,
13793
+ x,
13794
+ y
13795
+ };
13796
+ };
13797
+ const pointEntityHits = (x, y) => {
13798
+ const point = pointHitElements(x, y);
13799
+ if (!point) return [];
13800
+ const { container, elements: primaryEls } = point;
13565
13801
  const markerEls = [];
13566
13802
  const addMarkerEl = (el) => {
13567
13803
  if (el && container.contains(el) && !markerEls.includes(el) && !primaryEls.includes(el)) markerEls.push(el);
@@ -13575,7 +13811,7 @@ function createSuperDocUI(options) {
13575
13811
  } catch {
13576
13812
  return false;
13577
13813
  }
13578
- return rect != null && x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom;
13814
+ return rect != null && point.x >= rect.left && point.x <= rect.right && point.y >= rect.top && point.y <= rect.bottom;
13579
13815
  };
13580
13816
  for (const el of primaryEls) {
13581
13817
  const queryAll = el.querySelectorAll;
@@ -13599,6 +13835,18 @@ function createSuperDocUI(options) {
13599
13835
  const seen = /* @__PURE__ */ new Set();
13600
13836
  const hits = [];
13601
13837
  for (const hit of rawHits) {
13838
+ if (hit.type === "decoration") {
13839
+ const owner = decorationOwnerForProvider(hit.id);
13840
+ if (!owner) continue;
13841
+ const key = `decoration:${owner.id}`;
13842
+ if (seen.has(key)) continue;
13843
+ seen.add(key);
13844
+ hits.push({
13845
+ type: "decoration",
13846
+ id: owner.id
13847
+ });
13848
+ continue;
13849
+ }
13602
13850
  if (hit.type !== "trackedChange") {
13603
13851
  const layoutStory = hit.type === "contentControl" ? layoutStoryDatasetToStoryLocator(hit.story) : void 0;
13604
13852
  const story = hit.type === "contentControl" ? isTextboxStory(layoutStory) ? layoutStory : storyKeyToStoryLocator(hit.storyKey) ?? layoutStory : void 0;
@@ -13754,6 +14002,149 @@ function createSuperDocUI(options) {
13754
14002
  if (status !== "ready") return { reason: "not-ready" };
13755
14003
  return value ? { target: value } : { reason: "unresolved" };
13756
14004
  };
14005
+ const liveDecorations = /* @__PURE__ */ new Map();
14006
+ const decorationOwnerForProvider = (providerId) => {
14007
+ const owner = currentHostVisuals()?.providerOwner?.(providerId);
14008
+ return isPlainObject(owner) && typeof owner.id === "string" ? owner : null;
14009
+ };
14010
+ const currentHostVisuals = () => getEditor()?.visuals ?? null;
14011
+ const decorationListeners = /* @__PURE__ */ new Set();
14012
+ let detachDecorationPointer = null;
14013
+ let hoveredDecorations = [];
14014
+ let lastPointer = {
14015
+ clientX: 0,
14016
+ clientY: 0
14017
+ };
14018
+ const decorationIdsAt = (x, y) => {
14019
+ const point = pointHitElements(x, y);
14020
+ if (!point) return [];
14021
+ const ids = /* @__PURE__ */ new Set();
14022
+ for (const element of point.elements) for (const provider of collectDecorationProvidersFromChain(element, point.container)) {
14023
+ const owner = decorationOwnerForProvider(provider);
14024
+ if (owner) ids.add(owner.id);
14025
+ }
14026
+ return [...ids];
14027
+ };
14028
+ const decorationData = (id) => {
14029
+ const owner = currentHostVisuals()?.ownerById?.(id);
14030
+ return isPlainObject(owner?.data) ? owner.data : void 0;
14031
+ };
14032
+ const emitDecorationEvent = (type, decoration, point) => {
14033
+ const payload = {
14034
+ type,
14035
+ id: decoration.id,
14036
+ ...decoration.data ? { data: decoration.data } : {},
14037
+ clientX: point.clientX,
14038
+ clientY: point.clientY
14039
+ };
14040
+ for (const listener of Array.from(decorationListeners)) safeCall(() => listener(payload), void 0);
14041
+ };
14042
+ const leaveHoveredDecorations = (onlyId) => {
14043
+ const leaving = hoveredDecorations.filter((decoration) => onlyId === void 0 || decoration.id === onlyId);
14044
+ hoveredDecorations = hoveredDecorations.filter((decoration) => !leaving.includes(decoration));
14045
+ for (const decoration of leaving) emitDecorationEvent("hover-leave", decoration, lastPointer);
14046
+ };
14047
+ const attachDecorationPointer = () => {
14048
+ if (detachDecorationPointer || decorationListeners.size === 0) return;
14049
+ const host = resolveVisibleHost();
14050
+ if (!host) return;
14051
+ const onClick = (event) => {
14052
+ for (const id of decorationIdsAt(event.clientX, event.clientY)) emitDecorationEvent("click", {
14053
+ id,
14054
+ data: decorationData(id)
14055
+ }, event);
14056
+ };
14057
+ const onMove = (event) => {
14058
+ lastPointer = {
14059
+ clientX: event.clientX,
14060
+ clientY: event.clientY
14061
+ };
14062
+ const nextIds = decorationIdsAt(event.clientX, event.clientY);
14063
+ const leaving = hoveredDecorations.filter((decoration) => !nextIds.includes(decoration.id));
14064
+ const kept = hoveredDecorations.filter((decoration) => nextIds.includes(decoration.id));
14065
+ const entering = nextIds.filter((id) => !kept.some((decoration) => decoration.id === id)).map((id) => ({
14066
+ id,
14067
+ data: decorationData(id)
14068
+ }));
14069
+ hoveredDecorations = [...kept, ...entering];
14070
+ for (const decoration of leaving) emitDecorationEvent("hover-leave", decoration, event);
14071
+ for (const decoration of entering) emitDecorationEvent("hover-enter", decoration, event);
14072
+ };
14073
+ const onLeave = (event) => {
14074
+ lastPointer = {
14075
+ clientX: event.clientX,
14076
+ clientY: event.clientY
14077
+ };
14078
+ leaveHoveredDecorations();
14079
+ };
14080
+ host.addEventListener("click", onClick);
14081
+ host.addEventListener("pointermove", onMove);
14082
+ host.addEventListener("pointerleave", onLeave);
14083
+ detachDecorationPointer = () => {
14084
+ host.removeEventListener("click", onClick);
14085
+ host.removeEventListener("pointermove", onMove);
14086
+ host.removeEventListener("pointerleave", onLeave);
14087
+ leaveHoveredDecorations();
14088
+ detachDecorationPointer = null;
14089
+ };
14090
+ };
14091
+ const subscribeDecorationPointer = (listener) => {
14092
+ if (disposed) throw new Error("ui.decorations.subscribe called on a destroyed UI controller.");
14093
+ decorationListeners.add(listener);
14094
+ attachDecorationPointer();
14095
+ return () => {
14096
+ decorationListeners.delete(listener);
14097
+ if (decorationListeners.size === 0) detachDecorationPointer?.();
14098
+ };
14099
+ };
14100
+ documentResetHooks.push(() => {
14101
+ leaveHoveredDecorations();
14102
+ const current = currentHostVisuals();
14103
+ for (const [handle, owner] of Array.from(liveDecorations)) if (owner.visuals !== current || owner.generation !== current.generation()) handle.dispose();
14104
+ });
14105
+ activeEditorResetHooks.push(() => {
14106
+ detachDecorationPointer?.();
14107
+ attachDecorationPointer();
14108
+ });
14109
+ const createDecorationsHandle = (track) => ({
14110
+ subscribe: (listener) => {
14111
+ const unsubscribe = subscribeDecorationPointer(listener);
14112
+ track?.({ dispose: unsubscribe });
14113
+ return unsubscribe;
14114
+ },
14115
+ highlight: (id, options) => {
14116
+ if (disposed) throw new Error("ui.decorations.highlight called on a destroyed UI controller.");
14117
+ const visuals = currentHostVisuals();
14118
+ if (!visuals) throw new Error("ui.decorations.highlight requires a ready document.");
14119
+ const inner = visuals.highlight(id, options);
14120
+ let handleDisposed = false;
14121
+ const handle = {
14122
+ id: inner.id,
14123
+ replace: (targets) => inner.replace(targets),
14124
+ add: (targets) => inner.add(targets),
14125
+ clear: () => {
14126
+ if (handleDisposed) return;
14127
+ inner.clear();
14128
+ leaveHoveredDecorations(inner.id);
14129
+ },
14130
+ invalidate: () => inner.invalidate(),
14131
+ dispose: () => {
14132
+ if (handleDisposed) return;
14133
+ handleDisposed = true;
14134
+ liveDecorations.delete(handle);
14135
+ inner.dispose();
14136
+ leaveHoveredDecorations(inner.id);
14137
+ }
14138
+ };
14139
+ liveDecorations.set(handle, {
14140
+ visuals,
14141
+ generation: visuals.generation()
14142
+ });
14143
+ track?.(handle);
14144
+ return handle;
14145
+ }
14146
+ });
14147
+ const decorations = createDecorationsHandle();
13757
14148
  const viewport = {
13758
14149
  getRects: async (input) => {
13759
14150
  const initialHost = getHost();
@@ -14731,6 +15122,92 @@ function createSuperDocUI(options) {
14731
15122
  }
14732
15123
  }, 120);
14733
15124
  };
15125
+ const updateStyleToMatchSelection = (input) => {
15126
+ const mode = readDocumentMode();
15127
+ if (mode === "viewing") return failedReceipt("The document is read-only.", "DOCUMENT_READONLY");
15128
+ if (mode === "suggesting") return failedReceipt("A style definition cannot be changed as a tracked suggestion.", "PRECONDITION_FAILED");
15129
+ const sourceToken = contentToken();
15130
+ const readPositionKey = () => {
15131
+ const snapshot = readHostSelectionSnapshot();
15132
+ if (!snapshot) return null;
15133
+ return JSON.stringify({
15134
+ story: snapshot.story,
15135
+ tableCells: snapshot.tableCells,
15136
+ points: ["anchor", "focus"].map((side) => {
15137
+ const point = snapshot[side];
15138
+ return point ? [
15139
+ point.blockId,
15140
+ point.blockOffset,
15141
+ point.trackedBlockOffset,
15142
+ point.story,
15143
+ point.inlineAffinity,
15144
+ point.outsideContentControl,
15145
+ point.visualCaret
15146
+ ] : null;
15147
+ })
15148
+ });
15149
+ };
15150
+ const requestedPosition = readPositionKey();
15151
+ const stale = failedReceipt("The selection or document changed while reading its style.", "REVISION_MISMATCH");
15152
+ return readSelectionInfoFresh().then((selection) => {
15153
+ const selectionToken = selectionKey(selection);
15154
+ const isCurrent = () => !disposed && contentToken() === sourceToken && readPositionKey() === requestedPosition && selectionKey(state.selection) === selectionToken && readDocumentMode() === mode;
15155
+ if (!isCurrent()) return stale;
15156
+ const active = computeActiveParagraphStyle(selection, getStyleCatalog().cache).style;
15157
+ const story = storyLocatorSignature(selectionStory(selection));
15158
+ const keys = [styleCatalogReadKey({ includePreview: true }), ...selectionBlockIds(selection).map((id) => `node:${story}:${id}`)];
15159
+ const unreadable = failedReceipt("The selection formatting could not be read.", "PRECONDITION_FAILED");
15160
+ const run = (active) => {
15161
+ if (keys.some((key) => {
15162
+ const entry = asyncReads.get(key);
15163
+ return entry && (!entry.hasSettled || entry.token !== sourceToken || entry.failedToken === sourceToken);
15164
+ })) return unreadable;
15165
+ const styleId = input?.styleId ?? active.styleId;
15166
+ if (!styleId) return failedReceipt("Choose a style to update: the selection does not have a single paragraph style.", "INVALID_TARGET");
15167
+ const doc = getDoc();
15168
+ const readSelection = resolveDocOperation(doc, "format.readSelection");
15169
+ const apply = resolveDocOperation(doc, "styles.apply");
15170
+ if (!readSelection || !apply) return failedReceipt("Updating a style to match the selection is unavailable.");
15171
+ let reads;
15172
+ try {
15173
+ reads = [readSelection({
15174
+ selection: "current",
15175
+ keys: STYLE_MATCH_READ_KEYS,
15176
+ view: "effective"
15177
+ }), readSelection({
15178
+ selection: "current",
15179
+ keys: STYLE_MATCH_READ_KEYS,
15180
+ view: "direct"
15181
+ })];
15182
+ } catch {
15183
+ return unreadable;
15184
+ }
15185
+ return Promise.all(reads).then(([effective, direct]) => {
15186
+ if (!isCurrent()) return stale;
15187
+ const patch = buildStyleMatchPatch(effective, direct, { selectionUsesStyle: !active.mixed && active.styleId === styleId });
15188
+ if (Object.keys(patch).length === 0) return failedReceipt("The selection already matches the style.", "NO_OP");
15189
+ const fallback = failedReceipt("Updating the style failed.");
15190
+ return safeCall(() => settleWorkflowReceipt(apply({
15191
+ target: {
15192
+ scope: "style",
15193
+ styleId,
15194
+ channel: "run"
15195
+ },
15196
+ patch
15197
+ }), fallback), fallback);
15198
+ }, () => unreadable);
15199
+ };
15200
+ const pending = keys.flatMap((key) => {
15201
+ const entry = asyncReads.get(key);
15202
+ return entry?.inflightToken === sourceToken && entry.settled ? [entry.settled] : [];
15203
+ });
15204
+ if (pending.length === 0) return run(active);
15205
+ return Promise.all(pending).then(() => {
15206
+ if (!isCurrent()) return stale;
15207
+ return run(computeActiveParagraphStyle(selection, getStyleCatalog().cache).style);
15208
+ });
15209
+ });
15210
+ };
14734
15211
  const stylesSub = sliceHandle((s) => s.styles);
14735
15212
  const stylesSnap = snapshotHandle(stylesSub);
14736
15213
  const styles = {
@@ -14743,7 +15220,8 @@ function createSuperDocUI(options) {
14743
15220
  return readStyleCatalogLive(options).value;
14744
15221
  },
14745
15222
  getQuickGallery: () => stylesSub.get().quickGallery,
14746
- getActiveParagraphStyle: () => computeActiveParagraphStyle(state.selection, getStyleCatalog().cache).style
15223
+ getActiveParagraphStyle: () => computeActiveParagraphStyle(state.selection, getStyleCatalog().cache).style,
15224
+ updateToMatchSelection: (input) => updateStyleToMatchSelection(input)
14747
15225
  };
14748
15226
  const openWatermarkSurface = hostMethod(superdoc, "openSurface");
14749
15227
  const watermark = createWatermarkController({
@@ -14758,6 +15236,7 @@ function createSuperDocUI(options) {
14758
15236
  const createScope = () => {
14759
15237
  const scopeUnsubs = [];
14760
15238
  return {
15239
+ decorations: createDecorationsHandle((handle) => scopeUnsubs.push(() => handle.dispose())),
14761
15240
  select: (selector, equality) => {
14762
15241
  const sub = select(selector, equality);
14763
15242
  return {
@@ -14778,6 +15257,9 @@ function createSuperDocUI(options) {
14778
15257
  if (disposed) return;
14779
15258
  watermark.destroy();
14780
15259
  clearContentControlHighlight();
15260
+ for (const handle of Array.from(liveDecorations.keys())) handle.dispose();
15261
+ detachDecorationPointer?.();
15262
+ decorationListeners.clear();
14781
15263
  disposed = true;
14782
15264
  completedFallbackSearch = null;
14783
15265
  if (searchRefreshTimer !== null) clearTimeout(searchRefreshTimer);
@@ -14860,6 +15342,7 @@ function createSuperDocUI(options) {
14860
15342
  zoom,
14861
15343
  document: documentHandle,
14862
15344
  viewport,
15345
+ decorations,
14863
15346
  metadata,
14864
15347
  tables,
14865
15348
  search,
@@ -15150,6 +15633,12 @@ Object.defineProperty(exports, "isPlainObject", {
15150
15633
  return isPlainObject;
15151
15634
  }
15152
15635
  });
15636
+ Object.defineProperty(exports, "isRecord", {
15637
+ enumerable: true,
15638
+ get: function() {
15639
+ return isRecord;
15640
+ }
15641
+ });
15153
15642
  Object.defineProperty(exports, "isSyntheticTrackedChangeCommentLaneItem", {
15154
15643
  enumerable: true,
15155
15644
  get: function() {