// Islands infrastructure. // // An "island" is a custom element that attaches behavior to server-rendered // markup — it never re-renders the server's HTML (no hydration). A key payoff // over the old `querySelectorAll` + `DOMContentLoaded` + `htmx:afterSwap` // re-init dance: the browser upgrades a custom element automatically whenever // it's connected to the document, INCLUDING content HTMX swaps in, so islands // need no manual re-initialization after a swap. // // Pure, testable logic goes in a sibling `*.logic.ts` (no DOM imports) so // `node --test` can exercise it without a DOM; the element file wires that // logic over the light-DOM children. /** Base class: runs `init()` exactly once, the first time the element is * connected (a move/reconnect won't re-run it). */ export abstract class MnwElement extends HTMLElement { #initialized = false; connectedCallback(): void { if (this.#initialized) return; this.#initialized = true; this.init(); } /** Wire behavior over the already-rendered light-DOM children. */ protected abstract init(): void; } /** Define a custom element once (idempotent — safe if the module is imported * from more than one entry). */ export function define(name: string, ctor: CustomElementConstructor): void { if (!customElements.get(name)) customElements.define(name, ctor); }