1
0

Fixed a lot of bugs with new widget rendering

This commit is contained in:
Zef Hemel 2023-12-28 16:14:30 +01:00
parent 4d66f23391
commit d43dbcacec
17 changed files with 212 additions and 142 deletions

View File

@ -133,6 +133,13 @@ export type CodeWidgetContent = {
html?: string; html?: string;
markdown?: string; markdown?: string;
script?: string; script?: string;
buttons?: CodeWidgetButton[];
};
export type CodeWidgetButton = {
description: string;
svg: string;
invokeFunction: string;
}; };
export type LintDiagnostic = { export type LintDiagnostic = {

View File

@ -178,6 +178,9 @@ functions:
env: client env: client
panelWidget: top panelWidget: top
refreshTOC:
path: toc.ts:refreshTOC
lintYAML: lintYAML:
path: lint.ts:lintYAML path: lint.ts:lintYAML
events: events:

View File

@ -1,4 +1,4 @@
import { clientStore, editor, system } from "$sb/silverbullet-syscall/mod.ts"; import { clientStore, codeWidget, editor, system } from "$sb/syscalls.ts";
import { CodeWidgetContent } from "$sb/types.ts"; import { CodeWidgetContent } from "$sb/types.ts";
import { queryObjects } from "./api.ts"; import { queryObjects } from "./api.ts";
import { LinkObject } from "./page_links.ts"; import { LinkObject } from "./page_links.ts";
@ -9,14 +9,16 @@ export async function toggleMentions() {
let hideMentions = await clientStore.get(hideMentionsKey); let hideMentions = await clientStore.get(hideMentionsKey);
hideMentions = !hideMentions; hideMentions = !hideMentions;
await clientStore.set(hideMentionsKey, hideMentions); await clientStore.set(hideMentionsKey, hideMentions);
if (!hideMentions) { await codeWidget.refreshAll();
await renderMentions();
} else {
await editor.dispatch({});
}
} }
export async function renderMentions(): Promise<CodeWidgetContent | null> { export async function renderMentions(): Promise<CodeWidgetContent | null> {
console.log("Hide mentions", await clientStore.get(hideMentionsKey));
if (await clientStore.get(hideMentionsKey)) {
return null;
}
console.log("Stil here");
const page = await editor.getCurrentPage(); const page = await editor.getCurrentPage();
const linksResult = await queryObjects<LinkObject>("link", { const linksResult = await queryObjects<LinkObject>("link", {
// Query all links that point to this page, excluding those that are inside directives and self pointers. // Query all links that point to this page, excluding those that are inside directives and self pointers.
@ -41,6 +43,12 @@ export async function renderMentions(): Promise<CodeWidgetContent | null> {
} }
return { return {
markdown: renderedMd, markdown: renderedMd,
buttons: [{
description: "Hide",
svg:
`<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-eye-off"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"></path><line x1="1" y1="1" x2="23" y2="23"></line></svg>`,
invokeFunction: "index.toggleMentions",
}],
}; };
} }
} }

View File

@ -1,10 +1,10 @@
import { import {
clientStore, clientStore,
codeWidget,
editor, editor,
markdown, markdown,
system,
} from "$sb/silverbullet-syscall/mod.ts"; } from "$sb/silverbullet-syscall/mod.ts";
import { renderToText, traverseTree, traverseTreeAsync } from "$sb/lib/tree.ts"; import { renderToText, traverseTree } from "$sb/lib/tree.ts";
import { CodeWidgetContent } from "$sb/types.ts"; import { CodeWidgetContent } from "$sb/types.ts";
const hideTOCKey = "hideTOC"; const hideTOCKey = "hideTOC";
@ -16,23 +16,18 @@ type Header = {
level: number; level: number;
}; };
let cachedTOC: string | undefined;
export async function toggleTOC() { export async function toggleTOC() {
cachedTOC = undefined;
let hideTOC = await clientStore.get(hideTOCKey); let hideTOC = await clientStore.get(hideTOCKey);
hideTOC = !hideTOC; hideTOC = !hideTOC;
await clientStore.set(hideTOCKey, hideTOC); await clientStore.set(hideTOCKey, hideTOC);
await renderTOC(); // This will hide it if needed await codeWidget.refreshAll();
} }
async function markdownToHtml(text: string): Promise<string> { export async function refreshTOC() {
return system.invokeFunction("markdown.markdownToHtml", text); await codeWidget.refreshAll();
} }
export async function renderTOC( export async function renderTOC(): Promise<CodeWidgetContent | null> {
reload = false,
): Promise<CodeWidgetContent | null> {
if (await clientStore.get(hideTOCKey)) { if (await clientStore.get(hideTOCKey)) {
return null; return null;
} }
@ -40,7 +35,7 @@ export async function renderTOC(
const text = await editor.getText(); const text = await editor.getText();
const tree = await markdown.parseMarkdown(text); const tree = await markdown.parseMarkdown(text);
const headers: Header[] = []; const headers: Header[] = [];
await traverseTreeAsync(tree, async (n) => { traverseTree(tree, (n) => {
if (n.type?.startsWith("ATXHeading")) { if (n.type?.startsWith("ATXHeading")) {
headers.push({ headers.push({
name: n.children!.slice(1).map(renderToText).join("").trim(), name: n.children!.slice(1).map(renderToText).join("").trim(),
@ -66,5 +61,19 @@ export async function renderTOC(
// console.log("Markdown", renderedMd); // console.log("Markdown", renderedMd);
return { return {
markdown: renderedMd, markdown: renderedMd,
buttons: [
{
description: "Reload",
svg:
`<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-refresh-cw"><polyline points="23 4 23 10 17 10"></polyline><polyline points="1 20 1 14 7 14"></polyline><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path></svg>`,
invokeFunction: "index.refreshTOC",
},
{
description: "Hide",
svg:
`<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-eye-off"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"></path><line x1="1" y1="1" x2="23" y2="23"></line></svg>`,
invokeFunction: "index.toggleTOC",
},
],
}; };
} }

View File

@ -26,11 +26,15 @@ functions:
- editor:complete - editor:complete
refreshAllWidgets: refreshAllWidgets:
path: widget.ts:refreshAll path: widget.ts:refreshAllWidgets
command: command:
name: "Live Queries and Templates: Refresh All" name: "Live Queries and Templates: Refresh All"
key: "Alt-q" key: "Alt-q"
# Query widget buttons
editButton:
path: widget.ts:editButton
# Slash commands # Slash commands
insertQuery: insertQuery:
redirect: template.insertTemplateText redirect: template.insertTemplateText

View File

@ -9,12 +9,12 @@ import { astToKvQuery } from "$sb/lib/parse-query.ts";
import { jsonToMDTable, renderQueryTemplate } from "../directive/util.ts"; import { jsonToMDTable, renderQueryTemplate } from "../directive/util.ts";
import { loadPageObject, replaceTemplateVars } from "../template/template.ts"; import { loadPageObject, replaceTemplateVars } from "../template/template.ts";
import { cleanPageRef, resolvePath } from "$sb/lib/resolve.ts"; import { cleanPageRef, resolvePath } from "$sb/lib/resolve.ts";
import { LintDiagnostic } from "$sb/types.ts"; import { CodeWidgetContent, LintDiagnostic } from "$sb/types.ts";
export async function widget( export async function widget(
bodyText: string, bodyText: string,
pageName: string, pageName: string,
): Promise<WidgetContent> { ): Promise<CodeWidgetContent> {
const pageObject = await loadPageObject(pageName); const pageObject = await loadPageObject(pageName);
try { try {
@ -72,6 +72,20 @@ export async function widget(
return { return {
markdown: resultMarkdown, markdown: resultMarkdown,
buttons: [
{
description: "Edit",
svg:
`<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-edit"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg>`,
invokeFunction: "query.editButton",
},
{
description: "Reload",
svg:
`<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-refresh-cw"><polyline points="23 4 23 10 17 10"></polyline><polyline points="1 20 1 14 7 14"></polyline><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path></svg>`,
invokeFunction: "query.refreshAllWidgets",
},
],
}; };
} catch (e: any) { } catch (e: any) {
return { markdown: `**Error:** ${e.message}` }; return { markdown: `**Error:** ${e.message}` };

View File

@ -1,5 +1,9 @@
import { codeWidget } from "$sb/syscalls.ts"; import { codeWidget, editor } from "$sb/syscalls.ts";
export function refreshAll() { export function refreshAllWidgets() {
codeWidget.refreshAll(); codeWidget.refreshAll();
} }
export async function editButton(pos: number) {
await editor.moveCursor(pos);
}

View File

@ -98,7 +98,7 @@ export class ServerSystem {
), ),
eventHook, eventHook,
); );
const space = new Space(this.spacePrimitives, this.ds, eventHook); const space = new Space(this.spacePrimitives, eventHook);
// Add syscalls // Add syscalls
this.system.registerSyscalls( this.system.registerSyscalls(

View File

@ -38,7 +38,7 @@ import { OpenPages } from "./open_pages.ts";
import { MainUI } from "./editor_ui.tsx"; import { MainUI } from "./editor_ui.tsx";
import { cleanPageRef } from "$sb/lib/resolve.ts"; import { cleanPageRef } from "$sb/lib/resolve.ts";
import { SpacePrimitives } from "../common/spaces/space_primitives.ts"; import { SpacePrimitives } from "../common/spaces/space_primitives.ts";
import { FileMeta, PageMeta } from "$sb/types.ts"; import { CodeWidgetButton, FileMeta, PageMeta } from "$sb/types.ts";
import { DataStore } from "../plugos/lib/datastore.ts"; import { DataStore } from "../plugos/lib/datastore.ts";
import { IndexedDBKvPrimitives } from "../plugos/lib/indexeddb_kv_primitives.ts"; import { IndexedDBKvPrimitives } from "../plugos/lib/indexeddb_kv_primitives.ts";
import { DataStoreMQ } from "../plugos/lib/mq.datastore.ts"; import { DataStoreMQ } from "../plugos/lib/mq.datastore.ts";
@ -188,13 +188,7 @@ export class Client {
// Load settings // Load settings
this.settings = await ensureSettingsAndIndex(localSpacePrimitives); this.settings = await ensureSettingsAndIndex(localSpacePrimitives);
// Load widget cache await this.loadCaches();
this.widgetCache = new LimitedMap(
100,
await this.stateDataStore.get(["cache", "widgets"]) ||
{},
);
// Pinging a remote space to ensure we're authenticated properly, if not will result in a redirect to auth page // Pinging a remote space to ensure we're authenticated properly, if not will result in a redirect to auth page
try { try {
await this.httpSpacePrimitives.ping(); await this.httpSpacePrimitives.ping();
@ -484,7 +478,6 @@ export class Client {
this.space = new Space( this.space = new Space(
localSpacePrimitives, localSpacePrimitives,
this.stateDataStore,
this.eventHook, this.eventHook,
); );
@ -1013,7 +1006,56 @@ export class Client {
return; return;
} }
private widgetCache = new LimitedMap<WidgetCacheItem>(100); // Widget and image height caching
private widgetCache = new LimitedMap<WidgetCacheItem>(100); // bodyText -> WidgetCacheItem
private imageHeightCache = new LimitedMap<number>(100); // url -> height
private widgetHeightCache = new LimitedMap<number>(100); // bodytext -> height
async loadCaches() {
const [imageHeightCache, widgetHeightCache, widgetCache] = await this
.stateDataStore.batchGet([["cache", "imageHeight"], [
"cache",
"widgetHeight",
], ["cache", "widgets"]]);
this.imageHeightCache = new LimitedMap(100, imageHeightCache || {});
this.widgetHeightCache = new LimitedMap(100, widgetHeightCache || {});
this.widgetCache = new LimitedMap(100, widgetCache || {});
}
debouncedImageCacheFlush = throttle(() => {
this.stateDataStore.set(["cache", "imageHeight"], this.imageHeightCache)
.catch(
console.error,
);
console.log("Flushed image height cache to store");
}, 5000);
setCachedImageHeight(url: string, height: number) {
this.imageHeightCache.set(url, height);
this.debouncedImageCacheFlush();
}
getCachedImageHeight(url: string): number {
return this.imageHeightCache.get(url) ?? -1;
}
debouncedWidgetHeightCacheFlush = throttle(() => {
this.stateDataStore.set(
["cache", "widgetHeight"],
this.widgetHeightCache.toJSON(),
)
.catch(
console.error,
);
// console.log("Flushed widget height cache to store");
}, 5000);
setCachedWidgetHeight(bodyText: string, height: number) {
this.widgetHeightCache.set(bodyText, height);
this.debouncedWidgetHeightCacheFlush();
}
getCachedWidgetHeight(bodyText: string): number {
return this.widgetHeightCache.get(bodyText) ?? -1;
}
debouncedWidgetCacheFlush = throttle(() => { debouncedWidgetCacheFlush = throttle(() => {
this.stateDataStore.set(["cache", "widgets"], this.widgetCache.toJSON()) this.stateDataStore.set(["cache", "widgets"], this.widgetCache.toJSON())
@ -1023,8 +1065,8 @@ export class Client {
console.log("Flushed widget cache to store"); console.log("Flushed widget cache to store");
}, 5000); }, 5000);
setWidgetCache(key: string, height: number, html: string) { setWidgetCache(key: string, cacheItem: WidgetCacheItem) {
this.widgetCache.set(key, { height, html }); this.widgetCache.set(key, cacheItem);
this.debouncedWidgetCacheFlush(); this.debouncedWidgetCacheFlush();
} }
@ -1036,4 +1078,5 @@ export class Client {
type WidgetCacheItem = { type WidgetCacheItem = {
height: number; height: number;
html: string; html: string;
buttons?: CodeWidgetButton[];
}; };

View File

@ -57,7 +57,7 @@ export class IFrameWidget extends WidgetType {
} }
get estimatedHeight(): number { get estimatedHeight(): number {
const cachedHeight = this.client.space.getCachedWidgetHeight(this.bodyText); const cachedHeight = this.client.getCachedWidgetHeight(this.bodyText);
// console.log("Calling estimated height", cachedHeight); // console.log("Calling estimated height", cachedHeight);
return cachedHeight > 0 ? cachedHeight : 150; return cachedHeight > 0 ? cachedHeight : 150;
} }

View File

@ -25,7 +25,7 @@ class InlineImageWidget extends WidgetType {
} }
get estimatedHeight(): number { get estimatedHeight(): number {
const cachedHeight = this.client.space.getCachedImageHeight(this.url); const cachedHeight = this.client.getCachedImageHeight(this.url);
// console.log("Estimated height requested", this.url, cachedHeight); // console.log("Estimated height requested", this.url, cachedHeight);
return cachedHeight; return cachedHeight;
} }
@ -35,11 +35,11 @@ class InlineImageWidget extends WidgetType {
let url = this.url; let url = this.url;
url = resolvePath(this.client.currentPage!, url, true); url = resolvePath(this.client.currentPage!, url, true);
// console.log("Creating DOM", this.url); // console.log("Creating DOM", this.url);
const cachedImageHeight = this.client.space.getCachedImageHeight(url); const cachedImageHeight = this.client.getCachedImageHeight(url);
img.onload = () => { img.onload = () => {
// console.log("Loaded", this.url, "with height", img.height); // console.log("Loaded", this.url, "with height", img.height);
if (img.height !== cachedImageHeight) { if (img.height !== cachedImageHeight) {
this.client.space.setCachedImageHeight(url, img.height); this.client.setCachedImageHeight(url, img.height);
} }
}; };
img.src = url; img.src = url;

View File

@ -1,14 +1,17 @@
import { WidgetType } from "../deps.ts"; import { WidgetType } from "../deps.ts";
import type { Client } from "../client.ts"; import type { Client } from "../client.ts";
import type { CodeWidgetCallback } from "$sb/types.ts"; import type { CodeWidgetButton, CodeWidgetCallback } from "$sb/types.ts";
import { renderMarkdownToHtml } from "../../plugs/markdown/markdown_render.ts"; import { renderMarkdownToHtml } from "../../plugs/markdown/markdown_render.ts";
import { resolveAttachmentPath } from "$sb/lib/resolve.ts"; import { resolveAttachmentPath } from "$sb/lib/resolve.ts";
import { parse } from "../../common/markdown_parser/parse_tree.ts"; import { parse } from "../../common/markdown_parser/parse_tree.ts";
import buildMarkdown from "../../common/markdown_parser/parser.ts"; import buildMarkdown from "../../common/markdown_parser/parser.ts";
import { renderToText } from "$sb/lib/tree.ts"; import { renderToText } from "$sb/lib/tree.ts";
const activeWidgets = new Set<MarkdownWidget>();
export class MarkdownWidget extends WidgetType { export class MarkdownWidget extends WidgetType {
renderedMarkdown?: string; renderedMarkdown?: string;
public dom?: HTMLElement;
constructor( constructor(
readonly from: number | undefined, readonly from: number | undefined,
@ -27,19 +30,20 @@ export class MarkdownWidget extends WidgetType {
if (cacheItem) { if (cacheItem) {
div.innerHTML = this.wrapHtml( div.innerHTML = this.wrapHtml(
cacheItem.html, cacheItem.html,
this.from !== undefined, cacheItem.buttons,
this.from !== undefined,
); );
this.attachListeners(div); this.attachListeners(div, cacheItem.buttons);
} }
// Async kick-off of content renderer // Async kick-off of content renderer
this.renderContent(div, cacheItem?.html).catch(console.error); this.renderContent(div, cacheItem?.html).catch(console.error);
this.dom = div;
return div; return div;
} }
private async renderContent( async renderContent(
div: HTMLElement, div: HTMLElement,
cachedHtml: string | undefined, cachedHtml: string | undefined,
) { ) {
@ -47,6 +51,7 @@ export class MarkdownWidget extends WidgetType {
this.bodyText, this.bodyText,
this.client.currentPage!, this.client.currentPage!,
); );
activeWidgets.add(this);
if (!widgetContent) { if (!widgetContent) {
div.innerHTML = ""; div.innerHTML = "";
// div.style.display = "none"; // div.style.display = "none";
@ -89,42 +94,30 @@ export class MarkdownWidget extends WidgetType {
// HTML still same as in cache, no need to re-render // HTML still same as in cache, no need to re-render
return; return;
} }
div.innerHTML = this.wrapHtml( div.innerHTML = this.wrapHtml(html, widgetContent.buttons);
html, this.attachListeners(div, widgetContent.buttons);
this.from !== undefined,
this.from !== undefined,
);
this.attachListeners(div);
// Let's give it a tick, then measure and cache // Let's give it a tick, then measure and cache
setTimeout(() => { setTimeout(() => {
this.client.setWidgetCache( this.client.setWidgetCache(
this.bodyText, this.bodyText,
div.clientHeight, { height: div.clientHeight, html, buttons: widgetContent.buttons },
html,
); );
}); });
} }
private wrapHtml(html: string, editButton = true, sourceButton = true) { private wrapHtml(html: string, buttons?: CodeWidgetButton[]) {
return ` if (!buttons) {
<div class="button-bar"> return html;
${
sourceButton
? `<button class="source-button" title="Show Markdown source"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-code"><polyline points="16 18 22 12 16 6"></polyline><polyline points="8 6 2 12 8 18"></polyline></svg></button>`
: ""
} }
<button class="reload-button" title="Reload"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"></polyline><polyline points="1 20 1 14 7 14"></polyline><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path></svg></button> return `<div class="button-bar">${
${ buttons.map((button, idx) =>
editButton `<button data-button="${idx}" title="${button.description}">${button.svg}</button> `
? `<button class="edit-button" title="Edit"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-edit"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg></button>` ).join("")
: "" }</div>${html}`;
}
</div>
${html}`;
} }
private attachListeners(div: HTMLElement) { private attachListeners(div: HTMLElement, buttons?: CodeWidgetButton[]) {
div.querySelectorAll("a[data-ref]").forEach((el_) => { div.querySelectorAll("a[data-ref]").forEach((el_) => {
const el = el_ as HTMLElement; const el = el_ as HTMLElement;
// Override default click behavior with a local navigate (faster) // Override default click behavior with a local navigate (faster)
@ -160,20 +153,30 @@ export class MarkdownWidget extends WidgetType {
); );
}); });
if (this.from !== undefined) { if (!buttons) {
div.querySelector(".edit-button")!.addEventListener("click", () => { buttons = [];
this.client.editorView.dispatch({
selection: { anchor: this.from! },
});
this.client.focus();
});
div.querySelector(".source-button")!.addEventListener("click", () => {
div.innerText = this.renderedMarkdown!;
});
} }
div.querySelector(".reload-button")!.addEventListener("click", () => {
this.renderContent(div, undefined).catch(console.error); for (let i = 0; i < buttons.length; i++) {
}); const button = buttons[i];
div.querySelector(`button[data-button="${i}"]`)!.addEventListener(
"click",
() => {
this.client.system.localSyscall("system.invokeFunction", [
button.invokeFunction,
this.from,
]).then((newContent: string | undefined) => {
if (newContent) {
div.innerText = newContent;
}
this.client.focus();
}).catch(console.error);
},
);
}
// div.querySelectorAll("ul > li").forEach((el) => {
// el.classList.add("sb-line-li-1", "sb-line-ul");
// });
} }
get estimatedHeight(): number { get estimatedHeight(): number {
@ -189,3 +192,25 @@ export class MarkdownWidget extends WidgetType {
); );
} }
} }
export function reloadAllMarkdownWidgets() {
for (const widget of activeWidgets) {
// Garbage collect as we go
if (!widget.dom || !widget.dom.parentNode) {
activeWidgets.delete(widget);
continue;
}
widget.renderContent(widget.dom!, undefined).catch(console.error);
}
}
function garbageCollectWidgets() {
for (const widget of activeWidgets) {
if (!widget.dom || !widget.dom.parentNode) {
// console.log("Garbage collecting widget", widget.bodyText);
activeWidgets.delete(widget);
}
}
}
setInterval(garbageCollectWidgets, 5000);

View File

@ -1,4 +1,4 @@
import { Decoration, EditorState, WidgetType } from "../deps.ts"; import { Decoration, EditorState } from "../deps.ts";
import type { Client } from "../client.ts"; import type { Client } from "../client.ts";
import { decoratorStateField } from "./util.ts"; import { decoratorStateField } from "./util.ts";
import { MarkdownWidget } from "./markdown_widget.ts"; import { MarkdownWidget } from "./markdown_widget.ts";

View File

@ -149,7 +149,7 @@ export function mountIFrame(
case "setHeight": case "setHeight":
iframe.height = data.height + "px"; iframe.height = data.height + "px";
if (widgetHeightCacheKey) { if (widgetHeightCacheKey) {
client.space.setCachedWidgetHeight( client.setCachedWidgetHeight(
widgetHeightCacheKey, widgetHeightCacheKey,
data.height, data.height,
); );

View File

@ -11,40 +11,6 @@ import { LimitedMap } from "../common/limited_map.ts";
const pageWatchInterval = 5000; const pageWatchInterval = 5000;
export class Space { export class Space {
imageHeightCache = new LimitedMap<number>(100); // url -> height
widgetHeightCache = new LimitedMap<number>(100); // bodytext -> height
debouncedImageCacheFlush = throttle(() => {
this.ds.set(["cache", "imageHeight"], this.imageHeightCache).catch(
console.error,
);
console.log("Flushed image height cache to store");
}, 5000);
setCachedImageHeight(url: string, height: number) {
this.imageHeightCache.set(url, height);
this.debouncedImageCacheFlush();
}
getCachedImageHeight(url: string): number {
return this.imageHeightCache.get(url) ?? -1;
}
debouncedWidgetCacheFlush = throttle(() => {
this.ds.set(["cache", "widgetHeight"], this.widgetHeightCache.toJSON())
.catch(
console.error,
);
// console.log("Flushed widget height cache to store");
}, 5000);
setCachedWidgetHeight(bodyText: string, height: number) {
this.widgetHeightCache.set(bodyText, height);
this.debouncedWidgetCacheFlush();
}
getCachedWidgetHeight(bodyText: string): number {
return this.widgetHeightCache.get(bodyText) ?? -1;
}
// We do watch files in the background to detect changes // We do watch files in the background to detect changes
// This set of pages should only ever contain 1 page // This set of pages should only ever contain 1 page
watchedPages = new Set<string>(); watchedPages = new Set<string>();
@ -55,20 +21,8 @@ export class Space {
constructor( constructor(
readonly spacePrimitives: SpacePrimitives, readonly spacePrimitives: SpacePrimitives,
private ds: DataStore,
eventHook: EventHook, eventHook: EventHook,
) { ) {
// super();
this.ds.batchGet([["cache", "imageHeight"], ["cache", "widgetHeight"]])
.then(([imageCache, widgetCache]) => {
if (imageCache) {
this.imageHeightCache = new LimitedMap(100, imageCache);
}
if (widgetCache) {
// console.log("Loaded widget cache from store", widgetCache);
this.widgetHeightCache = new LimitedMap(100, widgetCache);
}
});
eventHook.addLocalListener("page:deleted", (pageName: string) => { eventHook.addLocalListener("page:deleted", (pageName: string) => {
if (this.watchedPages.has(pageName)) { if (this.watchedPages.has(pageName)) {
// Stop watching deleted pages already // Stop watching deleted pages already

View File

@ -451,13 +451,13 @@
.sb-markdown-bottom-widget h1 { .sb-markdown-bottom-widget h1 {
border-top-right-radius: 5px; border-top-right-radius: 5px;
border-top-left-radius: 5px; border-top-left-radius: 5px;
margin: 0; margin: 0 0 5px 0;
padding: 10px !important; padding: 10px !important;
background-color: var(--editor-directive-background-color); background-color: var(--editor-directive-background-color);
font-size: 1.2em; font-size: 1.2em;
} }
.sb-markdown-top-widget { .sb-markdown-top-widget:has(*) {
margin-bottom: 10px; margin-bottom: 10px;
} }
@ -471,7 +471,7 @@
overflow-y: scroll; overflow-y: scroll;
border: 1px solid var(--editor-directive-background-color); border: 1px solid var(--editor-directive-background-color);
border-radius: 5px; border-radius: 5px;
white-space: nowrap; white-space: wrap;
position: relative; position: relative;
ul, ul,
@ -482,7 +482,7 @@
ul { ul {
list-style: none; list-style: none;
padding-left: 1ch; // padding-left: 1ch;
} }
ul li::before { ul li::before {
@ -493,7 +493,7 @@
/* Needed to add space between the bullet and the text */ /* Needed to add space between the bullet and the text */
width: 1em; width: 1em;
/* Also needed for space (tweak if needed) */ /* Also needed for space (tweak if needed) */
// margin-left: -1em; margin-left: -1em;
} }
h1, h1,
@ -528,27 +528,24 @@
.button-bar { .button-bar {
position: absolute; position: absolute;
right: 6px; right: 10px;
top: 6px; top: 10px;
display: none; display: none;
background: var(--editor-directive-background-color); background: var(--editor-directive-background-color);
padding-inline: 3px; padding-inline: 3px;
padding-bottom: 1px; padding-bottom: 1px;
border-radius: 5px; border-radius: 5px;
button { button {
border: none; border: none;
background: none; background: none;
cursor: pointer; cursor: pointer;
color: var(--root-color); color: var(--root-color);
margin-right: -8px;
} }
}
.edit-button,
.reload-button {
margin-left: -10px;
} }
} }
.sb-fenced-code-iframe { .sb-fenced-code-iframe {

View File

@ -1,10 +1,12 @@
import { SysCallMapping } from "../../plugos/system.ts"; import { SysCallMapping } from "../../plugos/system.ts";
import { reloadAllMarkdownWidgets } from "../cm_plugins/markdown_widget.ts";
import { broadcastReload } from "../components/widget_sandbox_iframe.ts"; import { broadcastReload } from "../components/widget_sandbox_iframe.ts";
export function clientCodeWidgetSyscalls(): SysCallMapping { export function clientCodeWidgetSyscalls(): SysCallMapping {
return { return {
"codeWidget.refreshAll": () => { "codeWidget.refreshAll": () => {
broadcastReload(); broadcastReload();
reloadAllMarkdownWidgets();
}, },
}; };
} }