ilha 0.14.6 → 0.14.8

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,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) => {
@@ -805,6 +851,9 @@ const isUnsafeHtml = (x) => {
805
851
 
806
852
  //#endregion
807
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";
808
857
  const disposeHoles = (fiber, opts) => {
809
858
  const keep = [];
810
859
  for (const h of fiber.holes) {
@@ -816,6 +865,53 @@ const disposeHoles = (fiber, opts) => {
816
865
  }
817
866
  fiber.holes = keep;
818
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
+ };
900
+ const sweepIslandSlots = (fiber) => {
901
+ const frame = fiber.islandFrame;
902
+ if (!frame) return;
903
+ const active = new Set(frame.slots.slice(0, frame.i));
904
+ const keep = [];
905
+ for (const h of fiber.holes) {
906
+ if (h.islandSlot && !active.has(h.islandSlot)) {
907
+ h.dispose();
908
+ continue;
909
+ }
910
+ keep.push(h);
911
+ }
912
+ fiber.holes = keep;
913
+ frame.slots.length = Math.min(frame.slots.length, frame.i);
914
+ };
819
915
  const findReusableAtomHost = (fiber, atom) => {
820
916
  for (const h of fiber.holes) if (h.atom === atom && h.host?.isConnected && h.holeFiber && !h.holeFiber.closed) return {
821
917
  hole: h.holeFiber,
@@ -945,14 +1041,45 @@ const createPainter = (ops) => {
945
1041
  fiber.islandFrame = frame;
946
1042
  }
947
1043
  frame.i = 0;
1044
+ let cframe = fiber.componentFrame;
1045
+ if (!cframe) {
1046
+ cframe = {
1047
+ i: 0,
1048
+ slots: []
1049
+ };
1050
+ fiber.componentFrame = cframe;
1051
+ }
1052
+ cframe.i = 0;
948
1053
  const list = Array.isArray(view) ? view : null;
949
1054
  if (list && list.length > 0 && list.every((v) => isVNode(v) && v.key !== null && v.key !== void 0)) {
950
1055
  paintFns.keyedPaintHole(fiber, list);
1056
+ sweepComponentSlots(fiber);
1057
+ sweepIslandSlots(fiber);
951
1058
  return;
952
1059
  }
953
- disposeHoles(fiber);
954
- ops.clearRoot(fiber.root);
955
- api.insert(fiber, api.materialize(view, fiber));
1060
+ const kept = /* @__PURE__ */ new Set();
1061
+ for (const s of cframe.slots) if (s && !s.hole.closed) kept.add(s.hole);
1062
+ const keptIslands = new Set(frame.slots);
1063
+ const keep = [];
1064
+ for (const h of fiber.holes) {
1065
+ if (h.holeFiber && kept.has(h.holeFiber) || h.islandSlot && keptIslands.has(h.islandSlot)) {
1066
+ keep.push(h);
1067
+ continue;
1068
+ }
1069
+ h.dispose();
1070
+ }
1071
+ fiber.holes = keep;
1072
+ const fresh = api.materialize(view, fiber);
1073
+ if (canMorphRoot(fiber.root)) {
1074
+ const tmp = document.createElement("div");
1075
+ for (const n of fresh) tmp.append(n);
1076
+ morphInner(fiber.root, tmp);
1077
+ } else {
1078
+ ops.clearRoot(fiber.root);
1079
+ api.insert(fiber, fresh);
1080
+ }
1081
+ sweepComponentSlots(fiber);
1082
+ sweepIslandSlots(fiber);
956
1083
  }
957
1084
  };
958
1085
  const materializeAtom = (view, fiber) => {
@@ -981,9 +1108,9 @@ const createPainter = (ops) => {
981
1108
  };
982
1109
  const materializeStream = (view, fiber) => {
983
1110
  const { fiber: hole, nodes } = api.openHole(fiber);
984
- const src = fiber.runtime.ssr ? view.pipe(Stream.take(1)) : view;
985
- const effect = Effect.gen(function* streamPaint() {
986
- const result = yield* Effect.result(Stream.runForEach(src, (v) => Effect.sync(() => {
1111
+ const src = fiber.runtime.ssr ? view.pipe(Stream$1.take(1)) : view;
1112
+ const effect = Effect$1.gen(function* streamPaint() {
1113
+ const result = yield* Effect$1.result(Stream$1.runForEach(src, (v) => Effect$1.sync(() => {
987
1114
  if (!hole.closed) api.paintHole(hole, v);
988
1115
  })));
989
1116
  if (result._tag === "Failure" && !hole.closed) api.paintHole(hole, String(result.failure));
@@ -1008,7 +1135,14 @@ const createPainter = (ops) => {
1008
1135
  existing.updateProps(props);
1009
1136
  return [ops.asNode(existing.host)];
1010
1137
  }
1011
- existing?.unmount?.();
1138
+ if (existing) {
1139
+ const idx = fiber.holes.findIndex((h) => h.islandSlot === existing);
1140
+ if (idx === -1) existing.unmount?.();
1141
+ else {
1142
+ const [removed] = fiber.holes.splice(idx, 1);
1143
+ removed?.dispose();
1144
+ }
1145
+ }
1012
1146
  const tag = isString(type[ISLAND_SLOT_TAG]) ? type[ISLAND_SLOT_TAG] : "div";
1013
1147
  const el = ops.createElement(tag);
1014
1148
  el.dataset.ilha = "";
@@ -1028,11 +1162,56 @@ const createPainter = (ops) => {
1028
1162
  handle?.unmount?.();
1029
1163
  if (frame.slots[i] === slot) frame.slots[i] = void 0;
1030
1164
  },
1165
+ islandSlot: slot,
1031
1166
  keepOnMorph: true
1032
1167
  });
1033
1168
  }
1034
1169
  return [el];
1035
1170
  };
1171
+ const materializeUnkeyedComponent = (run, type, props, fiber) => {
1172
+ let { componentFrame: cframe } = fiber;
1173
+ if (!cframe) {
1174
+ cframe = {
1175
+ i: 0,
1176
+ slots: []
1177
+ };
1178
+ fiber.componentFrame = cframe;
1179
+ }
1180
+ const at = cframe.i;
1181
+ cframe.i = at + 1;
1182
+ const existing = cframe.slots[at];
1183
+ if (existing) {
1184
+ const { hole } = existing;
1185
+ if (existing.type === type && !hole.closed) {
1186
+ hole.propsBox = { current: props };
1187
+ runSetup(hole, () => run(props));
1188
+ return [ops.asNode(hole.root)];
1189
+ }
1190
+ if (!hole.closed) {
1191
+ const idx = fiber.holes.findIndex((h) => h.holeFiber === hole);
1192
+ if (idx === -1) closeFiber(hole);
1193
+ else {
1194
+ const [removed] = fiber.holes.splice(idx, 1);
1195
+ removed?.dispose();
1196
+ }
1197
+ }
1198
+ }
1199
+ const { fiber: hole, nodes } = api.openHole(fiber);
1200
+ hole.propsBox = { current: props };
1201
+ hole.componentType = type;
1202
+ cframe.slots[at] = {
1203
+ hole,
1204
+ type
1205
+ };
1206
+ fiber.runtime.later(() => {
1207
+ if (hole.closed) return;
1208
+ const box = hole.propsBox;
1209
+ if (!box) return;
1210
+ runSetup(hole, () => run(box.current));
1211
+ });
1212
+ trackHole(fiber, hole, void 0, { keyed: true });
1213
+ return nodes;
1214
+ };
1036
1215
  const materializeComponent = (view, type, fiber) => {
1037
1216
  const props = {
1038
1217
  ...view.props,
@@ -1049,23 +1228,25 @@ const createPainter = (ops) => {
1049
1228
  if (next && !isThenable(next) && !isSetupFn(next)) reuse.paint(next);
1050
1229
  return [ops.asNode(reuse.root)];
1051
1230
  }
1052
- const { fiber: hole, nodes } = api.openHole(fiber);
1053
- hole.propsBox = { current: props };
1054
- if (k) {
1055
- fiber.keyedHoles ??= /* @__PURE__ */ new Map();
1056
- fiber.keyedHoles.set(k, hole);
1057
- }
1231
+ if (!k) return materializeUnkeyedComponent(run, type, props, fiber);
1232
+ const { fiber: khole, nodes: knodes } = api.openHole(fiber);
1233
+ khole.propsBox = { current: props };
1234
+ fiber.keyedHoles ??= /* @__PURE__ */ new Map();
1235
+ fiber.keyedHoles.set(k, khole);
1058
1236
  fiber.runtime.later(() => {
1059
- if (hole.closed) return;
1060
- const box = hole.propsBox;
1237
+ if (khole.closed) return;
1238
+ const box = khole.propsBox;
1061
1239
  if (!box) return;
1062
- runSetup(hole, () => run(box.current));
1240
+ runSetup(khole, () => run(box.current));
1063
1241
  });
1064
- trackHole(fiber, hole, void 0, k ? { keyed: true } : void 0);
1065
- return nodes;
1242
+ trackHole(fiber, khole, void 0, { keyed: true });
1243
+ return knodes;
1066
1244
  };
1067
1245
  const materializeElement = (view, fiber) => {
1068
- const el = ops.createElement(view.type);
1246
+ const tag = view.type;
1247
+ const lower = tag.toLowerCase();
1248
+ const ns = elementNs(lower, parentNsOf(fiber.root));
1249
+ const el = ops.createElement(tag, ns);
1069
1250
  if (view.key !== null && view.key !== void 0) el.setAttribute(KEY_ATTR, String(view.key));
1070
1251
  api.applyProps(el, view.props, fiber);
1071
1252
  fiber.keyedHoles ??= /* @__PURE__ */ new Map();
@@ -1084,11 +1265,11 @@ const createPainter = (ops) => {
1084
1265
  return materializeElement(view, fiber);
1085
1266
  };
1086
1267
  paintFns.materialize = (view, fiber) => {
1087
- if (skip(view)) return [];
1268
+ if (skip(view)) return [ops.createText("")];
1088
1269
  if (isUnsafeHtml(view)) return ops.createRaw(view.html, fiber.root);
1089
1270
  if (isTextish(view)) return [ops.createText(String(view))];
1090
1271
  if (isAtomHandle(view)) return materializeAtom(view, fiber);
1091
- if (Stream.isStream(view)) return materializeStream(view, fiber);
1272
+ if (Stream$1.isStream(view)) return materializeStream(view, fiber);
1092
1273
  if (isSetupFn(view)) {
1093
1274
  const { fiber: hole, nodes } = api.openHole(fiber);
1094
1275
  fiber.runtime.later(() => {
@@ -1145,7 +1326,7 @@ const core$1 = createPainter({
1145
1326
  clearRoot: (root) => {
1146
1327
  if (root instanceof Element) root.replaceChildren();
1147
1328
  },
1148
- createElement: (tag) => document.createElement(tag),
1329
+ createElement: (tag, ns) => ns ? document.createElementNS(ns, tag) : document.createElement(tag),
1149
1330
  createRaw: (html, parent) => {
1150
1331
  const range = document.createRange();
1151
1332
  range.selectNodeContents(parent);
@@ -1161,9 +1342,23 @@ const core$1 = createPainter({
1161
1342
  disconnect: noopDisconnect,
1162
1343
  setFormControl: (el, key, v) => {
1163
1344
  const node = el;
1164
- if (key === "value") node.value = String(v ?? "");
1165
- else if (key === "checked") node.checked = Boolean(v);
1166
- else node.selected = Boolean(v);
1345
+ if (key === "value") {
1346
+ if (v === null || v === void 0) {
1347
+ el.removeAttribute("value");
1348
+ node.value = "";
1349
+ } else {
1350
+ el.setAttribute("value", String(v));
1351
+ node.value = String(v);
1352
+ }
1353
+ } else if (key === "checked") {
1354
+ node.checked = Boolean(v);
1355
+ if (v) el.setAttribute("checked", "");
1356
+ else el.removeAttribute("checked");
1357
+ } else {
1358
+ node.selected = Boolean(v);
1359
+ if (v) el.setAttribute("selected", "");
1360
+ else el.removeAttribute("selected");
1361
+ }
1167
1362
  },
1168
1363
  setStyle: (el, css) => {
1169
1364
  el.style.cssText = css;
@@ -1209,8 +1404,14 @@ const paintMorphLive = (fiber, view) => {
1209
1404
  if (!fiber.liveEl) return;
1210
1405
  core$1.disposeHoles(fiber, { morph: true });
1211
1406
  core$1.applyProps(fiber.liveEl, view.props, fiber);
1212
- const tmp = document.createElement(fiber.liveEl.localName);
1213
- for (const c of view.children) for (const n of core$1.materialize(c, fiber)) tmp.append(n);
1407
+ const liveNs = fiber.liveEl.namespaceURI;
1408
+ const tmp = liveNs && liveNs !== "http://www.w3.org/1999/xhtml" ? document.createElementNS(liveNs, fiber.liveEl.localName) : document.createElement(fiber.liveEl.localName);
1409
+ const childFiber = {
1410
+ ...fiber,
1411
+ holes: fiber.holes,
1412
+ root: tmp
1413
+ };
1414
+ for (const c of view.children) for (const n of core$1.materialize(c, childFiber)) tmp.append(n);
1214
1415
  morphInner(fiber.liveEl, tmp);
1215
1416
  refreshAllAtomHosts(fiber);
1216
1417
  pruneDisconnectedAtomHoles(fiber);
@@ -1233,7 +1434,7 @@ const paint$1 = (fiber, view) => {
1233
1434
  paintMorphLive(fiber, view);
1234
1435
  return;
1235
1436
  }
1236
- core$1.disposeHoles(fiber);
1437
+ core$1.disposeHoles(fiber, { morph: true });
1237
1438
  if (fiber.root instanceof Element && fiber.root.childNodes.length > 0) {
1238
1439
  const tmp = document.createElement("div");
1239
1440
  for (const n of core$1.materialize(view, fiber)) tmp.append(n);
@@ -1596,4 +1797,4 @@ const renderToString = async (fn, opts) => {
1596
1797
  };
1597
1798
 
1598
1799
  //#endregion
1599
- export { interpret as a, handleOwner as c, untrack as d, wrapHandle as f, makeFiber as g, getFiber as h, runSetup as i, instr as l, closeFiber as m, renderToString as n, atom as o, clearFiberView as p, unsafe as r, batch as s, mount as t, isAtomHandle as u };
1800
+ 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 { i as Fragment, l as VNode, o as JsxComponent, r as ComponentFn, s as PropBag, t as AtomHandle, u as View } from "./types-CyoYWhJf.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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ilha",
3
- "version": "0.14.6",
3
+ "version": "0.14.8",
4
4
  "description": "Generator-first island UI on Effect",
5
5
  "keywords": [
6
6
  "effect",
@@ -65,9 +65,9 @@
65
65
  },
66
66
  "devDependencies": {
67
67
  "@happy-dom/global-registrator": "^20.11.2",
68
- "effect": "^4.0.0-rc.112"
68
+ "effect": "^4.0.0-rc.118"
69
69
  },
70
70
  "peerDependencies": {
71
- "effect": "^4.0.0-rc.112"
71
+ "effect": "^4.0.0-rc.118"
72
72
  }
73
73
  }