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
@@ -2458,6 +2458,69 @@ function createDialogWorkflow(editor, options, onSuccess) {
2458
2458
  };
2459
2459
  }
2460
2460
  //#endregion
2461
+ //#region src/public/ui/style-match-patch.ts
2462
+ var STYLE_MATCH_KEYS = [
2463
+ ["bold", "bold"],
2464
+ ["italic", "italic"],
2465
+ ["underline", "underline"],
2466
+ ["color", "color"],
2467
+ ["fontSize", "fontSize"],
2468
+ ["rFonts", "fontFamily"]
2469
+ ];
2470
+ var STYLE_MATCH_READ_KEYS = [...STYLE_MATCH_KEYS.map(([readKey]) => readKey), "rStyle"];
2471
+ function buildStyleMatchPatch(effective, direct, options) {
2472
+ const patch = {};
2473
+ const characterStyle = direct.properties.rStyle;
2474
+ const hasCharacterStyle = characterStyle != null && characterStyle.state !== "unset";
2475
+ for (const [readKey, styleKey] of STYLE_MATCH_KEYS) {
2476
+ const read = effective.properties[readKey];
2477
+ if (read?.state !== "uniform") continue;
2478
+ if (options.selectionUsesStyle && !hasCharacterStyle && direct.properties[readKey]?.state === "unset") continue;
2479
+ patch[styleKey] = toStyleValue(readKey, read.value);
2480
+ }
2481
+ return patch;
2482
+ }
2483
+ function toStyleValue(key, value) {
2484
+ switch (key) {
2485
+ case "bold":
2486
+ case "italic": return value === true;
2487
+ case "underline": return underlineValue(value);
2488
+ case "color": return typeof value === "string" && value.length > 0 ? { val: value.replace(/^#/, "") } : null;
2489
+ case "fontSize": return typeof value === "number" && Number.isFinite(value) ? Math.round(value * 2) : null;
2490
+ case "rFonts": return fontsValue(value);
2491
+ default: return null;
2492
+ }
2493
+ }
2494
+ function underlineValue(value) {
2495
+ if (value === true) return { val: "single" };
2496
+ if (!value || typeof value !== "object") return { val: "none" };
2497
+ const underline = value;
2498
+ const result = { val: typeof underline.style === "string" && underline.style ? underline.style : "single" };
2499
+ if (typeof underline.color === "string") result.color = underline.color;
2500
+ if (typeof underline.themeColor === "string") result.themeColor = underline.themeColor;
2501
+ return result;
2502
+ }
2503
+ var FONT_SLOTS = {
2504
+ ascii: "ascii",
2505
+ hAnsi: "hAnsi",
2506
+ eastAsia: "eastAsia",
2507
+ cs: "cs",
2508
+ asciiTheme: "asciiTheme",
2509
+ hAnsiTheme: "hAnsiTheme",
2510
+ eastAsiaTheme: "eastAsiaTheme",
2511
+ csTheme: "cstheme",
2512
+ hint: "hint"
2513
+ };
2514
+ function fontsValue(value) {
2515
+ if (!value || typeof value !== "object") return null;
2516
+ const result = {};
2517
+ for (const [slot, styleSlot] of Object.entries(FONT_SLOTS)) {
2518
+ const font = value[slot];
2519
+ if (typeof font === "string" && font.length > 0) result[styleSlot] = font;
2520
+ }
2521
+ return Object.keys(result).length > 0 ? result : null;
2522
+ }
2523
+ //#endregion
2461
2524
  //#region src/public/ui/format-painter-helpers.js
2462
2525
  /**
2463
2526
  * Maps SDRunProps field names to InlineRunPatch field names.
@@ -3628,6 +3691,25 @@ function getCommandDescriptor(id) {
3628
3691
  }
3629
3692
  //#endregion
3630
3693
  //#region src/public/ui/entity-at.ts
3694
+ function readDecorationProviders(node) {
3695
+ const encoded = node.getAttribute("data-superdoc-extension-decorations");
3696
+ if (encoded) try {
3697
+ const parsed = JSON.parse(encoded);
3698
+ if (Array.isArray(parsed)) return parsed.filter((id) => typeof id === "string");
3699
+ } catch {
3700
+ return [];
3701
+ }
3702
+ const single = node.getAttribute("data-superdoc-extension-decoration");
3703
+ return single ? [single] : [];
3704
+ }
3705
+ function collectDecorationProvidersFromChain(start, stopAt) {
3706
+ const providers = /* @__PURE__ */ new Set();
3707
+ for (let element = start; element; element = element.parentElement) {
3708
+ for (const provider of readDecorationProviders(element)) providers.add(provider);
3709
+ if (element === stopAt) break;
3710
+ }
3711
+ return [...providers];
3712
+ }
3631
3713
  function parseCommaSeparatedIds(value) {
3632
3714
  return value.split(",").map((id) => id.trim()).filter(Boolean);
3633
3715
  }
@@ -3699,6 +3781,16 @@ function collectEntityHitsFromChain(start, stopAt) {
3699
3781
  for (const hit of pendingStory) hit.story = layoutStory;
3700
3782
  pendingStory = [];
3701
3783
  }
3784
+ for (const decorationProvider of readDecorationProviders(node)) {
3785
+ const key = `decoration:${decorationProvider}`;
3786
+ if (!seen.has(key)) {
3787
+ seen.add(key);
3788
+ hits.push({
3789
+ type: "decoration",
3790
+ id: decorationProvider
3791
+ });
3792
+ }
3793
+ }
3702
3794
  const citationId = node.getAttribute("data-citation-id");
3703
3795
  if (citationId) {
3704
3796
  const key = `citation:${citationId}`;
@@ -6089,6 +6181,7 @@ function createSuperDocUI(options) {
6089
6181
  pointerSelecting: false,
6090
6182
  keyboardSelecting: false
6091
6183
  };
6184
+ const paintedInlineTargets = /* @__PURE__ */ new WeakSet();
6092
6185
  let painterCaptureEpoch = 0;
6093
6186
  let painterCommandDoc = null;
6094
6187
  const painterModeListeners = /* @__PURE__ */ new Set();
@@ -6185,7 +6278,7 @@ function createSuperDocUI(options) {
6185
6278
  const readHostSelectionSnapshot = () => {
6186
6279
  const handles = safeCall(() => hostMethod(getHost(), "getHandles")?.() ?? null, null);
6187
6280
  const editing = isPlainObject(handles) ? handles.editing : void 0;
6188
- const getSnapshot = hostMethod(isPlainObject(editing) ? editing.selection : void 0, "getSnapshot");
6281
+ const getSnapshot = hostMethod(isPlainObject(editing) ? editing.selection : void 0, "getSnapshot") ?? hostMethod(isPlainObject(handles) ? handles.selection : void 0, "getSnapshot");
6189
6282
  if (!getSnapshot) return null;
6190
6283
  const snapshot = safeCall(() => getSnapshot(), null);
6191
6284
  return snapshot && typeof snapshot === "object" ? snapshot : null;
@@ -6399,7 +6492,7 @@ function createSuperDocUI(options) {
6399
6492
  return left.id === right.id && left.paintedEntityId === right.paintedEntityId && storyLocatorSignature(left.story) === storyLocatorSignature(right.story);
6400
6493
  };
6401
6494
  const hasPendingTrackChangeRevealFocus = (focus) => Array.from(pendingTrackChangeRevealFocuses).some((pendingFocus) => explicitActiveChangesEqual(pendingFocus, focus));
6402
- const mirrorTrackedChangeFocusToHost = (next) => {
6495
+ const mirrorReviewFocusToHost = (entityType, next) => {
6403
6496
  const readHandles = hostMethod(getHost(), "getHandles");
6404
6497
  if (!readHandles) return;
6405
6498
  const handles = safeCall(() => readHandles(), null);
@@ -6410,15 +6503,15 @@ function createSuperDocUI(options) {
6410
6503
  if (!setActiveReviewTarget) return;
6411
6504
  const layout = isPlainObject(handles.layout) ? handles.layout : void 0;
6412
6505
  safeCall(() => setActiveReviewTarget({
6413
- entityType: "trackedChange",
6506
+ entityType,
6414
6507
  entityId: next.id,
6415
6508
  ...next.paintedEntityId ? { paintedEntityId: next.paintedEntityId } : {},
6416
6509
  origin: "panel",
6417
6510
  layoutEpoch: typeof layout?.generation === "number" ? layout.generation : 0,
6418
- story: next.story ?? {
6511
+ ...entityType === "trackedChange" ? { story: next.story ?? {
6419
6512
  kind: "story",
6420
6513
  storyType: "body"
6421
- }
6514
+ } } : {}
6422
6515
  }), null);
6423
6516
  return;
6424
6517
  }
@@ -6426,10 +6519,22 @@ function createSuperDocUI(options) {
6426
6519
  const clearActiveReviewTarget = hostMethod(review, "clearActiveReviewTarget");
6427
6520
  if (!getActiveReviewTarget || !clearActiveReviewTarget) return;
6428
6521
  const activeTarget = safeCall(() => getActiveReviewTarget(), null);
6429
- if (isPlainObject(activeTarget) && activeTarget.entityType === "trackedChange") safeCall(() => clearActiveReviewTarget(), void 0);
6522
+ if (isPlainObject(activeTarget) && activeTarget.entityType === entityType) safeCall(() => clearActiveReviewTarget(), void 0);
6523
+ };
6524
+ const mirrorTrackedChangeFocusToHost = (next) => mirrorReviewFocusToHost("trackedChange", next);
6525
+ const readReviewFocus = () => ({
6526
+ change: explicitActiveChange,
6527
+ commentId: explicitActiveCommentId
6528
+ });
6529
+ const restoreReviewFocus = (previous) => {
6530
+ setExplicitActiveChange(previous.change, { invalidateQueuedNavigation: false });
6531
+ explicitActiveCommentId = previous.commentId;
6532
+ if (previous.commentId) mirrorReviewFocusToHost("comment", { id: previous.commentId });
6533
+ else mirrorTrackedChangeFocusToHost(previous.change);
6430
6534
  };
6431
6535
  const setExplicitActiveChange = (next, options) => {
6432
6536
  explicitActiveChange = next;
6537
+ if (next) explicitActiveCommentId = null;
6433
6538
  explicitActiveChangeRevision += 1;
6434
6539
  trackChangeRevealInvalidation += 1;
6435
6540
  if (options?.invalidateQueuedNavigation !== false) queuedTrackChangeNavigationInvalidation += 1;
@@ -7099,7 +7204,7 @@ function createSuperDocUI(options) {
7099
7204
  failureCount: retryingSameToken ? prev.failureCount ?? 0 : 0,
7100
7205
  retryAtMs: 0
7101
7206
  });
7102
- Promise.resolve(raw).then((resolved) => {
7207
+ const settled = Promise.resolve(raw).then((resolved) => {
7103
7208
  const entry = asyncReads.get(key);
7104
7209
  if (!entry || entry.inflightToken !== token) return;
7105
7210
  asyncReads.set(key, {
@@ -7138,6 +7243,7 @@ function createSuperDocUI(options) {
7138
7243
  const retryAtMs = Date.now() + retryDelayMs;
7139
7244
  asyncReads.set(key, {
7140
7245
  ...entry,
7246
+ settled: void 0,
7141
7247
  inflightToken: null,
7142
7248
  failedToken: token,
7143
7249
  failureCount,
@@ -7145,6 +7251,8 @@ function createSuperDocUI(options) {
7145
7251
  });
7146
7252
  scheduleAsyncReadFailureRetry(retryAtMs);
7147
7253
  });
7254
+ const entry = asyncReads.get(key);
7255
+ if (entry?.inflightToken === token) entry.settled = settled;
7148
7256
  return null;
7149
7257
  };
7150
7258
  /**
@@ -7486,15 +7594,53 @@ function createSuperDocUI(options) {
7486
7594
  * Explicit whole-table changes have a complete local source range and also
7487
7595
  * seed clears, so a delayed worker read cannot retain a cleared table target.
7488
7596
  */
7489
- const refreshSelectionFromHost = (hostSelectionSnapshot, afterTypingPaint = false, wholeTableSelectionChanged = false) => {
7597
+ function captureWholeTableParagraphSeed(host, seed, nativeSelection) {
7598
+ const editor = getEditor();
7599
+ const rawLifetime = safeCall(() => hostMethod(host, "getForegroundInteractionLifetime")?.() ?? null, null);
7600
+ const lifetime = hasAbortedFlag(rawLifetime) ? rawLifetime : null;
7601
+ const mount = safeCall(() => hostMethod(host, "getMountedHandle")?.() ?? null, null);
7602
+ const mode = readDocumentMode();
7603
+ 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;
7604
+ if (nativeSelection) {
7605
+ const presentation = safeCall(() => hostMethod(host, "readAppliedSelectionPresentation")?.(), null);
7606
+ const applied = normalizeSelectionInfo(isPlainObject(presentation) ? presentation.selection : null);
7607
+ if (readHostSelectionSnapshot() !== nativeSelection || !isNonCollapsedHostSelection(nativeSelection) || !applied || selectionSignature(selectionSliceFromInfo(applied, "ready")) !== selectionSignature(selectionSliceFromInfo(seed, "ready")) || JSON.stringify(applied.tableSelection) !== JSON.stringify(seed.tableSelection)) return false;
7608
+ const snapshot = nativeSelection;
7609
+ const first = seed.target.segments[0]?.blockId, last = seed.target.segments.at(-1)?.blockId;
7610
+ const matches = (point, blockId) => !!blockId && isPlainObject(point) && point.blockId === blockId && storyLocatorSignature(point.story ?? snapshot.story) === storyLocatorSignature(seed.target?.story);
7611
+ if (!(matches(snapshot.anchor, first) && matches(snapshot.focus, last) || matches(snapshot.focus, first) && matches(snapshot.anchor, last))) return false;
7612
+ }
7613
+ wholeTableParagraphSeed = {
7614
+ editor,
7615
+ host,
7616
+ doc: editor.doc,
7617
+ mount,
7618
+ lifetime,
7619
+ sourceToken: contentToken(),
7620
+ mode,
7621
+ info: seed,
7622
+ nativeSelection
7623
+ };
7624
+ return true;
7625
+ }
7626
+ const refreshSelectionFromHost = (hostSelectionSnapshot, afterTypingPaint = false, wholeTableSelectionChanged = false, initialTableSelection = null) => {
7627
+ const host = getHost();
7490
7628
  const foreground = foregroundMutationState();
7629
+ if (isNonCollapsedHostSelection(hostSelectionSnapshot) && readHostSelectionSnapshot() === hostSelectionSnapshot) {
7630
+ const seed = normalizeSelectionInfo(safeCall(() => hostMethod(host, "readLiveSelectionSyncSnapshot")?.(), null));
7631
+ if (seed && !seed.selectionTarget && captureWholeTableParagraphSeed(host, seed, hostSelectionSnapshot)) asyncReads.set("selection", {
7632
+ token: asyncReads.get("selection")?.token ?? null,
7633
+ value: seed,
7634
+ hasSettled: true,
7635
+ inflightToken: null
7636
+ });
7637
+ }
7491
7638
  if (isNonCollapsedHostSelection(hostSelectionSnapshot) && !(foreground && foreground.active > 0)) {
7492
7639
  typedCaretValidationHeld = false;
7493
7640
  pendingSelectionSeedValidationToken = null;
7494
7641
  issueAsyncRead("selection", selectionReadToken(), selectionCurrentRun, normalizeSelectionInfo);
7495
7642
  return;
7496
7643
  }
7497
- const host = getHost();
7498
7644
  const read = host?.readLiveSelectionSyncSnapshot;
7499
7645
  if (typeof read !== "function") return;
7500
7646
  const seed = normalizeSelectionInfo(safeCall(() => read.call(host), null));
@@ -7503,21 +7649,7 @@ function createSuperDocUI(options) {
7503
7649
  typedCaretValidationHeld = false;
7504
7650
  pendingSelectionSeedValidationToken = null;
7505
7651
  wholeTableParagraphSeed = null;
7506
- const editor = getEditor();
7507
- const rawLifetime = safeCall(() => hostMethod(host, "getForegroundInteractionLifetime")?.() ?? null, null);
7508
- const lifetime = hasAbortedFlag(rawLifetime) ? rawLifetime : null;
7509
- const mount = safeCall(() => hostMethod(host, "getMountedHandle")?.() ?? null, null);
7510
- const mode = readDocumentMode();
7511
- 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 = {
7512
- editor,
7513
- host,
7514
- doc: editor.doc,
7515
- mount,
7516
- lifetime,
7517
- sourceToken: contentToken(),
7518
- mode,
7519
- info: seed
7520
- };
7652
+ captureWholeTableParagraphSeed(host, seed, initialTableSelection);
7521
7653
  const token = selectionReadToken();
7522
7654
  asyncReads.set("selection", {
7523
7655
  token,
@@ -7675,7 +7807,7 @@ function createSuperDocUI(options) {
7675
7807
  const directory = asyncReads.get("comments");
7676
7808
  const directoryItems = directory?.token === contentToken() && directory.hasSettled && Array.isArray(directory.value) ? directory.value : null;
7677
7809
  const activeValidationItems = directoryLeaseCounts.comments > 0 ? directoryItems : items;
7678
- if (explicitActiveCommentId && activeValidationItems && !activeValidationItems.some((item) => readEntityId(item) === explicitActiveCommentId)) explicitActiveCommentId = null;
7810
+ if (explicitActiveCommentId && activeValidationItems && (activeValidationItems !== items || listStatus === "ready")) explicitActiveCommentId = resolveActiveCommentId(activeValidationItems, explicitActiveCommentId);
7679
7811
  const activeId = explicitActiveCommentId ?? activeIds[0] ?? null;
7680
7812
  return {
7681
7813
  status: combineStatus(listStatus, selection.status),
@@ -8320,12 +8452,13 @@ function createSuperDocUI(options) {
8320
8452
  ];
8321
8453
  function effectiveInlineUniformityInput(selection) {
8322
8454
  const cellTarget = currentCellSelectionTarget();
8323
- const target = cellTarget ?? selection.selectionTarget;
8455
+ const paintedTarget = cellTarget ? null : currentUnrepresentableSelectionTarget(selection);
8456
+ const target = cellTarget ?? paintedTarget ?? selection.selectionTarget;
8324
8457
  if (!target) return null;
8325
8458
  const tableCellSelection = cellTarget ? tableCellSelectionForTarget(cellTarget) : null;
8326
8459
  return {
8327
8460
  target,
8328
- offsetSpace: "selection",
8461
+ offsetSpace: cellTarget || paintedTarget ? "selection" : "kernel",
8329
8462
  ...tableCellSelection ? { tableCellSelection } : {}
8330
8463
  };
8331
8464
  }
@@ -8463,7 +8596,11 @@ function createSuperDocUI(options) {
8463
8596
  const getNodeById = hostMethod(doc, "getNodeById");
8464
8597
  const raw = await Promise.resolve(getNode ? getNode(address) : getNodeById?.({
8465
8598
  nodeId: address["nodeId"],
8466
- nodeType: address["nodeType"]
8599
+ nodeType: address["nodeType"],
8600
+ story: address["story"] ?? {
8601
+ kind: "story",
8602
+ storyType: "body"
8603
+ }
8467
8604
  }));
8468
8605
  return isPlainObject(raw) ? raw : void 0;
8469
8606
  };
@@ -9009,7 +9146,8 @@ function createSuperDocUI(options) {
9009
9146
  const token = contentToken();
9010
9147
  const read = readAsync(key, token, () => isBodyStory ? doc.getNodeById({
9011
9148
  nodeId: blockId,
9012
- nodeType: "paragraph"
9149
+ nodeType: "paragraph",
9150
+ story
9013
9151
  }) : doc.getNode(paragraphTarget(blockId, story)), (raw) => raw && typeof raw === "object" ? raw : null);
9014
9152
  const entry = asyncReads.get(key);
9015
9153
  return {
@@ -9218,7 +9356,11 @@ function createSuperDocUI(options) {
9218
9356
  const isBodyStory = !story || story.storyType === "body";
9219
9357
  const raw = story && typeof doc.getNode === "function" ? await doc.getNode(paragraphTarget(blockId, story)) : isBodyStory && typeof doc.getNodeById === "function" ? await doc.getNodeById({
9220
9358
  nodeId: blockId,
9221
- nodeType: "paragraph"
9359
+ nodeType: "paragraph",
9360
+ story: {
9361
+ kind: "story",
9362
+ storyType: "body"
9363
+ }
9222
9364
  }) : null;
9223
9365
  return readParagraphIndentationFromResult(raw && typeof raw === "object" ? raw : null);
9224
9366
  } catch {
@@ -9895,14 +10037,15 @@ function createSuperDocUI(options) {
9895
10037
  const tableSelection = isPlainObject(handles) ? handles.selection : void 0;
9896
10038
  const subscribeWholeTable = hostMethod(tableSelection, "subscribeWholeTable");
9897
10039
  if (subscribeWholeTable) {
9898
- const refreshWholeTable = () => {
10040
+ const refreshWholeTable = (initial = false) => {
10041
+ const native = initial ? readHostSelectionSnapshot() : null;
9899
10042
  selectionEpoch += 1;
9900
- refreshSelectionFromHost(void 0, false, true);
10043
+ refreshSelectionFromHost(void 0, false, true, native);
9901
10044
  recompute("host-selection");
9902
10045
  };
9903
- const detach = subscribeWholeTable(refreshWholeTable);
10046
+ const detach = subscribeWholeTable(() => refreshWholeTable());
9904
10047
  if (typeof detach === "function") unsubscribeCallbacks.push(() => detach());
9905
- if (hostMethod(tableSelection, "getWholeTableNodeId")?.()) refreshWholeTable();
10048
+ if (hostMethod(tableSelection, "getWholeTableNodeId")?.()) refreshWholeTable(true);
9906
10049
  }
9907
10050
  } catch {
9908
10051
  currentHostSelectionSource = null;
@@ -9923,10 +10066,35 @@ function createSuperDocUI(options) {
9923
10066
  };
9924
10067
  let trackedChangeNavigationInFlight = 0;
9925
10068
  let lastPublishedHostReviewTarget;
10069
+ 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);
9926
10070
  const syncTrackedChangeFocusFromHostReviewTarget = (rawTarget, rawSnapshot) => {
9927
10071
  const rejection = (rawSnapshot && typeof rawSnapshot === "object" ? rawSnapshot : null)?.lastInteractionRejection;
9928
- if (rawTarget == null && rejection && typeof rejection === "object" && rejection.code === "review-target-invalidated" && rejection.detail === "not-painted" && explicitActiveChange) return false;
10072
+ if (rawTarget == null && rejection && typeof rejection === "object" && rejection.code === "review-target-invalidated" && rejection.detail === "not-painted" && (explicitActiveChange || explicitActiveCommentId)) return false;
9929
10073
  const target = rawTarget && typeof rawTarget === "object" ? rawTarget : null;
10074
+ if (target?.entityType === "comment" && typeof target.entityId === "string" && target.entityId.length > 0) {
10075
+ const commentId = resolveActiveCommentId(state?.comments.items ?? [], target.entityId) ?? target.entityId;
10076
+ const republished = isRepublishedHostReviewTarget(rawTarget, lastPublishedHostReviewTarget?.raw);
10077
+ lastPublishedHostReviewTarget = {
10078
+ raw: rawTarget,
10079
+ focus: null,
10080
+ origin: target.origin
10081
+ };
10082
+ if (republished || explicitActiveCommentId === commentId && !explicitActiveChange) return false;
10083
+ setExplicitActiveChange(null);
10084
+ explicitActiveCommentId = commentId;
10085
+ return true;
10086
+ }
10087
+ const previousTarget = lastPublishedHostReviewTarget?.raw;
10088
+ if (!target && isPlainObject(previousTarget) && previousTarget.entityType === "comment") {
10089
+ lastPublishedHostReviewTarget = {
10090
+ raw: rawTarget,
10091
+ focus: null,
10092
+ origin: void 0
10093
+ };
10094
+ if (!explicitActiveCommentId) return false;
10095
+ explicitActiveCommentId = null;
10096
+ return true;
10097
+ }
9930
10098
  let next = null;
9931
10099
  if (target?.entityType === "trackedChange" && typeof target.entityId === "string" && target.entityId.length > 0) {
9932
10100
  const targetStory = target.story && typeof target.story === "object" ? target.story : void 0;
@@ -9951,7 +10119,7 @@ function createSuperDocUI(options) {
9951
10119
  }
9952
10120
  if (rawTarget == null && next == null && explicitActiveChange != null && hasPendingTrackChangeRevealFocus(explicitActiveChange)) return false;
9953
10121
  const previousPublication = lastPublishedHostReviewTarget;
9954
- if (previousPublication && (rawTarget === previousPublication.raw || target?.origin === "panel" && previousPublication.origin === "panel" && next && previousPublication.focus && explicitActiveChangesEqual(previousPublication.focus, next))) return false;
10122
+ if (previousPublication && (isRepublishedHostReviewTarget(rawTarget, previousPublication.raw) || target?.origin === "panel" && previousPublication.origin === "panel" && next && previousPublication.focus && explicitActiveChangesEqual(previousPublication.focus, next))) return false;
9955
10123
  lastPublishedHostReviewTarget = {
9956
10124
  raw: rawTarget,
9957
10125
  focus: next,
@@ -10329,11 +10497,16 @@ function createSuperDocUI(options) {
10329
10497
  * ordinary/insertion selections are visible-space, while a selection that
10330
10498
  * reaches painted deleted text is projected in tracked space by the host.
10331
10499
  *
10332
- * Cross-block coverage must come from the model-backed tracked target;
10333
- * intermediate block lengths cannot be inferred from the endpoints.
10500
+ * Cross-block and object-containing coverage use the model-backed tracked
10501
+ * target when the explicit text selection cannot represent the range.
10334
10502
  */
10335
10503
  function trackDecisionRangeFromSelection(selection) {
10336
10504
  if (selection.empty) return null;
10505
+ if (!selection.selectionTarget && selection.trackedTarget) return {
10506
+ kind: "range",
10507
+ coordinateSpace: "tracked",
10508
+ range: selection.trackedTarget
10509
+ };
10337
10510
  const explicit = selection.selectionTarget;
10338
10511
  const start = explicit?.start;
10339
10512
  const end = explicit?.end;
@@ -10487,7 +10660,7 @@ function createSuperDocUI(options) {
10487
10660
  kind: "blocked",
10488
10661
  reason: SUPERDOC_UI_REASONS.operationUnavailable
10489
10662
  };
10490
- if (!selection.selectionTarget) return null;
10663
+ if (!selection.selectionTarget && !selection.trackedTarget && selection.target?.kind !== "text") return null;
10491
10664
  const target = trackDecisionRangeFromSelection(selection);
10492
10665
  if (!target && route() !== "id") return {
10493
10666
  kind: "blocked",
@@ -10827,25 +11000,35 @@ function createSuperDocUI(options) {
10827
11000
  pendingCommandSettlement = settledResults.length ? Promise.all(settledResults).then((results) => combineCommandResults(results)) : null;
10828
11001
  return combineCommandResults(immediateResults);
10829
11002
  };
10830
- /**
10831
- * Call an inline `format.*` operation against the live selection. Mirrors
10832
- * `callEditorMutation` (suggesting mode adds `changeMode: 'tracked'`), and
10833
- * when the target is the host's own editable `selectionTarget` it also
10834
- * carries the PRIVATE V2 option `offsetSpace: 'selection'` (browser
10835
- * selection offsets count an inline object as one caret position). The
10836
- * option is not part of public `MutationOptions`; this is the same cast
10837
- * pattern the delete/replace callers use.
10838
- */
10839
11003
  function currentCellSelectionTarget() {
11004
+ return captureInputSelectionTarget(true);
11005
+ }
11006
+ function captureInputSelectionTarget(requireTableCells = false) {
10840
11007
  const handles = safeCall(() => hostMethod(getHost(), "getHandles")?.(), null);
10841
11008
  const selection = isPlainObject(handles) ? handles.selection : void 0;
10842
11009
  const held = hostMethod(selection, "getSnapshot")?.();
10843
- if (!isPlainObject(held) || !held.tableCells) return null;
11010
+ if (requireTableCells && (!isPlainObject(held) || !held.tableCells)) return null;
10844
11011
  const resolved = hostMethod(selection, "toSelectionTarget")?.();
10845
11012
  return isPlainObject(resolved) && resolved.kind === "ok" && resolved.mode === "range" ? capturedTableCellTarget(resolved.target) : null;
10846
11013
  }
11014
+ function currentUnrepresentableSelectionTarget(selection) {
11015
+ if (selection.empty || selection.selectionTarget || selection.target?.kind !== "text") return null;
11016
+ return heldPaintedTextSelectionTarget();
11017
+ }
11018
+ function heldPaintedTextSelectionTarget() {
11019
+ const target = captureInputSelectionTarget();
11020
+ if (target) paintedInlineTargets.add(target);
11021
+ return target;
11022
+ }
10847
11023
  function resolveInlineCommandTarget(selection) {
10848
- return currentCellSelectionTarget() ?? resolveInlineSelectionTarget(selection);
11024
+ const cells = currentCellSelectionTarget();
11025
+ if (cells) return cells;
11026
+ if (!selection.empty && !selection.selectionTarget && selection.target?.kind === "text") {
11027
+ const snapshot = readHostSelectionSnapshot();
11028
+ const painted = currentUnrepresentableSelectionTarget(selection);
11029
+ if (painted) return snapshot && appliedSelectionPresentation(selection) && readHostSelectionSnapshot() === snapshot ? painted : null;
11030
+ }
11031
+ return resolveInlineSelectionTarget(selection);
10849
11032
  }
10850
11033
  function tableCellSelectionForTarget(target) {
10851
11034
  const cellTarget = currentCellSelectionTarget();
@@ -10865,10 +11048,15 @@ function createSuperDocUI(options) {
10865
11048
  offsetSpace: "selection",
10866
11049
  tableCellSelection
10867
11050
  });
11051
+ const paintedTarget = input?.target;
11052
+ if (paintedTarget !== null && typeof paintedTarget === "object" && (paintedInlineTargets.has(paintedTarget) || paintedTarget.coordinateSpace === "tracked")) return op(input, {
11053
+ ...options,
11054
+ offsetSpace: "selection"
11055
+ });
10868
11056
  if (!selection.selectionTarget) return options ? op(input, options) : op(input);
10869
11057
  return op(input, {
10870
11058
  ...options,
10871
- offsetSpace: "selection"
11059
+ offsetSpace: "kernel"
10872
11060
  });
10873
11061
  };
10874
11062
  /** Build the `format.paragraph.*` / `styles.paragraph.*` input for a block. */
@@ -11097,13 +11285,16 @@ function createSuperDocUI(options) {
11097
11285
  };
11098
11286
  pendingCommandSettlement = (async () => {
11099
11287
  const plans = await Promise.all(blockIds.map((blockId) => planBlock(blockId).catch(() => null)));
11100
- if (plans.length === blockIds.length && plans.every((plan) => plan?.kind === "list") && readDocumentMode() !== "suggesting") {
11288
+ const allListItems = plans.length === blockIds.length && plans.every((plan) => plan?.kind === "list");
11289
+ const preserveFirstItemLevel = blockIds.length === 1 && story.storyType === "body";
11290
+ if (allListItems && (readDocumentMode() !== "suggesting" || preserveFirstItemLevel)) {
11101
11291
  const rangeRoute = mode === "indent" ? "lists.indentRange" : "lists.outdentRange";
11102
11292
  const rangeOp = resolveDocOperation(doc, rangeRoute);
11103
11293
  if (rangeOp) try {
11104
11294
  return await settleOperationResult(callEditorMutation(rangeRoute, rangeOp, {
11105
11295
  paraIds: blockIds,
11106
- story
11296
+ story,
11297
+ ...preserveFirstItemLevel ? { preserveFirstItemLevel: true } : {}
11107
11298
  })).settled;
11108
11299
  } catch {
11109
11300
  return false;
@@ -11643,23 +11834,32 @@ function createSuperDocUI(options) {
11643
11834
  const record = readLinkPayloadRecord(payload);
11644
11835
  return Boolean(readLinkPayloadTarget(payload) || isPlainObject(record.hyperlinkTarget) || isPlainObject(record.textTarget));
11645
11836
  };
11646
- const currentWholeTableParagraphSelection = () => {
11837
+ const currentWholeTableParagraphOwner = () => {
11647
11838
  const owner = wholeTableParagraphSeed, host = getHost();
11648
- 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;
11839
+ 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;
11649
11840
  const tableId = owner.info.tableSelection?.tableId;
11650
11841
  const activeTable = safeCall(() => {
11651
11842
  const handles = hostMethod(host, "getHandles")?.();
11652
11843
  return hostMethod(isPlainObject(handles) ? handles.selection : void 0, "getWholeTableNodeId")?.();
11653
11844
  }, null);
11654
- if (!tableId || activeTable !== tableId || state.selection.tableSelection?.type !== "table" || state.selection.tableSelection.tableId !== tableId || selectionSignature(state.selection) !== selectionSignature(selectionSliceFromInfo(owner.info, "ready"))) return false;
11845
+ if (!tableId || activeTable !== tableId || state.selection.tableSelection?.type !== "table" || state.selection.tableSelection.tableId !== tableId || selectionSignature(state.selection) !== selectionSignature(selectionSliceFromInfo(owner.info, "ready"))) return null;
11655
11846
  const applied = appliedSelectionPresentation(state.selection);
11656
11847
  const info = normalizeSelectionInfo(applied?.selection);
11657
- return !!info && selectionInfoMatchesHost(info) && JSON.stringify(info.tableSelection) === JSON.stringify(owner.info.tableSelection);
11848
+ return info && JSON.stringify(info.tableSelection) === JSON.stringify(owner.info.tableSelection) ? {
11849
+ owner,
11850
+ info
11851
+ } : null;
11852
+ };
11853
+ const currentWholeTableParagraphSelection = () => {
11854
+ const current = currentWholeTableParagraphOwner();
11855
+ if (!current) return false;
11856
+ return current.owner.info.selectionTarget ? selectionInfoMatchesHost(current.info) : current.owner.nativeSelection !== null && readHostSelectionSnapshot() === current.owner.nativeSelection;
11658
11857
  };
11659
11858
  const commandSelectionIsReady = (id, descriptor, payload) => {
11660
11859
  const foreground = foregroundMutationState();
11661
11860
  if (foreground && (foreground.active > 0 || foreground.pending > 0)) return false;
11662
11861
  if (descriptor?.blockParagraph && currentWholeTableParagraphSelection()) return true;
11862
+ if (descriptor?.blockParagraph && state.selection.tableSelection?.type === "table" && !state.selection.selectionTarget) return false;
11663
11863
  if (state.selection.status !== "ready") return false;
11664
11864
  if (trackDecisionCommand(id)?.scope === "id") return true;
11665
11865
  if (!descriptor || descriptor.disposition !== "routed") return true;
@@ -12080,10 +12280,23 @@ function createSuperDocUI(options) {
12080
12280
  const cellTarget = currentCellSelectionTarget();
12081
12281
  const tableCellSelection = tableCellSelectionForTarget(cellTarget);
12082
12282
  const rawSelection = typeof selectionApi?.["current"] === "function" ? await Promise.resolve(selectionApi["current"]({ includeText: true })) : readSelectionInfoLive().value;
12283
+ const selection = normalizeSelectionInfo(rawSelection);
12284
+ let paintedTarget = null;
12285
+ if (selection && !selection.empty && !selection.selectionTarget && selection.target?.kind === "text" && !cellTarget) {
12286
+ const nativeSelection = readHostSelectionSnapshot();
12287
+ paintedTarget = heldPaintedTextSelectionTarget();
12288
+ if (paintedTarget && (!nativeSelection || !appliedSelectionPresentation(selectionSliceFromInfo(selection, "ready")) || readHostSelectionSnapshot() !== nativeSelection)) return {
12289
+ selection: null,
12290
+ cellTarget: null,
12291
+ tableCellSelection: null,
12292
+ paintedTarget: null
12293
+ };
12294
+ }
12083
12295
  return {
12084
- selection: normalizeSelectionInfo(rawSelection),
12296
+ selection,
12085
12297
  cellTarget,
12086
- tableCellSelection
12298
+ tableCellSelection,
12299
+ paintedTarget
12087
12300
  };
12088
12301
  };
12089
12302
  const waitForSelectionSettle = () => new Promise((resolve) => setTimeout(resolve, 16));
@@ -12101,7 +12314,7 @@ function createSuperDocUI(options) {
12101
12314
  break;
12102
12315
  }
12103
12316
  if (selectionRead?.selection && !selectionRead.selection.empty && key !== painter.sourceSelectionKey) {
12104
- await applyFormatPainter(selectionRead.selection, selectionRead.cellTarget, selectionRead.tableCellSelection);
12317
+ await applyFormatPainter(selectionRead.selection, selectionRead.cellTarget, selectionRead.tableCellSelection, selectionRead.paintedTarget);
12105
12318
  return;
12106
12319
  }
12107
12320
  if (painter.snapshot?.paragraph) {
@@ -12109,10 +12322,10 @@ function createSuperDocUI(options) {
12109
12322
  const caretSel = caretRead.selection;
12110
12323
  if (!caretSel) return;
12111
12324
  if (selectionKey(caretSel) === painter.sourceSelectionKey) return;
12112
- await applyFormatPainter(caretSel, caretRead.cellTarget, caretRead.tableCellSelection);
12325
+ await applyFormatPainter(caretSel, caretRead.cellTarget, caretRead.tableCellSelection, caretRead.paintedTarget);
12113
12326
  }
12114
12327
  };
12115
- const applyFormatPainter = async (selection, cellTarget, tableCellSelection) => {
12328
+ const applyFormatPainter = async (selection, cellTarget, tableCellSelection, paintedTarget) => {
12116
12329
  const snap = painter.snapshot;
12117
12330
  if (!snap) {
12118
12331
  if (painter.mode === "armed") exitFormatPainter();
@@ -12130,7 +12343,7 @@ function createSuperDocUI(options) {
12130
12343
  return;
12131
12344
  }
12132
12345
  const selTarget = selection.target;
12133
- const selectionTarget = cellTarget ?? selection.selectionTarget;
12346
+ const selectionTarget = cellTarget ?? paintedTarget ?? selection.selectionTarget;
12134
12347
  const selectionIsEmpty = selection.empty === true;
12135
12348
  const segments = Array.isArray(selTarget?.segments) ? selTarget.segments : [];
12136
12349
  const targetStory = selTarget?.story;
@@ -12368,13 +12581,16 @@ function createSuperDocUI(options) {
12368
12581
  const snapshot = selectionSub.get();
12369
12582
  if (snapshot.empty) return null;
12370
12583
  const cells = currentCellSelectionTarget();
12584
+ const painted = cells ? null : currentUnrepresentableSelectionTarget(snapshot);
12585
+ const hostSelection = selectionHostSnapshots.get(snapshot);
12586
+ if ((cells || painted || hostSelection && !snapshot.selectionTarget) && (!hostSelection || readHostSelectionSnapshot() !== hostSelection)) return null;
12371
12587
  if (!snapshot.target && !snapshot.selectionTarget && !cells) return null;
12372
12588
  const capture = {
12373
12589
  ...snapshot,
12374
12590
  capturedAt: Date.now(),
12375
- ...cells ? { _tableCellSelectionTarget: cells } : {}
12591
+ ...cells ? { _tableCellSelectionTarget: cells } : {},
12592
+ ...painted ? { _paintedSelectionTarget: painted } : {}
12376
12593
  };
12377
- const hostSelection = selectionHostSnapshots.get(snapshot);
12378
12594
  if (hostSelection) selectionHostSnapshots.set(capture, hostSelection);
12379
12595
  return capture;
12380
12596
  },
@@ -12400,7 +12616,13 @@ function createSuperDocUI(options) {
12400
12616
  ok: false,
12401
12617
  reason: SUPERDOC_UI_REASONS.targetUnresolved
12402
12618
  });
12403
- const target = cellTarget ?? selectionTargetForRestore(capture);
12619
+ const hasPaintedTarget = "_paintedSelectionTarget" in capture;
12620
+ const paintedTarget = hasPaintedTarget ? capturedTableCellTarget(capture._paintedSelectionTarget) : null;
12621
+ if (hasPaintedTarget && !paintedTarget) return done({
12622
+ ok: false,
12623
+ reason: SUPERDOC_UI_REASONS.targetUnresolved
12624
+ });
12625
+ const target = cellTarget ?? paintedTarget ?? selectionTargetForRestore(capture);
12404
12626
  if (!target) return done({
12405
12627
  ok: false,
12406
12628
  reason: SUPERDOC_UI_REASONS.targetUnresolved
@@ -12413,7 +12635,7 @@ function createSuperDocUI(options) {
12413
12635
  reason: resolved.reason
12414
12636
  });
12415
12637
  try {
12416
- return done(normalizeHostSelectionApplyResult(hostMethod(resolved.helper, "apply")?.(target, ...cellTarget ? [{ tableCells: true }] : [])));
12638
+ return done(normalizeHostSelectionApplyResult(hostMethod(resolved.helper, "apply")?.(target, ...cellTarget ? [{ tableCells: true }] : paintedTarget ? [{ offsetSpace: "selection" }] : [])));
12417
12639
  } catch {
12418
12640
  return done({
12419
12641
  ok: false,
@@ -12832,6 +13054,10 @@ function createSuperDocUI(options) {
12832
13054
  };
12833
13055
  const commentsSub = sliceHandle((s) => s.comments);
12834
13056
  const commentsSnap = directorySnapshotHandle(commentsSub, select((s) => computeCommentsDirectorySnapshot(s.selection, s.comments)), "comments");
13057
+ documentResetHooks.push(() => {
13058
+ setExplicitActiveChange(null);
13059
+ explicitActiveCommentId = null;
13060
+ });
12835
13061
  const comments = {
12836
13062
  get: commentsSnap.get,
12837
13063
  getSnapshot: commentsSnap.getSnapshot,
@@ -12925,6 +13151,7 @@ function createSuperDocUI(options) {
12925
13151
  setActive: (commentId) => {
12926
13152
  if (commentId == null) {
12927
13153
  explicitActiveCommentId = null;
13154
+ mirrorReviewFocusToHost("comment", null);
12928
13155
  recompute();
12929
13156
  return true;
12930
13157
  }
@@ -12937,7 +13164,9 @@ function createSuperDocUI(options) {
12937
13164
  const resolvedId = resolveActiveCommentId(items, commentId);
12938
13165
  if (!resolvedId) return false;
12939
13166
  if (!items.some((entry) => readEntityId(entry) === resolvedId)) return false;
13167
+ setExplicitActiveChange(null);
12940
13168
  explicitActiveCommentId = resolvedId;
13169
+ mirrorReviewFocusToHost("comment", { id: resolvedId });
12941
13170
  recompute();
12942
13171
  return true;
12943
13172
  },
@@ -12997,7 +13226,7 @@ function createSuperDocUI(options) {
12997
13226
  };
12998
13227
  const navigateAndScrollCurrentRequest = async (step, requestedAtInvalidation) => {
12999
13228
  if (queuedTrackChangeNavigationInvalidation !== requestedAtInvalidation) return { success: false };
13000
- const previousActiveChange = explicitActiveChange;
13229
+ const previousFocus = readReviewFocus();
13001
13230
  const activeId = navigateTrackChange(step, { invalidateQueuedNavigation: false });
13002
13231
  if (activeId == null) return { success: false };
13003
13232
  const optimisticActiveChange = explicitActiveChange;
@@ -13005,7 +13234,7 @@ function createSuperDocUI(options) {
13005
13234
  const isCurrent = () => queuedTrackChangeNavigationInvalidation === requestedAtInvalidation && explicitActiveChange === optimisticActiveChange && explicitActiveChangeRevision === optimisticRevision;
13006
13235
  const rollback = () => {
13007
13236
  if (!isCurrent()) return;
13008
- setExplicitActiveChange(previousActiveChange, { invalidateQueuedNavigation: false });
13237
+ restoreReviewFocus(previousFocus);
13009
13238
  recompute();
13010
13239
  };
13011
13240
  if (optimisticActiveChange) pendingTrackChangeRevealFocuses.add(optimisticActiveChange);
@@ -13025,10 +13254,8 @@ function createSuperDocUI(options) {
13025
13254
  shouldContinue: isCurrent
13026
13255
  });
13027
13256
  if (!isCurrent()) return { success: false };
13028
- if (!result.ok && result.reason !== SUPERDOC_UI_REASONS.targetNotVisible) {
13029
- rollback();
13030
- mirrorTrackedChangeFocusToHost(previousActiveChange);
13031
- } else {
13257
+ if (!result.ok && result.reason !== SUPERDOC_UI_REASONS.targetNotVisible) rollback();
13258
+ else {
13032
13259
  if (result.ok) await restampVisibleTrackedChangeCarrierAliases(activeId);
13033
13260
  if (!isCurrent()) return { success: false };
13034
13261
  mirrorTrackedChangeFocusToHost(optimisticActiveChange);
@@ -13159,7 +13386,7 @@ function createSuperDocUI(options) {
13159
13386
  const matchingRow = matchStory ? loadedItems.find((item) => entityRowMatchesRequest(item, publicId, matchStory)) : loadedItems.find((item) => readEntityId(item) === publicId);
13160
13387
  const story = matchStory ? requestedStory : readEntityRequestStory(matchingRow);
13161
13388
  const importedId = deriveTrackedChangeImportedId(matchingRow);
13162
- const previousActiveChange = explicitActiveChange;
13389
+ const previousFocus = readReviewFocus();
13163
13390
  const requestedActiveChange = {
13164
13391
  id: publicId,
13165
13392
  ...story ? { story } : {},
@@ -13187,7 +13414,7 @@ function createSuperDocUI(options) {
13187
13414
  const isCurrent = () => trackChangeRevealInvalidation === revealInvalidation && explicitActiveChange === optimisticActiveChange && explicitActiveChangeRevision === optimisticRevision;
13188
13415
  const rollback = () => {
13189
13416
  if (!isCurrent()) return;
13190
- setExplicitActiveChange(previousActiveChange, { invalidateQueuedNavigation: false });
13417
+ restoreReviewFocus(previousFocus);
13191
13418
  recompute();
13192
13419
  };
13193
13420
  recompute();
@@ -13201,10 +13428,8 @@ function createSuperDocUI(options) {
13201
13428
  success: false,
13202
13429
  ok: false
13203
13430
  };
13204
- if (!result.ok && result.reason !== SUPERDOC_UI_REASONS.targetNotVisible) {
13205
- rollback();
13206
- mirrorTrackedChangeFocusToHost(previousActiveChange);
13207
- } else {
13431
+ if (!result.ok && result.reason !== SUPERDOC_UI_REASONS.targetNotVisible) rollback();
13432
+ else {
13208
13433
  if (result.ok) await restampVisibleTrackedChangeCarrierAliases(publicId);
13209
13434
  if (!isCurrent()) return {
13210
13435
  success: false,
@@ -13539,14 +13764,14 @@ function createSuperDocUI(options) {
13539
13764
  if (root instanceof HTMLElement) return root;
13540
13765
  return null;
13541
13766
  };
13542
- const pointEntityHits = (x, y) => {
13543
- if (typeof x !== "number" || typeof y !== "number" || !Number.isFinite(x) || !Number.isFinite(y)) return [];
13767
+ const pointHitElements = (x, y) => {
13768
+ if (typeof x !== "number" || typeof y !== "number" || !Number.isFinite(x) || !Number.isFinite(y)) return null;
13544
13769
  const dom = globalThis.document;
13545
- if (!dom || typeof dom.elementFromPoint !== "function") return [];
13770
+ if (!dom || typeof dom.elementFromPoint !== "function") return null;
13546
13771
  const container = resolveVisibleHost();
13547
- if (!container) return [];
13772
+ if (!container) return null;
13548
13773
  const startEl = dom.elementFromPoint(x, y);
13549
- if (!startEl || !container.contains(startEl)) return [];
13774
+ if (!startEl || !container.contains(startEl)) return null;
13550
13775
  const primaryEls = [startEl];
13551
13776
  const addPrimaryEl = (el) => {
13552
13777
  if (el && container.contains(el) && !primaryEls.includes(el)) primaryEls.push(el);
@@ -13561,6 +13786,17 @@ function createSuperDocUI(options) {
13561
13786
  }
13562
13787
  for (const el of stacked) addPrimaryEl(el);
13563
13788
  }
13789
+ return {
13790
+ container,
13791
+ elements: primaryEls,
13792
+ x,
13793
+ y
13794
+ };
13795
+ };
13796
+ const pointEntityHits = (x, y) => {
13797
+ const point = pointHitElements(x, y);
13798
+ if (!point) return [];
13799
+ const { container, elements: primaryEls } = point;
13564
13800
  const markerEls = [];
13565
13801
  const addMarkerEl = (el) => {
13566
13802
  if (el && container.contains(el) && !markerEls.includes(el) && !primaryEls.includes(el)) markerEls.push(el);
@@ -13574,7 +13810,7 @@ function createSuperDocUI(options) {
13574
13810
  } catch {
13575
13811
  return false;
13576
13812
  }
13577
- return rect != null && x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom;
13813
+ return rect != null && point.x >= rect.left && point.x <= rect.right && point.y >= rect.top && point.y <= rect.bottom;
13578
13814
  };
13579
13815
  for (const el of primaryEls) {
13580
13816
  const queryAll = el.querySelectorAll;
@@ -13598,6 +13834,18 @@ function createSuperDocUI(options) {
13598
13834
  const seen = /* @__PURE__ */ new Set();
13599
13835
  const hits = [];
13600
13836
  for (const hit of rawHits) {
13837
+ if (hit.type === "decoration") {
13838
+ const owner = decorationOwnerForProvider(hit.id);
13839
+ if (!owner) continue;
13840
+ const key = `decoration:${owner.id}`;
13841
+ if (seen.has(key)) continue;
13842
+ seen.add(key);
13843
+ hits.push({
13844
+ type: "decoration",
13845
+ id: owner.id
13846
+ });
13847
+ continue;
13848
+ }
13601
13849
  if (hit.type !== "trackedChange") {
13602
13850
  const layoutStory = hit.type === "contentControl" ? layoutStoryDatasetToStoryLocator(hit.story) : void 0;
13603
13851
  const story = hit.type === "contentControl" ? isTextboxStory(layoutStory) ? layoutStory : storyKeyToStoryLocator(hit.storyKey) ?? layoutStory : void 0;
@@ -13753,6 +14001,149 @@ function createSuperDocUI(options) {
13753
14001
  if (status !== "ready") return { reason: "not-ready" };
13754
14002
  return value ? { target: value } : { reason: "unresolved" };
13755
14003
  };
14004
+ const liveDecorations = /* @__PURE__ */ new Map();
14005
+ const decorationOwnerForProvider = (providerId) => {
14006
+ const owner = currentHostVisuals()?.providerOwner?.(providerId);
14007
+ return isPlainObject(owner) && typeof owner.id === "string" ? owner : null;
14008
+ };
14009
+ const currentHostVisuals = () => getEditor()?.visuals ?? null;
14010
+ const decorationListeners = /* @__PURE__ */ new Set();
14011
+ let detachDecorationPointer = null;
14012
+ let hoveredDecorations = [];
14013
+ let lastPointer = {
14014
+ clientX: 0,
14015
+ clientY: 0
14016
+ };
14017
+ const decorationIdsAt = (x, y) => {
14018
+ const point = pointHitElements(x, y);
14019
+ if (!point) return [];
14020
+ const ids = /* @__PURE__ */ new Set();
14021
+ for (const element of point.elements) for (const provider of collectDecorationProvidersFromChain(element, point.container)) {
14022
+ const owner = decorationOwnerForProvider(provider);
14023
+ if (owner) ids.add(owner.id);
14024
+ }
14025
+ return [...ids];
14026
+ };
14027
+ const decorationData = (id) => {
14028
+ const owner = currentHostVisuals()?.ownerById?.(id);
14029
+ return isPlainObject(owner?.data) ? owner.data : void 0;
14030
+ };
14031
+ const emitDecorationEvent = (type, decoration, point) => {
14032
+ const payload = {
14033
+ type,
14034
+ id: decoration.id,
14035
+ ...decoration.data ? { data: decoration.data } : {},
14036
+ clientX: point.clientX,
14037
+ clientY: point.clientY
14038
+ };
14039
+ for (const listener of Array.from(decorationListeners)) safeCall(() => listener(payload), void 0);
14040
+ };
14041
+ const leaveHoveredDecorations = (onlyId) => {
14042
+ const leaving = hoveredDecorations.filter((decoration) => onlyId === void 0 || decoration.id === onlyId);
14043
+ hoveredDecorations = hoveredDecorations.filter((decoration) => !leaving.includes(decoration));
14044
+ for (const decoration of leaving) emitDecorationEvent("hover-leave", decoration, lastPointer);
14045
+ };
14046
+ const attachDecorationPointer = () => {
14047
+ if (detachDecorationPointer || decorationListeners.size === 0) return;
14048
+ const host = resolveVisibleHost();
14049
+ if (!host) return;
14050
+ const onClick = (event) => {
14051
+ for (const id of decorationIdsAt(event.clientX, event.clientY)) emitDecorationEvent("click", {
14052
+ id,
14053
+ data: decorationData(id)
14054
+ }, event);
14055
+ };
14056
+ const onMove = (event) => {
14057
+ lastPointer = {
14058
+ clientX: event.clientX,
14059
+ clientY: event.clientY
14060
+ };
14061
+ const nextIds = decorationIdsAt(event.clientX, event.clientY);
14062
+ const leaving = hoveredDecorations.filter((decoration) => !nextIds.includes(decoration.id));
14063
+ const kept = hoveredDecorations.filter((decoration) => nextIds.includes(decoration.id));
14064
+ const entering = nextIds.filter((id) => !kept.some((decoration) => decoration.id === id)).map((id) => ({
14065
+ id,
14066
+ data: decorationData(id)
14067
+ }));
14068
+ hoveredDecorations = [...kept, ...entering];
14069
+ for (const decoration of leaving) emitDecorationEvent("hover-leave", decoration, event);
14070
+ for (const decoration of entering) emitDecorationEvent("hover-enter", decoration, event);
14071
+ };
14072
+ const onLeave = (event) => {
14073
+ lastPointer = {
14074
+ clientX: event.clientX,
14075
+ clientY: event.clientY
14076
+ };
14077
+ leaveHoveredDecorations();
14078
+ };
14079
+ host.addEventListener("click", onClick);
14080
+ host.addEventListener("pointermove", onMove);
14081
+ host.addEventListener("pointerleave", onLeave);
14082
+ detachDecorationPointer = () => {
14083
+ host.removeEventListener("click", onClick);
14084
+ host.removeEventListener("pointermove", onMove);
14085
+ host.removeEventListener("pointerleave", onLeave);
14086
+ leaveHoveredDecorations();
14087
+ detachDecorationPointer = null;
14088
+ };
14089
+ };
14090
+ const subscribeDecorationPointer = (listener) => {
14091
+ if (disposed) throw new Error("ui.decorations.subscribe called on a destroyed UI controller.");
14092
+ decorationListeners.add(listener);
14093
+ attachDecorationPointer();
14094
+ return () => {
14095
+ decorationListeners.delete(listener);
14096
+ if (decorationListeners.size === 0) detachDecorationPointer?.();
14097
+ };
14098
+ };
14099
+ documentResetHooks.push(() => {
14100
+ leaveHoveredDecorations();
14101
+ const current = currentHostVisuals();
14102
+ for (const [handle, owner] of Array.from(liveDecorations)) if (owner.visuals !== current || owner.generation !== current.generation()) handle.dispose();
14103
+ });
14104
+ activeEditorResetHooks.push(() => {
14105
+ detachDecorationPointer?.();
14106
+ attachDecorationPointer();
14107
+ });
14108
+ const createDecorationsHandle = (track) => ({
14109
+ subscribe: (listener) => {
14110
+ const unsubscribe = subscribeDecorationPointer(listener);
14111
+ track?.({ dispose: unsubscribe });
14112
+ return unsubscribe;
14113
+ },
14114
+ highlight: (id, options) => {
14115
+ if (disposed) throw new Error("ui.decorations.highlight called on a destroyed UI controller.");
14116
+ const visuals = currentHostVisuals();
14117
+ if (!visuals) throw new Error("ui.decorations.highlight requires a ready document.");
14118
+ const inner = visuals.highlight(id, options);
14119
+ let handleDisposed = false;
14120
+ const handle = {
14121
+ id: inner.id,
14122
+ replace: (targets) => inner.replace(targets),
14123
+ add: (targets) => inner.add(targets),
14124
+ clear: () => {
14125
+ if (handleDisposed) return;
14126
+ inner.clear();
14127
+ leaveHoveredDecorations(inner.id);
14128
+ },
14129
+ invalidate: () => inner.invalidate(),
14130
+ dispose: () => {
14131
+ if (handleDisposed) return;
14132
+ handleDisposed = true;
14133
+ liveDecorations.delete(handle);
14134
+ inner.dispose();
14135
+ leaveHoveredDecorations(inner.id);
14136
+ }
14137
+ };
14138
+ liveDecorations.set(handle, {
14139
+ visuals,
14140
+ generation: visuals.generation()
14141
+ });
14142
+ track?.(handle);
14143
+ return handle;
14144
+ }
14145
+ });
14146
+ const decorations = createDecorationsHandle();
13756
14147
  const viewport = {
13757
14148
  getRects: async (input) => {
13758
14149
  const initialHost = getHost();
@@ -14730,6 +15121,92 @@ function createSuperDocUI(options) {
14730
15121
  }
14731
15122
  }, 120);
14732
15123
  };
15124
+ const updateStyleToMatchSelection = (input) => {
15125
+ const mode = readDocumentMode();
15126
+ if (mode === "viewing") return failedReceipt("The document is read-only.", "DOCUMENT_READONLY");
15127
+ if (mode === "suggesting") return failedReceipt("A style definition cannot be changed as a tracked suggestion.", "PRECONDITION_FAILED");
15128
+ const sourceToken = contentToken();
15129
+ const readPositionKey = () => {
15130
+ const snapshot = readHostSelectionSnapshot();
15131
+ if (!snapshot) return null;
15132
+ return JSON.stringify({
15133
+ story: snapshot.story,
15134
+ tableCells: snapshot.tableCells,
15135
+ points: ["anchor", "focus"].map((side) => {
15136
+ const point = snapshot[side];
15137
+ return point ? [
15138
+ point.blockId,
15139
+ point.blockOffset,
15140
+ point.trackedBlockOffset,
15141
+ point.story,
15142
+ point.inlineAffinity,
15143
+ point.outsideContentControl,
15144
+ point.visualCaret
15145
+ ] : null;
15146
+ })
15147
+ });
15148
+ };
15149
+ const requestedPosition = readPositionKey();
15150
+ const stale = failedReceipt("The selection or document changed while reading its style.", "REVISION_MISMATCH");
15151
+ return readSelectionInfoFresh().then((selection) => {
15152
+ const selectionToken = selectionKey(selection);
15153
+ const isCurrent = () => !disposed && contentToken() === sourceToken && readPositionKey() === requestedPosition && selectionKey(state.selection) === selectionToken && readDocumentMode() === mode;
15154
+ if (!isCurrent()) return stale;
15155
+ const active = computeActiveParagraphStyle(selection, getStyleCatalog().cache).style;
15156
+ const story = storyLocatorSignature(selectionStory(selection));
15157
+ const keys = [styleCatalogReadKey({ includePreview: true }), ...selectionBlockIds(selection).map((id) => `node:${story}:${id}`)];
15158
+ const unreadable = failedReceipt("The selection formatting could not be read.", "PRECONDITION_FAILED");
15159
+ const run = (active) => {
15160
+ if (keys.some((key) => {
15161
+ const entry = asyncReads.get(key);
15162
+ return entry && (!entry.hasSettled || entry.token !== sourceToken || entry.failedToken === sourceToken);
15163
+ })) return unreadable;
15164
+ const styleId = input?.styleId ?? active.styleId;
15165
+ if (!styleId) return failedReceipt("Choose a style to update: the selection does not have a single paragraph style.", "INVALID_TARGET");
15166
+ const doc = getDoc();
15167
+ const readSelection = resolveDocOperation(doc, "format.readSelection");
15168
+ const apply = resolveDocOperation(doc, "styles.apply");
15169
+ if (!readSelection || !apply) return failedReceipt("Updating a style to match the selection is unavailable.");
15170
+ let reads;
15171
+ try {
15172
+ reads = [readSelection({
15173
+ selection: "current",
15174
+ keys: STYLE_MATCH_READ_KEYS,
15175
+ view: "effective"
15176
+ }), readSelection({
15177
+ selection: "current",
15178
+ keys: STYLE_MATCH_READ_KEYS,
15179
+ view: "direct"
15180
+ })];
15181
+ } catch {
15182
+ return unreadable;
15183
+ }
15184
+ return Promise.all(reads).then(([effective, direct]) => {
15185
+ if (!isCurrent()) return stale;
15186
+ const patch = buildStyleMatchPatch(effective, direct, { selectionUsesStyle: !active.mixed && active.styleId === styleId });
15187
+ if (Object.keys(patch).length === 0) return failedReceipt("The selection already matches the style.", "NO_OP");
15188
+ const fallback = failedReceipt("Updating the style failed.");
15189
+ return safeCall(() => settleWorkflowReceipt(apply({
15190
+ target: {
15191
+ scope: "style",
15192
+ styleId,
15193
+ channel: "run"
15194
+ },
15195
+ patch
15196
+ }), fallback), fallback);
15197
+ }, () => unreadable);
15198
+ };
15199
+ const pending = keys.flatMap((key) => {
15200
+ const entry = asyncReads.get(key);
15201
+ return entry?.inflightToken === sourceToken && entry.settled ? [entry.settled] : [];
15202
+ });
15203
+ if (pending.length === 0) return run(active);
15204
+ return Promise.all(pending).then(() => {
15205
+ if (!isCurrent()) return stale;
15206
+ return run(computeActiveParagraphStyle(selection, getStyleCatalog().cache).style);
15207
+ });
15208
+ });
15209
+ };
14733
15210
  const stylesSub = sliceHandle((s) => s.styles);
14734
15211
  const stylesSnap = snapshotHandle(stylesSub);
14735
15212
  const styles = {
@@ -14742,7 +15219,8 @@ function createSuperDocUI(options) {
14742
15219
  return readStyleCatalogLive(options).value;
14743
15220
  },
14744
15221
  getQuickGallery: () => stylesSub.get().quickGallery,
14745
- getActiveParagraphStyle: () => computeActiveParagraphStyle(state.selection, getStyleCatalog().cache).style
15222
+ getActiveParagraphStyle: () => computeActiveParagraphStyle(state.selection, getStyleCatalog().cache).style,
15223
+ updateToMatchSelection: (input) => updateStyleToMatchSelection(input)
14746
15224
  };
14747
15225
  const openWatermarkSurface = hostMethod(superdoc, "openSurface");
14748
15226
  const watermark = createWatermarkController({
@@ -14757,6 +15235,7 @@ function createSuperDocUI(options) {
14757
15235
  const createScope = () => {
14758
15236
  const scopeUnsubs = [];
14759
15237
  return {
15238
+ decorations: createDecorationsHandle((handle) => scopeUnsubs.push(() => handle.dispose())),
14760
15239
  select: (selector, equality) => {
14761
15240
  const sub = select(selector, equality);
14762
15241
  return {
@@ -14777,6 +15256,9 @@ function createSuperDocUI(options) {
14777
15256
  if (disposed) return;
14778
15257
  watermark.destroy();
14779
15258
  clearContentControlHighlight();
15259
+ for (const handle of Array.from(liveDecorations.keys())) handle.dispose();
15260
+ detachDecorationPointer?.();
15261
+ decorationListeners.clear();
14780
15262
  disposed = true;
14781
15263
  completedFallbackSearch = null;
14782
15264
  if (searchRefreshTimer !== null) clearTimeout(searchRefreshTimer);
@@ -14859,6 +15341,7 @@ function createSuperDocUI(options) {
14859
15341
  zoom,
14860
15342
  document: documentHandle,
14861
15343
  viewport,
15344
+ decorations,
14862
15345
  metadata,
14863
15346
  tables,
14864
15347
  search,
@@ -15077,4 +15560,4 @@ function commandIsActive(descriptor, selection) {
15077
15560
  return descriptor.activeMark ? selection.activeMarks.includes(descriptor.activeMark) : false;
15078
15561
  }
15079
15562
  //#endregion
15080
- export { getWorkflowRecording as _, registerFirstPartyCommandMutation as a, getV2TrackedChangeMutationImpact as c, resolveImageSrc as d, shallowEqual as f, createInteractionHistory as g, closeInteractionHistory as h, readFirstPartyPageNumbers as i, isPlainObject as l, DOM_CLASS_NAMES as m, executeFirstPartyCommandAsync as n, BUILT_IN_COMMAND_IDS as o, isSyntheticTrackedChangeCommentLaneItem as p, executeFirstPartyShortcutAsync as r, applyV2CommentInvalidationFromMutation as s, createSuperDocUI as t, createImageFilePicker as u, isWorkflowRecordingEnabled as v, recordInteraction as y };
15563
+ export { createInteractionHistory as _, registerFirstPartyCommandMutation as a, recordInteraction as b, applyV2CommentInvalidationFromMutation as c, createImageFilePicker as d, resolveImageSrc as f, closeInteractionHistory as g, DOM_CLASS_NAMES as h, readFirstPartyPageNumbers as i, getV2TrackedChangeMutationImpact as l, isSyntheticTrackedChangeCommentLaneItem as m, executeFirstPartyCommandAsync as n, isRecord as o, shallowEqual as p, executeFirstPartyShortcutAsync as r, BUILT_IN_COMMAND_IDS as s, createSuperDocUI as t, isPlainObject as u, getWorkflowRecording as v, isWorkflowRecordingEnabled as y };