Experiment to rebuild Diffuse using web applets.
1import type { Applet, AppletEvent } from "@web-applets/sdk";
2
3import { applets } from "@web-applets/sdk";
4import { effect, signal } from "spellcaster/spellcaster.js";
5import { xxh32 } from "xxh32";
6
7////////////////////////////////////////////
8// 🪟 Applet initialiser
9////////////////////////////////////////////
10export async function applet<D>(
11 src: string,
12 opts: {
13 addSlashSuffix?: boolean;
14 context?: Window;
15 id?: string;
16 setHeight?: boolean;
17 } = {},
18): Promise<Applet<D>> {
19 src = `${src}${
20 src.endsWith("/")
21 ? ""
22 : opts.addSlashSuffix === undefined || opts.addSlashSuffix === true
23 ? "/"
24 : ""
25 }`;
26
27 const existingFrame: HTMLIFrameElement | null = (opts.context || window).document.querySelector(
28 `[src="${src}"]`,
29 );
30
31 let frame;
32
33 if (existingFrame) {
34 frame = existingFrame;
35 } else {
36 frame = document.createElement("iframe");
37 frame.src = src;
38 if (opts.id) frame.id = opts.id;
39
40 document.body.appendChild(frame);
41 }
42
43 if (frame.contentWindow === null) {
44 throw new Error("iframe does not have a contentWindow");
45 }
46
47 const applet = await applets.connect<D>(frame.contentWindow);
48
49 if (opts.setHeight) {
50 applet.onresize = () => {
51 frame.height = `${applet.height}px`;
52 frame.classList.add("has-loaded");
53 };
54 } else {
55 if (frame.contentDocument?.readyState === "complete") {
56 frame.classList.add("has-loaded");
57 }
58
59 frame.addEventListener("load", () => {
60 frame.classList.add("has-loaded");
61 });
62 }
63
64 return applet;
65}
66
67////////////////////////////////////////////
68// 🔮 Reactive state management
69////////////////////////////////////////////
70export function reactive<D, T>(
71 applet: Applet<D>,
72 dataFn: (data: D) => T,
73 effectFn: (t: T) => void,
74) {
75 const [getter, setter] = signal(dataFn(applet.data));
76
77 effect(() => effectFn(getter()));
78
79 applet.addEventListener("data", (event: AppletEvent) => {
80 setter(dataFn(event.data));
81 });
82}
83
84////////////////////////////////////////////
85// 🛠️
86////////////////////////////////////////////
87export function comparable(value: unknown) {
88 return xxh32(JSON.stringify(value));
89}
90
91export function isPrimitive(test: unknown) {
92 return test !== Object(test);
93}