ilha 0.14.5 → 0.14.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { i as Fragment, t as h } from "./vnode-DEMDQ0eA.js";
1
+ import { i as Fragment, t as h } from "./vnode-M-oJMKcv.js";
2
2
 
3
3
  //#region src/jsx-runtime.ts
4
4
  const jsx = (type, props, key) => h(type, key === void 0 ? props : {
@@ -1,12 +1,13 @@
1
- import { _ as skip, a as ISLAND, b as failureMessage, c as KEEP, d as errorView, f as isBigInt, g as isString, h as isObject, i as Fragment, l as KEY_ATTR, m as isNumber, n as isSetupFn, o as ISLAND_MOUNT_INTERNAL, p as isFunction, r as isVNode, s as ISLAND_SLOT_TAG, u as SLOT_ATTR, v as unwrap, x as toError, y as asFailure } from "./vnode-DEMDQ0eA.js";
2
- import * as Effect from "effect/Effect";
3
- import * as Stream from "effect/Stream";
4
- import * as Atom from "effect/unstable/reactivity/Atom";
1
+ import { _ as skip, a as ISLAND, b as failureMessage, c as KEEP, d as errorView, f as isBigInt, g as isString, h as isObject, i as Fragment, l as KEY_ATTR, m as isNumber, n as isSetupFn, o as ISLAND_MOUNT_INTERNAL, p as isFunction, r as isVNode, s as ISLAND_SLOT_TAG, u as SLOT_ATTR, v as unwrap, x as toError, y as asFailure } from "./vnode-M-oJMKcv.js";
2
+ import * as Atom from "effect/reactivity/Atom";
3
+ import { Equal } from "effect";
4
+ import * as Effect$1 from "effect/Effect";
5
+ import * as Stream$1 from "effect/Stream";
5
6
  import * as Cause from "effect/Cause";
6
7
  import * as Exit from "effect/Exit";
7
8
  import * as Fiber from "effect/Fiber";
9
+ import * as Registry from "effect/reactivity/AtomRegistry";
8
10
  import * as Scope from "effect/Scope";
9
- import * as Registry from "effect/unstable/reactivity/AtomRegistry";
10
11
 
11
12
  //#region src/runtime.ts
12
13
  const stack = [];
@@ -24,15 +25,15 @@ const withFiber = (fiber, fn) => {
24
25
  stack.pop();
25
26
  }
26
27
  };
27
- const provide = (effect, registry) => effect.pipe(Effect.provideService(Registry.AtomRegistry, registry));
28
+ const provide = (effect, registry) => effect.pipe(Effect$1.provideService(Registry.AtomRegistry, registry));
28
29
  const runInScope = (effect, registry, scope, onOk, onErr, onDone) => {
29
30
  const provided = provide(effect, registry);
30
- const f = Effect.runFork(onOk || onDone ? provided.pipe(Effect.onExit((ex) => Effect.sync(() => {
31
+ const f = Effect$1.runFork(onOk || onDone ? provided.pipe(Effect$1.onExit((ex) => Effect$1.sync(() => {
31
32
  onDone?.();
32
33
  if (Exit.isSuccess(ex)) onOk?.(ex.value);
33
34
  else if (!Cause.hasInterruptsOnly(ex.cause)) onErr?.(Cause.squash(ex.cause));
34
35
  }))) : provided);
35
- Effect.runSync(Scope.addFinalizer(scope, Fiber.interrupt(f)));
36
+ Effect$1.runSync(Scope.addFinalizer(scope, Fiber.interrupt(f)));
36
37
  };
37
38
  /** Clear render subscriptions and child holes without closing the fiber scope. */
38
39
  const clearFiberView = (fiber) => {
@@ -55,7 +56,7 @@ const closeFiber = (fiber) => {
55
56
  fiber.trackRestore = void 0;
56
57
  fiber.handleError = void 0;
57
58
  clearFiberView(fiber);
58
- Effect.runFork(Scope.close(fiber.scope, Exit.void));
59
+ Effect$1.runFork(Scope.close(fiber.scope, Exit.void));
59
60
  };
60
61
  const makeRuntime = (opts) => {
61
62
  const registry = Registry.make();
@@ -69,7 +70,7 @@ const makeRuntime = (opts) => {
69
70
  },
70
71
  close: () => {
71
72
  onIdle = void 0;
72
- Effect.runFork(Scope.close(scope, Exit.void));
73
+ Effect$1.runFork(Scope.close(scope, Exit.void));
73
74
  registry.dispose();
74
75
  },
75
76
  end: () => {
@@ -183,6 +184,8 @@ const instr = (effect) => {
183
184
  return out;
184
185
  };
185
186
  const isAtomHandle = (x) => isFunction(x) && x.$$atom === 1;
187
+ /** Key-order-insensitive deep equality for JSON-shaped values. */
188
+ const structuralEqual = Equal.equals;
186
189
  let trackGet;
187
190
  const trackStack = [];
188
191
  /** Read atoms inside `fn` without subscribing the active render. Sync only. */
@@ -218,6 +221,34 @@ const beginPrimitiveFrame = (fiber) => {
218
221
  islandFrame.slots.length = usedIslands;
219
222
  }
220
223
  islandFrame.i = 0;
224
+ let { componentFrame } = fiber;
225
+ if (!componentFrame) {
226
+ componentFrame = {
227
+ i: 0,
228
+ slots: []
229
+ };
230
+ fiber.componentFrame = componentFrame;
231
+ }
232
+ const usedComponents = componentFrame.i;
233
+ if (usedComponents < componentFrame.slots.length) {
234
+ for (let j = usedComponents; j < componentFrame.slots.length; j += 1) {
235
+ const slot = componentFrame.slots[j];
236
+ if (slot) {
237
+ const { hole } = slot;
238
+ if (!hole.closed) {
239
+ const idx = fiber.holes.findIndex((h) => h.holeFiber === hole);
240
+ if (idx === -1) closeFiber(hole);
241
+ else {
242
+ const [removed] = fiber.holes.splice(idx, 1);
243
+ removed?.dispose();
244
+ }
245
+ }
246
+ }
247
+ componentFrame.slots[j] = void 0;
248
+ }
249
+ componentFrame.slots.length = usedComponents;
250
+ }
251
+ componentFrame.i = 0;
221
252
  };
222
253
  const readTracked = (atom, registry) => {
223
254
  const frame = trackStack.at(-1);
@@ -333,19 +364,23 @@ const wrapHandle = (atom, fiber) => {
333
364
  });
334
365
  return read;
335
366
  };
336
- const makeAtom = (init) => {
367
+ const makeAtom = (init, options) => {
337
368
  const fiber = getFiber();
338
369
  const snap = fiber.runtime.hydrateValues;
339
370
  const { atom: a, fresh } = usePrimitiveSlot(fiber, () => {
340
371
  let seed = init;
341
- if (snap && !Atom.isAtom(init) && !Effect.isEffect(init) && !Stream.isStream(init)) {
372
+ if (snap && !Atom.isAtom(init) && !Effect$1.isEffect(init) && !Stream$1.isStream(init)) {
342
373
  const v = snap[fiber.runtime.hydrateI];
343
374
  fiber.runtime.hydrateI += 1;
344
375
  if (v !== void 0) seed = v;
345
376
  }
346
377
  if (Atom.isAtom(seed)) return seed;
347
- if (Effect.isEffect(seed) || Stream.isStream(seed)) return Atom.make(seed);
348
- return Atom.make(seed);
378
+ if (Effect$1.isEffect(seed) || Stream$1.isStream(seed)) return Atom.make(seed);
379
+ const made = Atom.make(seed);
380
+ const eq = options?.equals;
381
+ if (eq === void 0) return made;
382
+ const equalsFn = eq === "structural" ? structuralEqual : eq;
383
+ return Atom.withEquality(made, equalsFn);
349
384
  });
350
385
  const handle = wrapHandle(a, fiber);
351
386
  if (fresh && fiber.runtime.ssr) fiber.runtime.ssrValues.push(fiber.registry.get(a));
@@ -392,7 +427,7 @@ const interpret = (gen, fiber) => {
392
427
  }
393
428
  let effect;
394
429
  if (isInstruction(step.value)) ({effect} = step.value);
395
- else if (Effect.isEffect(step.value)) effect = step.value;
430
+ else if (Effect$1.isEffect(step.value)) effect = step.value;
396
431
  if (effect) {
397
432
  fiber.inFlight = true;
398
433
  fiber.run(effect, (a) => {
@@ -507,7 +542,7 @@ const bindEvents = (node, props, fiber) => {
507
542
  const fn = el.__ilhaProps?.[key];
508
543
  if (!isFunction(fn)) return;
509
544
  const out = fn(ev);
510
- if (Effect.isEffect(out)) fiber.run(out);
545
+ if (Effect$1.isEffect(out)) fiber.run(out);
511
546
  });
512
547
  }
513
548
  };
@@ -538,6 +573,7 @@ const syncAttributes = (from, to) => {
538
573
  const names = [];
539
574
  for (const { name } of from.attributes) names.push(name);
540
575
  for (const name of names) {
576
+ if (name === "open" && !to.hasAttribute("open")) continue;
541
577
  if (shouldPreserveMorphAttr(from, name)) continue;
542
578
  if (!to.hasAttribute(name)) from.removeAttribute(name);
543
579
  }
@@ -590,19 +626,29 @@ const collectKeys = (parent) => {
590
626
  return keys;
591
627
  };
592
628
  const morphInput = (fromEl, toEl) => {
593
- const hadChecked = fromEl.hasAttribute("checked");
594
- const hadValue = fromEl.getAttribute("value");
595
629
  syncAttributes(fromEl, toEl);
596
- const hasChecked = toEl.hasAttribute("checked");
597
- if (hasChecked !== hadChecked) fromEl.checked = hasChecked;
598
- const newValue = toEl.getAttribute("value");
599
- if (newValue !== hadValue) fromEl.value = newValue ?? "";
630
+ const from = fromEl;
631
+ const to = toEl;
632
+ const toChecked = toEl.hasAttribute("checked");
633
+ if (toChecked !== fromEl.hasAttribute("checked")) {
634
+ if (toChecked) fromEl.setAttribute("checked", "");
635
+ else fromEl.removeAttribute("checked");
636
+ }
637
+ if (toChecked && from.checked !== to.checked) from.checked = to.checked;
638
+ const toValue = toEl.getAttribute("value");
639
+ if (toValue !== fromEl.getAttribute("value")) {
640
+ if (toValue === null) fromEl.removeAttribute("value");
641
+ else fromEl.setAttribute("value", toValue);
642
+ }
643
+ if (toValue !== null && from.value !== to.value) from.value = to.value;
600
644
  };
601
645
  const morphTextarea = (fromEl, toEl) => {
602
- const newText = toEl.textContent ?? "";
603
- if (fromEl.textContent !== newText) {
604
- fromEl.textContent = newText;
605
- fromEl.value = newText;
646
+ syncAttributes(fromEl, toEl);
647
+ const from = fromEl;
648
+ const to = toEl;
649
+ if (to.textContent !== "" || from.textContent !== to.textContent) {
650
+ if (from.value !== to.value) from.value = to.value;
651
+ if (from.textContent !== to.textContent) from.textContent = to.textContent;
606
652
  }
607
653
  };
608
654
  const morphSelect = (fromEl, toEl, api) => {
@@ -784,8 +830,30 @@ const serializeStyleAttr = (value) => {
784
830
  return serializeStyleEntries(value);
785
831
  };
786
832
 
833
+ //#endregion
834
+ //#region src/unsafe.ts
835
+ /**
836
+ * Mark a string as pre-rendered HTML and paint it without escaping.
837
+ * Use it for markup you built or sanitized yourself — SVG from an external
838
+ * library, Markdown rendered to HTML, embed codes. Never pass untrusted
839
+ * user input: unlike interpolated strings, this content is not escaped and
840
+ * scripts inserted this way do not run, but event attributes and other
841
+ * markup still apply.
842
+ */
843
+ const unsafe = (html) => ({
844
+ $$ilhaUnsafe: 1,
845
+ html
846
+ });
847
+ const isUnsafeHtml = (x) => {
848
+ if (!isObject(x)) return false;
849
+ return x.$$ilhaUnsafe === 1;
850
+ };
851
+
787
852
  //#endregion
788
853
  //#region src/paint-core.ts
854
+ const SVG_NS = "http://www.w3.org/2000/svg";
855
+ const MATHML_NS = "http://www.w3.org/1998/Math/MathML";
856
+ const HTML_NS = "http://www.w3.org/1999/xhtml";
789
857
  const disposeHoles = (fiber, opts) => {
790
858
  const keep = [];
791
859
  for (const h of fiber.holes) {
@@ -797,6 +865,38 @@ const disposeHoles = (fiber, opts) => {
797
865
  }
798
866
  fiber.holes = keep;
799
867
  };
868
+ const parentNsOf = (root) => {
869
+ if ("namespaceURI" in root) return root.namespaceURI ?? null;
870
+ return null;
871
+ };
872
+ const elementNs = (lower, parentNs) => {
873
+ if (lower === "svg") return SVG_NS;
874
+ if (lower === "math") return MATHML_NS;
875
+ if (lower === "foreignobject") return HTML_NS;
876
+ if (parentNs === "http://www.w3.org/2000/svg" || parentNs === "http://www.w3.org/1998/Math/MathML") return parentNs;
877
+ };
878
+ const canMorphRoot = (root) => typeof Element !== "undefined" && typeof document !== "undefined" && root instanceof Element && root.childNodes.length > 0;
879
+ const sweepComponentSlots = (fiber) => {
880
+ const cframe = fiber.componentFrame;
881
+ if (!cframe) return;
882
+ const { slots } = cframe;
883
+ for (let j = cframe.i; j < slots.length; j += 1) {
884
+ const s = slots[j];
885
+ if (s) {
886
+ const { hole } = s;
887
+ if (!hole.closed) {
888
+ const idx = fiber.holes.findIndex((h) => h.holeFiber === hole);
889
+ if (idx === -1) closeFiber(hole);
890
+ else {
891
+ const [removed] = fiber.holes.splice(idx, 1);
892
+ removed?.dispose();
893
+ }
894
+ }
895
+ }
896
+ slots[j] = void 0;
897
+ }
898
+ slots.length = cframe.i;
899
+ };
800
900
  const findReusableAtomHost = (fiber, atom) => {
801
901
  for (const h of fiber.holes) if (h.atom === atom && h.host?.isConnected && h.holeFiber && !h.holeFiber.closed) return {
802
902
  hole: h.holeFiber,
@@ -926,14 +1026,41 @@ const createPainter = (ops) => {
926
1026
  fiber.islandFrame = frame;
927
1027
  }
928
1028
  frame.i = 0;
1029
+ let cframe = fiber.componentFrame;
1030
+ if (!cframe) {
1031
+ cframe = {
1032
+ i: 0,
1033
+ slots: []
1034
+ };
1035
+ fiber.componentFrame = cframe;
1036
+ }
1037
+ cframe.i = 0;
929
1038
  const list = Array.isArray(view) ? view : null;
930
1039
  if (list && list.length > 0 && list.every((v) => isVNode(v) && v.key !== null && v.key !== void 0)) {
931
1040
  paintFns.keyedPaintHole(fiber, list);
932
1041
  return;
933
1042
  }
934
- disposeHoles(fiber);
935
- ops.clearRoot(fiber.root);
936
- api.insert(fiber, api.materialize(view, fiber));
1043
+ const kept = /* @__PURE__ */ new Set();
1044
+ for (const s of cframe.slots) if (s && !s.hole.closed) kept.add(s.hole);
1045
+ const keep = [];
1046
+ for (const h of fiber.holes) {
1047
+ if (h.holeFiber && kept.has(h.holeFiber)) {
1048
+ keep.push(h);
1049
+ continue;
1050
+ }
1051
+ h.dispose();
1052
+ }
1053
+ fiber.holes = keep;
1054
+ const fresh = api.materialize(view, fiber);
1055
+ if (canMorphRoot(fiber.root)) {
1056
+ const tmp = document.createElement("div");
1057
+ for (const n of fresh) tmp.append(n);
1058
+ morphInner(fiber.root, tmp);
1059
+ } else {
1060
+ ops.clearRoot(fiber.root);
1061
+ api.insert(fiber, fresh);
1062
+ }
1063
+ sweepComponentSlots(fiber);
937
1064
  }
938
1065
  };
939
1066
  const materializeAtom = (view, fiber) => {
@@ -962,9 +1089,9 @@ const createPainter = (ops) => {
962
1089
  };
963
1090
  const materializeStream = (view, fiber) => {
964
1091
  const { fiber: hole, nodes } = api.openHole(fiber);
965
- const src = fiber.runtime.ssr ? view.pipe(Stream.take(1)) : view;
966
- const effect = Effect.gen(function* streamPaint() {
967
- const result = yield* Effect.result(Stream.runForEach(src, (v) => Effect.sync(() => {
1092
+ const src = fiber.runtime.ssr ? view.pipe(Stream$1.take(1)) : view;
1093
+ const effect = Effect$1.gen(function* streamPaint() {
1094
+ const result = yield* Effect$1.result(Stream$1.runForEach(src, (v) => Effect$1.sync(() => {
968
1095
  if (!hole.closed) api.paintHole(hole, v);
969
1096
  })));
970
1097
  if (result._tag === "Failure" && !hole.closed) api.paintHole(hole, String(result.failure));
@@ -1014,6 +1141,50 @@ const createPainter = (ops) => {
1014
1141
  }
1015
1142
  return [el];
1016
1143
  };
1144
+ const materializeUnkeyedComponent = (run, type, props, fiber) => {
1145
+ let { componentFrame: cframe } = fiber;
1146
+ if (!cframe) {
1147
+ cframe = {
1148
+ i: 0,
1149
+ slots: []
1150
+ };
1151
+ fiber.componentFrame = cframe;
1152
+ }
1153
+ const at = cframe.i;
1154
+ cframe.i = at + 1;
1155
+ const existing = cframe.slots[at];
1156
+ if (existing) {
1157
+ const { hole } = existing;
1158
+ if (existing.type === type && !hole.closed) {
1159
+ hole.propsBox = { current: props };
1160
+ runSetup(hole, () => run(props));
1161
+ return [ops.asNode(hole.root)];
1162
+ }
1163
+ if (!hole.closed) {
1164
+ const idx = fiber.holes.findIndex((h) => h.holeFiber === hole);
1165
+ if (idx === -1) closeFiber(hole);
1166
+ else {
1167
+ const [removed] = fiber.holes.splice(idx, 1);
1168
+ removed?.dispose();
1169
+ }
1170
+ }
1171
+ }
1172
+ const { fiber: hole, nodes } = api.openHole(fiber);
1173
+ hole.propsBox = { current: props };
1174
+ hole.componentType = type;
1175
+ cframe.slots[at] = {
1176
+ hole,
1177
+ type
1178
+ };
1179
+ fiber.runtime.later(() => {
1180
+ if (hole.closed) return;
1181
+ const box = hole.propsBox;
1182
+ if (!box) return;
1183
+ runSetup(hole, () => run(box.current));
1184
+ });
1185
+ trackHole(fiber, hole, void 0, { keyed: true });
1186
+ return nodes;
1187
+ };
1017
1188
  const materializeComponent = (view, type, fiber) => {
1018
1189
  const props = {
1019
1190
  ...view.props,
@@ -1030,23 +1201,25 @@ const createPainter = (ops) => {
1030
1201
  if (next && !isThenable(next) && !isSetupFn(next)) reuse.paint(next);
1031
1202
  return [ops.asNode(reuse.root)];
1032
1203
  }
1033
- const { fiber: hole, nodes } = api.openHole(fiber);
1034
- hole.propsBox = { current: props };
1035
- if (k) {
1036
- fiber.keyedHoles ??= /* @__PURE__ */ new Map();
1037
- fiber.keyedHoles.set(k, hole);
1038
- }
1204
+ if (!k) return materializeUnkeyedComponent(run, type, props, fiber);
1205
+ const { fiber: khole, nodes: knodes } = api.openHole(fiber);
1206
+ khole.propsBox = { current: props };
1207
+ fiber.keyedHoles ??= /* @__PURE__ */ new Map();
1208
+ fiber.keyedHoles.set(k, khole);
1039
1209
  fiber.runtime.later(() => {
1040
- if (hole.closed) return;
1041
- const box = hole.propsBox;
1210
+ if (khole.closed) return;
1211
+ const box = khole.propsBox;
1042
1212
  if (!box) return;
1043
- runSetup(hole, () => run(box.current));
1213
+ runSetup(khole, () => run(box.current));
1044
1214
  });
1045
- trackHole(fiber, hole, void 0, k ? { keyed: true } : void 0);
1046
- return nodes;
1215
+ trackHole(fiber, khole, void 0, { keyed: true });
1216
+ return knodes;
1047
1217
  };
1048
1218
  const materializeElement = (view, fiber) => {
1049
- const el = ops.createElement(view.type);
1219
+ const tag = view.type;
1220
+ const lower = tag.toLowerCase();
1221
+ const ns = elementNs(lower, parentNsOf(fiber.root));
1222
+ const el = ops.createElement(tag, ns);
1050
1223
  if (view.key !== null && view.key !== void 0) el.setAttribute(KEY_ATTR, String(view.key));
1051
1224
  api.applyProps(el, view.props, fiber);
1052
1225
  fiber.keyedHoles ??= /* @__PURE__ */ new Map();
@@ -1065,10 +1238,11 @@ const createPainter = (ops) => {
1065
1238
  return materializeElement(view, fiber);
1066
1239
  };
1067
1240
  paintFns.materialize = (view, fiber) => {
1068
- if (skip(view)) return [];
1241
+ if (skip(view)) return [ops.createText("")];
1242
+ if (isUnsafeHtml(view)) return ops.createRaw(view.html, fiber.root);
1069
1243
  if (isTextish(view)) return [ops.createText(String(view))];
1070
1244
  if (isAtomHandle(view)) return materializeAtom(view, fiber);
1071
- if (Stream.isStream(view)) return materializeStream(view, fiber);
1245
+ if (Stream$1.isStream(view)) return materializeStream(view, fiber);
1072
1246
  if (isSetupFn(view)) {
1073
1247
  const { fiber: hole, nodes } = api.openHole(fiber);
1074
1248
  fiber.runtime.later(() => {
@@ -1125,7 +1299,12 @@ const core$1 = createPainter({
1125
1299
  clearRoot: (root) => {
1126
1300
  if (root instanceof Element) root.replaceChildren();
1127
1301
  },
1128
- createElement: (tag) => document.createElement(tag),
1302
+ createElement: (tag, ns) => ns ? document.createElementNS(ns, tag) : document.createElement(tag),
1303
+ createRaw: (html, parent) => {
1304
+ const range = document.createRange();
1305
+ range.selectNodeContents(parent);
1306
+ return [...range.createContextualFragment(html).childNodes];
1307
+ },
1129
1308
  createSlotHost: (slotId) => {
1130
1309
  const host = document.createElement("span");
1131
1310
  if (slotId !== null) host.setAttribute(SLOT_ATTR, slotId);
@@ -1136,9 +1315,23 @@ const core$1 = createPainter({
1136
1315
  disconnect: noopDisconnect,
1137
1316
  setFormControl: (el, key, v) => {
1138
1317
  const node = el;
1139
- if (key === "value") node.value = String(v ?? "");
1140
- else if (key === "checked") node.checked = Boolean(v);
1141
- else node.selected = Boolean(v);
1318
+ if (key === "value") {
1319
+ if (v === null || v === void 0) {
1320
+ el.removeAttribute("value");
1321
+ node.value = "";
1322
+ } else {
1323
+ el.setAttribute("value", String(v));
1324
+ node.value = String(v);
1325
+ }
1326
+ } else if (key === "checked") {
1327
+ node.checked = Boolean(v);
1328
+ if (v) el.setAttribute("checked", "");
1329
+ else el.removeAttribute("checked");
1330
+ } else {
1331
+ node.selected = Boolean(v);
1332
+ if (v) el.setAttribute("selected", "");
1333
+ else el.removeAttribute("selected");
1334
+ }
1142
1335
  },
1143
1336
  setStyle: (el, css) => {
1144
1337
  el.style.cssText = css;
@@ -1184,8 +1377,14 @@ const paintMorphLive = (fiber, view) => {
1184
1377
  if (!fiber.liveEl) return;
1185
1378
  core$1.disposeHoles(fiber, { morph: true });
1186
1379
  core$1.applyProps(fiber.liveEl, view.props, fiber);
1187
- const tmp = document.createElement(fiber.liveEl.localName);
1188
- for (const c of view.children) for (const n of core$1.materialize(c, fiber)) tmp.append(n);
1380
+ const liveNs = fiber.liveEl.namespaceURI;
1381
+ const tmp = liveNs && liveNs !== "http://www.w3.org/1999/xhtml" ? document.createElementNS(liveNs, fiber.liveEl.localName) : document.createElement(fiber.liveEl.localName);
1382
+ const childFiber = {
1383
+ ...fiber,
1384
+ holes: fiber.holes,
1385
+ root: tmp
1386
+ };
1387
+ for (const c of view.children) for (const n of core$1.materialize(c, childFiber)) tmp.append(n);
1189
1388
  morphInner(fiber.liveEl, tmp);
1190
1389
  refreshAllAtomHosts(fiber);
1191
1390
  pruneDisconnectedAtomHoles(fiber);
@@ -1208,7 +1407,7 @@ const paint$1 = (fiber, view) => {
1208
1407
  paintMorphLive(fiber, view);
1209
1408
  return;
1210
1409
  }
1211
- core$1.disposeHoles(fiber);
1410
+ core$1.disposeHoles(fiber, { morph: true });
1212
1411
  if (fiber.root instanceof Element && fiber.root.childNodes.length > 0) {
1213
1412
  const tmp = document.createElement("div");
1214
1413
  for (const n of core$1.materialize(view, fiber)) tmp.append(n);
@@ -1298,6 +1497,10 @@ const VOID = /* @__PURE__ */ new Set([
1298
1497
  const RAW_TEXT = /* @__PURE__ */ new Set(["script", "style"]);
1299
1498
  const escapeText = (s) => s.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
1300
1499
  const escapeAttr = (s) => escapeText(s).replaceAll("\"", "&quot;");
1500
+ const createSsrRaw = (html) => ({
1501
+ html,
1502
+ kind: "raw"
1503
+ });
1301
1504
  const createSsrText = (text) => ({
1302
1505
  kind: "text",
1303
1506
  text
@@ -1308,6 +1511,7 @@ const serializeRawText = (tag, text) => {
1308
1511
  };
1309
1512
  const serializeNode = (node) => {
1310
1513
  if (node.kind === "text") return escapeText(node.text);
1514
+ if (node.kind === "raw") return node.html;
1311
1515
  const parts = [];
1312
1516
  for (const [name, value] of node.attrs) parts.push(`${name}="${escapeAttr(value)}"`);
1313
1517
  const attr = parts.length > 0 ? ` ${parts.join(" ")}` : "";
@@ -1364,7 +1568,11 @@ const createSsrElement = (tag) => {
1364
1568
  tag: lower,
1365
1569
  tagName: lower.toUpperCase(),
1366
1570
  get textContent() {
1367
- return children.map((n) => n.kind === "text" ? n.text : n.textContent).join("");
1571
+ return children.map((n) => {
1572
+ if (n.kind === "text") return n.text;
1573
+ if (n.kind === "raw") return n.html;
1574
+ return n.textContent;
1575
+ }).join("");
1368
1576
  },
1369
1577
  set textContent(value) {
1370
1578
  children.length = 0;
@@ -1435,6 +1643,7 @@ const ssrOps = {
1435
1643
  bindEvents: bindSsrEvents,
1436
1644
  clearRoot: (root) => ssrAs(root).replaceChildren(),
1437
1645
  createElement: createSsrElement,
1646
+ createRaw: (html, _parent) => [createSsrRaw(html)],
1438
1647
  createSlotHost: (slotId) => {
1439
1648
  const host = createSsrElement("span");
1440
1649
  if (slotId !== null) host.setAttribute(SLOT_ATTR, slotId);
@@ -1561,4 +1770,4 @@ const renderToString = async (fn, opts) => {
1561
1770
  };
1562
1771
 
1563
1772
  //#endregion
1564
- export { atom as a, instr as c, wrapHandle as d, clearFiberView as f, makeFiber as h, interpret as i, isAtomHandle as l, getFiber as m, renderToString as n, batch as o, closeFiber as p, runSetup as r, handleOwner as s, mount as t, untrack as u };
1773
+ export { makeFiber as _, interpret as a, handleOwner as c, structuralEqual as d, untrack as f, getFiber as g, closeFiber as h, runSetup as i, instr as l, clearFiberView as m, renderToString as n, atom as o, wrapHandle as p, unsafe as r, batch as s, mount as t, isAtomHandle as u };
@@ -0,0 +1,100 @@
1
+ import { Effect, Stream } from "effect";
2
+ import { AtomRegistry } from "effect/reactivity/AtomRegistry";
3
+ import "effect/Scope";
4
+ import { Atom } from "effect/reactivity";
5
+ //#region src/types.d.ts
6
+ type JsonText = string | number | bigint | boolean | null | undefined;
7
+ /** Runtime prop bag values painted onto elements / passed to components. */
8
+ type PropValue = JsonText | PropBag | StyleObject | EventHandler | AtomHandle<JsonText> | readonly PropValue[];
9
+ type StyleObject = Readonly<Record<string, string | number | null | undefined>>;
10
+ type EventHandler = (event: Event) => void;
11
+ /** Indexed bag — interface so PropValue can recurse without a circular type alias. */
12
+ interface PropBag {
13
+ readonly [key: string]: PropValue | undefined;
14
+ }
15
+ type View = VNode | UnsafeHtml | JsonText | AtomHandle<unknown> | Stream.Stream<View, unknown, unknown> | GeneratorFn | Component | View[] | Iterable<View>;
16
+ declare const Fragment: unique symbol;
17
+ type Fragment = typeof Fragment;
18
+ type ComponentFn = (props: PropBag) => View | Promise<View | undefined> | undefined;
19
+ /** JSX/tag component with its own props shape (Provider, ErrorBoundary, …). */
20
+ type JsxComponent = (props: never) => View | Promise<View | undefined> | Generator<View, View | undefined, View> | undefined;
21
+ /** Raw HTML view produced by `unsafe()` — painted without escaping. */
22
+ interface UnsafeHtml {
23
+ readonly $$ilhaUnsafe: 1;
24
+ readonly html: string;
25
+ }
26
+ interface VNode {
27
+ readonly $$ilha: 1;
28
+ readonly type: string | Fragment | ComponentFn | JsxComponent;
29
+ readonly props: PropBag;
30
+ readonly children: View[];
31
+ readonly key?: string | number;
32
+ }
33
+ type GeneratorFn = () => Generator<Yielded, View | undefined, View>;
34
+ type Yielded = View | Instruction<unknown> | Effect.Effect<unknown, unknown, unknown>;
35
+ interface Instruction<A, E = Error> extends Iterable<Instruction<A, E>, A>, PromiseLike<A> {
36
+ readonly $$ilhaOp: 1;
37
+ readonly effect: Effect.Effect<A, E, AtomRegistry>;
38
+ }
39
+ type Component = (() => View | undefined | Promise<View | undefined>) | GeneratorFn;
40
+ interface AtomHandle<A> {
41
+ readonly $$atom: 1;
42
+ readonly atom: Atom.Atom<A>;
43
+ (): A;
44
+ set: (next: A) => void;
45
+ update: (f: (current: A) => A) => void;
46
+ }
47
+ /** Equality for plain-value atoms. `"structural"` deep-compares JSON-shaped values. */
48
+ type AtomEquality<A> = "structural" | ((prev: A, next: A) => boolean);
49
+ interface AtomOptions<A = unknown> {
50
+ /** Skip subscriber notify when `equals(prev, next)` holds. Plain values only. */
51
+ readonly equals?: AtomEquality<A>;
52
+ }
53
+ /** Optional disposer returned from `watch.once`. */
54
+ type WatchOnceCleanup = () => void;
55
+ /**
56
+ * Cancellation context handed to watch callbacks. `signal` aborts when the
57
+ * run goes stale (a newer value arrives), on unmount, or on slot disposal.
58
+ * Register extra teardowns with `onCleanup`.
59
+ */
60
+ interface WatchContext {
61
+ readonly signal: AbortSignal;
62
+ onCleanup: (fn: () => void) => void;
63
+ }
64
+ type WatchCallback<A> = (value: A, ctx: WatchContext) => WatchOnceCleanup | undefined | Promise<WatchOnceCleanup | undefined>;
65
+ /** Options for `resource()`. */
66
+ interface ResourceOptions {
67
+ /** Serve cached data while revalidating in the background. Default `true`. */
68
+ readonly staleWhileRevalidate?: boolean;
69
+ }
70
+ /** Async fetch scoped to a resource key. Receives an abort signal. */
71
+ type ResourceFetcher<T> = (key: string, ctx: {
72
+ signal: AbortSignal;
73
+ }) => Promise<T>;
74
+ /** Handles returned by `resource()`. */
75
+ interface Resource<T> {
76
+ readonly data: AtomHandle<T | undefined>;
77
+ readonly error: AtomHandle<unknown>;
78
+ readonly loading: AtomHandle<boolean>;
79
+ refetch: () => Promise<T | undefined>;
80
+ }
81
+ /** Connection state of a `fromEventSource` feed. */
82
+ type EventSourceStatus = "connecting" | "open" | "retrying" | "closed";
83
+ /** Options for `fromEventSource()`. */
84
+ interface EventSourceOptions<T = string> {
85
+ /** Named event to listen for. Default: the `message` event. */
86
+ readonly event?: string;
87
+ /** Decode (and validate) `data` into `T`. Throwing skips the message. */
88
+ readonly schema?: (data: string) => T;
89
+ /** Base backoff between reconnects. Default `1000`. Capped at 30s. */
90
+ readonly retryBaseMs?: number;
91
+ }
92
+ /** Live server-sent-events feed. */
93
+ interface EventSourceFeed<T = string> {
94
+ /** Paint with `yield Stream.map(feed.stream, ...)`; watch `latest` instead in sync components. */
95
+ readonly stream: Stream.Stream<T, never, never>;
96
+ readonly status: AtomHandle<EventSourceStatus>;
97
+ readonly latest: AtomHandle<T | undefined>;
98
+ }
99
+ //#endregion
100
+ export { Yielded as S, VNode as _, ComponentFn as a, WatchContext as b, EventSourceStatus as c, JsxComponent as d, PropBag as f, UnsafeHtml as g, ResourceOptions as h, Component as i, Fragment as l, ResourceFetcher as m, AtomHandle as n, EventSourceFeed as o, Resource as p, AtomOptions as r, EventSourceOptions as s, AtomEquality as t, Instruction as u, View as v, WatchOnceCleanup as x, WatchCallback as y };
@@ -1,4 +1,4 @@
1
- import { c as VNode, i as Fragment, l as View, o as JsxComponent, r as ComponentFn, s as PropBag, t as AtomHandle } from "./types-CUQdA6hR.js";
1
+ import { _ as VNode, a as ComponentFn, d as JsxComponent, f as PropBag, l as Fragment, n as AtomHandle, v as View } from "./types-CJF2qMK6.js";
2
2
  //#region src/jsx-types.d.ts
3
3
  declare namespace JSX {
4
4
  export type Element = View;
@@ -71,9 +71,21 @@ declare namespace JSX {
71
71
  onanimationend?: Handler<Target, globalThis.AnimationEvent>;
72
72
  onanimationiteration?: Handler<Target, globalThis.AnimationEvent>;
73
73
  ontransitionend?: Handler<Target, globalThis.TransitionEvent>;
74
+ ontoggle?: Handler<Target, globalThis.Event>;
75
+ onbeforetoggle?: Handler<Target, globalThis.Event>;
76
+ onclose?: Handler<Target, globalThis.Event>;
77
+ oncancel?: Handler<Target, globalThis.Event>;
74
78
  onload?: Handler<Target, globalThis.Event>;
75
79
  onerror?: Handler<Target, globalThis.Event>;
76
80
  }
81
+ /**
82
+ * Props every JSX tag accepts without a hand-added declaration.
83
+ * Components receive `key` through this interface, so you never add
84
+ * `key` to your own prop types.
85
+ */
86
+ export interface IntrinsicAttributes {
87
+ key?: string | number;
88
+ }
77
89
  type AriaProps = Partial<Record<`aria-${string}`, Attr>>;
78
90
  type DataProps = Partial<Record<`data-${string}`, Attr>>;
79
91
  /** Shared HTML attributes. Prefer `class` / `for`; `className` / `htmlFor` also work. */
@@ -1,4 +1,4 @@
1
- import * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
1
+ import * as AsyncResult from "effect/reactivity/AsyncResult";
2
2
  import * as Option from "effect/Option";
3
3
  import * as Result from "effect/Result";
4
4