* Don't require auth for .ping * Finally introducing loggers * Remove file list caching from disk_space_primitives I don't think this is required anymore with the sync model * Simplification of space primitives and more tests * More reliable online status * Add .zed settings * Remove selfUpdate argument from `writeFile` no longer used - Add `sync` config schema and document sync options - Move sync config handling to client boot logic - Move BootConfig type to ui_types.ts - Remove sync env vars from server - Update documentation for sync and architecture * Improve sync logic and proxy routing for full sync state * Add BoxProxy for late-bound client and improve Lua boot - Add isMobile syscall for editor * Add concurrency to sync process and log indexing duration * Better service worker lifecycle management
156 lines
4.1 KiB
TypeScript
156 lines
4.1 KiB
TypeScript
import type { EditorState } from "@codemirror/state";
|
|
import { Decoration, WidgetType } from "@codemirror/view";
|
|
import type { Client } from "../client.ts";
|
|
import { decoratorStateField } from "./util.ts";
|
|
import { LuaWidget, type LuaWidgetContent } from "./lua_widget.ts";
|
|
import { activeWidgets } from "./code_widget.ts";
|
|
|
|
class ArrayWidget extends WidgetType {
|
|
public dom?: HTMLElement;
|
|
|
|
constructor(
|
|
readonly client: Client,
|
|
readonly cacheKey: string,
|
|
readonly callback: (
|
|
pageName: string,
|
|
) => Promise<LuaWidgetContent[] | null>,
|
|
readonly childClass: string,
|
|
) {
|
|
super();
|
|
}
|
|
|
|
override get estimatedHeight(): number {
|
|
const cacheItem = this.client.getWidgetCache(this.cacheKey);
|
|
return cacheItem ? cacheItem.height : -1;
|
|
}
|
|
|
|
toDOM(): HTMLElement {
|
|
activeWidgets.add(this);
|
|
|
|
const div = document.createElement("div");
|
|
div.className = "sb-widget-array";
|
|
|
|
// This doesn't do that much, but it also doesn't really hurt
|
|
const cacheItem = this.client.getWidgetCache(this.cacheKey);
|
|
if (cacheItem) {
|
|
div.innerHTML = cacheItem.html;
|
|
}
|
|
|
|
// Async kick-off of content renderer
|
|
this.renderContent(div).catch(console.error);
|
|
this.dom = div;
|
|
return div;
|
|
}
|
|
|
|
async renderContent(
|
|
div: HTMLElement,
|
|
) {
|
|
const content = await this.callback(this.client.currentName());
|
|
if (!content) return;
|
|
|
|
const renderedWidgets: HTMLElement[] = [];
|
|
|
|
for (const [i, widgetContent] of content.entries()) {
|
|
// Filter out any "empty" widgets. Leaving the content empty, but
|
|
// returning a valid widgets, seems to be a common pattern
|
|
if (
|
|
!widgetContent ||
|
|
widgetContent === "" ||
|
|
(widgetContent instanceof Object &&
|
|
!widgetContent.markdown &&
|
|
!widgetContent.html)
|
|
) continue;
|
|
|
|
const widget = new LuaWidget(
|
|
this.client,
|
|
`${this.cacheKey}:${i}`,
|
|
"",
|
|
() => Promise.resolve(widgetContent),
|
|
false,
|
|
false,
|
|
);
|
|
|
|
// Throw away the wrapper, as it only causes trouble and we are rewrapping
|
|
// anyways
|
|
const html = widget.toDOM().querySelector<HTMLDivElement>(":scope > div");
|
|
if (!html) {
|
|
// This should never really happen, just in case
|
|
console.log("There was an error rendering one of the panel widgets");
|
|
continue;
|
|
}
|
|
|
|
html.classList.add(this.childClass);
|
|
|
|
renderedWidgets.push(html);
|
|
}
|
|
|
|
if (renderedWidgets.length === 0) {
|
|
div.style.display = "none";
|
|
return;
|
|
}
|
|
|
|
div.replaceChildren(...renderedWidgets);
|
|
|
|
// Wait for the clientHeight to settle
|
|
setTimeout(() => {
|
|
this.client.setWidgetCache(this.cacheKey, {
|
|
height: div.clientHeight,
|
|
block: true,
|
|
html: div.innerHTML,
|
|
});
|
|
});
|
|
}
|
|
|
|
override eq(other: WidgetType): boolean {
|
|
// This class isn't really used for stuff that's updated. If that's
|
|
// needed in the future, one could e.g. add a `bodyText` property again
|
|
return other instanceof ArrayWidget && other.cacheKey === this.cacheKey;
|
|
}
|
|
}
|
|
|
|
export function postScriptPrefacePlugin(
|
|
editor: Client,
|
|
) {
|
|
return decoratorStateField((state: EditorState) => {
|
|
if (!editor.clientSystem.scriptsLoaded) {
|
|
// console.info("System not yet ready, not rendering panel widgets.");
|
|
return Decoration.none;
|
|
}
|
|
const widgets: any[] = [];
|
|
|
|
widgets.push(
|
|
Decoration.widget({
|
|
widget: new ArrayWidget(
|
|
editor,
|
|
`top:lua:${editor.currentPath()}`,
|
|
async () =>
|
|
await client.dispatchAppEvent(
|
|
"hooks:renderTopWidgets",
|
|
),
|
|
"sb-lua-top-widget",
|
|
),
|
|
side: -1,
|
|
block: true,
|
|
}).range(0),
|
|
);
|
|
|
|
widgets.push(
|
|
Decoration.widget({
|
|
widget: new ArrayWidget(
|
|
editor,
|
|
`bottom:lua:${editor.currentPath()}`,
|
|
async () =>
|
|
await client.dispatchAppEvent(
|
|
"hooks:renderBottomWidgets",
|
|
),
|
|
"sb-lua-bottom-widget",
|
|
),
|
|
side: 1,
|
|
block: true,
|
|
}).range(state.doc.length),
|
|
);
|
|
|
|
return Decoration.set(widgets);
|
|
});
|
|
}
|