kerfjs 4.4.0 → 5.0.0-beta.3

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