kerfjs 5.0.0-beta.9 → 5.0.0-beta.91
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/CHANGELOG.md +2592 -107
- package/README.md +96 -37
- package/ai/cursorrules +110 -61
- package/ai/manifest.json +15 -7
- package/ai/skill.md +117 -72
- package/dist/actions.d.ts +1 -1
- package/dist/actions.js +10 -6
- package/dist/actions.js.map +1 -1
- package/dist/array-signal.js +1 -1
- package/dist/async.js +22 -19
- package/dist/async.js.map +1 -1
- package/dist/attach.js +1 -66
- package/dist/attach.js.map +1 -1
- package/dist/{chunk-HW7KSM2Y.js → chunk-3C6F6NM4.js} +1 -1
- package/dist/{chunk-HW7KSM2Y.js.map → chunk-3C6F6NM4.js.map} +1 -1
- package/dist/{chunk-GY4XV2UV.js → chunk-4TX6YJDM.js} +10 -3
- package/dist/chunk-4TX6YJDM.js.map +1 -0
- package/dist/{chunk-QIP723L4.js → chunk-5WRGJZV6.js} +3 -2
- package/dist/chunk-5WRGJZV6.js.map +1 -0
- package/dist/{chunk-KZJXHFIB.js → chunk-BI4HGICV.js} +3 -3
- package/dist/chunk-BI4HGICV.js.map +1 -0
- package/dist/chunk-FM6N2B4N.js +70 -0
- package/dist/chunk-FM6N2B4N.js.map +1 -0
- package/dist/{chunk-LVH3GC6B.js → chunk-H7WTRM6F.js} +3 -3
- package/dist/chunk-H7WTRM6F.js.map +1 -0
- package/dist/{chunk-U6FK33SG.js → chunk-IHMDBBUJ.js} +2 -2
- package/dist/chunk-IHMDBBUJ.js.map +1 -0
- package/dist/chunk-JN7EJM3B.js +86 -0
- package/dist/chunk-JN7EJM3B.js.map +1 -0
- package/dist/{chunk-UZJ6I4T6.js → chunk-MK42GLPV.js} +1 -1
- package/dist/chunk-MK42GLPV.js.map +1 -0
- package/dist/{chunk-ZDCJZCNO.js → chunk-TFLNYFDO.js} +88 -48
- package/dist/chunk-TFLNYFDO.js.map +1 -0
- package/dist/{chunk-V46JKE44.js → chunk-WKIPLNVO.js} +1 -1
- package/dist/chunk-WKIPLNVO.js.map +1 -0
- package/dist/{chunk-SVATPF5R.js → chunk-XHHGPZRQ.js} +261 -165
- package/dist/chunk-XHHGPZRQ.js.map +1 -0
- package/dist/{delegate-CL9VTZFb.d.ts → delegate--CrfgYtC.d.ts} +0 -34
- package/dist/dev.d.ts +31 -13
- package/dist/dev.js +101 -35
- package/dist/dev.js.map +1 -1
- package/dist/html.js +5 -4
- package/dist/html.js.map +1 -1
- package/dist/index.d.ts +14 -9
- package/dist/index.js +29 -20
- package/dist/index.js.map +1 -1
- package/dist/jsx-runtime.d.ts +13 -5
- package/dist/jsx-runtime.js +1 -1
- package/dist/list.d.ts +7 -3
- package/dist/list.js +153 -46
- package/dist/list.js.map +1 -1
- package/dist/overlay.d.ts +128 -129
- package/dist/overlay.js +675 -366
- package/dist/overlay.js.map +1 -1
- package/dist/remount.js +2 -2
- package/dist/router.d.ts +13 -4
- package/dist/router.js +37 -10
- package/dist/router.js.map +1 -1
- package/dist/scope.d.ts +35 -8
- package/dist/scope.js +61 -30
- package/dist/scope.js.map +1 -1
- package/dist/testing.js +1 -1
- package/dist/timing.d.ts +3 -2
- package/dist/timing.js +1 -1
- package/dist/timing.js.map +1 -1
- package/llms.txt +13 -6
- package/package.json +33 -8
- package/setup/cli.mjs +90 -0
- package/setup/index.d.mts +37 -0
- package/setup/index.mjs +1347 -0
- package/setup/jsonc.mjs +201 -0
- package/setup/state.schema.json +56 -0
- package/dist/chunk-GY4XV2UV.js.map +0 -1
- package/dist/chunk-KPXIOG2C.js +0 -54
- package/dist/chunk-KPXIOG2C.js.map +0 -1
- package/dist/chunk-KZJXHFIB.js.map +0 -1
- package/dist/chunk-LVH3GC6B.js.map +0 -1
- package/dist/chunk-QIP723L4.js.map +0 -1
- package/dist/chunk-SVATPF5R.js.map +0 -1
- package/dist/chunk-U6FK33SG.js.map +0 -1
- package/dist/chunk-UZJ6I4T6.js.map +0 -1
- package/dist/chunk-V46JKE44.js.map +0 -1
- package/dist/chunk-ZDCJZCNO.js.map +0 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { itemVersion } from './chunk-
|
|
2
|
-
import { parseRowTemplate, rowContractError, parseSingleRow, collectTemplateChildren } from './chunk-
|
|
3
|
-
import { captureRowBindings, listSafeHtml, boundTextNodeOf, syncFormProp, newBindingContext,
|
|
4
|
-
import { effect } from './chunk-
|
|
5
|
-
import { LIST_MARKER_PREFIX, flattenWithoutListItems, collectLists, flatten } from './chunk-
|
|
6
|
-
import { devHooks } from './chunk-
|
|
1
|
+
import { itemVersion } from './chunk-5WRGJZV6.js';
|
|
2
|
+
import { parseRowTemplate, rowContractError, parseSingleRow, collectTemplateChildren } from './chunk-WKIPLNVO.js';
|
|
3
|
+
import { captureRowBindings, listSafeHtml, boundTextNodeOf, syncFormProp, newBindingContext, wireBindings, disposeRowBindings, isSafeHtml, _setBindingContext, TEXT_MARKER_PREFIX, ROW_TEXT_PREFIX, wireRowBindings, carryOrRewireRowBindings, granularListSafeHtml } from './chunk-TFLNYFDO.js';
|
|
4
|
+
import { effect } from './chunk-IHMDBBUJ.js';
|
|
5
|
+
import { LIST_MARKER_PREFIX, flattenWithoutListItems, collectLists, isOverlayHost, flatten } from './chunk-4TX6YJDM.js';
|
|
6
|
+
import { devHooks } from './chunk-3C6F6NM4.js';
|
|
7
7
|
|
|
8
8
|
// src/list-render-state.ts
|
|
9
9
|
function deriveListRenderState(bindingCount) {
|
|
@@ -18,7 +18,8 @@ function decideListPath(state, patches, snapshotLength, previousBindingCount) {
|
|
|
18
18
|
for (const p of patches) {
|
|
19
19
|
if (p.type === "insert") netDelta += 1;
|
|
20
20
|
else if (p.type === "remove") netDelta -= 1;
|
|
21
|
-
else if (p.type === "replace")
|
|
21
|
+
else if (p.type === "replace")
|
|
22
|
+
return { path: "snapshot", reason: "replace" };
|
|
22
23
|
}
|
|
23
24
|
const count = previousBindingCount ?? 0;
|
|
24
25
|
if (count + netDelta !== snapshotLength) {
|
|
@@ -110,11 +111,18 @@ function _hasGranularIndexShift(previousBindingCount, patches) {
|
|
|
110
111
|
const rendered = [];
|
|
111
112
|
for (let i = 0; i < previousBindingCount; i++) rendered.push(i);
|
|
112
113
|
for (const patch of patches) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
114
|
+
switch (patch.type) {
|
|
115
|
+
case "insert":
|
|
116
|
+
rendered.splice(patch.index, 0, patch.index);
|
|
117
|
+
break;
|
|
118
|
+
case "remove":
|
|
119
|
+
rendered.splice(patch.index, 1);
|
|
120
|
+
break;
|
|
121
|
+
case "move": {
|
|
122
|
+
const [moved] = rendered.splice(patch.from, 1);
|
|
123
|
+
rendered.splice(patch.to, 0, moved);
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
118
126
|
}
|
|
119
127
|
}
|
|
120
128
|
return rendered.some((renderedAt, index) => renderedAt !== index);
|
|
@@ -153,10 +161,12 @@ function eachGranular(sig, render, cacheKey, listKey) {
|
|
|
153
161
|
return eachSnapshotById(snapshot, render, cacheKey, id, sig);
|
|
154
162
|
}
|
|
155
163
|
}
|
|
156
|
-
const renderRow = (item, index) => captureRowBindings(
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
164
|
+
const renderRow = (item, index) => captureRowBindings(
|
|
165
|
+
() => inRowScope(() => {
|
|
166
|
+
const out = render(item, index);
|
|
167
|
+
return isSafeHtml(out) ? out.toString() : out;
|
|
168
|
+
})
|
|
169
|
+
);
|
|
160
170
|
const internalPatches = new Array(patches.length);
|
|
161
171
|
const cache = ctx.caches.get(id);
|
|
162
172
|
try {
|
|
@@ -223,13 +233,16 @@ function eachSnapshotById(items, render, cacheKey, id, source) {
|
|
|
223
233
|
devHooks.staleIndex?.(id);
|
|
224
234
|
}
|
|
225
235
|
} else {
|
|
226
|
-
const captured = captureRowBindings(
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
236
|
+
const captured = captureRowBindings(
|
|
237
|
+
() => inRowScope(() => {
|
|
238
|
+
const out = render(item, i);
|
|
239
|
+
return isSafeHtml(out) ? out.toString() : out;
|
|
240
|
+
})
|
|
241
|
+
);
|
|
230
242
|
html = captured.html;
|
|
231
243
|
bindings = captured.bindings;
|
|
232
|
-
if (cache !== null)
|
|
244
|
+
if (cache !== null)
|
|
245
|
+
cache.set(item, { cacheKey: k, html, bindings, version, index: i });
|
|
233
246
|
}
|
|
234
247
|
segItems[i] = { ref: item, cacheKey: k, html, bindings };
|
|
235
248
|
}
|
|
@@ -281,16 +294,19 @@ function restoreFocus(snap) {
|
|
|
281
294
|
if (domSelection === null) return;
|
|
282
295
|
const [anchorNode, anchorOffset, focusNode, focusOffset] = domSelection;
|
|
283
296
|
try {
|
|
284
|
-
document.getSelection()?.setBaseAndExtent(
|
|
285
|
-
anchorNode,
|
|
286
|
-
anchorOffset,
|
|
287
|
-
focusNode,
|
|
288
|
-
focusOffset
|
|
289
|
-
);
|
|
297
|
+
document.getSelection()?.setBaseAndExtent(anchorNode, anchorOffset, focusNode, focusOffset);
|
|
290
298
|
} catch {
|
|
291
299
|
}
|
|
292
300
|
}
|
|
293
301
|
|
|
302
|
+
// src/utils/isUserAgentOwnedAttr.ts
|
|
303
|
+
function isUserAgentOwnedAttr(tagNameUpper, name) {
|
|
304
|
+
if (tagNameUpper.includes("-")) {
|
|
305
|
+
return name === "open" || name === "role" || name.startsWith("aria-");
|
|
306
|
+
}
|
|
307
|
+
return name === "open" && (tagNameUpper === "DETAILS" || tagNameUpper === "DIALOG");
|
|
308
|
+
}
|
|
309
|
+
|
|
294
310
|
// src/utils/moveNode.ts
|
|
295
311
|
function moveNode(parent, node, ref) {
|
|
296
312
|
const move = parent.moveBefore;
|
|
@@ -343,7 +359,11 @@ function protectionTag(node) {
|
|
|
343
359
|
const { dataset } = node;
|
|
344
360
|
return (dataset.morphSkip !== void 0 ? "s" : "") + (dataset.morphSkipChildren !== void 0 ? "c" : "") + (dataset.morphPreserve !== void 0 ? "p" : "");
|
|
345
361
|
}
|
|
346
|
-
var MARKER_PREFIXES = [
|
|
362
|
+
var MARKER_PREFIXES = [
|
|
363
|
+
LIST_MARKER_PREFIX,
|
|
364
|
+
TEXT_MARKER_PREFIX,
|
|
365
|
+
ROW_TEXT_PREFIX
|
|
366
|
+
];
|
|
347
367
|
function isMarker(node) {
|
|
348
368
|
if (node.nodeType !== COMMENT_NODE) return false;
|
|
349
369
|
const { data } = node;
|
|
@@ -421,7 +441,8 @@ function morphChildren(fromParent, toParent, ownedItems) {
|
|
|
421
441
|
if (matched === null && fromChild !== null && toChild.nodeType === COMMENT_NODE && toChild.data.startsWith(LIST_MARKER_PREFIX)) {
|
|
422
442
|
const wantData = toChild.data;
|
|
423
443
|
for (let scan = fromChild.nextSibling; scan !== null; scan = scan.nextSibling) {
|
|
424
|
-
if (scan.nodeType !== COMMENT_NODE || scan.data !== wantData)
|
|
444
|
+
if (scan.nodeType !== COMMENT_NODE || scan.data !== wantData)
|
|
445
|
+
continue;
|
|
425
446
|
const regionEnd = afterListRegion(scan, ownedItems);
|
|
426
447
|
const run = [];
|
|
427
448
|
for (let r = scan; r !== null && r !== regionEnd; r = r.nextSibling) {
|
|
@@ -487,9 +508,6 @@ function morphElement(fromEl, toEl, ownedItems) {
|
|
|
487
508
|
fromEl.value = toEl.textContent;
|
|
488
509
|
}
|
|
489
510
|
}
|
|
490
|
-
function isUserAgentOwnedAttr(tagName, name) {
|
|
491
|
-
return name === "open" && (tagName === "DETAILS" || tagName === "DIALOG");
|
|
492
|
-
}
|
|
493
511
|
function morphAttributes(fromEl, toEl) {
|
|
494
512
|
const toAttrs = toEl.attributes;
|
|
495
513
|
for (let i = 0; i < toAttrs.length; i++) {
|
|
@@ -534,7 +552,10 @@ function preserveTextEntryState(fromEl, toEl) {
|
|
|
534
552
|
const toInput = toEl;
|
|
535
553
|
toInput.value = fromInput.value;
|
|
536
554
|
try {
|
|
537
|
-
toInput.setSelectionRange(
|
|
555
|
+
toInput.setSelectionRange(
|
|
556
|
+
fromInput.selectionStart,
|
|
557
|
+
fromInput.selectionEnd
|
|
558
|
+
);
|
|
538
559
|
} catch {
|
|
539
560
|
}
|
|
540
561
|
}
|
|
@@ -567,7 +588,8 @@ function tryAttributeOnlyFastPath(liveNode, oldHtml, newHtml) {
|
|
|
567
588
|
if (oldGt === -1 || newGt === -1) return false;
|
|
568
589
|
if (oldHtml.length - oldGt !== newHtml.length - newGt) return false;
|
|
569
590
|
if (oldHtml.slice(oldGt) !== newHtml.slice(newGt)) return false;
|
|
570
|
-
if (containsDataMorphSkip(oldHtml) || containsDataMorphSkip(newHtml))
|
|
591
|
+
if (containsDataMorphSkip(oldHtml) || containsDataMorphSkip(newHtml))
|
|
592
|
+
return false;
|
|
571
593
|
const oldTag = parseOpeningTag(oldHtml, oldGt);
|
|
572
594
|
const newTag = parseOpeningTag(newHtml, newGt);
|
|
573
595
|
if (oldTag === null || newTag === null) return false;
|
|
@@ -588,14 +610,15 @@ function tryAttributeOnlyFastPath(liveNode, oldHtml, newHtml) {
|
|
|
588
610
|
}
|
|
589
611
|
for (const name of oldTag.attrs.keys()) {
|
|
590
612
|
if (newTag.attrs.has(name)) continue;
|
|
591
|
-
if (
|
|
613
|
+
if (isUserAgentOwnedAttr(liveTagUpper, name)) continue;
|
|
592
614
|
liveNode.removeAttribute(name);
|
|
593
615
|
syncFormProp(liveNode, name, "", false);
|
|
594
616
|
}
|
|
595
617
|
return true;
|
|
596
618
|
}
|
|
597
619
|
function tryTextContentFastPath(liveNode, oldHtml, newHtml) {
|
|
598
|
-
if (containsDataMorphSkip(oldHtml) || containsDataMorphSkip(newHtml))
|
|
620
|
+
if (containsDataMorphSkip(oldHtml) || containsDataMorphSkip(newHtml))
|
|
621
|
+
return false;
|
|
599
622
|
let p = 0;
|
|
600
623
|
const minLen = Math.min(oldHtml.length, newHtml.length);
|
|
601
624
|
while (p < minLen && oldHtml.charCodeAt(p) === newHtml.charCodeAt(p)) p++;
|
|
@@ -610,7 +633,8 @@ function tryTextContentFastPath(liveNode, oldHtml, newHtml) {
|
|
|
610
633
|
if (!isPureTextWindow(newHtml, p, newWinEnd)) return false;
|
|
611
634
|
if (p === 0) return false;
|
|
612
635
|
const boundaryCc = oldHtml.charCodeAt(p - 1);
|
|
613
|
-
if (boundaryCc === LT || boundaryCc === DQUOTE || boundaryCc === SQUOTE || boundaryCc === EQ || boundaryCc === AMP)
|
|
636
|
+
if (boundaryCc === LT || boundaryCc === DQUOTE || boundaryCc === SQUOTE || boundaryCc === EQ || boundaryCc === AMP)
|
|
637
|
+
return false;
|
|
614
638
|
const textStart = lastIndexOfChar(oldHtml, GT, p - 1);
|
|
615
639
|
if (textStart === -1) return false;
|
|
616
640
|
const textEnd = oldHtml.indexOf("<", p);
|
|
@@ -637,7 +661,8 @@ function containsDataMorphSkip(html) {
|
|
|
637
661
|
function isPureTextWindow(html, start, end) {
|
|
638
662
|
for (let i = start; i < end; i++) {
|
|
639
663
|
const cc = html.charCodeAt(i);
|
|
640
|
-
if (cc === LT || cc === GT || cc === DQUOTE || cc === SQUOTE || cc === AMP || cc === EQ)
|
|
664
|
+
if (cc === LT || cc === GT || cc === DQUOTE || cc === SQUOTE || cc === AMP || cc === EQ)
|
|
665
|
+
return false;
|
|
641
666
|
}
|
|
642
667
|
return true;
|
|
643
668
|
}
|
|
@@ -730,9 +755,6 @@ function unescapeAttrValue(s) {
|
|
|
730
755
|
if (s.indexOf("&") === -1) return s;
|
|
731
756
|
return s.replace(/"/g, '"').replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&");
|
|
732
757
|
}
|
|
733
|
-
function isUserAgentOwnedAttr2(tagNameUpper, name) {
|
|
734
|
-
return name === "open" && (tagNameUpper === "DETAILS" || tagNameUpper === "DIALOG");
|
|
735
|
-
}
|
|
736
758
|
|
|
737
759
|
// src/list-reconcile-granular.ts
|
|
738
760
|
function reconcileGranular(binding, patches) {
|
|
@@ -769,7 +791,14 @@ function reconcileGranular(binding, patches) {
|
|
|
769
791
|
if (runLen === 1) {
|
|
770
792
|
applySingleInsert(liveParent, items, patch, endAnchor(binding));
|
|
771
793
|
} else {
|
|
772
|
-
applyBulkInsert(
|
|
794
|
+
applyBulkInsert(
|
|
795
|
+
liveParent,
|
|
796
|
+
items,
|
|
797
|
+
patches,
|
|
798
|
+
i,
|
|
799
|
+
runEnd,
|
|
800
|
+
endAnchor(binding)
|
|
801
|
+
);
|
|
773
802
|
}
|
|
774
803
|
i = runEnd;
|
|
775
804
|
continue;
|
|
@@ -881,7 +910,10 @@ function applyBulkUpdate(liveParent, items, patches, start, end) {
|
|
|
881
910
|
morphChanges.push({ patchIdx: k, html: p.html });
|
|
882
911
|
}
|
|
883
912
|
if (morphChanges.length === 0) return;
|
|
884
|
-
const { content, count } = parseRowTemplate(
|
|
913
|
+
const { content, count } = parseRowTemplate(
|
|
914
|
+
morphChanges.map((c) => c.html).join(""),
|
|
915
|
+
liveParent
|
|
916
|
+
);
|
|
885
917
|
if (count !== morphChanges.length) {
|
|
886
918
|
throw findOffendingChange(patches, morphChanges, liveParent);
|
|
887
919
|
}
|
|
@@ -924,18 +956,30 @@ function applyBulkInsert(liveParent, items, patches, start, end, tailAnchor) {
|
|
|
924
956
|
function findOffendingInsert(patches, start, htmls, liveParent) {
|
|
925
957
|
for (let i = 0; i < htmls.length; i++) {
|
|
926
958
|
if (parseRowTemplate(htmls[i], liveParent).count !== 1) {
|
|
927
|
-
return rowContractError(
|
|
959
|
+
return rowContractError(
|
|
960
|
+
patches[start + i].index,
|
|
961
|
+
htmls[i],
|
|
962
|
+
liveParent
|
|
963
|
+
);
|
|
928
964
|
}
|
|
929
965
|
}
|
|
930
|
-
return new Error(
|
|
966
|
+
return new Error(
|
|
967
|
+
"each(): bulk-insert mismatch with no per-row offender (kerf bug)."
|
|
968
|
+
);
|
|
931
969
|
}
|
|
932
970
|
function findOffendingChange(patches, changes, liveParent) {
|
|
933
971
|
for (const c of changes) {
|
|
934
972
|
if (parseRowTemplate(c.html, liveParent).count !== 1) {
|
|
935
|
-
return rowContractError(
|
|
973
|
+
return rowContractError(
|
|
974
|
+
patches[c.patchIdx].index,
|
|
975
|
+
c.html,
|
|
976
|
+
liveParent
|
|
977
|
+
);
|
|
936
978
|
}
|
|
937
979
|
}
|
|
938
|
-
return new Error(
|
|
980
|
+
return new Error(
|
|
981
|
+
"each(): bulk-update mismatch with no per-row offender (kerf bug)."
|
|
982
|
+
);
|
|
939
983
|
}
|
|
940
984
|
|
|
941
985
|
// src/list-reconcile-inplace.ts
|
|
@@ -960,7 +1004,12 @@ function tryInPlaceContentUpdate(binding, listSeg) {
|
|
|
960
1004
|
}
|
|
961
1005
|
function updateRowInPlace(liveParent, old, ni, index) {
|
|
962
1006
|
if (old.html === ni.html || tryAttributeOnlyFastPath(old.node, old.html, ni.html) || tryTextContentFastPath(old.node, old.html, ni.html)) {
|
|
963
|
-
const kept = carryOrRewireRowBindings(
|
|
1007
|
+
const kept = carryOrRewireRowBindings(
|
|
1008
|
+
old.node,
|
|
1009
|
+
old.bindings,
|
|
1010
|
+
old.bindingDisposers,
|
|
1011
|
+
ni.bindings
|
|
1012
|
+
);
|
|
964
1013
|
return {
|
|
965
1014
|
ref: ni.ref,
|
|
966
1015
|
cacheKey: ni.cacheKey,
|
|
@@ -973,7 +1022,12 @@ function updateRowInPlace(liveParent, old, ni, index) {
|
|
|
973
1022
|
const newNode = parseSingleRow(ni.html, index, liveParent);
|
|
974
1023
|
if (old.node.tagName === newNode.tagName) {
|
|
975
1024
|
_morphElement(old.node, newNode);
|
|
976
|
-
const kept = carryOrRewireRowBindings(
|
|
1025
|
+
const kept = carryOrRewireRowBindings(
|
|
1026
|
+
old.node,
|
|
1027
|
+
old.bindings,
|
|
1028
|
+
old.bindingDisposers,
|
|
1029
|
+
ni.bindings
|
|
1030
|
+
);
|
|
977
1031
|
return {
|
|
978
1032
|
ref: ni.ref,
|
|
979
1033
|
cacheKey: ni.cacheKey,
|
|
@@ -985,7 +1039,12 @@ function updateRowInPlace(liveParent, old, ni, index) {
|
|
|
985
1039
|
}
|
|
986
1040
|
disposeRowBindings(old.bindingDisposers);
|
|
987
1041
|
liveParent.replaceChild(newNode, old.node);
|
|
988
|
-
const fresh = carryOrRewireRowBindings(
|
|
1042
|
+
const fresh = carryOrRewireRowBindings(
|
|
1043
|
+
newNode,
|
|
1044
|
+
void 0,
|
|
1045
|
+
void 0,
|
|
1046
|
+
ni.bindings
|
|
1047
|
+
);
|
|
989
1048
|
return {
|
|
990
1049
|
ref: ni.ref,
|
|
991
1050
|
cacheKey: ni.cacheKey,
|
|
@@ -1070,15 +1129,22 @@ function buildFreshNodes(newRecord, freshIndices, freshHtmls, liveParent) {
|
|
|
1070
1129
|
function findOffendingRow(newRecord, freshIndices, freshHtmls, liveParent) {
|
|
1071
1130
|
for (let i = 0; i < freshHtmls.length; i++) {
|
|
1072
1131
|
if (parseRowTemplate(freshHtmls[i], liveParent).count !== 1) {
|
|
1073
|
-
return rowContractError(
|
|
1132
|
+
return rowContractError(
|
|
1133
|
+
freshIndices[i],
|
|
1134
|
+
newRecord[freshIndices[i]].html,
|
|
1135
|
+
liveParent
|
|
1136
|
+
);
|
|
1074
1137
|
}
|
|
1075
1138
|
}
|
|
1076
|
-
return new Error(
|
|
1139
|
+
return new Error(
|
|
1140
|
+
"each(): bulk-parse mismatch with no per-row offender (kerf bug)."
|
|
1141
|
+
);
|
|
1077
1142
|
}
|
|
1078
1143
|
function removeOldNodes(liveParent, removedItems) {
|
|
1079
1144
|
for (const item of removedItems) {
|
|
1080
1145
|
disposeRowBindings(item.bindingDisposers);
|
|
1081
|
-
if (item.node.parentElement === liveParent)
|
|
1146
|
+
if (item.node.parentElement === liveParent)
|
|
1147
|
+
liveParent.removeChild(item.node);
|
|
1082
1148
|
}
|
|
1083
1149
|
}
|
|
1084
1150
|
function applyMoves(liveParent, newRecord, prevIdx, stable, tailAnchor) {
|
|
@@ -1130,6 +1196,111 @@ function reconcileList(binding, listSeg) {
|
|
|
1130
1196
|
reconcileSnapshot(binding, listSeg);
|
|
1131
1197
|
}
|
|
1132
1198
|
|
|
1199
|
+
// src/mount-list-bindings.ts
|
|
1200
|
+
function anyRebuiltListIsGranular(segment, rebuilt) {
|
|
1201
|
+
if (!rebuilt.size) return false;
|
|
1202
|
+
const lists = collectLists(segment);
|
|
1203
|
+
for (const id of rebuilt) {
|
|
1204
|
+
if (lists.get(id)?.patches) return true;
|
|
1205
|
+
}
|
|
1206
|
+
return false;
|
|
1207
|
+
}
|
|
1208
|
+
function bindListsFromMarkers(rootEl, segment, bindings, inlinedItems, rebuiltLists) {
|
|
1209
|
+
const lists = collectLists(segment);
|
|
1210
|
+
const found = [];
|
|
1211
|
+
collectComments(rootEl, found);
|
|
1212
|
+
for (const marker of found) {
|
|
1213
|
+
if (!marker.data.startsWith(LIST_MARKER_PREFIX)) continue;
|
|
1214
|
+
const id = marker.data.slice(LIST_MARKER_PREFIX.length);
|
|
1215
|
+
const existing = bindings.get(id);
|
|
1216
|
+
if (existing) {
|
|
1217
|
+
if (existing.marker === marker && rootEl.contains(existing.marker))
|
|
1218
|
+
continue;
|
|
1219
|
+
for (const item of existing.items) {
|
|
1220
|
+
disposeRowBindings(item.bindingDisposers);
|
|
1221
|
+
if (rootEl.contains(item.node)) {
|
|
1222
|
+
item.node.parentElement?.removeChild(item.node);
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
bindings.delete(id);
|
|
1226
|
+
rebuiltLists?.add(id);
|
|
1227
|
+
devHooks.listRebind?.(id, marker.parentElement);
|
|
1228
|
+
}
|
|
1229
|
+
const listSeg = lists.get(id);
|
|
1230
|
+
const liveParent = marker.parentElement;
|
|
1231
|
+
const items = [];
|
|
1232
|
+
if (inlinedItems) {
|
|
1233
|
+
let next = marker.nextElementSibling;
|
|
1234
|
+
for (let i = 0; i < listSeg.items.length && next !== null; i++) {
|
|
1235
|
+
validateInlinedRowMatch(listSeg.items[i].html, i, next, liveParent);
|
|
1236
|
+
items.push({
|
|
1237
|
+
ref: listSeg.items[i].ref,
|
|
1238
|
+
cacheKey: listSeg.items[i].cacheKey,
|
|
1239
|
+
html: listSeg.items[i].html,
|
|
1240
|
+
node: next,
|
|
1241
|
+
bindings: listSeg.items[i].bindings
|
|
1242
|
+
});
|
|
1243
|
+
next = next.nextElementSibling;
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
const binding = { liveParent, items, marker };
|
|
1247
|
+
if (items.length > 0) {
|
|
1248
|
+
devHooks.missingRowKey?.(items[0].node, items[0].html, binding);
|
|
1249
|
+
}
|
|
1250
|
+
devHooks.eachInMorphSkip?.(id, liveParent, rootEl);
|
|
1251
|
+
bindings.set(id, binding);
|
|
1252
|
+
for (const bound of items) {
|
|
1253
|
+
if (bound.bindings?.length) {
|
|
1254
|
+
bound.bindingDisposers = wireRowBindings(bound.node, bound.bindings);
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
function validateInlinedRowMatch(expectedHtml, index, boundEl, liveParent) {
|
|
1260
|
+
if (boundEl.outerHTML === expectedHtml) return;
|
|
1261
|
+
const { content, count } = parseRowTemplate(expectedHtml, liveParent);
|
|
1262
|
+
if (count !== 1) throw rowContractError(index, expectedHtml, liveParent);
|
|
1263
|
+
const expectedTag = content.firstElementChild.tagName;
|
|
1264
|
+
if (boundEl.tagName !== expectedTag)
|
|
1265
|
+
throw rowStructureError(index, boundEl.tagName, expectedTag);
|
|
1266
|
+
}
|
|
1267
|
+
function rowStructureError(index, gotTag, wantTag) {
|
|
1268
|
+
const got = gotTag.toLowerCase();
|
|
1269
|
+
const want = wantTag.toLowerCase();
|
|
1270
|
+
return new Error(
|
|
1271
|
+
`each(): row ${index} renders <${want}>, but the HTML parser wrapped the rows in <${got}> \u2014 so kerf cannot bind one row per element. This happens when an each() of <${want}> sits directly inside a table: the parser inserts <${got}> around the whole run. Put the each() inside an explicit <${got}> (e.g. <table><${got}>{each(...)}</${got}></table>) so the rows are the direct children kerf binds.`
|
|
1272
|
+
);
|
|
1273
|
+
}
|
|
1274
|
+
function collectOwnedItems(bindings) {
|
|
1275
|
+
const owned = /* @__PURE__ */ new Set();
|
|
1276
|
+
for (const b of bindings.values()) {
|
|
1277
|
+
for (const item of b.items) owned.add(item.node);
|
|
1278
|
+
}
|
|
1279
|
+
return owned;
|
|
1280
|
+
}
|
|
1281
|
+
function cleanupOrphanBindings(segment, bindings, renderCtx) {
|
|
1282
|
+
const liveIds = collectLists(segment);
|
|
1283
|
+
for (const [id, binding] of bindings) {
|
|
1284
|
+
if (liveIds.has(id)) continue;
|
|
1285
|
+
for (const item of binding.items) {
|
|
1286
|
+
disposeRowBindings(item.bindingDisposers);
|
|
1287
|
+
item.node.remove();
|
|
1288
|
+
}
|
|
1289
|
+
binding.marker.remove();
|
|
1290
|
+
bindings.delete(id);
|
|
1291
|
+
renderCtx.bindingCounts.delete(id);
|
|
1292
|
+
renderCtx.bindingSources.delete(id);
|
|
1293
|
+
renderCtx.caches.delete(id);
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
function collectComments(node, out) {
|
|
1297
|
+
for (let c = node.firstChild; c; c = c.nextSibling) {
|
|
1298
|
+
if (c.nodeType === Node.COMMENT_NODE) out.push(c);
|
|
1299
|
+
else if (c.nodeType === Node.ELEMENT_NODE && !isOverlayHost(c))
|
|
1300
|
+
collectComments(c, out);
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1133
1304
|
// src/mount.ts
|
|
1134
1305
|
var MOUNTED_MARKER = /* @__PURE__ */ Symbol.for("kerfjs.mounted");
|
|
1135
1306
|
var NESTED_MOUNT_MSG = "mount: rootEl is already inside (or contains) a mounted tree. kerf supports one mount per tree \u2014 compose with plain functions that return JSX instead of nesting mounts.";
|
|
@@ -1155,15 +1326,17 @@ function assertNotInsideMountedTree(rootEl) {
|
|
|
1155
1326
|
);
|
|
1156
1327
|
}
|
|
1157
1328
|
let ancestor = rootEl.parentElement;
|
|
1158
|
-
while (ancestor) {
|
|
1329
|
+
while (ancestor && !isOverlayHost(ancestor)) {
|
|
1159
1330
|
if (isMounted(ancestor)) throw new Error(NESTED_MOUNT_MSG);
|
|
1160
1331
|
ancestor = ancestor.parentElement;
|
|
1161
1332
|
}
|
|
1162
1333
|
const stack = [];
|
|
1163
|
-
for (let i = 0; i < rootEl.children.length; i++)
|
|
1334
|
+
for (let i = 0; i < rootEl.children.length; i++)
|
|
1335
|
+
stack.push(rootEl.children[i]);
|
|
1164
1336
|
while (stack.length > 0) {
|
|
1165
1337
|
const cur = stack.pop();
|
|
1166
1338
|
if (isMounted(cur)) throw new Error(NESTED_MOUNT_MSG);
|
|
1339
|
+
if (isOverlayHost(cur)) continue;
|
|
1167
1340
|
for (let i = 0; i < cur.children.length; i++) stack.push(cur.children[i]);
|
|
1168
1341
|
}
|
|
1169
1342
|
}
|
|
@@ -1241,7 +1414,8 @@ function mount(rootEl, render) {
|
|
|
1241
1414
|
valueOnlyWarnCtx
|
|
1242
1415
|
);
|
|
1243
1416
|
if (anyRebuiltListIsGranular(segment, renderCtx.rebuiltLists)) {
|
|
1244
|
-
for (const id of renderCtx.rebuiltLists)
|
|
1417
|
+
for (const id of renderCtx.rebuiltLists)
|
|
1418
|
+
renderCtx.bindingCounts.delete(id);
|
|
1245
1419
|
result = runRenderPass();
|
|
1246
1420
|
segment = resultToSegment(result);
|
|
1247
1421
|
nextStaticHtml = runSubsequentRender(
|
|
@@ -1255,7 +1429,8 @@ function mount(rootEl, render) {
|
|
|
1255
1429
|
}
|
|
1256
1430
|
if (nextStaticHtml !== prevStaticHtml) {
|
|
1257
1431
|
bindingDisposers = wireBindings(rootEl, bindingCtx, bindingDisposers);
|
|
1258
|
-
if (devHooks.staleBindingEnabled?.())
|
|
1432
|
+
if (devHooks.staleBindingEnabled?.())
|
|
1433
|
+
prevWiredBindings = bindingCtx.list;
|
|
1259
1434
|
} else {
|
|
1260
1435
|
devHooks.staleBinding?.(prevWiredBindings, bindingCtx.list);
|
|
1261
1436
|
}
|
|
@@ -1283,11 +1458,7 @@ function mount(rootEl, render) {
|
|
|
1283
1458
|
renderCtx.previousCallCount = renderCtx.counter;
|
|
1284
1459
|
devHooks.listInvariants?.(rootEl, bindings, expectedCounts || void 0);
|
|
1285
1460
|
}
|
|
1286
|
-
const
|
|
1287
|
-
reconcileAndCommitListPhase(renderStaticPhase());
|
|
1288
|
-
});
|
|
1289
|
-
return () => {
|
|
1290
|
-
disposeEffect();
|
|
1461
|
+
const releaseResources = () => {
|
|
1291
1462
|
for (const d of bindingDisposers) d();
|
|
1292
1463
|
bindingDisposers = [];
|
|
1293
1464
|
for (const b of bindings.values()) {
|
|
@@ -1296,6 +1467,21 @@ function mount(rootEl, render) {
|
|
|
1296
1467
|
listenerWarnObserver?.disconnect();
|
|
1297
1468
|
setMounted(rootEl, false);
|
|
1298
1469
|
};
|
|
1470
|
+
const priorChildren = rootEl.firstChild ? Array.from(rootEl.childNodes) : [];
|
|
1471
|
+
let disposeEffect;
|
|
1472
|
+
try {
|
|
1473
|
+
disposeEffect = effect(() => {
|
|
1474
|
+
reconcileAndCommitListPhase(renderStaticPhase());
|
|
1475
|
+
});
|
|
1476
|
+
} catch (err) {
|
|
1477
|
+
releaseResources();
|
|
1478
|
+
rootEl.replaceChildren(...priorChildren);
|
|
1479
|
+
throw err;
|
|
1480
|
+
}
|
|
1481
|
+
return () => {
|
|
1482
|
+
disposeEffect();
|
|
1483
|
+
releaseResources();
|
|
1484
|
+
};
|
|
1299
1485
|
}
|
|
1300
1486
|
function runFirstRender(rootEl, segment, bindings) {
|
|
1301
1487
|
rootEl.innerHTML = flatten(segment, true);
|
|
@@ -1307,12 +1493,22 @@ function runSubsequentRender(rootEl, segment, bindings, renderCtx, prevStaticHtm
|
|
|
1307
1493
|
if (currentStaticHtml === prevStaticHtml) {
|
|
1308
1494
|
return prevStaticHtml;
|
|
1309
1495
|
}
|
|
1310
|
-
devHooks.valueOnlyRerender?.(
|
|
1496
|
+
devHooks.valueOnlyRerender?.(
|
|
1497
|
+
prevStaticHtml,
|
|
1498
|
+
currentStaticHtml,
|
|
1499
|
+
valueOnlyWarnCtx
|
|
1500
|
+
);
|
|
1311
1501
|
cleanupOrphanBindings(segment, bindings, renderCtx);
|
|
1312
1502
|
const template = rootEl.cloneNode(false);
|
|
1313
1503
|
template.innerHTML = currentStaticHtml;
|
|
1314
1504
|
morph(rootEl, template, collectOwnedItems(bindings));
|
|
1315
|
-
bindListsFromMarkers(
|
|
1505
|
+
bindListsFromMarkers(
|
|
1506
|
+
rootEl,
|
|
1507
|
+
segment,
|
|
1508
|
+
bindings,
|
|
1509
|
+
false,
|
|
1510
|
+
renderCtx.rebuiltLists
|
|
1511
|
+
);
|
|
1316
1512
|
return currentStaticHtml;
|
|
1317
1513
|
}
|
|
1318
1514
|
function coerceRenderResult(result) {
|
|
@@ -1322,107 +1518,7 @@ function coerceRenderResult(result) {
|
|
|
1322
1518
|
function resultToSegment(result) {
|
|
1323
1519
|
return isSafeHtml(result) ? result.__segment ?? { kind: "static", html: result.__html } : { kind: "static", html: coerceRenderResult(result) };
|
|
1324
1520
|
}
|
|
1325
|
-
function anyRebuiltListIsGranular(segment, rebuilt) {
|
|
1326
|
-
if (!rebuilt.size) return false;
|
|
1327
|
-
const lists = collectLists(segment);
|
|
1328
|
-
for (const id of rebuilt) {
|
|
1329
|
-
if (lists.get(id)?.patches) return true;
|
|
1330
|
-
}
|
|
1331
|
-
return false;
|
|
1332
|
-
}
|
|
1333
|
-
function bindListsFromMarkers(rootEl, segment, bindings, inlinedItems, rebuiltLists) {
|
|
1334
|
-
const lists = collectLists(segment);
|
|
1335
|
-
const found = [];
|
|
1336
|
-
collectComments(rootEl, found);
|
|
1337
|
-
for (const marker of found) {
|
|
1338
|
-
if (!marker.data.startsWith(LIST_MARKER_PREFIX)) continue;
|
|
1339
|
-
const id = marker.data.slice(LIST_MARKER_PREFIX.length);
|
|
1340
|
-
const existing = bindings.get(id);
|
|
1341
|
-
if (existing) {
|
|
1342
|
-
if (existing.marker === marker && rootEl.contains(existing.marker)) continue;
|
|
1343
|
-
for (const item of existing.items) {
|
|
1344
|
-
disposeRowBindings(item.bindingDisposers);
|
|
1345
|
-
if (rootEl.contains(item.node)) {
|
|
1346
|
-
item.node.parentElement?.removeChild(item.node);
|
|
1347
|
-
}
|
|
1348
|
-
}
|
|
1349
|
-
bindings.delete(id);
|
|
1350
|
-
rebuiltLists?.add(id);
|
|
1351
|
-
devHooks.listRebind?.(id, marker.parentElement);
|
|
1352
|
-
}
|
|
1353
|
-
const listSeg = lists.get(id);
|
|
1354
|
-
const liveParent = marker.parentElement;
|
|
1355
|
-
const items = [];
|
|
1356
|
-
if (inlinedItems) {
|
|
1357
|
-
let next = marker.nextElementSibling;
|
|
1358
|
-
for (let i = 0; i < listSeg.items.length && next !== null; i++) {
|
|
1359
|
-
validateInlinedRowMatch(listSeg.items[i].html, i, next, liveParent);
|
|
1360
|
-
const rowBindings = listSeg.items[i].bindings;
|
|
1361
|
-
const bound = {
|
|
1362
|
-
ref: listSeg.items[i].ref,
|
|
1363
|
-
cacheKey: listSeg.items[i].cacheKey,
|
|
1364
|
-
html: listSeg.items[i].html,
|
|
1365
|
-
node: next,
|
|
1366
|
-
bindings: rowBindings
|
|
1367
|
-
};
|
|
1368
|
-
if (rowBindings?.length) {
|
|
1369
|
-
bound.bindingDisposers = wireRowBindings(next, rowBindings);
|
|
1370
|
-
}
|
|
1371
|
-
items.push(bound);
|
|
1372
|
-
next = next.nextElementSibling;
|
|
1373
|
-
}
|
|
1374
|
-
}
|
|
1375
|
-
const binding = { liveParent, items, marker };
|
|
1376
|
-
if (items.length > 0) {
|
|
1377
|
-
devHooks.missingRowKey?.(items[0].node, items[0].html, binding);
|
|
1378
|
-
}
|
|
1379
|
-
devHooks.eachInMorphSkip?.(id, liveParent, rootEl);
|
|
1380
|
-
bindings.set(id, binding);
|
|
1381
|
-
}
|
|
1382
|
-
}
|
|
1383
|
-
function validateInlinedRowMatch(expectedHtml, index, boundEl, liveParent) {
|
|
1384
|
-
if (boundEl.outerHTML === expectedHtml) return;
|
|
1385
|
-
const { content, count } = parseRowTemplate(expectedHtml, liveParent);
|
|
1386
|
-
if (count !== 1) throw rowContractError(index, expectedHtml, liveParent);
|
|
1387
|
-
const expectedTag = content.firstElementChild.tagName;
|
|
1388
|
-
if (boundEl.tagName !== expectedTag) throw rowStructureError(index, boundEl.tagName, expectedTag);
|
|
1389
|
-
}
|
|
1390
|
-
function rowStructureError(index, gotTag, wantTag) {
|
|
1391
|
-
const got = gotTag.toLowerCase();
|
|
1392
|
-
const want = wantTag.toLowerCase();
|
|
1393
|
-
return new Error(
|
|
1394
|
-
`each(): row ${index} renders <${want}>, but the HTML parser wrapped the rows in <${got}> \u2014 so kerf cannot bind one row per element. This happens when an each() of <${want}> sits directly inside a table: the parser inserts <${got}> around the whole run. Put the each() inside an explicit <${got}> (e.g. <table><${got}>{each(...)}</${got}></table>) so the rows are the direct children kerf binds.`
|
|
1395
|
-
);
|
|
1396
|
-
}
|
|
1397
|
-
function collectOwnedItems(bindings) {
|
|
1398
|
-
const owned = /* @__PURE__ */ new Set();
|
|
1399
|
-
for (const b of bindings.values()) {
|
|
1400
|
-
for (const item of b.items) owned.add(item.node);
|
|
1401
|
-
}
|
|
1402
|
-
return owned;
|
|
1403
|
-
}
|
|
1404
|
-
function cleanupOrphanBindings(segment, bindings, renderCtx) {
|
|
1405
|
-
const liveIds = collectLists(segment);
|
|
1406
|
-
for (const [id, binding] of bindings) {
|
|
1407
|
-
if (liveIds.has(id)) continue;
|
|
1408
|
-
for (const item of binding.items) {
|
|
1409
|
-
disposeRowBindings(item.bindingDisposers);
|
|
1410
|
-
item.node.remove();
|
|
1411
|
-
}
|
|
1412
|
-
binding.marker.remove();
|
|
1413
|
-
bindings.delete(id);
|
|
1414
|
-
renderCtx.bindingCounts.delete(id);
|
|
1415
|
-
renderCtx.bindingSources.delete(id);
|
|
1416
|
-
renderCtx.caches.delete(id);
|
|
1417
|
-
}
|
|
1418
|
-
}
|
|
1419
|
-
function collectComments(node, out) {
|
|
1420
|
-
for (let c = node.firstChild; c; c = c.nextSibling) {
|
|
1421
|
-
if (c.nodeType === Node.COMMENT_NODE) out.push(c);
|
|
1422
|
-
else if (c.nodeType === Node.ELEMENT_NODE) collectComments(c, out);
|
|
1423
|
-
}
|
|
1424
|
-
}
|
|
1425
1521
|
|
|
1426
1522
|
export { captureFocus, each, morph, mount, moveNode, restoreFocus };
|
|
1427
1523
|
|
|
1428
|
-
//# sourceMappingURL=chunk-
|
|
1524
|
+
//# sourceMappingURL=chunk-XHHGPZRQ.js.map
|