import type { Neko as DesktopWindow } from "https://deno.land/x/neko/core/Neko.ts"; export default class MemViz { #width?: number; #height?: number; #framebuffer?: Uint8Array; #window?: DesktopWindow; static async new(buffer: ArrayBufferLike, title = "memviz") { if (!("window" in globalThis)) throw "MemViz must be in Main thread"; const self = new MemViz(MemViz._guard); self.#height = self.#width = Math.floor(Math.sqrt(buffer.byteLength) / 2); if ("Deno" in globalThis) { self.#window = new (await import("https://deno.land/x/neko/core/Neko.ts")) .Neko({ width: self.#width, height: self.#height, title, // resize: true, topmost: true, }); self.#framebuffer = new Uint8Array(buffer); } else { self.#img = new ImageData( new Uint8ClampedArray(buffer), self.#width, self.#height, ); const canvas: HTMLCanvasElement = document.querySelector("canvas#memviz") ?? document.createElement("canvas"); self.#ctx = canvas.getContext("2d")!; document.body.append(canvas); } return self; } draw() { if ("Deno" in globalThis) { this.#window!.setFrameBuffer( this.#framebuffer!, this.#width, this.#height, ); } else this.#ctx!.putImageData(this.#img!, 0, 0); } #ctx?: CanvasRenderingContext2D; #img?: ImageData; private static readonly _guard = Symbol(); // to bad typesccrypt can't do typeof MemViz.#guard constructor(guard: typeof MemViz._guard) { if (guard !== MemViz._guard) throw null; } }