@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.
- package/README.md +99 -0
- package/dist/module.d.mts +8 -0
- package/dist/module.json +9 -0
- package/dist/module.mjs +25 -0
- package/dist/runtime/assets/css/editor.css +1 -0
- package/dist/runtime/components/Editor/ImageEmbedComponent.vue +11 -0
- package/dist/runtime/components/Editor/ImageEmbedComponent.vue.d.ts +5 -0
- package/dist/runtime/components/Editor/TestCustomCodeBlock.vue +16 -0
- package/dist/runtime/components/Editor/TestCustomCodeBlock.vue.d.ts +5 -0
- package/dist/runtime/components/Editor.client.vue +182 -0
- package/dist/runtime/components/Editor.client.vue.d.ts +23 -0
- package/dist/runtime/editor/lezer-parsers/customOFMParsers.d.ts +1 -0
- package/dist/runtime/editor/lezer-parsers/customOFMParsers.js +23 -0
- package/dist/runtime/editor/lezer-parsers/lezerCalloutParser.d.ts +8 -0
- package/dist/runtime/editor/lezer-parsers/lezerCalloutParser.js +53 -0
- package/dist/runtime/editor/lezer-parsers/lezerHashtagParser.d.ts +6 -0
- package/dist/runtime/editor/lezer-parsers/lezerHashtagParser.js +35 -0
- package/dist/runtime/editor/lezer-parsers/lezerIndentationParser.d.ts +4 -0
- package/dist/runtime/editor/lezer-parsers/lezerIndentationParser.js +23 -0
- package/dist/runtime/editor/lezer-parsers/lezerInternalLinkParser.d.ts +10 -0
- package/dist/runtime/editor/lezer-parsers/lezerInternalLinkParser.js +110 -0
- package/dist/runtime/editor/lezer-parsers/lezerLatexParser.d.ts +7 -0
- package/dist/runtime/editor/lezer-parsers/lezerLatexParser.js +75 -0
- package/dist/runtime/editor/lezer-parsers/lezerYamlFrontmatterParser.d.ts +13 -0
- package/dist/runtime/editor/lezer-parsers/lezerYamlFrontmatterParser.js +55 -0
- package/dist/runtime/editor/plugins/codemirror-editor-plugins/customBracketClosingPlugin.d.ts +4 -0
- package/dist/runtime/editor/plugins/codemirror-editor-plugins/customBracketClosingPlugin.js +94 -0
- package/dist/runtime/editor/plugins/codemirror-editor-plugins/editorAttributesPlugin.d.ts +8 -0
- package/dist/runtime/editor/plugins/codemirror-editor-plugins/editorAttributesPlugin.js +136 -0
- package/dist/runtime/editor/plugins/codemirror-editor-plugins/editorInternalLinkAutocompletePlugin.d.ts +1 -0
- package/dist/runtime/editor/plugins/codemirror-editor-plugins/editorInternalLinkAutocompletePlugin.js +43 -0
- package/dist/runtime/editor/plugins/codemirror-editor-plugins/editorKeymapPlugin.d.ts +1 -0
- package/dist/runtime/editor/plugins/codemirror-editor-plugins/editorKeymapPlugin.js +95 -0
- package/dist/runtime/editor/plugins/codemirror-editor-plugins/editorLinkClickPlugin.d.ts +1 -0
- package/dist/runtime/editor/plugins/codemirror-editor-plugins/editorLinkClickPlugin.js +43 -0
- package/dist/runtime/editor/plugins/codemirror-editor-plugins/indentationGuidesPlugin.d.ts +10 -0
- package/dist/runtime/editor/plugins/codemirror-editor-plugins/indentationGuidesPlugin.js +121 -0
- package/dist/runtime/editor/plugins/codemirror-editor-plugins/indentationListPlugin.d.ts +5 -0
- package/dist/runtime/editor/plugins/codemirror-editor-plugins/indentationListPlugin.js +66 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseCalloutPlugin.d.ts +3 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseCalloutPlugin.js +63 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseCodeBlockCodemirrorViewPlugin.d.ts +3 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseCodeBlockCodemirrorViewPlugin.js +98 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseHashtagCodemirrorViewPlugin.d.ts +3 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseHashtagCodemirrorViewPlugin.js +27 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseHighlightCodemirrorViewPlugin.d.ts +3 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseHighlightCodemirrorViewPlugin.js +51 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseInternalLinkCodemirrorViewPlugin.d.ts +6 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseInternalLinkCodemirrorViewPlugin.js +112 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseLatexCodemirrorViewPlugin.d.ts +2 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseLatexCodemirrorViewPlugin.js +160 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseLinkCodemirrorViewPlugin.d.ts +3 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseLinkCodemirrorViewPlugin.js +98 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseQuoteblockCodemirrorViewPlugin.d.ts +3 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseQuoteblockCodemirrorViewPlugin.js +76 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseTaskListPlugin.d.ts +8 -0
- package/dist/runtime/editor/plugins/codemirror-plugin-proses/proseTaskListPlugin.js +106 -0
- package/dist/runtime/editor/plugins/codemirror-widgets/proseCalloutWidget.d.ts +13 -0
- package/dist/runtime/editor/plugins/codemirror-widgets/proseCalloutWidget.js +87 -0
- package/dist/runtime/editor/plugins/codemirror-widgets/proseCodeBlockWidgets.d.ts +15 -0
- package/dist/runtime/editor/plugins/codemirror-widgets/proseCodeBlockWidgets.js +53 -0
- package/dist/runtime/editor/plugins/codemirror-widgets/proseLatexWidgets.d.ts +11 -0
- package/dist/runtime/editor/plugins/codemirror-widgets/proseLatexWidgets.js +50 -0
- package/dist/runtime/editor/plugins/codemirror-widgets/proseVueComponentEmbedWidget.d.ts +12 -0
- package/dist/runtime/editor/plugins/codemirror-widgets/proseVueComponentEmbedWidget.js +32 -0
- package/dist/runtime/editor/plugins/customBracketClosingConfig.d.ts +2 -0
- package/dist/runtime/editor/plugins/customBracketClosingConfig.js +4 -0
- package/dist/runtime/editor/plugins/lezerStylesHighlightingPlugin.d.ts +3 -0
- package/dist/runtime/editor/plugins/lezerStylesHighlightingPlugin.js +55 -0
- package/dist/runtime/editor/plugins/linkMappingConfig.d.ts +3 -0
- package/dist/runtime/editor/plugins/linkMappingConfig.js +4 -0
- package/dist/runtime/editor/plugins/richTextPlugin.d.ts +10 -0
- package/dist/runtime/editor/plugins/richTextPlugin.js +94 -0
- package/dist/runtime/editor/plugins/specialCodeBlockMappingConfig.d.ts +3 -0
- package/dist/runtime/editor/plugins/specialCodeBlockMappingConfig.js +4 -0
- package/dist/runtime/editor/types/editor-types.d.ts +17 -0
- package/dist/runtime/editor/utility/decorations.d.ts +9 -0
- package/dist/runtime/editor/utility/decorations.js +9 -0
- package/dist/runtime/editor/utility/tools.d.ts +12 -0
- package/dist/runtime/editor/utility/tools.js +32 -0
- package/dist/runtime/editor/wysiwyg.d.ts +4 -0
- package/dist/runtime/editor/wysiwyg.js +80 -0
- package/dist/types.d.mts +3 -0
- package/package.json +80 -0
package/README.md
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# CodeMirror 6 WYSIWYG OFM Editor
|
|
2
|
+
|
|
3
|
+
## Credits and References, first of all
|
|
4
|
+
### Primary Credits
|
|
5
|
+
- https://github.com/segphault/codemirror-rich-markdoc, **_for the foundation of this entire project._**
|
|
6
|
+
- https://github.com/erykwalder/lezer-markdown-obsidian, for OFM Lezer Parsers.
|
|
7
|
+
- https://github.com/surmon-china/vue-codemirror, for the CodeMirror 6 component in Vue.
|
|
8
|
+
- https://github.com/ebullient/markdown-it-obsidian-callouts, for his awesome markdown-to-html callouts markdown-it plugin
|
|
9
|
+
- https://github.com/mgmeyers/obsidian-indentation-guides, for indentation guides
|
|
10
|
+
- Markdown-It
|
|
11
|
+
|
|
12
|
+
### Related References & Resources
|
|
13
|
+
- https://github.com/heavycircle/remark-obsidian, for mostly wiki link alias & highlights & callouts parsing
|
|
14
|
+
- https://github.com/flowershow/remark-wiki-link, for wiki link parsing
|
|
15
|
+
- https://github.com/CTRL-Neo-Studios/simple-markdown-editor, my initial trial that quickly degraded into a shitslop because of overuse of AI
|
|
16
|
+
- https://github.com/nothingislost/obsidian-codemirror-options
|
|
17
|
+
- https://github.com/nothingislost/obsidian-cm6-attributes
|
|
18
|
+
|
|
19
|
+
## Disclaimer
|
|
20
|
+
I have used Gemini 2.5 Pro in the process of developing this editor numerous times, so do expect errors or inconsistencies in some parts of the code.
|
|
21
|
+
|
|
22
|
+
## Introduction
|
|
23
|
+
Do I even need an intro?
|
|
24
|
+
|
|
25
|
+
This is a CodeMirror 6 WYSIWYG Obsidian-Flavored Markdown Editor project that aims to recreate Obsidian's implementation of a WYSIWYG Markdown editor in CodeMirror 6.
|
|
26
|
+
|
|
27
|
+
That being said, please do note that:
|
|
28
|
+
- This is more of a synthesized project from multiple different projects, as you can see from the credits section
|
|
29
|
+
- The CodeMirror implementation is not a one-on-one replica. Since we don't have access to Obsidian's AST, we don't know how they parse markdown into node-marks and decorating them with CodeMirror. We could only try to imitate how they parse and decorate their markdowns judging from the class styling in their raw HTML.
|
|
30
|
+
- When using this editor, you may feel that some small user experiences does not mach the UX of Obsidian's markdown editor. Yes, this is a known issue, and we're trying to "fix" them.
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
Run with your preferred package manager:
|
|
35
|
+
```shell
|
|
36
|
+
bun add @type32/codemirror-rich-obsidian-editor
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Add modules in Nuxt Config:
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
export default defineNuxtConfig({
|
|
43
|
+
// Your config...
|
|
44
|
+
modules: [
|
|
45
|
+
// Your other modules...
|
|
46
|
+
'@type32/codemirror-rich-obsidian-editor',
|
|
47
|
+
],
|
|
48
|
+
})
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Customize the editor fonts:
|
|
52
|
+
|
|
53
|
+
```css
|
|
54
|
+
@import 'tailwindcss';
|
|
55
|
+
@import '@nuxt/ui';
|
|
56
|
+
|
|
57
|
+
@theme static {
|
|
58
|
+
/* Your Config Here... */
|
|
59
|
+
|
|
60
|
+
--font-sans: /* Your Config Here... */;
|
|
61
|
+
|
|
62
|
+
--font-editor: 'SF Pro Display', 'Segoe UI Variable Static Display', var(--font-sans, sans-serif);
|
|
63
|
+
|
|
64
|
+
--font-editor-code: 'Google Sans Code', 'JetBrains Mono', 'Consolas', var(--font-mono, ui-monospace);
|
|
65
|
+
|
|
66
|
+
--list-indent: 1.5rem;
|
|
67
|
+
|
|
68
|
+
--indent-level: 0;
|
|
69
|
+
|
|
70
|
+
/* Your Config Here... */
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Known Issues
|
|
75
|
+
- Same as `segphault/codemirror-rich-markdoc`, the rendered block replacement code is not yet optimized, so it recomputes all of the replaced regions on every operation instead of only updating them as needed.
|
|
76
|
+
- Progress is being made on this issue: we've optimized the Rich Text Plugin to update based on only the updated ranges instead of the entire document.
|
|
77
|
+
- In Obsidian, the hidden marks of nodes are revealed at `mouseup`, whereas in this implementation, they're revealed at `mousedown`.
|
|
78
|
+
- The editor errs when trying to parse nested callouts, to the extent where you might loose your data.
|
|
79
|
+
- Regular Callouts works fine.
|
|
80
|
+
- Ordered List sequencing is different than that of Obsidian. We think that they probably use a sort of counter to keep track of lists of the same level beneath the hood, but we don't know for sure.
|
|
81
|
+
- It might be how CodeMirror keeps track of tabs in lists. The issue is not being worked on right now.
|
|
82
|
+
- ~~Indents are currently tabs. In Obsidian, they seem to be parsed as nodes judging from their raw HTML. We suspect that this node may be accounted for some of the weird fuckery with leveled list, but we don't know for sure.~~
|
|
83
|
+
- We've implemented `mgmeyers/obsidian-indentation-guides` for indentation styling. Indents doesn't seem to be nodes.
|
|
84
|
+
- ~~Task lists doesn't work for now.~~
|
|
85
|
+
- Task lists are working, but customization is sparse.
|
|
86
|
+
- List indents are a pain.
|
|
87
|
+
- ~~(Not much of an issue but still kept track of) YAML Frontmatter is parsed as raw text instead of TOML. We're currently determining whether to leave this as it is or try to add our own implementation to imitate how Obsidian parses and modifies their markdown files' Frontmatter.~~
|
|
88
|
+
- **We've decided to leave it alone for people who want to implement their own YAML Frontmatter parsing logic.**
|
|
89
|
+
- ~~Support for embedded videos, notes, bases, and canvases are currently lacking~~
|
|
90
|
+
- We have a mapping prop that allows developers to add their own link-to-file implementations. (Specific to Vue/Nuxt)
|
|
91
|
+
- ~~Support for code-block mermaid graph rendering & bases is lacking.~~
|
|
92
|
+
- We have a mapping prop that allows developers to add their own custom codeblock widgets. (Specific to Vue/Nuxt)
|
|
93
|
+
- Light/Dark themes are not yet supported in code-block syntax highlighting.
|
|
94
|
+
|
|
95
|
+
## Contributions
|
|
96
|
+
- To anyone who wants to fork this, **make sure you preserve the original credits and references to the libraries that are used in this project. It means a lot to them and to us.**
|
|
97
|
+
- To anyone who wants to fork this project into another framework - such as React, Angular, Svelte, or PHP - **best of luck. We don't have react/solidjs/Angular/Svelte/PHP developers on the team so we can't help with that. This project is developed is mostly just Vue/Nuxt in mind.**
|
|
98
|
+
|
|
99
|
+
PS: You may notice some inconsistencies in the use of pronouns in the README.md: it's not written using AI. It's just my weird writing style.
|
package/dist/module.json
ADDED
package/dist/module.mjs
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { defineNuxtModule, createResolver, addComponentsDir } from '@nuxt/kit';
|
|
2
|
+
|
|
3
|
+
const module = defineNuxtModule({
|
|
4
|
+
meta: {
|
|
5
|
+
name: "@type32/codemirror-rich-obsidian-editor",
|
|
6
|
+
configKey: "cmOfmEditor"
|
|
7
|
+
},
|
|
8
|
+
// Default configuration options of the Nuxt module
|
|
9
|
+
defaults: {},
|
|
10
|
+
setup(_options, _nuxt) {
|
|
11
|
+
const resolver = createResolver(import.meta.url);
|
|
12
|
+
_nuxt.options.alias["#codemirror-rich-obsidian-editor"] = resolver.resolve(
|
|
13
|
+
"./runtime/editor/types"
|
|
14
|
+
);
|
|
15
|
+
_nuxt.options.alias["#codemirror-rich-obsidian-editor#css"] = resolver.resolve(
|
|
16
|
+
"./runtime/assets/css"
|
|
17
|
+
);
|
|
18
|
+
_nuxt.options.css.unshift(resolver.resolve("./runtime/assets/css/editor.css"));
|
|
19
|
+
addComponentsDir({
|
|
20
|
+
path: resolver.resolve("./runtime/components")
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export { module as default };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import url("https://fonts.cdnfonts.com/css/segoe-ui-variable-static-display");@import url("https://fonts.cdnfonts.com/css/sf-pro-display");@import "katex/dist/katex.min.css";@import "tailwindcss";@import "@nuxt/ui";@theme{--font-sans:"Public Sans","Inter","SF Pro Display","Segoe UI Variable Static Display",sans-serif;--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--font-editor:"Inter","SF Pro Display","Segoe UI Variable Static Display",var(--font-sans,sans-serif);--font-editor-code:"Google Sans Code","JetBrains Mono","Consolas",var(--font-mono,ui-monospace);--list-indent:1.5rem;--indent-level:0}.cm-content{@apply font-editor}.cm-content .cm-meta{@apply text-dimmed! no-underline!}.cm-content .cm-heading{text-decoration:none!important}.cm-content .cm-mono{@apply font-editor-code bg-muted px-1 rounded-lg border border-default text-toned!}.cm-content .cm-hashtag{@apply bg-primary/20! ring ring-primary/70! px-1.5 text-sm rounded-4xl text-primary! inline-flex}.cm-content .cm-hashtag .cm-heading{@apply text-primary!}.cm-content .cm-highlighted{@apply bg-primary/20 text-primary px-0.5 rounded-sm}.cm-content .cm-internal-link-display,.cm-content .cm-internal-link-path,.cm-content .cm-internal-link-subpath,.cm-content .cm-link{@apply text-primary underline}.cm-content .cm-internal-link-display[data-type=internal-link],.cm-content .cm-internal-link-path[data-type=internal-link],.cm-content .cm-internal-link-subpath[data-type=internal-link],.cm-content .cm-link[data-type=internal-link]{@apply cursor-pointer}.cm-content .cm-clickable-link{@apply cursor-pointer}.cm-content .cm-unresolved-link .cm-internal-link-display,.cm-content .cm-unresolved-link .cm-internal-link-path,.cm-content .cm-unresolved-link .cm-internal-link-subpath,.cm-content .cm-unresolved-link .cm-link{@apply text-error/75! cursor-not-allowed}.cm-content .cm-codeblock,.cm-content .cm-line-codeblock-begin,.cm-content .cm-line-codeblock-end{@apply font-editor-code py-0 my-0 text-sm! font-semibold border-x border-x-default mx-2 px-3 bg-muted relative}.cm-content .cm-codeblock .cm-codeblock-flair-container,.cm-content .cm-line-codeblock-begin .cm-codeblock-flair-container,.cm-content .cm-line-codeblock-end .cm-codeblock-flair-container{@apply absolute top-2 right-2 overflow-visible z-10}.cm-content .cm-codeblock .cm-codeblock-flair-container .cm-codeblock-copy-button,.cm-content .cm-line-codeblock-begin .cm-codeblock-flair-container .cm-codeblock-copy-button,.cm-content .cm-line-codeblock-end .cm-codeblock-flair-container .cm-codeblock-copy-button{@apply rounded hover:bg-accented transition duration-200 cursor-pointer py-1.5 px-2 font-sans text-dimmed z-10}.cm-content .cm-codeblock .cm-mono,.cm-content .cm-line-codeblock-begin .cm-mono,.cm-content .cm-line-codeblock-end .cm-mono{@apply border-none bg-none text-default}.cm-content .cm-line-codeblock-content{@apply border-x border-x-accented}.cm-content .cm-line-codeblock-begin{@apply rounded-t-lg border-t border-x border-t-accented border-x-accented pt-1 px-1}.cm-content .cm-line-codeblock-end{@apply rounded-b-lg border-b border-x border-b-accented border-x-accented pb-1 px-1}.cm-content .cm-task-checkbox-wrapper{@apply size-4 mb-0.5 rounded-xl border border-default bg-default inline-flex items-center justify-center align-middle cursor-pointer}.cm-content .cm-task-checkbox-wrapper[data-checked=true]{@apply bg-primary border border-primary}.cm-content .cm-task-checkbox-wrapper[data-checked=true]:before{content:"✔";@apply text-xs text-inverted}.cm-content .cm-task-checkbox-wrapper[data-special]{@apply font-editor-code text-xs text-highlighted}.cm-content .cm-task-checked{@apply line-through text-dimmed}.cm-content .cm-tooltip{@apply rounded-lg bg-default/70 shadow-lg backdrop-blur-sm border border-default font-sans p-2}.cm-content .cm-tooltip ul{@apply grid grid-cols-1 font-sans}.cm-content .cm-tooltip ul li{@apply grid grid-cols-1 rounded font-sans gap-1}.cm-content .cm-tooltip ul li .cm-completionLabel{@apply mx-1 mt-1 text-toned}.cm-content .cm-tooltip ul li .cm-completionDetail{@apply text-xs font-editor-code bg-elevated w-fit rounded text-muted px-1 mb-1}.cm-content .cm-tooltip ul li[aria-selected=true]{@apply bg-accented/70 backdrop-blur-sm border border-accented}.cm-content .cm-fold-widget{@apply absolute -left-0.5 pt-1}.cm-content .cm-gutter,.cm-content .cm-gutters{@apply bg-transparent! border-none}.cm-content .cm-foldPlaceholder{@apply bg-accented! text-dimmed! border border-none ml-0.5 h-fit inline-flex items-center justify-center align-middle}.cm-content .cm-gutterElement{@apply inline-flex justify-center items-center}.cm-content .cm-heading-1{@apply text-4xl! text-highlighted scroll-mt-[calc(45px+var(--ui-header-height))]}.cm-content .cm-heading-2{@apply text-2xl! text-highlighted scroll-mt-[calc(45px+var(--ui-header-height))]}.cm-content .cm-heading-3{@apply text-xl! text-highlighted scroll-mt-[calc(45px+var(--ui-header-height))]}.cm-content .cm-heading-4{@apply text-lg! text-highlighted scroll-mt-[calc(45px+var(--ui-header-height))]}.cm-content .cm-quoteblock,.cm-content blockquote{@apply border-l-6 bg-muted border-accented ps-2 mx-2}.cm-content .cm-quoteblock.cm-quoteblock-single,.cm-content blockquote.cm-quoteblock-single{@apply rounded py-2}.cm-content .cm-quoteblock.cm-quoteblock-start,.cm-content blockquote.cm-quoteblock-start{@apply rounded-t pt-2}.cm-content .cm-quoteblock.cm-quoteblock-end,.cm-content blockquote.cm-quoteblock-end{@apply rounded-b pb-2}.cm-content .cm-quoteblock .cm-formatting-quote.cm-meta,.cm-content blockquote .cm-formatting-quote.cm-meta{@apply invisible}.cm-content .cm-quoteblock .cm-formatting-quote-active.cm-meta,.cm-content blockquote .cm-formatting-quote-active.cm-meta{@apply visible!}.cm-content .cm-obsidian-bullet{@apply text-transparent}.cm-content .cm-obsidian-bullet .cm-meta{@apply invisible}.cm-content .cm-obsidian-bullet:before{content:"•";@apply text-dimmed absolute}.cm-content .callout,.cm-content .cm-callout-widget{@apply p-3 relative flex flex-col h-fit mx-2 my-2 rounded-lg border border-primary/20 bg-primary/20}.cm-content .callout summary,.cm-content .cm-callout-widget summary{@apply inline-flex}.cm-content .callout .callout-title,.cm-content .cm-callout-widget .callout-title{@apply flex gap-1.5 items-center}.cm-content .callout .callout-title .callout-title-icon,.cm-content .cm-callout-widget .callout-title .callout-title-icon{@apply flex size-4 items-center justify-center fill-primary text-primary}.cm-content .callout .callout-title .callout-title-inner,.cm-content .cm-callout-widget .callout-title .callout-title-inner{@apply text-primary text-lg font-bold flex-grow}.cm-content .callout .callout-title .edit-block-button,.cm-content .cm-callout-widget .callout-title .edit-block-button{@apply rounded hover:bg-elevated transition duration-200 cursor-pointer p-1 font-sans right-0 top-0 text-dimmed}.cm-content .callout .callout-content,.cm-content .cm-callout-widget .callout-content{@apply flex flex-col gap-1}.cm-content .callout[data-callout=info],.cm-content .cm-callout-widget[data-callout=info]{@apply bg-info/20 border-info/20}.cm-content .callout[data-callout=info] .callout-fold,.cm-content .cm-callout-widget[data-callout=info] .callout-fold{@apply text-info-400}.cm-content .callout[data-callout=info] .callout-title .callout-title-icon,.cm-content .callout[data-callout=info] .callout-title .callout-title-inner,.cm-content .cm-callout-widget[data-callout=info] .callout-title .callout-title-icon,.cm-content .cm-callout-widget[data-callout=info] .callout-title .callout-title-inner{@apply text-info}.cm-content .callout[data-callout=check],.cm-content .callout[data-callout=done],.cm-content .callout[data-callout=success],.cm-content .cm-callout-widget[data-callout=check],.cm-content .cm-callout-widget[data-callout=done],.cm-content .cm-callout-widget[data-callout=success]{@apply bg-success/20 border-success/20}.cm-content .callout[data-callout=check] .callout-fold,.cm-content .callout[data-callout=done] .callout-fold,.cm-content .callout[data-callout=success] .callout-fold,.cm-content .cm-callout-widget[data-callout=check] .callout-fold,.cm-content .cm-callout-widget[data-callout=done] .callout-fold,.cm-content .cm-callout-widget[data-callout=success] .callout-fold{@apply text-success-400}.cm-content .callout[data-callout=check] .callout-title .callout-title-icon,.cm-content .callout[data-callout=check] .callout-title .callout-title-inner,.cm-content .callout[data-callout=done] .callout-title .callout-title-icon,.cm-content .callout[data-callout=done] .callout-title .callout-title-inner,.cm-content .callout[data-callout=success] .callout-title .callout-title-icon,.cm-content .callout[data-callout=success] .callout-title .callout-title-inner,.cm-content .cm-callout-widget[data-callout=check] .callout-title .callout-title-icon,.cm-content .cm-callout-widget[data-callout=check] .callout-title .callout-title-inner,.cm-content .cm-callout-widget[data-callout=done] .callout-title .callout-title-icon,.cm-content .cm-callout-widget[data-callout=done] .callout-title .callout-title-inner,.cm-content .cm-callout-widget[data-callout=success] .callout-title .callout-title-icon,.cm-content .cm-callout-widget[data-callout=success] .callout-title .callout-title-inner{@apply text-success}.cm-content .callout[data-callout=faq],.cm-content .callout[data-callout=help],.cm-content .callout[data-callout=question],.cm-content .callout[data-callout=warning],.cm-content .cm-callout-widget[data-callout=faq],.cm-content .cm-callout-widget[data-callout=help],.cm-content .cm-callout-widget[data-callout=question],.cm-content .cm-callout-widget[data-callout=warning]{@apply bg-warning/20 border-warning/20}.cm-content .callout[data-callout=faq] .callout-fold,.cm-content .callout[data-callout=help] .callout-fold,.cm-content .callout[data-callout=question] .callout-fold,.cm-content .callout[data-callout=warning] .callout-fold,.cm-content .cm-callout-widget[data-callout=faq] .callout-fold,.cm-content .cm-callout-widget[data-callout=help] .callout-fold,.cm-content .cm-callout-widget[data-callout=question] .callout-fold,.cm-content .cm-callout-widget[data-callout=warning] .callout-fold{@apply text-warning-400}.cm-content .callout[data-callout=faq] .callout-title .callout-title-icon,.cm-content .callout[data-callout=faq] .callout-title .callout-title-inner,.cm-content .callout[data-callout=help] .callout-title .callout-title-icon,.cm-content .callout[data-callout=help] .callout-title .callout-title-inner,.cm-content .callout[data-callout=question] .callout-title .callout-title-icon,.cm-content .callout[data-callout=question] .callout-title .callout-title-inner,.cm-content .callout[data-callout=warning] .callout-title .callout-title-icon,.cm-content .callout[data-callout=warning] .callout-title .callout-title-inner,.cm-content .cm-callout-widget[data-callout=faq] .callout-title .callout-title-icon,.cm-content .cm-callout-widget[data-callout=faq] .callout-title .callout-title-inner,.cm-content .cm-callout-widget[data-callout=help] .callout-title .callout-title-icon,.cm-content .cm-callout-widget[data-callout=help] .callout-title .callout-title-inner,.cm-content .cm-callout-widget[data-callout=question] .callout-title .callout-title-icon,.cm-content .cm-callout-widget[data-callout=question] .callout-title .callout-title-inner,.cm-content .cm-callout-widget[data-callout=warning] .callout-title .callout-title-icon,.cm-content .cm-callout-widget[data-callout=warning] .callout-title .callout-title-inner{@apply text-warning}.cm-content .callout[data-callout=bug],.cm-content .callout[data-callout=error],.cm-content .callout[data-callout=failure],.cm-content .cm-callout-widget[data-callout=bug],.cm-content .cm-callout-widget[data-callout=error],.cm-content .cm-callout-widget[data-callout=failure]{@apply bg-error/20 border-error/20}.cm-content .callout[data-callout=bug] .callout-fold,.cm-content .callout[data-callout=error] .callout-fold,.cm-content .callout[data-callout=failure] .callout-fold,.cm-content .cm-callout-widget[data-callout=bug] .callout-fold,.cm-content .cm-callout-widget[data-callout=error] .callout-fold,.cm-content .cm-callout-widget[data-callout=failure] .callout-fold{@apply text-error-400}.cm-content .callout[data-callout=bug] .callout-title .callout-title-icon,.cm-content .callout[data-callout=bug] .callout-title .callout-title-inner,.cm-content .callout[data-callout=error] .callout-title .callout-title-icon,.cm-content .callout[data-callout=error] .callout-title .callout-title-inner,.cm-content .callout[data-callout=failure] .callout-title .callout-title-icon,.cm-content .callout[data-callout=failure] .callout-title .callout-title-inner,.cm-content .cm-callout-widget[data-callout=bug] .callout-title .callout-title-icon,.cm-content .cm-callout-widget[data-callout=bug] .callout-title .callout-title-inner,.cm-content .cm-callout-widget[data-callout=error] .callout-title .callout-title-icon,.cm-content .cm-callout-widget[data-callout=error] .callout-title .callout-title-inner,.cm-content .cm-callout-widget[data-callout=failure] .callout-title .callout-title-icon,.cm-content .cm-callout-widget[data-callout=failure] .callout-title .callout-title-inner{@apply text-error}.cm-content .callout[data-callout=example],.cm-content .callout[data-callout=highlight],.cm-content .callout[data-callout=tip],.cm-content .cm-callout-widget[data-callout=example],.cm-content .cm-callout-widget[data-callout=highlight],.cm-content .cm-callout-widget[data-callout=tip]{@apply bg-secondary/20 border-secondary/20}.cm-content .callout[data-callout=example] .callout-fold,.cm-content .callout[data-callout=highlight] .callout-fold,.cm-content .callout[data-callout=tip] .callout-fold,.cm-content .cm-callout-widget[data-callout=example] .callout-fold,.cm-content .cm-callout-widget[data-callout=highlight] .callout-fold,.cm-content .cm-callout-widget[data-callout=tip] .callout-fold{@apply text-secondary}.cm-content .callout[data-callout=example] .callout-title .callout-title-icon,.cm-content .callout[data-callout=example] .callout-title .callout-title-inner,.cm-content .callout[data-callout=highlight] .callout-title .callout-title-icon,.cm-content .callout[data-callout=highlight] .callout-title .callout-title-inner,.cm-content .callout[data-callout=tip] .callout-title .callout-title-icon,.cm-content .callout[data-callout=tip] .callout-title .callout-title-inner,.cm-content .cm-callout-widget[data-callout=example] .callout-title .callout-title-icon,.cm-content .cm-callout-widget[data-callout=example] .callout-title .callout-title-inner,.cm-content .cm-callout-widget[data-callout=highlight] .callout-title .callout-title-icon,.cm-content .cm-callout-widget[data-callout=highlight] .callout-title .callout-title-inner,.cm-content .cm-callout-widget[data-callout=tip] .callout-title .callout-title-icon,.cm-content .cm-callout-widget[data-callout=tip] .callout-title .callout-title-inner{@apply text-secondary}.cm-content .callout[data-callout=neutral],.cm-content .callout[data-callout=quote],.cm-content .cm-callout-widget[data-callout=neutral],.cm-content .cm-callout-widget[data-callout=quote]{@apply bg-accented/30 border-accented/30}.cm-content .callout[data-callout=neutral] .callout-fold,.cm-content .callout[data-callout=quote] .callout-fold,.cm-content .cm-callout-widget[data-callout=neutral] .callout-fold,.cm-content .cm-callout-widget[data-callout=quote] .callout-fold{@apply text-highlighted}.cm-content .callout[data-callout=neutral] .callout-title .callout-title-icon,.cm-content .callout[data-callout=neutral] .callout-title .callout-title-inner,.cm-content .callout[data-callout=quote] .callout-title .callout-title-icon,.cm-content .callout[data-callout=quote] .callout-title .callout-title-inner,.cm-content .cm-callout-widget[data-callout=neutral] .callout-title .callout-title-icon,.cm-content .cm-callout-widget[data-callout=neutral] .callout-title .callout-title-inner,.cm-content .cm-callout-widget[data-callout=quote] .callout-title .callout-title-icon,.cm-content .cm-callout-widget[data-callout=quote] .callout-title .callout-title-inner{@apply text-highlighted}.cm-content .callout .callout-fold,.cm-content .cm-callout-widget .callout-fold{@apply rounded hover:bg-elevated transition duration-200 cursor-pointer text-primary font-sans text-center align-middle p-1 size-6}.cm-content .callout .callout-fold svg,.cm-content .cm-callout-widget .callout-fold svg{@apply transition-transform duration-300 ease-in-out w-full h-full}.cm-content .callout[open] .callout-fold svg,.cm-content .cm-callout-widget[open] .callout-fold svg{@apply transition-transform duration-300 ease-in-out rotate-90 w-full h-full}.cm-content .callout[data-fold-state=closed] .callout-content,.cm-content .cm-callout-widget[data-fold-state=closed] .callout-content{@apply max-h-0 opacity-0 p-0}.cm-content .callout[data-fold-state=open] .callout-content,.cm-content .cm-callout-widget[data-fold-state=open] .callout-content{@apply max-h-none opacity-100 py-1 mt-1}.cm-content .callout .edit-block-button,.cm-content .cm-callout-widget .edit-block-button{@apply rounded hover:bg-elevated transition duration-200 cursor-pointer p-1 font-sans text-dimmed}.cm-content .callout .editing-utils-container,.cm-content .cm-callout-widget .editing-utils-container{@apply top-2 right-2 font-sans text-dimmed absolute flex items-center justify-center gap-1 p-1}.cm-content details .callout-content{@apply -mt-11}.cm-content .cm-indent{min-width:var(--list-indent,1.5rem);@apply relative inline-block}.cm-content .cm-indent:before{@apply content-["\200B"] absolute top-0 bottom-0 w-1;@apply border-r border-r-muted;@apply left-[var(--indentation-guide-source-indent)]}.cm-content .cm-indent-group .cm-active-indent:before{@apply border-primary}.cm-content .cm-list-line .cm-indent:before{@apply top-0}.cm-content .cm-list-internal[style*="--indent-level"]{@apply border-primary;padding-inline-start:calc(var(--indent-level)*var(--list-indent));text-indent:calc(var(--indent-level)*var(--list-indent))}.cm-content .cm-indent-list-bullet:before{content:"•";@apply text-dimmed border-none absolute;left:1rem}.cm-content .cm-indent-list-bullet+.cm-obsidian-bullet{@apply hidden}.cm-content .cm-obsidian-hidden{@apply hidden}.cm-content .cm-hidden-latex{@apply hidden}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
defineProps({
|
|
3
|
+
filePath: { type: String, required: true }
|
|
4
|
+
});
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<template>
|
|
8
|
+
<div class="p-3">
|
|
9
|
+
<NuxtImg :src="filePath" class="w-full h-auto object-cover rounded-lg" format="jpeg" :quality="80" alt=""/>
|
|
10
|
+
</div>
|
|
11
|
+
</template>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
filePath: string;
|
|
3
|
+
};
|
|
4
|
+
declare const _default: 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
|
+
export default _default;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
const props = defineProps({
|
|
3
|
+
codeContent: { type: String, required: false }
|
|
4
|
+
});
|
|
5
|
+
const clicks = useState(() => 0);
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<template>
|
|
9
|
+
<div class="m-3 p-3 border border-default rounded">
|
|
10
|
+
<div>This is a test codeblock.</div>
|
|
11
|
+
<div>{{props?.codeContent}}</div>
|
|
12
|
+
<UButton :label="`You could also add buttons in here: ${clicks}`" @click="() => {
|
|
13
|
+
clicks += 1;
|
|
14
|
+
}"/>
|
|
15
|
+
</div>
|
|
16
|
+
</template>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
codeContent?: string;
|
|
3
|
+
};
|
|
4
|
+
declare const _default: 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
|
+
export default _default;
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import CodeMirror from "vue-codemirror6";
|
|
3
|
+
import {
|
|
4
|
+
keymap,
|
|
5
|
+
EditorView,
|
|
6
|
+
drawSelection,
|
|
7
|
+
rectangularSelection,
|
|
8
|
+
highlightActiveLine,
|
|
9
|
+
highlightActiveLineGutter
|
|
10
|
+
} from "@codemirror/view";
|
|
11
|
+
import { standardKeymap, history, historyKeymap, indentWithTab } from "@codemirror/commands";
|
|
12
|
+
import { defaultHighlightStyle, syntaxHighlighting, indentOnInput, foldGutter } from "@codemirror/language";
|
|
13
|
+
import { Compartment } from "@codemirror/state";
|
|
14
|
+
import { languages } from "@codemirror/language-data";
|
|
15
|
+
import wysiwyg from "../editor/wysiwyg";
|
|
16
|
+
import { internalLinkMapFacet } from "../editor/plugins/linkMappingConfig";
|
|
17
|
+
import { specialCodeBlockMapFacet } from "../editor/plugins/specialCodeBlockMappingConfig";
|
|
18
|
+
import { customBracketClosingConfig } from "../editor/plugins/customBracketClosingConfig";
|
|
19
|
+
import { ref, shallowRef, computed, onMounted, onBeforeUnmount, unref, watch } from "vue";
|
|
20
|
+
const doc = defineModel({ type: String });
|
|
21
|
+
const props = defineProps({
|
|
22
|
+
class: { type: String, required: false },
|
|
23
|
+
internalLinkMap: { type: Array, required: false },
|
|
24
|
+
specialCodeBlockMap: { type: Array, required: false },
|
|
25
|
+
bracketClosing: { type: Boolean, required: false },
|
|
26
|
+
foldGutter: { type: Boolean, required: false },
|
|
27
|
+
disabled: { type: Boolean, required: false },
|
|
28
|
+
debug: { type: Boolean, required: false }
|
|
29
|
+
});
|
|
30
|
+
const emit = defineEmits(["internal-link-click", "external-link-click"]);
|
|
31
|
+
const extensions = shallowRef([]);
|
|
32
|
+
const view = shallowRef();
|
|
33
|
+
const ast = ref([]);
|
|
34
|
+
const internalLinkCompartment = new Compartment();
|
|
35
|
+
const specialCodeBlockCompartment = new Compartment();
|
|
36
|
+
const bracketClosingCompartment = new Compartment();
|
|
37
|
+
const foldGutterCompartment = new Compartment();
|
|
38
|
+
const editorElement = ref();
|
|
39
|
+
const keymaps = computed(() => {
|
|
40
|
+
return props.disabled ? keymap.of([]) : keymap.of([...standardKeymap, ...historyKeymap, indentWithTab]);
|
|
41
|
+
});
|
|
42
|
+
onMounted(() => {
|
|
43
|
+
const wysiwygPlugin = wysiwyg({
|
|
44
|
+
lezer: {
|
|
45
|
+
codeLanguages: languages
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
extensions.value = [
|
|
49
|
+
EditorView.lineWrapping,
|
|
50
|
+
history(),
|
|
51
|
+
drawSelection(),
|
|
52
|
+
rectangularSelection(),
|
|
53
|
+
indentOnInput(),
|
|
54
|
+
syntaxHighlighting(defaultHighlightStyle),
|
|
55
|
+
// highlightActiveLine(),
|
|
56
|
+
// highlightActiveLineGutter(),
|
|
57
|
+
unref(keymaps),
|
|
58
|
+
internalLinkCompartment.of(internalLinkMapFacet.of(props.internalLinkMap || [])),
|
|
59
|
+
specialCodeBlockCompartment.of(specialCodeBlockMapFacet.of(props.specialCodeBlockMap || [])),
|
|
60
|
+
bracketClosingCompartment.of(customBracketClosingConfig.of(props.bracketClosing ?? true)),
|
|
61
|
+
foldGutterCompartment.of(props.foldGutter ?? true ? foldGutter() : []),
|
|
62
|
+
wysiwygPlugin,
|
|
63
|
+
EditorView.editable.of(unref(!props.disabled))
|
|
64
|
+
];
|
|
65
|
+
if (editorElement.value) {
|
|
66
|
+
editorElement.value.addEventListener("internal-link-click", handleInternalLinkClick);
|
|
67
|
+
editorElement.value.addEventListener("external-link-click", handleExternalLinkClick);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
onBeforeUnmount(() => {
|
|
71
|
+
if (editorElement.value) {
|
|
72
|
+
editorElement.value.removeEventListener("internal-link-click", handleInternalLinkClick);
|
|
73
|
+
editorElement.value.removeEventListener("external-link-click", handleExternalLinkClick);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
function handleInternalLinkClick(event) {
|
|
77
|
+
emit("internal-link-click", event.detail);
|
|
78
|
+
}
|
|
79
|
+
function handleExternalLinkClick(event) {
|
|
80
|
+
emit("external-link-click", event.detail);
|
|
81
|
+
}
|
|
82
|
+
watch(
|
|
83
|
+
() => props.internalLinkMap,
|
|
84
|
+
(newMap) => {
|
|
85
|
+
if (view.value) {
|
|
86
|
+
view.value.dispatch({
|
|
87
|
+
effects: internalLinkCompartment.reconfigure(internalLinkMapFacet.of(newMap || []))
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
{ deep: true }
|
|
92
|
+
);
|
|
93
|
+
watch(
|
|
94
|
+
() => props.specialCodeBlockMap,
|
|
95
|
+
(newMap) => {
|
|
96
|
+
if (view.value) {
|
|
97
|
+
view.value.dispatch({
|
|
98
|
+
effects: specialCodeBlockCompartment.reconfigure(specialCodeBlockMapFacet.of(newMap || []))
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
{ deep: true }
|
|
103
|
+
);
|
|
104
|
+
watch(
|
|
105
|
+
() => props.bracketClosing,
|
|
106
|
+
(newValue) => {
|
|
107
|
+
if (view.value) {
|
|
108
|
+
view.value.dispatch({
|
|
109
|
+
effects: bracketClosingCompartment.reconfigure(customBracketClosingConfig.of(newValue ?? true))
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
{ deep: true }
|
|
114
|
+
);
|
|
115
|
+
watch(
|
|
116
|
+
() => props.foldGutter,
|
|
117
|
+
(newValue) => {
|
|
118
|
+
if (view.value) {
|
|
119
|
+
view.value.dispatch({
|
|
120
|
+
effects: foldGutterCompartment.reconfigure(newValue ?? true ? foldGutter() : [])
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
{ deep: true }
|
|
125
|
+
);
|
|
126
|
+
function handleReady(payload) {
|
|
127
|
+
view.value = payload.view;
|
|
128
|
+
}
|
|
129
|
+
function log(...args) {
|
|
130
|
+
}
|
|
131
|
+
function iterate() {
|
|
132
|
+
ast.value = [];
|
|
133
|
+
try {
|
|
134
|
+
view.value?.state?.tree.iterate({
|
|
135
|
+
from: 0,
|
|
136
|
+
to: view.value.state.doc.length,
|
|
137
|
+
//@ts-ignore
|
|
138
|
+
enter(node) {
|
|
139
|
+
ast.value.push(`Node: ${node.name}, From: ${node.from}, To: ${node.to}, Text: "${view.value?.state.doc.sliceString(node.from, node.to)}"`);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
} catch (e) {
|
|
143
|
+
console.log(e);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
</script>
|
|
147
|
+
|
|
148
|
+
<template>
|
|
149
|
+
<div :class="props.class ? props.class : 'w-full h-full overflow-visible'" ref="editorElement">
|
|
150
|
+
<ClientOnly class="overflow-visible">
|
|
151
|
+
<div class="w-full cm-content overflow-visible">
|
|
152
|
+
<CodeMirror
|
|
153
|
+
v-model="doc"
|
|
154
|
+
placeholder="Start typing your markdown content here..."
|
|
155
|
+
:autofocus="true"
|
|
156
|
+
:indent-with-tab="true"
|
|
157
|
+
:tab-size="4"
|
|
158
|
+
:tab="true"
|
|
159
|
+
:indent-unit="' '"
|
|
160
|
+
:extensions="extensions"
|
|
161
|
+
@ready="handleReady"
|
|
162
|
+
@change="log('change', $event)"
|
|
163
|
+
@focus="log('focus', $event)"
|
|
164
|
+
@blur="log('blur', $event)"
|
|
165
|
+
class="w-full h-full cm-content overflow-visible"
|
|
166
|
+
:disabled="props.disabled"
|
|
167
|
+
:readonly="props.disabled"
|
|
168
|
+
/>
|
|
169
|
+
</div>
|
|
170
|
+
<template v-if="props.debug">
|
|
171
|
+
<UButton label="Iterate AST" @click="iterate" />
|
|
172
|
+
<div class="grid grid-cols-1 gap-2 py-2 w-full">
|
|
173
|
+
<div v-for="(content, index) in ast" :key="index">{{ content }}</div>
|
|
174
|
+
</div>
|
|
175
|
+
</template>
|
|
176
|
+
</ClientOnly>
|
|
177
|
+
</div>
|
|
178
|
+
</template>
|
|
179
|
+
|
|
180
|
+
<style>
|
|
181
|
+
@reference "../assets/css/editor.css";.cm-cursor{@apply border-l-primary! border-l-[1.6px]!}.cm-selectionBackground{@apply bg-primary/50! z-120!}div[contenteditable=true]:focus{@apply outline-none border-none h-full shadow-none}.cm-focused{@apply outline-none!}.cm-placeholder{@apply font-editor text-muted}.cm-activeLine{@apply bg-none! border-l-primary border-l-4 relative -left-1 content-[""] mask-no-clip overflow-visible}
|
|
182
|
+
</style>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { InternalLink, SpecialCodeBlockMapping } from '#codemirror-rich-obsidian-editor/editor-types';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
class?: string;
|
|
4
|
+
internalLinkMap?: InternalLink[];
|
|
5
|
+
specialCodeBlockMap?: SpecialCodeBlockMapping[];
|
|
6
|
+
bracketClosing?: boolean;
|
|
7
|
+
foldGutter?: boolean;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
debug?: boolean;
|
|
10
|
+
};
|
|
11
|
+
type __VLS_PublicProps = __VLS_Props & {
|
|
12
|
+
modelValue?: string;
|
|
13
|
+
};
|
|
14
|
+
declare const _default: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
15
|
+
"internal-link-click": (...args: any[]) => void;
|
|
16
|
+
"external-link-click": (...args: any[]) => void;
|
|
17
|
+
"update:modelValue": (value: string | undefined) => void;
|
|
18
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
19
|
+
"onInternal-link-click"?: ((...args: any[]) => any) | undefined;
|
|
20
|
+
"onExternal-link-click"?: ((...args: any[]) => any) | undefined;
|
|
21
|
+
"onUpdate:modelValue"?: ((value: string | undefined) => any) | undefined;
|
|
22
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
23
|
+
export default _default;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const CustomOFM: (import("@lezer/markdown").MarkdownConfig | import("@lezer/markdown").MarkdownConfig)[];
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Strikethrough, Table } from "@lezer/markdown";
|
|
2
|
+
import { lezerHashtagParser } from "./lezerHashtagParser.js";
|
|
3
|
+
import { Mark, Comment, Footnote, TaskList } from "lezer-markdown-obsidian";
|
|
4
|
+
import { lezerYamlFrontmatterParser } from "./lezerYamlFrontmatterParser.js";
|
|
5
|
+
import { lezerInternalLinkParser } from "./lezerInternalLinkParser.js";
|
|
6
|
+
import { lezerLatexParser } from "./lezerLatexParser.js";
|
|
7
|
+
import { lezerCalloutParser } from "./lezerCalloutParser.js";
|
|
8
|
+
import { lezerIndentationParser } from "./lezerIndentationParser.js";
|
|
9
|
+
export const CustomOFM = [
|
|
10
|
+
// the array here must remain implicit because the version of @lezer/markdown that lezer-markdown-obsidian uses is different than the one this project is using.
|
|
11
|
+
Comment,
|
|
12
|
+
Footnote,
|
|
13
|
+
lezerHashtagParser,
|
|
14
|
+
lezerInternalLinkParser,
|
|
15
|
+
Mark,
|
|
16
|
+
Strikethrough,
|
|
17
|
+
Table,
|
|
18
|
+
TaskList,
|
|
19
|
+
lezerLatexParser,
|
|
20
|
+
lezerYamlFrontmatterParser,
|
|
21
|
+
lezerCalloutParser,
|
|
22
|
+
lezerIndentationParser
|
|
23
|
+
];
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type MarkdownConfig } from '@lezer/markdown';
|
|
2
|
+
import { Tag } from "@lezer/highlight";
|
|
3
|
+
export declare const lezerHighlightCallout: Tag;
|
|
4
|
+
export declare const lezerHighlightCalloutMark: Tag;
|
|
5
|
+
export declare const lezerHighlightCalloutType: Tag;
|
|
6
|
+
export declare const lezerHighlightCalloutFoldMark: Tag;
|
|
7
|
+
export declare const lezerHighlightCalloutTitle: Tag;
|
|
8
|
+
export declare const lezerCalloutParser: MarkdownConfig;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Tag } from "@lezer/highlight";
|
|
2
|
+
export const lezerHighlightCallout = Tag.define();
|
|
3
|
+
export const lezerHighlightCalloutMark = Tag.define(lezerHighlightCallout);
|
|
4
|
+
export const lezerHighlightCalloutType = Tag.define(lezerHighlightCallout);
|
|
5
|
+
export const lezerHighlightCalloutFoldMark = Tag.define(lezerHighlightCallout);
|
|
6
|
+
export const lezerHighlightCalloutTitle = Tag.define(lezerHighlightCallout);
|
|
7
|
+
const calloutRegex = /^\[!(?<type>[^\]]+)\](?<fold>[+-])?(?<title>.*)/;
|
|
8
|
+
export const lezerCalloutParser = {
|
|
9
|
+
defineNodes: [
|
|
10
|
+
{ name: "Callout", style: lezerHighlightCallout },
|
|
11
|
+
{ name: "CalloutMark", style: lezerHighlightCalloutMark },
|
|
12
|
+
{ name: "CalloutType", style: lezerHighlightCalloutType },
|
|
13
|
+
{ name: "CalloutFoldMark", style: lezerHighlightCalloutFoldMark },
|
|
14
|
+
{ name: "CalloutTitle", style: lezerHighlightCalloutTitle }
|
|
15
|
+
],
|
|
16
|
+
parseInline: [{
|
|
17
|
+
name: "Callout",
|
|
18
|
+
parse(cx, next, pos) {
|
|
19
|
+
const text = cx.slice(pos, cx.end);
|
|
20
|
+
const match = calloutRegex.exec(text);
|
|
21
|
+
if (!match || !match.groups) {
|
|
22
|
+
return -1;
|
|
23
|
+
}
|
|
24
|
+
const { type, fold, title } = match.groups;
|
|
25
|
+
if (!type) {
|
|
26
|
+
return -1;
|
|
27
|
+
}
|
|
28
|
+
const fullMatchLength = match[0].length;
|
|
29
|
+
const children = [];
|
|
30
|
+
let currentPosInMatch = 0;
|
|
31
|
+
children.push(cx.elt("CalloutMark", pos + currentPosInMatch, pos + currentPosInMatch + 2));
|
|
32
|
+
currentPosInMatch += 2;
|
|
33
|
+
children.push(cx.elt("CalloutType", pos + currentPosInMatch, pos + currentPosInMatch + type.length));
|
|
34
|
+
currentPosInMatch += type.length;
|
|
35
|
+
children.push(cx.elt("CalloutMark", pos + currentPosInMatch, pos + currentPosInMatch + 1));
|
|
36
|
+
currentPosInMatch += 1;
|
|
37
|
+
if (fold) {
|
|
38
|
+
children.push(cx.elt("CalloutFoldMark", pos + currentPosInMatch, pos + currentPosInMatch + 1));
|
|
39
|
+
currentPosInMatch += 1;
|
|
40
|
+
}
|
|
41
|
+
if (title) {
|
|
42
|
+
const trimmedTitle = title.trim();
|
|
43
|
+
if (trimmedTitle.length > 0) {
|
|
44
|
+
const titleStartOffset = title.indexOf(trimmedTitle);
|
|
45
|
+
const titleStart = pos + currentPosInMatch + titleStartOffset;
|
|
46
|
+
children.push(cx.elt("CalloutTitle", titleStart, titleStart + trimmedTitle.length));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return cx.addElement(cx.elt("Callout", pos, pos + fullMatchLength, children));
|
|
50
|
+
},
|
|
51
|
+
before: "Link"
|
|
52
|
+
}]
|
|
53
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type MarkdownConfig } from "@lezer/markdown";
|
|
2
|
+
import { Tag } from '@lezer/highlight';
|
|
3
|
+
export declare const lezerHighlightHashtagTag: Tag;
|
|
4
|
+
export declare const lezerHighlightHashtagTagMark: Tag;
|
|
5
|
+
export declare const lezerHighlightHashtagTagLabel: Tag;
|
|
6
|
+
export declare const lezerHashtagParser: MarkdownConfig;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Tag } from "@lezer/highlight";
|
|
2
|
+
const hashtagRE = /^[^\u2000-\u206F\u2E00-\u2E7F'!"#$%&()*+,.:;<=>?@^`{|}~\[\]\\\s]+/;
|
|
3
|
+
export const lezerHighlightHashtagTag = Tag.define("HashtagTag");
|
|
4
|
+
export const lezerHighlightHashtagTagMark = Tag.define("HashtagTagMark", lezerHighlightHashtagTag);
|
|
5
|
+
export const lezerHighlightHashtagTagLabel = Tag.define("HashtagTagLabel", lezerHighlightHashtagTag);
|
|
6
|
+
export const lezerHashtagParser = {
|
|
7
|
+
defineNodes: [
|
|
8
|
+
{ name: "HashtagTag", style: lezerHighlightHashtagTag },
|
|
9
|
+
{ name: "HashtagTagMark", style: lezerHighlightHashtagTagMark },
|
|
10
|
+
{ name: "HashtagTagLabel", style: lezerHighlightHashtagTagLabel }
|
|
11
|
+
],
|
|
12
|
+
parseInline: [
|
|
13
|
+
{
|
|
14
|
+
name: "HashtagTag",
|
|
15
|
+
parse(cx, next, pos) {
|
|
16
|
+
if (next != 35) {
|
|
17
|
+
return -1;
|
|
18
|
+
}
|
|
19
|
+
const start = pos;
|
|
20
|
+
pos += 1;
|
|
21
|
+
const match = hashtagRE.exec(cx.text.slice(pos - cx.offset));
|
|
22
|
+
if (match && /\D/.test(match[0])) {
|
|
23
|
+
pos += match[0].length;
|
|
24
|
+
return cx.addElement(
|
|
25
|
+
cx.elt("HashtagTag", start, pos, [
|
|
26
|
+
cx.elt("HashtagTagMark", start, start + 1),
|
|
27
|
+
cx.elt("HashtagTagLabel", start + 1, pos)
|
|
28
|
+
])
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
return -1;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Tag } from "@lezer/highlight";
|
|
2
|
+
export const lezerHighlightIndentation = Tag.define();
|
|
3
|
+
const indentationRegex = /^((?:\t| {4})+)/;
|
|
4
|
+
export const lezerIndentationParser = {
|
|
5
|
+
defineNodes: [
|
|
6
|
+
{ name: "Indentation", style: lezerHighlightIndentation }
|
|
7
|
+
],
|
|
8
|
+
parseInline: [{
|
|
9
|
+
name: "Indentation",
|
|
10
|
+
parse(cx, next, pos) {
|
|
11
|
+
if (pos > 0 && cx.slice(pos - 1, pos) !== "\n") {
|
|
12
|
+
return -1;
|
|
13
|
+
}
|
|
14
|
+
const match = indentationRegex.exec(cx.slice(pos, cx.end));
|
|
15
|
+
if (!match) {
|
|
16
|
+
return -1;
|
|
17
|
+
}
|
|
18
|
+
const matchLength = match[0].length;
|
|
19
|
+
return cx.addElement(cx.elt("Indentation", pos, pos + matchLength));
|
|
20
|
+
},
|
|
21
|
+
before: "Emphasis"
|
|
22
|
+
}]
|
|
23
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { MarkdownConfig } from '@lezer/markdown';
|
|
2
|
+
import { Tag } from "@lezer/highlight";
|
|
3
|
+
export declare const lezerHighlightEmbed: Tag;
|
|
4
|
+
export declare const lezerHighlightEmbedMark: Tag;
|
|
5
|
+
export declare const lezerHighlightInternalLink: Tag;
|
|
6
|
+
export declare const lezerHighlightInternalMark: Tag;
|
|
7
|
+
export declare const lezerHighlightInternalPath: Tag;
|
|
8
|
+
export declare const lezerHighlightInternalSubpath: Tag;
|
|
9
|
+
export declare const lezerHighlightInternalDisplay: Tag;
|
|
10
|
+
export declare const lezerInternalLinkParser: MarkdownConfig;
|