@type32/codemirror-rich-obsidian-editor 0.0.25 → 0.0.26
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/module.json +1 -1
- package/dist/runtime/components/Editor/ImageEmbedComponent.vue.d.ts +2 -1
- package/dist/runtime/components/Editor/TestCustomCodeBlock.vue.d.ts +2 -1
- package/dist/runtime/components/Editor.client.vue.d.ts +4 -2
- package/dist/runtime/composables/useDocumentUtils.js +3 -9
- package/dist/runtime/composables/useEditorFrontmatter.js +3 -20
- package/dist/runtime/composables/useEditorUtils.d.ts +2 -1
- package/dist/runtime/composables/useEditorUtils.js +16 -25
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/createProsePlugin.d.ts +53 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/createProsePlugin.js +23 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseHashtagCodemirrorViewPlugin.d.ts +1 -3
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseHashtagCodemirrorViewPlugin.js +5 -13
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseInternalLinkCodemirrorViewPlugin.d.ts +0 -3
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseInternalLinkCodemirrorViewPlugin.js +1 -10
- package/dist/runtime/editor/plugins/richTextPlugin.js +37 -35
- package/dist/runtime/utils/frontmatter.js +2 -6
- package/dist/runtime/utils/internalLinks.js +2 -6
- package/dist/runtime/utils/markdownParser.d.ts +12 -0
- package/dist/runtime/utils/markdownParser.js +11 -0
- package/package.json +13 -14
package/dist/module.json
CHANGED
|
@@ -2,5 +2,6 @@ type __VLS_Props = {
|
|
|
2
2
|
filePath: string;
|
|
3
3
|
display?: string;
|
|
4
4
|
};
|
|
5
|
-
declare const
|
|
5
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
6
|
+
declare const _default: typeof __VLS_export;
|
|
6
7
|
export default _default;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
type __VLS_Props = {
|
|
2
2
|
codeContent?: string;
|
|
3
3
|
};
|
|
4
|
-
declare const
|
|
4
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
5
|
+
declare const _default: typeof __VLS_export;
|
|
5
6
|
export default _default;
|
|
@@ -10,10 +10,11 @@ type __VLS_Props = {
|
|
|
10
10
|
debug?: boolean;
|
|
11
11
|
searchOptions?: SearchOptions;
|
|
12
12
|
};
|
|
13
|
-
type
|
|
13
|
+
type __VLS_ModelProps = {
|
|
14
14
|
modelValue?: string;
|
|
15
15
|
};
|
|
16
|
-
|
|
16
|
+
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
17
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
17
18
|
view: import("vue").ShallowRef<EditorView | undefined, EditorView | undefined>;
|
|
18
19
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
19
20
|
"internal-link-click": (detail: InternalLinkClickDetail) => any;
|
|
@@ -24,4 +25,5 @@ declare const _default: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
|
24
25
|
"onExternal-link-click"?: ((detail: ExternalLinkClickDetail) => any) | undefined;
|
|
25
26
|
"onUpdate:modelValue"?: ((value: string | undefined) => any) | undefined;
|
|
26
27
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
28
|
+
declare const _default: typeof __VLS_export;
|
|
27
29
|
export default _default;
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { useAlfaaz } from "./useAlfaaz.js";
|
|
2
|
-
import {
|
|
3
|
-
import { GFM } from "@lezer/markdown";
|
|
4
|
-
import { CustomOFM } from "../editor/lezer-parsers/customOFMParsers.js";
|
|
2
|
+
import { parseMarkdownToAST } from "../utils/markdownParser.js";
|
|
5
3
|
export function useDocumentUtils() {
|
|
6
4
|
const alfaaz = useAlfaaz();
|
|
7
5
|
function getWordCount(text) {
|
|
@@ -32,9 +30,7 @@ export function useDocumentUtils() {
|
|
|
32
30
|
function getTableOfContents(text) {
|
|
33
31
|
const toc = [];
|
|
34
32
|
if (!text) return toc;
|
|
35
|
-
const tree =
|
|
36
|
-
extensions: [GFM, CustomOFM, { remove: ["SetextHeading"] }]
|
|
37
|
-
}).language.parser.parse(text);
|
|
33
|
+
const tree = parseMarkdownToAST(text);
|
|
38
34
|
tree.iterate({
|
|
39
35
|
enter: (node) => {
|
|
40
36
|
if (node.name.startsWith("ATXHeading")) {
|
|
@@ -61,9 +57,7 @@ export function useDocumentUtils() {
|
|
|
61
57
|
function getAllTags(text) {
|
|
62
58
|
const tags = [];
|
|
63
59
|
if (!text) return tags;
|
|
64
|
-
const tree =
|
|
65
|
-
extensions: [GFM, CustomOFM, { remove: ["SetextHeading"] }]
|
|
66
|
-
}).language.parser.parse(text);
|
|
60
|
+
const tree = parseMarkdownToAST(text);
|
|
67
61
|
let frontmatterEnd = 0;
|
|
68
62
|
const frontmatterNode = tree.topNode.firstChild;
|
|
69
63
|
if (frontmatterNode && frontmatterNode.name === "Frontmatter") {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { dump } from "js-yaml";
|
|
2
2
|
import { useEditorUtils } from "./useEditorUtils.js";
|
|
3
|
+
import { parseFrontmatter } from "../utils/frontmatter.js";
|
|
3
4
|
export function useEditorFrontmatter(editor) {
|
|
4
5
|
const editorUtils = useEditorUtils(editor);
|
|
5
6
|
function getFrontmatter() {
|
|
@@ -7,25 +8,7 @@ export function useEditorFrontmatter(editor) {
|
|
|
7
8
|
if (!doc) {
|
|
8
9
|
return {};
|
|
9
10
|
}
|
|
10
|
-
|
|
11
|
-
const firstNode = ast.topNode.firstChild;
|
|
12
|
-
if (!firstNode || firstNode.name !== "YAMLFrontMatter") {
|
|
13
|
-
return {};
|
|
14
|
-
}
|
|
15
|
-
const contentNode = firstNode.getChild("YAMLContent");
|
|
16
|
-
const yamlContent = contentNode ? doc.slice(contentNode.from, contentNode.to) : "";
|
|
17
|
-
try {
|
|
18
|
-
const data = load(yamlContent);
|
|
19
|
-
if (data === null || data === void 0) {
|
|
20
|
-
return { data: {} };
|
|
21
|
-
}
|
|
22
|
-
if (typeof data === "object") {
|
|
23
|
-
return { data };
|
|
24
|
-
}
|
|
25
|
-
return { error: new Error("Frontmatter is not a valid object.") };
|
|
26
|
-
} catch (e) {
|
|
27
|
-
return { error: e };
|
|
28
|
-
}
|
|
11
|
+
return parseFrontmatter(doc);
|
|
29
12
|
}
|
|
30
13
|
function setFrontmatterProperties(properties) {
|
|
31
14
|
const doc = editorUtils.getDoc() || "";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { SyntaxNode, Tree } from '@lezer/common';
|
|
2
2
|
import type { Ref } from 'vue';
|
|
3
3
|
import type { TransactionSpec } from '@codemirror/state';
|
|
4
|
+
import { parseMarkdownToAST } from '../utils/markdownParser.js';
|
|
4
5
|
import type { SearchMatch, SearchOptions } from '../editor/types/editor-types.js';
|
|
5
6
|
export declare function useEditorUtils(editor: Ref<any>): {
|
|
6
7
|
getDoc: () => string | undefined;
|
|
@@ -8,7 +9,7 @@ export declare function useEditorUtils(editor: Ref<any>): {
|
|
|
8
9
|
getSelection: () => any;
|
|
9
10
|
replaceSelection: (text: string) => void;
|
|
10
11
|
dispatch: (...specs: TransactionSpec[]) => void;
|
|
11
|
-
parseMarkdownToAST:
|
|
12
|
+
parseMarkdownToAST: typeof parseMarkdownToAST;
|
|
12
13
|
getDocAst: () => Tree;
|
|
13
14
|
findNodesByType: (tree: Tree, nodeTypeName: string) => SyntaxNode[];
|
|
14
15
|
getDocNodesByType: (nodeTypeName: string) => SyntaxNode[];
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { computed, ref, unref } from "vue";
|
|
2
2
|
import { EditorView } from "@codemirror/view";
|
|
3
|
-
import {
|
|
4
|
-
import { CustomOFM } from "../../runtime/editor/lezer-parsers/customOFMParsers";
|
|
5
|
-
import { GFM } from "@lezer/markdown";
|
|
3
|
+
import { parseMarkdownToAST } from "../utils/markdownParser.js";
|
|
6
4
|
export function useEditorUtils(editor) {
|
|
7
5
|
const view = computed(() => {
|
|
8
6
|
const instance = unref(editor);
|
|
@@ -12,6 +10,18 @@ export function useEditorUtils(editor) {
|
|
|
12
10
|
const searchResults = ref([]);
|
|
13
11
|
const currentMatchIndex = ref(-1);
|
|
14
12
|
const searchQuery = ref(null);
|
|
13
|
+
function createSearchRegex(options) {
|
|
14
|
+
return new RegExp(options.query, options.caseSensitive ? "g" : "gi");
|
|
15
|
+
}
|
|
16
|
+
function findAllMatches(doc, options) {
|
|
17
|
+
const matches = [];
|
|
18
|
+
const regex = createSearchRegex(options);
|
|
19
|
+
let match;
|
|
20
|
+
while ((match = regex.exec(doc)) !== null) {
|
|
21
|
+
matches.push({ from: match.index, to: match.index + match[0].length });
|
|
22
|
+
}
|
|
23
|
+
return matches;
|
|
24
|
+
}
|
|
15
25
|
function getDoc() {
|
|
16
26
|
return unref(view)?.state.doc.toString();
|
|
17
27
|
}
|
|
@@ -29,15 +39,6 @@ export function useEditorUtils(editor) {
|
|
|
29
39
|
function dispatch(...specs) {
|
|
30
40
|
unref(view)?.dispatch(...specs);
|
|
31
41
|
}
|
|
32
|
-
function parseMarkdownToAST(markdownText) {
|
|
33
|
-
return markdown({
|
|
34
|
-
extensions: [
|
|
35
|
-
GFM,
|
|
36
|
-
CustomOFM,
|
|
37
|
-
{ remove: ["SetextHeading"] }
|
|
38
|
-
]
|
|
39
|
-
}).language.parser.parse(markdownText);
|
|
40
|
-
}
|
|
41
42
|
function getDocAst() {
|
|
42
43
|
return parseMarkdownToAST(getDoc() || "");
|
|
43
44
|
}
|
|
@@ -69,13 +70,7 @@ export function useEditorUtils(editor) {
|
|
|
69
70
|
currentMatchIndex.value = -1;
|
|
70
71
|
return;
|
|
71
72
|
}
|
|
72
|
-
|
|
73
|
-
const regex = new RegExp(options.query, options.caseSensitive ? "g" : "gi");
|
|
74
|
-
let match;
|
|
75
|
-
while ((match = regex.exec(doc)) !== null) {
|
|
76
|
-
matches.push({ from: match.index, to: match.index + match[0].length });
|
|
77
|
-
}
|
|
78
|
-
searchResults.value = matches;
|
|
73
|
+
searchResults.value = findAllMatches(doc, options);
|
|
79
74
|
currentMatchIndex.value = -1;
|
|
80
75
|
}
|
|
81
76
|
function selectAndScrollToMatch(match) {
|
|
@@ -125,12 +120,7 @@ export function useEditorUtils(editor) {
|
|
|
125
120
|
if (!searchQuery.value || !searchQuery.value.query) return;
|
|
126
121
|
const doc = getDoc();
|
|
127
122
|
if (!doc) return;
|
|
128
|
-
const matches =
|
|
129
|
-
const regex = new RegExp(searchQuery.value.query, searchQuery.value.caseSensitive ? "g" : "gi");
|
|
130
|
-
let match;
|
|
131
|
-
while ((match = regex.exec(doc)) !== null) {
|
|
132
|
-
matches.push({ from: match.index, to: match.index + match[0].length });
|
|
133
|
-
}
|
|
123
|
+
const matches = findAllMatches(doc, searchQuery.value);
|
|
134
124
|
if (matches.length === 0) return;
|
|
135
125
|
const changes = matches.map((m) => ({
|
|
136
126
|
from: m.from,
|
|
@@ -155,6 +145,7 @@ export function useEditorUtils(editor) {
|
|
|
155
145
|
replaceSelection,
|
|
156
146
|
dispatch,
|
|
157
147
|
parseMarkdownToAST,
|
|
148
|
+
// Re-exported from utils for convenience
|
|
158
149
|
getDocAst,
|
|
159
150
|
findNodesByType,
|
|
160
151
|
getDocNodesByType,
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Decoration, type DecorationSet } from '@codemirror/view';
|
|
2
|
+
import { StateField } from '@codemirror/state';
|
|
3
|
+
import type { EditorState, Range as EditorRange } from '@codemirror/state';
|
|
4
|
+
/**
|
|
5
|
+
* Configuration options for creating a prose plugin
|
|
6
|
+
*/
|
|
7
|
+
export interface ProsePluginConfig {
|
|
8
|
+
/**
|
|
9
|
+
* Function to build decorations from the editor state
|
|
10
|
+
*/
|
|
11
|
+
buildDecorations: (state: EditorState) => EditorRange<Decoration>[];
|
|
12
|
+
/**
|
|
13
|
+
* Whether to rebuild decorations on document changes (default: true)
|
|
14
|
+
*/
|
|
15
|
+
rebuildOnDocChange?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Whether to rebuild decorations on selection changes (default: false)
|
|
18
|
+
*/
|
|
19
|
+
rebuildOnSelection?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Whether to sort the decorations (default: true)
|
|
22
|
+
*/
|
|
23
|
+
sortDecorations?: boolean;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Creates a CodeMirror StateField plugin for prose decorations with the standard pattern.
|
|
27
|
+
* This reduces boilerplate for simple prose plugins that follow the common pattern:
|
|
28
|
+
* - Build decorations from syntax tree
|
|
29
|
+
* - Update on document/selection changes
|
|
30
|
+
* - Provide decorations to editor view
|
|
31
|
+
*
|
|
32
|
+
* @param config Configuration object for the plugin
|
|
33
|
+
* @returns A CodeMirror StateField that can be added to the editor
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* export const myPlugin = createProsePlugin({
|
|
38
|
+
* buildDecorations: (state) => {
|
|
39
|
+
* const decorations: EditorRange<Decoration>[] = []
|
|
40
|
+
* syntaxTree(state).iterate({
|
|
41
|
+
* enter(node) {
|
|
42
|
+
* if (node.name === 'MyNode') {
|
|
43
|
+
* decorations.push(myDecoration.range(node.from, node.to))
|
|
44
|
+
* }
|
|
45
|
+
* }
|
|
46
|
+
* })
|
|
47
|
+
* return decorations
|
|
48
|
+
* },
|
|
49
|
+
* rebuildOnSelection: true
|
|
50
|
+
* })
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export declare function createProsePlugin(config: ProsePluginConfig): StateField<DecorationSet>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { EditorView } from "@codemirror/view";
|
|
2
|
+
import { StateField, RangeSet } from "@codemirror/state";
|
|
3
|
+
export function createProsePlugin(config) {
|
|
4
|
+
const {
|
|
5
|
+
buildDecorations,
|
|
6
|
+
rebuildOnDocChange = true,
|
|
7
|
+
rebuildOnSelection = false,
|
|
8
|
+
sortDecorations = true
|
|
9
|
+
} = config;
|
|
10
|
+
return StateField.define({
|
|
11
|
+
create(state) {
|
|
12
|
+
return RangeSet.of(buildDecorations(state), sortDecorations);
|
|
13
|
+
},
|
|
14
|
+
update(value, tr) {
|
|
15
|
+
const shouldRebuild = rebuildOnDocChange && tr.docChanged || rebuildOnSelection && tr.selection;
|
|
16
|
+
if (shouldRebuild) {
|
|
17
|
+
return RangeSet.of(buildDecorations(tr.state), sortDecorations);
|
|
18
|
+
}
|
|
19
|
+
return value.map(tr.changes);
|
|
20
|
+
},
|
|
21
|
+
provide: (f) => EditorView.decorations.from(f)
|
|
22
|
+
});
|
|
23
|
+
}
|
package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseHashtagCodemirrorViewPlugin.d.ts
CHANGED
|
@@ -1,3 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { StateField } from '@codemirror/state';
|
|
3
|
-
export declare const proseHashtagCodemirrorViewPlugin: StateField<DecorationSet>;
|
|
1
|
+
export declare const proseHashtagCodemirrorViewPlugin: import("@codemirror/state").StateField<import("@codemirror/view").DecorationSet>;
|
package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseHashtagCodemirrorViewPlugin.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { EditorView } from "@codemirror/view";
|
|
2
|
-
import { StateField, RangeSet } from "@codemirror/state";
|
|
3
1
|
import { syntaxTree } from "@codemirror/language";
|
|
4
2
|
import { decorationProseHashtag } from "../../utility/decorations.js";
|
|
3
|
+
import { createProsePlugin } from "./createProsePlugin.js";
|
|
5
4
|
function buildHashtagWrappers(state) {
|
|
6
5
|
const decorations = [];
|
|
7
6
|
syntaxTree(state).iterate({
|
|
@@ -13,15 +12,8 @@ function buildHashtagWrappers(state) {
|
|
|
13
12
|
});
|
|
14
13
|
return decorations;
|
|
15
14
|
}
|
|
16
|
-
export const proseHashtagCodemirrorViewPlugin =
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
update(value, tr) {
|
|
21
|
-
if (tr.docChanged) {
|
|
22
|
-
return RangeSet.of(buildHashtagWrappers(tr.state));
|
|
23
|
-
}
|
|
24
|
-
return value.map(tr.changes);
|
|
25
|
-
},
|
|
26
|
-
provide: (f) => EditorView.decorations.from(f)
|
|
15
|
+
export const proseHashtagCodemirrorViewPlugin = createProsePlugin({
|
|
16
|
+
buildDecorations: buildHashtagWrappers,
|
|
17
|
+
rebuildOnDocChange: true,
|
|
18
|
+
rebuildOnSelection: false
|
|
27
19
|
});
|
|
@@ -1,6 +1,3 @@
|
|
|
1
1
|
import { type DecorationSet } from '@codemirror/view';
|
|
2
2
|
import { StateField } from '@codemirror/state';
|
|
3
|
-
import type { EditorState } from '@codemirror/state';
|
|
4
|
-
declare function isNodeRangeActive(state: EditorState, nodeFrom: number, nodeTo: number): boolean;
|
|
5
|
-
export { isNodeRangeActive };
|
|
6
3
|
export declare const proseInternalLinkCodemirrorViewPlugin: StateField<DecorationSet>;
|
|
@@ -3,15 +3,7 @@ import { StateField, RangeSet } from "@codemirror/state";
|
|
|
3
3
|
import { syntaxTree } from "@codemirror/language";
|
|
4
4
|
import { internalLinkMapFacet } from "../linkMappingConfig.js";
|
|
5
5
|
import { ProseVueComponentEmbedWidget } from "../codemirror-widgets/proseVueComponentEmbedWidget.js";
|
|
6
|
-
import { cursorSelectionCoveredNode, toCursorNodePositions } from "../../utility/tools.js";
|
|
7
|
-
function isNodeRangeActive(state, nodeFrom, nodeTo) {
|
|
8
|
-
const cursor = state.selection.main;
|
|
9
|
-
if (cursor.empty) {
|
|
10
|
-
return cursor.from >= nodeFrom && cursor.from <= nodeTo;
|
|
11
|
-
} else {
|
|
12
|
-
return Math.max(nodeFrom, cursor.from) < Math.min(nodeTo, cursor.to);
|
|
13
|
-
}
|
|
14
|
-
}
|
|
6
|
+
import { cursorSelectionCoveredNode, toCursorNodePositions, isNodeRangeActive } from "../../utility/tools.js";
|
|
15
7
|
function buildInternalLinkDecorations(state) {
|
|
16
8
|
const decorations = [];
|
|
17
9
|
const linkMap = state.facet(internalLinkMapFacet);
|
|
@@ -102,7 +94,6 @@ function buildInternalLinkDecorations(state) {
|
|
|
102
94
|
});
|
|
103
95
|
return [...decorations, ...widgets];
|
|
104
96
|
}
|
|
105
|
-
export { isNodeRangeActive };
|
|
106
97
|
export const proseInternalLinkCodemirrorViewPlugin = StateField.define({
|
|
107
98
|
create(state) {
|
|
108
99
|
return RangeSet.of(buildInternalLinkDecorations(state), true);
|
|
@@ -47,16 +47,20 @@ export default class RichEditPlugin {
|
|
|
47
47
|
}
|
|
48
48
|
update(update) {
|
|
49
49
|
if (update.docChanged) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
decorations = decorations.update
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
50
|
+
if (update.selectionSet) {
|
|
51
|
+
this.decorations = this.process(update.view);
|
|
52
|
+
} else {
|
|
53
|
+
let decorations = this.decorations.map(update.changes);
|
|
54
|
+
update.changes.iterChangedRanges((fromA, toA, fromB, toB) => {
|
|
55
|
+
const newWidgets = this.processRange(update.view, fromB, toB);
|
|
56
|
+
decorations = decorations.update({
|
|
57
|
+
filter: (f, t) => f < fromB || t > toB,
|
|
58
|
+
add: newWidgets,
|
|
59
|
+
sort: true
|
|
60
|
+
});
|
|
57
61
|
});
|
|
58
|
-
|
|
59
|
-
|
|
62
|
+
this.decorations = decorations;
|
|
63
|
+
}
|
|
60
64
|
} else if (update.viewportChanged || update.selectionSet) {
|
|
61
65
|
this.decorations = this.process(update.view);
|
|
62
66
|
}
|
|
@@ -71,35 +75,33 @@ export default class RichEditPlugin {
|
|
|
71
75
|
processRange(view, from, to) {
|
|
72
76
|
const widgets = [];
|
|
73
77
|
const [cursor] = view.state.selection.ranges;
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if (
|
|
83
|
-
if (cursorInNode(cursor?.from, cursor?.to, nodeFrom, nodeTo)) {
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
const line = view.state.doc.lineAt(nodeFrom);
|
|
87
|
-
widgets.push(decorationHidden.range(nodeFrom, nodeTo));
|
|
88
|
-
widgets.push(Decoration.line({ attributes: { class: "hr" } }).range(line.from));
|
|
78
|
+
syntaxTree(view.state).iterate({
|
|
79
|
+
from,
|
|
80
|
+
to,
|
|
81
|
+
enter(node) {
|
|
82
|
+
const nodeName = node.name;
|
|
83
|
+
const nodeFrom = node.from;
|
|
84
|
+
const nodeTo = node.to;
|
|
85
|
+
if (nodeName === "HorizontalRule") {
|
|
86
|
+
if (cursorInNode(cursor?.from, cursor?.to, nodeFrom, nodeTo)) {
|
|
89
87
|
return;
|
|
90
88
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
widgets.push(decorationBullet.range(nodeFrom, nodeTo));
|
|
96
|
-
if (hideComponentMarkTokens.includes(node.name))
|
|
97
|
-
widgets.push(decorationHidden.range(nodeFrom, nodeTo));
|
|
98
|
-
if (nodeName === "HeaderMark")
|
|
99
|
-
widgets.push(decorationHidden.range(nodeFrom, nodeTo + 1));
|
|
89
|
+
const line = view.state.doc.lineAt(nodeFrom);
|
|
90
|
+
widgets.push(decorationHidden.range(nodeFrom, nodeTo));
|
|
91
|
+
widgets.push(Decoration.line({ attributes: { class: "hr" } }).range(line.from));
|
|
92
|
+
return;
|
|
100
93
|
}
|
|
101
|
-
|
|
102
|
-
|
|
94
|
+
if ((nodeName.startsWith("ATXHeading") || revealComponentMarkTokensOnCursor.includes(nodeName)) && cursorInNode(cursor?.from, cursor?.to, nodeFrom, nodeTo)) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
if (nodeName === "ListMark" && node.matchContext(["BulletList", "ListItem"]) && cursor?.from != nodeFrom && cursor?.from != nodeFrom + 1)
|
|
98
|
+
widgets.push(decorationBullet.range(nodeFrom, nodeTo));
|
|
99
|
+
if (hideComponentMarkTokens.includes(node.name))
|
|
100
|
+
widgets.push(decorationHidden.range(nodeFrom, nodeTo));
|
|
101
|
+
if (nodeName === "HeaderMark")
|
|
102
|
+
widgets.push(decorationHidden.range(nodeFrom, nodeTo + 1));
|
|
103
|
+
}
|
|
104
|
+
});
|
|
103
105
|
return widgets;
|
|
104
106
|
}
|
|
105
107
|
}
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import { load } from "js-yaml";
|
|
2
|
-
import {
|
|
3
|
-
import { GFM } from "@lezer/markdown";
|
|
4
|
-
import { CustomOFM } from "../editor/lezer-parsers/customOFMParsers.js";
|
|
2
|
+
import { parseMarkdownToAST } from "./markdownParser.js";
|
|
5
3
|
export function parseFrontmatter(markdownText) {
|
|
6
4
|
if (!markdownText) {
|
|
7
5
|
return {};
|
|
8
6
|
}
|
|
9
|
-
const tree =
|
|
10
|
-
extensions: [GFM, CustomOFM, { remove: ["SetextHeading"] }]
|
|
11
|
-
}).language.parser.parse(markdownText);
|
|
7
|
+
const tree = parseMarkdownToAST(markdownText);
|
|
12
8
|
const firstNode = tree.topNode.firstChild;
|
|
13
9
|
if (!firstNode || firstNode.name !== "YAMLFrontMatter") {
|
|
14
10
|
return {};
|
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { GFM } from "@lezer/markdown";
|
|
3
|
-
import { CustomOFM } from "../editor/lezer-parsers/customOFMParsers.js";
|
|
1
|
+
import { parseMarkdownToAST } from "./markdownParser.js";
|
|
4
2
|
export function getInternalLinks(markdownText) {
|
|
5
3
|
const links = [];
|
|
6
|
-
const tree =
|
|
7
|
-
extensions: [GFM, CustomOFM, { remove: ["SetextHeading"] }]
|
|
8
|
-
}).language.parser.parse(markdownText);
|
|
4
|
+
const tree = parseMarkdownToAST(markdownText);
|
|
9
5
|
tree.iterate({
|
|
10
6
|
enter: (node) => {
|
|
11
7
|
if (node.name === "InternalLink") {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Tree } from '@lezer/common';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a markdown parser with standard OFM extensions
|
|
4
|
+
* This configuration is used consistently across the codebase
|
|
5
|
+
*/
|
|
6
|
+
export declare function createMarkdownParser(): import("@codemirror/language").LanguageSupport;
|
|
7
|
+
/**
|
|
8
|
+
* Parses markdown text to AST using the standard OFM parser configuration
|
|
9
|
+
* @param markdownText The markdown text to parse
|
|
10
|
+
* @returns The parsed syntax tree
|
|
11
|
+
*/
|
|
12
|
+
export declare function parseMarkdownToAST(markdownText: string): Tree;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { markdown } from "@codemirror/lang-markdown";
|
|
2
|
+
import { GFM } from "@lezer/markdown";
|
|
3
|
+
import { CustomOFM } from "../editor/lezer-parsers/customOFMParsers.js";
|
|
4
|
+
export function createMarkdownParser() {
|
|
5
|
+
return markdown({
|
|
6
|
+
extensions: [GFM, CustomOFM, { remove: ["SetextHeading"] }]
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
export function parseMarkdownToAST(markdownText) {
|
|
10
|
+
return createMarkdownParser().language.parser.parse(markdownText);
|
|
11
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@type32/codemirror-rich-obsidian-editor",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.26",
|
|
4
4
|
"description": "OFM Editor Component for Nuxt.",
|
|
5
5
|
"repository": "Type-32/codemirror-rich-obsidian",
|
|
6
6
|
"license": "MIT",
|
|
@@ -35,45 +35,44 @@
|
|
|
35
35
|
"test:types": "bunx vue-tsc --noEmit && cd playground && bunx vue-tsc --noEmit"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@codemirror/autocomplete": "^6.
|
|
38
|
+
"@codemirror/autocomplete": "^6.19.0",
|
|
39
39
|
"@codemirror/lang-json": "^6.0.2",
|
|
40
|
-
"@codemirror/lang-markdown": "^6.
|
|
40
|
+
"@codemirror/lang-markdown": "^6.4.0",
|
|
41
41
|
"@codemirror/lang-yaml": "^6.1.2",
|
|
42
42
|
"@codemirror/language": "^6.11.3",
|
|
43
43
|
"@codemirror/language-data": "^6.5.1",
|
|
44
44
|
"@hsorby/vue3-katex": "0.6.0-rc.7",
|
|
45
45
|
"@lezer/markdown": "^1.4.3",
|
|
46
46
|
"@nuxt/image": "1.10.0",
|
|
47
|
-
"@nuxt/kit": "^4.1.
|
|
48
|
-
"@nuxt/ui": "^4.0.
|
|
47
|
+
"@nuxt/kit": "^4.1.3",
|
|
48
|
+
"@nuxt/ui": "^4.0.1",
|
|
49
49
|
"alfaaz": "^1.1.0",
|
|
50
50
|
"codemirror": "^6.0.2",
|
|
51
51
|
"js-yaml": "^4.1.0",
|
|
52
|
-
"katex": "^0.16.
|
|
52
|
+
"katex": "^0.16.23",
|
|
53
53
|
"lezer-markdown-obsidian": "^0.0.3",
|
|
54
54
|
"markdown-it": "^14.1.0",
|
|
55
55
|
"markdown-it-obsidian-callouts": "^0.3.2",
|
|
56
56
|
"vue-codemirror6": "^1.3.22"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
|
-
"@iconify-json/lucide": "^1.2.
|
|
60
|
-
"@iconify-json/simple-icons": "^1.2.
|
|
59
|
+
"@iconify-json/lucide": "^1.2.69",
|
|
60
|
+
"@iconify-json/simple-icons": "^1.2.54",
|
|
61
61
|
"@nuxt/devtools": "^2.6.5",
|
|
62
62
|
"@nuxt/eslint-config": "^1.9.0",
|
|
63
63
|
"@nuxt/fonts": "0.11.4",
|
|
64
64
|
"@nuxt/icon": "1.15.0",
|
|
65
65
|
"@nuxt/module-builder": "^1.0.2",
|
|
66
|
-
"@nuxt/schema": "^4.1.
|
|
66
|
+
"@nuxt/schema": "^4.1.3",
|
|
67
67
|
"@nuxt/test-utils": "^3.19.2",
|
|
68
68
|
"@types/js-yaml": "^4.0.9",
|
|
69
69
|
"@types/node": "latest",
|
|
70
70
|
"changelogen": "^0.6.2",
|
|
71
|
-
"eslint": "^9.
|
|
72
|
-
"nuxt": "^4.1.
|
|
73
|
-
"typescript": "~5.9.
|
|
71
|
+
"eslint": "^9.37.0",
|
|
72
|
+
"nuxt": "^4.1.3",
|
|
73
|
+
"typescript": "~5.9.3",
|
|
74
74
|
"vitest": "^3.2.4",
|
|
75
|
-
"vue-tsc": "^3.
|
|
76
|
-
"alfaaz": "^1.1.0"
|
|
75
|
+
"vue-tsc": "^3.1.1"
|
|
77
76
|
},
|
|
78
77
|
"trustedDependencies": [
|
|
79
78
|
"@parcel/watcher",
|