@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,7 +1,20 @@
1
- import { Plugin, PluginKey } from "prosemirror-state";
1
+ import { createPluginKeyOwner } from "../pluginKeyOwner.js";
2
+ import { Plugin } from "prosemirror-state";
2
3
  import { Decoration, DecorationSet } from "prosemirror-view";
3
4
  //#region src/prosemirror/plugins/aiCitationDecorations.ts
4
- const aiCitationDecorationsKey = new PluginKey("aiCitationDecorations");
5
+ /**
6
+ * AI Citation Decorations Plugin
7
+ *
8
+ * Renders pointer-back-to-source highlights on Folio (DOCX) ranges
9
+ * cited by an AI answer. Distinct from the suggestion-decoration
10
+ * plugin: citations are read-only pointers (no edit semantics) and
11
+ * usually appear briefly when the user clicks a citation chip in the
12
+ * thread.
13
+ *
14
+ * The host pushes updates via the `setAICitationsMeta` /
15
+ * `setActiveCitationMeta` helpers below.
16
+ */
17
+ const aiCitationDecorationsKey = createPluginKeyOwner("aiCitationDecorations");
5
18
  const SET_META = "set";
6
19
  const ACTIVE_META = "active";
7
20
  function buildDecorationSet(doc, citations, activeId) {
@@ -31,7 +44,7 @@ function buildDecorationSet(doc, citations, activeId) {
31
44
  */
32
45
  function createAICitationDecorationsPlugin() {
33
46
  return new Plugin({
34
- key: aiCitationDecorationsKey,
47
+ key: aiCitationDecorationsKey.createKey(),
35
48
  state: {
36
49
  init(_, state) {
37
50
  return {
@@ -1,8 +1,15 @@
1
1
  import { AISuggestion, AISuggestionSeverity } from "../../ai-suggestions/types.js";
2
- import { Plugin, PluginKey } from "prosemirror-state";
2
+ import { PluginKeyOwner } from "../pluginKeyOwner.js";
3
+ import { Plugin } from "prosemirror-state";
3
4
  import { DecorationSet } from "prosemirror-view";
4
5
  //#region src/prosemirror/plugins/aiSuggestionDecorations.d.ts
5
- declare const aiSuggestionDecorationsKey: PluginKey<AISuggestionDecorationState>;
6
+ declare const aiSuggestionDecorationsKey: Readonly<{
7
+ key: string;
8
+ createKey: () => import("prosemirror-state").PluginKey<AISuggestionDecorationState>;
9
+ owns: (plugin: Plugin<AISuggestionDecorationState>) => boolean;
10
+ get: (state: import("prosemirror-state").EditorState) => Plugin<AISuggestionDecorationState> | undefined;
11
+ getState: (state: import("prosemirror-state").EditorState) => AISuggestionDecorationState | undefined;
12
+ }>;
6
13
  declare const SET_META = "set";
7
14
  declare const FOCUS_META = "focus";
8
15
  type AISuggestionDecorationState = {
@@ -24,14 +31,14 @@ declare const AI_SUGGESTION_SEVERITY_CLASS: Record<AISuggestionSeverity, string>
24
31
  */
25
32
  declare function createAISuggestionDecorationsPlugin(): Plugin<AISuggestionDecorationState>;
26
33
  declare function setAISuggestionsMeta(suggestions: AISuggestion[]): {
27
- key: PluginKey<AISuggestionDecorationState>;
34
+ key: PluginKeyOwner<AISuggestionDecorationState>;
28
35
  payload: {
29
36
  type: typeof SET_META;
30
37
  suggestions: AISuggestion[];
31
38
  };
32
39
  };
33
40
  declare function setFocusedSuggestionMeta(focusedId: string | null): {
34
- key: PluginKey<AISuggestionDecorationState>;
41
+ key: PluginKeyOwner<AISuggestionDecorationState>;
35
42
  payload: {
36
43
  type: typeof FOCUS_META;
37
44
  focusedId: string | null;
@@ -1,7 +1,22 @@
1
- import { Plugin, PluginKey } from "prosemirror-state";
1
+ import { createPluginKeyOwner } from "../pluginKeyOwner.js";
2
+ import { Plugin } from "prosemirror-state";
2
3
  import { Decoration, DecorationSet } from "prosemirror-view";
3
4
  //#region src/prosemirror/plugins/aiSuggestionDecorations.ts
4
- const aiSuggestionDecorationsKey = new PluginKey("aiSuggestionDecorations");
5
+ /**
6
+ * AI Suggestion Decorations Plugin
7
+ *
8
+ * Renders pending AI suggestions as non-mutating ProseMirror decorations
9
+ * — subtle dotted underlines on the suggested ranges. The focused
10
+ * suggestion additionally previews its change in the text: the original
11
+ * range is struck through and the suggested text is injected after it
12
+ * as a widget. The document itself is never modified by the suggestion
13
+ * queue; decorations live purely in the view layer.
14
+ *
15
+ * The plugin keeps a `DecorationSet` derived from the suggestion list.
16
+ * Updates are pushed via the `setAISuggestions` meta key, which the
17
+ * Folio editor wires up whenever the suggestion list changes.
18
+ */
19
+ const aiSuggestionDecorationsKey = createPluginKeyOwner("aiSuggestionDecorations");
5
20
  const SET_META = "set";
6
21
  const FOCUS_META = "focus";
7
22
  /**
@@ -69,7 +84,7 @@ function buildDecorationSet(doc, suggestions, focusedId) {
69
84
  */
70
85
  function createAISuggestionDecorationsPlugin() {
71
86
  return new Plugin({
72
- key: aiSuggestionDecorationsKey,
87
+ key: aiSuggestionDecorationsKey.createKey(),
73
88
  state: {
74
89
  init(_, state) {
75
90
  return {
@@ -1,4 +1,5 @@
1
- import { EditorState, Plugin, PluginKey } from "prosemirror-state";
1
+ import { PluginKeyOwner } from "../pluginKeyOwner.js";
2
+ import { EditorState, Plugin } from "prosemirror-state";
2
3
  //#region src/prosemirror/plugins/anonymizationDecorations.d.ts
3
4
  type AnonymizationTerm = {
4
5
  /** Canonical surface form, displayed in tooltips. */
@@ -21,7 +22,13 @@ type AnonymizationDecorationState = {
21
22
  terms: readonly AnonymizationTerm[];
22
23
  matches: readonly AnonymizationMatch[];
23
24
  };
24
- declare const anonymizationDecorationsKey: PluginKey<AnonymizationDecorationState>;
25
+ declare const anonymizationDecorationsKey: Readonly<{
26
+ key: string;
27
+ createKey: () => import("prosemirror-state").PluginKey<AnonymizationDecorationState>;
28
+ owns: (plugin: Plugin<AnonymizationDecorationState>) => boolean;
29
+ get: (state: EditorState) => Plugin<AnonymizationDecorationState> | undefined;
30
+ getState: (state: EditorState) => AnonymizationDecorationState | undefined;
31
+ }>;
25
32
  declare const slugAnonymizationLabel: (label: string) => string;
26
33
  /**
27
34
  * Plugin that keeps a list of anonymization-term match ranges
@@ -43,20 +50,13 @@ type AnonymizationPluginOptions = {
43
50
  };
44
51
  declare const createAnonymizationDecorationsPlugin: ({ onMatchesChange }?: AnonymizationPluginOptions) => Plugin<AnonymizationDecorationState>;
45
52
  declare const setAnonymizationTermsMeta: (terms: readonly AnonymizationTerm[]) => {
46
- key: PluginKey<AnonymizationDecorationState>;
53
+ key: PluginKeyOwner<AnonymizationDecorationState>;
47
54
  payload: {
48
55
  type: typeof SET_META;
49
56
  terms: readonly AnonymizationTerm[];
50
57
  };
51
58
  };
52
- /**
53
- * Read the plugin's current match list out of a Folio editor
54
- * view. Lives inside the same module as the plugin key so a host
55
- * that imports it via `@stll/folio` gets the *same* key instance
56
- * as the plugin registration — bypassing the dev-HMR hazard
57
- * where re-evaluating the plugin module would create a second
58
- * PluginKey and break key-based lookups.
59
- */
59
+ /** Read anonymization matches owned by this editor state. */
60
60
  declare const getAnonymizationMatches: (state: EditorState) => readonly AnonymizationMatch[];
61
61
  //#endregion
62
62
  export { AnonymizationMatch, AnonymizationPluginOptions, AnonymizationTerm, anonymizationDecorationsKey, createAnonymizationDecorationsPlugin, getAnonymizationMatches, setAnonymizationTermsMeta, slugAnonymizationLabel };
@@ -1,10 +1,27 @@
1
+ import { createPluginKeyOwner } from "../pluginKeyOwner.js";
1
2
  import { collectBlockChunks, joinChunks, offsetToDocPos } from "./pmTextScan.js";
2
- import { Plugin, PluginKey } from "prosemirror-state";
3
+ import { Plugin } from "prosemirror-state";
3
4
  //#region src/prosemirror/plugins/anonymizationDecorations.ts
5
+ /**
6
+ * Anonymization Match Plugin
7
+ *
8
+ * Owns the "what to highlight" half of the anonymization
9
+ * overlay: the host pushes a list of terms via the
10
+ * `setAnonymizationTermsMeta` meta, this plugin scans text
11
+ * nodes for each surface form, and exposes the resulting
12
+ * match ranges through its state.
13
+ *
14
+ * The plugin does **not** produce ProseMirror decorations.
15
+ * Folio's editor lives off-screen (HiddenProseMirror) and PM
16
+ * decorations never reach the visible paged DOM. The visible
17
+ * highlights are painted by {@link AnonymizationRectsOverlay},
18
+ * which reads the match ranges from this plugin's state and
19
+ * projects them through `selectionToRects` to coordinates on
20
+ * the paged canvas. Single source of truth for the ranges,
21
+ * one painter.
22
+ */
4
23
  const SET_META = "set";
5
- const KEY_HOLDER_SYMBOL = Symbol.for("stll.folio.anonymizationDecorationsKey");
6
- const keyHolder = globalThis;
7
- const anonymizationDecorationsKey = keyHolder[KEY_HOLDER_SYMBOL] ?? (keyHolder[KEY_HOLDER_SYMBOL] = new PluginKey("anonymizationDecorations"));
24
+ const anonymizationDecorationsKey = createPluginKeyOwner("anonymizationDecorations");
8
25
  const slugAnonymizationLabel = (label) => label.toLowerCase().replace(/[^a-z0-9]+/gu, "-");
9
26
  const buildMatcher = (terms) => {
10
27
  const escapeChar = (value) => value.replaceAll(/[\\^$.*+?()[\]{}|]/gu, "\\$&").replaceAll(/\s+/gu, "\\s+");
@@ -60,7 +77,7 @@ const buildMatches = (doc, terms) => {
60
77
  };
61
78
  const createAnonymizationDecorationsPlugin = ({ onMatchesChange } = {}) => {
62
79
  const spec = {
63
- key: anonymizationDecorationsKey,
80
+ key: anonymizationDecorationsKey.createKey(),
64
81
  state: {
65
82
  init() {
66
83
  return {
@@ -107,14 +124,7 @@ const setAnonymizationTermsMeta = (terms) => ({
107
124
  terms
108
125
  }
109
126
  });
110
- /**
111
- * Read the plugin's current match list out of a Folio editor
112
- * view. Lives inside the same module as the plugin key so a host
113
- * that imports it via `@stll/folio` gets the *same* key instance
114
- * as the plugin registration — bypassing the dev-HMR hazard
115
- * where re-evaluating the plugin module would create a second
116
- * PluginKey and break key-based lookups.
117
- */
127
+ /** Read anonymization matches owned by this editor state. */
118
128
  const getAnonymizationMatches = (state) => anonymizationDecorationsKey.getState(state)?.matches ?? [];
119
129
  //#endregion
120
130
  export { anonymizationDecorationsKey, createAnonymizationDecorationsPlugin, getAnonymizationMatches, setAnonymizationTermsMeta, slugAnonymizationLabel };
@@ -1,4 +1,4 @@
1
- import { EditorState, Plugin, PluginKey, Transaction } from "prosemirror-state";
1
+ import { EditorState, Plugin, Transaction } from "prosemirror-state";
2
2
  import { DecorationSet } from "prosemirror-view";
3
3
  //#region src/prosemirror/plugins/autocompleteSuggestion.d.ts
4
4
  type AutocompleteSuggestionStatus = "idle" | "streaming" | "shown";
@@ -13,7 +13,13 @@ type AutocompleteSuggestionState = {
13
13
  text: string;
14
14
  requestId: string;
15
15
  };
16
- declare const autocompleteSuggestionKey: PluginKey<AutocompleteSuggestionPluginState>;
16
+ declare const autocompleteSuggestionKey: Readonly<{
17
+ key: string;
18
+ createKey: () => import("prosemirror-state").PluginKey<AutocompleteSuggestionPluginState>;
19
+ owns: (plugin: Plugin<AutocompleteSuggestionPluginState>) => boolean;
20
+ get: (state: EditorState) => Plugin<AutocompleteSuggestionPluginState> | undefined;
21
+ getState: (state: EditorState) => AutocompleteSuggestionPluginState | undefined;
22
+ }>;
17
23
  type AutocompleteSuggestionPluginState = {
18
24
  suggestion: AutocompleteSuggestionState;
19
25
  decorations: DecorationSet;
@@ -1,15 +1,37 @@
1
- import { Plugin, PluginKey } from "prosemirror-state";
1
+ import { createPluginKeyOwner } from "../pluginKeyOwner.js";
2
+ import { Plugin } from "prosemirror-state";
2
3
  import { Decoration, DecorationSet } from "prosemirror-view";
3
4
  //#region src/prosemirror/plugins/autocompleteSuggestion.ts
5
+ /**
6
+ * Autocomplete Suggestion Plugin
7
+ *
8
+ * Headless ProseMirror plugin for inline ghost-text autocomplete.
9
+ * Mirrors the anonymization plugin shape: this module owns the
10
+ * "what" (a single in-flight suggestion anchored at a doc
11
+ * position) and {@link AutocompleteCaretOverlay} owns the paint.
12
+ *
13
+ * Lifecycle:
14
+ * idle ──setSuggestionStart──▶ streaming ──appendToken*──▶ shown
15
+ * ◀──clearSuggestion──────────────────────────────────────┘
16
+ *
17
+ * The document is never mutated while the suggestion is shown;
18
+ * the ghost text and "stella" caret are projected onto the page
19
+ * via {@link AutocompleteCaretOverlay} reading this plugin's
20
+ * state and a single inline widget decoration carrying the
21
+ * anchor position.
22
+ *
23
+ * On accept (Tab/⌘→), call {@link acceptAutocompleteSuggestion}
24
+ * to commit the suggestion as a real text insertion in a single
25
+ * transaction. On dismiss (Esc, edit, blur), call
26
+ * {@link clearAutocompleteSuggestion}.
27
+ */
4
28
  const IDLE = {
5
29
  status: "idle",
6
30
  anchor: null,
7
31
  text: "",
8
32
  requestId: null
9
33
  };
10
- const KEY_HOLDER_SYMBOL = Symbol.for("stll.folio.autocompleteSuggestionKey");
11
- const keyHolder = globalThis;
12
- const autocompleteSuggestionKey = keyHolder[KEY_HOLDER_SYMBOL] ?? (keyHolder[KEY_HOLDER_SYMBOL] = new PluginKey("autocompleteSuggestion"));
34
+ const autocompleteSuggestionKey = createPluginKeyOwner("autocompleteSuggestion");
13
35
  const buildAnchorWidget = () => {
14
36
  const el = document.createElement("span");
15
37
  el.dataset["folioAutocompleteAnchor"] = "true";
@@ -76,7 +98,7 @@ const autocompleteSuggestionPlugin = (options = {}) => {
76
98
  keymap: options.keymap ?? false
77
99
  };
78
100
  return new Plugin({
79
- key: autocompleteSuggestionKey,
101
+ key: autocompleteSuggestionKey.createKey(),
80
102
  state: {
81
103
  init: (_config, instance) => ({
82
104
  suggestion: IDLE,
@@ -1,6 +1,6 @@
1
1
  import { ContentControlBoundError, ContentControlLockedError, ContentControlTypeError } from "../../content-controls/errors.js";
2
2
  import "../../content-controls/index.js";
3
- import { Plugin, PluginKey } from "prosemirror-state";
3
+ import { Plugin } from "prosemirror-state";
4
4
  import { EditorView } from "prosemirror-view";
5
5
  //#region src/prosemirror/plugins/contentControlWidgets.d.ts
6
6
  type ContentControlWidgetAnchor = {
@@ -47,7 +47,13 @@ type HandleContentControlWidgetClickOptions = {
47
47
  onEvent: ContentControlWidgetCallback;
48
48
  view: ContentControlWidgetView;
49
49
  };
50
- declare const contentControlWidgetsPluginKey: PluginKey<unknown>;
50
+ declare const contentControlWidgetsPluginKey: Readonly<{
51
+ key: string;
52
+ createKey: () => import("prosemirror-state").PluginKey<unknown>;
53
+ owns: (plugin: Plugin<unknown>) => boolean;
54
+ get: (state: import("prosemirror-state").EditorState) => Plugin<unknown> | undefined;
55
+ getState: (state: import("prosemirror-state").EditorState) => unknown;
56
+ }>;
51
57
  /**
52
58
  * CustomEvent name dispatched on the editor view DOM whenever the plugin
53
59
  * needs to surface a `ContentControlWidgetEvent` to an adapter shell. The
@@ -1,6 +1,7 @@
1
1
  import { ContentControlBoundError, ContentControlLockedError, ContentControlTypeError } from "../../content-controls/errors.js";
2
2
  import { findBlockSdtMatch, setContentControlValueTr } from "../commands/contentControls.js";
3
- import { Plugin, PluginKey } from "prosemirror-state";
3
+ import { createPluginKeyOwner } from "../pluginKeyOwner.js";
4
+ import { Plugin } from "prosemirror-state";
4
5
  //#region src/prosemirror/plugins/contentControlWidgets.ts
5
6
  /**
6
7
  * Interactive content-control widget delegation.
@@ -16,7 +17,7 @@ import { Plugin, PluginKey } from "prosemirror-state";
16
17
  * higher-level UI (a popover menu / date picker rendered by the editor
17
18
  * shell) can subscribe; the plugin does not own the menu/picker chrome.
18
19
  */
19
- const contentControlWidgetsPluginKey = new PluginKey("contentControlWidgets");
20
+ const contentControlWidgetsPluginKey = createPluginKeyOwner("contentControlWidgets");
20
21
  /**
21
22
  * CustomEvent name dispatched on the editor view DOM whenever the plugin
22
23
  * needs to surface a `ContentControlWidgetEvent` to an adapter shell. The
@@ -151,7 +152,7 @@ function createContentControlWidgetsPlugin(onEvent = () => void 0) {
151
152
  }));
152
153
  };
153
154
  return new Plugin({
154
- key: contentControlWidgetsPluginKey,
155
+ key: contentControlWidgetsPluginKey.createKey(),
155
156
  props: { handleDOMEvents: { click(view, event) {
156
157
  return handleContentControlWidgetClick({
157
158
  view,
@@ -1,5 +1,6 @@
1
+ import { PluginKeyOwner } from "../pluginKeyOwner.js";
1
2
  import { Node } from "prosemirror-model";
2
- import { EditorState, Plugin, PluginKey } from "prosemirror-state";
3
+ import { EditorState, Plugin } from "prosemirror-state";
3
4
  //#region src/prosemirror/plugins/createDocScanPlugin.d.ts
4
5
  declare const SET_META = "set";
5
6
  type DocScanState<TConfig, TRange> = {
@@ -7,7 +8,7 @@ type DocScanState<TConfig, TRange> = {
7
8
  ranges: readonly TRange[];
8
9
  };
9
10
  type DocScanPluginOptions<TConfig, TRange> = {
10
- key: PluginKey<DocScanState<TConfig, TRange>>;
11
+ key: PluginKeyOwner<DocScanState<TConfig, TRange>>;
11
12
  initialConfig: TConfig;
12
13
  /** Recompute the ranges from the current doc + config. */
13
14
  scan: (doc: Node, config: TConfig) => TRange[];
@@ -20,13 +21,13 @@ type DocScanPluginOptions<TConfig, TRange> = {
20
21
  onRangesChange?: (ranges: readonly TRange[]) => void;
21
22
  };
22
23
  declare const createDocScanPlugin: <TConfig, TRange>({ key, initialConfig, scan, onRangesChange }: DocScanPluginOptions<TConfig, TRange>) => Plugin<DocScanState<TConfig, TRange>>;
23
- declare const setDocScanConfigMeta: <TConfig, TRange>(key: PluginKey<DocScanState<TConfig, TRange>>, config: TConfig) => {
24
- key: PluginKey<DocScanState<TConfig, TRange>>;
24
+ declare const setDocScanConfigMeta: <TConfig, TRange>(key: PluginKeyOwner<DocScanState<TConfig, TRange>>, config: TConfig) => {
25
+ key: PluginKeyOwner<DocScanState<TConfig, TRange>>;
25
26
  payload: {
26
27
  type: typeof SET_META;
27
28
  config: TConfig;
28
29
  };
29
30
  };
30
- declare const getDocScanRanges: <TConfig, TRange>(key: PluginKey<DocScanState<TConfig, TRange>>, state: EditorState) => readonly TRange[];
31
+ declare const getDocScanRanges: <TConfig, TRange>(key: PluginKeyOwner<DocScanState<TConfig, TRange>>, state: EditorState) => readonly TRange[];
31
32
  //#endregion
32
33
  export { DocScanPluginOptions, DocScanState, createDocScanPlugin, getDocScanRanges, setDocScanConfigMeta };
@@ -3,7 +3,7 @@ import { Plugin } from "prosemirror-state";
3
3
  const SET_META = "set";
4
4
  const createDocScanPlugin = ({ key, initialConfig, scan, onRangesChange }) => {
5
5
  const spec = {
6
- key,
6
+ key: key.createKey(),
7
7
  state: {
8
8
  init(_config, instance) {
9
9
  return {
@@ -1,9 +1,11 @@
1
1
  import { getCachedNumberingMap } from "../../docx/numberingParser.js";
2
2
  import { completeNumberingForDoc, paragraphListReferences } from "../listInstanceReferences.js";
3
+ import { createPluginKeyOwner } from "../pluginKeyOwner.js";
3
4
  import { AddMarkStep, DocAttrStep, RemoveMarkStep, ReplaceAroundStep, ReplaceStep } from "prosemirror-transform";
4
- import { Plugin, PluginKey } from "prosemirror-state";
5
+ import { Plugin } from "prosemirror-state";
5
6
  //#region src/prosemirror/plugins/documentNumbering.ts
6
- const documentNumberingKey = new PluginKey("documentNumbering");
7
+ /** Document-scoped numbering definitions for list-editing commands. */
8
+ const documentNumberingKey = createPluginKeyOwner("documentNumbering");
7
9
  const completedState = (definitions, doc) => {
8
10
  const completed = completeNumberingForDoc(definitions ?? void 0, doc);
9
11
  return {
@@ -49,7 +51,7 @@ const mayReferenceUnknownInstance = (tr, map) => {
49
51
  });
50
52
  };
51
53
  const createDocumentNumberingPlugin = (definitions) => new Plugin({
52
- key: documentNumberingKey,
54
+ key: documentNumberingKey.createKey(),
53
55
  state: {
54
56
  init: (_config, state) => completedState(definitions ?? null, state.doc),
55
57
  apply: (tr, current, _oldState, state) => tr.docChanged && mayReferenceUnknownInstance(tr, current.map) ? completedState(current.definitions, state.doc) : current
@@ -1,9 +1,15 @@
1
1
  import { document_d_exports } from "../../types/document.js";
2
2
  import { BuiltInStyleIndex } from "../../docx/builtInStyles.js";
3
3
  import { StyleResolver } from "../styles/styleResolver.js";
4
- import { EditorState, PluginKey } from "prosemirror-state";
4
+ import { EditorState } from "prosemirror-state";
5
5
  //#region src/prosemirror/plugins/documentStyleState.d.ts
6
- declare const documentStylesKey: PluginKey<StyleResolver | null>;
6
+ declare const documentStylesKey: Readonly<{
7
+ key: string;
8
+ createKey: () => import("prosemirror-state").PluginKey<StyleResolver | null>;
9
+ owns: (plugin: import("prosemirror-state").Plugin<StyleResolver | null>) => boolean;
10
+ get: (state: EditorState) => import("prosemirror-state").Plugin<StyleResolver | null> | undefined;
11
+ getState: (state: EditorState) => StyleResolver | null | undefined;
12
+ }>;
7
13
  /** Read the document's StyleResolver, or null when the plugin isn't installed. */
8
14
  declare function getDocumentStyleResolver(state: EditorState): StyleResolver | null;
9
15
  /**
@@ -1,7 +1,7 @@
1
1
  import { EMPTY_BUILT_IN_STYLE_INDEX } from "../../docx/builtInStyles.js";
2
- import { PluginKey } from "prosemirror-state";
2
+ import { createPluginKeyOwner } from "../pluginKeyOwner.js";
3
3
  //#region src/prosemirror/plugins/documentStyleState.ts
4
- const documentStylesKey = new PluginKey("documentStyles");
4
+ const documentStylesKey = createPluginKeyOwner("documentStyles");
5
5
  /** Read the document's StyleResolver, or null when the plugin isn't installed. */
6
6
  function getDocumentStyleResolver(state) {
7
7
  return documentStylesKey.getState(state) ?? null;
@@ -82,7 +82,7 @@ const documentStyleContextSpec = (styles) => {
82
82
  else if (styles) resolver = createStyleResolver(styles);
83
83
  else resolver = null;
84
84
  return {
85
- key: documentStylesKey,
85
+ key: documentStylesKey.createKey(),
86
86
  state: {
87
87
  init: () => resolver,
88
88
  apply: (_tr, value) => value
@@ -1,6 +1,6 @@
1
1
  import { document_d_exports } from "../../types/document.js";
2
2
  import { ListState } from "../listState.js";
3
- import { EditorState, Plugin, PluginKey } from "prosemirror-state";
3
+ import { EditorState, Plugin } from "prosemirror-state";
4
4
  //#region src/prosemirror/plugins/selectionTracker.d.ts
5
5
  /**
6
6
  * Selection context for toolbar state
@@ -30,7 +30,13 @@ type SelectionContext = {
30
30
  /**
31
31
  * Plugin key for accessing selection tracker state
32
32
  */
33
- declare const selectionTrackerKey: PluginKey<SelectionContext>;
33
+ declare const selectionTrackerKey: Readonly<{
34
+ key: string;
35
+ createKey: () => import("prosemirror-state").PluginKey<SelectionContext>;
36
+ owns: (plugin: Plugin<SelectionContext>) => boolean;
37
+ get: (state: EditorState) => Plugin<SelectionContext> | undefined;
38
+ getState: (state: EditorState) => SelectionContext | undefined;
39
+ }>;
34
40
  /**
35
41
  * Callback type for selection changes
36
42
  */
@@ -1,7 +1,8 @@
1
1
  import { expectCommentMarkAttrs, expectTrackedChangeMarkAttrs } from "../attrs/index.js";
2
2
  import { sameListState } from "../listState.js";
3
+ import { createPluginKeyOwner } from "../pluginKeyOwner.js";
3
4
  import { extractSelectionSnapshot } from "../selectionState.js";
4
- import { Plugin, PluginKey } from "prosemirror-state";
5
+ import { Plugin } from "prosemirror-state";
5
6
  //#region src/prosemirror/plugins/selectionTracker.ts
6
7
  /**
7
8
  * Selection Tracker Plugin
@@ -15,7 +16,7 @@ import { Plugin, PluginKey } from "prosemirror-state";
15
16
  /**
16
17
  * Plugin key for accessing selection tracker state
17
18
  */
18
- const selectionTrackerKey = new PluginKey("selectionTracker");
19
+ const selectionTrackerKey = createPluginKeyOwner("selectionTracker");
19
20
  /**
20
21
  * Extract selection context from editor state
21
22
  */
@@ -60,7 +61,7 @@ function extractSelectionContext(state) {
60
61
  */
61
62
  function createSelectionTrackerPlugin(onSelectionChange) {
62
63
  return new Plugin({
63
- key: selectionTrackerKey,
64
+ key: selectionTrackerKey.createKey(),
64
65
  state: {
65
66
  init(_, state) {
66
67
  return extractSelectionContext(state);
@@ -1,8 +1,14 @@
1
1
  import { Slice } from "prosemirror-model";
2
- import { EditorState, Plugin, PluginKey, Transaction } from "prosemirror-state";
2
+ import { EditorState, Plugin, Transaction } from "prosemirror-state";
3
3
  import { EditorView } from "prosemirror-view";
4
4
  //#region src/prosemirror/plugins/suggestionMode.d.ts
5
- declare const suggestionModeKey: PluginKey<SuggestionModeState>;
5
+ declare const suggestionModeKey: Readonly<{
6
+ key: string;
7
+ createKey: () => import("prosemirror-state").PluginKey<SuggestionModeState>;
8
+ owns: (plugin: Plugin<SuggestionModeState>) => boolean;
9
+ get: (state: EditorState) => Plugin<SuggestionModeState> | undefined;
10
+ getState: (state: EditorState) => SuggestionModeState | undefined;
11
+ }>;
6
12
  declare const SUGGESTION_META = "suggestionModeApplied";
7
13
  declare const SUGGESTION_BYPASS_META = "suggestionModeBypass";
8
14
  /**
@@ -8,6 +8,7 @@ import { splitBlockClearBorders } from "../extensions/features/BaseKeymapExtensi
8
8
  import { JOINED_RUNS_RESTYLED_META } from "../extensions/features/JoinedRunStyleExtension.js";
9
9
  import { expandNoteReferenceDeletionRange } from "../extensions/marks/noteReferenceDeletion.js";
10
10
  import { carryParagraphProperties, paragraphLeftAfter, recordReplacedParagraphProperties } from "../paragraphPropertyCarry.js";
11
+ import { createPluginKeyOwner } from "../pluginKeyOwner.js";
11
12
  import { rebaseParagraphRuns } from "../rebaseParagraphRunFormatting.js";
12
13
  import { RUN_FORMATTING_MARK_NAMES } from "../runFormattingMarkNames.js";
13
14
  import { paragraphRunStyleContext } from "../runStyleFormatting.js";
@@ -20,7 +21,7 @@ import { mintRevisionId, seedRevisionIdsFromDoc } from "./revisionIds.js";
20
21
  import { Slice } from "prosemirror-model";
21
22
  import { CellSelection } from "prosemirror-tables";
22
23
  import { Mapping, ReplaceStep } from "prosemirror-transform";
23
- import { AllSelection, EditorState, Plugin, PluginKey, Selection, TextSelection } from "prosemirror-state";
24
+ import { AllSelection, EditorState, Plugin, Selection, TextSelection } from "prosemirror-state";
24
25
  import { joinBackward, joinForward, selectNodeBackward, selectNodeForward } from "prosemirror-commands";
25
26
  import { isHistoryTransaction } from "prosemirror-history";
26
27
  import { undoInputRule } from "prosemirror-inputrules";
@@ -37,7 +38,7 @@ import { undoInputRule } from "prosemirror-inputrules";
37
38
  * - Text already marked as insertion by the current author is deleted normally
38
39
  * (retracting your own suggestion)
39
40
  */
40
- const suggestionModeKey = new PluginKey("suggestionMode");
41
+ const suggestionModeKey = createPluginKeyOwner("suggestionMode");
41
42
  const SUGGESTION_META = "suggestionModeApplied";
42
43
  const SUGGESTION_BYPASS_META = "suggestionModeBypass";
43
44
  /**
@@ -939,7 +940,7 @@ function createSuggestionModePlugin(initialActive = false, author = "User") {
939
940
  let compositionReplaced = null;
940
941
  let compositionId = null;
941
942
  return new Plugin({
942
- key: suggestionModeKey,
943
+ key: suggestionModeKey.createKey(),
943
944
  state: {
944
945
  init(_config, instance) {
945
946
  seedRevisionIdsFromDoc(instance.doc);
@@ -1,6 +1,6 @@
1
1
  import { DocScanState } from "./createDocScanPlugin.js";
2
2
  import { Node } from "prosemirror-model";
3
- import { EditorState, PluginKey } from "prosemirror-state";
3
+ import { EditorState } from "prosemirror-state";
4
4
  import { DirectiveKind } from "@stll/template-conditions";
5
5
  //#region src/prosemirror/plugins/templateDirectives.d.ts
6
6
  type DirectiveRange = {
@@ -50,7 +50,13 @@ type DirectiveRange = {
50
50
  */
51
51
  declare const computeBlockDepths: (ranges: readonly DirectiveRange[]) => Map<number, number>;
52
52
  declare const scanDirectives: (doc: Node) => DirectiveRange[];
53
- declare const templateDirectivesKey: PluginKey<DocScanState<undefined, DirectiveRange>>;
53
+ declare const templateDirectivesKey: Readonly<{
54
+ key: string;
55
+ createKey: () => import("prosemirror-state").PluginKey<DocScanState<undefined, DirectiveRange>>;
56
+ owns: (plugin: import("prosemirror-state").Plugin<DocScanState<undefined, DirectiveRange>>) => boolean;
57
+ get: (state: EditorState) => import("prosemirror-state").Plugin<DocScanState<undefined, DirectiveRange>> | undefined;
58
+ getState: (state: EditorState) => DocScanState<undefined, DirectiveRange> | undefined;
59
+ }>;
54
60
  type TemplateDirectivesPluginOptions = {
55
61
  onRangesChange?: (ranges: readonly DirectiveRange[]) => void;
56
62
  };
@@ -1,9 +1,26 @@
1
+ import { createPluginKeyOwner } from "../pluginKeyOwner.js";
1
2
  import { createDocScanPlugin, getDocScanRanges } from "./createDocScanPlugin.js";
2
3
  import { collectBlockChunks, joinChunks, offsetToDocPos } from "./pmTextScan.js";
3
- import { PluginKey } from "prosemirror-state";
4
4
  import { assertNever, isBlockDirectiveKind, scanMarkers } from "@stll/template-conditions";
5
5
  //#region src/prosemirror/plugins/templateDirectives.ts
6
6
  /**
7
+ * Template-directives scan plugin.
8
+ *
9
+ * Scans the document for legal-template markers and exposes their
10
+ * PM ranges so the paged-canvas overlay can paint rich widgets
11
+ * (field chips, conditional/loop bands) in place of the raw
12
+ * `{{ ... }}` / `{% ... %}` text. Mirrors the anonymization plugin's
13
+ * shape via the shared {@link createDocScanPlugin} factory; the
14
+ * document text is the only input, so there is no host-pushed config.
15
+ *
16
+ * The grammar itself is NOT defined here: the kinds and the marker
17
+ * parser come from `@stll/template-conditions` ({@link scanMarkers}),
18
+ * the docxtpl dialect of Jinja the fill pipeline uses. This file only
19
+ * maps the scanner's text offsets onto ProseMirror positions, so a new
20
+ * directive added to the shared grammar is highlighted here
21
+ * automatically.
22
+ */
23
+ /**
7
24
  * The body of an `if` / `elif` tag as authored: its inner text after the
8
25
  * keyword. The grammar reads a body that is one path plus a filter chain as
9
26
  * that path, with the chain in `filters`; the document still holds the whole
@@ -113,9 +130,7 @@ const scanDirectives = (doc) => {
113
130
  }
114
131
  return ranges;
115
132
  };
116
- const KEY_HOLDER_SYMBOL = Symbol.for("stll.folio.templateDirectivesKey");
117
- const keyHolder = globalThis;
118
- const templateDirectivesKey = keyHolder[KEY_HOLDER_SYMBOL] ?? (keyHolder[KEY_HOLDER_SYMBOL] = new PluginKey("templateDirectives"));
133
+ const templateDirectivesKey = createPluginKeyOwner("templateDirectives");
119
134
  const createTemplateDirectivesPlugin = ({ onRangesChange } = {}) => createDocScanPlugin({
120
135
  key: templateDirectivesKey,
121
136
  initialConfig: void 0,
@@ -1,4 +1,4 @@
1
- import { Plugin, PluginKey } from "prosemirror-state";
1
+ import { Plugin } from "prosemirror-state";
2
2
  import { DecorationSet, EditorView } from "prosemirror-view";
3
3
  //#region src/prosemirror/plugins/templatePreviewValues.d.ts
4
4
  /** One formatted span of a rich preview value. The flags layer over the
@@ -87,7 +87,13 @@ type TemplatePreviewState = {
87
87
  hidden: TemplatePreviewHiddenRange[];
88
88
  decorationSet: DecorationSet;
89
89
  };
90
- declare const templatePreviewValuesKey: PluginKey<TemplatePreviewState>;
90
+ declare const templatePreviewValuesKey: Readonly<{
91
+ key: string;
92
+ createKey: () => import("prosemirror-state").PluginKey<TemplatePreviewState>;
93
+ owns: (plugin: Plugin<TemplatePreviewState>) => boolean;
94
+ get: (state: import("prosemirror-state").EditorState) => Plugin<TemplatePreviewState> | undefined;
95
+ getState: (state: import("prosemirror-state").EditorState) => TemplatePreviewState | undefined;
96
+ }>;
91
97
  declare function createTemplatePreviewValuesPlugin(): Plugin<TemplatePreviewState>;
92
98
  /** Push (or clear, with `null`) the live fill preview into the editor. */
93
99
  declare const setTemplatePreviewValues: (view: EditorView, preview: TemplatePreviewValues | null) => void;