@type32/codemirror-rich-obsidian-editor 0.0.3

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 (84) hide show
  1. package/README.md +99 -0
  2. package/dist/module.d.mts +8 -0
  3. package/dist/module.json +9 -0
  4. package/dist/module.mjs +25 -0
  5. package/dist/runtime/assets/css/editor.css +1 -0
  6. package/dist/runtime/components/Editor/ImageEmbedComponent.vue +11 -0
  7. package/dist/runtime/components/Editor/ImageEmbedComponent.vue.d.ts +5 -0
  8. package/dist/runtime/components/Editor/TestCustomCodeBlock.vue +16 -0
  9. package/dist/runtime/components/Editor/TestCustomCodeBlock.vue.d.ts +5 -0
  10. package/dist/runtime/components/Editor.client.vue +182 -0
  11. package/dist/runtime/components/Editor.client.vue.d.ts +23 -0
  12. package/dist/runtime/editor/lezer-parsers/customOFMParsers.d.ts +1 -0
  13. package/dist/runtime/editor/lezer-parsers/customOFMParsers.js +23 -0
  14. package/dist/runtime/editor/lezer-parsers/lezerCalloutParser.d.ts +8 -0
  15. package/dist/runtime/editor/lezer-parsers/lezerCalloutParser.js +53 -0
  16. package/dist/runtime/editor/lezer-parsers/lezerHashtagParser.d.ts +6 -0
  17. package/dist/runtime/editor/lezer-parsers/lezerHashtagParser.js +35 -0
  18. package/dist/runtime/editor/lezer-parsers/lezerIndentationParser.d.ts +4 -0
  19. package/dist/runtime/editor/lezer-parsers/lezerIndentationParser.js +23 -0
  20. package/dist/runtime/editor/lezer-parsers/lezerInternalLinkParser.d.ts +10 -0
  21. package/dist/runtime/editor/lezer-parsers/lezerInternalLinkParser.js +110 -0
  22. package/dist/runtime/editor/lezer-parsers/lezerLatexParser.d.ts +7 -0
  23. package/dist/runtime/editor/lezer-parsers/lezerLatexParser.js +75 -0
  24. package/dist/runtime/editor/lezer-parsers/lezerYamlFrontmatterParser.d.ts +13 -0
  25. package/dist/runtime/editor/lezer-parsers/lezerYamlFrontmatterParser.js +55 -0
  26. package/dist/runtime/editor/plugins/codemirror-editor-plugins/customBracketClosingPlugin.d.ts +4 -0
  27. package/dist/runtime/editor/plugins/codemirror-editor-plugins/customBracketClosingPlugin.js +94 -0
  28. package/dist/runtime/editor/plugins/codemirror-editor-plugins/editorAttributesPlugin.d.ts +8 -0
  29. package/dist/runtime/editor/plugins/codemirror-editor-plugins/editorAttributesPlugin.js +136 -0
  30. package/dist/runtime/editor/plugins/codemirror-editor-plugins/editorInternalLinkAutocompletePlugin.d.ts +1 -0
  31. package/dist/runtime/editor/plugins/codemirror-editor-plugins/editorInternalLinkAutocompletePlugin.js +43 -0
  32. package/dist/runtime/editor/plugins/codemirror-editor-plugins/editorKeymapPlugin.d.ts +1 -0
  33. package/dist/runtime/editor/plugins/codemirror-editor-plugins/editorKeymapPlugin.js +95 -0
  34. package/dist/runtime/editor/plugins/codemirror-editor-plugins/editorLinkClickPlugin.d.ts +1 -0
  35. package/dist/runtime/editor/plugins/codemirror-editor-plugins/editorLinkClickPlugin.js +43 -0
  36. package/dist/runtime/editor/plugins/codemirror-editor-plugins/indentationGuidesPlugin.d.ts +10 -0
  37. package/dist/runtime/editor/plugins/codemirror-editor-plugins/indentationGuidesPlugin.js +121 -0
  38. package/dist/runtime/editor/plugins/codemirror-editor-plugins/indentationListPlugin.d.ts +5 -0
  39. package/dist/runtime/editor/plugins/codemirror-editor-plugins/indentationListPlugin.js +66 -0
  40. package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseCalloutPlugin.d.ts +3 -0
  41. package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseCalloutPlugin.js +63 -0
  42. package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseCodeBlockCodemirrorViewPlugin.d.ts +3 -0
  43. package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseCodeBlockCodemirrorViewPlugin.js +98 -0
  44. package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseHashtagCodemirrorViewPlugin.d.ts +3 -0
  45. package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseHashtagCodemirrorViewPlugin.js +27 -0
  46. package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseHighlightCodemirrorViewPlugin.d.ts +3 -0
  47. package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseHighlightCodemirrorViewPlugin.js +51 -0
  48. package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseInternalLinkCodemirrorViewPlugin.d.ts +6 -0
  49. package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseInternalLinkCodemirrorViewPlugin.js +112 -0
  50. package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseLatexCodemirrorViewPlugin.d.ts +2 -0
  51. package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseLatexCodemirrorViewPlugin.js +160 -0
  52. package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseLinkCodemirrorViewPlugin.d.ts +3 -0
  53. package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseLinkCodemirrorViewPlugin.js +98 -0
  54. package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseQuoteblockCodemirrorViewPlugin.d.ts +3 -0
  55. package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseQuoteblockCodemirrorViewPlugin.js +76 -0
  56. package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseTaskListPlugin.d.ts +8 -0
  57. package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseTaskListPlugin.js +106 -0
  58. package/dist/runtime/editor/plugins/codemirror-widgets/proseCalloutWidget.d.ts +13 -0
  59. package/dist/runtime/editor/plugins/codemirror-widgets/proseCalloutWidget.js +87 -0
  60. package/dist/runtime/editor/plugins/codemirror-widgets/proseCodeBlockWidgets.d.ts +15 -0
  61. package/dist/runtime/editor/plugins/codemirror-widgets/proseCodeBlockWidgets.js +53 -0
  62. package/dist/runtime/editor/plugins/codemirror-widgets/proseLatexWidgets.d.ts +11 -0
  63. package/dist/runtime/editor/plugins/codemirror-widgets/proseLatexWidgets.js +50 -0
  64. package/dist/runtime/editor/plugins/codemirror-widgets/proseVueComponentEmbedWidget.d.ts +12 -0
  65. package/dist/runtime/editor/plugins/codemirror-widgets/proseVueComponentEmbedWidget.js +32 -0
  66. package/dist/runtime/editor/plugins/customBracketClosingConfig.d.ts +2 -0
  67. package/dist/runtime/editor/plugins/customBracketClosingConfig.js +4 -0
  68. package/dist/runtime/editor/plugins/lezerStylesHighlightingPlugin.d.ts +3 -0
  69. package/dist/runtime/editor/plugins/lezerStylesHighlightingPlugin.js +55 -0
  70. package/dist/runtime/editor/plugins/linkMappingConfig.d.ts +3 -0
  71. package/dist/runtime/editor/plugins/linkMappingConfig.js +4 -0
  72. package/dist/runtime/editor/plugins/richTextPlugin.d.ts +10 -0
  73. package/dist/runtime/editor/plugins/richTextPlugin.js +94 -0
  74. package/dist/runtime/editor/plugins/specialCodeBlockMappingConfig.d.ts +3 -0
  75. package/dist/runtime/editor/plugins/specialCodeBlockMappingConfig.js +4 -0
  76. package/dist/runtime/editor/types/editor-types.d.ts +17 -0
  77. package/dist/runtime/editor/utility/decorations.d.ts +9 -0
  78. package/dist/runtime/editor/utility/decorations.js +9 -0
  79. package/dist/runtime/editor/utility/tools.d.ts +12 -0
  80. package/dist/runtime/editor/utility/tools.js +32 -0
  81. package/dist/runtime/editor/wysiwyg.d.ts +4 -0
  82. package/dist/runtime/editor/wysiwyg.js +80 -0
  83. package/dist/types.d.mts +3 -0
  84. package/package.json +80 -0
@@ -0,0 +1,160 @@
1
+ import { EditorView, ViewPlugin, Decoration } from "@codemirror/view";
2
+ import { syntaxTree } from "@codemirror/language";
3
+ import { StateField } from "@codemirror/state";
4
+ import { BlockLatexWidget, InlineLatexWidget } from "../codemirror-widgets/proseLatexWidgets.js";
5
+ import { cursorSelectionCoveredNode, toCursorNodePositions } from "../../utility/tools.js";
6
+ function decorate(state) {
7
+ const decorations = [];
8
+ const tree = syntaxTree(state);
9
+ const cursor = state.selection.main;
10
+ tree.iterate({
11
+ enter: (node) => {
12
+ if (node.name === "TexBlock") {
13
+ const isNodeRangeActive = (nodeFrom, nodeTo) => {
14
+ if (cursor.empty) {
15
+ return cursor.from >= nodeFrom && cursor.from <= nodeTo;
16
+ } else {
17
+ return Math.max(nodeFrom, cursor.from) < Math.min(nodeTo, cursor.to);
18
+ }
19
+ };
20
+ const poses = toCursorNodePositions(state, node);
21
+ if (isNodeRangeActive(node.from, node.to) || cursorSelectionCoveredNode(poses.cursorFrom, poses.cursorTo, poses.nodeFrom, poses.nodeTo)) {
22
+ return;
23
+ }
24
+ const content = state.doc.sliceString(node.from + 2, node.to - 2);
25
+ const nodeText = state.doc.sliceString(node.from, node.to);
26
+ const hasLineBreaks = nodeText.includes("\n");
27
+ if (hasLineBreaks) {
28
+ decorations.push(Decoration.mark({ class: "cm-hidden-latex" }).range(node.from, node.to));
29
+ const line = state.doc.lineAt(node.to);
30
+ const widget = Decoration.widget({
31
+ widget: new BlockLatexWidget(content),
32
+ block: true,
33
+ side: 1
34
+ });
35
+ decorations.push(widget.range(line.to));
36
+ } else {
37
+ const deco = Decoration.replace({
38
+ widget: new BlockLatexWidget(content)
39
+ });
40
+ decorations.push(deco.range(node.from, node.to));
41
+ }
42
+ }
43
+ }
44
+ });
45
+ return Decoration.set(decorations);
46
+ }
47
+ function decorateInline(view) {
48
+ const decorations = [];
49
+ const tree = syntaxTree(view.state);
50
+ const cursor = view.state.selection.main;
51
+ tree.iterate({
52
+ enter: (node) => {
53
+ if (node.name === "TexInline") {
54
+ const isNodeRangeActive = (nodeFrom, nodeTo) => {
55
+ if (cursor.empty) {
56
+ return cursor.from >= nodeFrom && cursor.from <= nodeTo;
57
+ } else {
58
+ return Math.max(nodeFrom, cursor.from) < Math.min(nodeTo, cursor.to);
59
+ }
60
+ };
61
+ if (isNodeRangeActive(node.from, node.to)) {
62
+ return;
63
+ }
64
+ const content = view.state.doc.sliceString(node.from + 1, node.to - 1);
65
+ const deco = Decoration.replace({
66
+ widget: new InlineLatexWidget(content)
67
+ });
68
+ decorations.push(deco.range(node.from, node.to));
69
+ }
70
+ }
71
+ });
72
+ return Decoration.set(decorations);
73
+ }
74
+ const latexBlockPlugin = StateField.define({
75
+ create(state) {
76
+ return decorate(state);
77
+ },
78
+ update(value, tr) {
79
+ if (tr.docChanged || tr.selection) {
80
+ return decorate(tr.state);
81
+ }
82
+ return value.map(tr.changes);
83
+ },
84
+ provide: (f) => EditorView.decorations.from(f)
85
+ });
86
+ const latexInlinePlugin = ViewPlugin.fromClass(
87
+ class {
88
+ decorations;
89
+ constructor(view) {
90
+ this.decorations = this.buildDecorations(view);
91
+ }
92
+ update(update) {
93
+ if (update.docChanged) {
94
+ this.decorations = this.decorations.map(update.changes);
95
+ update.changes.iterChangedRanges((fromA, toA, fromB, toB) => {
96
+ const newWidgets = this.buildDecorationsForRange(
97
+ update.view,
98
+ fromB,
99
+ toB
100
+ );
101
+ this.decorations = this.decorations.update({
102
+ filter: (f, t) => f < fromB || t > toB,
103
+ add: newWidgets,
104
+ sort: true
105
+ });
106
+ });
107
+ } else if (update.viewportChanged || update.selectionSet) {
108
+ this.decorations = this.buildDecorations(update.view);
109
+ }
110
+ }
111
+ buildDecorations(view) {
112
+ const decorations = [];
113
+ for (const { from, to } of view.visibleRanges) {
114
+ decorations.push(...this.buildDecorationsForRange(view, from, to));
115
+ }
116
+ return Decoration.set(decorations);
117
+ }
118
+ buildDecorationsForRange(view, from, to) {
119
+ const decorations = [];
120
+ const tree = syntaxTree(view.state);
121
+ const cursor = view.state.selection.main;
122
+ tree.iterate({
123
+ from,
124
+ to,
125
+ enter: (node) => {
126
+ if (node.name === "TexInline") {
127
+ const isNodeRangeActive = (nodeFrom, nodeTo) => {
128
+ if (cursor.empty) {
129
+ return cursor.from >= nodeFrom && cursor.from <= nodeTo;
130
+ } else {
131
+ return Math.max(nodeFrom, cursor.from) < Math.min(nodeTo, cursor.to);
132
+ }
133
+ };
134
+ if (isNodeRangeActive(node.from, node.to)) {
135
+ return;
136
+ }
137
+ const content = view.state.doc.sliceString(
138
+ node.from + 1,
139
+ node.to - 1
140
+ );
141
+ const deco = Decoration.replace({
142
+ widget: new InlineLatexWidget(content)
143
+ });
144
+ decorations.push(deco.range(node.from, node.to));
145
+ }
146
+ }
147
+ });
148
+ return decorations;
149
+ }
150
+ },
151
+ {
152
+ decorations: (v) => v.decorations
153
+ }
154
+ );
155
+ export function proseLatexCodemirrorViewPlugin() {
156
+ return [
157
+ latexBlockPlugin,
158
+ latexInlinePlugin
159
+ ];
160
+ }
@@ -0,0 +1,3 @@
1
+ import { type DecorationSet } from '@codemirror/view';
2
+ import { StateField } from '@codemirror/state';
3
+ export declare const proseLinkCodemirrorViewPlugin: StateField<DecorationSet>;
@@ -0,0 +1,98 @@
1
+ import { Decoration, EditorView } from "@codemirror/view";
2
+ import { StateField, RangeSet } from "@codemirror/state";
3
+ import { syntaxTree } from "@codemirror/language";
4
+ import { cursorSelectionCoveredNode, isNodeRangeActive, toCursorNodePositions } from "../../utility/tools.js";
5
+ import { ProseVueComponentEmbedWidget } from "../codemirror-widgets/proseVueComponentEmbedWidget.js";
6
+ import ImageEmbedComponent from "../../../components/Editor/ImageEmbedComponent.vue";
7
+ function buildLinkDecorations(state) {
8
+ const decorations = [];
9
+ const widgets = [];
10
+ syntaxTree(state).iterate({
11
+ enter({ node }) {
12
+ if (node.name === "Image") {
13
+ const poses = toCursorNodePositions(state, node);
14
+ const isActive = isNodeRangeActive(state, node.from, node.to) || cursorSelectionCoveredNode(poses.cursorFrom, poses.cursorTo, poses.nodeFrom, poses.nodeTo);
15
+ if (!isActive) {
16
+ const urlNode = node.getChild("URL");
17
+ if (urlNode) {
18
+ const url = state.doc.sliceString(urlNode.from, urlNode.to);
19
+ decorations.push(Decoration.replace({
20
+ widget: new ProseVueComponentEmbedWidget(ImageEmbedComponent, { filePath: url }, node.from),
21
+ block: true
22
+ }).range(node.from, node.to));
23
+ }
24
+ }
25
+ return false;
26
+ }
27
+ if (node.name === "Link") {
28
+ const poses = toCursorNodePositions(state, node);
29
+ const isActive = isNodeRangeActive(state, node.from, node.to) || cursorSelectionCoveredNode(poses.cursorFrom, poses.cursorTo, poses.nodeFrom, poses.nodeTo);
30
+ if (!isActive) {
31
+ const allMarks = node.getChildren("LinkMark");
32
+ const urlNode = node.getChild("URL");
33
+ const openBracket = allMarks.find((m) => state.doc.sliceString(m.from, m.to) === "[");
34
+ const closeBracket = allMarks.find((m) => state.doc.sliceString(m.from, m.to) === "]");
35
+ if (urlNode && openBracket && closeBracket) {
36
+ const linkTextStart = openBracket.to;
37
+ const linkTextEnd = closeBracket.from;
38
+ const url = state.doc.sliceString(urlNode.from, urlNode.to);
39
+ const text = state.doc.sliceString(linkTextStart, linkTextEnd);
40
+ const linkAttributes = {
41
+ "href": url,
42
+ "target": "_blank",
43
+ "class": "cm-clickable-link",
44
+ "data-external-link": "true",
45
+ "data-url": url,
46
+ "data-text": text
47
+ };
48
+ decorations.push(Decoration.replace({}).range(node.from, linkTextStart));
49
+ decorations.push(Decoration.replace({}).range(linkTextEnd, node.to));
50
+ decorations.push(Decoration.mark({
51
+ tagName: "a",
52
+ attributes: linkAttributes
53
+ }).range(linkTextStart, linkTextEnd));
54
+ return false;
55
+ }
56
+ }
57
+ return false;
58
+ }
59
+ if (node.name === "URL") {
60
+ const url = state.doc.sliceString(node.from, node.to);
61
+ const isImage = /\.(png|jpg|jpeg|gif|svg|webp)$/i.test(url) || url.includes("picsum.photos");
62
+ if (isImage) {
63
+ const line = state.doc.lineAt(node.from);
64
+ widgets.push(Decoration.widget({
65
+ widget: new ProseVueComponentEmbedWidget(ImageEmbedComponent, { filePath: url }, node.from),
66
+ block: true,
67
+ side: 1
68
+ }).range(line.to));
69
+ }
70
+ if (!isNodeRangeActive(state, node.from, node.to)) {
71
+ decorations.push(Decoration.mark({
72
+ tagName: "a",
73
+ attributes: {
74
+ href: url,
75
+ target: "_blank",
76
+ class: "cm-clickable-link",
77
+ "data-external-link": "true",
78
+ "data-url": url
79
+ }
80
+ }).range(node.from, node.to));
81
+ }
82
+ }
83
+ }
84
+ });
85
+ return [...decorations, ...widgets];
86
+ }
87
+ export const proseLinkCodemirrorViewPlugin = StateField.define({
88
+ create(state) {
89
+ return RangeSet.of(buildLinkDecorations(state), true);
90
+ },
91
+ update(value, tr) {
92
+ if (tr.docChanged || tr.selection) {
93
+ return RangeSet.of(buildLinkDecorations(tr.state), true);
94
+ }
95
+ return value.map(tr.changes);
96
+ },
97
+ provide: (f) => EditorView.decorations.from(f)
98
+ });
@@ -0,0 +1,3 @@
1
+ import { StateField } from '@codemirror/state';
2
+ import type { DecorationSet } from '@codemirror/view';
3
+ export declare const proseQuoteblockCodemirrorViewPlugin: StateField<DecorationSet>;
@@ -0,0 +1,76 @@
1
+ import { Decoration, EditorView } from "@codemirror/view";
2
+ import { StateField, RangeSet } from "@codemirror/state";
3
+ import { syntaxTree } from "@codemirror/language";
4
+ import { cursorSelectionCoveredNode } from "../../utility/tools.js";
5
+ function isCursorOnLine(state, lineStart, lineEnd) {
6
+ const cursor = state.selection.main;
7
+ return cursor.from >= lineStart && cursor.from <= lineEnd;
8
+ }
9
+ function buildQuoteblockDecorations(state) {
10
+ const decorations = [];
11
+ syntaxTree(state).iterate({
12
+ enter(node) {
13
+ if (node.name === "Blockquote") {
14
+ const firstLine = state.doc.lineAt(node.from);
15
+ const lastLine = state.doc.lineAt(node.to);
16
+ const lineCount = lastLine.number - firstLine.number + 1;
17
+ const [cursor] = state.selection.ranges;
18
+ const cursorFrom = cursor?.from || 0, cursorTo = cursor?.to || 0, nodeFrom = node.from || 0, nodeTo = node.to || 0;
19
+ for (let i = firstLine.number; i <= lastLine.number; i++) {
20
+ const line = state.doc.line(i);
21
+ let lineClass = "cm-quoteblock";
22
+ if (lineCount === 1) {
23
+ lineClass += " cm-quoteblock-single";
24
+ } else if (i === firstLine.number) {
25
+ lineClass += " cm-quoteblock-start";
26
+ } else if (i === lastLine.number) {
27
+ lineClass += " cm-quoteblock-end";
28
+ } else {
29
+ lineClass += " cm-quoteblock-middle";
30
+ }
31
+ decorations.push(
32
+ Decoration.line({
33
+ attributes: { class: lineClass }
34
+ }).range(line.from)
35
+ );
36
+ const isCursorActive = isCursorOnLine(state, line.from, line.to) || cursorSelectionCoveredNode(cursorFrom, cursorTo, nodeFrom, nodeTo);
37
+ const lineText = line.text;
38
+ const markMatch = lineText.match(/^(\s*>+)/);
39
+ if (markMatch) {
40
+ const markEnd = line.from + markMatch[0].length;
41
+ let markClass = "cm-formatting cm-formatting-quote cm-meta";
42
+ if (isCursorActive) {
43
+ markClass += " cm-formatting-quote-active";
44
+ }
45
+ decorations.push(
46
+ Decoration.mark({
47
+ class: markClass
48
+ }).range(line.from, markEnd)
49
+ );
50
+ if (markEnd < line.to) {
51
+ decorations.push(
52
+ Decoration.mark({
53
+ class: "cm-quote"
54
+ }).range(markEnd, line.to)
55
+ );
56
+ }
57
+ }
58
+ }
59
+ return false;
60
+ }
61
+ }
62
+ });
63
+ return decorations;
64
+ }
65
+ export const proseQuoteblockCodemirrorViewPlugin = StateField.define({
66
+ create(state) {
67
+ return RangeSet.of(buildQuoteblockDecorations(state), true);
68
+ },
69
+ update(value, tr) {
70
+ if (tr.docChanged || tr.selection) {
71
+ return RangeSet.of(buildQuoteblockDecorations(tr.state), true);
72
+ }
73
+ return value.map(tr.changes);
74
+ },
75
+ provide: (f) => EditorView.decorations.from(f)
76
+ });
@@ -0,0 +1,8 @@
1
+ import { EditorView, ViewPlugin, ViewUpdate } from '@codemirror/view';
2
+ import { StateField } from '@codemirror/state';
3
+ export declare const editorLivePreviewField: StateField<boolean>;
4
+ export declare const proseTaskListPlugin: ViewPlugin<{
5
+ decorations: import("@codemirror/view").DecorationSet;
6
+ update(update: ViewUpdate): void;
7
+ buildDecorations(view: EditorView): import("@codemirror/view").DecorationSet;
8
+ }, undefined>;
@@ -0,0 +1,106 @@
1
+ import { Decoration, ViewPlugin, WidgetType } from "@codemirror/view";
2
+ import { StateField } from "@codemirror/state";
3
+ import { syntaxTree } from "@codemirror/language";
4
+ import { isCursorInRange } from "../../utility/tools.js";
5
+ export const editorLivePreviewField = StateField.define({
6
+ create: () => true,
7
+ update: (value) => value
8
+ });
9
+ class CheckboxWidget extends WidgetType {
10
+ constructor(checked, from, to) {
11
+ super();
12
+ this.checked = checked;
13
+ this.from = from;
14
+ this.to = to;
15
+ }
16
+ eq(other) {
17
+ return other.checked === this.checked && other.from === this.from && other.to === this.to;
18
+ }
19
+ toDOM(view) {
20
+ const checkbox = document.createElement("div");
21
+ checkbox.classList.add("cm-task-checkbox-wrapper");
22
+ if (this.checked && this.checked.toLowerCase() === "x") {
23
+ checkbox.dataset.checked = "true";
24
+ } else if (this.checked && this.checked !== " ") {
25
+ checkbox.dataset.special = this.checked;
26
+ checkbox.innerText = this.checked;
27
+ } else {
28
+ checkbox.dataset.checked = "false";
29
+ }
30
+ checkbox.onclick = (event) => {
31
+ event.preventDefault();
32
+ const isChecked = checkbox.dataset.checked === "true";
33
+ const char = checkbox.dataset.special ? " " : !isChecked ? "x" : " ";
34
+ view.dispatch({
35
+ changes: {
36
+ from: this.from + 1,
37
+ to: this.to - 1,
38
+ insert: char
39
+ }
40
+ });
41
+ };
42
+ return checkbox;
43
+ }
44
+ }
45
+ export const proseTaskListPlugin = ViewPlugin.fromClass(
46
+ class {
47
+ decorations = Decoration.none;
48
+ constructor(view) {
49
+ this.decorations = this.buildDecorations(view);
50
+ }
51
+ update(update) {
52
+ if (update.docChanged || update.viewportChanged || update.selectionSet) {
53
+ this.decorations = this.buildDecorations(update.view);
54
+ }
55
+ }
56
+ buildDecorations(view) {
57
+ const decorations = /* @__PURE__ */ new Set();
58
+ const { state } = view;
59
+ const isLivePreview = state.field(editorLivePreviewField);
60
+ for (const { from, to } of view.visibleRanges) {
61
+ syntaxTree(state).iterate({
62
+ from,
63
+ to,
64
+ enter: (node) => {
65
+ if (node.type.name !== "Task") return;
66
+ const { from: nodeFrom, to: nodeTo } = node;
67
+ const listItem = node.node.parent;
68
+ if (!listItem || listItem.type.name !== "ListItem") return;
69
+ const listMark = listItem.getChild("ListMark");
70
+ if (!listMark) return;
71
+ const taskMarker = node.node.getChild("TaskMarker");
72
+ if (!taskMarker) return;
73
+ const checkedChar = state.doc.sliceString(
74
+ taskMarker.from + 1,
75
+ taskMarker.to - 1
76
+ );
77
+ const isChecked = checkedChar && checkedChar.toLowerCase() === "x";
78
+ if (isChecked) {
79
+ const line = state.doc.lineAt(listItem.from);
80
+ decorations.add(
81
+ Decoration.line({
82
+ attributes: { class: "cm-task-checked" }
83
+ }).range(line.from)
84
+ );
85
+ }
86
+ if (isLivePreview && (isCursorInRange(state, [listMark.from, listMark.to]) || isCursorInRange(state, [taskMarker.from, taskMarker.to])))
87
+ return;
88
+ decorations.add(
89
+ Decoration.replace({
90
+ widget: new CheckboxWidget(
91
+ state.doc.sliceString(taskMarker.from + 1, taskMarker.to - 1),
92
+ taskMarker.from,
93
+ taskMarker.to
94
+ )
95
+ }).range(listMark.from, taskMarker.to)
96
+ );
97
+ }
98
+ });
99
+ }
100
+ return Decoration.set([...decorations]);
101
+ }
102
+ },
103
+ {
104
+ decorations: (value) => value.decorations
105
+ }
106
+ );
@@ -0,0 +1,13 @@
1
+ import { WidgetType } from '@codemirror/view';
2
+ import type { EditorView } from '@codemirror/view';
3
+ export declare class CalloutWidget extends WidgetType {
4
+ private sourceNodeFrom;
5
+ private sourceNodeTo;
6
+ private rawContent;
7
+ constructor(sourceNodeFrom: number, sourceNodeTo: number);
8
+ private findParentBlockquote;
9
+ private extractDetails;
10
+ toDOM(view: EditorView): HTMLElement;
11
+ eq(other: CalloutWidget): boolean;
12
+ ignoreEvent(event: Event): boolean;
13
+ }
@@ -0,0 +1,87 @@
1
+ import { WidgetType } from "@codemirror/view";
2
+ import { syntaxTree } from "@codemirror/language";
3
+ import MarkdownIt from "markdown-it";
4
+ import markdownItObsidianCallouts from "markdown-it-obsidian-callouts";
5
+ const md = new MarkdownIt({ html: true }).use(markdownItObsidianCallouts);
6
+ export class CalloutWidget extends WidgetType {
7
+ constructor(sourceNodeFrom, sourceNodeTo) {
8
+ super();
9
+ this.sourceNodeFrom = sourceNodeFrom;
10
+ this.sourceNodeTo = sourceNodeTo;
11
+ }
12
+ rawContent = "";
13
+ findParentBlockquote(node) {
14
+ let current = node;
15
+ while (current) {
16
+ if (current.name === "Blockquote") {
17
+ return current;
18
+ }
19
+ current = current.parent;
20
+ }
21
+ return null;
22
+ }
23
+ extractDetails(view) {
24
+ const state = view.state;
25
+ const calloutNode = syntaxTree(state).resolve(this.sourceNodeFrom, 1);
26
+ const parentBlockquote = this.findParentBlockquote(calloutNode);
27
+ if (parentBlockquote) {
28
+ this.rawContent = view.state.doc.sliceString(parentBlockquote.from, parentBlockquote.to);
29
+ }
30
+ }
31
+ toDOM(view) {
32
+ this.extractDetails(view);
33
+ const renderedHtml = md.render(this.rawContent);
34
+ const tempDiv = document.createElement("div");
35
+ tempDiv.innerHTML = renderedHtml;
36
+ const calloutEl = tempDiv.querySelector(".callout");
37
+ if (!calloutEl) {
38
+ const fallback = document.createElement("div");
39
+ fallback.className = `cm-callout-widget callout callout-error`;
40
+ fallback.textContent = "Error rendering callout";
41
+ return fallback;
42
+ }
43
+ calloutEl.classList.add("cm-callout-widget");
44
+ calloutEl.setAttribute("contenteditable", "false");
45
+ const editButton = document.createElement("div");
46
+ editButton.className = "edit-block-button";
47
+ editButton.setAttribute("aria-label", "Edit this block");
48
+ editButton.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m18 16 4-4-4-4"></path><path d="m6 8-4 4 4 4"></path><path d="m14.5 4-5 16"></path></svg>`;
49
+ editButton.onmousedown = (e) => {
50
+ e.preventDefault();
51
+ e.stopPropagation();
52
+ };
53
+ editButton.onclick = (e) => {
54
+ e.stopPropagation();
55
+ const parentBlockquote = this.findParentBlockquote(syntaxTree(view.state).resolve(this.sourceNodeFrom, 1));
56
+ if (parentBlockquote) {
57
+ view.dispatch({ selection: { anchor: parentBlockquote.from } });
58
+ view.focus();
59
+ }
60
+ };
61
+ const titleDiv = calloutEl.querySelector(".callout-title");
62
+ if (titleDiv) {
63
+ const foldDiv = calloutEl.querySelector(".callout-fold");
64
+ if (foldDiv) {
65
+ const foldIcon = document.createElement("svg");
66
+ foldIcon.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-left-icon lucide-chevron-left"><path d="m15 18-6-6 6-6"/></svg>`;
67
+ foldDiv.appendChild(foldIcon);
68
+ }
69
+ titleDiv.appendChild(editButton);
70
+ } else {
71
+ calloutEl.prepend(editButton);
72
+ }
73
+ return calloutEl;
74
+ }
75
+ eq(other) {
76
+ return other.sourceNodeFrom === this.sourceNodeFrom && other.sourceNodeTo === this.sourceNodeTo && other.rawContent === this.rawContent;
77
+ }
78
+ ignoreEvent(event) {
79
+ if (event.type === "click" || event.type === "mousedown") {
80
+ const target = event.target;
81
+ if (target.closest(".edit-block-button") || target.closest(".callout-fold")) {
82
+ return true;
83
+ }
84
+ }
85
+ return false;
86
+ }
87
+ }
@@ -0,0 +1,15 @@
1
+ import { WidgetType } from '@codemirror/view';
2
+ import type { EditorView } from '@codemirror/view';
3
+ export declare class LanguageFlairWidget extends WidgetType {
4
+ private lang;
5
+ private codeContent;
6
+ private button;
7
+ constructor(lang: string, codeContent: string);
8
+ toDOM(view: EditorView): HTMLElement;
9
+ eq(other: LanguageFlairWidget): boolean;
10
+ ignoreEvent(event: Event): boolean;
11
+ }
12
+ export declare class EndFenceWidget extends WidgetType {
13
+ toDOM(view: EditorView): HTMLElement;
14
+ ignoreEvent(): boolean;
15
+ }
@@ -0,0 +1,53 @@
1
+ import { WidgetType } from "@codemirror/view";
2
+ export class LanguageFlairWidget extends WidgetType {
3
+ constructor(lang, codeContent) {
4
+ super();
5
+ this.lang = lang;
6
+ this.codeContent = codeContent;
7
+ }
8
+ button = null;
9
+ toDOM(view) {
10
+ const widgetRoot = document.createElement("div");
11
+ widgetRoot.className = "cm-codeblock-flair-container";
12
+ widgetRoot.setAttribute("contenteditable", "false");
13
+ this.button = document.createElement("button");
14
+ this.button.className = "cm-codeblock-copy-button";
15
+ this.button.textContent = this.lang || "";
16
+ this.button.onclick = (event) => {
17
+ event.stopPropagation();
18
+ navigator.clipboard.writeText(this.codeContent).then(() => {
19
+ if (this.button) {
20
+ this.button.disabled = true;
21
+ setTimeout(() => {
22
+ if (this.button) {
23
+ this.button.disabled = false;
24
+ }
25
+ }, 1500);
26
+ }
27
+ }).catch((err) => {
28
+ console.error("Failed to copy code to clipboard:", err);
29
+ });
30
+ };
31
+ widgetRoot.appendChild(this.button);
32
+ return widgetRoot;
33
+ }
34
+ eq(other) {
35
+ return other.lang === this.lang && other.codeContent === this.codeContent;
36
+ }
37
+ ignoreEvent(event) {
38
+ if ((event.type === "mousedown" || event.type === "click") && this.button && this.button.contains(event.target)) {
39
+ return true;
40
+ }
41
+ return false;
42
+ }
43
+ }
44
+ export class EndFenceWidget extends WidgetType {
45
+ toDOM(view) {
46
+ const div = document.createElement("div");
47
+ div.className = "hidden";
48
+ return div;
49
+ }
50
+ ignoreEvent() {
51
+ return true;
52
+ }
53
+ }
@@ -0,0 +1,11 @@
1
+ import { type EditorView, WidgetType } from "@codemirror/view";
2
+ export declare class InlineLatexWidget extends WidgetType {
3
+ private readonly content;
4
+ constructor(content: string);
5
+ toDOM(view: EditorView): HTMLElement;
6
+ }
7
+ export declare class BlockLatexWidget extends WidgetType {
8
+ private readonly content;
9
+ constructor(content: string);
10
+ toDOM(view: EditorView): HTMLElement;
11
+ }