@stll/folio-core 0.39.0 → 0.39.1
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/dist/controller/hiddenEditorManager.js +2 -1
- package/dist/display-list/primitives.d.ts +1 -1
- package/dist/prosemirror/extensions/ExtensionManager.js +6 -1
- package/dist/prosemirror/plugins/suggestionMode.js +3 -9
- package/dist/prosemirror/textInput.d.ts +21 -0
- package/dist/prosemirror/textInput.js +61 -0
- package/package.json +1 -1
|
@@ -7,6 +7,7 @@ import { ensureParaIdsInDoc, ensureParaIdsInState } from "../prosemirror/extensi
|
|
|
7
7
|
import { createDocumentNumberingPlugin } from "../prosemirror/plugins/documentNumbering.js";
|
|
8
8
|
import { createDocumentStylesPlugin } from "../prosemirror/plugins/documentStyles.js";
|
|
9
9
|
import { schema } from "../prosemirror/schema/index.js";
|
|
10
|
+
import { createTextInputPlugin } from "../prosemirror/textInput.js";
|
|
10
11
|
import { proseDocumentParagraphSourceContract, readYjsParagraphSourceContract, withParagraphSourceContract, writeYjsParagraphSourceContract } from "../prosemirror/yjsParagraphSourceContract.js";
|
|
11
12
|
import { createHiddenEditorApi } from "./hiddenEditorApi.js";
|
|
12
13
|
import { panic } from "better-result";
|
|
@@ -113,7 +114,7 @@ function createHiddenEditorState(options) {
|
|
|
113
114
|
const numberingPlugin = createDocumentNumberingPlugin(document?.package.numbering);
|
|
114
115
|
const plugins = [
|
|
115
116
|
...externalPlugins,
|
|
116
|
-
...manager?.getPlugins() ?? [],
|
|
117
|
+
...manager?.getPlugins() ?? [createTextInputPlugin()],
|
|
117
118
|
styleResolverPlugin,
|
|
118
119
|
numberingPlugin
|
|
119
120
|
];
|
|
@@ -15,7 +15,7 @@ import { hasComplexScript, isComplexScriptCodePoint } from "../utils/scriptSegme
|
|
|
15
15
|
* advances rightward from its origin whatever the paragraph does.
|
|
16
16
|
*/
|
|
17
17
|
declare const glyphCellOffsetsPx: (run: DisplayGlyphRun) => readonly number[];
|
|
18
|
-
declare const DISPLAY_PRIMITIVE_KINDS: ("
|
|
18
|
+
declare const DISPLAY_PRIMITIVE_KINDS: ("glyphRun" | "rect" | "line" | "image" | "clipGroup" | "rotateGroup" | "opacityGroup")[];
|
|
19
19
|
/**
|
|
20
20
|
* Dash geometry of each stroke pattern, as multiples of the stroke thickness.
|
|
21
21
|
*
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createTextInputPlugin } from "../textInput.js";
|
|
1
2
|
import { panic } from "better-result";
|
|
2
3
|
import { Schema } from "prosemirror-model";
|
|
3
4
|
import { keymap } from "prosemirror-keymap";
|
|
@@ -46,7 +47,11 @@ var ExtensionManager = class {
|
|
|
46
47
|
if (runtime.keyboardShortcuts) allKeyboardShortcuts.push(runtime.keyboardShortcuts);
|
|
47
48
|
if (runtime.plugins) allPlugins.push(...runtime.plugins);
|
|
48
49
|
}
|
|
49
|
-
this.plugins = [
|
|
50
|
+
this.plugins = [
|
|
51
|
+
createTextInputPlugin(),
|
|
52
|
+
...allPlugins,
|
|
53
|
+
...allKeyboardShortcuts.map((shortcuts) => keymap(shortcuts))
|
|
54
|
+
];
|
|
50
55
|
this.commands = allCommands;
|
|
51
56
|
}
|
|
52
57
|
/**
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { splitBlockClearBorders } from "../extensions/features/BaseKeymapExtension.js";
|
|
2
|
+
import { handleEditorBeforeInput } from "../textInput.js";
|
|
2
3
|
import { canCarryTrackedRunMark } from "../trackedRunInlineAtoms.js";
|
|
3
4
|
import { mintRevisionId, seedRevisionIdsFromDoc } from "./revisionIds.js";
|
|
4
5
|
import { Plugin, PluginKey, TextSelection } from "prosemirror-state";
|
|
@@ -398,15 +399,8 @@ function createSuggestionModePlugin(initialActive = false, author = "User") {
|
|
|
398
399
|
return false;
|
|
399
400
|
},
|
|
400
401
|
beforeinput(view, event) {
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
if (composing || event.isComposing) return false;
|
|
404
|
-
if (event.inputType === "insertText" && event.data) {
|
|
405
|
-
event.preventDefault();
|
|
406
|
-
const { from, to } = view.state.selection;
|
|
407
|
-
return applySuggestionInsert(view, from, to, event.data, pluginState);
|
|
408
|
-
}
|
|
409
|
-
return false;
|
|
402
|
+
if (!suggestionModeKey.getState(view.state)?.active || composing) return false;
|
|
403
|
+
return handleEditorBeforeInput(view, event);
|
|
410
404
|
}
|
|
411
405
|
},
|
|
412
406
|
handleKeyDown(view, event) {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { EditorState, Plugin, Transaction } from "prosemirror-state";
|
|
2
|
+
import { EditorView } from "prosemirror-view";
|
|
3
|
+
//#region src/prosemirror/textInput.d.ts
|
|
4
|
+
type TextInputHandler<TView> = (view: TView, from: number, to: number, text: string, defaultTransaction: () => Transaction) => unknown;
|
|
5
|
+
type TextInputDispatchTarget<TView> = {
|
|
6
|
+
dispatch: (tr: Transaction) => void;
|
|
7
|
+
someProp: (propName: "handleTextInput", f: (handler: TextInputHandler<TView>) => unknown) => unknown;
|
|
8
|
+
state: EditorState;
|
|
9
|
+
};
|
|
10
|
+
type TextInput = {
|
|
11
|
+
text: string;
|
|
12
|
+
from: number;
|
|
13
|
+
to: number;
|
|
14
|
+
};
|
|
15
|
+
declare const dispatchEditorTextInput: <TView extends TextInputDispatchTarget<TView>>(view: TView, input: string | TextInput) => void;
|
|
16
|
+
/** Shared DOM boundary for editor runtimes and standalone suggestion views. */
|
|
17
|
+
declare const handleEditorBeforeInput: (view: EditorView, event: InputEvent) => boolean;
|
|
18
|
+
/** Route committed text through the model before the browser mutates mark spans. */
|
|
19
|
+
declare const createTextInputPlugin: () => Plugin<any>;
|
|
20
|
+
//#endregion
|
|
21
|
+
export { createTextInputPlugin, dispatchEditorTextInput, handleEditorBeforeInput };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Plugin } from "prosemirror-state";
|
|
2
|
+
//#region src/prosemirror/textInput.ts
|
|
3
|
+
const dispatchEditorTextInput = (view, input) => {
|
|
4
|
+
const { from, to } = typeof input === "string" ? view.state.selection : input;
|
|
5
|
+
const text = typeof input === "string" ? input : input.text;
|
|
6
|
+
const defaultTransaction = () => view.state.tr.insertText(text, from, to);
|
|
7
|
+
if (!view.someProp("handleTextInput", (handler) => handler(view, from, to, text, defaultTransaction))) view.dispatch(defaultTransaction());
|
|
8
|
+
};
|
|
9
|
+
const replacementSelection = (view, event) => {
|
|
10
|
+
if (typeof event.getTargetRanges !== "function") return null;
|
|
11
|
+
const ranges = event.getTargetRanges();
|
|
12
|
+
if (ranges.length !== 1) return null;
|
|
13
|
+
const range = ranges[0];
|
|
14
|
+
if (!range || !view.dom.contains(range.startContainer) || !view.dom.contains(range.endContainer)) return null;
|
|
15
|
+
const from = view.posAtDOM(range.startContainer, range.startOffset);
|
|
16
|
+
const to = view.posAtDOM(range.endContainer, range.endOffset);
|
|
17
|
+
if (from < 0 || to < from || to > view.state.doc.content.size) return null;
|
|
18
|
+
return {
|
|
19
|
+
from,
|
|
20
|
+
to
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
/** Shared DOM boundary for editor runtimes and standalone suggestion views. */
|
|
24
|
+
const handleEditorBeforeInput = (view, event) => {
|
|
25
|
+
if (!view.editable || !event.cancelable || event.defaultPrevented || view.composing || event.isComposing) return false;
|
|
26
|
+
if (event.inputType !== "insertText" && event.inputType !== "insertReplacementText") return false;
|
|
27
|
+
const text = event.data ?? (event.dataTransfer?.types.includes("text/plain") ? event.dataTransfer.getData("text/plain") : null);
|
|
28
|
+
if (text === null) return false;
|
|
29
|
+
const selection = event.inputType === "insertReplacementText" ? replacementSelection(view, event) : view.state.selection;
|
|
30
|
+
if (!selection) return false;
|
|
31
|
+
event.preventDefault();
|
|
32
|
+
dispatchEditorTextInput(view, {
|
|
33
|
+
text,
|
|
34
|
+
from: selection.from,
|
|
35
|
+
to: selection.to
|
|
36
|
+
});
|
|
37
|
+
return true;
|
|
38
|
+
};
|
|
39
|
+
/** Route committed text through the model before the browser mutates mark spans. */
|
|
40
|
+
const createTextInputPlugin = () => {
|
|
41
|
+
const compositions = /* @__PURE__ */ new WeakMap();
|
|
42
|
+
return new Plugin({ props: { handleDOMEvents: {
|
|
43
|
+
compositionstart(view, event) {
|
|
44
|
+
compositions.set(view, event);
|
|
45
|
+
return false;
|
|
46
|
+
},
|
|
47
|
+
compositionend(view, event) {
|
|
48
|
+
compositions.set(view, event);
|
|
49
|
+
queueMicrotask(() => {
|
|
50
|
+
if (compositions.get(view) === event) compositions.delete(view);
|
|
51
|
+
});
|
|
52
|
+
return false;
|
|
53
|
+
},
|
|
54
|
+
beforeinput(view, event) {
|
|
55
|
+
if (compositions.has(view)) return false;
|
|
56
|
+
return handleEditorBeforeInput(view, event);
|
|
57
|
+
}
|
|
58
|
+
} } });
|
|
59
|
+
};
|
|
60
|
+
//#endregion
|
|
61
|
+
export { createTextInputPlugin, dispatchEditorTextInput, handleEditorBeforeInput };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stll/folio-core",
|
|
3
|
-
"version": "0.39.
|
|
3
|
+
"version": "0.39.1",
|
|
4
4
|
"description": "Headless, framework-neutral core of folio: the OOXML (.docx) parser, document model, ProseMirror integration, and page-layout engine. No React.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"document-model",
|