@stll/folio-core 0.59.0 → 0.60.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/README.md +10 -1
  2. package/dist/compat/eigenpal.d.ts +2 -2
  3. package/dist/compat/eigenpal.js +2 -2
  4. package/dist/controller/canonicalSession.js +3 -2
  5. package/dist/controller/canonicalStructure.js +10 -7
  6. package/dist/controller/fontReadiness.js +1 -1
  7. package/dist/controller/layoutPipeline.js +7 -1
  8. package/dist/controller/layoutSession.d.ts +2 -0
  9. package/dist/display-list/build/fontTable.js +7 -5
  10. package/dist/display-list/build/furniture.d.ts +1 -0
  11. package/dist/display-list/build/furniture.js +1 -0
  12. package/dist/display-list/build/watermarkPrimitives.d.ts +2 -1
  13. package/dist/display-list/build/watermarkPrimitives.js +9 -3
  14. package/dist/display-list/editor/pageRenderer.js +1 -0
  15. package/dist/docx/bookmarkIdNormalization.d.ts +10 -0
  16. package/dist/docx/bookmarkIdNormalization.js +100 -0
  17. package/dist/docx/bookmarkParser.js +4 -2
  18. package/dist/docx/importedIdCollisions.d.ts +136 -0
  19. package/dist/docx/importedIdCollisions.js +215 -0
  20. package/dist/docx/numberingParser.js +2 -2
  21. package/dist/docx/paragraphPropertySource.js +1 -2
  22. package/dist/docx/parseContext.d.ts +2 -1
  23. package/dist/docx/parseContext.js +1 -0
  24. package/dist/docx/parseWarningMessage.js +1 -0
  25. package/dist/docx/parser.js +5 -1
  26. package/dist/docx/relsParser.js +1 -1
  27. package/dist/docx/rezip.js +2 -1
  28. package/dist/docx/selectedXmlIdentityScan.d.ts +10 -0
  29. package/dist/docx/selectedXmlIdentityScan.js +36 -0
  30. package/dist/docx/selectiveSave.js +5 -4
  31. package/dist/docx/selectiveXmlPatch.js +44 -52
  32. package/dist/docx/serializer/commentSerializer.d.ts +3 -1
  33. package/dist/docx/serializer/commentSerializer.js +11 -2
  34. package/dist/docx/serializer/runSerializer.js +2 -2
  35. package/dist/docx/serializer/trackedChangeAttributes.d.ts +1 -3
  36. package/dist/docx/serializer/trackedChangeAttributes.js +1 -11
  37. package/dist/fonts/embeddedFontFingerprint.d.ts +5 -0
  38. package/dist/fonts/embeddedFontFingerprint.js +18 -0
  39. package/dist/fonts/embeddedFonts.d.ts +1 -1
  40. package/dist/fonts/embeddedFonts.js +1 -1
  41. package/dist/fonts/headlessMeasure.js +0 -0
  42. package/dist/headless-layout.js +1 -0
  43. package/dist/index.d.ts +2 -2
  44. package/dist/index.js +2 -2
  45. package/dist/layout-bridge/convert/flowConversionShared.d.ts +2 -0
  46. package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -1
  47. package/dist/layout-bridge/convert/footnoteLayout.js +3 -0
  48. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
  49. package/dist/layout-bridge/convert/headerFooterLayout.js +2 -0
  50. package/dist/layout-bridge/convert/paragraphConversion.js +8 -1
  51. package/dist/layout-bridge/convert/toFlowBlocks.js +25 -1
  52. package/dist/layout-engine/measure/cache.js +39 -7
  53. package/dist/layout-engine/measure/lineTypography.js +1 -0
  54. package/dist/layout-engine/measure/listMarkerWidth.d.ts +1 -0
  55. package/dist/layout-engine/measure/listMarkerWidth.js +1 -0
  56. package/dist/layout-engine/measure/measureContainer.js +3 -4
  57. package/dist/layout-engine/measure/measureHelpers.d.ts +6 -9
  58. package/dist/layout-engine/measure/measureHelpers.js +21 -18
  59. package/dist/layout-engine/measure/measureParagraph.js +2 -0
  60. package/dist/layout-engine/measure/measureTypes.d.ts +2 -0
  61. package/dist/layout-engine/types.d.ts +4 -1
  62. package/dist/layout-painter/renderPage.d.ts +1 -0
  63. package/dist/layout-painter/renderPage.js +11 -3
  64. package/dist/layout-painter/renderParagraph.js +13 -3
  65. package/dist/layout-painter/renderWatermark.d.ts +1 -0
  66. package/dist/layout-painter/renderWatermark.js +7 -3
  67. package/dist/managers/editorShortcuts.d.ts +2 -1
  68. package/dist/managers/editorShortcuts.js +2 -1
  69. package/dist/prosemirror/canonicalSelectionRange.d.ts +4 -1
  70. package/dist/prosemirror/canonicalSelectionRange.js +9 -3
  71. package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.d.ts +8 -2
  72. package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.js +39 -3
  73. package/dist/prosemirror/extensions/features/EmptyParagraphFormatExtension.d.ts +8 -2
  74. package/dist/prosemirror/extensions/features/EmptyParagraphFormatExtension.js +4 -3
  75. package/dist/prosemirror/extensions/features/ImageDragExtension.js +4 -3
  76. package/dist/prosemirror/extensions/features/JoinedRunStyleExtension.js +4 -3
  77. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +9 -3
  78. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +6 -5
  79. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +8 -2
  80. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +11 -3
  81. package/dist/prosemirror/extensions/marks/RunIdentityExtension.js +4 -3
  82. package/dist/prosemirror/extensions/marks/markUtils.js +1 -1
  83. package/dist/prosemirror/pluginKeyOwner.d.ts +13 -0
  84. package/dist/prosemirror/pluginKeyOwner.js +35 -0
  85. package/dist/prosemirror/plugins/aiCitationDecorations.d.ts +11 -4
  86. package/dist/prosemirror/plugins/aiCitationDecorations.js +16 -3
  87. package/dist/prosemirror/plugins/aiSuggestionDecorations.d.ts +11 -4
  88. package/dist/prosemirror/plugins/aiSuggestionDecorations.js +18 -3
  89. package/dist/prosemirror/plugins/anonymizationDecorations.d.ts +11 -11
  90. package/dist/prosemirror/plugins/anonymizationDecorations.js +23 -13
  91. package/dist/prosemirror/plugins/autocompleteSuggestion.d.ts +8 -2
  92. package/dist/prosemirror/plugins/autocompleteSuggestion.js +27 -5
  93. package/dist/prosemirror/plugins/contentControlWidgets.d.ts +8 -2
  94. package/dist/prosemirror/plugins/contentControlWidgets.js +4 -3
  95. package/dist/prosemirror/plugins/createDocScanPlugin.d.ts +6 -5
  96. package/dist/prosemirror/plugins/createDocScanPlugin.js +1 -1
  97. package/dist/prosemirror/plugins/documentNumbering.js +5 -3
  98. package/dist/prosemirror/plugins/documentStyleState.d.ts +8 -2
  99. package/dist/prosemirror/plugins/documentStyleState.js +2 -2
  100. package/dist/prosemirror/plugins/documentStyles.js +1 -1
  101. package/dist/prosemirror/plugins/selectionTracker.d.ts +8 -2
  102. package/dist/prosemirror/plugins/selectionTracker.js +4 -3
  103. package/dist/prosemirror/plugins/suggestionMode.d.ts +8 -2
  104. package/dist/prosemirror/plugins/suggestionMode.js +4 -3
  105. package/dist/prosemirror/plugins/templateDirectives.d.ts +8 -2
  106. package/dist/prosemirror/plugins/templateDirectives.js +19 -4
  107. package/dist/prosemirror/plugins/templatePreviewValues.d.ts +8 -2
  108. package/dist/prosemirror/plugins/templatePreviewValues.js +27 -5
  109. package/dist/prosemirror/plugins/templateSlashMenu.d.ts +8 -2
  110. package/dist/prosemirror/plugins/templateSlashMenu.js +23 -5
  111. package/dist/prosemirror/plugins/transactionInvariants.js +23 -3
  112. package/dist/utils/fontLoader.d.ts +2 -2
  113. package/dist/utils/fontLoader.js +2 -7
  114. package/dist/utils/fontResolver.d.ts +7 -3
  115. package/dist/utils/fontResolver.js +6 -8
  116. package/package.json +24 -8
@@ -1,3 +1,4 @@
1
+ import { embeddedFontFingerprint } from "../fonts/embeddedFontFingerprint.js";
1
2
  import "../layout-engine/measure/floatingLineClearance.js";
2
3
  import { resolveFloatingTableX } from "../layout-engine/measure/floatingTablePosition.js";
3
4
  import { rectsToFloatingZones } from "../layout-engine/measure/floatingZones.js";
@@ -59,7 +60,7 @@ function applyPageStyles(element, width, height, options) {
59
60
  element.style.border = "none";
60
61
  element.style.boxShadow = "none";
61
62
  element.style.outline = "none";
62
- element.style.fontFamily = getDefaultPageFontFamily();
63
+ element.style.fontFamily = resolveFontFamily(DEFAULT_FONT_FAMILY, options).cssFallback;
63
64
  element.style.fontSize = `${1056 / 72}px`;
64
65
  element.style.color = "var(--doc-canvas-text, #000)";
65
66
  }
@@ -179,7 +180,10 @@ function syncPageWatermarkOverlay(pageEl, page, options, doc) {
179
180
  for (const stale of Array.from(pageEl.querySelectorAll(":scope > .layout-page-watermark"))) stale.remove();
180
181
  const watermark = options.watermark;
181
182
  if (!watermark) return;
182
- const overlay = renderWatermarkLayer(watermark, page, doc, options.watermarkImageSrc !== void 0 ? { imageSrc: options.watermarkImageSrc } : {});
183
+ const overlay = renderWatermarkLayer(watermark, page, doc, {
184
+ ...options.watermarkImageSrc !== void 0 ? { imageSrc: options.watermarkImageSrc } : {},
185
+ ...options.embeddedFontFamilyMap ? { embeddedFontFamilyMap: options.embeddedFontFamilyMap } : {}
186
+ });
183
187
  if (!overlay) return;
184
188
  const contentEl = pageEl.querySelector(`:scope > .${PAGE_CLASS_NAMES.content}`);
185
189
  if (contentEl) {
@@ -785,7 +789,10 @@ function renderPage(page, context, options = {}) {
785
789
  const pageBorderEl = renderPageBorderOverlay(page, options, doc);
786
790
  if (pageBorderEl && options.pageBorders?.zOrder === "back") pageEl.append(pageBorderEl);
787
791
  if (options.watermark) {
788
- const watermarkEl = renderWatermarkLayer(options.watermark, page, doc, options.watermarkImageSrc !== void 0 ? { imageSrc: options.watermarkImageSrc } : {});
792
+ const watermarkEl = renderWatermarkLayer(options.watermark, page, doc, {
793
+ ...options.watermarkImageSrc !== void 0 ? { imageSrc: options.watermarkImageSrc } : {},
794
+ ...options.embeddedFontFamilyMap ? { embeddedFontFamilyMap: options.embeddedFontFamilyMap } : {}
795
+ });
789
796
  if (watermarkEl) pageEl.append(watermarkEl);
790
797
  }
791
798
  const contentEl = doc.createElement("div");
@@ -1403,6 +1410,7 @@ function headerFooterContentFingerprint(prefix, entries) {
1403
1410
  */
1404
1411
  function computeOptionsHash(options) {
1405
1412
  const parts = [];
1413
+ if (options.embeddedFontFamilyMap) parts.push(`embedded-fonts:${embeddedFontFingerprint(options.embeddedFontFamilyMap)}`);
1406
1414
  if (options.headerContent) parts.push(`hdr:${options.headerContent.blocks.length},${options.headerContent.height},${options.headerContent.visualTop ?? 0},${options.headerContent.visualBottom ?? options.headerContent.height},${options.headerContent.rId ?? ""},${options.headerContent.textSig ?? ""}`);
1407
1415
  if (options.footerContent) parts.push(`ftr:${options.footerContent.blocks.length},${options.footerContent.height},${options.footerContent.visualTop ?? 0},${options.footerContent.visualBottom ?? options.footerContent.height},${options.footerContent.rId ?? ""},${options.footerContent.textSig ?? ""}`);
1408
1416
  parts.push(...headerFooterContentFingerprint("hdr-map", options.headerContentByRId));
@@ -190,7 +190,10 @@ const UNICODE_BIDI_BY_WRAPPER_CONTROL = {
190
190
  * Apply text run styles to an element
191
191
  */
192
192
  function applyRunStyles(element, run) {
193
- if (run.fontFamily) element.style.fontFamily = resolveFontFamily(run.fontFamily, run.alternateFontFamily).cssFallback;
193
+ if (run.fontFamily) element.style.fontFamily = resolveFontFamily(run.fontFamily, {
194
+ alternateFontName: run.alternateFontFamily,
195
+ embeddedFontFamilyMap: run.embeddedFontFamilyMap
196
+ }).cssFallback;
194
197
  if (run.fontSize) element.style.fontSize = `${fontSizePtToPx(run.fontSize)}px`;
195
198
  if (run.bold) element.style.fontWeight = "700";
196
199
  if (run.italic) element.style.fontStyle = "italic";
@@ -1043,6 +1046,7 @@ function runVisualSpaceContraction({ text, horizontalScale, visualSpaceContracti
1043
1046
  */
1044
1047
  function runMeasureStyle(run) {
1045
1048
  return {
1049
+ ...run.embeddedFontFamilyMap ? { embeddedFontFamilyMap: run.embeddedFontFamilyMap } : {},
1046
1050
  ...run.bold !== void 0 ? { bold: run.bold } : {},
1047
1051
  ...run.italic !== void 0 ? { italic: run.italic } : {},
1048
1052
  ...run.letterSpacing !== void 0 ? { letterSpacing: run.letterSpacing } : {},
@@ -1168,7 +1172,10 @@ function createTextMeasurer(doc) {
1168
1172
  if (style.smallCaps) fontPrefixParts.push("small-caps");
1169
1173
  if (bold) fontPrefixParts.push("700");
1170
1174
  fontPrefixParts.push(`${size * 96 / 72}px`);
1171
- return `${fontPrefixParts.join(" ")} ${resolveFontFamily(family, alternateFamily).cssFallback}`;
1175
+ return `${fontPrefixParts.join(" ")} ${resolveFontFamily(family, {
1176
+ alternateFontName: alternateFamily,
1177
+ embeddedFontFamilyMap: style.embeddedFontFamilyMap
1178
+ }).cssFallback}`;
1172
1179
  };
1173
1180
  const baseFont = fontString(fontFamily, fontSize, style.bold, style.italic, style.alternateFontFamily);
1174
1181
  const complex = resolveComplexScriptFormatting(style);
@@ -1670,7 +1677,10 @@ function renderListMarker({ marker, inlineWidth, visualOffset, doc, formatting,
1670
1677
  const span = doc.createElement("span");
1671
1678
  span.className = "layout-list-marker";
1672
1679
  span.style.display = "inline-block";
1673
- span.style.fontFamily = resolveFontFamily(formatting.fontFamily, formatting.alternateFontFamily).cssFallback;
1680
+ span.style.fontFamily = resolveFontFamily(formatting.fontFamily, {
1681
+ alternateFontName: formatting.alternateFontFamily,
1682
+ embeddedFontFamilyMap: formatting.embeddedFontFamilyMap
1683
+ }).cssFallback;
1674
1684
  if (formatting.fontSize) span.style.fontSize = `${formatting.fontSize * 96 / 72}px`;
1675
1685
  if (formatting.bold !== void 0) span.style.fontWeight = formatting.bold ? "700" : "normal";
1676
1686
  if (formatting.italic !== void 0) span.style.fontStyle = formatting.italic ? "italic" : "normal";
@@ -2,6 +2,7 @@ import { document_d_exports } from "../types/document.js";
2
2
  import { Page } from "../layout-engine/types.js";
3
3
  //#region src/layout-painter/renderWatermark.d.ts
4
4
  type RenderWatermarkOptions = {
5
+ embeddedFontFamilyMap?: ReadonlyMap<string, string>;
5
6
  /** Resolved image src for picture watermarks (`data:` / `blob:` only). */
6
7
  imageSrc?: string;
7
8
  };
@@ -25,7 +25,11 @@ function renderWatermarkLayer(watermark, page, doc, options = {}) {
25
25
  layer.style.display = "flex";
26
26
  layer.style.alignItems = "center";
27
27
  layer.style.justifyContent = "center";
28
- if (watermark.kind === "text") layer.append(renderTextWatermark(watermark, doc));
28
+ if (watermark.kind === "text") layer.append(renderTextWatermark({
29
+ watermark,
30
+ doc,
31
+ options
32
+ }));
29
33
  else {
30
34
  const imageSrc = options.imageSrc;
31
35
  if (!imageSrc) return null;
@@ -33,10 +37,10 @@ function renderWatermarkLayer(watermark, page, doc, options = {}) {
33
37
  }
34
38
  return layer;
35
39
  }
36
- function renderTextWatermark(watermark, doc) {
40
+ function renderTextWatermark({ watermark, doc, options }) {
37
41
  const el = doc.createElement("div");
38
42
  el.textContent = watermark.text;
39
- el.style.fontFamily = resolveFontFamily(watermark.font ?? "Calibri").cssFallback;
43
+ el.style.fontFamily = resolveFontFamily(watermark.font ?? "Calibri", options).cssFallback;
40
44
  el.style.fontSize = "144px";
41
45
  el.style.fontWeight = "bold";
42
46
  el.style.whiteSpace = "nowrap";
@@ -1,6 +1,7 @@
1
1
  import { HistoryShortcutOwner } from "../prosemirror/extensions/core/HistoryExtension.js";
2
2
  import { EditorState, Transaction } from "prosemirror-state";
3
3
  //#region src/managers/editorShortcuts.d.ts
4
+ declare const DEFAULT_KEYBOARD_SHORTCUT_SCOPE = "editor";
4
5
  declare function isMacPlatform(): boolean;
5
6
  /**
6
7
  * `target` is an input-like element that the user is typing into. We must
@@ -82,4 +83,4 @@ declare function isKeydownInShortcutScope(event: {
82
83
  */
83
84
  declare function deleteSelectedTable(state: EditorState, dispatch: (tr: Transaction) => void): boolean;
84
85
  //#endregion
85
- export { ClassifyEditorKeydownOptions, EditorKeydownIntent, HostShortcut, IsKeydownInShortcutScopeOptions, KeyboardShortcutScope, ShortcutScopeRoot, classifyEditorKeydown, deleteSelectedTable, historyShortcutOwner, isFocusInInputLike, isKeydownInShortcutScope, isMacPlatform };
86
+ export { ClassifyEditorKeydownOptions, DEFAULT_KEYBOARD_SHORTCUT_SCOPE, EditorKeydownIntent, HostShortcut, IsKeydownInShortcutScopeOptions, KeyboardShortcutScope, ShortcutScopeRoot, classifyEditorKeydown, deleteSelectedTable, historyShortcutOwner, isFocusInInputLike, isKeydownInShortcutScope, isMacPlatform };
@@ -1,6 +1,7 @@
1
1
  import { deleteTable } from "../prosemirror/commands/table.js";
2
2
  import { getTableContext } from "../prosemirror/extensions/nodes/TableExtension.js";
3
3
  //#region src/managers/editorShortcuts.ts
4
+ const DEFAULT_KEYBOARD_SHORTCUT_SCOPE = "editor";
4
5
  function isMacPlatform() {
5
6
  if (typeof navigator === "undefined") return false;
6
7
  return /Mac|iPod|iPhone|iPad/u.test(navigator.platform);
@@ -78,4 +79,4 @@ function deleteSelectedTable(state, dispatch) {
78
79
  return false;
79
80
  }
80
81
  //#endregion
81
- export { classifyEditorKeydown, deleteSelectedTable, historyShortcutOwner, isFocusInInputLike, isKeydownInShortcutScope, isMacPlatform };
82
+ export { DEFAULT_KEYBOARD_SHORTCUT_SCOPE, classifyEditorKeydown, deleteSelectedTable, historyShortcutOwner, isFocusInInputLike, isKeydownInShortcutScope, isMacPlatform };
@@ -1,9 +1,12 @@
1
+ import { Node } from "prosemirror-model";
1
2
  import { EditorState } from "prosemirror-state";
2
3
  //#region src/prosemirror/canonicalSelectionRange.d.ts
4
+ /** Document edges denote the first and last editable content gaps. */
5
+ declare const canonicalContentPosition: (doc: Node, position: number) => number;
3
6
  /** Whole-document selection addresses paragraph content rather than document edges. */
4
7
  declare const canonicalSelectionRange: ({ doc, selection }: EditorState) => {
5
8
  from: number;
6
9
  to: number;
7
10
  };
8
11
  //#endregion
9
- export { canonicalSelectionRange };
12
+ export { canonicalContentPosition, canonicalSelectionRange };
@@ -1,10 +1,16 @@
1
1
  import { AllSelection, Selection } from "prosemirror-state";
2
2
  //#region src/prosemirror/canonicalSelectionRange.ts
3
+ /** Document edges denote the first and last editable content gaps. */
4
+ const canonicalContentPosition = (doc, position) => {
5
+ if (position === 0) return Selection.atStart(doc).from;
6
+ if (position === doc.content.size) return Selection.atEnd(doc).to;
7
+ return position;
8
+ };
3
9
  /** Whole-document selection addresses paragraph content rather than document edges. */
4
10
  const canonicalSelectionRange = ({ doc, selection }) => {
5
11
  if (selection instanceof AllSelection) return {
6
- from: Selection.atStart(doc).from,
7
- to: Selection.atEnd(doc).to
12
+ from: canonicalContentPosition(doc, selection.from),
13
+ to: canonicalContentPosition(doc, selection.to)
8
14
  };
9
15
  return {
10
16
  from: selection.from,
@@ -12,4 +18,4 @@ const canonicalSelectionRange = ({ doc, selection }) => {
12
18
  };
13
19
  };
14
20
  //#endregion
15
- export { canonicalSelectionRange };
21
+ export { canonicalContentPosition, canonicalSelectionRange };
@@ -1,7 +1,13 @@
1
1
  import { Extension } from "../types.js";
2
- import { EditorState, PluginKey } from "prosemirror-state";
2
+ import { EditorState, Plugin } from "prosemirror-state";
3
3
  //#region src/prosemirror/extensions/features/AutoBidiDetectionExtension.d.ts
4
- declare const autoBidiDetectionKey: PluginKey<any>;
4
+ declare const autoBidiDetectionKey: Readonly<{
5
+ key: string;
6
+ createKey: () => import("prosemirror-state").PluginKey<unknown>;
7
+ owns: (plugin: Plugin<unknown>) => boolean;
8
+ get: (state: EditorState) => Plugin<unknown> | undefined;
9
+ getState: (state: EditorState) => unknown;
10
+ }>;
5
11
  /**
6
12
  * Imperatively apply auto-bidi detection to a freshly built state (initial
7
13
  * load: Markdown import, DOCX import, template). `appendTransaction` does not
@@ -2,11 +2,47 @@ import { setProseParagraphMarkupWithPropertySource } from "../../../docx/paragra
2
2
  import { getTransactionDirtyRange } from "../../../paged-layout/transactionDirtyRange.js";
3
3
  import { detectBaseDirection } from "../../../utils/baseDirection.js";
4
4
  import { directionIsAutoManaged } from "../../paragraphDirection.js";
5
+ import { createPluginKeyOwner } from "../../pluginKeyOwner.js";
5
6
  import { createExtension } from "../create.js";
6
7
  import { ignoreTrackedChanges } from "./ParagraphChangeTrackerExtension.js";
7
- import { Plugin, PluginKey } from "prosemirror-state";
8
+ import { Plugin } from "prosemirror-state";
8
9
  //#region src/prosemirror/extensions/features/AutoBidiDetectionExtension.ts
9
- const autoBidiDetectionKey = new PluginKey("autoBidiDetection");
10
+ /**
11
+ * AutoBidiDetection — sets the paragraph `direction` (to `{ source: "auto" }`)
12
+ * on RTL-led paragraphs that arrived without an explicit direction decision.
13
+ *
14
+ * Why: Folio only ever rendered/exported RTL when a paragraph already carried
15
+ * `w:bidi` (from a Word import) or the user toggled it by hand. Content that
16
+ * never had a flag — AI-generated text, Markdown/HTML/plain-text imports, paste,
17
+ * or just typing Arabic without the shortcut — stayed left-to-right both in the
18
+ * editor and in the exported `.docx`. The ProseMirror doc is the single source
19
+ * of truth for both the layout painter (`toFlowBlocks`) and the serializer
20
+ * (`fromProseDoc`), so writing the attribute here fixes the editor and the Word
21
+ * export from one place.
22
+ *
23
+ * Detection uses the first-strong-character rule (`detectBaseDirection`), the
24
+ * same logic the layout painter uses for base direction.
25
+ *
26
+ * Auto vs. explicit: only "auto-managed" paragraphs are (re-)evaluated — those
27
+ * whose `direction` is undecided (absent) or was set by this detector
28
+ * (`source: "auto"`). A manual user toggle or imported `w:bidi`
29
+ * (`source: "manual"`) is authoritative and never touched. Because the detector
30
+ * re-evaluates its own decisions, replacing an auto-RTL paragraph's text with
31
+ * Latin clears it again (no stale "sticky" RTL).
32
+ *
33
+ * The `direction` discriminated union is editor-runtime state. Its `"auto"`
34
+ * source is a *rendering* aid only — `directionToAuthoredBidi` maps it to
35
+ * `undefined`, so an auto-detected paragraph never serializes `w:bidi` on its
36
+ * own; only a manual decision (an explicit user toggle, or `w:bidi` the
37
+ * source document already carried) is authored content. This keeps a no-edit
38
+ * save byte-identical on direction, while the editor and layout painter still
39
+ * treat `"auto"` as RTL (`directionToBidi` / `directionIsRtl`) so detected
40
+ * text displays correctly before the user ever makes it explicit.
41
+ *
42
+ * Mirrors the appendTransaction + ensure-in-state pattern of
43
+ * `ParaIdAllocatorExtension`.
44
+ */
45
+ const autoBidiDetectionKey = createPluginKeyOwner("autoBidiDetection");
10
46
  const paragraphDirectionalText = (node) => {
11
47
  let text = "";
12
48
  node.descendants((child) => {
@@ -95,7 +131,7 @@ const ensureBaseDirectionInState = (state) => {
95
131
  return state.apply(applyBidiUpdates(state, updates));
96
132
  };
97
133
  const createAutoBidiDetectionPlugin = () => new Plugin({
98
- key: autoBidiDetectionKey,
134
+ key: autoBidiDetectionKey.createKey(),
99
135
  appendTransaction(transactions, _oldState, newState) {
100
136
  if (!transactions.some((t) => t.docChanged)) return null;
101
137
  const [first, ...rest] = transactions;
@@ -1,7 +1,13 @@
1
1
  import { Extension } from "../types.js";
2
- import { PluginKey } from "prosemirror-state";
2
+ import { Plugin } from "prosemirror-state";
3
3
  //#region src/prosemirror/extensions/features/EmptyParagraphFormatExtension.d.ts
4
- declare const emptyParagraphFormatKey: PluginKey<any>;
4
+ declare const emptyParagraphFormatKey: Readonly<{
5
+ key: string;
6
+ createKey: () => import("prosemirror-state").PluginKey<unknown>;
7
+ owns: (plugin: Plugin<unknown>) => boolean;
8
+ get: (state: import("prosemirror-state").EditorState) => Plugin<unknown> | undefined;
9
+ getState: (state: import("prosemirror-state").EditorState) => unknown;
10
+ }>;
5
11
  declare const EmptyParagraphFormatExtension: (options?: Partial<Record<string, unknown>> | undefined) => Extension;
6
12
  //#endregion
7
13
  export { EmptyParagraphFormatExtension, emptyParagraphFormatKey };
@@ -1,6 +1,7 @@
1
+ import { createPluginKeyOwner } from "../../pluginKeyOwner.js";
1
2
  import { createExtension } from "../create.js";
2
3
  import { textFormattingToMarks } from "../marks/markUtils.js";
3
- import { Plugin, PluginKey } from "prosemirror-state";
4
+ import { Plugin } from "prosemirror-state";
4
5
  //#region src/prosemirror/extensions/features/EmptyParagraphFormatExtension.ts
5
6
  /**
6
7
  * EmptyParagraphFormat — re-establishes stored marks for an empty styled
@@ -22,7 +23,7 @@ import { Plugin, PluginKey } from "prosemirror-state";
22
23
  * (bold/italic/color/…). Plain body paragraphs (font + size only) are left
23
24
  * untouched so ordinary typed text stays mark-free and serializes cleanly.
24
25
  */
25
- const emptyParagraphFormatKey = new PluginKey("emptyParagraphFormat");
26
+ const emptyParagraphFormatKey = createPluginKeyOwner("emptyParagraphFormat");
26
27
  /**
27
28
  * Run-formatting properties the painter does NOT reproduce from
28
29
  * `defaultTextFormatting` on its own (it forwards only font family + size).
@@ -37,7 +38,7 @@ function hasNonFontDefaults(dtf) {
37
38
  }
38
39
  function createEmptyParagraphFormatPlugin() {
39
40
  return new Plugin({
40
- key: emptyParagraphFormatKey,
41
+ key: emptyParagraphFormatKey.createKey(),
41
42
  appendTransaction(transactions, _oldState, newState) {
42
43
  if (!transactions.some((t) => t.selectionSet || t.docChanged)) return null;
43
44
  const { selection } = newState;
@@ -1,5 +1,6 @@
1
+ import { createPluginKeyOwner } from "../../pluginKeyOwner.js";
1
2
  import { createExtension } from "../create.js";
2
- import { NodeSelection, Plugin, PluginKey } from "prosemirror-state";
3
+ import { NodeSelection, Plugin } from "prosemirror-state";
3
4
  //#region src/prosemirror/extensions/features/ImageDragExtension.ts
4
5
  /**
5
6
  * Image Drag Extension — handles drag-to-reposition for images
@@ -10,12 +11,12 @@ import { NodeSelection, Plugin, PluginKey } from "prosemirror-state";
10
11
  * - Inline images: reorder within text flow via ProseMirror's default drag
11
12
  * - Custom drag initiation from mousedown on selected images
12
13
  */
13
- const imageDragKey = new PluginKey("imageDrag");
14
+ const imageDragKey = createPluginKeyOwner("imageDrag");
14
15
  const ImageDragExtension = createExtension({
15
16
  name: "imageDrag",
16
17
  onSchemaReady(_ctx) {
17
18
  return { plugins: [new Plugin({
18
- key: imageDragKey,
19
+ key: imageDragKey.createKey(),
19
20
  props: { handleDOMEvents: {
20
21
  dragstart(view, event) {
21
22
  const { selection } = view.state;
@@ -1,5 +1,6 @@
1
+ import { createPluginKeyOwner } from "../../pluginKeyOwner.js";
1
2
  import { createExtension } from "../create.js";
2
- import { Plugin, PluginKey } from "prosemirror-state";
3
+ import { Plugin } from "prosemirror-state";
3
4
  //#region src/prosemirror/extensions/features/JoinedRunStyleExtension.ts
4
5
  /**
5
6
  * Runs that a join moves into another paragraph take that paragraph's style.
@@ -16,7 +17,7 @@ import { Plugin, PluginKey } from "prosemirror-state";
16
17
  * {@link JOINED_RUNS_RESTYLED_META}, and is left as it is.
17
18
  */
18
19
  const JOINED_RUNS_RESTYLED_META = "joinedRunsRestyled";
19
- const pastedTableEndKey = new PluginKey("pastedTableEnd");
20
+ const pastedTableEndKey = createPluginKeyOwner("pastedTableEnd");
20
21
  /**
21
22
  * A table pasted over a story's last paragraph leaves that paragraph, emptied,
22
23
  * after it: a story ends with a paragraph, and the paste replaced the words,
@@ -24,7 +25,7 @@ const pastedTableEndKey = new PluginKey("pastedTableEnd");
24
25
  * paragraph, so the empty one is put back.
25
26
  */
26
27
  const createPastedTableEndPlugin = () => new Plugin({
27
- key: pastedTableEndKey,
28
+ key: pastedTableEndKey.createKey(),
28
29
  appendTransaction(transactions, oldState, newState) {
29
30
  if (!transactions.some((transaction) => transaction.docChanged && transaction.getMeta("uiEvent") === "paste")) return null;
30
31
  if (newState.doc.lastChild?.type.spec["tableRole"] !== "table" || oldState.doc.lastChild?.type.name !== "paragraph") return null;
@@ -1,12 +1,18 @@
1
1
  import { Extension } from "../types.js";
2
2
  import { Node } from "prosemirror-model";
3
- import { EditorState, PluginKey, Transaction } from "prosemirror-state";
3
+ import { EditorState, Plugin, Transaction } from "prosemirror-state";
4
4
  //#region src/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts
5
5
  type ParagraphPropertySourceSeed = {
6
6
  contract: string | null;
7
7
  tokens: ReadonlySet<string>;
8
8
  };
9
- declare const paraIdAllocatorKey: PluginKey<ParagraphPropertySourceSeed>;
9
+ declare const paraIdAllocatorKey: Readonly<{
10
+ key: string;
11
+ createKey: () => import("prosemirror-state").PluginKey<ParagraphPropertySourceSeed>;
12
+ owns: (plugin: Plugin<ParagraphPropertySourceSeed>) => boolean;
13
+ get: (state: EditorState) => Plugin<ParagraphPropertySourceSeed> | undefined;
14
+ getState: (state: EditorState) => ParagraphPropertySourceSeed | undefined;
15
+ }>;
10
16
  /**
11
17
  * Transaction meta asking this plugin to derive the ids it mints from a
12
18
  * caller-supplied seed instead of `Math.random()`.
@@ -17,7 +23,7 @@ declare const paraIdAllocatorKey: PluginKey<ParagraphPropertySourceSeed>;
17
23
  * be deterministic, so it says so on its own transaction rather than leaving
18
24
  * the plugin to guess.
19
25
  */
20
- declare const deterministicParaIdSeedKey: PluginKey<string>;
26
+ declare const deterministicParaIdSeedKey = "stll.folio.meta.deterministicParaIdSeed";
21
27
  /** Ask the allocator to mint reproducible ids for the paragraphs `tr` creates. */
22
28
  declare const requestDeterministicParaIds: (tr: Transaction, seed: string) => Transaction;
23
29
  type ParagraphPropertySourceKeeper = {
@@ -1,11 +1,12 @@
1
1
  import { PROSE_PARAGRAPH_SOURCE_TOKEN_ATTR, getExplicitParagraphPropertySourceTransfers, getProseDocumentParagraphPropertySourceContract, getProseParagraphPropertySourceToken, paragraphPropertySourceTokenMatchesContract, recreateProseNodeWithParagraphPropertySource, setProseParagraphMarkupWithPropertySource, transferProseParagraphPropertySource } from "../../../docx/paragraphPropertySource.js";
2
2
  import { deterministicHexId, generateHexId } from "../../../utils/hexId.js";
3
+ import { createPluginKeyOwner } from "../../pluginKeyOwner.js";
3
4
  import { sweepPositions } from "../../positionSweep.js";
4
5
  import { createExtension } from "../create.js";
5
6
  import { ignoreTrackedChanges } from "./ParagraphChangeTrackerExtension.js";
6
7
  import { panic } from "better-result";
7
8
  import { Fragment } from "prosemirror-model";
8
- import { Plugin, PluginKey } from "prosemirror-state";
9
+ import { Plugin } from "prosemirror-state";
9
10
  //#region src/prosemirror/extensions/features/ParaIdAllocatorExtension.ts
10
11
  /**
11
12
  * ParaIdAllocator — assigns a stable `w14:paraId` to every paragraph.
@@ -44,7 +45,7 @@ import { Plugin, PluginKey } from "prosemirror-state";
44
45
  * split therefore restores the same allocated id instead of minting
45
46
  * another one.
46
47
  */
47
- const paraIdAllocatorKey = new PluginKey("paraIdAllocator");
48
+ const paraIdAllocatorKey = createPluginKeyOwner("paraIdAllocator");
48
49
  /**
49
50
  * Transaction meta asking this plugin to derive the ids it mints from a
50
51
  * caller-supplied seed instead of `Math.random()`.
@@ -55,7 +56,7 @@ const paraIdAllocatorKey = new PluginKey("paraIdAllocator");
55
56
  * be deterministic, so it says so on its own transaction rather than leaving
56
57
  * the plugin to guess.
57
58
  */
58
- const deterministicParaIdSeedKey = new PluginKey("deterministicParaIdSeed");
59
+ const deterministicParaIdSeedKey = "stll.folio.meta.deterministicParaIdSeed";
59
60
  /** Ask the allocator to mint reproducible ids for the paragraphs `tr` creates. */
60
61
  const requestDeterministicParaIds = (tr, seed) => tr.setMeta(deterministicParaIdSeedKey, seed);
61
62
  /**
@@ -297,7 +298,7 @@ const ensureParaIdsInState = (state) => {
297
298
  return state.apply(tr);
298
299
  };
299
300
  const createParaIdAllocatorPlugin = () => new Plugin({
300
- key: paraIdAllocatorKey,
301
+ key: paraIdAllocatorKey.createKey(),
301
302
  state: {
302
303
  init: (_config, state) => collectParagraphPropertySourceSeed(state.doc),
303
304
  apply: (_transaction, seed) => seed
@@ -307,7 +308,7 @@ const createParaIdAllocatorPlugin = () => new Plugin({
307
308
  const paragraphSourceSeed = paraIdAllocatorKey.getState(oldState);
308
309
  if (!paragraphSourceSeed) panic("ParaId allocator lost its paragraph-property source seed");
309
310
  const keeperPositions = mapParagraphKeepers(oldState.doc, transactions, paragraphSourceSeed);
310
- const deterministicSeed = transactions.map((transaction) => transaction.getMeta(deterministicParaIdSeedKey)).find((value) => typeof value === "string") ?? null;
311
+ const deterministicSeed = transactions.map((transaction) => transaction.getMeta("stll.folio.meta.deterministicParaIdSeed")).find((value) => typeof value === "string") ?? null;
311
312
  const census = collectParagraphCensus(newState.doc, paragraphSourceSeed);
312
313
  const updates = collectParaIdUpdatesFromCensus(census, keeperPositions.paraIds, deterministicSeed);
313
314
  const paragraphSourceUpdates = collectParagraphPropertySourceUpdates(census, keeperPositions.sourceTokens, transactions);
@@ -2,9 +2,15 @@ import { document_d_exports } from "../../../types/document.js";
2
2
  import { Extension } from "../types.js";
3
3
  import { RemovedSectionReference, TrackedSectionEndpointRemoval } from "../../../internal/sectionEndpointResolution.js";
4
4
  import { Node } from "prosemirror-model";
5
- import { EditorState, Plugin, PluginKey, Transaction } from "prosemirror-state";
5
+ import { EditorState, Plugin, Transaction } from "prosemirror-state";
6
6
  //#region src/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts
7
- declare const paragraphChangeTrackerKey: PluginKey<InternalParagraphChangeTrackerState>;
7
+ declare const paragraphChangeTrackerKey: Readonly<{
8
+ key: string;
9
+ createKey: () => import("prosemirror-state").PluginKey<InternalParagraphChangeTrackerState>;
10
+ owns: (plugin: Plugin<InternalParagraphChangeTrackerState>) => boolean;
11
+ get: (state: EditorState) => Plugin<InternalParagraphChangeTrackerState> | undefined;
12
+ getState: (state: EditorState) => InternalParagraphChangeTrackerState | undefined;
13
+ }>;
8
14
  type ParagraphChangeTrackerState = {
9
15
  /** Set of paraIds that were modified since last clear */
10
16
  changedParaIds: Set<string>;
@@ -1,15 +1,23 @@
1
1
  import { withTrackedSectionEndpointRemoval } from "../../../internal/sectionEndpointResolution.js";
2
2
  import { canonicalJson } from "../../../utils/canonicalJson.js";
3
3
  import { enclosingParagraphIndexed, nodeAtIndexed, nodesBetweenIndexed } from "../../indexedNodeLookup.js";
4
+ import { createPluginKeyOwner } from "../../pluginKeyOwner.js";
4
5
  import { sweepPositions } from "../../positionSweep.js";
5
6
  import { sectionPropertiesOf } from "../../sectionCarrier.js";
6
7
  import { sectionMarkEditsOf } from "../../sectionMarkEdits.js";
7
8
  import { createExtension } from "../create.js";
8
9
  import { AddMarkStep, AddNodeMarkStep, AttrStep, RemoveMarkStep, RemoveNodeMarkStep } from "prosemirror-transform";
9
- import { Plugin, PluginKey } from "prosemirror-state";
10
+ import { Plugin } from "prosemirror-state";
10
11
  import { isHistoryTransaction } from "prosemirror-history";
11
12
  //#region src/prosemirror/extensions/features/ParagraphChangeTrackerExtension.ts
12
- const paragraphChangeTrackerKey = new PluginKey("paragraphChangeTracker");
13
+ /**
14
+ * Paragraph Change Tracker Extension
15
+ *
16
+ * Watches ProseMirror transactions and records which paragraph IDs (paraId)
17
+ * were modified. Also detects paragraph and block-structure changes.
18
+ * Used by the selective save system to patch only changed paragraphs in document.xml.
19
+ */
20
+ const paragraphChangeTrackerKey = createPluginKeyOwner("paragraphChangeTracker");
13
21
  const CLEAR_META = "clear";
14
22
  const IGNORE_META = "ignore";
15
23
  const STRUCTURAL_META = "structural";
@@ -196,7 +204,7 @@ const mapStepTouches = (tr, batches) => {
196
204
  };
197
205
  function createParagraphChangeTrackerPlugin() {
198
206
  return new Plugin({
199
- key: paragraphChangeTrackerKey,
207
+ key: paragraphChangeTrackerKey.createKey(),
200
208
  state: {
201
209
  init(_config, state) {
202
210
  const counts = countDocumentStructure(state.doc);
@@ -1,9 +1,10 @@
1
1
  import { expectRunIdentityMarkAttrs } from "../../attrs/index.js";
2
2
  import { nodesBetweenIndexed } from "../../indexedNodeLookup.js";
3
+ import { createPluginKeyOwner } from "../../pluginKeyOwner.js";
3
4
  import { sweepPositions } from "../../positionSweep.js";
4
5
  import { RUN_IDENTITY_ATTRIBUTE, RUN_IDENTITY_MARK_NAME, runIdentityAttrs } from "../../runIdentity.js";
5
6
  import { createMarkExtension } from "../create.js";
6
- import { Plugin, PluginKey } from "prosemirror-state";
7
+ import { Plugin } from "prosemirror-state";
7
8
  import { isHistoryTransaction, undoDepth } from "prosemirror-history";
8
9
  //#region src/prosemirror/extensions/marks/RunIdentityExtension.ts
9
10
  /**
@@ -28,7 +29,7 @@ import { isHistoryTransaction, undoDepth } from "prosemirror-history";
28
29
  * supplies the session the format itself says such a run belongs to.
29
30
  */
30
31
  const CANONICAL_RUN_IDENTITY_ID = /^(?:0|[1-9]\d*)$/u;
31
- const runIdentityStripKey = new PluginKey("runIdentityStrip");
32
+ const runIdentityStripKey = createPluginKeyOwner("runIdentityStrip");
32
33
  /**
33
34
  * Every range `transactions` inserted, in the document they produced.
34
35
  *
@@ -87,7 +88,7 @@ const insertedRanges = (transactions, skip = () => false) => {
87
88
  * collaborative apply all arrive here and nowhere else in common.
88
89
  */
89
90
  const createRunIdentityStripPlugin = () => new Plugin({
90
- key: runIdentityStripKey,
91
+ key: runIdentityStripKey.createKey(),
91
92
  appendTransaction(transactions, oldState, newState) {
92
93
  if (!transactions.some((transaction) => transaction.docChanged)) return null;
93
94
  const markType = newState.schema.marks[RUN_IDENTITY_MARK_NAME];
@@ -409,7 +409,7 @@ function isMarkActive(state, type, attrs) {
409
409
  });
410
410
  let hasMark = false;
411
411
  state.doc.nodesBetween(from, to, (node) => {
412
- if (node.isText) {
412
+ if (node.isText && !(RUN_FORMATTING_MARK_NAMES.has(markType.name) && node.marks.some((mark) => mark.type.name === "footnoteRef"))) {
413
413
  const mark = markType.isInSet(node.marks);
414
414
  if (mark) {
415
415
  if (!attrs) {
@@ -0,0 +1,13 @@
1
+ import { EditorState, Plugin, PluginKey } from "prosemirror-state";
2
+ //#region src/prosemirror/pluginKeyOwner.d.ts
3
+ type PluginKeyOwner<State> = Readonly<{
4
+ key: string;
5
+ createKey: () => PluginKey<State>;
6
+ owns: (plugin: Plugin<State>) => boolean;
7
+ get: (state: EditorState) => Plugin<State> | undefined;
8
+ getState: (state: EditorState) => State | undefined;
9
+ }>;
10
+ /** An immutable plugin family descriptor; each plugin owns its actual key. */
11
+ declare const createPluginKeyOwner: <State = unknown>(name: string) => PluginKeyOwner<State>;
12
+ //#endregion
13
+ export { PluginKeyOwner, createPluginKeyOwner };
@@ -0,0 +1,35 @@
1
+ import { panic } from "better-result";
2
+ import { PluginKey } from "prosemirror-state";
3
+ //#region src/prosemirror/pluginKeyOwner.ts
4
+ const PLUGIN_KEY_PREFIX = "stll.folio.plugin.";
5
+ const METADATA_KEY_PREFIX = "stll.folio.meta.";
6
+ const PLUGIN_OWNER_BRAND = Symbol.for("stll.folio.plugin.owner");
7
+ /** An immutable plugin family descriptor; each plugin owns its actual key. */
8
+ const createPluginKeyOwner = (name) => {
9
+ const pluginsByState = /* @__PURE__ */ new WeakMap();
10
+ const owns = (plugin) => plugin.spec.key !== void 0 && PLUGIN_OWNER_BRAND in plugin.spec.key && plugin.spec.key[PLUGIN_OWNER_BRAND] === name;
11
+ const get = (state) => {
12
+ if (pluginsByState.has(state)) return pluginsByState.get(state);
13
+ let found;
14
+ for (const plugin of state.plugins) {
15
+ if (!owns(plugin)) continue;
16
+ if (found) panic(`Multiple plugins belong to ${name} in one editor state`);
17
+ found = plugin;
18
+ }
19
+ pluginsByState.set(state, found);
20
+ return found;
21
+ };
22
+ return Object.freeze({
23
+ key: `${METADATA_KEY_PREFIX}${name}`,
24
+ createKey: () => {
25
+ const key = new PluginKey(`${PLUGIN_KEY_PREFIX}${name}`);
26
+ Object.defineProperty(key, PLUGIN_OWNER_BRAND, { value: name });
27
+ return key;
28
+ },
29
+ owns,
30
+ get,
31
+ getState: (state) => get(state)?.getState(state)
32
+ });
33
+ };
34
+ //#endregion
35
+ export { createPluginKeyOwner };
@@ -1,7 +1,14 @@
1
- import { Plugin, PluginKey } from "prosemirror-state";
1
+ import { PluginKeyOwner } from "../pluginKeyOwner.js";
2
+ import { Plugin } from "prosemirror-state";
2
3
  import { DecorationSet } from "prosemirror-view";
3
4
  //#region src/prosemirror/plugins/aiCitationDecorations.d.ts
4
- declare const aiCitationDecorationsKey: PluginKey<AICitationDecorationState>;
5
+ declare const aiCitationDecorationsKey: Readonly<{
6
+ key: string;
7
+ createKey: () => import("prosemirror-state").PluginKey<AICitationDecorationState>;
8
+ owns: (plugin: Plugin<AICitationDecorationState>) => boolean;
9
+ get: (state: import("prosemirror-state").EditorState) => Plugin<AICitationDecorationState> | undefined;
10
+ getState: (state: import("prosemirror-state").EditorState) => AICitationDecorationState | undefined;
11
+ }>;
5
12
  declare const SET_META = "set";
6
13
  declare const ACTIVE_META = "active";
7
14
  type AICitationRange = {
@@ -23,14 +30,14 @@ type AICitationDecorationState = {
23
30
  */
24
31
  declare function createAICitationDecorationsPlugin(): Plugin<AICitationDecorationState>;
25
32
  declare function setAICitationsMeta(citations: AICitationRange[]): {
26
- key: PluginKey<AICitationDecorationState>;
33
+ key: PluginKeyOwner<AICitationDecorationState>;
27
34
  payload: {
28
35
  type: typeof SET_META;
29
36
  citations: AICitationRange[];
30
37
  };
31
38
  };
32
39
  declare function setActiveCitationMeta(activeId: string | null): {
33
- key: PluginKey<AICitationDecorationState>;
40
+ key: PluginKeyOwner<AICitationDecorationState>;
34
41
  payload: {
35
42
  type: typeof ACTIVE_META;
36
43
  activeId: string | null;