Experiment to rebuild Diffuse using web applets.
1import type {
2 Applet,
3 AppletEvent,
4} from "https://denopkg.com/unternet-co/web-applets@e3e9ca6/sdk/src/index.ts";
5import { applets } from "@web-applets/sdk";
6import { effect, signal } from "spellcaster/spellcaster.js";
7
8////////////////////////////////////////////
9// 🪟 Applet initialiser
10////////////////////////////////////////////
11export async function applet<D>(
12 src: string,
13 opts: { setHeight?: boolean } = {},
14): Promise<Applet<D>> {
15 const frame: HTMLIFrameElement | null = document.querySelector(
16 `[src="${src}${src.endsWith("/") ? "" : "/"}"]`,
17 );
18
19 if (frame === null) throw new Error("iframe element not found, src: " + src);
20 if (frame.contentWindow === null) {
21 throw new Error("iframe does not have a contentWindow");
22 }
23
24 const applet = await applets.connect<D>(frame.contentWindow);
25
26 if (opts.setHeight) {
27 applet.onresize = () => {
28 frame.height = `${applet.height}px`;
29 frame.classList.add("has-loaded");
30 };
31 } else {
32 if (frame.contentDocument?.readyState === "complete") {
33 frame.classList.add("has-loaded");
34 }
35
36 frame.addEventListener("load", () => {
37 frame.classList.add("has-loaded");
38 });
39 }
40
41 return applet;
42}
43
44////////////////////////////////////////////
45// 🔮 Reactive state management
46////////////////////////////////////////////
47export function reactive<D, T>(
48 applet: Applet<D>,
49 dataFn: (data: D) => T,
50 effectFn: (t: T) => void,
51) {
52 const [getter, setter] = signal(
53 dataFn(applet.data),
54 );
55
56 effect(() => effectFn(getter()));
57
58 applet.addEventListener("data", (event: AppletEvent) => {
59 setter(dataFn(event.data));
60 });
61}