Implement extendedAutolinks & customHTMLRenderer
authorAdam Dullage <redacted>
Tue, 7 May 2024 11:24:19 +0000 (12:24 +0100)
committerAdam Dullage <redacted>
Tue, 7 May 2024 11:24:19 +0000 (12:24 +0100)
client/components/toastui/ToastEditor.vue
client/components/toastui/ToastViewer.vue
client/components/toastui/baseOptions.js [new file with mode: 0644]
client/style.css

index 0479cadcca778288ade5c39ae49eb7cb70b97658..49faaabac15e070510c2dbc79aa270e15ea2e57a 100644 (file)
@@ -4,9 +4,10 @@
 
 <script setup>
 import Editor from "@toast-ui/editor";
-import codeSyntaxHighlight from "@toast-ui/editor-plugin-code-syntax-highlight/dist/toastui-editor-plugin-code-syntax-highlight-all.js";
 import { onMounted, ref } from "vue";
 
+import baseOptions from "./baseOptions.js";
+
 const props = defineProps({
   initialValue: String,
 });
@@ -16,11 +17,9 @@ let toastEditor;
 
 onMounted(() => {
   toastEditor = new Editor({
+    ...baseOptions,
     el: editorElement.value,
-    height: "100%",
     initialValue: props.initialValue,
-    plugins: [codeSyntaxHighlight],
-    usageStatistics: false,
   });
 });
 
index a595f290ed3b9d445046b2c8a4abddacda1e4bcb..7a12c31011c294427a0b5a0114a9112db0d3c26a 100644 (file)
@@ -3,10 +3,11 @@
 </template>
 
 <script setup>
-import codeSyntaxHighlight from "@toast-ui/editor-plugin-code-syntax-highlight/dist/toastui-editor-plugin-code-syntax-highlight-all.js";
 import Viewer from "@toast-ui/editor/dist/toastui-editor-viewer";
 import { onMounted, ref } from "vue";
 
+import baseOptions from "./baseOptions.js";
+
 const props = defineProps({
   initialValue: String,
 });
@@ -15,10 +16,9 @@ const viewerElement = ref();
 
 onMounted(() => {
   new Viewer({
+    ...baseOptions,
     el: viewerElement.value,
     initialValue: props.initialValue,
-    plugins: [codeSyntaxHighlight],
-    usageStatistics: false,
   });
 });
 </script>
diff --git a/client/components/toastui/baseOptions.js b/client/components/toastui/baseOptions.js
new file mode 100644 (file)
index 0000000..60ff3ca
--- /dev/null
@@ -0,0 +1,130 @@
+import codeSyntaxHighlight from "@toast-ui/editor-plugin-code-syntax-highlight/dist/toastui-editor-plugin-code-syntax-highlight-all.js";
+import router from "../../router.js";
+
+/*
+ * Sourced from toast-ui. Their autolink options are
+ * either override their built in functionality or
+ * use their built in functionality. We'd like to have
+ * both so this is the source of their parsers.
+ */
+const DOMAIN = "(?:[w-]+.)*[A-Za-z0-9-]+.[A-Za-z0-9-]+";
+const PATH = "[^<\\s]*[^<?!.,:*_?~\\s]";
+const EMAIL = "[\\w.+-]+@(?:[\\w-]+\\.)+[\\w-]+";
+function trimUnmatchedTrailingParens(source) {
+  const trailingParen = /\)+$/.exec(source);
+  if (trailingParen) {
+    let count = 0;
+    for (const ch of source) {
+      if (ch === "(") {
+        if (count < 0) {
+          count = 1;
+        } else {
+          count += 1;
+        }
+      } else if (ch === ")") {
+        count -= 1;
+      }
+    }
+
+    if (count < 0) {
+      const trimCount = Math.min(-count, trailingParen[0].length);
+      return source.substring(0, source.length - trimCount);
+    }
+  }
+  return source;
+}
+
+function trimTrailingEntity(source) {
+  return source.replace(/&[A-Za-z0-9]+;$/, "");
+}
+export function parseEmailLink(source) {
+  const reEmailLink = new RegExp(EMAIL, "g");
+  const result = [];
+  let m;
+  while ((m = reEmailLink.exec(source))) {
+    const text = m[0];
+    if (!/[_-]+$/.test(text)) {
+      result.push({
+        text,
+        range: [m.index, m.index + text.length - 1],
+        url: `mailto:${text}`,
+      });
+    }
+  }
+
+  return result;
+}
+
+export function parseUrlLink(source) {
+  const reWwwAutolink = new RegExp(`(www|https?://)\.${DOMAIN}${PATH}`, "g");
+  const result = [];
+  let m;
+
+  while ((m = reWwwAutolink.exec(source))) {
+    const text = trimTrailingEntity(trimUnmatchedTrailingParens(m[0]));
+    const scheme = m[1] === "www" ? "http://" : "";
+    result.push({
+      text,
+      range: [m.index, m.index + text.length - 1],
+      url: `${scheme}${text}`,
+    });
+  }
+
+  return result;
+}
+// end of raw toast-ui source
+
+function parseWikiLink(source) {
+  const matched = source.matchAll(/\[\[(.*)\]\]/g);
+  if (matched) {
+    return Array.from(matched).map((match) => {
+      const text = match[1];
+      return {
+        text,
+        url: `${router.resolve({ name: "note", params: { title: text.trim() } }).href}`,
+        range: [match.index, match.index + match[0].length - 1],
+      };
+    });
+  }
+
+  return null;
+}
+
+function extendedAutolinks(source) {
+  return [
+    ...parseUrlLink(source),
+    ...parseEmailLink(source),
+    ...parseWikiLink(source),
+  ].sort((a, b) => a.range[0] - b.range[0]);
+}
+
+const customHTMLRenderer = {
+  heading(node, { entering, getChildrenText }) {
+    const tagName = `h${node.level}`;
+
+    if (entering) {
+      return {
+        type: "openTag",
+        tagName,
+        attributes: {
+          id: getChildrenText(node)
+            .toLowerCase()
+            .replace(/[^a-z0-9-\s]*/g, "")
+            .trim()
+            .replace(/\s/g, "-"),
+        },
+      };
+    }
+    return { type: "closeTag", tagName };
+  },
+};
+
+const baseOptions = {
+  height: "100%",
+  plugins: [codeSyntaxHighlight],
+  customHTMLRenderer: customHTMLRenderer,
+  usageStatistics: false,
+  extendedAutolinks,
+};
+
+export default baseOptions;
index 634af87bb328e7f0838eb4cf629fcac5005582f4..713c238d5de5df345b503b6b34b2d6cfc8c3a215 100644 (file)
 \r
     body {\r
         --theme-brand: 248 166 107;\r
-\r
         --theme-background: 255 255 255;\r
         --theme-background-elevated: 243 244 245;\r
-\r
         --theme-text: 44 49 57;\r
         --theme-text-muted: 136 145 161;\r
         --theme-text-very-muted: 193 199 208;\r
-\r
         --theme-shadow: 236 238 240;\r
         --theme-border: 236 238 240;\r
     }\r
 \r
     body.dark {\r
         /* --theme-brand: 248 166 107; */\r
-\r
         --theme-background: 34 38 44;\r
         --theme-background-elevated: 44 49 57;\r
-\r
         --theme-text: 193 199 208;\r
         --theme-text-muted: 136 145 161;\r
         --theme-text-very-muted: 94 107 128;\r
-\r
         --theme-shadow: none;\r
         --theme-border: 94 107 128;\r
     }\r
git clone https://git.99rst.org/PROJECT