Rebuilt frontmatter templates as template widgets

This commit is contained in:
Zef Hemel
2024-01-08 17:08:35 +01:00
parent 5b3dd500e4
commit 848e11a773
45 changed files with 471 additions and 483 deletions
+4 -1
View File
@@ -315,7 +315,10 @@ export class Client {
setTimeout(() => {
this.editorView.dispatch({
selection: { anchor: pos as number },
effects: EditorView.scrollIntoView(pos as number, { y: "start" }),
effects: EditorView.scrollIntoView(pos as number, {
y: "start",
yMargin: 5,
}),
});
});
} else if (!stateRestored) {
+3 -1
View File
@@ -8,6 +8,7 @@ import {
} from "./util.ts";
import { MarkdownWidget } from "./markdown_widget.ts";
import { IFrameWidget } from "./iframe_widget.ts";
import { isTemplate } from "$sb/lib/cheap_yaml.ts";
export function fencedCodePlugin(editor: Client) {
return decoratorStateField((state: EditorState) => {
@@ -27,7 +28,8 @@ export function fencedCodePlugin(editor: Client) {
const renderMode = editor.system.codeWidgetHook.codeWidgetModes.get(
lang,
);
if (codeWidgetCallback) {
// Only custom render when we have a custom renderer, and the current page is not a template
if (codeWidgetCallback && !isTemplate(state.sliceDoc(0, from))) {
// We got a custom renderer!
const lineStrings = text.split("\n");
+17 -8
View File
@@ -35,14 +35,23 @@ export class IFrameWidget extends WidgetType {
case "reload":
this.codeWidgetCallback(this.bodyText, this.client.currentPage!)
.then(
(widgetContent: WidgetContent) => {
iframe.contentWindow!.postMessage({
type: "html",
html: widgetContent.html,
script: widgetContent.script,
theme:
document.getElementsByTagName("html")[0].dataset.theme,
});
(widgetContent: WidgetContent | null) => {
if (widgetContent === null) {
iframe.contentWindow!.postMessage({
type: "html",
html: "",
theme:
document.getElementsByTagName("html")[0].dataset.theme,
});
} else {
iframe.contentWindow!.postMessage({
type: "html",
html: widgetContent.html,
script: widgetContent.script,
theme:
document.getElementsByTagName("html")[0].dataset.theme,
});
}
},
);
break;
+11 -22
View File
@@ -27,12 +27,10 @@ export class MarkdownWidget extends WidgetType {
div.className = this.className;
const cacheItem = this.client.getWidgetCache(this.cacheKey);
if (cacheItem) {
div.innerHTML = this.wrapHtml(
cacheItem.html,
cacheItem.buttons || [],
cacheItem.banner,
);
this.attachListeners(div, cacheItem.buttons);
div.innerHTML = this.wrapHtml(cacheItem.html, cacheItem.buttons);
if (cacheItem.html) {
this.attachListeners(div, cacheItem.buttons);
}
}
// Async kick-off of content renderer
@@ -90,6 +88,7 @@ export class MarkdownWidget extends WidgetType {
},
preserveAttributes: true,
});
// console.log("Got html", html);
if (cachedHtml === html) {
// HTML still same as in cache, no need to re-render
@@ -97,10 +96,11 @@ export class MarkdownWidget extends WidgetType {
}
div.innerHTML = this.wrapHtml(
html,
widgetContent.buttons || [],
widgetContent.banner,
widgetContent.buttons,
);
this.attachListeners(div, widgetContent.buttons);
if (html) {
this.attachListeners(div, widgetContent.buttons);
}
// Let's give it a tick, then measure and cache
setTimeout(() => {
@@ -110,7 +110,6 @@ export class MarkdownWidget extends WidgetType {
height: div.offsetHeight,
html,
buttons: widgetContent.buttons,
banner: widgetContent.banner,
},
);
// Because of the rejiggering of the DOM, we need to do a no-op cursor move to make sure it's positioned correctly
@@ -124,8 +123,7 @@ export class MarkdownWidget extends WidgetType {
private wrapHtml(
html: string,
buttons: CodeWidgetButton[],
banner?: string,
buttons: CodeWidgetButton[] = [],
) {
if (!html) {
return "";
@@ -134,9 +132,7 @@ export class MarkdownWidget extends WidgetType {
buttons.filter((button) => !button.widgetTarget).map((button, idx) =>
`<button data-button="${idx}" title="${button.description}">${button.svg}</button> `
).join("")
}</div>${
banner ? `<div class="sb-banner">${escapeHtml(banner)}</div>` : ""
}${html}`;
}</div>${html}`;
}
private attachListeners(div: HTMLElement, buttons?: CodeWidgetButton[]) {
@@ -255,10 +251,3 @@ function garbageCollectWidgets() {
}
setInterval(garbageCollectWidgets, 5000);
function escapeHtml(text: string) {
return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(
/>/g,
"&gt;",
);
}
+2 -2
View File
@@ -17,7 +17,7 @@ export function postScriptPrefacePlugin(
undefined,
editor,
`top:${editor.currentPage}`,
"",
"top",
topCallback,
"sb-markdown-top-widget",
),
@@ -34,7 +34,7 @@ export function postScriptPrefacePlugin(
undefined,
editor,
`bottom:${editor.currentPage}`,
"",
"bottom",
bottomCallback,
"sb-markdown-bottom-widget",
),
+23 -21
View File
@@ -104,7 +104,7 @@ export function mountIFrame(
preloadedIFrame: PreloadedIFrame,
client: Client,
widgetHeightCacheKey: string | null,
content: WidgetContent | Promise<WidgetContent>,
content: WidgetContent | null | Promise<WidgetContent | null>,
onMessage?: (message: any) => void,
) {
const iframe = preloadedIFrame.iframe;
@@ -174,26 +174,28 @@ export function mountIFrame(
console.warn("Iframe went away or content was not loaded");
return;
}
if (resolvedContent.html) {
iframe.contentWindow!.postMessage({
type: "html",
html: resolvedContent.html,
script: resolvedContent.script,
theme: document.getElementsByTagName("html")[0].dataset.theme,
});
} else if (resolvedContent.url) {
iframe.contentWindow!.location.href = resolvedContent.url;
if (resolvedContent.height) {
iframe.height = resolvedContent.height + "px";
if (widgetHeightCacheKey) {
client.setCachedWidgetHeight(
widgetHeightCacheKey!,
resolvedContent.height,
);
if (resolvedContent) {
if (resolvedContent.html) {
iframe.contentWindow!.postMessage({
type: "html",
html: resolvedContent.html,
script: resolvedContent.script,
theme: document.getElementsByTagName("html")[0].dataset.theme,
});
} else if (resolvedContent.url) {
iframe.contentWindow!.location.href = resolvedContent.url;
if (resolvedContent.height) {
iframe.height = resolvedContent.height + "px";
if (widgetHeightCacheKey) {
client.setCachedWidgetHeight(
widgetHeightCacheKey!,
resolvedContent.height,
);
}
}
if (resolvedContent.width) {
iframe.width = resolvedContent.width + "px";
}
}
if (resolvedContent.width) {
iframe.width = resolvedContent.width + "px";
}
}
}).catch(console.error);
@@ -202,7 +204,7 @@ export function mountIFrame(
export function createWidgetSandboxIFrame(
client: Client,
widgetHeightCacheKey: string | null,
content: WidgetContent | Promise<WidgetContent>,
content: WidgetContent | null | Promise<WidgetContent | null>,
onMessage?: (message: any) => void,
) {
// console.log("Claiming iframe");
+1 -1
View File
@@ -48,7 +48,7 @@ export class PanelWidgetHook implements Hook<PanelWidgetT> {
if (!functionDef.panelWidget) {
continue;
}
if (!["top", "bottom", "frontmatter"].includes(functionDef.panelWidget)) {
if (!["top", "bottom"].includes(functionDef.panelWidget)) {
errors.push(
`Panel widgets must be attached to either 'top' or 'bottom'.`,
);
-2
View File
@@ -429,8 +429,6 @@
margin-top: 10px;
}
.sb-markdown-widget,
.sb-markdown-top-widget:has(*),
.sb-markdown-bottom-widget:has(*) {
+1 -1
View File
@@ -11,7 +11,7 @@ export function codeWidgetSyscalls(
lang: string,
body: string,
pageName: string,
): Promise<CodeWidgetContent> => {
): Promise<CodeWidgetContent | null> => {
const langCallback = codeWidgetHook.codeWidgetCallbacks.get(
lang,
);