diff --git a/web/cm_plugins/inline_image.ts b/web/cm_plugins/inline_image.ts index 41a9c00..71ba211 100644 --- a/web/cm_plugins/inline_image.ts +++ b/web/cm_plugins/inline_image.ts @@ -16,14 +16,29 @@ class InlineImageWidget extends WidgetType { readonly space: Space, ) { super(); + // console.log("Creating widget", url); } eq(other: InlineImageWidget) { return other.url === this.url && other.title === this.title; } + get estimatedHeight(): number { + const cachedHeight = this.space.getCachedImageHeight(this.url); + // console.log("Estimated height requested", this.url, cachedHeight); + return cachedHeight; + } + toDOM() { const img = document.createElement("img"); + // console.log("Creating DOM", this.url); + const cachedImageHeight = this.space.getCachedImageHeight(this.url); + img.onload = () => { + // console.log("Loaded", this.url, "with height", img.height); + if (img.height !== cachedImageHeight) { + this.space.setCachedImageHeight(this.url, img.height); + } + }; if (this.url.startsWith("http")) { img.src = this.url; } else { @@ -35,6 +50,9 @@ class InlineImageWidget extends WidgetType { img.title = this.title; img.style.display = "block"; img.className = "sb-inline-img"; + if (cachedImageHeight > 0) { + img.height = cachedImageHeight; + } return img; } diff --git a/web/editor.tsx b/web/editor.tsx index dd8de56..9acbd9a 100644 --- a/web/editor.tsx +++ b/web/editor.tsx @@ -270,7 +270,7 @@ export class Editor { }, ); - this.space = new Space(localSpacePrimitives); + this.space = new Space(localSpacePrimitives, this.kvStore); this.space.watch(); this.syncService = new SyncService( diff --git a/web/space.ts b/web/space.ts index 20014b8..525d014 100644 --- a/web/space.ts +++ b/web/space.ts @@ -3,12 +3,9 @@ import { FileMeta } from "../common/types.ts"; import { EventEmitter } from "../plugos/event.ts"; import { plugPrefix } from "../common/spaces/constants.ts"; import { safeRun } from "../common/util.ts"; -import { - base64DecodeDataUrl, - base64EncodedDataUrl, -} from "../plugos/asset_bundle/base64.ts"; -import { mime } from "./deps.ts"; import { AttachmentMeta, PageMeta } from "./types.ts"; +import { DexieKVStore } from "../plugos/lib/kv_store.dexie.ts"; +import { throttle } from "../common/async_util.ts"; export type SpaceEvents = { pageCreated: (meta: PageMeta) => void; @@ -22,6 +19,23 @@ const pageWatchInterval = 5000; export class Space extends EventEmitter { pageMetaCache = new Map(); + imageHeightCache: Record = {}; + + debouncedCacheFlush = throttle(() => { + this.kvStore.set("imageHeightCache", this.imageHeightCache).catch( + console.error, + ); + console.log("Flushed image height cache to store"); + }, 5000); + + setCachedImageHeight(url: string, height: number) { + this.imageHeightCache[url] = height; + this.debouncedCacheFlush(); + } + getCachedImageHeight(url: string): number { + return this.imageHeightCache[url] ?? -1; + } + // We do watch files in the background to detect changes // This set of pages should only ever contain 1 page watchedPages = new Set(); @@ -30,35 +44,19 @@ export class Space extends EventEmitter { private initialPageListLoad = true; private saving = false; - constructor(readonly spacePrimitives: SpacePrimitives) { + constructor( + readonly spacePrimitives: SpacePrimitives, + private kvStore: DexieKVStore, + ) { super(); + this.kvStore.get("imageHeightCache").then((cache) => { + if (cache) { + console.log("Loaded image height cache from KV store", cache); + this.imageHeightCache = cache; + } + }); } - // // Filesystem interface implementation - // async readFile(path: string, encoding: "dataurl" | "utf8"): Promise { - // return (await this.spacePrimitives.readFile(path, encoding)).data as string; - // } - // getFileMeta(path: string): Promise { - // return this.spacePrimitives.getFileMeta(path); - // } - // writeFile( - // path: string, - // text: string, - // encoding: "dataurl" | "utf8", - // ): Promise { - // return this.spacePrimitives.writeFile(path, encoding, text); - // } - // deleteFile(path: string): Promise { - // return this.spacePrimitives.deleteFile(path); - // } - // async listFiles(path: string): Promise { - // return (await this.spacePrimitives.fetchFileList()).filter((f) => - // f.name.startsWith(path) - // ); - // } - - // // The more domain-specific methods - public async updatePageList() { const newPageList = await this.fetchPageList(); const deletedPages = new Set(this.pageMetaCache.keys());