Factor out markdown widget rendering
parent
23e99b0c46
commit
50caba8522
|
@ -54,6 +54,7 @@ export type CompleteEvent = {
|
||||||
export type WidgetContent = {
|
export type WidgetContent = {
|
||||||
html?: string;
|
html?: string;
|
||||||
script?: string;
|
script?: string;
|
||||||
|
markdown?: string;
|
||||||
url?: string;
|
url?: string;
|
||||||
height?: number;
|
height?: number;
|
||||||
width?: number;
|
width?: number;
|
||||||
|
|
|
@ -115,3 +115,14 @@ export type ObjectValue<T> = {
|
||||||
} & T;
|
} & T;
|
||||||
|
|
||||||
export type ObjectQuery = Omit<Query, "prefix">;
|
export type ObjectQuery = Omit<Query, "prefix">;
|
||||||
|
|
||||||
|
// Code widget stuff
|
||||||
|
export type CodeWidgetCallback = (
|
||||||
|
bodyText: string,
|
||||||
|
) => Promise<CodeWidgetContent>;
|
||||||
|
|
||||||
|
export type CodeWidgetContent = {
|
||||||
|
html?: string;
|
||||||
|
markdown?: string;
|
||||||
|
script?: string;
|
||||||
|
};
|
||||||
|
|
|
@ -2,6 +2,13 @@ name: markdown
|
||||||
assets:
|
assets:
|
||||||
- "assets/*"
|
- "assets/*"
|
||||||
functions:
|
functions:
|
||||||
|
|
||||||
|
# API
|
||||||
|
markdownContentWidget:
|
||||||
|
path: markdown_content_widget.ts:markdownContentWidget
|
||||||
|
|
||||||
|
# User facing
|
||||||
|
|
||||||
toggle:
|
toggle:
|
||||||
path: "./markdown.ts:togglePreview"
|
path: "./markdown.ts:togglePreview"
|
||||||
command:
|
command:
|
||||||
|
|
|
@ -1,9 +1,25 @@
|
||||||
import { asset } from "$sb/syscalls.ts";
|
import { WidgetContent } from "$sb/app_event.ts";
|
||||||
|
import { asset, markdown } from "$sb/syscalls.ts";
|
||||||
import { panelHtml } from "../../web/components/panel_html.ts";
|
import { panelHtml } from "../../web/components/panel_html.ts";
|
||||||
|
import { renderMarkdownToHtml } from "./markdown_render.ts";
|
||||||
|
|
||||||
|
export async function markdownContentWidget(
|
||||||
|
markdownText: string,
|
||||||
|
): Promise<WidgetContent> {
|
||||||
|
// Parse markdown to a ParseTree
|
||||||
|
const mdTree = await markdown.parseMarkdown(markdownText);
|
||||||
|
// And then render it to HTML
|
||||||
|
const html = renderMarkdownToHtml(mdTree, { smartHardBreak: true });
|
||||||
|
return {
|
||||||
|
html: await wrapHTML(html),
|
||||||
|
script: await prepareJS(),
|
||||||
|
// And add back the markdown text so we can render it in a different way if desired
|
||||||
|
markdown: markdownText,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export async function prepareJS() {
|
export async function prepareJS() {
|
||||||
const iframeJS = await asset.readAsset("assets/common.js");
|
const iframeJS = await asset.readAsset("assets/markdown_widget.js");
|
||||||
|
|
||||||
return `
|
return `
|
||||||
const panelHtml = \`${panelHtml}\`;
|
const panelHtml = \`${panelHtml}\`;
|
||||||
${iframeJS}
|
${iframeJS}
|
||||||
|
@ -11,7 +27,7 @@ export async function prepareJS() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function wrapHTML(html: string): Promise<string> {
|
export async function wrapHTML(html: string): Promise<string> {
|
||||||
const css = await asset.readAsset("assets/style.css");
|
const css = await asset.readAsset("assets/markdown_widget.css");
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<!-- Load SB's own CSS here too -->
|
<!-- Load SB's own CSS here too -->
|
|
@ -10,8 +10,8 @@ export async function updateMarkdownPreview() {
|
||||||
const text = await editor.getText();
|
const text = await editor.getText();
|
||||||
const mdTree = await markdown.parseMarkdown(text);
|
const mdTree = await markdown.parseMarkdown(text);
|
||||||
// const cleanMd = await cleanMarkdown(text);
|
// const cleanMd = await cleanMarkdown(text);
|
||||||
const css = await asset.readAsset("assets/styles.css");
|
const css = await asset.readAsset("assets/preview.css");
|
||||||
const js = await asset.readAsset("assets/handler.js");
|
const js = await asset.readAsset("assets/preview.js");
|
||||||
const html = renderMarkdownToHtml(mdTree, {
|
const html = renderMarkdownToHtml(mdTree, {
|
||||||
smartHardBreak: true,
|
smartHardBreak: true,
|
||||||
annotationPositions: true,
|
annotationPositions: true,
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
name: query
|
name: query
|
||||||
assets:
|
|
||||||
- "assets/*"
|
|
||||||
functions:
|
functions:
|
||||||
queryWidget:
|
queryWidget:
|
||||||
path: query.ts:widget
|
path: query.ts:widget
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
import type { WidgetContent } from "$sb/app_event.ts";
|
import type { WidgetContent } from "$sb/app_event.ts";
|
||||||
import { editor, events, language, markdown, space } from "$sb/syscalls.ts";
|
import { editor, events, language, space, system } from "$sb/syscalls.ts";
|
||||||
import { parseTreeToAST } from "$sb/lib/tree.ts";
|
import { parseTreeToAST } from "$sb/lib/tree.ts";
|
||||||
import { astToKvQuery } from "$sb/lib/parse-query.ts";
|
import { astToKvQuery } from "$sb/lib/parse-query.ts";
|
||||||
import { jsonToMDTable, renderTemplate } from "../directive/util.ts";
|
import { jsonToMDTable, renderTemplate } from "../directive/util.ts";
|
||||||
import { renderMarkdownToHtml } from "../markdown/markdown_render.ts";
|
|
||||||
import { replaceTemplateVars } from "../template/template.ts";
|
import { replaceTemplateVars } from "../template/template.ts";
|
||||||
import { prepareJS, wrapHTML } from "./util.ts";
|
|
||||||
|
|
||||||
export async function widget(bodyText: string): Promise<WidgetContent> {
|
export async function widget(bodyText: string): Promise<WidgetContent> {
|
||||||
const pageMeta = await space.getPageMeta(await editor.getCurrentPage());
|
const pageMeta = await space.getPageMeta(await editor.getCurrentPage());
|
||||||
|
@ -57,21 +55,14 @@ export async function widget(bodyText: string): Promise<WidgetContent> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse markdown to a ParseTree
|
return system.invokeFunction(
|
||||||
const mdTree = await markdown.parseMarkdown(resultMarkdown);
|
"markdown.markdownContentWidget",
|
||||||
// And then render it to HTML
|
resultMarkdown,
|
||||||
const html = renderMarkdownToHtml(mdTree, { smartHardBreak: true });
|
);
|
||||||
return {
|
|
||||||
html: await wrapHTML(`
|
|
||||||
${parsedQuery.render ? "" : `<div class="sb-table-widget">`}
|
|
||||||
${html}
|
|
||||||
${parsedQuery.render ? "" : `</div>`}
|
|
||||||
`),
|
|
||||||
script: await prepareJS(),
|
|
||||||
};
|
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
return {
|
return system.invokeFunction(
|
||||||
html: await wrapHTML(`<b>Error:</b> ${e.message}`),
|
"markdown.markdownContentWidget",
|
||||||
};
|
`**Error:** ${e.message}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
import { WidgetContent } from "$sb/app_event.ts";
|
import { WidgetContent } from "$sb/app_event.ts";
|
||||||
import { editor, handlebars, markdown, space, YAML } from "$sb/syscalls.ts";
|
import {
|
||||||
|
editor,
|
||||||
|
handlebars,
|
||||||
|
markdown,
|
||||||
|
space,
|
||||||
|
system,
|
||||||
|
YAML,
|
||||||
|
} from "$sb/syscalls.ts";
|
||||||
import { rewritePageRefs } from "$sb/lib/resolve.ts";
|
import { rewritePageRefs } from "$sb/lib/resolve.ts";
|
||||||
import { renderMarkdownToHtml } from "../markdown/markdown_render.ts";
|
|
||||||
import { prepareJS, wrapHTML } from "./util.ts";
|
|
||||||
import { replaceTemplateVars } from "../template/template.ts";
|
import { replaceTemplateVars } from "../template/template.ts";
|
||||||
|
import { renderToText } from "$sb/lib/tree.ts";
|
||||||
|
|
||||||
type TemplateConfig = {
|
type TemplateConfig = {
|
||||||
// Pull the template from a page
|
// Pull the template from a page
|
||||||
|
@ -36,31 +42,28 @@ export async function widget(bodyText: string): Promise<WidgetContent> {
|
||||||
)
|
)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const rendered = config.raw
|
let rendered = config.raw ? templateText : await handlebars.renderTemplate(
|
||||||
? templateText
|
templateText,
|
||||||
: await handlebars.renderTemplate(
|
value,
|
||||||
templateText,
|
{
|
||||||
value,
|
page: pageMeta,
|
||||||
{
|
},
|
||||||
page: pageMeta,
|
);
|
||||||
},
|
|
||||||
);
|
|
||||||
const parsedMarkdown = await markdown.parseMarkdown(rendered);
|
|
||||||
|
|
||||||
if (templatePage) {
|
if (templatePage) {
|
||||||
|
const parsedMarkdown = await markdown.parseMarkdown(rendered);
|
||||||
rewritePageRefs(parsedMarkdown, templatePage);
|
rewritePageRefs(parsedMarkdown, templatePage);
|
||||||
|
rendered = renderToText(parsedMarkdown);
|
||||||
}
|
}
|
||||||
const html = renderMarkdownToHtml(parsedMarkdown, {
|
|
||||||
smartHardBreak: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return system.invokeFunction(
|
||||||
html: await wrapHTML(html),
|
"markdown.markdownContentWidget",
|
||||||
script: await prepareJS(),
|
rendered,
|
||||||
};
|
);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
return {
|
return system.invokeFunction(
|
||||||
html: `<b>Error:</b> ${e.message}`,
|
"markdown.markdownContentWidget",
|
||||||
};
|
`**Error:** ${e.message}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import { WidgetContent } from "../../plug-api/app_event.ts";
|
import { WidgetContent } from "../../plug-api/app_event.ts";
|
||||||
import { Decoration, EditorState, syntaxTree, WidgetType } from "../deps.ts";
|
import { Decoration, EditorState, syntaxTree, WidgetType } from "../deps.ts";
|
||||||
import type { Client } from "../client.ts";
|
import type { Client } from "../client.ts";
|
||||||
import { CodeWidgetCallback } from "../hooks/code_widget.ts";
|
|
||||||
import {
|
import {
|
||||||
decoratorStateField,
|
decoratorStateField,
|
||||||
invisibleDecoration,
|
invisibleDecoration,
|
||||||
isCursorInRange,
|
isCursorInRange,
|
||||||
} from "./util.ts";
|
} from "./util.ts";
|
||||||
import { createWidgetSandboxIFrame } from "../components/widget_sandbox_iframe.ts";
|
import { createWidgetSandboxIFrame } from "../components/widget_sandbox_iframe.ts";
|
||||||
|
import type { CodeWidgetCallback } from "$sb/types.ts";
|
||||||
|
|
||||||
class IFrameWidget extends WidgetType {
|
class IFrameWidget extends WidgetType {
|
||||||
iframe?: HTMLIFrameElement;
|
iframe?: HTMLIFrameElement;
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
import { Hook, Manifest } from "../../plugos/types.ts";
|
import { Hook, Manifest } from "../../plugos/types.ts";
|
||||||
import { System } from "../../plugos/system.ts";
|
import { System } from "../../plugos/system.ts";
|
||||||
|
import { CodeWidgetCallback } from "$sb/types.ts";
|
||||||
|
|
||||||
export type CodeWidgetT = {
|
export type CodeWidgetT = {
|
||||||
codeWidget?: string;
|
codeWidget?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CodeWidgetCallback = (
|
|
||||||
bodyText: string,
|
|
||||||
) => Promise<{ html: string; script: string }>;
|
|
||||||
|
|
||||||
export class CodeWidgetHook implements Hook<CodeWidgetT> {
|
export class CodeWidgetHook implements Hook<CodeWidgetT> {
|
||||||
codeWidgetCallbacks = new Map<string, CodeWidgetCallback>();
|
codeWidgetCallbacks = new Map<string, CodeWidgetCallback>();
|
||||||
|
|
||||||
|
|
|
@ -216,15 +216,13 @@
|
||||||
color: var(--editor-line-meta-color);
|
color: var(--editor-line-meta-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sb-table-widget {
|
thead tr {
|
||||||
thead tr {
|
background-color: var(--editor-table-head-background-color);
|
||||||
background-color: var(--editor-table-head-background-color);
|
color: var(--editor-table-head-color);
|
||||||
color: var(--editor-table-head-color);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
tbody tr:nth-of-type(even) {
|
tbody tr:nth-of-type(even) {
|
||||||
background-color: var(--editor-table-even-background-color);
|
background-color: var(--editor-table-even-background-color);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.sb-line-blockquote {
|
.sb-line-blockquote {
|
||||||
|
|
|
@ -393,22 +393,24 @@
|
||||||
margin-bottom: -3rem;
|
margin-bottom: -3rem;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|
||||||
table {
|
|
||||||
width: 100%;
|
|
||||||
border-spacing: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
thead tr {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
th,
|
|
||||||
td {
|
|
||||||
padding: 8px;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-spacing: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead tr {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
padding: 8px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// dont apply background color twice for (fenced) code blocks
|
// dont apply background color twice for (fenced) code blocks
|
||||||
.sb-line-code .sb-code {
|
.sb-line-code .sb-code {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { CodeWidgetContent } from "$sb/types.ts";
|
||||||
import { SysCallMapping } from "../../plugos/system.ts";
|
import { SysCallMapping } from "../../plugos/system.ts";
|
||||||
import { Client } from "../client.ts";
|
import { Client } from "../client.ts";
|
||||||
|
|
||||||
|
@ -9,7 +10,7 @@ export function widgetSyscalls(
|
||||||
_ctx,
|
_ctx,
|
||||||
lang: string,
|
lang: string,
|
||||||
body: string,
|
body: string,
|
||||||
): Promise<{ html: string; script: string }> => {
|
): Promise<CodeWidgetContent> => {
|
||||||
const langCallback = client.system.codeWidgetHook.codeWidgetCallbacks.get(
|
const langCallback = client.system.codeWidgetHook.codeWidgetCallbacks.get(
|
||||||
lang,
|
lang,
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue