kerfjs 4.4.0 → 4.5.0-beta.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.
Files changed (84) hide show
  1. package/CHANGELOG.md +284 -104
  2. package/LICENSE +1 -1
  3. package/README.md +73 -54
  4. package/ai/cursorrules +130 -48
  5. package/ai/manifest.json +63 -5
  6. package/ai/skill.md +148 -65
  7. package/dist/actions.d.ts +1 -1
  8. package/dist/actions.js +4 -4
  9. package/dist/actions.js.map +1 -1
  10. package/dist/array-signal.js +5 -5
  11. package/dist/async.js +34 -24
  12. package/dist/async.js.map +1 -1
  13. package/dist/attach.d.ts +11 -8
  14. package/dist/attach.js +54 -4
  15. package/dist/attach.js.map +1 -1
  16. package/dist/{chunk-QIP723L4.js → chunk-5WRGJZV6.js} +4 -3
  17. package/dist/chunk-5WRGJZV6.js.map +1 -0
  18. package/dist/{chunk-GY4XV2UV.js → chunk-BDX3R4OM.js} +4 -3
  19. package/dist/chunk-BDX3R4OM.js.map +1 -0
  20. package/dist/{chunk-VVDJLWMP.js → chunk-CEQMZYLR.js} +2 -2
  21. package/dist/chunk-CEQMZYLR.js.map +1 -0
  22. package/dist/{chunk-KEZTD6H4.js → chunk-E5R5GNKE.js} +15 -5
  23. package/dist/chunk-E5R5GNKE.js.map +1 -0
  24. package/dist/{chunk-MRYM3O3V.js → chunk-ELXVRKY2.js} +12 -9
  25. package/dist/chunk-ELXVRKY2.js.map +1 -0
  26. package/dist/{chunk-U32TFTGZ.js → chunk-MK42GLPV.js} +3 -3
  27. package/dist/chunk-MK42GLPV.js.map +1 -0
  28. package/dist/{chunk-SUPUPSBE.js → chunk-QFUNWHKH.js} +42 -20
  29. package/dist/chunk-QFUNWHKH.js.map +1 -0
  30. package/dist/{chunk-SRWQKB33.js → chunk-V757BT6U.js} +206 -111
  31. package/dist/chunk-V757BT6U.js.map +1 -0
  32. package/dist/{chunk-YHH7OUFA.js → chunk-WKIPLNVO.js} +3 -3
  33. package/dist/chunk-WKIPLNVO.js.map +1 -0
  34. package/dist/{chunk-3APBEVHF.js → chunk-Y2FOYPBV.js} +3 -3
  35. package/dist/{chunk-3APBEVHF.js.map → chunk-Y2FOYPBV.js.map} +1 -1
  36. package/dist/{chunk-SAYPJ6XR.js → chunk-ZOIERTUW.js} +10 -6
  37. package/dist/chunk-ZOIERTUW.js.map +1 -0
  38. package/dist/dev.d.ts +9 -6
  39. package/dist/dev.js +66 -19
  40. package/dist/dev.js.map +1 -1
  41. package/dist/html.d.ts +1 -1
  42. package/dist/html.js +8 -7
  43. package/dist/html.js.map +1 -1
  44. package/dist/index.d.ts +1 -1
  45. package/dist/index.js +28 -19
  46. package/dist/index.js.map +1 -1
  47. package/dist/jsx-runtime.js +5 -5
  48. package/dist/list.d.ts +1 -1
  49. package/dist/list.js +304 -213
  50. package/dist/list.js.map +1 -1
  51. package/dist/overlay.d.ts +190 -280
  52. package/dist/overlay.js +471 -364
  53. package/dist/overlay.js.map +1 -1
  54. package/dist/remount.d.ts +5 -3
  55. package/dist/remount.js +29 -10
  56. package/dist/remount.js.map +1 -1
  57. package/dist/router.d.ts +1 -1
  58. package/dist/router.js +73 -28
  59. package/dist/router.js.map +1 -1
  60. package/dist/scope.d.ts +4 -3
  61. package/dist/scope.js +11 -10
  62. package/dist/scope.js.map +1 -1
  63. package/dist/testing.js +4 -4
  64. package/dist/timing.d.ts +3 -2
  65. package/dist/timing.js +5 -5
  66. package/dist/timing.js.map +1 -1
  67. package/llms.txt +13 -6
  68. package/package.json +35 -13
  69. package/setup/cli.mjs +90 -0
  70. package/setup/index.d.mts +37 -0
  71. package/setup/index.mjs +1347 -0
  72. package/setup/jsonc.mjs +201 -0
  73. package/setup/state.schema.json +56 -0
  74. package/dist/chunk-GY4XV2UV.js.map +0 -1
  75. package/dist/chunk-KEZTD6H4.js.map +0 -1
  76. package/dist/chunk-MRYM3O3V.js.map +0 -1
  77. package/dist/chunk-QIP723L4.js.map +0 -1
  78. package/dist/chunk-SAYPJ6XR.js.map +0 -1
  79. package/dist/chunk-SRWQKB33.js.map +0 -1
  80. package/dist/chunk-SUPUPSBE.js.map +0 -1
  81. package/dist/chunk-U32TFTGZ.js.map +0 -1
  82. package/dist/chunk-VVDJLWMP.js.map +0 -1
  83. package/dist/chunk-YHH7OUFA.js.map +0 -1
  84. /package/dist/{attrSelector-Cmu2ZoGO.d.ts → attr-Cmu2ZoGO.d.ts} +0 -0
package/dist/list.js CHANGED
@@ -1,226 +1,224 @@
1
- import { ARRAY_SIGNAL_BRAND } from './chunk-MRYM3O3V.js';
2
- import { moveNode, mount } from './chunk-SRWQKB33.js';
3
- import './chunk-QIP723L4.js';
4
- import './chunk-YHH7OUFA.js';
5
- import './chunk-SUPUPSBE.js';
6
- import { effect } from './chunk-3APBEVHF.js';
7
- import './chunk-GY4XV2UV.js';
8
- import './chunk-VVDJLWMP.js';
1
+ import { ARRAY_SIGNAL_BRAND } from './chunk-ELXVRKY2.js';
2
+ import { captureFocus, restoreFocus, moveNode, mount } from './chunk-V757BT6U.js';
9
3
 
10
- // src/list.ts
11
- function bindList(parent, source, options) {
12
- const { key, render, tag = "div", virtualize, before } = options;
13
- const overscan = virtualize?.overscan ?? 3;
14
- const minRows = virtualize?.minRows;
15
- const contentVisibility = virtualize?.mode === "content-visibility";
16
- const endAnchor = () => {
17
- if (virtualize !== void 0 || before === void 0) return null;
18
- return (typeof before === "function" ? before() : before) ?? null;
19
- };
4
+
5
+
6
+ import { effect } from './chunk-Y2FOYPBV.js';
7
+
8
+
9
+
10
+ // src/list-row-controller.ts
11
+ var NOOP = () => {
12
+ };
13
+ function createListRowController(options) {
14
+ const { container, key, render, tag, endAnchor } = options;
20
15
  const rows = /* @__PURE__ */ new Map();
21
16
  const order = [];
22
- let items = [];
23
- let disposed = false;
24
- let rafPending = false;
25
- let firstRender = true;
26
- const patchSource = source;
27
- const granularEligible = virtualize === void 0 && patchSource[ARRAY_SIGNAL_BRAND] === true;
28
- const container = virtualize === void 0 ? parent : document.createElement("div");
29
- if (virtualize !== void 0) {
30
- if (virtualize.containerClass !== void 0) container.className = virtualize.containerClass;
31
- if (virtualize.containerId !== void 0) container.id = virtualize.containerId;
32
- parent.appendChild(container);
33
- }
34
- const NOOP = () => {
35
- };
36
17
  const asElementRow = (rendered) => {
37
18
  if (rendered instanceof HTMLElement) return { el: rendered, dispose: NOOP };
38
19
  if (rendered !== null && typeof rendered === "object" && "el" in rendered && rendered.el instanceof HTMLElement) {
39
- const r = rendered;
40
- return { el: r.el, dispose: r.dispose ?? NOOP, update: r.update };
20
+ const row = rendered;
21
+ return { el: row.el, dispose: row.dispose ?? NOOP, update: row.update };
41
22
  }
42
23
  return null;
43
24
  };
44
25
  const makeRow = (item) => {
45
26
  const elementRow = asElementRow(render(item));
46
27
  if (elementRow !== null) {
47
- return { el: elementRow.el, item, dispose: elementRow.dispose, elementMode: true, update: elementRow.update };
28
+ return {
29
+ el: elementRow.el,
30
+ item,
31
+ dispose: elementRow.dispose,
32
+ elementMode: true,
33
+ update: elementRow.update
34
+ };
48
35
  }
49
36
  const el = document.createElement(tag);
50
37
  const dispose2 = mount(el, () => render(item));
51
38
  return { el, item, dispose: dispose2, elementMode: false };
52
39
  };
53
- const reconcileItem = (row, k, item) => {
40
+ const reconcileItem = (row, item) => {
54
41
  if (row.item === item) return row;
42
+ const oldKey = key(row.item);
43
+ const newKey = key(item);
55
44
  if (row.elementMode) {
56
45
  row.item = item;
46
+ if (newKey !== oldKey) {
47
+ rows.delete(oldKey);
48
+ rows.set(newKey, row);
49
+ }
57
50
  row.update?.(item);
58
51
  return row;
59
52
  }
60
53
  row.dispose();
61
54
  row.el.remove();
62
- rows.delete(k);
55
+ rows.delete(oldKey);
63
56
  const fresh = makeRow(item);
64
- rows.set(k, fresh);
57
+ rows.set(newKey, fresh);
65
58
  return fresh;
66
59
  };
67
- const syncRows = (visible) => {
68
- const wanted = /* @__PURE__ */ new Set();
69
- for (const item of visible) wanted.add(key(item));
70
- for (const [k, row] of rows) {
71
- if (!wanted.has(k)) {
72
- row.dispose();
73
- row.el.remove();
74
- rows.delete(k);
75
- }
60
+ const preserveFocus = (operation) => {
61
+ const focus = captureFocus(container);
62
+ try {
63
+ operation();
64
+ } finally {
65
+ if (focus !== null) restoreFocus(focus);
76
66
  }
77
- order.length = 0;
78
- for (const item of visible) {
79
- const k = key(item);
80
- const existing = rows.get(k);
81
- let row;
82
- if (existing !== void 0) {
83
- row = reconcileItem(existing, k, item);
84
- } else {
85
- row = makeRow(item);
86
- rows.set(k, row);
67
+ };
68
+ const sync = (visible) => {
69
+ preserveFocus(() => {
70
+ const wanted = /* @__PURE__ */ new Set();
71
+ for (const item of visible) wanted.add(key(item));
72
+ for (const [rowKey, row] of rows) {
73
+ if (!wanted.has(rowKey)) {
74
+ row.dispose();
75
+ row.el.remove();
76
+ rows.delete(rowKey);
77
+ }
87
78
  }
88
- order.push(row);
89
- }
90
- let ref = endAnchor();
91
- for (let i = order.length - 1; i >= 0; i--) {
92
- const el = order[i].el;
93
- if (el.parentNode !== container || el.nextSibling !== ref) {
94
- moveNode(container, el, ref);
79
+ order.length = 0;
80
+ for (const item of visible) {
81
+ const rowKey = key(item);
82
+ const existing = rows.get(rowKey);
83
+ const row = existing === void 0 ? makeRow(item) : reconcileItem(existing, item);
84
+ if (existing === void 0) rows.set(rowKey, row);
85
+ order.push(row);
95
86
  }
96
- ref = el;
97
- }
87
+ let ref = endAnchor();
88
+ for (let index = order.length - 1; index >= 0; index--) {
89
+ const el = order[index].el;
90
+ if (el.parentNode !== container || el.nextSibling !== ref)
91
+ moveNode(container, el, ref);
92
+ ref = el;
93
+ }
94
+ });
98
95
  };
99
96
  const applyPatches = (patches) => {
100
- for (const patch of patches) {
101
- if (patch.type === "insert") {
102
- const row = makeRow(patch.item);
103
- rows.set(key(patch.item), row);
104
- order.splice(patch.index, 0, row);
105
- container.insertBefore(row.el, order[patch.index + 1]?.el ?? endAnchor());
106
- } else if (patch.type === "remove") {
107
- const [row] = order.splice(patch.index, 1);
108
- row.dispose();
109
- row.el.remove();
110
- rows.delete(key(row.item));
111
- } else if (patch.type === "move") {
112
- const [row] = order.splice(patch.from, 1);
113
- order.splice(patch.to, 0, row);
114
- moveNode(container, row.el, order[patch.to + 1]?.el ?? endAnchor());
115
- } else if (patch.type === "update") {
116
- const current = order[patch.index];
117
- if (current.item !== patch.item) {
118
- if (current.elementMode) {
119
- const oldKey = key(current.item);
120
- const newKey = key(patch.item);
121
- current.item = patch.item;
122
- if (newKey !== oldKey) {
123
- rows.delete(oldKey);
124
- rows.set(newKey, current);
125
- }
126
- current.update?.(patch.item);
127
- } else {
128
- current.dispose();
129
- current.el.remove();
130
- rows.delete(key(current.item));
131
- const row = makeRow(patch.item);
132
- rows.set(key(patch.item), row);
97
+ preserveFocus(() => {
98
+ for (const patch of patches) {
99
+ if (patch.type === "insert") {
100
+ const row = makeRow(patch.item);
101
+ rows.set(key(patch.item), row);
102
+ order.splice(patch.index, 0, row);
103
+ container.insertBefore(
104
+ row.el,
105
+ order[patch.index + 1]?.el ?? endAnchor()
106
+ );
107
+ } else if (patch.type === "remove") {
108
+ const [row] = order.splice(patch.index, 1);
109
+ row.dispose();
110
+ row.el.remove();
111
+ rows.delete(key(row.item));
112
+ } else if (patch.type === "move") {
113
+ const [row] = order.splice(patch.from, 1);
114
+ order.splice(patch.to, 0, row);
115
+ moveNode(container, row.el, order[patch.to + 1]?.el ?? endAnchor());
116
+ } else if (patch.type === "update") {
117
+ const current = order[patch.index];
118
+ const row = reconcileItem(current, patch.item);
119
+ if (row !== current) {
133
120
  order[patch.index] = row;
134
- container.insertBefore(row.el, order[patch.index + 1]?.el ?? endAnchor());
121
+ container.insertBefore(
122
+ row.el,
123
+ order[patch.index + 1]?.el ?? endAnchor()
124
+ );
135
125
  }
136
126
  }
137
127
  }
128
+ });
129
+ };
130
+ const dispose = (removeElements) => {
131
+ for (const row of rows.values()) {
132
+ row.dispose();
133
+ if (removeElements) row.el.remove();
138
134
  }
135
+ rows.clear();
136
+ order.length = 0;
139
137
  };
140
- const rowHeight = virtualize?.rowHeight;
138
+ return { order, sync, applyPatches, dispose };
139
+ }
140
+
141
+ // src/list-virtualization-controller.ts
142
+ function createListVirtualizationController(options) {
143
+ const { parent, container, virtualize, key, rows } = options;
144
+ const overscan = virtualize.overscan ?? 3;
145
+ const { minRows, rowHeight } = virtualize;
146
+ const contentVisibility = virtualize.mode === "content-visibility";
141
147
  const fixedHeight = typeof rowHeight === "number" ? rowHeight : null;
142
148
  const measuring = typeof rowHeight === "object" && rowHeight !== null;
143
149
  const measured = /* @__PURE__ */ new Map();
144
- const estimateAt = (index) => {
145
- const est = rowHeight.estimate;
146
- return typeof est === "function" ? est(items[index], index) : est;
147
- };
148
- const variableHeightAt = fixedHeight !== null ? null : measuring ? (index) => {
149
- const k = key(items[index]);
150
- return measured.has(k) ? measured.get(k) : estimateAt(index);
151
- } : (index) => rowHeight(items[index], index);
152
- const intrinsicSizeAt = (index) => fixedHeight !== null ? fixedHeight : variableHeightAt(index);
150
+ const indexByKey = /* @__PURE__ */ new Map();
151
+ const renderSubscribers = /* @__PURE__ */ new Set();
152
+ let items = [];
153
153
  let offsets = [0];
154
154
  let heightsDirty = true;
155
- const indexByKey = /* @__PURE__ */ new Map();
156
155
  let pendingAnchorDelta = 0;
156
+ let rafPending = false;
157
+ let disposed = false;
158
+ const estimateAt = (index) => {
159
+ const estimate = rowHeight.estimate;
160
+ return typeof estimate === "function" ? estimate(items[index], index) : estimate;
161
+ };
162
+ const variableHeightAt = fixedHeight !== null ? null : measuring ? (index) => measured.get(key(items[index])) ?? estimateAt(index) : (index) => rowHeight(
163
+ items[index],
164
+ index
165
+ );
166
+ const intrinsicSizeAt = (index) => fixedHeight ?? variableHeightAt(index);
157
167
  const rebuildOffsets = () => {
158
- const fn = variableHeightAt;
159
- const total = items.length;
160
- offsets = new Array(total + 1);
168
+ const heightAt = variableHeightAt;
169
+ offsets = new Array(items.length + 1);
161
170
  offsets[0] = 0;
162
171
  if (measuring) indexByKey.clear();
163
- for (let i = 0; i < total; i++) {
164
- offsets[i + 1] = offsets[i] + fn(i);
165
- if (measuring) indexByKey.set(key(items[i]), i);
172
+ for (let index = 0; index < items.length; index++) {
173
+ offsets[index + 1] = offsets[index] + heightAt(index);
174
+ if (measuring) indexByKey.set(key(items[index]), index);
166
175
  }
167
176
  if (measuring) {
168
- for (const k of measured.keys()) if (!indexByKey.has(k)) measured.delete(k);
177
+ for (const rowKey of measured.keys()) {
178
+ if (!indexByKey.has(rowKey)) measured.delete(rowKey);
179
+ }
169
180
  }
170
181
  };
171
182
  const findStart = (target, total) => {
172
- let lo = 0;
173
- let hi = total;
174
- while (lo < hi) {
175
- const mid = lo + hi + 1 >> 1;
176
- if (offsets[mid] <= target) lo = mid;
177
- else hi = mid - 1;
183
+ let low = 0;
184
+ let high = total;
185
+ while (low < high) {
186
+ const middle = low + high + 1 >> 1;
187
+ if (offsets[middle] <= target) low = middle;
188
+ else high = middle - 1;
178
189
  }
179
- return lo;
190
+ return low;
180
191
  };
181
192
  const findEnd = (target, total) => {
182
- let lo = 0;
183
- let hi = total;
184
- while (lo < hi) {
185
- const mid = lo + hi >> 1;
186
- if (offsets[mid] >= target) hi = mid;
187
- else lo = mid + 1;
193
+ let low = 0;
194
+ let high = total;
195
+ while (low < high) {
196
+ const middle = low + high >> 1;
197
+ if (offsets[middle] >= target) high = middle;
198
+ else low = middle + 1;
188
199
  }
189
- return lo;
200
+ return low;
190
201
  };
191
- const sizeVisibleRows = (start) => {
202
+ const sizeRows = (start2) => {
192
203
  if (measuring) return;
193
- for (let j = 0; j < order.length; j++) {
194
- const abs = start + j;
195
- const h = fixedHeight !== null ? fixedHeight : offsets[abs + 1] - offsets[abs];
196
- order[j].el.style.height = `${h}px`;
204
+ for (let index = 0; index < rows.order.length; index++) {
205
+ const absoluteIndex = start2 + index;
206
+ const height = fixedHeight ?? offsets[absoluteIndex + 1] - offsets[absoluteIndex];
207
+ rows.order[index].el.style.height = `${height}px`;
197
208
  }
198
209
  };
199
- const renderSubscribers = /* @__PURE__ */ new Set();
200
- const renderWindow = () => {
201
- if (virtualize === void 0) {
202
- if (granularEligible) {
203
- const patches = patchSource._consumePatches();
204
- if (!firstRender && patches.length > 0 && !patches.some((p) => p.type === "replace")) {
205
- applyPatches(patches);
206
- return;
207
- }
208
- }
209
- syncRows(items);
210
- firstRender = false;
211
- return;
212
- }
210
+ const renderCurrent = () => {
213
211
  if (contentVisibility) {
214
- syncRows(items);
215
- for (let j = 0; j < order.length; j++) {
216
- const el = order[j].el;
212
+ rows.sync(items);
213
+ for (let index = 0; index < rows.order.length; index++) {
214
+ const el = rows.order[index].el;
217
215
  el.style.contentVisibility = "auto";
218
- el.style.containIntrinsicSize = `0 ${intrinsicSizeAt(j)}px`;
216
+ el.style.containIntrinsicSize = `0 ${intrinsicSizeAt(index)}px`;
219
217
  }
220
218
  return;
221
219
  }
222
220
  const total = items.length;
223
- let start;
221
+ let start2;
224
222
  let end;
225
223
  let padTop;
226
224
  let padBottom;
@@ -229,40 +227,41 @@ function bindList(parent, source, options) {
229
227
  rebuildOffsets();
230
228
  heightsDirty = false;
231
229
  }
232
- start = 0;
230
+ start2 = 0;
233
231
  end = total;
234
232
  padTop = 0;
235
233
  padBottom = 0;
234
+ } else if (fixedHeight !== null) {
235
+ const viewportBottom = parent.scrollTop + parent.clientHeight;
236
+ start2 = Math.max(
237
+ 0,
238
+ Math.floor(parent.scrollTop / fixedHeight) - overscan
239
+ );
240
+ end = Math.min(total, Math.ceil(viewportBottom / fixedHeight) + overscan);
241
+ padTop = start2 * fixedHeight;
242
+ padBottom = Math.max(0, total - end) * fixedHeight;
236
243
  } else {
237
- const scrollTop = parent.scrollTop;
238
- const viewportBottom = scrollTop + parent.clientHeight;
239
- if (fixedHeight !== null) {
240
- start = Math.max(0, Math.floor(scrollTop / fixedHeight) - overscan);
241
- end = Math.min(total, Math.ceil(viewportBottom / fixedHeight) + overscan);
242
- padTop = start * fixedHeight;
243
- padBottom = Math.max(0, total - end) * fixedHeight;
244
- } else {
245
- if (heightsDirty) {
246
- rebuildOffsets();
247
- heightsDirty = false;
248
- }
249
- start = Math.max(0, findStart(scrollTop, total) - overscan);
250
- end = Math.min(total, findEnd(viewportBottom, total) + overscan);
251
- padTop = offsets[start];
252
- padBottom = offsets[total] - offsets[end];
244
+ if (heightsDirty) {
245
+ rebuildOffsets();
246
+ heightsDirty = false;
253
247
  }
248
+ const viewportBottom = parent.scrollTop + parent.clientHeight;
249
+ start2 = Math.max(0, findStart(parent.scrollTop, total) - overscan);
250
+ end = Math.min(total, findEnd(viewportBottom, total) + overscan);
251
+ padTop = offsets[start2];
252
+ padBottom = offsets[total] - offsets[end];
254
253
  }
255
- syncRows(items.slice(start, end));
256
- sizeVisibleRows(start);
254
+ rows.sync(items.slice(start2, end));
255
+ sizeRows(start2);
257
256
  container.style.paddingTop = `${padTop}px`;
258
257
  container.style.paddingBottom = `${padBottom}px`;
259
- for (const cb of renderSubscribers) cb();
258
+ for (const callback of renderSubscribers) callback();
260
259
  };
261
- const stopEffect = effect(() => {
262
- items = source.value;
260
+ const render = (nextItems) => {
261
+ items = nextItems;
263
262
  heightsDirty = true;
264
- renderWindow();
265
- });
263
+ renderCurrent();
264
+ };
266
265
  const scheduleRender = () => {
267
266
  if (rafPending) return;
268
267
  rafPending = true;
@@ -273,51 +272,143 @@ function bindList(parent, source, options) {
273
272
  parent.scrollTop += pendingAnchorDelta;
274
273
  pendingAnchorDelta = 0;
275
274
  }
276
- renderWindow();
275
+ renderCurrent();
277
276
  });
278
277
  };
279
- if (virtualize !== void 0 && !contentVisibility) parent.addEventListener("scroll", scheduleRender);
280
- const RO = globalThis.ResizeObserver;
281
- const parentResize = virtualize !== void 0 && !contentVisibility && RO !== void 0 ? new RO(scheduleRender) : void 0;
282
- parentResize?.observe(parent);
283
- const setHeight = (k, height) => {
278
+ const resizeObserver = !contentVisibility && globalThis.ResizeObserver !== void 0 ? new globalThis.ResizeObserver(scheduleRender) : void 0;
279
+ const start = () => {
280
+ if (contentVisibility) return;
281
+ parent.addEventListener("scroll", scheduleRender);
282
+ resizeObserver?.observe(parent);
283
+ };
284
+ const setHeight = (rowKey, height) => {
284
285
  if (!measuring || contentVisibility) return;
285
- const idx = indexByKey.get(k);
286
- if (idx === void 0) return;
287
- const oldHeight = measured.has(k) ? measured.get(k) : estimateAt(idx);
286
+ const index = indexByKey.get(rowKey);
287
+ if (index === void 0) return;
288
+ const oldHeight = measured.get(rowKey) ?? estimateAt(index);
288
289
  if (height === oldHeight) return;
289
- measured.set(k, height);
290
- if (offsets[idx + 1] <= parent.scrollTop) pendingAnchorDelta += height - oldHeight;
290
+ measured.set(rowKey, height);
291
+ if (offsets[index + 1] <= parent.scrollTop)
292
+ pendingAnchorDelta += height - oldHeight;
291
293
  heightsDirty = true;
292
294
  scheduleRender();
293
295
  };
294
- const dispose = (() => {
296
+ const onRender = (callback) => {
297
+ renderSubscribers.add(callback);
298
+ return () => renderSubscribers.delete(callback);
299
+ };
300
+ const dispose = () => {
295
301
  disposed = true;
296
- stopEffect();
297
- for (const row of rows.values()) {
298
- row.dispose();
299
- if (virtualize === void 0) row.el.remove();
300
- }
301
- rows.clear();
302
+ parent.removeEventListener("scroll", scheduleRender);
303
+ resizeObserver?.disconnect();
302
304
  renderSubscribers.clear();
303
- if (virtualize !== void 0) {
304
- parent.removeEventListener("scroll", scheduleRender);
305
- parentResize?.disconnect();
305
+ };
306
+ return { contentVisibility, render, start, setHeight, onRender, dispose };
307
+ }
308
+
309
+ // src/list.ts
310
+ var NOOP2 = () => {
311
+ };
312
+ function assertUniqueListKeys(items, key) {
313
+ const firstIndex = /* @__PURE__ */ new Map();
314
+ for (let index = 0; index < items.length; index++) {
315
+ const rowKey = key(items[index]);
316
+ const first = firstIndex.get(rowKey);
317
+ if (first !== void 0) {
318
+ const displayed = typeof rowKey === "string" ? JSON.stringify(rowKey) : String(rowKey);
319
+ throw new Error(
320
+ `bindList: duplicate key ${displayed} at indices ${first} and ${index} \u2014 every row key must be unique.`
321
+ );
322
+ }
323
+ firstIndex.set(rowKey, index);
324
+ }
325
+ }
326
+ function bindList(parent, source, options) {
327
+ const { key, render, tag = "div", virtualize, before } = options;
328
+ let firstRender = true;
329
+ let forceSnapshot = false;
330
+ const patchSource = source;
331
+ const granularEligible = virtualize === void 0 && patchSource[ARRAY_SIGNAL_BRAND] === true;
332
+ const container = virtualize === void 0 ? parent : document.createElement("div");
333
+ if (virtualize !== void 0) {
334
+ if (virtualize.containerClass !== void 0)
335
+ container.className = virtualize.containerClass;
336
+ if (virtualize.containerId !== void 0)
337
+ container.id = virtualize.containerId;
338
+ }
339
+ const endAnchor = () => {
340
+ if (virtualize !== void 0 || before === void 0) return null;
341
+ return (typeof before === "function" ? before() : before) ?? null;
342
+ };
343
+ const rows = createListRowController({
344
+ container,
345
+ key,
346
+ render,
347
+ tag,
348
+ endAnchor
349
+ });
350
+ const virtualization = virtualize === void 0 ? void 0 : createListVirtualizationController({
351
+ parent,
352
+ container,
353
+ virtualize,
354
+ key,
355
+ rows
356
+ });
357
+ const renderItems = (items) => {
358
+ if (virtualization !== void 0) {
359
+ virtualization.render(items);
360
+ return;
361
+ }
362
+ if (granularEligible) {
363
+ const patches = patchSource._consumePatches();
364
+ if (!firstRender && !forceSnapshot && patches.length > 0 && !patches.some((patch) => patch.type === "replace")) {
365
+ try {
366
+ rows.applyPatches(patches);
367
+ } catch (error) {
368
+ forceSnapshot = true;
369
+ throw error;
370
+ }
371
+ return;
372
+ }
373
+ }
374
+ rows.sync(items);
375
+ firstRender = false;
376
+ forceSnapshot = false;
377
+ };
378
+ const stopEffect = effect(() => {
379
+ const items = source.value;
380
+ try {
381
+ assertUniqueListKeys(items, key);
382
+ } catch (error) {
383
+ if (granularEligible) {
384
+ patchSource._consumePatches();
385
+ forceSnapshot = true;
386
+ }
387
+ throw error;
388
+ }
389
+ renderItems(items);
390
+ });
391
+ if (virtualization !== void 0) {
392
+ parent.appendChild(container);
393
+ virtualization.start();
394
+ }
395
+ const dispose = (() => {
396
+ stopEffect();
397
+ rows.dispose(virtualization === void 0);
398
+ if (virtualization !== void 0) {
399
+ virtualization.dispose();
306
400
  container.remove();
307
401
  VIRTUAL_INTERNALS.delete(handle);
308
402
  }
309
403
  });
310
404
  const handle = dispose;
311
- handle.setHeight = setHeight;
312
- if (virtualize !== void 0) {
405
+ handle.setHeight = virtualization?.setHeight ?? NOOP2;
406
+ if (virtualization !== void 0) {
313
407
  handle.container = container;
314
- if (!contentVisibility) {
408
+ if (!virtualization.contentVisibility) {
315
409
  VIRTUAL_INTERNALS.set(handle, {
316
- visibleRows: () => order.map((row) => ({ key: key(row.item), el: row.el })),
317
- onRender: (cb) => {
318
- renderSubscribers.add(cb);
319
- return () => renderSubscribers.delete(cb);
320
- }
410
+ visibleRows: () => rows.order.map((row) => ({ key: key(row.item), el: row.el })),
411
+ onRender: virtualization.onRender
321
412
  });
322
413
  }
323
414
  }
@@ -327,13 +418,13 @@ var VIRTUAL_INTERNALS = /* @__PURE__ */ new WeakMap();
327
418
  function observeRowHeights(handle) {
328
419
  const internals = VIRTUAL_INTERNALS.get(handle);
329
420
  const RO = globalThis.ResizeObserver;
330
- if (internals === void 0 || RO === void 0) return () => {
331
- };
421
+ if (internals === void 0 || RO === void 0) return NOOP2;
332
422
  const keyByEl = /* @__PURE__ */ new WeakMap();
333
423
  const observer = new RO((entries) => {
334
424
  for (const entry of entries) {
335
425
  const k = keyByEl.get(entry.target);
336
- if (k !== void 0) handle.setHeight(k, entry.target.offsetHeight);
426
+ if (k !== void 0)
427
+ handle.setHeight(k, entry.target.offsetHeight);
337
428
  }
338
429
  });
339
430
  const resync = () => {
@@ -352,5 +443,5 @@ function observeRowHeights(handle) {
352
443
  }
353
444
 
354
445
  export { bindList, observeRowHeights };
355
- //# sourceMappingURL=list.js.map
446
+
356
447
  //# sourceMappingURL=list.js.map