diff --git a/Cargo.lock b/Cargo.lock
index 5e7341a..1385259 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1240,6 +1240,15 @@ dependencies = [
"version_check",
]
+[[package]]
+name = "getopts"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
+dependencies = [
+ "unicode-width",
+]
+
[[package]]
name = "getrandom"
version = "0.2.15"
@@ -2257,6 +2266,7 @@ dependencies = [
"free-icons",
"minijinja",
"minijinja-autoreload",
+ "pulldown-cmark",
"rand 0.8.5",
"redb",
"serde",
@@ -2346,6 +2356,25 @@ dependencies = [
"syn 1.0.109",
]
+[[package]]
+name = "pulldown-cmark"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8746739f11d39ce5ad5c2520a9b75285310dbfe78c541ccf832d38615765aec0"
+dependencies = [
+ "bitflags 2.5.0",
+ "getopts",
+ "memchr",
+ "pulldown-cmark-escape",
+ "unicase",
+]
+
+[[package]]
+name = "pulldown-cmark-escape"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae"
+
[[package]]
name = "quanta"
version = "0.12.3"
@@ -3686,6 +3715,12 @@ version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
+[[package]]
+name = "unicode-width"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6"
+
[[package]]
name = "unicode_categories"
version = "0.1.1"
diff --git a/Cargo.toml b/Cargo.toml
index b5b484e..93a5012 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -23,6 +23,7 @@ env_logger = "0.11.3"
free-icons = "0.7.0"
minijinja = { version = "1.0.14", features = ["loader", "json", "builtins"] }
minijinja-autoreload = "1.0.14"
+pulldown-cmark = "0.11.0"
rand = "0.8.5"
redb = "2.1.0"
serde = { version = "1.0.197", features = ["derive"] }
diff --git a/frontend/editor.ts b/frontend/editor.ts
new file mode 100644
index 0000000..8cd22e6
--- /dev/null
+++ b/frontend/editor.ts
@@ -0,0 +1,44 @@
+import {basicSetup} from "codemirror"
+import {EditorView, keymap} from "@codemirror/view"
+import {indentWithTab} from "@codemirror/commands"
+import {markdown} from "@codemirror/lang-markdown"
+
+export function makeEditor(divSelector, value) {
+ let div = document.querySelector(divSelector);
+ div.classList.remove("hidden");
+ div.classList.remove("h-0");
+
+ let documentTheme = EditorView.theme({
+ "&": {
+ "background-color": "white",
+ },
+ ".cm-editor": {
+ "height": "100%",
+ },
+ ".cm-scroller": {overflow: "auto"}
+ }, {})
+
+ // add a hidden textarea inside the div for form submission
+ let textarea = document.createElement("textarea");
+ textarea.setAttribute("name", "content");
+ textarea.style.display = "none";
+ div.appendChild(textarea);
+
+ let extensions = [
+ basicSetup,
+ keymap.of([indentWithTab]),
+ markdown(),
+ documentTheme,
+ EditorView.lineWrapping,
+ ];
+
+ let view = new EditorView({parent: div, doc: value, extensions})
+
+ textarea.form.addEventListener("submit", () => {
+ textarea.value = view.state.doc.toString()
+ })
+
+ // remove the "hidden" and "h-0" classes from the div
+
+ return view
+}
diff --git a/frontend/main.css b/frontend/main.css
index 6260af8..2cce384 100644
--- a/frontend/main.css
+++ b/frontend/main.css
@@ -2,59 +2,16 @@
@tailwind components;
@tailwind utilities;
-
-
-
-/* Borrowed from https://github.com/Milkdown/milkdown/blob/main/e2e/src/list-item-block/style.css
-which is licensed under MIT. */
-
-.prose :where(li):not(:where([class~="not-prose"] *)) {
- margin-top: 0.5em;
- margin-bottom: 0;
+.prose li {
+ margin-top: 0;
+ margin-bottom: 0;
}
-.prose :where(blockquote):not(:where([class~="not-prose"] *)) {
- font-style: inherit;
- font-weight: inherit;
-}
-
-.prose :where(ul ul, ul ol, ol ul, ol ol):not(:where([class~="not-prose"] *)) {
- margin-top: 0.5em;
- margin-bottom: 0;
-}
-
-.prose ol,
.prose ul {
- list-style: none !important;
- padding: 0;
+ margin-top: 0;
+ margin-bottom: 0;
}
-.prose li p {
- @apply !m-0 !leading-6;
+.prose h1 {
+ margin-bottom: 0.25em;
}
-
-.prose li p + p {
- @apply !mt-2;
-}
-
-.prose li.ProseMirror-selectednode {
- outline: 2px solid #8cf;
-}
-
-.prose li::after {
- all: unset !important;
-}
-
-milkdown-list-item-block .list-item {
- gap: 8px;
-}
-
-milkdown-list-item-block .label-wrapper {
- height: 24px;
- display: inline-flex;
- justify-content: center;
- align-items: center;
- color: darkcyan;
-}
-
-/* End borrowed block. */
diff --git a/frontend/main.ts b/frontend/main.ts
index 83332f4..b873d11 100644
--- a/frontend/main.ts
+++ b/frontend/main.ts
@@ -1,52 +1,2 @@
-import { defaultValueCtx, Editor, rootCtx } from '@milkdown/core';
-import { html } from 'atomico';
-import { listItemBlockComponent, listItemBlockConfig, ListItemBlockConfig, listItemBlockView } from '@milkdown/components/list-item-block'
-import { commonmark } from '@milkdown/preset-commonmark';
-import { gfm } from '@milkdown/preset-gfm';
-import { nord } from '@milkdown/theme-nord'
-import { listener, listenerCtx } from '@milkdown/plugin-listener';
-import '@milkdown/theme-nord/style.css'
-
-function configureListItem(ctx: Ctx) {
- ctx.set(listItemBlockConfig.key, {
- renderLabel: (label: string, listType, checked?: boolean) => {
- if (checked == null) {
- if (listType === 'bullet') {
- return html`•`
- }
-
- return html`${label}`
- } else {
- return html``
- }
- },
- })
-}
-
-function createEditor(rootId, fieldId, content) {
- Editor
- .make()
- .config(ctx => {
- ctx.set(rootCtx, rootId)
- ctx.set(defaultValueCtx, content)
-
- const listener = ctx.get(listenerCtx);
- listener.markdownUpdated((ctx, markdown, prevMarkdown) => {
- if (markdown !== prevMarkdown) {
- console.log(markdown);
- console.log(fieldId);
- document.getElementById(fieldId).value = markdown;
- console.log("updated");
- }
- })
- })
- .config(configureListItem)
- .use(commonmark)
- .use(gfm)
- .use(nord)
- .use(listener)
- .use(listItemBlockComponent)
- .create();
-}
-
-window.createEditor = createEditor;
+import { makeEditor } from './editor';
+window.makeEditor = makeEditor;
diff --git a/package-lock.json b/package-lock.json
index edc791f..2d8ba45 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5,6 +5,7 @@
"packages": {
"": {
"dependencies": {
+ "@codemirror/lang-markdown": "^6.2.5",
"@milkdown/components": "^7.3.6",
"@milkdown/core": "^7.3.6",
"@milkdown/plugin-listener": "^7.3.6",
@@ -13,6 +14,7 @@
"@milkdown/theme-nord": "^7.3.6",
"@prosemirror-adapter/lit": "^0.2.6",
"clsx": "^2.1.1",
+ "codemirror": "^6.0.1",
"tailwindcss": "^3.4.3"
},
"devDependencies": {
@@ -387,11 +389,94 @@
"atomico": "^1.75.1"
}
},
+ "node_modules/@codemirror/autocomplete": {
+ "version": "6.16.2",
+ "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.16.2.tgz",
+ "integrity": "sha512-MjfDrHy0gHKlPWsvSsikhO1+BOh+eBHNgfH1OXs1+DAf30IonQldgMM3kxLDTG9ktE7kDLaA1j/l7KMPA4KNfw==",
+ "dependencies": {
+ "@codemirror/language": "^6.0.0",
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.17.0",
+ "@lezer/common": "^1.0.0"
+ },
+ "peerDependencies": {
+ "@codemirror/language": "^6.0.0",
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.0.0",
+ "@lezer/common": "^1.0.0"
+ }
+ },
+ "node_modules/@codemirror/commands": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.5.0.tgz",
+ "integrity": "sha512-rK+sj4fCAN/QfcY9BEzYMgp4wwL/q5aj/VfNSoH1RWPF9XS/dUwBkvlL3hpWgEjOqlpdN1uLC9UkjJ4tmyjJYg==",
+ "dependencies": {
+ "@codemirror/language": "^6.0.0",
+ "@codemirror/state": "^6.4.0",
+ "@codemirror/view": "^6.0.0",
+ "@lezer/common": "^1.1.0"
+ }
+ },
+ "node_modules/@codemirror/lang-css": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.2.1.tgz",
+ "integrity": "sha512-/UNWDNV5Viwi/1lpr/dIXJNWiwDxpw13I4pTUAsNxZdg6E0mI2kTQb0P2iHczg1Tu+H4EBgJR+hYhKiHKko7qg==",
+ "dependencies": {
+ "@codemirror/autocomplete": "^6.0.0",
+ "@codemirror/language": "^6.0.0",
+ "@codemirror/state": "^6.0.0",
+ "@lezer/common": "^1.0.2",
+ "@lezer/css": "^1.0.0"
+ }
+ },
+ "node_modules/@codemirror/lang-html": {
+ "version": "6.4.9",
+ "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.9.tgz",
+ "integrity": "sha512-aQv37pIMSlueybId/2PVSP6NPnmurFDVmZwzc7jszd2KAF8qd4VBbvNYPXWQq90WIARjsdVkPbw29pszmHws3Q==",
+ "dependencies": {
+ "@codemirror/autocomplete": "^6.0.0",
+ "@codemirror/lang-css": "^6.0.0",
+ "@codemirror/lang-javascript": "^6.0.0",
+ "@codemirror/language": "^6.4.0",
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.17.0",
+ "@lezer/common": "^1.0.0",
+ "@lezer/css": "^1.1.0",
+ "@lezer/html": "^1.3.0"
+ }
+ },
+ "node_modules/@codemirror/lang-javascript": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.2.tgz",
+ "integrity": "sha512-VGQfY+FCc285AhWuwjYxQyUQcYurWlxdKYT4bqwr3Twnd5wP5WSeu52t4tvvuWmljT4EmgEgZCqSieokhtY8hg==",
+ "dependencies": {
+ "@codemirror/autocomplete": "^6.0.0",
+ "@codemirror/language": "^6.6.0",
+ "@codemirror/lint": "^6.0.0",
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.17.0",
+ "@lezer/common": "^1.0.0",
+ "@lezer/javascript": "^1.0.0"
+ }
+ },
+ "node_modules/@codemirror/lang-markdown": {
+ "version": "6.2.5",
+ "resolved": "https://registry.npmjs.org/@codemirror/lang-markdown/-/lang-markdown-6.2.5.tgz",
+ "integrity": "sha512-Hgke565YcO4fd9pe2uLYxnMufHO5rQwRr+AAhFq8ABuhkrjyX8R5p5s+hZUTdV60O0dMRjxKhBLxz8pu/MkUVA==",
+ "dependencies": {
+ "@codemirror/autocomplete": "^6.7.1",
+ "@codemirror/lang-html": "^6.0.0",
+ "@codemirror/language": "^6.3.0",
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.0.0",
+ "@lezer/common": "^1.2.1",
+ "@lezer/markdown": "^1.0.0"
+ }
+ },
"node_modules/@codemirror/language": {
"version": "6.10.1",
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.1.tgz",
"integrity": "sha512-5GrXzrhq6k+gL5fjkAwt90nYDmjlzTIJV8THnxNFtNKWotMIlzzN+CpqxqwXOECnUdOndmSeWntVrVcv5axWRQ==",
- "peer": true,
"dependencies": {
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.23.0",
@@ -401,17 +486,35 @@
"style-mod": "^4.0.0"
}
},
+ "node_modules/@codemirror/lint": {
+ "version": "6.8.0",
+ "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.0.tgz",
+ "integrity": "sha512-lsFofvaw0lnPRJlQylNsC4IRt/1lI4OD/yYslrSGVndOJfStc58v+8p9dgGiD90ktOfL7OhBWns1ZETYgz0EJA==",
+ "dependencies": {
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.0.0",
+ "crelt": "^1.0.5"
+ }
+ },
+ "node_modules/@codemirror/search": {
+ "version": "6.5.6",
+ "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.6.tgz",
+ "integrity": "sha512-rpMgcsh7o0GuCDUXKPvww+muLA1pDJaFrpq/CCHtpQJYz8xopu4D1hPcKRoDD0YlF8gZaqTNIRa4VRBWyhyy7Q==",
+ "dependencies": {
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.0.0",
+ "crelt": "^1.0.5"
+ }
+ },
"node_modules/@codemirror/state": {
"version": "6.4.1",
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.1.tgz",
- "integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==",
- "peer": true
+ "integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A=="
},
"node_modules/@codemirror/view": {
"version": "6.26.3",
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.26.3.tgz",
"integrity": "sha512-gmqxkPALZjkgSxIeeweY/wGQXBfwTUaLs8h7OKtSwfbj9Ct3L11lD+u1sS7XHppxFQoMDiMDp07P9f3I2jWOHw==",
- "peer": true,
"dependencies": {
"@codemirror/state": "^6.4.0",
"style-mod": "^4.1.0",
@@ -876,27 +979,63 @@
"node_modules/@lezer/common": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.1.tgz",
- "integrity": "sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ==",
- "peer": true
+ "integrity": "sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ=="
+ },
+ "node_modules/@lezer/css": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.1.8.tgz",
+ "integrity": "sha512-7JhxupKuMBaWQKjQoLtzhGj83DdnZY9MckEOG5+/iLKNK2ZJqKc6hf6uc0HjwCX7Qlok44jBNqZhHKDhEhZYLA==",
+ "dependencies": {
+ "@lezer/common": "^1.2.0",
+ "@lezer/highlight": "^1.0.0",
+ "@lezer/lr": "^1.0.0"
+ }
},
"node_modules/@lezer/highlight": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.0.tgz",
"integrity": "sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA==",
- "peer": true,
"dependencies": {
"@lezer/common": "^1.0.0"
}
},
+ "node_modules/@lezer/html": {
+ "version": "1.3.10",
+ "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.10.tgz",
+ "integrity": "sha512-dqpT8nISx/p9Do3AchvYGV3qYc4/rKr3IBZxlHmpIKam56P47RSHkSF5f13Vu9hebS1jM0HmtJIwLbWz1VIY6w==",
+ "dependencies": {
+ "@lezer/common": "^1.2.0",
+ "@lezer/highlight": "^1.0.0",
+ "@lezer/lr": "^1.0.0"
+ }
+ },
+ "node_modules/@lezer/javascript": {
+ "version": "1.4.16",
+ "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.16.tgz",
+ "integrity": "sha512-84UXR3N7s11MPQHWgMnjb9571fr19MmXnr5zTv2XX0gHXXUvW3uPJ8GCjKrfTXmSdfktjRK0ayKklw+A13rk4g==",
+ "dependencies": {
+ "@lezer/common": "^1.2.0",
+ "@lezer/highlight": "^1.1.3",
+ "@lezer/lr": "^1.3.0"
+ }
+ },
"node_modules/@lezer/lr": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.0.tgz",
"integrity": "sha512-Wst46p51km8gH0ZUmeNrtpRYmdlRHUpN1DQd3GFAyKANi8WVz8c2jHYTf1CVScFaCjQw1iO3ZZdqGDxQPRErTg==",
- "peer": true,
"dependencies": {
"@lezer/common": "^1.0.0"
}
},
+ "node_modules/@lezer/markdown": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@lezer/markdown/-/markdown-1.3.0.tgz",
+ "integrity": "sha512-ErbEQ15eowmJUyT095e9NJc3BI9yZ894fjSDtHftD0InkfUBGgnKSU6dvan9jqsZuNHg2+ag/1oyDRxNsENupQ==",
+ "dependencies": {
+ "@lezer/common": "^1.0.0",
+ "@lezer/highlight": "^1.0.0"
+ }
+ },
"node_modules/@lit-labs/context": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/@lit-labs/context/-/context-0.3.3.tgz",
@@ -1535,6 +1674,20 @@
"node": ">=6"
}
},
+ "node_modules/codemirror": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz",
+ "integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==",
+ "dependencies": {
+ "@codemirror/autocomplete": "^6.0.0",
+ "@codemirror/commands": "^6.0.0",
+ "@codemirror/language": "^6.0.0",
+ "@codemirror/lint": "^6.0.0",
+ "@codemirror/search": "^6.0.0",
+ "@codemirror/state": "^6.0.0",
+ "@codemirror/view": "^6.0.0"
+ }
+ },
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@@ -1562,6 +1715,11 @@
"node": ">= 6"
}
},
+ "node_modules/crelt": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
+ "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="
+ },
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -3618,8 +3776,7 @@
"node_modules/style-mod": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz",
- "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==",
- "peer": true
+ "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw=="
},
"node_modules/sucrase": {
"version": "3.35.0",
@@ -3859,8 +4016,7 @@
"node_modules/w3c-keyname": {
"version": "2.2.8",
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
- "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",
- "peer": true
+ "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="
},
"node_modules/which": {
"version": "2.0.2",
diff --git a/package.json b/package.json
index d595706..0373cb9 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,7 @@
"css": "tailwindcss -i ./frontend/main.css -o ./static/style.css"
},
"dependencies": {
+ "@codemirror/lang-markdown": "^6.2.5",
"@milkdown/components": "^7.3.6",
"@milkdown/core": "^7.3.6",
"@milkdown/plugin-listener": "^7.3.6",
@@ -21,6 +22,7 @@
"@milkdown/theme-nord": "^7.3.6",
"@prosemirror-adapter/lit": "^0.2.6",
"clsx": "^2.1.1",
+ "codemirror": "^6.0.1",
"tailwindcss": "^3.4.3"
}
}
diff --git a/src/handler/documents.rs b/src/handler/documents.rs
index 449219f..5170155 100644
--- a/src/handler/documents.rs
+++ b/src/handler/documents.rs
@@ -104,6 +104,45 @@ pub async fn create_document_submit(
Ok(Redirect::to("/documents").into_response())
}
+pub async fn view_document_page(
+ State(provider): State,
+ auth_session: AuthSession,
+ Path((id,)): Path<(Uuid,)>,
+) -> Result {
+ let user = match auth_session.user {
+ Some(user) => user,
+ None => return Ok(Redirect::to("/login").into_response()),
+ };
+
+ let mut db = provider.db_pool.get().map_err(internal_error)?;
+
+ let document_allowed =
+ permissions::q::check_user_document(&mut db, &user.id, &id.to_string(), Permission::Write)
+ .map_err(internal_error)?;
+
+ if !document_allowed {
+ return Err((StatusCode::FORBIDDEN, "permission denied".to_owned()));
+ }
+
+ let document = match documents::q::by_id(&mut db, &id.to_string()).map_err(internal_error)? {
+ Some(doc) => doc,
+ None => return Err((StatusCode::NOT_FOUND, "document not found".to_owned())),
+ };
+ let projects =
+ permissions::q::accessible_projects(&mut db, &user.id).map_err(internal_error)?;
+
+ let rendered_document = document.render_html();
+
+ let values = context! {
+ user => user,
+ document => document,
+ projects => projects,
+ rendered_document => rendered_document,
+ };
+
+ provider.render_resp("documents/view_document.html", values)
+}
+
pub async fn edit_document_page(
State(provider): State,
auth_session: AuthSession,
@@ -176,5 +215,6 @@ pub async fn edit_document_submit(
)
.map_err(internal_error)?;
- Ok(Redirect::to("/documents").into_response())
+ let view_url = format!("/documents/view/{}", document_id);
+ Ok(Redirect::to(&view_url).into_response())
}
diff --git a/src/handler/login.rs b/src/handler/login.rs
index 4dbfb25..1a7d94f 100644
--- a/src/handler/login.rs
+++ b/src/handler/login.rs
@@ -48,7 +48,7 @@ pub async fn login_submit(
Form(creds): Form,
) -> Result {
if let Some(user) = auth_session.authenticate(creds).await.map_err(internal_error)? {
- let _ = auth_session.login(&user).await.map_err(internal_error)?;
+ auth_session.login(&user).await.map_err(internal_error)?;
Ok(Redirect::to("/").into_response())
} else {
render_login_page(&provider, "", "", Some(LOGIN_ERROR_MSG))
diff --git a/src/models/documents.rs b/src/models/documents.rs
index 0067275..5674e8a 100644
--- a/src/models/documents.rs
+++ b/src/models/documents.rs
@@ -1,4 +1,5 @@
use diesel::prelude::*;
+use pulldown_cmark as markdown;
use serde::Serialize;
use uuid::Uuid;
@@ -16,6 +17,21 @@ pub struct Document {
pub content: String,
}
+impl Document {
+ pub fn render_html(&self) -> String {
+ let mut options = markdown::Options::empty();
+ options.insert(markdown::Options::ENABLE_TASKLISTS);
+ options.insert(markdown::Options::ENABLE_STRIKETHROUGH);
+
+ let parser = markdown::Parser::new_ext(&self.content, options);
+
+ let mut html_output = String::new();
+ markdown::html::push_html(&mut html_output, parser);
+
+ html_output
+ }
+}
+
#[derive(Insertable)]
#[diesel(table_name = crate::schema::documents)]
pub struct NewDocument {
diff --git a/src/server.rs b/src/server.rs
index 873ecbd..490c518 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -17,7 +17,7 @@ use crate::config::CommandLineOptions;
use crate::db;
use crate::handler::documents::{
create_document_page, create_document_submit, documents_page, edit_document_page,
- edit_document_submit,
+ edit_document_submit, view_document_page,
};
use crate::handler::home::home_page;
use crate::handler::login::logout;
@@ -66,6 +66,7 @@ pub async fn run() -> Result<()> {
.route("/documents", get(documents_page))
.route("/documents/new", get(create_document_page))
.route("/documents/new", post(create_document_submit))
+ .route("/documents/view/:id", get(view_document_page))
.route("/documents/edit/:id", get(edit_document_page))
.route("/documents/edit/:id", post(edit_document_submit))
.layer(trace_layer)
diff --git a/static/main.css b/static/main.css
deleted file mode 100644
index 0456fee..0000000
--- a/static/main.css
+++ /dev/null
@@ -1,196 +0,0 @@
-/* node_modules/@milkdown/theme-nord/lib/style.css */
-.ProseMirror {
- position: relative;
- word-wrap: break-word;
- white-space: pre-wrap;
- white-space: break-spaces;
- font-variant-ligatures: none;
- font-feature-settings: "liga" 0;
-}
-.ProseMirror pre {
- white-space: pre-wrap;
-}
-.ProseMirror li {
- position: relative;
-}
-.ProseMirror-hideselection *::selection {
- background: transparent;
-}
-.ProseMirror-hideselection *::-moz-selection {
- background: transparent;
-}
-.ProseMirror-hideselection {
- caret-color: transparent;
-}
-.ProseMirror [draggable][contenteditable=false] {
- -webkit-user-select: text;
- -moz-user-select: text;
- user-select: text;
-}
-.ProseMirror-selectednode {
- outline: 2px solid #8cf;
-}
-li.ProseMirror-selectednode {
- outline: none;
-}
-li.ProseMirror-selectednode:after {
- content: "";
- position: absolute;
- left: -32px;
- right: -2px;
- top: -2px;
- bottom: -2px;
- border: 2px solid #8cf;
- pointer-events: none;
-}
-img.ProseMirror-separator {
- display: inline !important;
- border: none !important;
- margin: 0 !important;
-}
-.ProseMirror .tableWrapper {
- overflow-x: auto;
-}
-.ProseMirror table {
- border-collapse: collapse;
- table-layout: fixed;
- width: 100%;
- overflow: hidden;
-}
-.ProseMirror td,
-.ProseMirror th {
- vertical-align: top;
- box-sizing: border-box;
- position: relative;
-}
-.ProseMirror .column-resize-handle {
- position: absolute;
- right: -2px;
- top: 0;
- bottom: 0;
- width: 4px;
- z-index: 20;
- background-color: #adf;
- pointer-events: none;
-}
-.ProseMirror.resize-cursor {
- cursor: ew-resize;
- cursor: col-resize;
-}
-.ProseMirror .selectedCell:after {
- z-index: 2;
- position: absolute;
- content: "";
- left: 0;
- right: 0;
- top: 0;
- bottom: 0;
- background: #c8c8ff66;
- pointer-events: none;
-}
-.milkdown-theme-nord blockquote {
- border-left-width: 4px;
- --tw-border-opacity: 1;
- border-color: rgb(94 129 172 / var(--tw-border-opacity));
- padding-left: 1rem;
- font-family:
- ui-serif,
- Georgia,
- Cambria,
- Times New Roman,
- Times,
- serif;
- font-style: normal;
-}
-.milkdown-theme-nord code {
- font-family:
- ui-monospace,
- SFMono-Regular,
- Menlo,
- Monaco,
- Consolas,
- Liberation Mono,
- Courier New,
- monospace;
- font-weight: 400;
- --tw-text-opacity: 1;
- color: rgb(94 129 172 / var(--tw-text-opacity));
-}
-.milkdown-theme-nord pre code {
- color: inherit;
-}
-.milkdown-theme-nord img {
- margin-top: 0 !important;
- margin-bottom: 0 !important;
- display: inline-block;
- max-width: 100%;
-}
-.milkdown-theme-nord.prose :where(blockquote):not(:where([class~=not-prose] *)) {
- font-weight: 400;
-}
-.milkdown-theme-nord.prose :where(ol > li):not(:where([class~=not-prose] *))::marker,
-.milkdown-theme-nord.prose :where(ul > li):not(:where([class~=not-prose] *))::marker {
- --tw-text-opacity: 1;
- color: rgb(94 129 172 / var(--tw-text-opacity));
-}
-.milkdown-theme-nord.prose :where(blockquote p:first-of-type):not(:where([class~=not-prose] *)):before,
-.milkdown-theme-nord.prose :where(blockquote p:first-of-type):not(:where([class~=not-prose] *)):after {
- content: "";
-}
-.milkdown-theme-nord.prose :where(code):not(:where([class~=not-prose] *)):before,
-.milkdown-theme-nord.prose :where(code):not(:where([class~=not-prose] *)):after {
- content: "";
-}
-.milkdown-theme-nord.prose .tableWrapper {
- position: relative;
- margin-bottom: .5rem;
- overflow-x: auto;
-}
-.milkdown-theme-nord.prose table {
- margin: 1rem !important;
- overflow: visible !important;
- font-size: .875rem;
- line-height: 1.25rem;
- --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);
- --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);
- box-shadow:
- var(--tw-ring-offset-shadow, 0 0 #0000),
- var(--tw-ring-shadow, 0 0 #0000),
- var(--tw-shadow);
-}
-@media (min-width: 640px) {
- .milkdown-theme-nord.prose table {
- border-radius: .5rem;
- }
-}
-.milkdown-theme-nord.prose td,
-.milkdown-theme-nord.prose th {
- padding: .75rem 1.5rem !important;
-}
-.milkdown-theme-nord.prose tr {
- border-bottom-width: 1px;
- --tw-border-opacity: 1;
- border-color: rgb(229 231 235 / var(--tw-border-opacity));
-}
-:is(.dark .milkdown-theme-nord.prose tr) {
- --tw-border-opacity: 1;
- border-color: rgb(75 85 99 / var(--tw-border-opacity));
-}
-.milkdown-theme-nord.prose :where(td, th) p {
- margin: 0 !important;
-}
-.milkdown-theme-nord.prose :where(td, th):nth-child(odd) {
- --tw-bg-opacity: 1;
- background-color: rgb(249 250 251 / var(--tw-bg-opacity));
-}
-:is(.dark .milkdown-theme-nord.prose :where(td, th):nth-child(odd)) {
- --tw-bg-opacity: 1;
- background-color: rgb(17 24 39 / var(--tw-bg-opacity));
-}
-.milkdown-theme-nord.prose.ProseMirror .selectedCell:after {
- background-color: #88c0d04d;
-}
-.milkdown-theme-nord.prose br[data-is-inline=true],
-.milkdown-theme-nord.prose br[data-is-inline=true]:after {
- content: " ";
-}
diff --git a/static/main.js b/static/main.js
index a0e49f3..6d07f92 100644
--- a/static/main.js
+++ b/static/main.js
@@ -1,20584 +1,3517 @@
(() => {
- var __create = Object.create;
- var __defProp = Object.defineProperty;
- var __defProps = Object.defineProperties;
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
- var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
- var __getOwnPropNames = Object.getOwnPropertyNames;
- var __getOwnPropSymbols = Object.getOwnPropertySymbols;
- var __getProtoOf = Object.getPrototypeOf;
- var __hasOwnProp = Object.prototype.hasOwnProperty;
- var __propIsEnum = Object.prototype.propertyIsEnumerable;
- var __typeError = (msg) => {
- throw TypeError(msg);
- };
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
- var __spreadValues = (a2, b4) => {
- for (var prop in b4 || (b4 = {}))
- if (__hasOwnProp.call(b4, prop))
- __defNormalProp(a2, prop, b4[prop]);
- if (__getOwnPropSymbols)
- for (var prop of __getOwnPropSymbols(b4)) {
- if (__propIsEnum.call(b4, prop))
- __defNormalProp(a2, prop, b4[prop]);
- }
- return a2;
- };
- var __spreadProps = (a2, b4) => __defProps(a2, __getOwnPropDescs(b4));
- var __objRest = (source, exclude) => {
- var target = {};
- for (var prop in source)
- if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
- target[prop] = source[prop];
- if (source != null && __getOwnPropSymbols)
- for (var prop of __getOwnPropSymbols(source)) {
- if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
- target[prop] = source[prop];
- }
- return target;
- };
- var __commonJS = (cb, mod) => function __require() {
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
- };
- var __export = (target, all2) => {
- for (var name in all2)
- __defProp(target, name, { get: all2[name], enumerable: true });
- };
- var __copyProps = (to, from, except, desc) => {
- if (from && typeof from === "object" || typeof from === "function") {
- for (let key of __getOwnPropNames(from))
- if (!__hasOwnProp.call(to, key) && key !== except)
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
- }
- return to;
- };
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
- // If the importer is in node compatibility mode or this is not an ESM
- // file that has been converted to a CommonJS file using a Babel-
- // compatible transform (i.e. "__esModule" has not been set), then set
- // "default" to the CommonJS "module.exports" for node compatibility.
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
- mod
- ));
- var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
- var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
- var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
- var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
-
- // node_modules/extend/index.js
- var require_extend = __commonJS({
- "node_modules/extend/index.js"(exports, module) {
- "use strict";
- var hasOwn = Object.prototype.hasOwnProperty;
- var toStr = Object.prototype.toString;
- var defineProperty = Object.defineProperty;
- var gOPD = Object.getOwnPropertyDescriptor;
- var isArray2 = function isArray3(arr) {
- if (typeof Array.isArray === "function") {
- return Array.isArray(arr);
- }
- return toStr.call(arr) === "[object Array]";
- };
- var isPlainObject2 = function isPlainObject3(obj) {
- if (!obj || toStr.call(obj) !== "[object Object]") {
- return false;
- }
- var hasOwnConstructor = hasOwn.call(obj, "constructor");
- var hasIsPrototypeOf = obj.constructor && obj.constructor.prototype && hasOwn.call(obj.constructor.prototype, "isPrototypeOf");
- if (obj.constructor && !hasOwnConstructor && !hasIsPrototypeOf) {
- return false;
- }
- var key;
- for (key in obj) {
- }
- return typeof key === "undefined" || hasOwn.call(obj, key);
- };
- var setProperty2 = function setProperty3(target, options2) {
- if (defineProperty && options2.name === "__proto__") {
- defineProperty(target, options2.name, {
- enumerable: true,
- configurable: true,
- value: options2.newValue,
- writable: true
- });
- } else {
- target[options2.name] = options2.newValue;
- }
- };
- var getProperty = function getProperty2(obj, name) {
- if (name === "__proto__") {
- if (!hasOwn.call(obj, name)) {
- return void 0;
- } else if (gOPD) {
- return gOPD(obj, name).value;
- }
- }
- return obj[name];
- };
- module.exports = function extend2() {
- var options2, name, src, copy2, copyIsArray, clone;
- var target = arguments[0];
- var i2 = 1;
- var length = arguments.length;
- var deep = false;
- if (typeof target === "boolean") {
- deep = target;
- target = arguments[1] || {};
- i2 = 2;
- }
- if (target == null || typeof target !== "object" && typeof target !== "function") {
- target = {};
- }
- for (; i2 < length; ++i2) {
- options2 = arguments[i2];
- if (options2 != null) {
- for (name in options2) {
- src = getProperty(target, name);
- copy2 = getProperty(options2, name);
- if (target !== copy2) {
- if (deep && copy2 && (isPlainObject2(copy2) || (copyIsArray = isArray2(copy2)))) {
- if (copyIsArray) {
- copyIsArray = false;
- clone = src && isArray2(src) ? src : [];
- } else {
- clone = src && isPlainObject2(src) ? src : {};
- }
- setProperty2(target, { name, newValue: extend2(deep, clone, copy2) });
- } else if (typeof copy2 !== "undefined") {
- setProperty2(target, { name, newValue: copy2 });
- }
- }
- }
- }
- }
- return target;
- };
- }
- });
-
- // node_modules/lodash.debounce/index.js
- var require_lodash = __commonJS({
- "node_modules/lodash.debounce/index.js"(exports, module) {
- var FUNC_ERROR_TEXT = "Expected a function";
- var NAN = 0 / 0;
- var symbolTag = "[object Symbol]";
- var reTrim = /^\s+|\s+$/g;
- var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
- var reIsBinary = /^0b[01]+$/i;
- var reIsOctal = /^0o[0-7]+$/i;
- var freeParseInt = parseInt;
- var freeGlobal = typeof global == "object" && global && global.Object === Object && global;
- var freeSelf = typeof self == "object" && self && self.Object === Object && self;
- var root2 = freeGlobal || freeSelf || Function("return this")();
- var objectProto = Object.prototype;
- var objectToString = objectProto.toString;
- var nativeMax = Math.max;
- var nativeMin = Math.min;
- var now = function() {
- return root2.Date.now();
- };
- function debounce(func, wait, options2) {
- var lastArgs, lastThis, maxWait, result, timerId, lastCallTime, lastInvokeTime = 0, leading = false, maxing = false, trailing = true;
- if (typeof func != "function") {
- throw new TypeError(FUNC_ERROR_TEXT);
- }
- wait = toNumber(wait) || 0;
- if (isObject2(options2)) {
- leading = !!options2.leading;
- maxing = "maxWait" in options2;
- maxWait = maxing ? nativeMax(toNumber(options2.maxWait) || 0, wait) : maxWait;
- trailing = "trailing" in options2 ? !!options2.trailing : trailing;
- }
- function invokeFunc(time) {
- var args = lastArgs, thisArg = lastThis;
- lastArgs = lastThis = void 0;
- lastInvokeTime = time;
- result = func.apply(thisArg, args);
- return result;
- }
- function leadingEdge(time) {
- lastInvokeTime = time;
- timerId = setTimeout(timerExpired, wait);
- return leading ? invokeFunc(time) : result;
- }
- function remainingWait(time) {
- var timeSinceLastCall = time - lastCallTime, timeSinceLastInvoke = time - lastInvokeTime, result2 = wait - timeSinceLastCall;
- return maxing ? nativeMin(result2, maxWait - timeSinceLastInvoke) : result2;
- }
- function shouldInvoke(time) {
- var timeSinceLastCall = time - lastCallTime, timeSinceLastInvoke = time - lastInvokeTime;
- return lastCallTime === void 0 || timeSinceLastCall >= wait || timeSinceLastCall < 0 || maxing && timeSinceLastInvoke >= maxWait;
- }
- function timerExpired() {
- var time = now();
- if (shouldInvoke(time)) {
- return trailingEdge(time);
- }
- timerId = setTimeout(timerExpired, remainingWait(time));
- }
- function trailingEdge(time) {
- timerId = void 0;
- if (trailing && lastArgs) {
- return invokeFunc(time);
- }
- lastArgs = lastThis = void 0;
- return result;
- }
- function cancel() {
- if (timerId !== void 0) {
- clearTimeout(timerId);
- }
- lastInvokeTime = 0;
- lastArgs = lastCallTime = lastThis = timerId = void 0;
- }
- function flush() {
- return timerId === void 0 ? result : trailingEdge(now());
- }
- function debounced() {
- var time = now(), isInvoking = shouldInvoke(time);
- lastArgs = arguments;
- lastThis = this;
- lastCallTime = time;
- if (isInvoking) {
- if (timerId === void 0) {
- return leadingEdge(lastCallTime);
- }
- if (maxing) {
- timerId = setTimeout(timerExpired, wait);
- return invokeFunc(lastCallTime);
- }
- }
- if (timerId === void 0) {
- timerId = setTimeout(timerExpired, wait);
- }
- return result;
- }
- debounced.cancel = cancel;
- debounced.flush = flush;
- return debounced;
- }
- function isObject2(value) {
- var type = typeof value;
- return !!value && (type == "object" || type == "function");
- }
- function isObjectLike(value) {
- return !!value && typeof value == "object";
- }
- function isSymbol(value) {
- return typeof value == "symbol" || isObjectLike(value) && objectToString.call(value) == symbolTag;
- }
- function toNumber(value) {
- if (typeof value == "number") {
- return value;
- }
- if (isSymbol(value)) {
- return NAN;
- }
- if (isObject2(value)) {
- var other = typeof value.valueOf == "function" ? value.valueOf() : value;
- value = isObject2(other) ? other + "" : other;
- }
- if (typeof value != "string") {
- return value === 0 ? value : +value;
- }
- value = value.replace(reTrim, "");
- var isBinary = reIsBinary.test(value);
- return isBinary || reIsOctal.test(value) ? freeParseInt(value.slice(2), isBinary ? 2 : 8) : reIsBadHex.test(value) ? NAN : +value;
- }
- module.exports = debounce;
- }
- });
-
- // node_modules/@milkdown/exception/lib/index.es.js
- var n = /* @__PURE__ */ ((e2) => (e2.docTypeError = "docTypeError", e2.contextNotFound = "contextNotFound", e2.timerNotFound = "timerNotFound", e2.ctxCallOutOfScope = "ctxCallOutOfScope", e2.createNodeInParserFail = "createNodeInParserFail", e2.stackOverFlow = "stackOverFlow", e2.parserMatchError = "parserMatchError", e2.serializerMatchError = "serializerMatchError", e2.getAtomFromSchemaFail = "getAtomFromSchemaFail", e2.expectDomTypeError = "expectDomTypeError", e2.callCommandBeforeEditorView = "callCommandBeforeEditorView", e2.missingRootElement = "missingRootElement", e2.missingNodeInSchema = "missingNodeInSchema", e2.missingMarkInSchema = "missingMarkInSchema", e2.ctxNotBind = "ctxNotBind", e2.missingYjsDoc = "missingYjsDoc", e2))(n || {});
- var t = class extends Error {
- constructor(o2, a2) {
- super(a2), this.name = "MilkdownError", this.code = o2;
- }
- };
- var u = (e2, o2) => typeof o2 == "function" ? "[Function]" : o2;
- var i = (e2) => JSON.stringify(e2, u);
- function l(e2) {
- return new t(n.docTypeError, `Doc type error, unsupported type: ${i(e2)}`);
- }
- function d(e2) {
- return new t(n.contextNotFound, `Context "${e2}" not found, do you forget to inject it?`);
- }
- function f(e2) {
- return new t(n.timerNotFound, `Timer "${e2}" not found, do you forget to record it?`);
- }
- function p() {
- return new t(n.ctxCallOutOfScope, "Should not call a context out of the plugin.");
- }
- function g(...e2) {
- const o2 = e2.reduce((a2, c6) => {
- if (!c6)
- return a2;
- const s2 = (r3) => Array.isArray(r3) ? r3.map((m3) => s2(m3)).join(", ") : r3.toJSON ? i(r3.toJSON()) : r3.spec ? i(r3.spec) : r3.toString();
- return `${a2}, ${s2(c6)}`;
- }, "Create prosemirror node from remark failed in parser");
- return new t(n.createNodeInParserFail, o2);
- }
- function h() {
- return new t(n.stackOverFlow, "Stack over flow, cannot pop on an empty stack.");
- }
- function w(e2) {
- return new t(n.parserMatchError, `Cannot match target parser for node: ${i(e2)}.`);
- }
- function F(e2) {
- return new t(n.serializerMatchError, `Cannot match target serializer for node: ${i(e2)}.`);
- }
- function S(e2) {
- return new t(n.expectDomTypeError, `Expect to be a dom, but get: ${i(e2)}.`);
- }
- function y() {
- return new t(
- n.callCommandBeforeEditorView,
- "You're trying to call a command before editor view initialized, make sure to get commandManager from ctx after editor view has been initialized"
- );
- }
- function M(e2) {
- return new t(
- n.missingNodeInSchema,
- `Missing node in schema, milkdown cannot find "${e2}" in schema.`
- );
- }
- function x(e2) {
- return new t(
- n.missingMarkInSchema,
- `Missing mark in schema, milkdown cannot find "${e2}" in schema.`
- );
- }
-
- // node_modules/@milkdown/ctx/lib/index.es.js
- var P = (o2, s2, i2) => {
- if (!s2.has(o2))
- throw TypeError("Cannot " + i2);
- };
- var e = (o2, s2, i2) => (P(o2, s2, "read from private field"), i2 ? i2.call(o2) : s2.get(o2));
- var a = (o2, s2, i2) => {
- if (s2.has(o2))
- throw TypeError("Cannot add the same private member more than once");
- s2 instanceof WeakSet ? s2.add(o2) : s2.set(o2, i2);
- };
- var n2 = (o2, s2, i2, r3) => (P(o2, s2, "write to private field"), r3 ? r3.call(o2, i2) : s2.set(o2, i2), i2);
- var G = class {
- constructor() {
- this.sliceMap = /* @__PURE__ */ new Map(), this.get = (s2) => {
- const i2 = typeof s2 == "string" ? [...this.sliceMap.values()].find((r3) => r3.type.name === s2) : this.sliceMap.get(s2.id);
- if (!i2) {
- const r3 = typeof s2 == "string" ? s2 : s2.name;
- throw d(r3);
- }
- return i2;
- }, this.remove = (s2) => {
- const i2 = typeof s2 == "string" ? [...this.sliceMap.values()].find((r3) => r3.type.name === s2) : this.sliceMap.get(s2.id);
- i2 && this.sliceMap.delete(i2.type.id);
- }, this.has = (s2) => typeof s2 == "string" ? [...this.sliceMap.values()].some((i2) => i2.type.name === s2) : this.sliceMap.has(s2.id);
- }
- };
- var u2;
- var m;
- var y2;
- var V = class {
- /// @internal
- constructor(s2, i2, r3) {
- a(this, u2, void 0);
- a(this, m, void 0);
- a(this, y2, void 0);
- n2(this, u2, []), n2(this, y2, () => {
- e(this, u2).forEach((t2) => t2(e(this, m)));
- }), this.set = (t2) => {
- n2(this, m, t2), e(this, y2).call(this);
- }, this.get = () => e(this, m), this.update = (t2) => {
- n2(this, m, t2(e(this, m))), e(this, y2).call(this);
- }, this.type = r3, n2(this, m, i2), s2.set(r3.id, this);
- }
- /// Add a watcher for changes in the slice.
- /// Returns a function to remove the watcher.
- on(s2) {
- return e(this, u2).push(s2), () => {
- n2(this, u2, e(this, u2).filter((i2) => i2 !== s2));
- };
- }
- /// Add a one-time watcher for changes in the slice.
- /// The watcher will be removed after it is called.
- /// Returns a function to remove the watcher.
- once(s2) {
- const i2 = this.on((r3) => {
- s2(r3), i2();
- });
- return i2;
- }
- /// Remove a watcher.
- off(s2) {
- n2(this, u2, e(this, u2).filter((i2) => i2 !== s2));
- }
- /// Remove all watchers.
- offAll() {
- n2(this, u2, []);
- }
- };
- u2 = /* @__PURE__ */ new WeakMap(), m = /* @__PURE__ */ new WeakMap(), y2 = /* @__PURE__ */ new WeakMap();
- var W = class {
- /// Create a slice type with a default value and a name.
- /// The name should be unique in the container.
- constructor(s2, i2) {
- this.id = Symbol(`Context-${i2}`), this.name = i2, this._defaultValue = s2, this._typeInfo = () => {
- throw p();
- };
- }
- /// Create a slice with a container.
- /// You can also pass a value to override the default value.
- create(s2, i2 = this._defaultValue) {
- return new V(s2, i2, this);
- }
- };
- var H = (o2, s2) => new W(o2, s2);
- var D;
- var x2;
- var R;
- var w2;
- var S2;
- var f2;
- var M2;
- var T;
- var j;
- var _ = class {
- /// Create an inspector with container, clock and metadata.
- constructor(s2, i2, r3) {
- a(this, D, void 0);
- a(this, x2, void 0);
- a(this, R, void 0);
- a(this, w2, void 0);
- a(this, S2, void 0);
- a(this, f2, void 0);
- a(this, M2, void 0);
- a(this, T, void 0);
- a(this, j, void 0);
- n2(this, w2, /* @__PURE__ */ new Set()), n2(this, S2, /* @__PURE__ */ new Set()), n2(this, f2, /* @__PURE__ */ new Map()), n2(this, M2, /* @__PURE__ */ new Map()), this.read = () => ({
- metadata: e(this, D),
- injectedSlices: [...e(this, w2)].map((t2) => ({
- name: typeof t2 == "string" ? t2 : t2.name,
- value: e(this, T).call(this, t2)
- })),
- consumedSlices: [...e(this, S2)].map((t2) => ({
- name: typeof t2 == "string" ? t2 : t2.name,
- value: e(this, T).call(this, t2)
- })),
- recordedTimers: [...e(this, f2)].map(([t2, { duration: h5 }]) => ({
- name: t2.name,
- duration: h5,
- status: e(this, j).call(this, t2)
- })),
- waitTimers: [...e(this, M2)].map(([t2, { duration: h5 }]) => ({
- name: t2.name,
- duration: h5,
- status: e(this, j).call(this, t2)
- }))
- }), this.onRecord = (t2) => {
- e(this, f2).set(t2, { start: Date.now(), duration: 0 });
- }, this.onClear = (t2) => {
- e(this, f2).delete(t2);
- }, this.onDone = (t2) => {
- const h5 = e(this, f2).get(t2);
- h5 && (h5.duration = Date.now() - h5.start);
- }, this.onWait = (t2, h5) => {
- const v4 = Date.now();
- h5.finally(() => {
- e(this, M2).set(t2, { duration: Date.now() - v4 });
- });
- }, this.onInject = (t2) => {
- e(this, w2).add(t2);
- }, this.onRemove = (t2) => {
- e(this, w2).delete(t2);
- }, this.onUse = (t2) => {
- e(this, S2).add(t2);
- }, n2(this, T, (t2) => e(this, x2).get(t2).get()), n2(this, j, (t2) => e(this, R).get(t2).status), n2(this, x2, s2), n2(this, R, i2), n2(this, D, r3);
- }
- };
- D = /* @__PURE__ */ new WeakMap(), x2 = /* @__PURE__ */ new WeakMap(), R = /* @__PURE__ */ new WeakMap(), w2 = /* @__PURE__ */ new WeakMap(), S2 = /* @__PURE__ */ new WeakMap(), f2 = /* @__PURE__ */ new WeakMap(), M2 = /* @__PURE__ */ new WeakMap(), T = /* @__PURE__ */ new WeakMap(), j = /* @__PURE__ */ new WeakMap();
- var d2;
- var l2;
- var b;
- var c;
- var L = class L2 {
- /// Create a ctx object with container and clock.
- constructor(s2, i2, r3) {
- a(this, d2, void 0);
- a(this, l2, void 0);
- a(this, b, void 0);
- a(this, c, void 0);
- this.produce = (t2) => t2 && Object.keys(t2).length ? new L2(e(this, d2), e(this, l2), __spreadValues({}, t2)) : this, this.inject = (t2, h5) => {
- var O4;
- const v4 = t2.create(e(this, d2).sliceMap);
- return h5 != null && v4.set(h5), (O4 = e(this, c)) == null || O4.onInject(t2), this;
- }, this.remove = (t2) => {
- var h5;
- return e(this, d2).remove(t2), (h5 = e(this, c)) == null || h5.onRemove(t2), this;
- }, this.record = (t2) => {
- var h5;
- return t2.create(e(this, l2).store), (h5 = e(this, c)) == null || h5.onRecord(t2), this;
- }, this.clearTimer = (t2) => {
- var h5;
- return e(this, l2).remove(t2), (h5 = e(this, c)) == null || h5.onClear(t2), this;
- }, this.isInjected = (t2) => e(this, d2).has(t2), this.isRecorded = (t2) => e(this, l2).has(t2), this.use = (t2) => {
- var h5;
- return (h5 = e(this, c)) == null || h5.onUse(t2), e(this, d2).get(t2);
- }, this.get = (t2) => this.use(t2).get(), this.set = (t2, h5) => this.use(t2).set(h5), this.update = (t2, h5) => this.use(t2).update(h5), this.timer = (t2) => e(this, l2).get(t2), this.done = (t2) => {
- var h5;
- this.timer(t2).done(), (h5 = e(this, c)) == null || h5.onDone(t2);
- }, this.wait = (t2) => {
- var v4;
- const h5 = this.timer(t2).start();
- return (v4 = e(this, c)) == null || v4.onWait(t2, h5), h5;
- }, this.waitTimers = async (t2) => {
- await Promise.all(this.get(t2).map((h5) => this.wait(h5)));
- }, n2(this, d2, s2), n2(this, l2, i2), n2(this, b, r3), r3 && n2(this, c, new _(s2, i2, r3));
- }
- /// Get metadata of the ctx.
- get meta() {
- return e(this, b);
- }
- /// Get the inspector of the ctx.
- get inspector() {
- return e(this, c);
- }
- };
- d2 = /* @__PURE__ */ new WeakMap(), l2 = /* @__PURE__ */ new WeakMap(), b = /* @__PURE__ */ new WeakMap(), c = /* @__PURE__ */ new WeakMap();
- var U = L;
- var J = class {
- constructor() {
- this.store = /* @__PURE__ */ new Map(), this.get = (s2) => {
- const i2 = this.store.get(s2.id);
- if (!i2)
- throw f(s2.name);
- return i2;
- }, this.remove = (s2) => {
- this.store.delete(s2.id);
- }, this.has = (s2) => this.store.has(s2.id);
- }
- };
- var C;
- var g2;
- var E;
- var p2;
- var I;
- var k;
- var q = class {
- /// @internal
- constructor(s2, i2) {
- a(this, C, void 0);
- a(this, g2, void 0);
- a(this, E, void 0);
- a(this, p2, void 0);
- a(this, I, void 0);
- a(this, k, void 0);
- n2(this, C, null), n2(this, g2, null), n2(this, p2, "pending"), this.start = () => {
- var _a;
- return (_a = e(this, C)) != null ? _a : n2(this, C, new Promise((r3, t2) => {
- n2(this, g2, (h5) => {
- h5 instanceof CustomEvent && h5.detail.id === e(this, E) && (n2(this, p2, "resolved"), e(this, I).call(this), h5.stopImmediatePropagation(), r3());
- }), e(this, k).call(this, () => {
- e(this, p2) === "pending" && n2(this, p2, "rejected"), e(this, I).call(this), t2(new Error(`Timing ${this.type.name} timeout.`));
- }), n2(this, p2, "pending"), addEventListener(this.type.name, e(this, g2));
- })), e(this, C);
- }, this.done = () => {
- const r3 = new CustomEvent(this.type.name, { detail: { id: e(this, E) } });
- dispatchEvent(r3);
- }, n2(this, I, () => {
- e(this, g2) && removeEventListener(this.type.name, e(this, g2));
- }), n2(this, k, (r3) => {
- setTimeout(() => {
- r3();
- }, this.type.timeout);
- }), n2(this, E, Symbol(i2.name)), this.type = i2, s2.set(i2.id, this);
- }
- /// The status of the timer.
- /// Can be `pending`, `resolved` or `rejected`.
- get status() {
- return e(this, p2);
- }
- };
- C = /* @__PURE__ */ new WeakMap(), g2 = /* @__PURE__ */ new WeakMap(), E = /* @__PURE__ */ new WeakMap(), p2 = /* @__PURE__ */ new WeakMap(), I = /* @__PURE__ */ new WeakMap(), k = /* @__PURE__ */ new WeakMap();
- var A = class {
- /// Create a timer type with a name and a timeout.
- /// The name should be unique in the clock.
- constructor(s2, i2 = 3e3) {
- this.create = (r3) => new q(r3, this), this.id = Symbol(`Timer-${s2}`), this.name = s2, this.timeout = i2;
- }
- };
- var K = (o2, s2 = 3e3) => new A(o2, s2);
-
- // node_modules/orderedmap/dist/index.js
- function OrderedMap(content3) {
- this.content = content3;
- }
- OrderedMap.prototype = {
- constructor: OrderedMap,
- find: function(key) {
- for (var i2 = 0; i2 < this.content.length; i2 += 2)
- if (this.content[i2] === key) return i2;
- return -1;
- },
- // :: (string) → ?any
- // Retrieve the value stored under `key`, or return undefined when
- // no such key exists.
- get: function(key) {
- var found2 = this.find(key);
- return found2 == -1 ? void 0 : this.content[found2 + 1];
- },
- // :: (string, any, ?string) → OrderedMap
- // Create a new map by replacing the value of `key` with a new
- // value, or adding a binding to the end of the map. If `newKey` is
- // given, the key of the binding will be replaced with that key.
- update: function(key, value, newKey) {
- var self2 = newKey && newKey != key ? this.remove(newKey) : this;
- var found2 = self2.find(key), content3 = self2.content.slice();
- if (found2 == -1) {
- content3.push(newKey || key, value);
- } else {
- content3[found2 + 1] = value;
- if (newKey) content3[found2] = newKey;
- }
- return new OrderedMap(content3);
- },
- // :: (string) → OrderedMap
- // Return a map with the given key removed, if it existed.
- remove: function(key) {
- var found2 = this.find(key);
- if (found2 == -1) return this;
- var content3 = this.content.slice();
- content3.splice(found2, 2);
- return new OrderedMap(content3);
- },
- // :: (string, any) → OrderedMap
- // Add a new key to the start of the map.
- addToStart: function(key, value) {
- return new OrderedMap([key, value].concat(this.remove(key).content));
- },
- // :: (string, any) → OrderedMap
- // Add a new key to the end of the map.
- addToEnd: function(key, value) {
- var content3 = this.remove(key).content.slice();
- content3.push(key, value);
- return new OrderedMap(content3);
- },
- // :: (string, string, any) → OrderedMap
- // Add a key after the given key. If `place` is not found, the new
- // key is added to the end.
- addBefore: function(place, key, value) {
- var without = this.remove(key), content3 = without.content.slice();
- var found2 = without.find(place);
- content3.splice(found2 == -1 ? content3.length : found2, 0, key, value);
- return new OrderedMap(content3);
- },
- // :: ((key: string, value: any))
- // Call the given function for each key/value pair in the map, in
- // order.
- forEach: function(f3) {
- for (var i2 = 0; i2 < this.content.length; i2 += 2)
- f3(this.content[i2], this.content[i2 + 1]);
- },
- // :: (union