superdoc 2.27.0-next.1 → 2.27.0-next.11

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 (53) hide show
  1. package/dist/chunks/{create-super-doc-ui-BCQ5RHxy.es.js → create-super-doc-ui-B29zw5qD.es.js} +192 -13
  2. package/dist/chunks/{create-super-doc-ui-DK14sCrT.cjs → create-super-doc-ui-D1eOhAjv.cjs} +197 -12
  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/contract/operation-definitions.d.ts +2 -2
  6. package/dist/document-api/src/history/history.types.d.ts +0 -6
  7. package/dist/document-api/src/index.d.ts +4 -4
  8. package/dist/document-api/src/lists/lists.types.d.ts +3 -0
  9. package/dist/document-api/src/styles/apply.d.ts +26 -2
  10. package/dist/document-api/src/styles/index.d.ts +3 -3
  11. package/dist/document-api/src/styles/schema.d.ts +3 -1
  12. package/dist/document-api/src/styles/validation.d.ts +10 -9
  13. package/dist/document-api/src/types/receipt.d.ts +2 -1
  14. package/dist/document-api/src/types/temporary-mutation.d.ts +39 -4
  15. package/dist/layout-engine/contracts/src/direction-context.d.ts +1 -2
  16. package/dist/layout-engine/contracts/src/resolved-layout.d.ts +0 -34
  17. package/dist/layout-engine/layout-bridge/src/neutral-hit.d.ts +0 -7
  18. package/dist/layout-engine/layout-engine/src/index.d.ts +0 -1
  19. package/dist/layout-engine/painters/dom/src/features/inline-direction/run-direction.d.ts +0 -3
  20. package/dist/public/ui-react.cjs +1 -1
  21. package/dist/public/ui-react.es.js +1 -1
  22. package/dist/public/ui-vue.cjs +1 -1
  23. package/dist/public/ui-vue.es.js +1 -1
  24. package/dist/public/ui.cjs +1 -1
  25. package/dist/public/ui.es.js +1 -1
  26. package/dist/style.css +1 -1
  27. package/dist/style.layered.css +1 -1
  28. package/dist/superdoc/src/core/types/index.d.ts +3 -60
  29. package/dist/superdoc/src/core/v2-integration/v2-integration.d.ts +2 -5
  30. package/dist/superdoc/src/helpers/tracked-change-comment-lane.d.ts +1 -0
  31. package/dist/superdoc/src/helpers/v2-review-mutation-impact.d.ts +0 -6
  32. package/dist/superdoc/src/internal/toolbar/built-in/LinkedStyle.vue.d.ts +3 -1
  33. package/dist/superdoc/src/internal/toolbar/built-in/toolbarTexts.d.ts +1 -0
  34. package/dist/superdoc/src/internal/toolbar/built-in-toolbar.d.ts +1 -0
  35. package/dist/superdoc/src/public/ui/style-match-patch.d.ts +7 -0
  36. package/dist/superdoc/src/public/ui/types.d.ts +5 -1
  37. package/dist/superdoc/src/public/ui-react.d.ts +0 -5
  38. package/dist/superdoc/src/public/ui-vue.d.ts +0 -5
  39. package/dist/superdoc/src/public/ui.d.cts +18 -0
  40. package/dist/superdoc/src/public/ui.d.ts +1 -10
  41. package/dist/superdoc.cjs +117 -85
  42. package/dist/superdoc.es.js +110 -80
  43. package/dist-cdn/style.layered.css +1 -1
  44. package/dist-cdn/superdoc.min.css +1 -1
  45. package/dist-cdn/superdoc.min.js +38 -38
  46. package/package.json +2 -5
  47. package/dist/superdoc/src/components/CommentsLayer/comment-schemas.d.ts +0 -18
  48. package/dist/superdoc/src/components/CommentsLayer/types.d.ts +0 -3
  49. package/dist/superdoc/src/components/CommentsLayer/use-conversation.d.ts +0 -22
  50. package/dist/superdoc/src/components/CommentsLayer/use-floating-comment.d.ts +0 -11
  51. package/dist/superdoc/src/core/collaboration/room-overwrite.d.ts +0 -22
  52. package/dist/superdoc/src/core/index.d.ts +0 -1
  53. package/dist/superdoc/src/stores/hrbr-fields-store.d.ts +0 -43
@@ -1078,6 +1078,15 @@ var decodeLayoutStoryDataset = (raw) => {
1078
1078
  `${DOM_CLASS_NAMES.BLOCK_SDT}${DATA_ATTRS.SDT_ID}`;
1079
1079
  `${DATA_ATTRS.DRAGGABLE}`;
1080
1080
  //#endregion
1081
+ //#region src/helpers/tracked-change-comment-lane.ts
1082
+ var TRACKED_CHANGE_COMMENT_LANE_ID_PREFIX = "tc-comment:";
1083
+ function isSyntheticTrackedChangeCommentLaneItem(item) {
1084
+ if (!item || typeof item !== "object") return false;
1085
+ const { id, commentId } = item;
1086
+ const itemId = id ?? commentId;
1087
+ return typeof itemId === "string" && itemId.startsWith(TRACKED_CHANGE_COMMENT_LANE_ID_PREFIX);
1088
+ }
1089
+ //#endregion
1081
1090
  //#region src/public/ui/viewport-batch.ts
1082
1091
  /** No rendering or scrolling: read only geometry already acknowledged by the host. */
1083
1092
  async function readViewportBatch(input, read, generation, disposed) {
@@ -2449,6 +2458,69 @@ function createDialogWorkflow(editor, options, onSuccess) {
2449
2458
  };
2450
2459
  }
2451
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
2452
2524
  //#region src/public/ui/format-painter-helpers.js
2453
2525
  /**
2454
2526
  * Maps SDRunProps field names to InlineRunPatch field names.
@@ -2634,12 +2706,6 @@ function getV2TrackedChangeMutationImpact(event) {
2634
2706
  }
2635
2707
  /**
2636
2708
  * Comment ids retired by a committed v2 mutation.
2637
- *
2638
- * AIDEV-NOTE: History commits put invalidated comment ids on `result`; cut and
2639
- * other command receipts use `receipt`. Same origin split as
2640
- * getV2TrackedChangeMutationImpact. Reading the wrong field, or treating a
2641
- * tracked-change entity as a comment, leaves a sidebar card after cut or
2642
- * undo/redo.
2643
2709
  */
2644
2710
  function collectV2InvalidatedCommentIds(event) {
2645
2711
  const removedIds = /* @__PURE__ */ new Set();
@@ -7096,7 +7162,7 @@ function createSuperDocUI(options) {
7096
7162
  failureCount: retryingSameToken ? prev.failureCount ?? 0 : 0,
7097
7163
  retryAtMs: 0
7098
7164
  });
7099
- Promise.resolve(raw).then((resolved) => {
7165
+ const settled = Promise.resolve(raw).then((resolved) => {
7100
7166
  const entry = asyncReads.get(key);
7101
7167
  if (!entry || entry.inflightToken !== token) return;
7102
7168
  asyncReads.set(key, {
@@ -7135,6 +7201,7 @@ function createSuperDocUI(options) {
7135
7201
  const retryAtMs = Date.now() + retryDelayMs;
7136
7202
  asyncReads.set(key, {
7137
7203
  ...entry,
7204
+ settled: void 0,
7138
7205
  inflightToken: null,
7139
7206
  failedToken: token,
7140
7207
  failureCount,
@@ -7142,6 +7209,8 @@ function createSuperDocUI(options) {
7142
7209
  });
7143
7210
  scheduleAsyncReadFailureRetry(retryAtMs);
7144
7211
  });
7212
+ const entry = asyncReads.get(key);
7213
+ if (entry?.inflightToken === token) entry.settled = settled;
7145
7214
  return null;
7146
7215
  };
7147
7216
  /**
@@ -7451,7 +7520,10 @@ function createSuperDocUI(options) {
7451
7520
  if (hostSelection && info && typeof info === "object") selectionHostSnapshots.set(info, hostSelection);
7452
7521
  return info;
7453
7522
  };
7454
- const result = current({ includeText: true });
7523
+ const result = current({
7524
+ includeText: true,
7525
+ ...getEditor()?.editorVersion === 2 ? { offsetSpace: "selection" } : {}
7526
+ });
7455
7527
  return isPromiseLike(result) ? Promise.resolve(result).then(tag) : tag(result);
7456
7528
  }
7457
7529
  /** True when a host sync snapshot is a resolved collapsed caret (not a range). */
@@ -7588,7 +7660,10 @@ function createSuperDocUI(options) {
7588
7660
  }
7589
7661
  let raw;
7590
7662
  try {
7591
- raw = current.call(selectionApi, { includeText: true });
7663
+ raw = current.call(selectionApi, {
7664
+ includeText: true,
7665
+ ...getEditor()?.editorVersion === 2 ? { offsetSpace: "selection" } : {}
7666
+ });
7592
7667
  if (isPromiseLike(raw)) raw = await Promise.resolve(raw);
7593
7668
  } catch {
7594
7669
  raw = null;
@@ -7606,9 +7681,26 @@ function createSuperDocUI(options) {
7606
7681
  recompute();
7607
7682
  return selection;
7608
7683
  };
7684
+ const realComments = (items) => items.filter((item) => !isSyntheticTrackedChangeCommentLaneItem(item));
7685
+ let lastWindowComments = null;
7686
+ const realCommentsOfWindow = (window) => {
7687
+ const items = window?.commentItems;
7688
+ if (!Array.isArray(items)) return null;
7689
+ const last = lastWindowComments;
7690
+ if (last?.source === items && shallowEqual(last.rows, items)) return last.real;
7691
+ lastWindowComments = {
7692
+ source: items,
7693
+ rows: [...items],
7694
+ real: realComments(items)
7695
+ };
7696
+ return lastWindowComments.real;
7697
+ };
7609
7698
  const readCommentsDirectory = () => {
7610
7699
  const listComments = hostMethod(getDoc()?.comments, "list");
7611
- return readAsync("comments", contentToken(), () => listComments ? listComments() : void 0, (raw) => raw && Array.isArray(raw.items) ? raw.items : []);
7700
+ return readAsync("comments", contentToken(), () => listComments ? listComments() : void 0, (raw) => {
7701
+ const items = isPlainObject(raw) ? raw.items : void 0;
7702
+ return Array.isArray(items) ? realComments(items) : [];
7703
+ });
7612
7704
  };
7613
7705
  const readTrackChangesDirectory = (key = "trackChanges") => {
7614
7706
  const token = contentToken();
@@ -7641,7 +7733,7 @@ function createSuperDocUI(options) {
7641
7733
  const reviewWindow = getV2ReviewWindowSnapshot();
7642
7734
  const hasWindowFeed = hasV2ReviewWindowFeed();
7643
7735
  const catalog = !hasWindowFeed ? readCommentsDirectory() : null;
7644
- const windowItems = hasWindowFeed && Array.isArray(reviewWindow?.commentItems) ? reviewWindow.commentItems : null;
7736
+ const windowItems = hasWindowFeed ? realCommentsOfWindow(reviewWindow) : null;
7645
7737
  const value = hasWindowFeed ? windowItems : catalog.value;
7646
7738
  const listStatus = hasWindowFeed ? reviewWindowSliceStatus(reviewWindow) : catalog.status;
7647
7739
  const items = value ?? [];
@@ -14704,6 +14796,92 @@ function createSuperDocUI(options) {
14704
14796
  }
14705
14797
  }, 120);
14706
14798
  };
14799
+ const updateStyleToMatchSelection = (input) => {
14800
+ const mode = readDocumentMode();
14801
+ if (mode === "viewing") return failedReceipt("The document is read-only.", "DOCUMENT_READONLY");
14802
+ if (mode === "suggesting") return failedReceipt("A style definition cannot be changed as a tracked suggestion.", "PRECONDITION_FAILED");
14803
+ const sourceToken = contentToken();
14804
+ const readPositionKey = () => {
14805
+ const snapshot = readHostSelectionSnapshot();
14806
+ if (!snapshot) return null;
14807
+ return JSON.stringify({
14808
+ story: snapshot.story,
14809
+ tableCells: snapshot.tableCells,
14810
+ points: ["anchor", "focus"].map((side) => {
14811
+ const point = snapshot[side];
14812
+ return point ? [
14813
+ point.blockId,
14814
+ point.blockOffset,
14815
+ point.trackedBlockOffset,
14816
+ point.story,
14817
+ point.inlineAffinity,
14818
+ point.outsideContentControl,
14819
+ point.visualCaret
14820
+ ] : null;
14821
+ })
14822
+ });
14823
+ };
14824
+ const requestedPosition = readPositionKey();
14825
+ const stale = failedReceipt("The selection or document changed while reading its style.", "REVISION_MISMATCH");
14826
+ return readSelectionInfoFresh().then((selection) => {
14827
+ const selectionToken = selectionKey(selection);
14828
+ const isCurrent = () => !disposed && contentToken() === sourceToken && readPositionKey() === requestedPosition && selectionKey(state.selection) === selectionToken && readDocumentMode() === mode;
14829
+ if (!isCurrent()) return stale;
14830
+ const active = computeActiveParagraphStyle(selection, getStyleCatalog().cache).style;
14831
+ const story = storyLocatorSignature(selectionStory(selection));
14832
+ const keys = [styleCatalogReadKey({ includePreview: true }), ...selectionBlockIds(selection).map((id) => `node:${story}:${id}`)];
14833
+ const unreadable = failedReceipt("The selection formatting could not be read.", "PRECONDITION_FAILED");
14834
+ const run = (active) => {
14835
+ if (keys.some((key) => {
14836
+ const entry = asyncReads.get(key);
14837
+ return entry && (!entry.hasSettled || entry.token !== sourceToken || entry.failedToken === sourceToken);
14838
+ })) return unreadable;
14839
+ const styleId = input?.styleId ?? active.styleId;
14840
+ if (!styleId) return failedReceipt("Choose a style to update: the selection does not have a single paragraph style.", "INVALID_TARGET");
14841
+ const doc = getDoc();
14842
+ const readSelection = resolveDocOperation(doc, "format.readSelection");
14843
+ const apply = resolveDocOperation(doc, "styles.apply");
14844
+ if (!readSelection || !apply) return failedReceipt("Updating a style to match the selection is unavailable.");
14845
+ let reads;
14846
+ try {
14847
+ reads = [readSelection({
14848
+ selection: "current",
14849
+ keys: STYLE_MATCH_READ_KEYS,
14850
+ view: "effective"
14851
+ }), readSelection({
14852
+ selection: "current",
14853
+ keys: STYLE_MATCH_READ_KEYS,
14854
+ view: "direct"
14855
+ })];
14856
+ } catch {
14857
+ return unreadable;
14858
+ }
14859
+ return Promise.all(reads).then(([effective, direct]) => {
14860
+ if (!isCurrent()) return stale;
14861
+ const patch = buildStyleMatchPatch(effective, direct, { selectionUsesStyle: !active.mixed && active.styleId === styleId });
14862
+ if (Object.keys(patch).length === 0) return failedReceipt("The selection already matches the style.", "NO_OP");
14863
+ const fallback = failedReceipt("Updating the style failed.");
14864
+ return safeCall(() => settleWorkflowReceipt(apply({
14865
+ target: {
14866
+ scope: "style",
14867
+ styleId,
14868
+ channel: "run"
14869
+ },
14870
+ patch
14871
+ }), fallback), fallback);
14872
+ }, () => unreadable);
14873
+ };
14874
+ const pending = keys.flatMap((key) => {
14875
+ const entry = asyncReads.get(key);
14876
+ return entry?.inflightToken === sourceToken && entry.settled ? [entry.settled] : [];
14877
+ });
14878
+ if (pending.length === 0) return run(active);
14879
+ return Promise.all(pending).then(() => {
14880
+ if (!isCurrent()) return stale;
14881
+ return run(computeActiveParagraphStyle(selection, getStyleCatalog().cache).style);
14882
+ });
14883
+ });
14884
+ };
14707
14885
  const stylesSub = sliceHandle((s) => s.styles);
14708
14886
  const stylesSnap = snapshotHandle(stylesSub);
14709
14887
  const styles = {
@@ -14716,7 +14894,8 @@ function createSuperDocUI(options) {
14716
14894
  return readStyleCatalogLive(options).value;
14717
14895
  },
14718
14896
  getQuickGallery: () => stylesSub.get().quickGallery,
14719
- getActiveParagraphStyle: () => computeActiveParagraphStyle(state.selection, getStyleCatalog().cache).style
14897
+ getActiveParagraphStyle: () => computeActiveParagraphStyle(state.selection, getStyleCatalog().cache).style,
14898
+ updateToMatchSelection: (input) => updateStyleToMatchSelection(input)
14720
14899
  };
14721
14900
  const openWatermarkSurface = hostMethod(superdoc, "openSurface");
14722
14901
  const watermark = createWatermarkController({
@@ -15051,4 +15230,4 @@ function commandIsActive(descriptor, selection) {
15051
15230
  return descriptor.activeMark ? selection.activeMarks.includes(descriptor.activeMark) : false;
15052
15231
  }
15053
15232
  //#endregion
15054
- export { isWorkflowRecordingEnabled as _, registerFirstPartyCommandMutation as a, getV2TrackedChangeMutationImpact as c, resolveImageSrc as d, shallowEqual as f, getWorkflowRecording as g, createInteractionHistory as h, readFirstPartyPageNumbers as i, isPlainObject as l, closeInteractionHistory as m, executeFirstPartyCommandAsync as n, BUILT_IN_COMMAND_IDS as o, DOM_CLASS_NAMES as p, executeFirstPartyShortcutAsync as r, applyV2CommentInvalidationFromMutation as s, createSuperDocUI as t, createImageFilePicker as u, recordInteraction as v };
15233
+ 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 };
@@ -1078,6 +1078,15 @@ var decodeLayoutStoryDataset = (raw) => {
1078
1078
  `${DOM_CLASS_NAMES.BLOCK_SDT}${DATA_ATTRS.SDT_ID}`;
1079
1079
  `${DATA_ATTRS.DRAGGABLE}`;
1080
1080
  //#endregion
1081
+ //#region src/helpers/tracked-change-comment-lane.ts
1082
+ var TRACKED_CHANGE_COMMENT_LANE_ID_PREFIX = "tc-comment:";
1083
+ function isSyntheticTrackedChangeCommentLaneItem(item) {
1084
+ if (!item || typeof item !== "object") return false;
1085
+ const { id, commentId } = item;
1086
+ const itemId = id ?? commentId;
1087
+ return typeof itemId === "string" && itemId.startsWith(TRACKED_CHANGE_COMMENT_LANE_ID_PREFIX);
1088
+ }
1089
+ //#endregion
1081
1090
  //#region src/public/ui/viewport-batch.ts
1082
1091
  /** No rendering or scrolling: read only geometry already acknowledged by the host. */
1083
1092
  async function readViewportBatch(input, read, generation, disposed) {
@@ -2450,6 +2459,69 @@ function createDialogWorkflow(editor, options, onSuccess) {
2450
2459
  };
2451
2460
  }
2452
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
2453
2525
  //#region src/public/ui/format-painter-helpers.js
2454
2526
  /**
2455
2527
  * Maps SDRunProps field names to InlineRunPatch field names.
@@ -2635,12 +2707,6 @@ function getV2TrackedChangeMutationImpact(event) {
2635
2707
  }
2636
2708
  /**
2637
2709
  * Comment ids retired by a committed v2 mutation.
2638
- *
2639
- * AIDEV-NOTE: History commits put invalidated comment ids on `result`; cut and
2640
- * other command receipts use `receipt`. Same origin split as
2641
- * getV2TrackedChangeMutationImpact. Reading the wrong field, or treating a
2642
- * tracked-change entity as a comment, leaves a sidebar card after cut or
2643
- * undo/redo.
2644
2710
  */
2645
2711
  function collectV2InvalidatedCommentIds(event) {
2646
2712
  const removedIds = /* @__PURE__ */ new Set();
@@ -7097,7 +7163,7 @@ function createSuperDocUI(options) {
7097
7163
  failureCount: retryingSameToken ? prev.failureCount ?? 0 : 0,
7098
7164
  retryAtMs: 0
7099
7165
  });
7100
- Promise.resolve(raw).then((resolved) => {
7166
+ const settled = Promise.resolve(raw).then((resolved) => {
7101
7167
  const entry = asyncReads.get(key);
7102
7168
  if (!entry || entry.inflightToken !== token) return;
7103
7169
  asyncReads.set(key, {
@@ -7136,6 +7202,7 @@ function createSuperDocUI(options) {
7136
7202
  const retryAtMs = Date.now() + retryDelayMs;
7137
7203
  asyncReads.set(key, {
7138
7204
  ...entry,
7205
+ settled: void 0,
7139
7206
  inflightToken: null,
7140
7207
  failedToken: token,
7141
7208
  failureCount,
@@ -7143,6 +7210,8 @@ function createSuperDocUI(options) {
7143
7210
  });
7144
7211
  scheduleAsyncReadFailureRetry(retryAtMs);
7145
7212
  });
7213
+ const entry = asyncReads.get(key);
7214
+ if (entry?.inflightToken === token) entry.settled = settled;
7146
7215
  return null;
7147
7216
  };
7148
7217
  /**
@@ -7452,7 +7521,10 @@ function createSuperDocUI(options) {
7452
7521
  if (hostSelection && info && typeof info === "object") selectionHostSnapshots.set(info, hostSelection);
7453
7522
  return info;
7454
7523
  };
7455
- const result = current({ includeText: true });
7524
+ const result = current({
7525
+ includeText: true,
7526
+ ...getEditor()?.editorVersion === 2 ? { offsetSpace: "selection" } : {}
7527
+ });
7456
7528
  return isPromiseLike(result) ? Promise.resolve(result).then(tag) : tag(result);
7457
7529
  }
7458
7530
  /** True when a host sync snapshot is a resolved collapsed caret (not a range). */
@@ -7589,7 +7661,10 @@ function createSuperDocUI(options) {
7589
7661
  }
7590
7662
  let raw;
7591
7663
  try {
7592
- raw = current.call(selectionApi, { includeText: true });
7664
+ raw = current.call(selectionApi, {
7665
+ includeText: true,
7666
+ ...getEditor()?.editorVersion === 2 ? { offsetSpace: "selection" } : {}
7667
+ });
7593
7668
  if (isPromiseLike(raw)) raw = await Promise.resolve(raw);
7594
7669
  } catch {
7595
7670
  raw = null;
@@ -7607,9 +7682,26 @@ function createSuperDocUI(options) {
7607
7682
  recompute();
7608
7683
  return selection;
7609
7684
  };
7685
+ const realComments = (items) => items.filter((item) => !isSyntheticTrackedChangeCommentLaneItem(item));
7686
+ let lastWindowComments = null;
7687
+ const realCommentsOfWindow = (window) => {
7688
+ const items = window?.commentItems;
7689
+ if (!Array.isArray(items)) return null;
7690
+ const last = lastWindowComments;
7691
+ if (last?.source === items && shallowEqual(last.rows, items)) return last.real;
7692
+ lastWindowComments = {
7693
+ source: items,
7694
+ rows: [...items],
7695
+ real: realComments(items)
7696
+ };
7697
+ return lastWindowComments.real;
7698
+ };
7610
7699
  const readCommentsDirectory = () => {
7611
7700
  const listComments = hostMethod(getDoc()?.comments, "list");
7612
- return readAsync("comments", contentToken(), () => listComments ? listComments() : void 0, (raw) => raw && Array.isArray(raw.items) ? raw.items : []);
7701
+ return readAsync("comments", contentToken(), () => listComments ? listComments() : void 0, (raw) => {
7702
+ const items = isPlainObject(raw) ? raw.items : void 0;
7703
+ return Array.isArray(items) ? realComments(items) : [];
7704
+ });
7613
7705
  };
7614
7706
  const readTrackChangesDirectory = (key = "trackChanges") => {
7615
7707
  const token = contentToken();
@@ -7642,7 +7734,7 @@ function createSuperDocUI(options) {
7642
7734
  const reviewWindow = getV2ReviewWindowSnapshot();
7643
7735
  const hasWindowFeed = hasV2ReviewWindowFeed();
7644
7736
  const catalog = !hasWindowFeed ? readCommentsDirectory() : null;
7645
- const windowItems = hasWindowFeed && Array.isArray(reviewWindow?.commentItems) ? reviewWindow.commentItems : null;
7737
+ const windowItems = hasWindowFeed ? realCommentsOfWindow(reviewWindow) : null;
7646
7738
  const value = hasWindowFeed ? windowItems : catalog.value;
7647
7739
  const listStatus = hasWindowFeed ? reviewWindowSliceStatus(reviewWindow) : catalog.status;
7648
7740
  const items = value ?? [];
@@ -14705,6 +14797,92 @@ function createSuperDocUI(options) {
14705
14797
  }
14706
14798
  }, 120);
14707
14799
  };
14800
+ const updateStyleToMatchSelection = (input) => {
14801
+ const mode = readDocumentMode();
14802
+ if (mode === "viewing") return failedReceipt("The document is read-only.", "DOCUMENT_READONLY");
14803
+ if (mode === "suggesting") return failedReceipt("A style definition cannot be changed as a tracked suggestion.", "PRECONDITION_FAILED");
14804
+ const sourceToken = contentToken();
14805
+ const readPositionKey = () => {
14806
+ const snapshot = readHostSelectionSnapshot();
14807
+ if (!snapshot) return null;
14808
+ return JSON.stringify({
14809
+ story: snapshot.story,
14810
+ tableCells: snapshot.tableCells,
14811
+ points: ["anchor", "focus"].map((side) => {
14812
+ const point = snapshot[side];
14813
+ return point ? [
14814
+ point.blockId,
14815
+ point.blockOffset,
14816
+ point.trackedBlockOffset,
14817
+ point.story,
14818
+ point.inlineAffinity,
14819
+ point.outsideContentControl,
14820
+ point.visualCaret
14821
+ ] : null;
14822
+ })
14823
+ });
14824
+ };
14825
+ const requestedPosition = readPositionKey();
14826
+ const stale = failedReceipt("The selection or document changed while reading its style.", "REVISION_MISMATCH");
14827
+ return readSelectionInfoFresh().then((selection) => {
14828
+ const selectionToken = selectionKey(selection);
14829
+ const isCurrent = () => !disposed && contentToken() === sourceToken && readPositionKey() === requestedPosition && selectionKey(state.selection) === selectionToken && readDocumentMode() === mode;
14830
+ if (!isCurrent()) return stale;
14831
+ const active = computeActiveParagraphStyle(selection, getStyleCatalog().cache).style;
14832
+ const story = storyLocatorSignature(selectionStory(selection));
14833
+ const keys = [styleCatalogReadKey({ includePreview: true }), ...selectionBlockIds(selection).map((id) => `node:${story}:${id}`)];
14834
+ const unreadable = failedReceipt("The selection formatting could not be read.", "PRECONDITION_FAILED");
14835
+ const run = (active) => {
14836
+ if (keys.some((key) => {
14837
+ const entry = asyncReads.get(key);
14838
+ return entry && (!entry.hasSettled || entry.token !== sourceToken || entry.failedToken === sourceToken);
14839
+ })) return unreadable;
14840
+ const styleId = input?.styleId ?? active.styleId;
14841
+ if (!styleId) return failedReceipt("Choose a style to update: the selection does not have a single paragraph style.", "INVALID_TARGET");
14842
+ const doc = getDoc();
14843
+ const readSelection = resolveDocOperation(doc, "format.readSelection");
14844
+ const apply = resolveDocOperation(doc, "styles.apply");
14845
+ if (!readSelection || !apply) return failedReceipt("Updating a style to match the selection is unavailable.");
14846
+ let reads;
14847
+ try {
14848
+ reads = [readSelection({
14849
+ selection: "current",
14850
+ keys: STYLE_MATCH_READ_KEYS,
14851
+ view: "effective"
14852
+ }), readSelection({
14853
+ selection: "current",
14854
+ keys: STYLE_MATCH_READ_KEYS,
14855
+ view: "direct"
14856
+ })];
14857
+ } catch {
14858
+ return unreadable;
14859
+ }
14860
+ return Promise.all(reads).then(([effective, direct]) => {
14861
+ if (!isCurrent()) return stale;
14862
+ const patch = buildStyleMatchPatch(effective, direct, { selectionUsesStyle: !active.mixed && active.styleId === styleId });
14863
+ if (Object.keys(patch).length === 0) return failedReceipt("The selection already matches the style.", "NO_OP");
14864
+ const fallback = failedReceipt("Updating the style failed.");
14865
+ return safeCall(() => settleWorkflowReceipt(apply({
14866
+ target: {
14867
+ scope: "style",
14868
+ styleId,
14869
+ channel: "run"
14870
+ },
14871
+ patch
14872
+ }), fallback), fallback);
14873
+ }, () => unreadable);
14874
+ };
14875
+ const pending = keys.flatMap((key) => {
14876
+ const entry = asyncReads.get(key);
14877
+ return entry?.inflightToken === sourceToken && entry.settled ? [entry.settled] : [];
14878
+ });
14879
+ if (pending.length === 0) return run(active);
14880
+ return Promise.all(pending).then(() => {
14881
+ if (!isCurrent()) return stale;
14882
+ return run(computeActiveParagraphStyle(selection, getStyleCatalog().cache).style);
14883
+ });
14884
+ });
14885
+ };
14708
14886
  const stylesSub = sliceHandle((s) => s.styles);
14709
14887
  const stylesSnap = snapshotHandle(stylesSub);
14710
14888
  const styles = {
@@ -14717,7 +14895,8 @@ function createSuperDocUI(options) {
14717
14895
  return readStyleCatalogLive(options).value;
14718
14896
  },
14719
14897
  getQuickGallery: () => stylesSub.get().quickGallery,
14720
- getActiveParagraphStyle: () => computeActiveParagraphStyle(state.selection, getStyleCatalog().cache).style
14898
+ getActiveParagraphStyle: () => computeActiveParagraphStyle(state.selection, getStyleCatalog().cache).style,
14899
+ updateToMatchSelection: (input) => updateStyleToMatchSelection(input)
14721
14900
  };
14722
14901
  const openWatermarkSurface = hostMethod(superdoc, "openSurface");
14723
14902
  const watermark = createWatermarkController({
@@ -15124,6 +15303,12 @@ Object.defineProperty(exports, "isPlainObject", {
15124
15303
  return isPlainObject;
15125
15304
  }
15126
15305
  });
15306
+ Object.defineProperty(exports, "isSyntheticTrackedChangeCommentLaneItem", {
15307
+ enumerable: true,
15308
+ get: function() {
15309
+ return isSyntheticTrackedChangeCommentLaneItem;
15310
+ }
15311
+ });
15127
15312
  Object.defineProperty(exports, "isWorkflowRecordingEnabled", {
15128
15313
  enumerable: true,
15129
15314
  get: function() {
@@ -19,7 +19,7 @@ var COLLABORATION_UPGRADE_ENGINE_MINIMUM_NODE_MAJOR = 20;
19
19
  var PRIVATE_ENGINE_INFO = (0, _superdoc_docx_engine_collaboration_upgrade_engine.getCollaborationUpgradeEngineInfo)();
20
20
  var ENGINE_INFO = Object.freeze({
21
21
  ...PRIVATE_ENGINE_INFO,
22
- superdocVersion: "2.27.0-next.1",
22
+ superdocVersion: "2.27.0-next.11",
23
23
  roomSchemaVersion: Object.freeze({ ...PRIVATE_ENGINE_INFO.roomSchemaVersion }),
24
24
  supportedBundleVersions: SUPPORTED_COLLABORATION_UPGRADE_BUNDLE_VERSIONS,
25
25
  supportedV1ReaderContractVersions: SUPPORTED_V1_READER_CONTRACT_VERSIONS
@@ -18,7 +18,7 @@ var COLLABORATION_UPGRADE_ENGINE_MINIMUM_NODE_MAJOR = 20;
18
18
  var PRIVATE_ENGINE_INFO = getCollaborationUpgradeEngineInfo$1();
19
19
  var ENGINE_INFO = Object.freeze({
20
20
  ...PRIVATE_ENGINE_INFO,
21
- superdocVersion: "2.27.0-next.1",
21
+ superdocVersion: "2.27.0-next.11",
22
22
  roomSchemaVersion: Object.freeze({ ...PRIVATE_ENGINE_INFO.roomSchemaVersion }),
23
23
  supportedBundleVersions: SUPPORTED_COLLABORATION_UPGRADE_BUNDLE_VERSIONS,
24
24
  supportedV1ReaderContractVersions: SUPPORTED_V1_READER_CONTRACT_VERSIONS
@@ -42,7 +42,7 @@ export declare const OPERATION_DEFINITIONS: {
42
42
  };
43
43
  readonly 'styles.apply': {
44
44
  readonly memberPath: "styles.apply";
45
- readonly description: "Apply document-level default style changes to the active Style Definitions part. Targets docDefaults run and paragraph channels with set-style patch semantics.";
45
+ readonly description: "Apply style changes to the active Style Definitions part. Targets docDefaults run and paragraph channels, or the run properties of an existing named paragraph style (scope \"style\"), with set-style patch semantics. Redefining a named style updates every paragraph that references it; its linked character style receives the same patch, null removes a property so the style inherits it again, and an object value replaces that property whole.";
46
46
  readonly expectedResult: "Returns a StylesApplyReceipt with per-channel success/failure details for each property change.";
47
47
  readonly requiresDocumentContext: true;
48
48
  readonly metadata: CommandStaticMetadata;
@@ -2523,7 +2523,7 @@ export declare const OPERATION_DEFINITIONS: {
2523
2523
  readonly 'contentControls.validateWordCompatibility': {
2524
2524
  readonly memberPath: "contentControls.validateWordCompatibility";
2525
2525
  readonly description: "Validate a content control for Word compatibility issues.";
2526
- readonly expectedResult: "Returns a compatibility result with diagnostics.";
2526
+ readonly expectedResult: "Returns read-only diagnostics for incompatible content structure and metadata or configuration issues. `compatible` is false only when a diagnostic has error severity; warnings leave it true.";
2527
2527
  readonly requiresDocumentContext: true;
2528
2528
  readonly metadata: CommandStaticMetadata;
2529
2529
  readonly referenceDocPath: "content-controls/validate-word-compatibility.mdx";
@@ -43,12 +43,6 @@ export interface SDHistoryCollaborationMeta {
43
43
  * Result of a history.undo or history.redo action.
44
44
  * Mirrors PlanReceipt's revision shape for consistency.
45
45
  *
46
- * AIDEV-NOTE: this shape carries optional ref-effect
47
- * fields. v1 callers leave them undefined; v2 (`V2HistoryAdapter`) populates
48
- * them from the stored semantic delta of the undone/redone transaction so
49
- * callers' held refs stay accurate across history actions. Never make any
50
- * of the new fields required — v1 adapters MUST keep compiling unchanged.
51
- *
52
46
  * The shape also carries three optional collaboration fields:
53
47
  * `status`, `diagnosticCode`, and `collaboration`. v1 leaves them
54
48
  * undefined; collaborative v2 sessions populate them so callers can react