1
0
This commit is contained in:
Zef Hemel 2023-02-23 15:33:51 +01:00
parent 495012f796
commit c1528eff08
6 changed files with 88 additions and 11 deletions

View File

@ -0,0 +1,20 @@
{
"version": 3,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "md.silverbullet",
"variantName": "release",
"elements": [
{
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 1,
"versionName": "1.0",
"outputFile": "app-release.apk"
}
],
"elementType": "File"
}

View File

@ -30,12 +30,12 @@ Deno.test("Markdown render", async () => {
// console.log("HTML", html); // console.log("HTML", html);
}); });
Deno.test("Smart hard break test", () => { Deno.test("Smart hard break test", async () => {
const example = `**Hello** const example = `**Hello**
*world!*`; *world!*`;
const lang = buildMarkdown([]); const lang = buildMarkdown([]);
const tree = parse(lang, example); const tree = parse(lang, example);
const html = renderMarkdownToHtml(tree, { const html = await renderMarkdownToHtml(tree, {
failOnUnknown: true, failOnUnknown: true,
smartHardBreak: true, smartHardBreak: true,
}); });

View File

@ -13,6 +13,8 @@ type MarkdownRenderOptions = {
annotationPositions?: true; annotationPositions?: true;
renderFrontMatter?: true; renderFrontMatter?: true;
attachmentUrlPrefix?: string; attachmentUrlPrefix?: string;
// When defined, use to inline images as data: urls
inlineAttachments?: (url: string) => Promise<string>;
}; };
function cleanTags(values: (Tag | null)[]): Tag[] { function cleanTags(values: (Tag | null)[]): Tag[] {
@ -390,11 +392,36 @@ function render(
} }
} }
export function renderMarkdownToHtml( async function traverseTag(
t: Tag,
fn: (t: Tag) => Promise<void>,
): Promise<void> {
await fn(t);
if (typeof t === "string") {
return;
}
if (t.body) {
for (const child of t.body) {
await traverseTag(child, fn);
}
}
}
export async function renderMarkdownToHtml(
t: ParseTree, t: ParseTree,
options: MarkdownRenderOptions = {}, options: MarkdownRenderOptions = {},
) { ) {
preprocess(t, options); preprocess(t, options);
const htmlTree = posPreservingRender(t, options); const htmlTree = posPreservingRender(t, options);
if (htmlTree && options.inlineAttachments) {
await traverseTag(htmlTree, async (t) => {
if (typeof t === "string") {
return;
}
if (t.name === "img") {
t.attrs!.src = await options.inlineAttachments!(t.attrs!.src!);
}
});
}
return renderHtml(htmlTree); return renderHtml(htmlTree);
} }

View File

@ -1,4 +1,9 @@
import { clientStore, editor, system } from "$sb/silverbullet-syscall/mod.ts"; import {
clientStore,
editor,
space,
system,
} from "$sb/silverbullet-syscall/mod.ts";
import { asset } from "$sb/plugos-syscall/mod.ts"; import { asset } from "$sb/plugos-syscall/mod.ts";
import { parseMarkdown } from "../../plug-api/silverbullet-syscall/markdown.ts"; import { parseMarkdown } from "../../plug-api/silverbullet-syscall/markdown.ts";
import { renderMarkdownToHtml } from "./markdown_render.ts"; import { renderMarkdownToHtml } from "./markdown_render.ts";
@ -12,11 +17,21 @@ export async function updateMarkdownPreview() {
// const cleanMd = await cleanMarkdown(text); // const cleanMd = await cleanMarkdown(text);
const css = await asset.readAsset("assets/styles.css"); const css = await asset.readAsset("assets/styles.css");
const js = await asset.readAsset("assets/handler.js"); const js = await asset.readAsset("assets/handler.js");
const html = renderMarkdownToHtml(mdTree, { const html = await renderMarkdownToHtml(mdTree, {
smartHardBreak: true, smartHardBreak: true,
annotationPositions: true, annotationPositions: true,
renderFrontMatter: true, renderFrontMatter: true,
attachmentUrlPrefix: "fs/", inlineAttachments: async (url): Promise<string> => {
if (!url.includes("://")) {
try {
return await space.readAttachment(url);
} catch (e: any) {
console.error(e);
return url;
}
}
return url;
},
}); });
await editor.showPanel( await editor.showPanel(
"rhs", "rhs",

View File

@ -7,7 +7,7 @@ export async function markdownWidget(
): Promise<WidgetContent> { ): Promise<WidgetContent> {
const mdTree = await parseMarkdown(bodyText); const mdTree = await parseMarkdown(bodyText);
const html = renderMarkdownToHtml(mdTree, { const html = await renderMarkdownToHtml(mdTree, {
smartHardBreak: true, smartHardBreak: true,
}); });
return Promise.resolve({ return Promise.resolve({

View File

@ -15,11 +15,12 @@ import { renderMarkdownToHtml } from "../../plugs/markdown/markdown_render.ts";
import { ParseTree } from "$sb/lib/tree.ts"; import { ParseTree } from "$sb/lib/tree.ts";
import { lezerToParseTree } from "../../common/markdown_parser/parse_tree.ts"; import { lezerToParseTree } from "../../common/markdown_parser/parse_tree.ts";
import type { Editor } from "../editor.tsx"; import type { Editor } from "../editor.tsx";
import { urlToPathname } from "../../plugos/util.ts";
class TableViewWidget extends WidgetType { class TableViewWidget extends WidgetType {
constructor( constructor(
readonly pos: number, readonly pos: number,
readonly editorView: EditorView, readonly editor: Editor,
readonly t: ParseTree, readonly t: ParseTree,
) { ) {
super(); super();
@ -32,17 +33,31 @@ class TableViewWidget extends WidgetType {
// Pulling data-pos to put the cursor in the right place, falling back // Pulling data-pos to put the cursor in the right place, falling back
// to the start of the table. // to the start of the table.
const dataAttributes = (e.target as any).dataset; const dataAttributes = (e.target as any).dataset;
this.editorView.dispatch({ this.editor.editorView!.dispatch({
selection: { selection: {
anchor: dataAttributes.pos ? +dataAttributes.pos : this.pos, anchor: dataAttributes.pos ? +dataAttributes.pos : this.pos,
}, },
}); });
}); });
dom.innerHTML = renderMarkdownToHtml(this.t, { renderMarkdownToHtml(this.t, {
// Annotate every element with its position so we can use it to put // Annotate every element with its position so we can use it to put
// the cursor there when the user clicks on the table. // the cursor there when the user clicks on the table.
annotationPositions: true, annotationPositions: true,
inlineAttachments: async (url): Promise<string> => {
if (!url.includes("://")) {
try {
const d = await this.editor.space.readAttachment(url, "dataurl");
return d.data as string;
} catch (e: any) {
console.error(e);
return url;
}
}
return url;
},
}).then((html) => {
dom.innerHTML = html;
}); });
return dom; return dom;
} }
@ -90,7 +105,7 @@ export function tablePlugin(editor: Editor) {
Decoration.widget({ Decoration.widget({
widget: new TableViewWidget( widget: new TableViewWidget(
from, from,
editor.editorView!, editor,
lezerToParseTree(text, node.node), lezerToParseTree(text, node.node),
), ),
}).range(from), }).range(from),