@zeus-js/runtime-dom 0.1.1-beta.1 → 0.1.1-beta.11

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,5 +1,5 @@
1
1
  /**
2
- * runtime-dom v0.1.1-beta.1
2
+ * runtime-dom v0.1.1-beta.11
3
3
  * (c) 2026 baicie
4
4
  * Released under the MIT License.
5
5
  **/
@@ -426,22 +426,14 @@ function stringifyText(value) {
426
426
  function setAttr(el, name, value) {
427
427
  const attrName = normalizeAttrName(name);
428
428
  const propName = getBooleanDomPropertyName(name);
429
- if (propName && el instanceof HTMLElement) {
430
- el[propName] = Boolean(value);
431
- if (value == null || value === false) el.removeAttribute(attrName);
432
- else if (value === true) el.setAttribute(attrName, "");
433
- else el.setAttribute(attrName, String(value));
434
- return;
435
- }
436
- if (value == null || value === false) {
437
- el.removeAttribute(attrName);
438
- return;
429
+ const serialized = value == null || value === false ? null : value === true ? "" : String(value);
430
+ if (serialized === null ? !el.hasAttribute(attrName) : el.getAttribute(attrName) === serialized) {
431
+ if (!propName || !(el instanceof HTMLElement)) return;
432
+ if (Boolean(value) === Boolean(el[propName])) return;
439
433
  }
440
- if (value === true) {
441
- el.setAttribute(attrName, "");
442
- return;
443
- }
444
- el.setAttribute(attrName, String(value));
434
+ if (propName && el instanceof HTMLElement) el[propName] = Boolean(value);
435
+ if (serialized === null) el.removeAttribute(attrName);
436
+ else el.setAttribute(attrName, serialized);
445
437
  }
446
438
  function getBooleanDomPropertyName(name) {
447
439
  return BOOLEAN_DOM_PROPERTY_NAME[name];
@@ -465,9 +457,7 @@ function normalizeAttrName(name) {
465
457
  return name === "className" ? "class" : name;
466
458
  }
467
459
  function bindAttr(el, name, value, once = false) {
468
- applyBinding(value, once, (next) => {
469
- setAttr(el, name, next);
470
- });
460
+ applyBinding(value, once, (next) => setAttr(el, name, next));
471
461
  }
472
462
  function bindProp(el, name, value, once = false) {
473
463
  applyBinding(value, once, (next) => {
@@ -501,42 +491,45 @@ function bindStyle(el, value, once = false) {
501
491
  prev = void 0;
502
492
  return;
503
493
  }
504
- patchStyle(el, prev, next);
505
- prev = next;
494
+ prev = patchStyle(el, prev, next);
506
495
  });
507
496
  }
508
497
  function patchStyle(el, prev, next) {
509
498
  const style = el.style;
510
- if (prev) {
511
- for (const key in prev) if (!(key in next)) style.setProperty(toKebabCase$2(key), "");
499
+ const values = prev ? prev[0] : Object.create(null);
500
+ const keys = Object.keys(next);
501
+ const state = style.cssText + keys.join(",");
502
+ const styleChanged = prev && state != prev[1];
503
+ let wrote = false;
504
+ for (const key in values) if (!(key in next)) {
505
+ style.setProperty(key.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`), "");
506
+ wrote = true;
507
+ delete values[key];
512
508
  }
513
509
  for (const key in next) {
514
510
  const value = next[key];
515
- const name = toKebabCase$2(key);
516
- if (value == null) style.setProperty(name, "");
517
- else style.setProperty(name, normalizeStyleValue(key, value));
511
+ const expected = value == null ? "" : normalizeStyleValue(key, value);
512
+ if (values[key] === expected && !styleChanged) continue;
513
+ style.setProperty(key.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`), expected);
514
+ wrote = true;
515
+ values[key] = expected;
518
516
  }
517
+ return prev && !wrote ? prev : [values, style.cssText + keys.join(",")];
519
518
  }
520
519
  function normalizeStyleValue(key, value) {
521
- if (typeof value === "number" && value !== 0 && !isUnitlessNumber(key)) return `${value}px`;
520
+ if (typeof value === "number" && value !== 0 && unitlessNumbers[key] !== true) return `${value}px`;
522
521
  return String(value);
523
522
  }
524
- const unitlessNumbers = /* @__PURE__ */ new Set([
525
- "opacity",
526
- "zIndex",
527
- "fontWeight",
528
- "lineHeight",
529
- "flex",
530
- "flexGrow",
531
- "flexShrink",
532
- "order"
533
- ]);
534
- function isUnitlessNumber(key) {
535
- return unitlessNumbers.has(key);
536
- }
537
- function toKebabCase$2(value) {
538
- return value.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
539
- }
523
+ const unitlessNumbers = {
524
+ opacity: true,
525
+ zIndex: true,
526
+ fontWeight: true,
527
+ lineHeight: true,
528
+ flex: true,
529
+ flexGrow: true,
530
+ flexShrink: true,
531
+ order: true
532
+ };
540
533
  //#endregion
541
534
  //#region packages/core/runtime-dom/src/events.ts
542
535
  const delegatedEvents = /* @__PURE__ */ new Set();
@@ -740,7 +733,9 @@ function mountIndexFor(parent, marker, each, render) {
740
733
  function mountKeyedFor(parent, marker, each, key, render) {
741
734
  let records = [];
742
735
  const subtreeContext = captureScopedSubtreeContext();
743
- let latestEntries = [];
736
+ let latestItems = [];
737
+ const latestKeys = [];
738
+ let latestItemsUseFixedSlots = false;
744
739
  let reconcileRequested = false;
745
740
  let reconciling = false;
746
741
  let disposed = false;
@@ -754,7 +749,7 @@ function mountKeyedFor(parent, marker, each, key, render) {
754
749
  for (const record of currentRecords) disposeListRecord(record);
755
750
  };
756
751
  (0, _zeus_js_signal_internal.onScopeDispose)(dispose, true);
757
- const reconcile = (nextEntries) => {
752
+ const reconcileEntries = (nextEntries) => {
758
753
  if (disposed) return;
759
754
  const oldMap = /* @__PURE__ */ new Map();
760
755
  for (const record of records) oldMap.set(record.key, record);
@@ -767,7 +762,10 @@ function mountKeyedFor(parent, marker, each, key, render) {
767
762
  const oldRecord = oldMap.get(itemKey);
768
763
  if (oldRecord) {
769
764
  oldMap.delete(itemKey);
770
- oldRecord.setItem(() => item);
765
+ if (!Object.is(oldRecord.item, item)) {
766
+ oldRecord.item = item;
767
+ oldRecord.setItem(() => item);
768
+ }
771
769
  oldRecord.setIndex(i);
772
770
  nextRecords.push(oldRecord);
773
771
  } else {
@@ -775,6 +773,7 @@ function mountKeyedFor(parent, marker, each, key, render) {
775
773
  const [readIndex, setIndex] = (0, _zeus_js_signal_internal.createSignal)(i);
776
774
  const subtree = new ScopedSubtree(parent, marker, subtreeContext);
777
775
  const record = {
776
+ item,
778
777
  key: itemKey,
779
778
  setItem,
780
779
  setIndex,
@@ -831,13 +830,48 @@ function mountKeyedFor(parent, marker, each, key, render) {
831
830
  throw error;
832
831
  }
833
832
  };
833
+ const reconcileLatestItems = () => {
834
+ if (disposed) return;
835
+ if (latestItemsUseFixedSlots) {
836
+ const items = latestItems;
837
+ (0, _zeus_js_signal_internal.batch)(() => {
838
+ for (let i = 0; i < items.length; i++) {
839
+ const item = items[i];
840
+ const record = records[i];
841
+ if (!Object.is(record.item, item)) {
842
+ record.item = item;
843
+ record.setItem(() => item);
844
+ }
845
+ }
846
+ });
847
+ if (!disposed) emitDevtoolsEvent({
848
+ type: "mount-for",
849
+ length: records.length
850
+ });
851
+ return;
852
+ }
853
+ const items = latestItems;
854
+ const nextEntries = [];
855
+ const nextKeys = /* @__PURE__ */ new Set();
856
+ for (let i = 0; i < items.length; i++) {
857
+ const item = items[i];
858
+ const itemKey = latestKeys[i];
859
+ if (nextKeys.has(itemKey)) throw duplicateKeyError(itemKey, i);
860
+ nextKeys.add(itemKey);
861
+ nextEntries.push({
862
+ item,
863
+ key: itemKey
864
+ });
865
+ }
866
+ reconcileEntries(nextEntries);
867
+ };
834
868
  const drainReconciliations = () => {
835
869
  if (reconciling) return;
836
870
  reconciling = true;
837
871
  try {
838
872
  while (reconcileRequested) {
839
873
  reconcileRequested = false;
840
- (0, _zeus_js_signal_internal.untrack)(() => reconcile(latestEntries));
874
+ (0, _zeus_js_signal_internal.untrack)(reconcileLatestItems);
841
875
  }
842
876
  } finally {
843
877
  reconciling = false;
@@ -854,19 +888,16 @@ function mountKeyedFor(parent, marker, each, key, render) {
854
888
  runner = (0, _zeus_js_signal_internal.effect)(() => {
855
889
  var _each2;
856
890
  const nextItems = (_each2 = each()) !== null && _each2 !== void 0 ? _each2 : [];
857
- const nextEntries = [];
858
- const nextKeys = /* @__PURE__ */ new Set();
891
+ let useFixedSlots = nextItems.length === records.length;
859
892
  for (let i = 0; i < nextItems.length; i++) {
860
893
  const item = nextItems[i];
861
894
  const itemKey = key(item, i);
862
- if (nextKeys.has(itemKey)) throw duplicateKeyError(itemKey, i);
863
- nextKeys.add(itemKey);
864
- nextEntries.push({
865
- item,
866
- key: itemKey
867
- });
895
+ latestKeys[i] = itemKey;
896
+ if (useFixedSlots && !Object.is(itemKey, records[i].key)) useFixedSlots = false;
868
897
  }
869
- latestEntries = nextEntries;
898
+ latestKeys.length = nextItems.length;
899
+ latestItems = nextItems;
900
+ latestItemsUseFixedSlots = useFixedSlots;
870
901
  }, { scheduler: scheduleReconciliation });
871
902
  } catch (error) {
872
903
  dispose();
@@ -1,5 +1,5 @@
1
1
  /**
2
- * runtime-dom v0.1.1-beta.1
2
+ * runtime-dom v0.1.1-beta.11
3
3
  * (c) 2026 baicie
4
4
  * Released under the MIT License.
5
5
  **/
@@ -278,7 +278,8 @@ var ReactiveEffect = class {
278
278
  trigger() {
279
279
  if (this.flags & 64) pausedQueueEffects.add(this);
280
280
  else if (this.scheduler) this.scheduler();
281
- else this.runIfDirty();
281
+ else if (this.flags & 128) this.runIfDirty();
282
+ else this.run();
282
283
  }
283
284
  /**
284
285
  * @internal
@@ -573,6 +574,7 @@ var Dep = class {
573
574
  }
574
575
  track(debugInfo) {
575
576
  if (!activeSub || !shouldTrack || activeSub === this.computed) return;
577
+ if (this.computed) activeSub.flags |= 128;
576
578
  let link = this.activeLink;
577
579
  if (link === void 0 || link.sub !== activeSub) {
578
580
  link = this.activeLink = new Link(activeSub, this);
@@ -1825,22 +1827,14 @@ function stringifyText(value) {
1825
1827
  function setAttr(el, name, value) {
1826
1828
  const attrName = normalizeAttrName(name);
1827
1829
  const propName = getBooleanDomPropertyName(name);
1828
- if (propName && el instanceof HTMLElement) {
1829
- el[propName] = Boolean(value);
1830
- if (value == null || value === false) el.removeAttribute(attrName);
1831
- else if (value === true) el.setAttribute(attrName, "");
1832
- else el.setAttribute(attrName, String(value));
1833
- return;
1834
- }
1835
- if (value == null || value === false) {
1836
- el.removeAttribute(attrName);
1837
- return;
1830
+ const serialized = value == null || value === false ? null : value === true ? "" : String(value);
1831
+ if (serialized === null ? !el.hasAttribute(attrName) : el.getAttribute(attrName) === serialized) {
1832
+ if (!propName || !(el instanceof HTMLElement)) return;
1833
+ if (Boolean(value) === Boolean(el[propName])) return;
1838
1834
  }
1839
- if (value === true) {
1840
- el.setAttribute(attrName, "");
1841
- return;
1842
- }
1843
- el.setAttribute(attrName, String(value));
1835
+ if (propName && el instanceof HTMLElement) el[propName] = Boolean(value);
1836
+ if (serialized === null) el.removeAttribute(attrName);
1837
+ else el.setAttribute(attrName, serialized);
1844
1838
  }
1845
1839
  function getBooleanDomPropertyName(name) {
1846
1840
  return BOOLEAN_DOM_PROPERTY_NAME[name];
@@ -1864,9 +1858,7 @@ function normalizeAttrName(name) {
1864
1858
  return name === "className" ? "class" : name;
1865
1859
  }
1866
1860
  function bindAttr(el, name, value, once = false) {
1867
- applyBinding(value, once, (next) => {
1868
- setAttr(el, name, next);
1869
- });
1861
+ applyBinding(value, once, (next) => setAttr(el, name, next));
1870
1862
  }
1871
1863
  function bindProp(el, name, value, once = false) {
1872
1864
  applyBinding(value, once, (next) => {
@@ -1900,42 +1892,45 @@ function bindStyle(el, value, once = false) {
1900
1892
  prev = void 0;
1901
1893
  return;
1902
1894
  }
1903
- patchStyle(el, prev, next);
1904
- prev = next;
1895
+ prev = patchStyle(el, prev, next);
1905
1896
  });
1906
1897
  }
1907
1898
  function patchStyle(el, prev, next) {
1908
1899
  const style = el.style;
1909
- if (prev) {
1910
- for (const key in prev) if (!(key in next)) style.setProperty(toKebabCase$2(key), "");
1900
+ const values = prev ? prev[0] : Object.create(null);
1901
+ const keys = Object.keys(next);
1902
+ const state = style.cssText + keys.join(",");
1903
+ const styleChanged = prev && state != prev[1];
1904
+ let wrote = false;
1905
+ for (const key in values) if (!(key in next)) {
1906
+ style.setProperty(key.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`), "");
1907
+ wrote = true;
1908
+ delete values[key];
1911
1909
  }
1912
1910
  for (const key in next) {
1913
1911
  const value = next[key];
1914
- const name = toKebabCase$2(key);
1915
- if (value == null) style.setProperty(name, "");
1916
- else style.setProperty(name, normalizeStyleValue(key, value));
1912
+ const expected = value == null ? "" : normalizeStyleValue(key, value);
1913
+ if (values[key] === expected && !styleChanged) continue;
1914
+ style.setProperty(key.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`), expected);
1915
+ wrote = true;
1916
+ values[key] = expected;
1917
1917
  }
1918
+ return prev && !wrote ? prev : [values, style.cssText + keys.join(",")];
1918
1919
  }
1919
1920
  function normalizeStyleValue(key, value) {
1920
- if (typeof value === "number" && value !== 0 && !isUnitlessNumber(key)) return `${value}px`;
1921
+ if (typeof value === "number" && value !== 0 && unitlessNumbers[key] !== true) return `${value}px`;
1921
1922
  return String(value);
1922
1923
  }
1923
- const unitlessNumbers = /* @__PURE__ */ new Set([
1924
- "opacity",
1925
- "zIndex",
1926
- "fontWeight",
1927
- "lineHeight",
1928
- "flex",
1929
- "flexGrow",
1930
- "flexShrink",
1931
- "order"
1932
- ]);
1933
- function isUnitlessNumber(key) {
1934
- return unitlessNumbers.has(key);
1935
- }
1936
- function toKebabCase$2(value) {
1937
- return value.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
1938
- }
1924
+ const unitlessNumbers = {
1925
+ opacity: true,
1926
+ zIndex: true,
1927
+ fontWeight: true,
1928
+ lineHeight: true,
1929
+ flex: true,
1930
+ flexGrow: true,
1931
+ flexShrink: true,
1932
+ order: true
1933
+ };
1939
1934
  //#endregion
1940
1935
  //#region packages/core/runtime-dom/src/events.ts
1941
1936
  const delegatedEvents = /* @__PURE__ */ new Set();
@@ -2139,7 +2134,9 @@ function mountIndexFor(parent, marker, each, render) {
2139
2134
  function mountKeyedFor(parent, marker, each, key, render) {
2140
2135
  let records = [];
2141
2136
  const subtreeContext = captureScopedSubtreeContext();
2142
- let latestEntries = [];
2137
+ let latestItems = [];
2138
+ const latestKeys = [];
2139
+ let latestItemsUseFixedSlots = false;
2143
2140
  let reconcileRequested = false;
2144
2141
  let reconciling = false;
2145
2142
  let disposed = false;
@@ -2153,7 +2150,7 @@ function mountKeyedFor(parent, marker, each, key, render) {
2153
2150
  for (const record of currentRecords) disposeListRecord(record);
2154
2151
  };
2155
2152
  onScopeDispose(dispose, true);
2156
- const reconcile = (nextEntries) => {
2153
+ const reconcileEntries = (nextEntries) => {
2157
2154
  if (disposed) return;
2158
2155
  const oldMap = /* @__PURE__ */ new Map();
2159
2156
  for (const record of records) oldMap.set(record.key, record);
@@ -2166,7 +2163,10 @@ function mountKeyedFor(parent, marker, each, key, render) {
2166
2163
  const oldRecord = oldMap.get(itemKey);
2167
2164
  if (oldRecord) {
2168
2165
  oldMap.delete(itemKey);
2169
- oldRecord.setItem(() => item);
2166
+ if (!Object.is(oldRecord.item, item)) {
2167
+ oldRecord.item = item;
2168
+ oldRecord.setItem(() => item);
2169
+ }
2170
2170
  oldRecord.setIndex(i);
2171
2171
  nextRecords.push(oldRecord);
2172
2172
  } else {
@@ -2174,6 +2174,7 @@ function mountKeyedFor(parent, marker, each, key, render) {
2174
2174
  const [readIndex, setIndex] = createSignal(i);
2175
2175
  const subtree = new ScopedSubtree(parent, marker, subtreeContext);
2176
2176
  const record = {
2177
+ item,
2177
2178
  key: itemKey,
2178
2179
  setItem,
2179
2180
  setIndex,
@@ -2230,13 +2231,48 @@ function mountKeyedFor(parent, marker, each, key, render) {
2230
2231
  throw error;
2231
2232
  }
2232
2233
  };
2234
+ const reconcileLatestItems = () => {
2235
+ if (disposed) return;
2236
+ if (latestItemsUseFixedSlots) {
2237
+ const items = latestItems;
2238
+ batch(() => {
2239
+ for (let i = 0; i < items.length; i++) {
2240
+ const item = items[i];
2241
+ const record = records[i];
2242
+ if (!Object.is(record.item, item)) {
2243
+ record.item = item;
2244
+ record.setItem(() => item);
2245
+ }
2246
+ }
2247
+ });
2248
+ if (!disposed) emitDevtoolsEvent({
2249
+ type: "mount-for",
2250
+ length: records.length
2251
+ });
2252
+ return;
2253
+ }
2254
+ const items = latestItems;
2255
+ const nextEntries = [];
2256
+ const nextKeys = /* @__PURE__ */ new Set();
2257
+ for (let i = 0; i < items.length; i++) {
2258
+ const item = items[i];
2259
+ const itemKey = latestKeys[i];
2260
+ if (nextKeys.has(itemKey)) throw duplicateKeyError(itemKey, i);
2261
+ nextKeys.add(itemKey);
2262
+ nextEntries.push({
2263
+ item,
2264
+ key: itemKey
2265
+ });
2266
+ }
2267
+ reconcileEntries(nextEntries);
2268
+ };
2233
2269
  const drainReconciliations = () => {
2234
2270
  if (reconciling) return;
2235
2271
  reconciling = true;
2236
2272
  try {
2237
2273
  while (reconcileRequested) {
2238
2274
  reconcileRequested = false;
2239
- untrack(() => reconcile(latestEntries));
2275
+ untrack(reconcileLatestItems);
2240
2276
  }
2241
2277
  } finally {
2242
2278
  reconciling = false;
@@ -2253,19 +2289,16 @@ function mountKeyedFor(parent, marker, each, key, render) {
2253
2289
  runner = effect(() => {
2254
2290
  var _each2;
2255
2291
  const nextItems = (_each2 = each()) !== null && _each2 !== void 0 ? _each2 : [];
2256
- const nextEntries = [];
2257
- const nextKeys = /* @__PURE__ */ new Set();
2292
+ let useFixedSlots = nextItems.length === records.length;
2258
2293
  for (let i = 0; i < nextItems.length; i++) {
2259
2294
  const item = nextItems[i];
2260
2295
  const itemKey = key(item, i);
2261
- if (nextKeys.has(itemKey)) throw duplicateKeyError(itemKey, i);
2262
- nextKeys.add(itemKey);
2263
- nextEntries.push({
2264
- item,
2265
- key: itemKey
2266
- });
2296
+ latestKeys[i] = itemKey;
2297
+ if (useFixedSlots && !Object.is(itemKey, records[i].key)) useFixedSlots = false;
2267
2298
  }
2268
- latestEntries = nextEntries;
2299
+ latestKeys.length = nextItems.length;
2300
+ latestItems = nextItems;
2301
+ latestItemsUseFixedSlots = useFixedSlots;
2269
2302
  }, { scheduler: scheduleReconciliation });
2270
2303
  } catch (error) {
2271
2304
  dispose();