@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.
- package/README.md +10 -1
- package/dist/compat/eigenpal.d.ts +2 -2
- package/dist/compat/eigenpal.js +2 -2
- package/dist/controller/canonicalSession.js +3 -2
- package/dist/controller/canonicalStructure.js +10 -7
- package/dist/controller/fontReadiness.js +1 -1
- package/dist/controller/layoutPipeline.js +7 -1
- package/dist/controller/layoutSession.d.ts +2 -0
- package/dist/display-list/build/fontTable.js +7 -5
- package/dist/display-list/build/furniture.d.ts +1 -0
- package/dist/display-list/build/furniture.js +1 -0
- package/dist/display-list/build/watermarkPrimitives.d.ts +2 -1
- package/dist/display-list/build/watermarkPrimitives.js +9 -3
- package/dist/display-list/editor/pageRenderer.js +1 -0
- package/dist/docx/bookmarkIdNormalization.d.ts +10 -0
- package/dist/docx/bookmarkIdNormalization.js +100 -0
- package/dist/docx/bookmarkParser.js +4 -2
- package/dist/docx/importedIdCollisions.d.ts +136 -0
- package/dist/docx/importedIdCollisions.js +215 -0
- package/dist/docx/numberingParser.js +2 -2
- package/dist/docx/paragraphPropertySource.js +1 -2
- package/dist/docx/parseContext.d.ts +2 -1
- package/dist/docx/parseContext.js +1 -0
- package/dist/docx/parseWarningMessage.js +1 -0
- package/dist/docx/parser.js +5 -1
- package/dist/docx/relsParser.js +1 -1
- package/dist/docx/rezip.js +2 -1
- package/dist/docx/selectedXmlIdentityScan.d.ts +10 -0
- package/dist/docx/selectedXmlIdentityScan.js +36 -0
- package/dist/docx/selectiveSave.js +5 -4
- package/dist/docx/selectiveXmlPatch.js +44 -52
- package/dist/docx/serializer/commentSerializer.d.ts +3 -1
- package/dist/docx/serializer/commentSerializer.js +11 -2
- package/dist/docx/serializer/runSerializer.js +2 -2
- package/dist/docx/serializer/trackedChangeAttributes.d.ts +1 -3
- package/dist/docx/serializer/trackedChangeAttributes.js +1 -11
- package/dist/fonts/embeddedFontFingerprint.d.ts +5 -0
- package/dist/fonts/embeddedFontFingerprint.js +18 -0
- package/dist/fonts/embeddedFonts.d.ts +1 -1
- package/dist/fonts/embeddedFonts.js +1 -1
- package/dist/fonts/headlessMeasure.js +0 -0
- package/dist/headless-layout.js +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/layout-bridge/convert/flowConversionShared.d.ts +2 -0
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -1
- package/dist/layout-bridge/convert/footnoteLayout.js +3 -0
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
- package/dist/layout-bridge/convert/headerFooterLayout.js +2 -0
- package/dist/layout-bridge/convert/paragraphConversion.js +8 -1
- package/dist/layout-bridge/convert/toFlowBlocks.js +25 -1
- package/dist/layout-engine/measure/cache.js +39 -7
- package/dist/layout-engine/measure/lineTypography.js +1 -0
- package/dist/layout-engine/measure/listMarkerWidth.d.ts +1 -0
- package/dist/layout-engine/measure/listMarkerWidth.js +1 -0
- package/dist/layout-engine/measure/measureContainer.js +3 -4
- package/dist/layout-engine/measure/measureHelpers.d.ts +6 -9
- package/dist/layout-engine/measure/measureHelpers.js +21 -18
- package/dist/layout-engine/measure/measureParagraph.js +2 -0
- package/dist/layout-engine/measure/measureTypes.d.ts +2 -0
- package/dist/layout-engine/types.d.ts +4 -1
- package/dist/layout-painter/renderPage.d.ts +1 -0
- package/dist/layout-painter/renderPage.js +11 -3
- package/dist/layout-painter/renderParagraph.js +13 -3
- package/dist/layout-painter/renderWatermark.d.ts +1 -0
- package/dist/layout-painter/renderWatermark.js +7 -3
- package/dist/managers/editorShortcuts.d.ts +2 -1
- package/dist/managers/editorShortcuts.js +2 -1
- package/dist/prosemirror/canonicalSelectionRange.d.ts +4 -1
- package/dist/prosemirror/canonicalSelectionRange.js +9 -3
- package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.d.ts +8 -2
- package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.js +39 -3
- package/dist/prosemirror/extensions/features/EmptyParagraphFormatExtension.d.ts +8 -2
- package/dist/prosemirror/extensions/features/EmptyParagraphFormatExtension.js +4 -3
- package/dist/prosemirror/extensions/features/ImageDragExtension.js +4 -3
- package/dist/prosemirror/extensions/features/JoinedRunStyleExtension.js +4 -3
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +9 -3
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +6 -5
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +8 -2
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +11 -3
- package/dist/prosemirror/extensions/marks/RunIdentityExtension.js +4 -3
- package/dist/prosemirror/extensions/marks/markUtils.js +1 -1
- package/dist/prosemirror/pluginKeyOwner.d.ts +13 -0
- package/dist/prosemirror/pluginKeyOwner.js +35 -0
- package/dist/prosemirror/plugins/aiCitationDecorations.d.ts +11 -4
- package/dist/prosemirror/plugins/aiCitationDecorations.js +16 -3
- package/dist/prosemirror/plugins/aiSuggestionDecorations.d.ts +11 -4
- package/dist/prosemirror/plugins/aiSuggestionDecorations.js +18 -3
- package/dist/prosemirror/plugins/anonymizationDecorations.d.ts +11 -11
- package/dist/prosemirror/plugins/anonymizationDecorations.js +23 -13
- package/dist/prosemirror/plugins/autocompleteSuggestion.d.ts +8 -2
- package/dist/prosemirror/plugins/autocompleteSuggestion.js +27 -5
- package/dist/prosemirror/plugins/contentControlWidgets.d.ts +8 -2
- package/dist/prosemirror/plugins/contentControlWidgets.js +4 -3
- package/dist/prosemirror/plugins/createDocScanPlugin.d.ts +6 -5
- package/dist/prosemirror/plugins/createDocScanPlugin.js +1 -1
- package/dist/prosemirror/plugins/documentNumbering.js +5 -3
- package/dist/prosemirror/plugins/documentStyleState.d.ts +8 -2
- package/dist/prosemirror/plugins/documentStyleState.js +2 -2
- package/dist/prosemirror/plugins/documentStyles.js +1 -1
- package/dist/prosemirror/plugins/selectionTracker.d.ts +8 -2
- package/dist/prosemirror/plugins/selectionTracker.js +4 -3
- package/dist/prosemirror/plugins/suggestionMode.d.ts +8 -2
- package/dist/prosemirror/plugins/suggestionMode.js +4 -3
- package/dist/prosemirror/plugins/templateDirectives.d.ts +8 -2
- package/dist/prosemirror/plugins/templateDirectives.js +19 -4
- package/dist/prosemirror/plugins/templatePreviewValues.d.ts +8 -2
- package/dist/prosemirror/plugins/templatePreviewValues.js +27 -5
- package/dist/prosemirror/plugins/templateSlashMenu.d.ts +8 -2
- package/dist/prosemirror/plugins/templateSlashMenu.js +23 -5
- package/dist/prosemirror/plugins/transactionInvariants.js +23 -3
- package/dist/utils/fontLoader.d.ts +2 -2
- package/dist/utils/fontLoader.js +2 -7
- package/dist/utils/fontResolver.d.ts +7 -3
- package/dist/utils/fontResolver.js +6 -8
- 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 =
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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(
|
|
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:
|
|
7
|
-
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,
|
|
2
|
+
import { EditorState, Plugin } from "prosemirror-state";
|
|
3
3
|
//#region src/prosemirror/extensions/features/AutoBidiDetectionExtension.d.ts
|
|
4
|
-
declare const autoBidiDetectionKey:
|
|
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
|
|
8
|
+
import { Plugin } from "prosemirror-state";
|
|
8
9
|
//#region src/prosemirror/extensions/features/AutoBidiDetectionExtension.ts
|
|
9
|
-
|
|
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 {
|
|
2
|
+
import { Plugin } from "prosemirror-state";
|
|
3
3
|
//#region src/prosemirror/extensions/features/EmptyParagraphFormatExtension.d.ts
|
|
4
|
-
declare const emptyParagraphFormatKey:
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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,
|
|
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:
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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(
|
|
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,
|
|
5
|
+
import { EditorState, Plugin, Transaction } from "prosemirror-state";
|
|
6
6
|
//#region src/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts
|
|
7
|
-
declare const paragraphChangeTrackerKey:
|
|
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
|
|
10
|
+
import { Plugin } from "prosemirror-state";
|
|
10
11
|
import { isHistoryTransaction } from "prosemirror-history";
|
|
11
12
|
//#region src/prosemirror/extensions/features/ParagraphChangeTrackerExtension.ts
|
|
12
|
-
|
|
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
|
|
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 =
|
|
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 {
|
|
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:
|
|
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:
|
|
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:
|
|
40
|
+
key: PluginKeyOwner<AICitationDecorationState>;
|
|
34
41
|
payload: {
|
|
35
42
|
type: typeof ACTIVE_META;
|
|
36
43
|
activeId: string | null;
|