@@ -0,0 +1,16 @@
|
||||
name: markdown
|
||||
functions:
|
||||
toggle:
|
||||
path: "./markdown.ts:togglePreview"
|
||||
command:
|
||||
name: "Markdown Preview: Toggle"
|
||||
key: Ctrl-p
|
||||
mac: Cmd-p
|
||||
preview:
|
||||
path: "./preview.ts:updateMarkdownPreview"
|
||||
env: client
|
||||
events:
|
||||
- plug:load
|
||||
- editor:updated
|
||||
- editor:pageLoaded
|
||||
- editor:pageReloaded
|
||||
@@ -0,0 +1,20 @@
|
||||
import { hideLhs, hideRhs } from "../../syscall/silverbullet-syscall/editor.ts";
|
||||
import { invokeFunction } from "../../syscall/silverbullet-syscall/system.ts";
|
||||
import * as clientStore from "../../syscall/silverbullet-syscall/clientStore.ts";
|
||||
import { readSettings } from "../lib/settings_page.ts";
|
||||
|
||||
export async function togglePreview() {
|
||||
let currentValue = !!(await clientStore.get("enableMarkdownPreview"));
|
||||
await clientStore.set("enableMarkdownPreview", !currentValue);
|
||||
if (!currentValue) {
|
||||
await invokeFunction("client", "preview");
|
||||
} else {
|
||||
await hideMarkdownPreview();
|
||||
}
|
||||
}
|
||||
|
||||
async function hideMarkdownPreview() {
|
||||
const setting = await readSettings({ previewOnRHS: true });
|
||||
const hide = setting.previewOnRHS ? hideRhs : hideLhs;
|
||||
await hide();
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
import MarkdownIt from "https://esm.sh/markdown-it@13.0.1";
|
||||
import {
|
||||
getText,
|
||||
showPanel,
|
||||
} from "../../syscall/silverbullet-syscall/editor.ts";
|
||||
import * as clientStore from "../../syscall/silverbullet-syscall/clientStore.ts";
|
||||
import { cleanMarkdown } from "./util.ts";
|
||||
|
||||
const css = `
|
||||
<style>
|
||||
body {
|
||||
font-family: georgia,times,serif;
|
||||
font-size: 14pt;
|
||||
max-width: 800px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
thead tr {
|
||||
background-color: #333;
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
tbody tr:nth-of-type(even) {
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
||||
a[href] {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left: 1px solid #333;
|
||||
margin-left: 2px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: 1em 0 1em 0;
|
||||
text-align: center;
|
||||
border-color: #777;
|
||||
border-width: 0;
|
||||
border-style: dotted;
|
||||
}
|
||||
|
||||
hr:after {
|
||||
content: "···";
|
||||
letter-spacing: 1em;
|
||||
}
|
||||
|
||||
</style>
|
||||
`;
|
||||
|
||||
import taskLists from "https://esm.sh/markdown-it-task-lists@2.1.1";
|
||||
|
||||
const md = new MarkdownIt({
|
||||
linkify: true,
|
||||
html: false,
|
||||
typographer: true,
|
||||
}).use(taskLists);
|
||||
|
||||
export async function updateMarkdownPreview() {
|
||||
if (!(await clientStore.get("enableMarkdownPreview"))) {
|
||||
return;
|
||||
}
|
||||
let text = await getText();
|
||||
let cleanMd = await cleanMarkdown(text);
|
||||
await showPanel(
|
||||
"rhs",
|
||||
2,
|
||||
`<html><head>${css}</head><body>${md.render(cleanMd)}</body></html>`,
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import {
|
||||
findNodeOfType,
|
||||
renderToText,
|
||||
replaceNodesMatching,
|
||||
} from "../../common/tree.ts";
|
||||
import { parseMarkdown } from "../../syscall/silverbullet-syscall/markdown.ts";
|
||||
|
||||
export function encodePageUrl(name: string): string {
|
||||
return name.replaceAll(" ", "_");
|
||||
}
|
||||
|
||||
export async function cleanMarkdown(
|
||||
text: string,
|
||||
validPages?: string[],
|
||||
): Promise<string> {
|
||||
const mdTree = await parseMarkdown(text);
|
||||
replaceNodesMatching(mdTree, (n) => {
|
||||
if (n.type === "WikiLink") {
|
||||
const page = n.children![1].children![0].text!;
|
||||
if (validPages && !validPages.includes(page)) {
|
||||
return {
|
||||
// HACK
|
||||
text: `_${page}_`,
|
||||
};
|
||||
}
|
||||
return {
|
||||
// HACK
|
||||
text: `[${page}](/${encodePageUrl(page)})`,
|
||||
};
|
||||
}
|
||||
// Simply get rid of these
|
||||
if (
|
||||
n.type === "CommentBlock" ||
|
||||
n.type === "Comment" ||
|
||||
n.type === "NamedAnchor"
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
if (n.type === "Hashtag") {
|
||||
return {
|
||||
text: `__${n.children![0].text}__`,
|
||||
};
|
||||
}
|
||||
if (n.type === "URL") {
|
||||
const url = n.children![0].text!;
|
||||
if (url.indexOf("://") === -1) {
|
||||
n.children![0].text = `fs/${url}`;
|
||||
}
|
||||
console.log("Link", url);
|
||||
}
|
||||
if (n.type === "FencedCode") {
|
||||
const codeInfoNode = findNodeOfType(n, "CodeInfo");
|
||||
if (!codeInfoNode) {
|
||||
return;
|
||||
}
|
||||
if (codeInfoNode.children![0].text === "meta") {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
});
|
||||
return renderToText(mdTree);
|
||||
}
|
||||
Reference in New Issue
Block a user