ilha 0.15.0 → 0.15.1

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.
package/dist/define.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as mount } from "./mount-qJHX_CfF.js";
1
+ import { t as mount } from "./mount-DCu8b0hE.js";
2
2
 
3
3
  //#region src/define.ts
4
4
  const define = (name, component) => {
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { C as WatchOnceCleanup, S as WatchContext, _ as ResourceOptions, b as View, c as EventSourceStatus, d as Instruction, g as ResourceFetcher, h as Resource, i as Component, l as Fragment, n as AtomHandle, o as EventSourceFeed, r as AtomOptions, s as EventSourceOptions, t as AtomEquality, v as UnsafeHtml, w as Yielded, x as WatchCallback } from "./types-DsdtZMhG.js";
2
2
  import { t as h } from "./vnode-CpSQZnLM.js";
3
- import { r as watch } from "./runtime-B-sEGtAa.js";
3
+ import { r as watch } from "./runtime-Baq7MtkE.js";
4
4
  import * as Atom from "effect/reactivity/Atom";
5
5
  import { Equal } from "effect";
6
6
  import * as Effect$1 from "effect/Effect";
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { _ as isString, f as errorView, i as Fragment, m as isFunction, t as h } from "./vnode-BHUY61Rm.js";
2
- import { C as makeFiber, S as getFiber, _ as structuralEqual, b as clearFiberView, d as interpret, f as atom, g as isAtomHandle, h as instr, m as handleOwner, n as unsafe, p as batch, u as runSetup, v as untrack, x as closeFiber, y as wrapHandle } from "./paint-core-Cw1HbMp6.js";
3
- import { n as renderToString, t as mount } from "./mount-qJHX_CfF.js";
2
+ import { C as makeFiber, S as getFiber, _ as structuralEqual, b as clearFiberView, d as interpret, f as atom, g as isAtomHandle, h as instr, m as handleOwner, n as unsafe, p as batch, u as runSetup, v as untrack, x as closeFiber, y as wrapHandle } from "./paint-core-D9VO-oUM.js";
3
+ import { n as renderToString, t as mount } from "./mount-DCu8b0hE.js";
4
4
  import * as Atom from "effect/reactivity/Atom";
5
5
  import * as Effect from "effect/Effect";
6
6
  import * as Stream from "effect/Stream";
@@ -1,5 +1,5 @@
1
1
  import { _ as isString, c as KEEP, d as defer, g as isObject, m as isFunction, r as isVNode, u as SLOT_ATTR, y as unwrap } from "./vnode-BHUY61Rm.js";
2
- import { T as makeRuntime, a as morphInner, c as bindEvents, i as insertRendered, l as eventTypeFromProp, o as placeChildren, r as committedElement, s as standInFor, t as createPainter, u as runSetup, w as makeRootFiber, x as closeFiber } from "./paint-core-Cw1HbMp6.js";
2
+ import { T as makeRuntime, a as morphInner, c as bindEvents, i as insertRendered, l as eventTypeFromProp, o as placeChildren, r as committedElement, s as standInFor, t as createPainter, u as runSetup, w as makeRootFiber, x as closeFiber } from "./paint-core-D9VO-oUM.js";
3
3
 
4
4
  //#region src/paint.ts
5
5
  const noopDisconnect = () => {};
@@ -362,8 +362,10 @@ const subscribeRenderDeps = (fiber, deps, run) => {
362
362
  const schedule = () => {
363
363
  if (scheduled) return;
364
364
  scheduled = true;
365
+ const seen = fiber.renderGen;
365
366
  fiber.runtime.later(() => {
366
367
  scheduled = false;
368
+ if (fiber.renderGen !== seen) return;
367
369
  run();
368
370
  });
369
371
  };
@@ -507,6 +509,7 @@ const runSetup = (fiber, fn) => {
507
509
  },
508
510
  render: () => {
509
511
  if (fiber.closed) return;
512
+ fiber.renderGen = (fiber.renderGen ?? 0) + 1;
510
513
  fiber.runtime.begin();
511
514
  beginPrimitiveFrame(fiber);
512
515
  withTrackGetRun(fiber, () => fn(), ({ value: out, deps }) => {
@@ -1133,36 +1136,26 @@ const elementNs = (lower, parentNs) => {
1133
1136
  if (parentNs === "http://www.w3.org/2000/svg" || parentNs === "http://www.w3.org/1998/Math/MathML") return parentNs;
1134
1137
  };
1135
1138
  const canMorphRoot = (root) => typeof Element !== "undefined" && typeof document !== "undefined" && root instanceof Element && root.childNodes.length > 0;
1139
+ /** Close a child hole through the parent's hole list, so it disposes once. */
1140
+ const releaseHole = (fiber, hole) => {
1141
+ const idx = fiber.holes.findIndex((h) => h.holeFiber === hole);
1142
+ if (idx === -1) closeFiber(hole);
1143
+ else {
1144
+ const [removed] = fiber.holes.splice(idx, 1);
1145
+ removed?.dispose();
1146
+ }
1147
+ };
1136
1148
  const sweepComponentSlots = (fiber) => {
1137
1149
  const cframe = fiber.componentFrame;
1138
1150
  if (!cframe) return;
1139
1151
  const { slots } = cframe;
1140
1152
  for (let j = cframe.i; j < slots.length; j += 1) {
1141
1153
  const s = slots[j];
1142
- if (s) {
1143
- const { hole } = s;
1144
- if (!hole.closed) {
1145
- const idx = fiber.holes.findIndex((h) => h.holeFiber === hole);
1146
- if (idx === -1) closeFiber(hole);
1147
- else {
1148
- const [removed] = fiber.holes.splice(idx, 1);
1149
- removed?.dispose();
1150
- }
1151
- }
1152
- }
1154
+ if (s && !s.hole.closed) releaseHole(fiber, s.hole);
1153
1155
  slots[j] = void 0;
1154
1156
  }
1155
1157
  slots.length = cframe.i;
1156
1158
  };
1157
- /** Close a child hole through the parent's hole list, so it disposes once. */
1158
- const releaseHole = (fiber, hole) => {
1159
- const idx = fiber.holes.findIndex((h) => h.holeFiber === hole);
1160
- if (idx === -1) closeFiber(hole);
1161
- else {
1162
- const [removed] = fiber.holes.splice(idx, 1);
1163
- removed?.dispose();
1164
- }
1165
- };
1166
1159
  /** Close the keyed components the paint that just ran did not reach. */
1167
1160
  const sweepKeyedHoles = (fiber) => {
1168
1161
  const { keyedHoles, keyedGen } = fiber;
@@ -1183,6 +1176,61 @@ const pass = (fiber, fn) => {
1183
1176
  fn();
1184
1177
  sweepKeyedHoles(fiber);
1185
1178
  };
1179
+ /**
1180
+ * Dispose a hole fiber's child holes before it repaints, except the component
1181
+ * and island holes the repaint can reuse: positional children, keyed
1182
+ * children, and islands. Reuse pushes new props instead of remounting, and the
1183
+ * sweeps that end the paint close whatever it did not reach.
1184
+ */
1185
+ const keepReusableHoles = (fiber, cframe, frame) => {
1186
+ const kept = /* @__PURE__ */ new Set();
1187
+ for (const s of cframe.slots) if (s && !s.hole.closed) kept.add(s.hole);
1188
+ for (const hole of fiber.keyedHoles?.values() ?? []) if (!hole.closed) kept.add(hole);
1189
+ const keptIslands = new Set(frame.slots);
1190
+ const keep = [];
1191
+ for (const h of fiber.holes) {
1192
+ if (h.holeFiber && kept.has(h.holeFiber) || h.islandSlot && keptIslands.has(h.islandSlot)) {
1193
+ keep.push(h);
1194
+ continue;
1195
+ }
1196
+ h.dispose();
1197
+ }
1198
+ fiber.holes = keep;
1199
+ };
1200
+ /** Shallow equality of two prop bags, children compared item by item. */
1201
+ const sameProps = (prev, next) => {
1202
+ if (!prev) return false;
1203
+ const keys = Object.keys(next);
1204
+ if (keys.length !== Object.keys(prev).length) return false;
1205
+ return keys.every((k) => {
1206
+ const a = prev[k];
1207
+ const b = next[k];
1208
+ if (k === "children" && Array.isArray(a) && Array.isArray(b)) return a.length === b.length && a.every((x, i) => Object.is(x, b[i]));
1209
+ return Object.is(a, b);
1210
+ });
1211
+ };
1212
+ /**
1213
+ * Rerun a reused component hole with new props, in its own fiber. The props
1214
+ * box is updated in place, so every render closure of the hole reads the
1215
+ * latest props, including a self-render queued while an async render ran.
1216
+ */
1217
+ const rerunHole = (hole, run, props) => {
1218
+ const box = hole.propsBox ?? { current: props };
1219
+ box.current = props;
1220
+ hole.propsBox = box;
1221
+ runSetup(hole, () => run(box.current));
1222
+ };
1223
+ /**
1224
+ * Run a new component hole's first render after the paint that opened it. A
1225
+ * repaint that reached the hole first already rendered it with newer props.
1226
+ */
1227
+ const mountHoleLater = (fiber, hole, run) => {
1228
+ fiber.runtime.later(() => {
1229
+ const box = hole.propsBox;
1230
+ if (hole.closed || hole.renderGen !== void 0 || !box) return;
1231
+ runSetup(hole, () => run(box.current));
1232
+ });
1233
+ };
1186
1234
  const warnedKeys = /* @__PURE__ */ new Set();
1187
1235
  const warnDuplicateKey = (key) => {
1188
1236
  if (warnedKeys.has(key)) return;
@@ -1221,10 +1269,6 @@ const styleCss = (v) => {
1221
1269
  if (isString(v) || isObject(v)) return serializeStyleAttr(v);
1222
1270
  return "";
1223
1271
  };
1224
- const isThenable = (value) => {
1225
- if (!isObject(value) && !isFunction(value)) return false;
1226
- return isFunction(value.then);
1227
- };
1228
1272
  const writeAttr = (el, k, v) => {
1229
1273
  if (v === false || v === null || v === void 0) {
1230
1274
  el.removeAttribute(k);
@@ -1369,18 +1413,7 @@ const createPainter = (ops) => {
1369
1413
  sweepIslandSlots(fiber);
1370
1414
  return;
1371
1415
  }
1372
- const kept = /* @__PURE__ */ new Set();
1373
- for (const s of cframe.slots) if (s && !s.hole.closed) kept.add(s.hole);
1374
- const keptIslands = new Set(frame.slots);
1375
- const keep = [];
1376
- for (const h of fiber.holes) {
1377
- if (h.holeFiber && kept.has(h.holeFiber) || h.islandSlot && keptIslands.has(h.islandSlot)) {
1378
- keep.push(h);
1379
- continue;
1380
- }
1381
- h.dispose();
1382
- }
1383
- fiber.holes = keep;
1416
+ keepReusableHoles(fiber, cframe, frame);
1384
1417
  const fresh = api.materialize(view, fiber);
1385
1418
  if (canMorphRoot(fiber.root)) {
1386
1419
  const tmp = document.createElement("div");
@@ -1494,18 +1527,10 @@ const createPainter = (ops) => {
1494
1527
  if (existing) {
1495
1528
  const { hole } = existing;
1496
1529
  if (existing.type === type && !hole.closed) {
1497
- hole.propsBox = { current: props };
1498
- runSetup(hole, () => run(props));
1530
+ rerunHole(hole, run, props);
1499
1531
  return [ops.reuseNode(hole.root)];
1500
1532
  }
1501
- if (!hole.closed) {
1502
- const idx = fiber.holes.findIndex((h) => h.holeFiber === hole);
1503
- if (idx === -1) closeFiber(hole);
1504
- else {
1505
- const [removed] = fiber.holes.splice(idx, 1);
1506
- removed?.dispose();
1507
- }
1508
- }
1533
+ if (!hole.closed) releaseHole(fiber, hole);
1509
1534
  }
1510
1535
  const { fiber: hole, nodes } = api.openHole(fiber);
1511
1536
  hole.propsBox = { current: props };
@@ -1514,51 +1539,53 @@ const createPainter = (ops) => {
1514
1539
  hole,
1515
1540
  type
1516
1541
  };
1517
- fiber.runtime.later(() => {
1518
- if (hole.closed) return;
1519
- const box = hole.propsBox;
1520
- if (!box) return;
1521
- runSetup(hole, () => run(box.current));
1522
- });
1542
+ mountHoleLater(fiber, hole, run);
1523
1543
  trackHole(fiber, hole, void 0, { keyed: true });
1524
1544
  return nodes;
1525
1545
  };
1526
- const materializeComponent = (view, type, fiber) => {
1527
- const props = {
1528
- ...view.props,
1529
- children: view.children
1530
- };
1531
- const branded = type;
1532
- if (branded[ISLAND] === true) return materializeIsland(branded, props, fiber);
1533
- const k = view.key === null || view.key === void 0 ? void 0 : String(view.key);
1534
- const reuse = k ? fiber.keyedHoles?.get(k) : void 0;
1535
- const run = type;
1546
+ const materializeKeyedComponent = ({ run, type, props, key, fiber }) => {
1547
+ let reuse = fiber.keyedHoles?.get(key);
1536
1548
  const gen = fiber.keyedGen;
1537
- if (k && reuse && !reuse.closed && reuse.keyedSeen === gen) {
1538
- warnDuplicateKey(k);
1549
+ if (reuse && !reuse.closed && reuse.keyedSeen === gen) {
1550
+ warnDuplicateKey(key);
1539
1551
  return materializeUnkeyedComponent(run, type, props, fiber);
1540
1552
  }
1553
+ if (reuse && !reuse.closed && reuse.componentType !== type) {
1554
+ fiber.keyedHoles?.delete(key);
1555
+ releaseHole(fiber, reuse);
1556
+ reuse = void 0;
1557
+ }
1541
1558
  if (reuse && !reuse.closed) {
1542
1559
  reuse.keyedSeen = gen;
1543
- if (reuse.propsBox) reuse.propsBox.current = props;
1544
- const next = withFiber(fiber, () => run(reuse.propsBox?.current ?? props));
1545
- if (next && !isThenable(next) && !isSetupFn(next)) reuse.paint(next);
1560
+ if (!sameProps(reuse.propsBox?.current, props)) rerunHole(reuse, run, props);
1546
1561
  return [ops.reuseNode(reuse.root)];
1547
1562
  }
1548
- if (!k) return materializeUnkeyedComponent(run, type, props, fiber);
1549
1563
  const { fiber: khole, nodes: knodes } = api.openHole(fiber);
1550
1564
  khole.propsBox = { current: props };
1565
+ khole.componentType = type;
1551
1566
  khole.keyedSeen = gen;
1552
- fiber.keyedHoles?.set(k, khole);
1553
- fiber.runtime.later(() => {
1554
- if (khole.closed) return;
1555
- const box = khole.propsBox;
1556
- if (!box) return;
1557
- runSetup(khole, () => run(box.current));
1558
- });
1567
+ fiber.keyedHoles?.set(key, khole);
1568
+ mountHoleLater(fiber, khole, run);
1559
1569
  trackHole(fiber, khole, void 0, { keyed: true });
1560
1570
  return knodes;
1561
1571
  };
1572
+ const materializeComponent = (view, type, fiber) => {
1573
+ const props = {
1574
+ ...view.props,
1575
+ children: view.children
1576
+ };
1577
+ const branded = type;
1578
+ if (branded[ISLAND] === true) return materializeIsland(branded, props, fiber);
1579
+ const run = type;
1580
+ if (view.key === null || view.key === void 0) return materializeUnkeyedComponent(run, type, props, fiber);
1581
+ return materializeKeyedComponent({
1582
+ fiber,
1583
+ key: String(view.key),
1584
+ props,
1585
+ run,
1586
+ type
1587
+ });
1588
+ };
1562
1589
  const materializeElement = (view, fiber) => {
1563
1590
  const tag = view.type;
1564
1591
  const lower = tag.toLowerCase();
@@ -1602,8 +1629,14 @@ const createPainter = (ops) => {
1602
1629
  const nodes = [];
1603
1630
  for (const v of views) {
1604
1631
  const reuse = fiber.keyedHoles?.get(String(v.key));
1605
- if (reuse && !reuse.closed) {
1632
+ const reusable = reuse && !reuse.closed && reuse.keyedSeen !== fiber.keyedGen && reuse.componentType === v.type;
1633
+ if (reuse && reusable) {
1606
1634
  reuse.keyedSeen = fiber.keyedGen;
1635
+ const props = {
1636
+ ...v.props,
1637
+ children: v.children
1638
+ };
1639
+ if (!sameProps(reuse.propsBox?.current, props)) rerunHole(reuse, v.type, props);
1607
1640
  nodes.push(ops.asNode(reuse.root));
1608
1641
  continue;
1609
1642
  }
@@ -1,5 +1,5 @@
1
1
  import { b as View, i as Component, m as PropValue, p as PropBag, u as IlhaRuntime } from "./types-DsdtZMhG.js";
2
- import { n as getFiber, t as FiberLocal } from "./runtime-B-sEGtAa.js";
2
+ import { n as getFiber, t as FiberLocal } from "./runtime-Baq7MtkE.js";
3
3
  //#region src/shared.d.ts
4
4
  declare const KEEP: unique symbol;
5
5
  //#endregion
package/dist/renderer.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { d as defer } from "./vnode-BHUY61Rm.js";
2
- import { S as getFiber, T as makeRuntime, c as bindEvents, t as createPainter, u as runSetup, w as makeRootFiber, x as closeFiber } from "./paint-core-Cw1HbMp6.js";
2
+ import { S as getFiber, T as makeRuntime, c as bindEvents, t as createPainter, u as runSetup, w as makeRootFiber, x as closeFiber } from "./paint-core-D9VO-oUM.js";
3
3
 
4
4
  //#region src/renderer.ts
5
5
  /**
@@ -78,6 +78,8 @@ interface FiberLocal {
78
78
  componentFrame?: ComponentFrame;
79
79
  componentType?: unknown;
80
80
  renderSub?: () => void;
81
+ /** Counts the renders this fiber started; see `subscribeRenderDeps`. */
82
+ renderGen?: number;
81
83
  trackRestore?: () => void;
82
84
  propsBox?: {
83
85
  current: PropBag;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ilha",
3
- "version": "0.15.0",
3
+ "version": "0.15.1",
4
4
  "description": "Generator-first island UI on Effect",
5
5
  "keywords": [
6
6
  "effect",