@type32/codemirror-rich-obsidian-editor 0.0.9 → 0.0.11

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 CHANGED
@@ -8,6 +8,7 @@
8
8
  - https://github.com/ebullient/markdown-it-obsidian-callouts, for his awesome markdown-to-html callouts markdown-it plugin
9
9
  - https://github.com/mgmeyers/obsidian-indentation-guides, for indentation guides
10
10
  - Markdown-It
11
+ - https://github.com/thecodrr/alfaaz, for insanely fast word/line-counting functions
11
12
 
12
13
  ### Related References & Resources
13
14
  - https://github.com/heavycircle/remark-obsidian, for mostly wiki link alias & highlights & callouts parsing
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@type32/codemirror-rich-obsidian-editor",
3
3
  "configKey": "cmOfmEditor",
4
- "version": "0.0.9",
4
+ "version": "0.0.11",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
@@ -14,6 +14,7 @@ import {
14
14
  import { standardKeymap, history, historyKeymap, indentWithTab } from "@codemirror/commands";
15
15
  import { defaultHighlightStyle, syntaxHighlighting, indentOnInput, foldGutter, syntaxTree } from "@codemirror/language";
16
16
  import { Compartment, RangeSetBuilder } from "@codemirror/state";
17
+ import { LanguageSupport, LRLanguage } from "@codemirror/language";
17
18
  import { languages } from "@codemirror/language-data";
18
19
  import wysiwyg from "../editor/wysiwyg";
19
20
  import { internalLinkMapFacet } from "../editor/plugins/linkMappingConfig";
@@ -43,10 +44,17 @@ const editorElement = ref();
43
44
  const keymaps = computed(() => {
44
45
  return props.disabled ? keymap.of([]) : keymap.of([...standardKeymap, ...historyKeymap, indentWithTab]);
45
46
  });
47
+ async function loadLanguage(info) {
48
+ const lang = languages.find((l) => l.name.toLowerCase() === info.toLowerCase() || l.alias.map((a) => a.toLowerCase()).includes(info.toLowerCase()));
49
+ if (lang) {
50
+ return await lang.load();
51
+ }
52
+ throw new Error(`Language ${info} not found`);
53
+ }
46
54
  onMounted(() => {
47
55
  const wysiwygPlugin = wysiwyg({
48
56
  lezer: {
49
- codeLanguages: languages
57
+ codeLanguages: loadLanguage
50
58
  }
51
59
  });
52
60
  extensions.value = [
@@ -1,5 +1,5 @@
1
1
  import { EditorView } from '@codemirror/view';
2
- import type { InternalLink, SpecialCodeBlockMapping } from '#codemirror-rich-obsidian-editor/editor-types';
2
+ import type { InternalLink, SpecialCodeBlockMapping, InternalLinkClickDetail, ExternalLinkClickDetail } from '#codemirror-rich-obsidian-editor/editor-types';
3
3
  type __VLS_Props = {
4
4
  class?: string;
5
5
  internalLinkMap?: InternalLink[];
@@ -15,12 +15,12 @@ type __VLS_PublicProps = __VLS_Props & {
15
15
  declare const _default: import("vue").DefineComponent<__VLS_PublicProps, {
16
16
  view: import("vue").ShallowRef<EditorView | undefined, EditorView | undefined>;
17
17
  }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
18
- "internal-link-click": (...args: any[]) => void;
19
- "external-link-click": (...args: any[]) => void;
20
- "update:modelValue": (value: string | undefined) => void;
18
+ "internal-link-click": (detail: InternalLinkClickDetail) => any;
19
+ "external-link-click": (detail: ExternalLinkClickDetail) => any;
20
+ "update:modelValue": (value: string | undefined) => any;
21
21
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
22
- "onInternal-link-click"?: ((...args: any[]) => any) | undefined;
23
- "onExternal-link-click"?: ((...args: any[]) => any) | undefined;
22
+ "onInternal-link-click"?: ((detail: InternalLinkClickDetail) => any) | undefined;
23
+ "onExternal-link-click"?: ((detail: ExternalLinkClickDetail) => any) | undefined;
24
24
  "onUpdate:modelValue"?: ((value: string | undefined) => any) | undefined;
25
25
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
26
26
  export default _default;
@@ -0,0 +1,9 @@
1
+ export declare function useDocumentUtils(): {
2
+ getWordCount: (text: string) => number;
3
+ getLineCount: (text: string) => number;
4
+ getCharacters: (text: string) => number;
5
+ getReadingTime: (text: string, wordsPerMinute?: number) => number;
6
+ getParagraphs: (text: string) => number;
7
+ getAvgWordLength: (text: string) => number;
8
+ isEmpty: (text: string) => boolean;
9
+ };
@@ -0,0 +1,37 @@
1
+ import { countWords, countLines } from "alfaaz";
2
+ export function useDocumentUtils() {
3
+ function getWordCount(text) {
4
+ return countWords(text);
5
+ }
6
+ function getLineCount(text) {
7
+ return countLines(text);
8
+ }
9
+ function getCharacters(text) {
10
+ return text.length;
11
+ }
12
+ function getReadingTime(text, wordsPerMinute = 200) {
13
+ const wordCount = countWords(text);
14
+ return Math.ceil(wordCount / wordsPerMinute);
15
+ }
16
+ function getParagraphs(text) {
17
+ return text.split(/\n\s*\n/).filter((p) => p.trim().length > 0).length;
18
+ }
19
+ function getAvgWordLength(text) {
20
+ const words = text.match(/\b\w+\b/g) || [];
21
+ if (words.length === 0) return 0;
22
+ const totalChars = words.reduce((sum, word) => sum + word.length, 0);
23
+ return totalChars / words.length;
24
+ }
25
+ function isEmpty(text) {
26
+ return text.trim().length === 0;
27
+ }
28
+ return {
29
+ getWordCount,
30
+ getLineCount,
31
+ getCharacters,
32
+ getReadingTime,
33
+ getParagraphs,
34
+ getAvgWordLength,
35
+ isEmpty
36
+ };
37
+ }
@@ -1,5 +1,7 @@
1
1
  import { computed, unref } from "vue";
2
2
  import { markdown } from "@codemirror/lang-markdown";
3
+ import { CustomOFM } from "../../runtime/editor/lezer-parsers/customOFMParsers";
4
+ import { GFM } from "@lezer/markdown";
3
5
  export function useEditorUtils(editor) {
4
6
  const view = computed(() => {
5
7
  const instance = unref(editor);
@@ -24,7 +26,13 @@ export function useEditorUtils(editor) {
24
26
  unref(view)?.dispatch(...specs);
25
27
  }
26
28
  function parseMarkdownToAST(markdownText) {
27
- return markdown().language.parser.parse(markdownText);
29
+ return markdown({
30
+ extensions: [
31
+ GFM,
32
+ CustomOFM,
33
+ { remove: ["SetextHeading"] }
34
+ ]
35
+ }).language.parser.parse(markdownText);
28
36
  }
29
37
  function getDocAst() {
30
38
  return parseMarkdownToAST(getDoc() || "");
@@ -1,5 +1,6 @@
1
1
  import { EditorView } from "@codemirror/view";
2
2
  import { syntaxTree } from "@codemirror/language";
3
+ import { internalLinkMapFacet } from "../linkMappingConfig.js";
3
4
  export const editorLinkClickPlugin = EditorView.domEventHandlers({
4
5
  mousedown(event, view) {
5
6
  let target = event.target;
@@ -21,21 +22,36 @@ export const editorLinkClickPlugin = EditorView.domEventHandlers({
21
22
  }
22
23
  if (anchor.dataset.internalLink === "true") {
23
24
  event.preventDefault();
25
+ const path = anchor.dataset.path;
26
+ if (!path) return true;
27
+ const linkMap = view.state.facet(internalLinkMapFacet);
28
+ const linkInfo = linkMap.find((l) => l.internalLinkName === path);
29
+ const type = anchor.dataset.type;
30
+ const detail = {
31
+ path,
32
+ subpath: anchor.dataset.subpath,
33
+ display: anchor.dataset.display,
34
+ type: type || "internal-link",
35
+ redirectToPath: linkInfo?.redirectToPath
36
+ };
24
37
  view.dom.dispatchEvent(new CustomEvent("internal-link-click", {
25
38
  bubbles: true,
26
39
  composed: true,
27
- detail: {
28
- path: anchor.dataset.path,
29
- subpath: anchor.dataset.subpath,
30
- display: anchor.dataset.display,
31
- type: anchor.dataset.type
32
- }
40
+ detail
33
41
  }));
34
42
  } else if (anchor.dataset.externalLink === "true") {
35
43
  event.preventDefault();
36
44
  const url = anchor.dataset.url;
37
45
  if (url) {
38
- window.open(url, "_blank");
46
+ const detail = {
47
+ url,
48
+ text: anchor.textContent
49
+ };
50
+ view.dom.dispatchEvent(new CustomEvent("external-link-click", {
51
+ bubbles: true,
52
+ composed: true,
53
+ detail
54
+ }));
39
55
  }
40
56
  }
41
57
  return true;
@@ -1,4 +1,5 @@
1
1
  import type { Component } from 'vue'
2
+ import type { LanguageSupport } from '@codemirror/language'
2
3
 
3
4
  export interface InternalLink {
4
5
  internalLinkName: string;
@@ -7,11 +8,27 @@ export interface InternalLink {
7
8
  embedComponent?: Component;
8
9
  }
9
10
 
11
+ export interface InternalLinkClickDetail {
12
+ path: string;
13
+ subpath?: string;
14
+ display?: string;
15
+ type: 'embed' | 'internal-link';
16
+ redirectToPath?: string;
17
+ }
18
+
19
+ export interface ExternalLinkClickDetail {
20
+ url: string;
21
+ text: string | null;
22
+ }
23
+
10
24
  export interface SpecialCodeBlockMapping {
11
25
  codeInfo: string
12
26
  component: Component
13
27
  }
14
28
 
15
29
  export type WysiwygPlugin = {
16
- lezer?: any
30
+ lezer?: {
31
+ codeLanguages?: (info: string) => LanguageSupport | Promise<LanguageSupport> | null
32
+ [key: string]: any
33
+ }
17
34
  }
@@ -33,9 +33,10 @@ import { editorLivePreviewField, proseTaskListPlugin } from "./plugins/codemirro
33
33
  import { proseCalloutPlugin } from "./plugins/codemirror-plugin-proses/proseCalloutPlugin.js";
34
34
  import { indentationListPlugin } from "./plugins/codemirror-editor-plugins/indentationListPlugin.js";
35
35
  export default function(config) {
36
+ const { codeLanguages, ...lezerRest } = config?.lezer ?? {};
36
37
  const mergedConfig = {
37
- ...config?.lezer ?? [],
38
- // Spreads user-passed lezer config (like codeLanguages)
38
+ ...lezerRest ?? [],
39
+ codeLanguages,
39
40
  extensions: [
40
41
  GFM,
41
42
  CustomOFM,
@@ -74,6 +75,7 @@ export default function(config) {
74
75
  indentationListPlugin,
75
76
  // editorAttributesPlugin,
76
77
  syntaxHighlighting(proseStylesPlugin),
78
+ //@ts-ignore
77
79
  markdown(mergedConfig)
78
80
  ]
79
81
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@type32/codemirror-rich-obsidian-editor",
3
- "version": "0.0.9",
3
+ "version": "0.0.11",
4
4
  "description": "OFM Editor Component for Nuxt.",
5
5
  "repository": "Type-32/codemirror-rich-obsidian",
6
6
  "license": "MIT",
@@ -46,6 +46,7 @@
46
46
  "@nuxt/image": "1.10.0",
47
47
  "@nuxt/kit": "^4.1.2",
48
48
  "@nuxt/ui": "^4.0.0-alpha.1",
49
+ "alfaaz": "^1.1.0",
49
50
  "codemirror": "^6.0.2",
50
51
  "js-yaml": "^4.1.0",
51
52
  "katex": "^0.16.22",