Skip to content

Commit

Permalink
Implement pagerefs
Browse files Browse the repository at this point in the history
  • Loading branch information
DanielPower committed May 24, 2024
1 parent aa434dd commit e3364a7
Show file tree
Hide file tree
Showing 6 changed files with 295 additions and 67 deletions.
80 changes: 80 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"dayjs": "^1.11.11",
"prism-themes": "^1.9.0",
"refractor": "^4.8.1",
"remark-directive": "^3.0.0",
"zod": "^3.23.8",
"zodix": "^0.4.4"
}
Expand Down
78 changes: 78 additions & 0 deletions src/lib/components/Editor.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<script lang="ts">
import { iframe } from "$lib/milkdown/iframe";
import { pageref } from "$lib/milkdown/pageref";
import { Editor, defaultValueCtx, rootCtx } from "@milkdown/core";
import { diagram } from "@milkdown/plugin-diagram";
import { listener, listenerCtx } from "@milkdown/plugin-listener";
import { prism, prismConfig } from "@milkdown/plugin-prism";
import { commonmark } from "@milkdown/preset-commonmark";
import { gfm } from "@milkdown/preset-gfm";
import javascript from "refractor/lang/javascript";
import markdown from "refractor/lang/markdown";
import { onDestroy, onMount } from "svelte";
export let initialValue: string;
export let fileName: string;
let unsavedMarkdown: string | null = null;
const save = () => {
if (unsavedMarkdown) {
fetch(`/api/save/${fileName}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ fileContents: unsavedMarkdown }),
});
unsavedMarkdown = null;
}
};
const editor = (dom: HTMLElement) => {
Editor.make()
.config((ctx) => {
const listener = ctx.get(listenerCtx);
listener.markdownUpdated((_ctx, markdown, prevMarkdown) => {
if (markdown === prevMarkdown) return;
unsavedMarkdown = markdown;
});
ctx.set(rootCtx, dom);
ctx.set(defaultValueCtx, initialValue);
ctx.set(prismConfig.key, {
configureRefractor: (refractor) => {
refractor.register(markdown);
refractor.register(javascript);
},
});
})
.use(commonmark)
.use(gfm)
.use(iframe)
.use(pageref)
.use(prism)
.use(diagram)
.use(listener)
.create();
};
onMount(() => {
const interval = setInterval(() => save(), 5000);
const onVisibilityChange = () => {
if (document.visibilityState === "hidden") {
save();
}
};
document.addEventListener("visibilitychange", onVisibilityChange);
return () => {
clearInterval(interval);
document.removeEventListener("visibilitychange", onVisibilityChange);
};
});
onDestroy(() => {
save();
});
</script>

<div use:editor></div>
56 changes: 56 additions & 0 deletions src/lib/milkdown/iframe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { $node, $remark, $inputRule } from "@milkdown/utils";
import directive from "remark-directive";
import { InputRule } from "prosemirror-inputrules";

const iframeNode = $node("iframe", () => ({
group: "block",
atom: true,
isolating: true,
marks: "",
attrs: {
src: { default: null },
},
parseDOM: [
{
tag: "iframe",
getAttrs: (dom) => ({
src: (dom as HTMLElement).getAttribute("src"),
}),
},
],
toDOM: (node) => ["iframe", { ...node.attrs, contenteditable: false }, 0],
parseMarkdown: {
match: (node) => node.type === "leafDirective" && node.name === "iframe",
runner: (state, node, type) => {
state.addNode(type, { src: (node.attributes as { src: string }).src });
},
},
toMarkdown: {
match: (node) => node.type.name === "iframe",
runner: (state, node) => {
state.addNode("leafDirective", undefined, undefined, {
name: "iframe",
attributes: { src: node.attrs.src },
});
},
},
}));

const remarkPluginId = "iframe";
const remarkDirective = $remark(remarkPluginId, () => directive);

const iframeInputRule = $inputRule(
(ctx) =>
new InputRule(/::iframe\{src\="(?<src>[^"]+)?"?\}/, (state, match, start, end) => {
const [okay, src = ""] = match;
const { tr } = state;

if (okay) {
tr.replaceWith(start - 1, end, iframeNode.type(ctx).create({ src }));
}

return tr;
}),
);

export const iframe = [remarkDirective, iframeNode, iframeInputRule];
73 changes: 73 additions & 0 deletions src/lib/milkdown/pageref.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { $node, $remark, $inputRule } from "@milkdown/utils";
import directive from "remark-directive";
import { InputRule } from "prosemirror-inputrules";

const pagerefNode = $node("pageref", () => ({
group: "inline",
inline: true,
attrs: {
page: { default: "" },
},
parseDOM: [
{
tag: "a",
getAttrs: (dom) => {
console.log("parseDOM getAttrs", { dom });
return {
page: (dom as HTMLElement).getAttribute("href").replace("/page/", ""),
};
},
},
],
toDOM: (node) => {
console.log("toDOM", { node });
return ["a", { href: `/page/${node.attrs.page}` }, node.attrs.page];
},
parseMarkdown: {
match: (node) => {
console.log("parseMarkdown match", { node });
return node.type === "textDirective" && node.name === "pageref";
},
runner: (state, node, type) => {
console.log("parseMarkdown runner", { node });
state.addNode(type, { page: (node.attributes as { page: string }).page });
console.log("parseMarkdown runner post-addnode", { node });
},
},
toMarkdown: {
match: (node) => {
console.log("toMarkdown match", { node });
return node.type.name === "pageref";
},
runner: (state, node) => {
console.log("toMarkdown runner", { node });
state.addNode("textDirective", undefined, undefined, {
name: "pageref",
attributes: { page: node.attrs.page },
});
},
},
}));

const remarkPluginId = "pageref";
const remarkDirective = $remark(remarkPluginId, () => directive);

const pagerefInputRule = $inputRule(
(ctx) =>
new InputRule(
/::pageref\{page\="(?<page>[^"]+)?"?\}/,
(state, match, start, end) => {
console.log("pagerefInputRule", { match, start, end });
const [okay, page = ""] = match;
const { tr } = state;

if (okay) {
tr.replaceWith(start - 1, end, pagerefNode.type(ctx).create({ page }));
}

return tr;
},
),
);

export const pageref = [remarkDirective, pagerefNode, pagerefInputRule];
Loading

0 comments on commit e3364a7

Please sign in to comment.