// build/dev/javascript/prelude.mjs var CustomType = class { withFields(fields) { let properties = Object.keys(this).map( (label) => label in fields ? fields[label] : this[label] ); return new this.constructor(...properties); } }; var List = class { static fromArray(array3, tail) { let t = tail || new Empty(); for (let i = array3.length - 1; i >= 0; --i) { t = new NonEmpty(array3[i], t); } return t; } [Symbol.iterator]() { return new ListIterator(this); } toArray() { return [...this]; } // @internal atLeastLength(desired) { for (let _ of this) { if (desired <= 0) return true; desired--; } return desired <= 0; } // @internal hasLength(desired) { for (let _ of this) { if (desired <= 0) return false; desired--; } return desired === 0; } countLength() { let length4 = 0; for (let _ of this) length4++; return length4; } }; function toList(elements, tail) { return List.fromArray(elements, tail); } var ListIterator = class { #current; constructor(current) { this.#current = current; } next() { if (this.#current instanceof Empty) { return { done: true }; } else { let { head, tail } = this.#current; this.#current = tail; return { value: head, done: false }; } } }; var Empty = class extends List { }; var NonEmpty = class extends List { constructor(head, tail) { super(); this.head = head; this.tail = tail; } }; var Result = class _Result extends CustomType { // @internal static isResult(data) { return data instanceof _Result; } }; var Ok = class extends Result { constructor(value) { super(); this[0] = value; } // @internal isOk() { return true; } }; var Error = class extends Result { constructor(detail) { super(); this[0] = detail; } // @internal isOk() { return false; } }; function makeError(variant, module, line, fn, message, extra) { let error = new globalThis.Error(message); error.gleam_error = variant; error.module = module; error.line = line; error.fn = fn; for (let k in extra) error[k] = extra[k]; return error; } // build/dev/javascript/gleam_stdlib/gleam/option.mjs var None = class extends CustomType { }; // build/dev/javascript/gleam_stdlib/dict.mjs var tempDataView = new DataView(new ArrayBuffer(8)); var SHIFT = 5; var BUCKET_SIZE = Math.pow(2, SHIFT); var MASK = BUCKET_SIZE - 1; var MAX_INDEX_NODE = BUCKET_SIZE / 2; var MIN_ARRAY_NODE = BUCKET_SIZE / 4; // build/dev/javascript/gleam_stdlib/gleam_stdlib.mjs function to_string3(term) { return term.toString(); } var unicode_whitespaces = [ " ", // Space " ", // Horizontal tab "\n", // Line feed "\v", // Vertical tab "\f", // Form feed "\r", // Carriage return "\x85", // Next line "\u2028", // Line separator "\u2029" // Paragraph separator ].join(); var left_trim_regex = new RegExp(`^([${unicode_whitespaces}]*)`, "g"); var right_trim_regex = new RegExp(`([${unicode_whitespaces}]*)$`, "g"); // build/dev/javascript/gleam_stdlib/gleam/int.mjs function to_string2(x) { return to_string3(x); } // build/dev/javascript/gleam_stdlib/gleam/bool.mjs function guard(requirement, consequence, alternative) { if (requirement) { return consequence; } else { return alternative(); } } // build/dev/javascript/lustre/lustre/effect.mjs var Effect = class extends CustomType { constructor(all) { super(); this.all = all; } }; function none() { return new Effect(toList([])); } // build/dev/javascript/lustre/lustre/internals/vdom.mjs var Text = class extends CustomType { constructor(content) { super(); this.content = content; } }; var Element = class extends CustomType { constructor(key, namespace, tag, attrs, children, self_closing, void$) { super(); this.key = key; this.namespace = namespace; this.tag = tag; this.attrs = attrs; this.children = children; this.self_closing = self_closing; this.void = void$; } }; var Event = class extends CustomType { constructor(x0, x1) { super(); this[0] = x0; this[1] = x1; } }; // build/dev/javascript/lustre/lustre/attribute.mjs function on(name, handler) { return new Event("on" + name, handler); } // build/dev/javascript/lustre/lustre/element.mjs function element(tag, attrs, children) { if (tag === "area") { return new Element("", "", tag, attrs, toList([]), false, true); } else if (tag === "base") { return new Element("", "", tag, attrs, toList([]), false, true); } else if (tag === "br") { return new Element("", "", tag, attrs, toList([]), false, true); } else if (tag === "col") { return new Element("", "", tag, attrs, toList([]), false, true); } else if (tag === "embed") { return new Element("", "", tag, attrs, toList([]), false, true); } else if (tag === "hr") { return new Element("", "", tag, attrs, toList([]), false, true); } else if (tag === "img") { return new Element("", "", tag, attrs, toList([]), false, true); } else if (tag === "input") { return new Element("", "", tag, attrs, toList([]), false, true); } else if (tag === "link") { return new Element("", "", tag, attrs, toList([]), false, true); } else if (tag === "meta") { return new Element("", "", tag, attrs, toList([]), false, true); } else if (tag === "param") { return new Element("", "", tag, attrs, toList([]), false, true); } else if (tag === "source") { return new Element("", "", tag, attrs, toList([]), false, true); } else if (tag === "track") { return new Element("", "", tag, attrs, toList([]), false, true); } else if (tag === "wbr") { return new Element("", "", tag, attrs, toList([]), false, true); } else { return new Element("", "", tag, attrs, children, false, false); } } function text(content) { return new Text(content); } // build/dev/javascript/lustre/lustre/internals/runtime.mjs var Debug = class extends CustomType { constructor(x0) { super(); this[0] = x0; } }; var Dispatch = class extends CustomType { constructor(x0) { super(); this[0] = x0; } }; var Shutdown = class extends CustomType { }; var ForceModel = class extends CustomType { constructor(x0) { super(); this[0] = x0; } }; // build/dev/javascript/lustre/vdom.ffi.mjs function morph(prev, next, dispatch, isComponent = false) { let out; let stack = [{ prev, next, parent: prev.parentNode }]; while (stack.length) { let { prev: prev2, next: next2, parent } = stack.pop(); if (next2.subtree !== void 0) next2 = next2.subtree(); if (next2.content !== void 0) { if (!prev2) { const created = document.createTextNode(next2.content); parent.appendChild(created); out ??= created; } else if (prev2.nodeType === Node.TEXT_NODE) { if (prev2.textContent !== next2.content) prev2.textContent = next2.content; out ??= prev2; } else { const created = document.createTextNode(next2.content); parent.replaceChild(created, prev2); out ??= created; } } else if (next2.tag !== void 0) { const created = createElementNode({ prev: prev2, next: next2, dispatch, stack, isComponent }); if (!prev2) { parent.appendChild(created); } else if (prev2 !== created) { parent.replaceChild(created, prev2); } out ??= created; } else if (next2.elements !== void 0) { iterateElement(next2, (fragmentElement) => { stack.unshift({ prev: prev2, next: fragmentElement, parent }); prev2 = prev2?.nextSibling; }); } else if (next2.subtree !== void 0) { stack.push({ prev: prev2, next: next2, parent }); } } return out; } function createElementNode({ prev, next, dispatch, stack }) { const namespace = next.namespace || "http://www.w3.org/1999/xhtml"; const canMorph = prev && prev.nodeType === Node.ELEMENT_NODE && prev.localName === next.tag && prev.namespaceURI === (next.namespace || "http://www.w3.org/1999/xhtml"); const el2 = canMorph ? prev : namespace ? document.createElementNS(namespace, next.tag) : document.createElement(next.tag); let handlersForEl; if (!registeredHandlers.has(el2)) { const emptyHandlers = /* @__PURE__ */ new Map(); registeredHandlers.set(el2, emptyHandlers); handlersForEl = emptyHandlers; } else { handlersForEl = registeredHandlers.get(el2); } const prevHandlers = canMorph ? new Set(handlersForEl.keys()) : null; const prevAttributes = canMorph ? new Set(Array.from(prev.attributes, (a) => a.name)) : null; let className = null; let style = null; let innerHTML = null; for (const attr of next.attrs) { const name = attr[0]; const value = attr[1]; if (attr.as_property) { if (el2[name] !== value) el2[name] = value; if (canMorph) prevAttributes.delete(name); } else if (name.startsWith("on")) { const eventName = name.slice(2); const callback = dispatch(value); if (!handlersForEl.has(eventName)) { el2.addEventListener(eventName, lustreGenericEventHandler); } handlersForEl.set(eventName, callback); if (canMorph) prevHandlers.delete(eventName); } else if (name.startsWith("data-lustre-on-")) { const eventName = name.slice(15); const callback = dispatch(lustreServerEventHandler); if (!handlersForEl.has(eventName)) { el2.addEventListener(eventName, lustreGenericEventHandler); } handlersForEl.set(eventName, callback); el2.setAttribute(name, value); } else if (name === "class") { className = className === null ? value : className + " " + value; } else if (name === "style") { style = style === null ? value : style + value; } else if (name === "dangerous-unescaped-html") { innerHTML = value; } else { if (el2.getAttribute(name) !== value) el2.setAttribute(name, value); if (name === "value" || name === "selected") el2[name] = value; if (canMorph) prevAttributes.delete(name); } } if (className !== null) { el2.setAttribute("class", className); if (canMorph) prevAttributes.delete("class"); } if (style !== null) { el2.setAttribute("style", style); if (canMorph) prevAttributes.delete("style"); } if (canMorph) { for (const attr of prevAttributes) { el2.removeAttribute(attr); } for (const eventName of prevHandlers) { handlersForEl.delete(eventName); el2.removeEventListener(eventName, lustreGenericEventHandler); } } if (next.key !== void 0 && next.key !== "") { el2.setAttribute("data-lustre-key", next.key); } else if (innerHTML !== null) { el2.innerHTML = innerHTML; return el2; } let prevChild = el2.firstChild; let seenKeys = null; let keyedChildren = null; let incomingKeyedChildren = null; let firstChild = next.children[Symbol.iterator]().next().value; if (canMorph && firstChild !== void 0 && // Explicit checks are more verbose but truthy checks force a bunch of comparisons // we don't care about: it's never gonna be a number etc. firstChild.key !== void 0 && firstChild.key !== "") { seenKeys = /* @__PURE__ */ new Set(); keyedChildren = getKeyedChildren(prev); incomingKeyedChildren = getKeyedChildren(next); } for (const child of next.children) { iterateElement(child, (currElement) => { if (currElement.key !== void 0 && seenKeys !== null) { prevChild = diffKeyedChild( prevChild, currElement, el2, stack, incomingKeyedChildren, keyedChildren, seenKeys ); } else { stack.unshift({ prev: prevChild, next: currElement, parent: el2 }); prevChild = prevChild?.nextSibling; } }); } while (prevChild) { const next2 = prevChild.nextSibling; el2.removeChild(prevChild); prevChild = next2; } return el2; } var registeredHandlers = /* @__PURE__ */ new WeakMap(); function lustreGenericEventHandler(event2) { const target = event2.currentTarget; if (!registeredHandlers.has(target)) { target.removeEventListener(event2.type, lustreGenericEventHandler); return; } const handlersForEventTarget = registeredHandlers.get(target); if (!handlersForEventTarget.has(event2.type)) { target.removeEventListener(event2.type, lustreGenericEventHandler); return; } handlersForEventTarget.get(event2.type)(event2); } function lustreServerEventHandler(event2) { const el2 = event2.currentTarget; const tag = el2.getAttribute(`data-lustre-on-${event2.type}`); const data = JSON.parse(el2.getAttribute("data-lustre-data") || "{}"); const include = JSON.parse(el2.getAttribute("data-lustre-include") || "[]"); switch (event2.type) { case "input": case "change": include.push("target.value"); break; } return { tag, data: include.reduce( (data2, property) => { const path = property.split("."); for (let i = 0, o = data2, e = event2; i < path.length; i++) { if (i === path.length - 1) { o[path[i]] = e[path[i]]; } else { o[path[i]] ??= {}; e = e[path[i]]; o = o[path[i]]; } } return data2; }, { data } ) }; } function getKeyedChildren(el2) { const keyedChildren = /* @__PURE__ */ new Map(); if (el2) { for (const child of el2.children) { iterateElement(child, (currElement) => { const key = currElement?.key || currElement?.getAttribute?.("data-lustre-key"); if (key) keyedChildren.set(key, currElement); }); } } return keyedChildren; } function diffKeyedChild(prevChild, child, el2, stack, incomingKeyedChildren, keyedChildren, seenKeys) { while (prevChild && !incomingKeyedChildren.has(prevChild.getAttribute("data-lustre-key"))) { const nextChild = prevChild.nextSibling; el2.removeChild(prevChild); prevChild = nextChild; } if (keyedChildren.size === 0) { iterateElement(child, (currChild) => { stack.unshift({ prev: prevChild, next: currChild, parent: el2 }); prevChild = prevChild?.nextSibling; }); return prevChild; } if (seenKeys.has(child.key)) { console.warn(`Duplicate key found in Lustre vnode: ${child.key}`); stack.unshift({ prev: null, next: child, parent: el2 }); return prevChild; } seenKeys.add(child.key); const keyedChild = keyedChildren.get(child.key); if (!keyedChild && !prevChild) { stack.unshift({ prev: null, next: child, parent: el2 }); return prevChild; } if (!keyedChild && prevChild !== null) { const placeholder = document.createTextNode(""); el2.insertBefore(placeholder, prevChild); stack.unshift({ prev: placeholder, next: child, parent: el2 }); return prevChild; } if (!keyedChild || keyedChild === prevChild) { stack.unshift({ prev: prevChild, next: child, parent: el2 }); prevChild = prevChild?.nextSibling; return prevChild; } el2.insertBefore(keyedChild, prevChild); stack.unshift({ prev: keyedChild, next: child, parent: el2 }); return prevChild; } function iterateElement(element2, processElement) { if (element2.elements !== void 0) { for (const currElement of element2.elements) { processElement(currElement); } } else { processElement(element2); } } // build/dev/javascript/lustre/client-runtime.ffi.mjs var LustreClientApplication2 = class _LustreClientApplication { #root = null; #queue = []; #effects = []; #didUpdate = false; #isComponent = false; #model = null; #update = null; #view = null; static start(flags, selector, init3, update2, view2) { if (!is_browser()) return new Error(new NotABrowser()); const root2 = selector instanceof HTMLElement ? selector : document.querySelector(selector); if (!root2) return new Error(new ElementNotFound(selector)); const app = new _LustreClientApplication(init3(flags), update2, view2, root2); return new Ok((msg) => app.send(msg)); } constructor([model, effects], update2, view2, root2 = document.body, isComponent = false) { this.#model = model; this.#update = update2; this.#view = view2; this.#root = root2; this.#effects = effects.all.toArray(); this.#didUpdate = true; this.#isComponent = isComponent; window.requestAnimationFrame(() => this.#tick()); } send(action) { switch (true) { case action instanceof Dispatch: { this.#queue.push(action[0]); this.#tick(); return; } case action instanceof Shutdown: { this.#shutdown(); return; } case action instanceof Debug: { this.#debug(action[0]); return; } default: return; } } emit(event2, data) { this.#root.dispatchEvent( new CustomEvent(event2, { bubbles: true, detail: data, composed: true }) ); } #tick() { this.#flush_queue(); if (this.#didUpdate) { const vdom = this.#view(this.#model); const dispatch = (handler) => (e) => { const result = handler(e); if (result instanceof Ok) { this.send(new Dispatch(result[0])); } }; this.#didUpdate = false; this.#root = morph(this.#root, vdom, dispatch, this.#isComponent); } } #flush_queue(iterations = 0) { while (this.#queue.length) { const [next, effects] = this.#update(this.#model, this.#queue.shift()); this.#didUpdate ||= this.#model !== next; this.#model = next; this.#effects = this.#effects.concat(effects.all.toArray()); } while (this.#effects.length) { this.#effects.shift()( (msg) => this.send(new Dispatch(msg)), (event2, data) => this.emit(event2, data) ); } if (this.#queue.length) { if (iterations < 5) { this.#flush_queue(++iterations); } else { window.requestAnimationFrame(() => this.#tick()); } } } #debug(action) { switch (true) { case action instanceof ForceModel: { const vdom = this.#view(action[0]); const dispatch = (handler) => (e) => { const result = handler(e); if (result instanceof Ok) { this.send(new Dispatch(result[0])); } }; this.#queue = []; this.#effects = []; this.#didUpdate = false; this.#root = morph(this.#root, vdom, dispatch, this.#isComponent); } } } #shutdown() { this.#root.remove(); this.#root = null; this.#model = null; this.#queue = []; this.#effects = []; this.#didUpdate = false; this.#update = () => { }; this.#view = () => { }; } }; var start = (app, selector, flags) => LustreClientApplication2.start( flags, selector, app.init, app.update, app.view ); var is_browser = () => globalThis.window && window.document; // build/dev/javascript/lustre/lustre.mjs var App = class extends CustomType { constructor(init3, update2, view2, on_attribute_change) { super(); this.init = init3; this.update = update2; this.view = view2; this.on_attribute_change = on_attribute_change; } }; var ElementNotFound = class extends CustomType { constructor(selector) { super(); this.selector = selector; } }; var NotABrowser = class extends CustomType { }; function application(init3, update2, view2) { return new App(init3, update2, view2, new None()); } function start3(app, selector, flags) { return guard( !is_browser(), new Error(new NotABrowser()), () => { return start(app, selector, flags); } ); } // build/dev/javascript/lustre/lustre/element/html.mjs function div(attrs, children) { return element("div", attrs, children); } function button(attrs, children) { return element("button", attrs, children); } // build/dev/javascript/lustre/lustre/event.mjs function on2(name, handler) { return on(name, handler); } function on_click(msg) { return on2("click", (_) => { return new Ok(msg); }); } // build/dev/javascript/app/app.mjs var Increment = class extends CustomType { }; var Decrement = class extends CustomType { }; function init2(_) { return [0, none()]; } function update(model, msg) { if (msg instanceof Increment) { return [model + 1, none()]; } else { return [model - 1, none()]; } } function view(model) { let count = to_string2(model); return div( toList([]), toList([ button( toList([on_click(new Increment())]), toList([text("+")]) ), text(count), button( toList([on_click(new Decrement())]), toList([text("-")]) ) ]) ); } function main() { let app = application(init2, update, view); let $ = start3(app, "#app", void 0); if (!$.isOk()) { throw makeError( "assignment_no_match", "app", 12, "main", "Assignment pattern did not match", { value: $ } ); } return void 0; } // build/.lustre/entry.mjs main();