kerfjs 5.0.0-beta.4 → 5.0.0-beta.40

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 (71) hide show
  1. package/CHANGELOG.md +344 -116
  2. package/README.md +43 -23
  3. package/ai/cursorrules +87 -48
  4. package/ai/manifest.json +10 -7
  5. package/ai/skill.md +101 -67
  6. package/dist/actions.js +2 -2
  7. package/dist/actions.js.map +1 -1
  8. package/dist/array-signal.js +1 -1
  9. package/dist/async.js +22 -19
  10. package/dist/async.js.map +1 -1
  11. package/dist/attach.js +3 -1
  12. package/dist/attach.js.map +1 -1
  13. package/dist/{chunk-QIP723L4.js → chunk-5WRGJZV6.js} +3 -2
  14. package/dist/chunk-5WRGJZV6.js.map +1 -0
  15. package/dist/{chunk-GY4XV2UV.js → chunk-BDX3R4OM.js} +3 -2
  16. package/dist/chunk-BDX3R4OM.js.map +1 -0
  17. package/dist/{chunk-HW7KSM2Y.js → chunk-CEQMZYLR.js} +1 -1
  18. package/dist/{chunk-HW7KSM2Y.js.map → chunk-CEQMZYLR.js.map} +1 -1
  19. package/dist/{chunk-SVATPF5R.js → chunk-DLY444MN.js} +121 -49
  20. package/dist/chunk-DLY444MN.js.map +1 -0
  21. package/dist/{chunk-KPXIOG2C.js → chunk-E5R5GNKE.js} +14 -4
  22. package/dist/chunk-E5R5GNKE.js.map +1 -0
  23. package/dist/{chunk-LVH3GC6B.js → chunk-ELXVRKY2.js} +3 -3
  24. package/dist/chunk-ELXVRKY2.js.map +1 -0
  25. package/dist/{chunk-ZDCJZCNO.js → chunk-JRKE6XEK.js} +37 -16
  26. package/dist/chunk-JRKE6XEK.js.map +1 -0
  27. package/dist/{chunk-UZJ6I4T6.js → chunk-MK42GLPV.js} +1 -1
  28. package/dist/chunk-MK42GLPV.js.map +1 -0
  29. package/dist/{chunk-V46JKE44.js → chunk-WKIPLNVO.js} +1 -1
  30. package/dist/chunk-WKIPLNVO.js.map +1 -0
  31. package/dist/{chunk-U6FK33SG.js → chunk-Y2FOYPBV.js} +2 -2
  32. package/dist/{chunk-U6FK33SG.js.map → chunk-Y2FOYPBV.js.map} +1 -1
  33. package/dist/{chunk-KZJXHFIB.js → chunk-ZOIERTUW.js} +3 -3
  34. package/dist/chunk-ZOIERTUW.js.map +1 -0
  35. package/dist/dev.js +64 -17
  36. package/dist/dev.js.map +1 -1
  37. package/dist/html.js +5 -4
  38. package/dist/html.js.map +1 -1
  39. package/dist/index.d.ts +1 -1
  40. package/dist/index.js +23 -14
  41. package/dist/index.js.map +1 -1
  42. package/dist/jsx-runtime.d.ts +12 -5
  43. package/dist/jsx-runtime.js +1 -1
  44. package/dist/list.js +43 -14
  45. package/dist/list.js.map +1 -1
  46. package/dist/overlay.js +99 -32
  47. package/dist/overlay.js.map +1 -1
  48. package/dist/remount.js +2 -2
  49. package/dist/router.js +33 -10
  50. package/dist/router.js.map +1 -1
  51. package/dist/scope.js +5 -4
  52. package/dist/scope.js.map +1 -1
  53. package/dist/testing.js +1 -1
  54. package/dist/timing.js +1 -1
  55. package/dist/timing.js.map +1 -1
  56. package/llms.txt +6 -2
  57. package/package.json +32 -7
  58. package/setup/cli.mjs +90 -0
  59. package/setup/index.d.mts +37 -0
  60. package/setup/index.mjs +1347 -0
  61. package/setup/jsonc.mjs +201 -0
  62. package/setup/state.schema.json +56 -0
  63. package/dist/chunk-GY4XV2UV.js.map +0 -1
  64. package/dist/chunk-KPXIOG2C.js.map +0 -1
  65. package/dist/chunk-KZJXHFIB.js.map +0 -1
  66. package/dist/chunk-LVH3GC6B.js.map +0 -1
  67. package/dist/chunk-QIP723L4.js.map +0 -1
  68. package/dist/chunk-SVATPF5R.js.map +0 -1
  69. package/dist/chunk-UZJ6I4T6.js.map +0 -1
  70. package/dist/chunk-V46JKE44.js.map +0 -1
  71. package/dist/chunk-ZDCJZCNO.js.map +0 -1
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ export { A as AttrSpec, a as attr } from './attr-Cmu2ZoGO.js';
2
2
  export { D as DelegateOptions, d as delegate, a as delegateCapture } from './delegate-CL9VTZFb.js';
3
3
  import { ArraySignal } from './array-signal.js';
4
4
  import { SafeHtml } from './jsx-runtime.js';
5
- export { Fragment, isSafeHtml, raw } from './jsx-runtime.js';
5
+ export { Fragment, JSXChildren, isSafeHtml, raw } from './jsx-runtime.js';
6
6
  export { M as MountResult, m as mount } from './mount-Bo2qOx25.js';
7
7
  import { Signal } from '@preact/signals-core';
8
8
  export { ReadonlySignal, Signal, batch, computed } from '@preact/signals-core';
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
- export { each, morph, mount } from './chunk-SVATPF5R.js';
2
- export { defineStore, resetAllStores } from './chunk-KZJXHFIB.js';
3
- export { attr } from './chunk-UZJ6I4T6.js';
4
- export { delegate, delegateCapture } from './chunk-KPXIOG2C.js';
1
+ export { each, morph, mount } from './chunk-DLY444MN.js';
2
+ export { defineStore, resetAllStores } from './chunk-ZOIERTUW.js';
3
+ export { attr } from './chunk-MK42GLPV.js';
4
+ export { delegate, delegateCapture } from './chunk-E5R5GNKE.js';
5
5
 
6
6
 
7
- export { Fragment, SafeHtml, isSafeHtml, raw } from './chunk-ZDCJZCNO.js';
8
- export { batch, computed, effect, signal } from './chunk-U6FK33SG.js';
7
+ export { Fragment, SafeHtml, isSafeHtml, raw } from './chunk-JRKE6XEK.js';
8
+ export { batch, computed, effect, signal } from './chunk-Y2FOYPBV.js';
9
9
 
10
10
 
11
11
 
@@ -55,8 +55,10 @@ function parseSvgOrThrow(html, label, originalHtml) {
55
55
  const doc = new DOMParser().parseFromString(html, "image/svg+xml");
56
56
  const err = doc.querySelector("parsererror");
57
57
  if (err !== null) {
58
- throw new Error(`toElement: ${label} parse error \u2014 ${err.textContent}
59
- input: ${excerpt(originalHtml)}`);
58
+ throw new Error(
59
+ `toElement: ${label} parse error \u2014 ${err.textContent}
60
+ input: ${excerpt(originalHtml)}`
61
+ );
60
62
  }
61
63
  return doc;
62
64
  }
@@ -86,19 +88,26 @@ function toElement(jsx) {
86
88
  const wrapped = `<svg xmlns="${SVG_NS}">${html}</svg>`;
87
89
  const doc = parseSvgOrThrow(wrapped, "SVG fragment", html);
88
90
  const first = doc.documentElement.firstElementChild;
89
- if (first === null) throw new Error(`toElement: SVG fragment produced no element
90
- input: ${excerpt(html)}`);
91
+ if (first === null)
92
+ throw new Error(
93
+ `toElement: SVG fragment produced no element
94
+ input: ${excerpt(html)}`
95
+ );
91
96
  return adopt(first);
92
97
  }
93
98
  return adopt(single);
94
99
  }
95
100
  if (content.childNodes.length === 0) {
96
- throw new Error(`toElement: produced no element
97
- input: ${excerpt(html)}`);
101
+ throw new Error(
102
+ `toElement: produced no element
103
+ input: ${excerpt(html)}`
104
+ );
98
105
  }
99
106
  if (content.children.length === 0) {
100
- throw new Error(`toElement: produced no element
101
- input: ${excerpt(html)}`);
107
+ throw new Error(
108
+ `toElement: produced no element
109
+ input: ${excerpt(html)}`
110
+ );
102
111
  }
103
112
  return adopt(content);
104
113
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/renderDocument.ts","../src/toElement.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * `renderDocument(node)` — prepend the doctype to a rendered document.\n *\n * Every kerf SSR app ends its routes with the identical concat\n * `\"<!DOCTYPE html>\" + page.toString()`. This is that one line, blessed, so the\n * doctype isn't reinvented (or forgotten) per route.\n *\n * import { renderDocument } from 'kerfjs';\n * return c.html(renderDocument(<Page />)); // \"<!DOCTYPE html><html>…\"\n *\n * `node` is a `SafeHtml` (from JSX / the `html` tagged template) or a raw string\n * — both are stringified via `.toString()`. The optional `doctype` overrides the\n * default `'html'`.\n */\nimport type { SafeHtml } from './jsx-runtime.js';\n\n/** Options for {@link renderDocument}. */\nexport interface RenderDocumentOptions {\n /** The doctype name. Default `'html'` → `<!DOCTYPE html>`. */\n doctype?: string;\n}\n\n/** Prepend `<!DOCTYPE …>` to a rendered `SafeHtml` (or string) document and return the full HTML string. */\nexport function renderDocument(node: SafeHtml | string, options: RenderDocumentOptions = {}): string {\n const { doctype = 'html' } = options;\n return `<!DOCTYPE ${doctype}>${node.toString()}`;\n}\n","/**\n * `toElement(jsx)` — JSX → DOM, with SVG-aware namespace handling.\n *\n * Single-root inputs return an `Element`. Multi-root inputs (text + element,\n * `<><svg/> label</>`, two icons side by side, …) return a `DocumentFragment`\n * containing every top-level node. The DOM insertion APIs (`appendChild`,\n * `replaceChildren`, `append`) inline a `DocumentFragment`'s children on\n * insert, so `parent.replaceChildren(toElement(<>{ICON} label</>))` does the\n * obvious thing — parent gets the svg AND the text, no silent loss.\n *\n * The returned node is always adopted into the live `document` before it's\n * handed back (see `adopt()` below) — never left owned by the inert\n * `<template>`/`DOMParser` document it was parsed in, which is unsafe to mutate\n * before insertion on some engines.\n *\n * SVG namespacing: the HTML5 parser handles `<svg>` as foreign content\n * correctly even when wrapped in a fragment, so multi-root inputs containing\n * SVGs come out namespaced correctly via the `<template>.innerHTML` path. For\n * single-root SVG inputs the XML parser is preferred (stricter — catches\n * malformed markup the HTML5 parser would silently auto-correct). For orphan\n * SVG-namespace fragments without an `<svg>` wrapper (`<path/>`, `<g>`, …)\n * the input is wrapped in `<svg xmlns=...>` and XML-parsed.\n */\n\nimport type { SafeHtml } from './jsx-runtime.js';\n\nconst SVG_NS = 'http://www.w3.org/2000/svg';\n\nconst SVG_FRAGMENT_TAGS = new Set([\n 'g', 'path', 'circle', 'rect', 'line', 'polygon', 'polyline', 'ellipse',\n 'text', 'tspan', 'defs', 'use', 'symbol', 'clipPath', 'mask', 'pattern',\n 'filter', 'marker', 'linearGradient', 'radialGradient', 'stop', 'image',\n 'foreignObject',\n]);\n\nconst EXCERPT_MAX_LEN = 100;\n\nfunction excerpt(html: string): string {\n const trimmed = html.trim();\n return trimmed.length > EXCERPT_MAX_LEN ? `${trimmed.slice(0, EXCERPT_MAX_LEN)}…` : trimmed;\n}\n\n/**\n * Adopt `node` into the live `document` before returning it.\n *\n * Both parse paths produce nodes that belong to an *inert* document, not the\n * one the caller renders into: the `<template>.content` path yields nodes\n * owned by the template-contents owner document, and the `DOMParser` path\n * yields nodes owned by the parser's document. Returning such a node is a\n * footgun — operating on it before it's inserted (most notably `mount()`'s\n * first-render `rootEl.innerHTML = …`) runs against an inert-document element,\n * which on WebKit trips a fragment-parsing bug: under rapid bursts the parser\n * can hand back a *previous* parse's nodes, so a freshly-built element silently\n * inherits unrelated DOM. (Chromium never reproduces it; the originating\n * report was Safari-only — a feed mounting many `toElement(<div/>)` cards in\n * one flush, each painting a stale sibling's state.) `adoptNode` moves the node\n * into the live document (preserving identity and namespaces), so the returned\n * node is always safe to mutate before insertion.\n */\nfunction adopt<T extends Node>(node: T): T {\n document.adoptNode(node);\n return node;\n}\n\nfunction parseSvgOrThrow(html: string, label: string, originalHtml: string): Document {\n const doc = new DOMParser().parseFromString(html, 'image/svg+xml');\n const err = doc.querySelector('parsererror');\n if (err !== null) {\n throw new Error(`toElement: ${label} parse error — ${err.textContent}\\n input: ${excerpt(originalHtml)}`);\n }\n return doc;\n}\n\n// True when the fragment is effectively a single root — exactly one element\n// child, all sibling nodes whitespace-only text. Returns that element, else\n// null. The whitespace tolerance matches the pre-KF-232 behavior for inputs\n// like ` <svg/>\\n` (which were treated as a single SVG root).\nfunction singleRootElement(content: DocumentFragment): Element | null {\n if (content.children.length !== 1) return null;\n const onlyElement = content.firstElementChild;\n /* c8 ignore next — guarded by children.length === 1 above. */\n if (onlyElement === null) return null;\n for (const node of content.childNodes) {\n if (node === onlyElement) continue;\n if (node.nodeType !== Node.TEXT_NODE) return null;\n /* c8 ignore next — `textContent` on a TEXT_NODE is always a string; the `??` is a TypeScript-only guard. */\n if ((node.textContent ?? '').trim() !== '') return null;\n }\n return onlyElement;\n}\n\nexport function toElement(jsx: SafeHtml | string): Element | DocumentFragment {\n const html = typeof jsx === 'string' ? jsx : jsx.toString();\n\n // First pass: HTML <template> parse. This gives us correct namespacing for\n // `<svg>` (foreign content), reveals the multi-root vs single-root shape,\n // and is the universal fallback path. Orphan SVG fragments without an\n // `<svg>` wrapper are the one case that needs a different parser — handled\n // below.\n const t = document.createElement('template');\n t.innerHTML = html;\n const content = t.content;\n\n const single = singleRootElement(content);\n\n if (single !== null) {\n const tag = single.tagName.toLowerCase();\n\n // Single SVG root → re-parse via XML for strict validation. The HTML5\n // parser silently auto-corrects malformed SVG (`<svg><unclosed</svg>`),\n // which is worse than throwing. Trim so trailing whitespace in the input\n // doesn't trip up the XML parser.\n if (tag === 'svg') {\n return adopt(parseSvgOrThrow(html.trim(), 'SVG', html).documentElement);\n }\n\n // Single orphan SVG-namespace fragment (`<path/>`, `<g>`, …) — the HTML5\n // parser puts these in the XHTML namespace, so wrap in `<svg>` and\n // XML-parse to get the right namespace on the returned element.\n if (SVG_FRAGMENT_TAGS.has(tag)) {\n const wrapped = `<svg xmlns=\"${SVG_NS}\">${html}</svg>`;\n const doc = parseSvgOrThrow(wrapped, 'SVG fragment', html);\n const first = doc.documentElement.firstElementChild;\n /* c8 ignore next 2 — defensive: a successful XML parse of a wrapped svg always yields ≥1 child. */\n if (first === null) throw new Error(`toElement: SVG fragment produced no element\\n input: ${excerpt(html)}`);\n return adopt(first);\n }\n\n return adopt(single);\n }\n\n // Multi-root (or no roots): return the parsed DocumentFragment so the caller\n // can splat all the top-level nodes into their container via appendChild /\n // replaceChildren / append. If the input produced nothing at all, throw —\n // there's no useful Node to hand back.\n if (content.childNodes.length === 0) {\n throw new Error(`toElement: produced no element\\n input: ${excerpt(html)}`);\n }\n if (content.children.length === 0) {\n // No element children at all — only text or comments. Same \"no element\"\n // failure as the empty case; toElement's name implies at least one\n // element somewhere.\n throw new Error(`toElement: produced no element\\n input: ${excerpt(html)}`);\n }\n return adopt(content);\n}\n"]}
1
+ {"version":3,"sources":["../src/renderDocument.ts","../src/toElement.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * `renderDocument(node)` — prepend the doctype to a rendered document.\n *\n * Every kerf SSR app ends its routes with the identical concat\n * `\"<!DOCTYPE html>\" + page.toString()`. This is that one line, blessed, so the\n * doctype isn't reinvented (or forgotten) per route.\n *\n * import { renderDocument } from 'kerfjs';\n * return c.html(renderDocument(<Page />)); // \"<!DOCTYPE html><html>…\"\n *\n * `node` is a `SafeHtml` (from JSX / the `html` tagged template) or a raw string\n * — both are stringified via `.toString()`. The optional `doctype` overrides the\n * default `'html'`.\n */\nimport type { SafeHtml } from './jsx-runtime.js';\n\n/** Options for {@link renderDocument}. */\nexport interface RenderDocumentOptions {\n /** The doctype name. Default `'html'` → `<!DOCTYPE html>`. */\n doctype?: string;\n}\n\n/** Prepend `<!DOCTYPE …>` to a rendered `SafeHtml` (or string) document and return the full HTML string. */\nexport function renderDocument(\n node: SafeHtml | string,\n options: RenderDocumentOptions = {},\n): string {\n const { doctype = 'html' } = options;\n return `<!DOCTYPE ${doctype}>${node.toString()}`;\n}\n","/**\n * `toElement(jsx)` — JSX → DOM, with SVG-aware namespace handling.\n *\n * Single-root inputs return an `Element`. Multi-root inputs (text + element,\n * `<><svg/> label</>`, two icons side by side, …) return a `DocumentFragment`\n * containing every top-level node. The DOM insertion APIs (`appendChild`,\n * `replaceChildren`, `append`) inline a `DocumentFragment`'s children on\n * insert, so `parent.replaceChildren(toElement(<>{ICON} label</>))` does the\n * obvious thing — parent gets the svg AND the text, no silent loss.\n *\n * The returned node is always adopted into the live `document` before it's\n * handed back (see `adopt()` below) — never left owned by the inert\n * `<template>`/`DOMParser` document it was parsed in, which is unsafe to mutate\n * before insertion on some engines.\n *\n * SVG namespacing: the HTML5 parser handles `<svg>` as foreign content\n * correctly even when wrapped in a fragment, so multi-root inputs containing\n * SVGs come out namespaced correctly via the `<template>.innerHTML` path. For\n * single-root SVG inputs the XML parser is preferred (stricter — catches\n * malformed markup the HTML5 parser would silently auto-correct). For orphan\n * SVG-namespace fragments without an `<svg>` wrapper (`<path/>`, `<g>`, …)\n * the input is wrapped in `<svg xmlns=...>` and XML-parsed.\n */\n\nimport type { SafeHtml } from './jsx-runtime.js';\n\nconst SVG_NS = 'http://www.w3.org/2000/svg';\n\nconst SVG_FRAGMENT_TAGS = new Set([\n 'g',\n 'path',\n 'circle',\n 'rect',\n 'line',\n 'polygon',\n 'polyline',\n 'ellipse',\n 'text',\n 'tspan',\n 'defs',\n 'use',\n 'symbol',\n 'clipPath',\n 'mask',\n 'pattern',\n 'filter',\n 'marker',\n 'linearGradient',\n 'radialGradient',\n 'stop',\n 'image',\n 'foreignObject',\n]);\n\nconst EXCERPT_MAX_LEN = 100;\n\nfunction excerpt(html: string): string {\n const trimmed = html.trim();\n return trimmed.length > EXCERPT_MAX_LEN\n ? `${trimmed.slice(0, EXCERPT_MAX_LEN)}…`\n : trimmed;\n}\n\n/**\n * Adopt `node` into the live `document` before returning it.\n *\n * Both parse paths produce nodes that belong to an *inert* document, not the\n * one the caller renders into: the `<template>.content` path yields nodes\n * owned by the template-contents owner document, and the `DOMParser` path\n * yields nodes owned by the parser's document. Returning such a node is a\n * footgun — operating on it before it's inserted (most notably `mount()`'s\n * first-render `rootEl.innerHTML = …`) runs against an inert-document element,\n * which on WebKit trips a fragment-parsing bug: under rapid bursts the parser\n * can hand back a *previous* parse's nodes, so a freshly-built element silently\n * inherits unrelated DOM. (Chromium never reproduces it; the originating\n * report was Safari-only — a feed mounting many `toElement(<div/>)` cards in\n * one flush, each painting a stale sibling's state.) `adoptNode` moves the node\n * into the live document (preserving identity and namespaces), so the returned\n * node is always safe to mutate before insertion.\n */\nfunction adopt<T extends Node>(node: T): T {\n document.adoptNode(node);\n return node;\n}\n\nfunction parseSvgOrThrow(\n html: string,\n label: string,\n originalHtml: string,\n): Document {\n const doc = new DOMParser().parseFromString(html, 'image/svg+xml');\n const err = doc.querySelector('parsererror');\n if (err !== null) {\n throw new Error(\n `toElement: ${label} parse error — ${err.textContent}\\n input: ${excerpt(originalHtml)}`,\n );\n }\n return doc;\n}\n\n// True when the fragment is effectively a single root — exactly one element\n// child, all sibling nodes whitespace-only text. Returns that element, else\n// null. The whitespace tolerance matches the pre-KF-232 behavior for inputs\n// like ` <svg/>\\n` (which were treated as a single SVG root).\nfunction singleRootElement(content: DocumentFragment): Element | null {\n if (content.children.length !== 1) return null;\n const onlyElement = content.firstElementChild;\n /* c8 ignore next — guarded by children.length === 1 above. */\n if (onlyElement === null) return null;\n for (const node of content.childNodes) {\n if (node === onlyElement) continue;\n if (node.nodeType !== Node.TEXT_NODE) return null;\n /* c8 ignore next — `textContent` on a TEXT_NODE is always a string; the `??` is a TypeScript-only guard. */\n if ((node.textContent ?? '').trim() !== '') return null;\n }\n return onlyElement;\n}\n\nexport function toElement(jsx: SafeHtml | string): Element | DocumentFragment {\n const html = typeof jsx === 'string' ? jsx : jsx.toString();\n\n // First pass: HTML <template> parse. This gives us correct namespacing for\n // `<svg>` (foreign content), reveals the multi-root vs single-root shape,\n // and is the universal fallback path. Orphan SVG fragments without an\n // `<svg>` wrapper are the one case that needs a different parser — handled\n // below.\n const t = document.createElement('template');\n t.innerHTML = html;\n const content = t.content;\n\n const single = singleRootElement(content);\n\n if (single !== null) {\n const tag = single.tagName.toLowerCase();\n\n // Single SVG root → re-parse via XML for strict validation. The HTML5\n // parser silently auto-corrects malformed SVG (`<svg><unclosed</svg>`),\n // which is worse than throwing. Trim so trailing whitespace in the input\n // doesn't trip up the XML parser.\n if (tag === 'svg') {\n return adopt(parseSvgOrThrow(html.trim(), 'SVG', html).documentElement);\n }\n\n // Single orphan SVG-namespace fragment (`<path/>`, `<g>`, …) — the HTML5\n // parser puts these in the XHTML namespace, so wrap in `<svg>` and\n // XML-parse to get the right namespace on the returned element.\n if (SVG_FRAGMENT_TAGS.has(tag)) {\n const wrapped = `<svg xmlns=\"${SVG_NS}\">${html}</svg>`;\n const doc = parseSvgOrThrow(wrapped, 'SVG fragment', html);\n const first = doc.documentElement.firstElementChild;\n /* c8 ignore next 2 — defensive: a successful XML parse of a wrapped svg always yields ≥1 child. */\n if (first === null)\n throw new Error(\n `toElement: SVG fragment produced no element\\n input: ${excerpt(html)}`,\n );\n return adopt(first);\n }\n\n return adopt(single);\n }\n\n // Multi-root (or no roots): return the parsed DocumentFragment so the caller\n // can splat all the top-level nodes into their container via appendChild /\n // replaceChildren / append. If the input produced nothing at all, throw —\n // there's no useful Node to hand back.\n if (content.childNodes.length === 0) {\n throw new Error(\n `toElement: produced no element\\n input: ${excerpt(html)}`,\n );\n }\n if (content.children.length === 0) {\n // No element children at all — only text or comments. Same \"no element\"\n // failure as the empty case; toElement's name implies at least one\n // element somewhere.\n throw new Error(\n `toElement: produced no element\\n input: ${excerpt(html)}`,\n );\n }\n return adopt(content);\n}\n"]}
@@ -1204,10 +1204,17 @@ declare function listSafeHtml(id: string, items: ListSegment['items'], source?:
1204
1204
  * row rendering.
1205
1205
  */
1206
1206
  declare function granularListSafeHtml(id: string, items: ListSegment['items'], patches: NonNullable<ListSegment['patches']>, source?: object): SafeHtml;
1207
- type Child = SafeHtml | string | number | boolean | null | undefined | ReadonlySignal<unknown>;
1208
- type Children = Child | Children[];
1207
+ /**
1208
+ * Content accepted by a Kerf JSX child position.
1209
+ *
1210
+ * Use this for `children` (and equivalent content slots) on third-party
1211
+ * function components. It deliberately mirrors the runtime: arrays may be
1212
+ * readonly and nested to any depth, signals bind fine-grained inside a mount,
1213
+ * and boolean/nullish values render nothing.
1214
+ */
1215
+ type JSXChildren = SafeHtml | string | number | boolean | null | undefined | ReadonlySignal<unknown> | readonly JSXChildren[];
1209
1216
  interface Props {
1210
- children?: Children;
1217
+ children?: JSXChildren;
1211
1218
  [key: string]: unknown;
1212
1219
  }
1213
1220
  /**
@@ -1237,7 +1244,7 @@ declare function assertEmittableAttrName(key: string, name: string, isFn: boolea
1237
1244
  declare function jsx(tag: string | ((props: Props) => SafeHtml), props: Props): SafeHtml;
1238
1245
 
1239
1246
  declare function Fragment({ children }: {
1240
- children?: Children;
1247
+ children?: JSXChildren;
1241
1248
  }): SafeHtml;
1242
1249
  declare namespace JSX {
1243
1250
  type Element = SafeHtml;
@@ -1260,4 +1267,4 @@ declare namespace JSX {
1260
1267
  declare function _toSegment(child: unknown): Segment;
1261
1268
  declare function _renderAttrVerbatim(name: string, value: unknown): string;
1262
1269
 
1263
- export { type AttrLike, type AttrValue, type DataAriaAttrs, Fragment, JSX, type KerfBaseAttrs, type KerfCustomElement, SafeHtml, assertEmittableAttrName as _assertEmittableAttrName, _renderAttrVerbatim, _toSegment, granularListSafeHtml, isSafeHtml, jsx, jsx as jsxDEV, jsx as jsxs, listSafeHtml, raw };
1270
+ export { type AttrLike, type AttrValue, type DataAriaAttrs, Fragment, JSX, type JSXChildren, type KerfBaseAttrs, type KerfCustomElement, SafeHtml, assertEmittableAttrName as _assertEmittableAttrName, _renderAttrVerbatim, _toSegment, granularListSafeHtml, isSafeHtml, jsx, jsx as jsxDEV, jsx as jsxs, listSafeHtml, raw };
@@ -1,4 +1,4 @@
1
- export { Fragment, SafeHtml, assertEmittableAttrName as _assertEmittableAttrName, _renderAttrVerbatim, _toSegment, granularListSafeHtml, isSafeHtml, jsx, jsx as jsxDEV, jsx as jsxs, listSafeHtml, raw } from './chunk-ZDCJZCNO.js';
1
+ export { Fragment, SafeHtml, assertEmittableAttrName as _assertEmittableAttrName, _renderAttrVerbatim, _toSegment, granularListSafeHtml, isSafeHtml, jsx, jsx as jsxDEV, jsx as jsxs, listSafeHtml, raw } from './chunk-JRKE6XEK.js';
2
2
 
3
3
 
4
4
 
package/dist/list.js CHANGED
@@ -1,9 +1,9 @@
1
- import { ARRAY_SIGNAL_BRAND } from './chunk-LVH3GC6B.js';
2
- import { captureFocus, restoreFocus, moveNode, mount } from './chunk-SVATPF5R.js';
1
+ import { ARRAY_SIGNAL_BRAND } from './chunk-ELXVRKY2.js';
2
+ import { captureFocus, restoreFocus, moveNode, mount } from './chunk-DLY444MN.js';
3
3
 
4
4
 
5
5
 
6
- import { effect } from './chunk-U6FK33SG.js';
6
+ import { effect } from './chunk-Y2FOYPBV.js';
7
7
 
8
8
 
9
9
 
@@ -87,7 +87,8 @@ function createListRowController(options) {
87
87
  let ref = endAnchor();
88
88
  for (let index = order.length - 1; index >= 0; index--) {
89
89
  const el = order[index].el;
90
- if (el.parentNode !== container || el.nextSibling !== ref) moveNode(container, el, ref);
90
+ if (el.parentNode !== container || el.nextSibling !== ref)
91
+ moveNode(container, el, ref);
91
92
  ref = el;
92
93
  }
93
94
  });
@@ -99,7 +100,10 @@ function createListRowController(options) {
99
100
  const row = makeRow(patch.item);
100
101
  rows.set(key(patch.item), row);
101
102
  order.splice(patch.index, 0, row);
102
- container.insertBefore(row.el, order[patch.index + 1]?.el ?? endAnchor());
103
+ container.insertBefore(
104
+ row.el,
105
+ order[patch.index + 1]?.el ?? endAnchor()
106
+ );
103
107
  } else if (patch.type === "remove") {
104
108
  const [row] = order.splice(patch.index, 1);
105
109
  row.dispose();
@@ -114,7 +118,10 @@ function createListRowController(options) {
114
118
  const row = reconcileItem(current, patch.item);
115
119
  if (row !== current) {
116
120
  order[patch.index] = row;
117
- container.insertBefore(row.el, order[patch.index + 1]?.el ?? endAnchor());
121
+ container.insertBefore(
122
+ row.el,
123
+ order[patch.index + 1]?.el ?? endAnchor()
124
+ );
118
125
  }
119
126
  }
120
127
  }
@@ -152,7 +159,10 @@ function createListVirtualizationController(options) {
152
159
  const estimate = rowHeight.estimate;
153
160
  return typeof estimate === "function" ? estimate(items[index], index) : estimate;
154
161
  };
155
- const variableHeightAt = fixedHeight !== null ? null : measuring ? (index) => measured.get(key(items[index])) ?? estimateAt(index) : (index) => rowHeight(items[index], index);
162
+ const variableHeightAt = fixedHeight !== null ? null : measuring ? (index) => measured.get(key(items[index])) ?? estimateAt(index) : (index) => rowHeight(
163
+ items[index],
164
+ index
165
+ );
156
166
  const intrinsicSizeAt = (index) => fixedHeight ?? variableHeightAt(index);
157
167
  const rebuildOffsets = () => {
158
168
  const heightAt = variableHeightAt;
@@ -223,7 +233,10 @@ function createListVirtualizationController(options) {
223
233
  padBottom = 0;
224
234
  } else if (fixedHeight !== null) {
225
235
  const viewportBottom = parent.scrollTop + parent.clientHeight;
226
- start2 = Math.max(0, Math.floor(parent.scrollTop / fixedHeight) - overscan);
236
+ start2 = Math.max(
237
+ 0,
238
+ Math.floor(parent.scrollTop / fixedHeight) - overscan
239
+ );
227
240
  end = Math.min(total, Math.ceil(viewportBottom / fixedHeight) + overscan);
228
241
  padTop = start2 * fixedHeight;
229
242
  padBottom = Math.max(0, total - end) * fixedHeight;
@@ -275,7 +288,8 @@ function createListVirtualizationController(options) {
275
288
  const oldHeight = measured.get(rowKey) ?? estimateAt(index);
276
289
  if (height === oldHeight) return;
277
290
  measured.set(rowKey, height);
278
- if (offsets[index + 1] <= parent.scrollTop) pendingAnchorDelta += height - oldHeight;
291
+ if (offsets[index + 1] <= parent.scrollTop)
292
+ pendingAnchorDelta += height - oldHeight;
279
293
  heightsDirty = true;
280
294
  scheduleRender();
281
295
  };
@@ -317,15 +331,29 @@ function bindList(parent, source, options) {
317
331
  const granularEligible = virtualize === void 0 && patchSource[ARRAY_SIGNAL_BRAND] === true;
318
332
  const container = virtualize === void 0 ? parent : document.createElement("div");
319
333
  if (virtualize !== void 0) {
320
- if (virtualize.containerClass !== void 0) container.className = virtualize.containerClass;
321
- if (virtualize.containerId !== void 0) container.id = virtualize.containerId;
334
+ if (virtualize.containerClass !== void 0)
335
+ container.className = virtualize.containerClass;
336
+ if (virtualize.containerId !== void 0)
337
+ container.id = virtualize.containerId;
322
338
  }
323
339
  const endAnchor = () => {
324
340
  if (virtualize !== void 0 || before === void 0) return null;
325
341
  return (typeof before === "function" ? before() : before) ?? null;
326
342
  };
327
- const rows = createListRowController({ container, key, render, tag, endAnchor });
328
- const virtualization = virtualize === void 0 ? void 0 : createListVirtualizationController({ parent, container, virtualize, key, rows });
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
+ });
329
357
  const renderItems = (items) => {
330
358
  if (virtualization !== void 0) {
331
359
  virtualization.render(items);
@@ -395,7 +423,8 @@ function observeRowHeights(handle) {
395
423
  const observer = new RO((entries) => {
396
424
  for (const entry of entries) {
397
425
  const k = keyByEl.get(entry.target);
398
- if (k !== void 0) handle.setHeight(k, entry.target.offsetHeight);
426
+ if (k !== void 0)
427
+ handle.setHeight(k, entry.target.offsetHeight);
399
428
  }
400
429
  });
401
430
  const resync = () => {
package/dist/list.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/list-row-controller.ts","../src/list-virtualization-controller.ts","../src/list.ts"],"names":["dispose","start","NOOP"],"mappings":"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","file":"list.js","sourcesContent":["import type { ArrayPatch } from './array-signal.js';\nimport type { BindListOptions,ListKey,RowElement } from './list.js';\nimport { captureFocus,restoreFocus } from './list-reconcile-focus.js';\nimport { mount,type MountResult } from './mount.js';\nimport { moveNode } from './utils/moveNode.js';\n\ninterface ListRow<T> {\n el: HTMLElement;\n item: T;\n dispose: () => void;\n elementMode: boolean;\n update?: (item: T) => void;\n}\n\nexport interface ListRowController<T> {\n readonly order: Array<ListRow<T>>;\n sync: (visible: readonly T[]) => void;\n applyPatches: (patches: readonly ArrayPatch<T>[]) => void;\n dispose: (removeElements: boolean) => void;\n}\n\ninterface ListRowControllerOptions<T> {\n container: HTMLElement;\n key: (item: T) => ListKey;\n render: BindListOptions<T>['render'];\n tag: string;\n endAnchor: () => Node | null;\n}\n\nconst NOOP = (): void => { /* element-mode rows with no caller teardown */ };\n\n/** Own the keyed row map, DOM order, row lifecycles, and both reconcile paths. */\nexport function createListRowController<T>(\n options: ListRowControllerOptions<T>,\n): ListRowController<T> {\n const { container,key,render,tag,endAnchor } = options;\n const rows = new Map<ListKey,ListRow<T>>();\n const order: Array<ListRow<T>> = [];\n\n const asElementRow = (\n rendered: MountResult | RowElement<T>,\n ): { el: HTMLElement; dispose: () => void; update?: (item: T) => void } | null => {\n if (rendered instanceof HTMLElement) return { el: rendered,dispose: NOOP };\n if (\n rendered !== null\n && typeof rendered === 'object'\n && 'el' in rendered\n && (rendered as { el: unknown }).el instanceof HTMLElement\n ) {\n const row = rendered as { el: HTMLElement; update?: (item: T) => void; dispose?: () => void };\n return { el: row.el,dispose: row.dispose ?? NOOP,update: row.update };\n }\n return null;\n };\n\n const makeRow = (item: T): ListRow<T> => {\n const elementRow = asElementRow(render(item));\n if (elementRow !== null) {\n return {\n el: elementRow.el,\n item,\n dispose: elementRow.dispose,\n elementMode: true,\n update: elementRow.update,\n };\n }\n const el = document.createElement(tag);\n const dispose = mount(el,() => render(item) as MountResult);\n return { el,item,dispose,elementMode: false };\n };\n\n // The single item-replacement contract used by snapshot and granular paths.\n // Element-mode rows keep their caller-owned element and are re-keyed; content\n // rows replace their mount. DOM placement remains the caller's responsibility.\n const reconcileItem = (row: ListRow<T>,item: T): ListRow<T> => {\n if (row.item === item) return row;\n const oldKey = key(row.item);\n const newKey = key(item);\n if (row.elementMode) {\n row.item = item;\n if (newKey !== oldKey) {\n rows.delete(oldKey);\n rows.set(newKey,row);\n }\n row.update?.(item);\n return row;\n }\n row.dispose();\n row.el.remove();\n rows.delete(oldKey);\n const fresh = makeRow(item);\n rows.set(newKey,fresh);\n return fresh;\n };\n\n const preserveFocus = (operation: () => void): void => {\n const focus = captureFocus(container);\n try {\n operation();\n } finally {\n if (focus !== null) restoreFocus(focus);\n }\n };\n\n const sync = (visible: readonly T[]): void => {\n preserveFocus(() => {\n const wanted = new Set<ListKey>();\n for (const item of visible) wanted.add(key(item));\n\n for (const [rowKey,row] of rows) {\n if (!wanted.has(rowKey)) {\n row.dispose();\n row.el.remove();\n rows.delete(rowKey);\n }\n }\n\n order.length = 0;\n for (const item of visible) {\n const rowKey = key(item);\n const existing = rows.get(rowKey);\n const row = existing === undefined ? makeRow(item) : reconcileItem(existing,item);\n if (existing === undefined) rows.set(rowKey,row);\n order.push(row);\n }\n\n let ref: Node | null = endAnchor();\n for (let index = order.length - 1; index >= 0; index--) {\n const el = order[index].el;\n if (el.parentNode !== container || el.nextSibling !== ref) moveNode(container,el,ref);\n ref = el;\n }\n });\n };\n\n const applyPatches = (patches: readonly ArrayPatch<T>[]): void => {\n preserveFocus(() => {\n for (const patch of patches) {\n if (patch.type === 'insert') {\n const row = makeRow(patch.item);\n rows.set(key(patch.item),row);\n order.splice(patch.index,0,row);\n container.insertBefore(row.el,order[patch.index + 1]?.el ?? endAnchor());\n } else if (patch.type === 'remove') {\n const [row] = order.splice(patch.index,1);\n row.dispose();\n row.el.remove();\n rows.delete(key(row.item));\n } else if (patch.type === 'move') {\n const [row] = order.splice(patch.from,1);\n order.splice(patch.to,0,row);\n moveNode(container,row.el,order[patch.to + 1]?.el ?? endAnchor());\n } else if (patch.type === 'update') {\n const current = order[patch.index];\n const row = reconcileItem(current,patch.item);\n if (row !== current) {\n order[patch.index] = row;\n container.insertBefore(row.el,order[patch.index + 1]?.el ?? endAnchor());\n }\n }\n }\n });\n };\n\n const dispose = (removeElements: boolean): void => {\n for (const row of rows.values()) {\n row.dispose();\n if (removeElements) row.el.remove();\n }\n rows.clear();\n order.length = 0;\n };\n\n return { order,sync,applyPatches,dispose };\n}\n","import type { BindListOptions,ListKey } from './list.js';\nimport type { ListRowController } from './list-row-controller.js';\n\ninterface ListVirtualizationController<T> {\n readonly contentVisibility: boolean;\n render: (items: readonly T[]) => void;\n start: () => void;\n setHeight: (key: ListKey,height: number) => void;\n onRender: (callback: () => void) => () => void;\n dispose: () => void;\n}\n\ninterface ListVirtualizationControllerOptions<T> {\n parent: HTMLElement;\n container: HTMLElement;\n virtualize: NonNullable<BindListOptions<T>['virtualize']>;\n key: (item: T) => ListKey;\n rows: ListRowController<T>;\n}\n\n/** Own window calculation, height models, scheduling, observers, and CSS strategy. */\nexport function createListVirtualizationController<T>(\n options: ListVirtualizationControllerOptions<T>,\n): ListVirtualizationController<T> {\n const { parent,container,virtualize,key,rows } = options;\n const overscan = virtualize.overscan ?? 3;\n const { minRows,rowHeight } = virtualize;\n const contentVisibility = virtualize.mode === 'content-visibility';\n const fixedHeight = typeof rowHeight === 'number' ? rowHeight : null;\n const measuring = typeof rowHeight === 'object' && rowHeight !== null;\n const measured = new Map<ListKey,number>();\n const indexByKey = new Map<ListKey,number>();\n const renderSubscribers = new Set<() => void>();\n let items: readonly T[] = [];\n let offsets: number[] = [0];\n let heightsDirty = true;\n let pendingAnchorDelta = 0;\n let rafPending = false;\n let disposed = false;\n\n const estimateAt = (index: number): number => {\n const estimate = (rowHeight as { estimate: number | ((item: T,index: number) => number) }).estimate;\n return typeof estimate === 'function' ? estimate(items[index],index) : estimate;\n };\n const variableHeightAt: ((index: number) => number) | null =\n fixedHeight !== null\n ? null\n : measuring\n ? (index): number => measured.get(key(items[index])) ?? estimateAt(index)\n : (index): number => (rowHeight as (item: T,index: number) => number)(items[index],index);\n const intrinsicSizeAt = (index: number): number =>\n fixedHeight ?? (variableHeightAt as (index: number) => number)(index);\n\n const rebuildOffsets = (): void => {\n const heightAt = variableHeightAt as (index: number) => number;\n offsets = new Array<number>(items.length + 1);\n offsets[0] = 0;\n if (measuring) indexByKey.clear();\n for (let index = 0; index < items.length; index++) {\n offsets[index + 1] = offsets[index] + heightAt(index);\n if (measuring) indexByKey.set(key(items[index]),index);\n }\n if (measuring) {\n for (const rowKey of measured.keys()) {\n if (!indexByKey.has(rowKey)) measured.delete(rowKey);\n }\n }\n };\n\n const findStart = (target: number,total: number): number => {\n let low = 0;\n let high = total;\n while (low < high) {\n const middle = (low + high + 1) >> 1;\n if (offsets[middle] <= target) low = middle;\n else high = middle - 1;\n }\n return low;\n };\n\n const findEnd = (target: number,total: number): number => {\n let low = 0;\n let high = total;\n while (low < high) {\n const middle = (low + high) >> 1;\n if (offsets[middle] >= target) high = middle;\n else low = middle + 1;\n }\n return low;\n };\n\n const sizeRows = (start: number): void => {\n if (measuring) return;\n for (let index = 0; index < rows.order.length; index++) {\n const absoluteIndex = start + index;\n const height = fixedHeight ?? offsets[absoluteIndex + 1] - offsets[absoluteIndex];\n rows.order[index].el.style.height = `${height}px`;\n }\n };\n\n const renderCurrent = (): void => {\n if (contentVisibility) {\n rows.sync(items);\n for (let index = 0; index < rows.order.length; index++) {\n const el = rows.order[index].el;\n el.style.contentVisibility = 'auto';\n el.style.containIntrinsicSize = `0 ${intrinsicSizeAt(index)}px`;\n }\n return;\n }\n\n const total = items.length;\n let start: number;\n let end: number;\n let padTop: number;\n let padBottom: number;\n if (minRows !== undefined && total < minRows) {\n if (fixedHeight === null && heightsDirty) {\n rebuildOffsets();\n heightsDirty = false;\n }\n start = 0;\n end = total;\n padTop = 0;\n padBottom = 0;\n } else if (fixedHeight !== null) {\n const viewportBottom = parent.scrollTop + parent.clientHeight;\n start = Math.max(0,Math.floor(parent.scrollTop / fixedHeight) - overscan);\n end = Math.min(total,Math.ceil(viewportBottom / fixedHeight) + overscan);\n padTop = start * fixedHeight;\n padBottom = Math.max(0,total - end) * fixedHeight;\n } else {\n if (heightsDirty) {\n rebuildOffsets();\n heightsDirty = false;\n }\n const viewportBottom = parent.scrollTop + parent.clientHeight;\n start = Math.max(0,findStart(parent.scrollTop,total) - overscan);\n end = Math.min(total,findEnd(viewportBottom,total) + overscan);\n padTop = offsets[start];\n padBottom = offsets[total] - offsets[end];\n }\n rows.sync(items.slice(start,end));\n sizeRows(start);\n container.style.paddingTop = `${padTop}px`;\n container.style.paddingBottom = `${padBottom}px`;\n for (const callback of renderSubscribers) callback();\n };\n\n const render = (nextItems: readonly T[]): void => {\n items = nextItems;\n heightsDirty = true;\n renderCurrent();\n };\n\n const scheduleRender = (): void => {\n if (rafPending) return;\n rafPending = true;\n globalThis.requestAnimationFrame(() => {\n rafPending = false;\n if (disposed) return;\n if (pendingAnchorDelta !== 0) {\n parent.scrollTop += pendingAnchorDelta;\n pendingAnchorDelta = 0;\n }\n renderCurrent();\n });\n };\n\n const resizeObserver = !contentVisibility && globalThis.ResizeObserver !== undefined\n ? new globalThis.ResizeObserver(scheduleRender)\n : undefined;\n\n const start = (): void => {\n if (contentVisibility) return;\n parent.addEventListener('scroll',scheduleRender);\n resizeObserver?.observe(parent);\n };\n\n const setHeight = (rowKey: ListKey,height: number): void => {\n if (!measuring || contentVisibility) return;\n const index = indexByKey.get(rowKey);\n if (index === undefined) return;\n const oldHeight = measured.get(rowKey) ?? estimateAt(index);\n if (height === oldHeight) return;\n measured.set(rowKey,height);\n if (offsets[index + 1] <= parent.scrollTop) pendingAnchorDelta += height - oldHeight;\n heightsDirty = true;\n scheduleRender();\n };\n\n const onRender = (callback: () => void): (() => void) => {\n renderSubscribers.add(callback);\n return () => renderSubscribers.delete(callback);\n };\n\n const dispose = (): void => {\n disposed = true;\n parent.removeEventListener('scroll',scheduleRender);\n resizeObserver?.disconnect();\n renderSubscribers.clear();\n };\n\n return { contentVisibility,render,start,setHeight,onRender,dispose };\n}\n","/**\n * `kerfjs/list` — `bindList`, a keyed list with a live per-row mount and\n * optional viewport virtualization.\n *\n * This is a DELIBERATE second list API, distinct from `each()`. It does two\n * things `each()` structurally cannot:\n * 1. **Per-row reactivity.** Every row is individually `mount()`ed, so a signal\n * the row's `render` reads updates just that row (fine-grained binding or a\n * one-row morph) without touching its siblings — no full-list pass.\n * 2. **Virtualization.** With `{ virtualize: { rowHeight } }` only the rows in\n * the scroll viewport are rendered; padding on the scroll container keeps\n * `scrollHeight` honest. `rowHeight` is a fixed `number` (O(1) windowing), a\n * `(item, index) => number` for **app-declared variable** heights (a prefix\n * sum + binary-search window), or `{ estimate }` for **measured** heights —\n * the app reports real heights via the returned handle's `setHeight` (or the\n * `observeRowHeights` helper) and kerf anchor-corrects `scrollTop`. See\n * `docs/17-list-virtualization.md`.\n *\n * `each()` stays the choice for item-owned-state lists rendered to HTML strings;\n * reach for `bindList` when you need surgical per-row updates or windowing.\n *\n * import { bindList } from 'kerfjs/list';\n *\n * const dispose = bindList(listEl, itemsSignal, {\n * key: (row) => row.id,\n * render: (row) => <span class={selected} data-id={row.id}>{row.label}</span>,\n * tag: 'li',\n * virtualize: { rowHeight: 32 },\n * });\n *\n * `render` reads signals for reactivity (external state like a `selectedId`, or\n * signals the item carries) — keep the item OBJECTS stable across renders and\n * drive structure (add/remove/move) through `itemsSignal`. A row whose item\n * object identity changes is rebuilt (same rule as `each()`'s memo). `bindList`\n * OWNS `parent`'s children by default (append/move to the end) — to share\n * `parent` with fixed trailing siblings (an \"add\" button, an indicator), pass\n * `before` so the rows end just before that node. It reads `itemsSignal.value`,\n * so a plain `signal<T[]>` or an `arraySignal<T>` both work.\n */\nimport { ARRAY_SIGNAL_BRAND, type ArrayPatch } from './array-signal.js';\nimport { createListRowController } from './list-row-controller.js';\nimport { createListVirtualizationController } from './list-virtualization-controller.js';\nimport type { MountResult } from './mount.js';\nimport { effect } from './reactive.js';\n\nconst NOOP = (): void => { /* intentional no-op */ };\n\n/** A row's stable key. */\nexport type ListKey = string | number;\n\n/** Anything with a tracking `.value` array read — a `signal<readonly T[]>` or an `arraySignal<T>`. */\nexport interface ListSource<T> {\n readonly value: readonly T[];\n}\n\n/**\n * A row built imperatively by `render`: return the row **element** itself (kerf\n * keys/moves/reuses it and owns nothing inside it), or `{ el, update?, dispose? }`\n * to also hand back an `update(item)` — called on the SAME element when the row's\n * key persists but its item changes — and a `dispose` that runs only when the row\n * is removed.\n */\nexport type RowElement<T> =\n | HTMLElement\n | { el: HTMLElement; update?: (item: T) => void; dispose?: () => void };\n\n/**\n * The virtualization height model:\n * - **`number`** — every row is this fixed pixel height (O(1) windowing).\n * - **`(item, index) => number`** — app-declared **variable** heights, derived\n * purely from the item and its index.\n * - **`{ estimate }`** — **measured** heights: kerf uses `estimate` for a row\n * until the app reports its real height through {@link BindListHandle.setHeight}\n * (or the `observeRowHeights` helper). See `docs/17-list-virtualization.md`.\n */\nexport type RowHeight<T> =\n | number\n | ((item: T, index: number) => number)\n | { estimate: number | ((item: T, index: number) => number) };\n\n/**\n * The value {@link bindList} returns: a disposer you call to tear the list down,\n * augmented with `setHeight` for the **measured** virtualization mode.\n */\nexport type BindListHandle = (() => void) & {\n /**\n * Report a row's real pixel height (measured after layout) for\n * `virtualize: { rowHeight: { estimate } }` lists. Keyed by the list `key`, so\n * a report survives reorders. kerf recomputes the window and, if the row sits\n * ABOVE the viewport, anchor-corrects `scrollTop` so content doesn't jump.\n * A no-op for fixed / declared-height lists and for unknown keys.\n */\n setHeight: (key: ListKey, height: number) => void;\n /**\n * The inner container element kerf creates to hold the rows in a **virtualized**\n * list (the \"sizer\"). `undefined` for a non-virtualized list (there the rows\n * live directly in `parent`). Use it to style, id, or otherwise reach the row\n * block without guessing at `parent.lastElementChild` — though `containerClass`\n * / `containerId` on `virtualize` set those declaratively.\n */\n container?: HTMLElement;\n};\n\n/** Options for {@link bindList}. */\nexport interface BindListOptions<T> {\n /** Stable, unique per-row key. Duplicate keys are rejected before DOM mutation. */\n key: (item: T) => ListKey;\n /**\n * Build a row. Two modes, chosen per call by what you return:\n * - **Content mode** (a `MountResult` — JSX / `SafeHtml`): kerf creates the\n * row element (`tag`) and `mount()`s your content inside it, so signals your\n * content reads drive per-row reactivity.\n * - **Element mode** (an `HTMLElement`, or `{ el, update?, dispose? }`): the\n * element you return IS the row, so you own its tag, class, `data-*`, and\n * listeners. kerf **keys/moves/reuses** it — the SAME element survives an\n * append/remove/reorder or a fresh item object at the same key. Refresh its\n * content by reading signals inside it, or by returning an `update(item)`\n * that kerf calls on the existing element when the item changes. `dispose`\n * runs only when the row is genuinely removed.\n */\n render: (item: T) => MountResult | RowElement<T>;\n /** Row element tag for **content mode**. Default `'div'` (use `'li'` inside a `<ul>`, `'tr'` inside a `<tbody>`, …). Ignored in element mode. */\n tag?: string;\n /**\n * Keep the rows as a contiguous block that ENDS just before this node, instead\n * of at the very end of `parent`. Use it when `parent` also holds non-row\n * siblings that must stay put — a trailing \"add\" button, a sliding indicator:\n * `before: () => addButton`. The node (a function is re-read each reconcile, or\n * pass the node directly) must be a child of `parent`. Without it, bindList\n * assumes exclusive ownership and appends rows to the end. Ignored when\n * virtualized (the rows own bindList's inner sizer exclusively).\n */\n before?: Node | (() => Node | null);\n /**\n * Turn on viewport virtualization. `parent` must be a scroll container (your\n * CSS: a fixed height + `overflow: auto`). `overscan` (default 3) is how many\n * extra rows to render above and below the viewport.\n *\n * `rowHeight` (a {@link RowHeight}) is the height model:\n * - **`number`** — every row is this fixed pixel height. O(1) windowing, no\n * cumulative model built.\n * - **`(item, index) => number`** — app-declared **variable** heights, derived\n * purely from the item and its index. kerf builds a prefix sum of the\n * heights (rebuilt when the source array changes, not per scroll frame) and\n * binary-searches it to find the visible window. Return a non-negative\n * number of pixels.\n * - **`{ estimate }`** — **measured** heights for rows whose height is only\n * known after layout. kerf sizes an unmeasured row by `estimate` (a number\n * or an `(item, index) => number`), and the app reports each row's real\n * height via {@link BindListHandle.setHeight} (or the `observeRowHeights`\n * helper). kerf anchor-corrects `scrollTop` when an above-viewport row is\n * remeasured, so content doesn't jump.\n *\n * `minRows` renders **every** row (no windowing, zero padding) while the list\n * is shorter than it, and windows only at or above it — the DOM structure (the\n * inner container) is the same either way, so the call site never branches. A\n * fully-rendered short list is friendlier to find-in-page, screen readers, and\n * DOM-count assertions, which only see rows actually in the DOM.\n *\n * `containerClass` / `containerId` are set on the inner container kerf creates\n * to hold the rows, so it's reachable from CSS and tests without guessing at\n * `parent.lastElementChild` (it's also on the handle as `handle.container`).\n *\n * kerf re-windows on `parent`'s `scroll` and, where `ResizeObserver` exists, on\n * `parent` resizing — so a list that mounts before layout (a hidden tab,\n * `clientHeight` 0) fills in once it's sized, and a resized container re-windows.\n *\n * `mode` (default `'window'`) picks the virtualization STRATEGY:\n * - **`'window'`** — the JS windowing above: only the visible rows are in the\n * DOM, bounded node count, works on every engine. Off-window rows are removed\n * (see the findability tradeoff below).\n * - **`'content-visibility'`** — **every** row stays in the DOM and kerf sets\n * `content-visibility: auto` + `contain-intrinsic-size: 0 <rowHeight>px` on\n * each one, so a supporting engine (Chromium, Safari 18) skips the *layout /\n * paint* of off-screen rows while keeping them findable. `rowHeight` here is\n * used **only** as the `contain-intrinsic-size` placeholder (scrollbar\n * accuracy before a row is first rendered) — there is no windowing math, no\n * padding, no scroll listener, no anchor correction, and `setHeight` /\n * `observeRowHeights` are **no-ops** (the browser owns real measurement).\n * `minRows` is ignored (all rows already render). On an engine without\n * `content-visibility` the CSS is simply inert — all rows render, correct and\n * fully findable, only without the skip optimization. Choose this mode for\n * medium lists where find-in-page / a11y / anchor links matter more than the\n * node ceiling; keep `'window'` for very large (100k-row) lists.\n *\n * **Findability tradeoff (`'window'` mode).** Off-window rows are removed from\n * the DOM (not merely hidden), so with `mode: 'window'`: **find-in-page\n * (Cmd/Ctrl+F)**, **screen readers / the a11y tree**, and **anchor links /\n * `scrollIntoView`** only reach the visible window — a match, an announced row,\n * or a linked element that has been windowed out isn't in the DOM to find.\n * Convey the true total via ARIA (`aria-rowcount` / `aria-setsize`) if it\n * matters, and use a non-virtualized list — `minRows` above the list length, or\n * `mode: 'content-visibility'` — when full findability matters more than the DOM\n * node ceiling. See `docs/17-list-virtualization.md` §17.10 / §17.11.\n */\n virtualize?: {\n rowHeight: RowHeight<T>;\n overscan?: number;\n minRows?: number;\n containerClass?: string;\n containerId?: string;\n /**\n * Virtualization strategy. `'window'` (default) removes off-window rows from\n * the DOM; `'content-visibility'` keeps every row in the DOM and lets the\n * browser skip off-screen layout/paint (full find-in-page / a11y, at the cost\n * of an unbounded node count). See the option JSDoc above.\n */\n mode?: 'window' | 'content-visibility';\n };\n}\n\n/** Reject a keyed snapshot before reconciliation can alias two rows in the DOM. */\nfunction assertUniqueListKeys<T>(items: readonly T[], key: (item: T) => ListKey): void {\n const firstIndex = new Map<ListKey,number>();\n for (let index = 0; index < items.length; index++) {\n const rowKey = key(items[index]);\n const first = firstIndex.get(rowKey);\n if (first !== undefined) {\n const displayed = typeof rowKey === 'string' ? JSON.stringify(rowKey) : String(rowKey);\n throw new Error(\n `bindList: duplicate key ${displayed} at indices ${first} and ${index} — every row key must be unique.`,\n );\n }\n firstIndex.set(rowKey,index);\n }\n}\n\n/**\n * Bind a keyed, per-row-reactive list to `parent`, driven by `source` (a\n * `signal<readonly T[]>` or an `arraySignal<T>`). Returns a disposer that tears\n * down every row mount, the scroll listener (if virtualized), and the source\n * subscription.\n */\nexport function bindList<T>(\n parent: HTMLElement,\n source: ListSource<T>,\n options: BindListOptions<T>,\n): BindListHandle {\n const { key,render,tag = 'div',virtualize,before } = options;\n let firstRender = true;\n let forceSnapshot = false;\n\n const patchSource = source as {\n [ARRAY_SIGNAL_BRAND]?: boolean;\n _consumePatches?: () => ArrayPatch<T>[];\n };\n const granularEligible = virtualize === undefined && patchSource[ARRAY_SIGNAL_BRAND] === true;\n\n const container = virtualize === undefined ? parent : document.createElement('div');\n if (virtualize !== undefined) {\n if (virtualize.containerClass !== undefined) container.className = virtualize.containerClass;\n if (virtualize.containerId !== undefined) container.id = virtualize.containerId;\n }\n const endAnchor = (): Node | null => {\n if (virtualize !== undefined || before === undefined) return null;\n return (typeof before === 'function' ? before() : before) ?? null;\n };\n\n const rows = createListRowController({ container,key,render,tag,endAnchor });\n const virtualization = virtualize === undefined\n ? undefined\n : createListVirtualizationController({ parent,container,virtualize,key,rows });\n\n const renderItems = (items: readonly T[]): void => {\n if (virtualization !== undefined) {\n virtualization.render(items);\n return;\n }\n if (granularEligible) {\n const patches = patchSource._consumePatches!();\n if (\n !firstRender\n && !forceSnapshot\n && patches.length > 0\n && !patches.some((patch) => patch.type === 'replace')\n ) {\n try {\n rows.applyPatches(patches);\n } catch (error) {\n forceSnapshot = true;\n throw error;\n }\n return;\n }\n }\n rows.sync(items);\n firstRender = false;\n forceSnapshot = false;\n };\n\n const stopEffect = effect(() => {\n const items = source.value;\n try {\n assertUniqueListKeys(items,key);\n } catch (error) {\n if (granularEligible) {\n patchSource._consumePatches!();\n forceSnapshot = true;\n }\n throw error;\n }\n renderItems(items);\n });\n\n if (virtualization !== undefined) {\n parent.appendChild(container);\n virtualization.start();\n }\n\n const dispose = ((): void => {\n stopEffect();\n rows.dispose(virtualization === undefined);\n if (virtualization !== undefined) {\n virtualization.dispose();\n container.remove();\n VIRTUAL_INTERNALS.delete(handle);\n }\n }) as BindListHandle;\n const handle = dispose;\n handle.setHeight = virtualization?.setHeight ?? NOOP;\n\n if (virtualization !== undefined) {\n handle.container = container;\n if (!virtualization.contentVisibility) {\n VIRTUAL_INTERNALS.set(handle,{\n visibleRows: () => rows.order.map((row) => ({ key: key(row.item),el: row.el })),\n onRender: virtualization.onRender,\n });\n }\n }\n\n return handle;\n}\n\n/** Internal coordination surface between {@link bindList} and {@link observeRowHeights}. */\ninterface VirtualInternals {\n /** The current visible rows, in order, with their keys. */\n visibleRows: () => Array<{ key: ListKey; el: HTMLElement }>;\n /** Subscribe to each window render; returns an unsubscribe. */\n onRender: (cb: () => void) => () => void;\n}\n\n// GC-tied (WeakMap) coordination store — a pure cache, not counted against\n// Design rule 5 (same class as `bindings.ts:insertedTextNodes`).\nconst VIRTUAL_INTERNALS = new WeakMap<object, VirtualInternals>();\n\n/**\n * Drive a **measured** virtualized `bindList` (`virtualize: { rowHeight: {\n * estimate } }`) from real layout: install ONE `ResizeObserver` over the visible\n * rows and forward each row's `offsetHeight` to `handle.setHeight`, re-observing\n * as the window shifts. Returns a disposer.\n *\n * This is the batteries-included measurement path; it is deliberately separate\n * from `bindList` (which never depends on `ResizeObserver`) — you can measure\n * however you like and call `handle.setHeight` yourself instead. A no-op for a\n * non-virtualized handle or where `ResizeObserver` is unavailable (SSR).\n *\n * const list = bindList(scrollEl, source, { key, render, virtualize: { rowHeight: { estimate: 64 } } });\n * const stopMeasuring = observeRowHeights(list);\n */\nexport function observeRowHeights(handle: BindListHandle): () => void {\n const internals = VIRTUAL_INTERNALS.get(handle);\n const RO = globalThis.ResizeObserver;\n if (internals === undefined || RO === undefined) return NOOP;\n\n const keyByEl = new WeakMap<Element, ListKey>();\n const observer = new RO((entries) => {\n for (const entry of entries) {\n const k = keyByEl.get(entry.target);\n if (k !== undefined) handle.setHeight(k, (entry.target as HTMLElement).offsetHeight);\n }\n });\n\n const resync = (): void => {\n observer.disconnect();\n for (const { key: k, el } of internals.visibleRows()) {\n keyByEl.set(el, k);\n observer.observe(el);\n }\n };\n\n const unsubscribe = internals.onRender(resync);\n resync(); // observe the initial window\n\n return () => {\n observer.disconnect();\n unsubscribe();\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/list-row-controller.ts","../src/list-virtualization-controller.ts","../src/list.ts"],"names":["dispose","start","NOOP"],"mappings":"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","file":"list.js","sourcesContent":["import type { ArrayPatch } from './array-signal.js';\nimport type { BindListOptions, ListKey, RowElement } from './list.js';\nimport { captureFocus, restoreFocus } from './list-reconcile-focus.js';\nimport { mount, type MountResult } from './mount.js';\nimport { moveNode } from './utils/moveNode.js';\n\ninterface ListRow<T> {\n el: HTMLElement;\n item: T;\n dispose: () => void;\n elementMode: boolean;\n update?: (item: T) => void;\n}\n\nexport interface ListRowController<T> {\n readonly order: Array<ListRow<T>>;\n sync: (visible: readonly T[]) => void;\n applyPatches: (patches: readonly ArrayPatch<T>[]) => void;\n dispose: (removeElements: boolean) => void;\n}\n\ninterface ListRowControllerOptions<T> {\n container: HTMLElement;\n key: (item: T) => ListKey;\n render: BindListOptions<T>['render'];\n tag: string;\n endAnchor: () => Node | null;\n}\n\nconst NOOP = (): void => {\n /* element-mode rows with no caller teardown */\n};\n\n/** Own the keyed row map, DOM order, row lifecycles, and both reconcile paths. */\nexport function createListRowController<T>(\n options: ListRowControllerOptions<T>,\n): ListRowController<T> {\n const { container, key, render, tag, endAnchor } = options;\n const rows = new Map<ListKey, ListRow<T>>();\n const order: Array<ListRow<T>> = [];\n\n const asElementRow = (\n rendered: MountResult | RowElement<T>,\n ): {\n el: HTMLElement;\n dispose: () => void;\n update?: (item: T) => void;\n } | null => {\n if (rendered instanceof HTMLElement) return { el: rendered, dispose: NOOP };\n if (\n rendered !== null &&\n typeof rendered === 'object' &&\n 'el' in rendered &&\n (rendered as { el: unknown }).el instanceof HTMLElement\n ) {\n const row = rendered as {\n el: HTMLElement;\n update?: (item: T) => void;\n dispose?: () => void;\n };\n return { el: row.el, dispose: row.dispose ?? NOOP, update: row.update };\n }\n return null;\n };\n\n const makeRow = (item: T): ListRow<T> => {\n const elementRow = asElementRow(render(item));\n if (elementRow !== null) {\n return {\n el: elementRow.el,\n item,\n dispose: elementRow.dispose,\n elementMode: true,\n update: elementRow.update,\n };\n }\n const el = document.createElement(tag);\n const dispose = mount(el, () => render(item) as MountResult);\n return { el, item, dispose, elementMode: false };\n };\n\n // The single item-replacement contract used by snapshot and granular paths.\n // Element-mode rows keep their caller-owned element and are re-keyed; content\n // rows replace their mount. DOM placement remains the caller's responsibility.\n const reconcileItem = (row: ListRow<T>, item: T): ListRow<T> => {\n if (row.item === item) return row;\n const oldKey = key(row.item);\n const newKey = key(item);\n if (row.elementMode) {\n row.item = item;\n if (newKey !== oldKey) {\n rows.delete(oldKey);\n rows.set(newKey, row);\n }\n row.update?.(item);\n return row;\n }\n row.dispose();\n row.el.remove();\n rows.delete(oldKey);\n const fresh = makeRow(item);\n rows.set(newKey, fresh);\n return fresh;\n };\n\n const preserveFocus = (operation: () => void): void => {\n const focus = captureFocus(container);\n try {\n operation();\n } finally {\n if (focus !== null) restoreFocus(focus);\n }\n };\n\n const sync = (visible: readonly T[]): void => {\n preserveFocus(() => {\n const wanted = new Set<ListKey>();\n for (const item of visible) wanted.add(key(item));\n\n for (const [rowKey, row] of rows) {\n if (!wanted.has(rowKey)) {\n row.dispose();\n row.el.remove();\n rows.delete(rowKey);\n }\n }\n\n order.length = 0;\n for (const item of visible) {\n const rowKey = key(item);\n const existing = rows.get(rowKey);\n const row =\n existing === undefined\n ? makeRow(item)\n : reconcileItem(existing, item);\n if (existing === undefined) rows.set(rowKey, row);\n order.push(row);\n }\n\n let ref: Node | null = endAnchor();\n for (let index = order.length - 1; index >= 0; index--) {\n const el = order[index].el;\n if (el.parentNode !== container || el.nextSibling !== ref)\n moveNode(container, el, ref);\n ref = el;\n }\n });\n };\n\n const applyPatches = (patches: readonly ArrayPatch<T>[]): void => {\n preserveFocus(() => {\n for (const patch of patches) {\n if (patch.type === 'insert') {\n const row = makeRow(patch.item);\n rows.set(key(patch.item), row);\n order.splice(patch.index, 0, row);\n container.insertBefore(\n row.el,\n order[patch.index + 1]?.el ?? endAnchor(),\n );\n } else if (patch.type === 'remove') {\n const [row] = order.splice(patch.index, 1);\n row.dispose();\n row.el.remove();\n rows.delete(key(row.item));\n } else if (patch.type === 'move') {\n const [row] = order.splice(patch.from, 1);\n order.splice(patch.to, 0, row);\n moveNode(container, row.el, order[patch.to + 1]?.el ?? endAnchor());\n } else if (patch.type === 'update') {\n const current = order[patch.index];\n const row = reconcileItem(current, patch.item);\n if (row !== current) {\n order[patch.index] = row;\n container.insertBefore(\n row.el,\n order[patch.index + 1]?.el ?? endAnchor(),\n );\n }\n }\n }\n });\n };\n\n const dispose = (removeElements: boolean): void => {\n for (const row of rows.values()) {\n row.dispose();\n if (removeElements) row.el.remove();\n }\n rows.clear();\n order.length = 0;\n };\n\n return { order, sync, applyPatches, dispose };\n}\n","import type { BindListOptions, ListKey } from './list.js';\nimport type { ListRowController } from './list-row-controller.js';\n\ninterface ListVirtualizationController<T> {\n readonly contentVisibility: boolean;\n render: (items: readonly T[]) => void;\n start: () => void;\n setHeight: (key: ListKey, height: number) => void;\n onRender: (callback: () => void) => () => void;\n dispose: () => void;\n}\n\ninterface ListVirtualizationControllerOptions<T> {\n parent: HTMLElement;\n container: HTMLElement;\n virtualize: NonNullable<BindListOptions<T>['virtualize']>;\n key: (item: T) => ListKey;\n rows: ListRowController<T>;\n}\n\n/** Own window calculation, height models, scheduling, observers, and CSS strategy. */\nexport function createListVirtualizationController<T>(\n options: ListVirtualizationControllerOptions<T>,\n): ListVirtualizationController<T> {\n const { parent, container, virtualize, key, rows } = options;\n const overscan = virtualize.overscan ?? 3;\n const { minRows, rowHeight } = virtualize;\n const contentVisibility = virtualize.mode === 'content-visibility';\n const fixedHeight = typeof rowHeight === 'number' ? rowHeight : null;\n const measuring = typeof rowHeight === 'object' && rowHeight !== null;\n const measured = new Map<ListKey, number>();\n const indexByKey = new Map<ListKey, number>();\n const renderSubscribers = new Set<() => void>();\n let items: readonly T[] = [];\n let offsets: number[] = [0];\n let heightsDirty = true;\n let pendingAnchorDelta = 0;\n let rafPending = false;\n let disposed = false;\n\n const estimateAt = (index: number): number => {\n const estimate = (\n rowHeight as { estimate: number | ((item: T, index: number) => number) }\n ).estimate;\n return typeof estimate === 'function'\n ? estimate(items[index], index)\n : estimate;\n };\n const variableHeightAt: ((index: number) => number) | null =\n fixedHeight !== null\n ? null\n : measuring\n ? (index): number =>\n measured.get(key(items[index])) ?? estimateAt(index)\n : (index): number =>\n (rowHeight as (item: T, index: number) => number)(\n items[index],\n index,\n );\n const intrinsicSizeAt = (index: number): number =>\n fixedHeight ?? (variableHeightAt as (index: number) => number)(index);\n\n const rebuildOffsets = (): void => {\n const heightAt = variableHeightAt as (index: number) => number;\n offsets = new Array<number>(items.length + 1);\n offsets[0] = 0;\n if (measuring) indexByKey.clear();\n for (let index = 0; index < items.length; index++) {\n offsets[index + 1] = offsets[index] + heightAt(index);\n if (measuring) indexByKey.set(key(items[index]), index);\n }\n if (measuring) {\n for (const rowKey of measured.keys()) {\n if (!indexByKey.has(rowKey)) measured.delete(rowKey);\n }\n }\n };\n\n const findStart = (target: number, total: number): number => {\n let low = 0;\n let high = total;\n while (low < high) {\n const middle = (low + high + 1) >> 1;\n if (offsets[middle] <= target) low = middle;\n else high = middle - 1;\n }\n return low;\n };\n\n const findEnd = (target: number, total: number): number => {\n let low = 0;\n let high = total;\n while (low < high) {\n const middle = (low + high) >> 1;\n if (offsets[middle] >= target) high = middle;\n else low = middle + 1;\n }\n return low;\n };\n\n const sizeRows = (start: number): void => {\n if (measuring) return;\n for (let index = 0; index < rows.order.length; index++) {\n const absoluteIndex = start + index;\n const height =\n fixedHeight ?? offsets[absoluteIndex + 1] - offsets[absoluteIndex];\n rows.order[index].el.style.height = `${height}px`;\n }\n };\n\n const renderCurrent = (): void => {\n if (contentVisibility) {\n rows.sync(items);\n for (let index = 0; index < rows.order.length; index++) {\n const el = rows.order[index].el;\n el.style.contentVisibility = 'auto';\n el.style.containIntrinsicSize = `0 ${intrinsicSizeAt(index)}px`;\n }\n return;\n }\n\n const total = items.length;\n let start: number;\n let end: number;\n let padTop: number;\n let padBottom: number;\n if (minRows !== undefined && total < minRows) {\n if (fixedHeight === null && heightsDirty) {\n rebuildOffsets();\n heightsDirty = false;\n }\n start = 0;\n end = total;\n padTop = 0;\n padBottom = 0;\n } else if (fixedHeight !== null) {\n const viewportBottom = parent.scrollTop + parent.clientHeight;\n start = Math.max(\n 0,\n Math.floor(parent.scrollTop / fixedHeight) - overscan,\n );\n end = Math.min(total, Math.ceil(viewportBottom / fixedHeight) + overscan);\n padTop = start * fixedHeight;\n padBottom = Math.max(0, total - end) * fixedHeight;\n } else {\n if (heightsDirty) {\n rebuildOffsets();\n heightsDirty = false;\n }\n const viewportBottom = parent.scrollTop + parent.clientHeight;\n start = Math.max(0, findStart(parent.scrollTop, total) - overscan);\n end = Math.min(total, findEnd(viewportBottom, total) + overscan);\n padTop = offsets[start];\n padBottom = offsets[total] - offsets[end];\n }\n rows.sync(items.slice(start, end));\n sizeRows(start);\n container.style.paddingTop = `${padTop}px`;\n container.style.paddingBottom = `${padBottom}px`;\n for (const callback of renderSubscribers) callback();\n };\n\n const render = (nextItems: readonly T[]): void => {\n items = nextItems;\n heightsDirty = true;\n renderCurrent();\n };\n\n const scheduleRender = (): void => {\n if (rafPending) return;\n rafPending = true;\n globalThis.requestAnimationFrame(() => {\n rafPending = false;\n if (disposed) return;\n if (pendingAnchorDelta !== 0) {\n parent.scrollTop += pendingAnchorDelta;\n pendingAnchorDelta = 0;\n }\n renderCurrent();\n });\n };\n\n const resizeObserver =\n !contentVisibility && globalThis.ResizeObserver !== undefined\n ? new globalThis.ResizeObserver(scheduleRender)\n : undefined;\n\n const start = (): void => {\n if (contentVisibility) return;\n parent.addEventListener('scroll', scheduleRender);\n resizeObserver?.observe(parent);\n };\n\n const setHeight = (rowKey: ListKey, height: number): void => {\n if (!measuring || contentVisibility) return;\n const index = indexByKey.get(rowKey);\n if (index === undefined) return;\n const oldHeight = measured.get(rowKey) ?? estimateAt(index);\n if (height === oldHeight) return;\n measured.set(rowKey, height);\n if (offsets[index + 1] <= parent.scrollTop)\n pendingAnchorDelta += height - oldHeight;\n heightsDirty = true;\n scheduleRender();\n };\n\n const onRender = (callback: () => void): (() => void) => {\n renderSubscribers.add(callback);\n return () => renderSubscribers.delete(callback);\n };\n\n const dispose = (): void => {\n disposed = true;\n parent.removeEventListener('scroll', scheduleRender);\n resizeObserver?.disconnect();\n renderSubscribers.clear();\n };\n\n return { contentVisibility, render, start, setHeight, onRender, dispose };\n}\n","/**\n * `kerfjs/list` — `bindList`, a keyed list with a live per-row mount and\n * optional viewport virtualization.\n *\n * This is a DELIBERATE second list API, distinct from `each()`. It does two\n * things `each()` structurally cannot:\n * 1. **Per-row reactivity.** Every row is individually `mount()`ed, so a signal\n * the row's `render` reads updates just that row (fine-grained binding or a\n * one-row morph) without touching its siblings — no full-list pass.\n * 2. **Virtualization.** With `{ virtualize: { rowHeight } }` only the rows in\n * the scroll viewport are rendered; padding on the scroll container keeps\n * `scrollHeight` honest. `rowHeight` is a fixed `number` (O(1) windowing), a\n * `(item, index) => number` for **app-declared variable** heights (a prefix\n * sum + binary-search window), or `{ estimate }` for **measured** heights —\n * the app reports real heights via the returned handle's `setHeight` (or the\n * `observeRowHeights` helper) and kerf anchor-corrects `scrollTop`. See\n * `docs/17-list-virtualization.md`.\n *\n * `each()` stays the choice for item-owned-state lists rendered to HTML strings;\n * reach for `bindList` when you need surgical per-row updates or windowing.\n *\n * import { bindList } from 'kerfjs/list';\n *\n * const dispose = bindList(listEl, itemsSignal, {\n * key: (row) => row.id,\n * render: (row) => <span class={selected} data-id={row.id}>{row.label}</span>,\n * tag: 'li',\n * virtualize: { rowHeight: 32 },\n * });\n *\n * `render` reads signals for reactivity (external state like a `selectedId`, or\n * signals the item carries) — keep the item OBJECTS stable across renders and\n * drive structure (add/remove/move) through `itemsSignal`. A row whose item\n * object identity changes is rebuilt (same rule as `each()`'s memo). `bindList`\n * OWNS `parent`'s children by default (append/move to the end) — to share\n * `parent` with fixed trailing siblings (an \"add\" button, an indicator), pass\n * `before` so the rows end just before that node. It reads `itemsSignal.value`,\n * so a plain `signal<T[]>` or an `arraySignal<T>` both work.\n */\nimport { ARRAY_SIGNAL_BRAND, type ArrayPatch } from './array-signal.js';\nimport { createListRowController } from './list-row-controller.js';\nimport { createListVirtualizationController } from './list-virtualization-controller.js';\nimport type { MountResult } from './mount.js';\nimport { effect } from './reactive.js';\n\nconst NOOP = (): void => {\n /* intentional no-op */\n};\n\n/** A row's stable key. */\nexport type ListKey = string | number;\n\n/** Anything with a tracking `.value` array read — a `signal<readonly T[]>` or an `arraySignal<T>`. */\nexport interface ListSource<T> {\n readonly value: readonly T[];\n}\n\n/**\n * A row built imperatively by `render`: return the row **element** itself (kerf\n * keys/moves/reuses it and owns nothing inside it), or `{ el, update?, dispose? }`\n * to also hand back an `update(item)` — called on the SAME element when the row's\n * key persists but its item changes — and a `dispose` that runs only when the row\n * is removed.\n */\nexport type RowElement<T> =\n | HTMLElement\n | { el: HTMLElement; update?: (item: T) => void; dispose?: () => void };\n\n/**\n * The virtualization height model:\n * - **`number`** — every row is this fixed pixel height (O(1) windowing).\n * - **`(item, index) => number`** — app-declared **variable** heights, derived\n * purely from the item and its index.\n * - **`{ estimate }`** — **measured** heights: kerf uses `estimate` for a row\n * until the app reports its real height through {@link BindListHandle.setHeight}\n * (or the `observeRowHeights` helper). See `docs/17-list-virtualization.md`.\n */\nexport type RowHeight<T> =\n | number\n | ((item: T, index: number) => number)\n | { estimate: number | ((item: T, index: number) => number) };\n\n/**\n * The value {@link bindList} returns: a disposer you call to tear the list down,\n * augmented with `setHeight` for the **measured** virtualization mode.\n */\nexport type BindListHandle = (() => void) & {\n /**\n * Report a row's real pixel height (measured after layout) for\n * `virtualize: { rowHeight: { estimate } }` lists. Keyed by the list `key`, so\n * a report survives reorders. kerf recomputes the window and, if the row sits\n * ABOVE the viewport, anchor-corrects `scrollTop` so content doesn't jump.\n * A no-op for fixed / declared-height lists and for unknown keys.\n */\n setHeight: (key: ListKey, height: number) => void;\n /**\n * The inner container element kerf creates to hold the rows in a **virtualized**\n * list (the \"sizer\"). `undefined` for a non-virtualized list (there the rows\n * live directly in `parent`). Use it to style, id, or otherwise reach the row\n * block without guessing at `parent.lastElementChild` — though `containerClass`\n * / `containerId` on `virtualize` set those declaratively.\n */\n container?: HTMLElement;\n};\n\n/** Options for {@link bindList}. */\nexport interface BindListOptions<T> {\n /** Stable, unique per-row key. Duplicate keys are rejected before DOM mutation. */\n key: (item: T) => ListKey;\n /**\n * Build a row. Two modes, chosen per call by what you return:\n * - **Content mode** (a `MountResult` — JSX / `SafeHtml`): kerf creates the\n * row element (`tag`) and `mount()`s your content inside it, so signals your\n * content reads drive per-row reactivity.\n * - **Element mode** (an `HTMLElement`, or `{ el, update?, dispose? }`): the\n * element you return IS the row, so you own its tag, class, `data-*`, and\n * listeners. kerf **keys/moves/reuses** it — the SAME element survives an\n * append/remove/reorder or a fresh item object at the same key. Refresh its\n * content by reading signals inside it, or by returning an `update(item)`\n * that kerf calls on the existing element when the item changes. `dispose`\n * runs only when the row is genuinely removed.\n */\n render: (item: T) => MountResult | RowElement<T>;\n /** Row element tag for **content mode**. Default `'div'` (use `'li'` inside a `<ul>`, `'tr'` inside a `<tbody>`, …). Ignored in element mode. */\n tag?: string;\n /**\n * Keep the rows as a contiguous block that ENDS just before this node, instead\n * of at the very end of `parent`. Use it when `parent` also holds non-row\n * siblings that must stay put — a trailing \"add\" button, a sliding indicator:\n * `before: () => addButton`. The node (a function is re-read each reconcile, or\n * pass the node directly) must be a child of `parent`. Without it, bindList\n * assumes exclusive ownership and appends rows to the end. Ignored when\n * virtualized (the rows own bindList's inner sizer exclusively).\n */\n before?: Node | (() => Node | null);\n /**\n * Turn on viewport virtualization. `parent` must be a scroll container (your\n * CSS: a fixed height + `overflow: auto`). `overscan` (default 3) is how many\n * extra rows to render above and below the viewport.\n *\n * `rowHeight` (a {@link RowHeight}) is the height model:\n * - **`number`** — every row is this fixed pixel height. O(1) windowing, no\n * cumulative model built.\n * - **`(item, index) => number`** — app-declared **variable** heights, derived\n * purely from the item and its index. kerf builds a prefix sum of the\n * heights (rebuilt when the source array changes, not per scroll frame) and\n * binary-searches it to find the visible window. Return a non-negative\n * number of pixels.\n * - **`{ estimate }`** — **measured** heights for rows whose height is only\n * known after layout. kerf sizes an unmeasured row by `estimate` (a number\n * or an `(item, index) => number`), and the app reports each row's real\n * height via {@link BindListHandle.setHeight} (or the `observeRowHeights`\n * helper). kerf anchor-corrects `scrollTop` when an above-viewport row is\n * remeasured, so content doesn't jump.\n *\n * `minRows` renders **every** row (no windowing, zero padding) while the list\n * is shorter than it, and windows only at or above it — the DOM structure (the\n * inner container) is the same either way, so the call site never branches. A\n * fully-rendered short list is friendlier to find-in-page, screen readers, and\n * DOM-count assertions, which only see rows actually in the DOM.\n *\n * `containerClass` / `containerId` are set on the inner container kerf creates\n * to hold the rows, so it's reachable from CSS and tests without guessing at\n * `parent.lastElementChild` (it's also on the handle as `handle.container`).\n *\n * kerf re-windows on `parent`'s `scroll` and, where `ResizeObserver` exists, on\n * `parent` resizing — so a list that mounts before layout (a hidden tab,\n * `clientHeight` 0) fills in once it's sized, and a resized container re-windows.\n *\n * `mode` (default `'window'`) picks the virtualization STRATEGY:\n * - **`'window'`** — the JS windowing above: only the visible rows are in the\n * DOM, bounded node count, works on every engine. Off-window rows are removed\n * (see the findability tradeoff below).\n * - **`'content-visibility'`** — **every** row stays in the DOM and kerf sets\n * `content-visibility: auto` + `contain-intrinsic-size: 0 <rowHeight>px` on\n * each one, so a supporting engine (Chromium, Safari 18) skips the *layout /\n * paint* of off-screen rows while keeping them findable. `rowHeight` here is\n * used **only** as the `contain-intrinsic-size` placeholder (scrollbar\n * accuracy before a row is first rendered) — there is no windowing math, no\n * padding, no scroll listener, no anchor correction, and `setHeight` /\n * `observeRowHeights` are **no-ops** (the browser owns real measurement).\n * `minRows` is ignored (all rows already render). On an engine without\n * `content-visibility` the CSS is simply inert — all rows render, correct and\n * fully findable, only without the skip optimization. Choose this mode for\n * medium lists where find-in-page / a11y / anchor links matter more than the\n * node ceiling; keep `'window'` for very large (100k-row) lists.\n *\n * **Findability tradeoff (`'window'` mode).** Off-window rows are removed from\n * the DOM (not merely hidden), so with `mode: 'window'`: **find-in-page\n * (Cmd/Ctrl+F)**, **screen readers / the a11y tree**, and **anchor links /\n * `scrollIntoView`** only reach the visible window — a match, an announced row,\n * or a linked element that has been windowed out isn't in the DOM to find.\n * Convey the true total via ARIA (`aria-rowcount` / `aria-setsize`) if it\n * matters, and use a non-virtualized list — `minRows` above the list length, or\n * `mode: 'content-visibility'` — when full findability matters more than the DOM\n * node ceiling. See `docs/17-list-virtualization.md` §17.10 / §17.11.\n */\n virtualize?: {\n rowHeight: RowHeight<T>;\n overscan?: number;\n minRows?: number;\n containerClass?: string;\n containerId?: string;\n /**\n * Virtualization strategy. `'window'` (default) removes off-window rows from\n * the DOM; `'content-visibility'` keeps every row in the DOM and lets the\n * browser skip off-screen layout/paint (full find-in-page / a11y, at the cost\n * of an unbounded node count). See the option JSDoc above.\n */\n mode?: 'window' | 'content-visibility';\n };\n}\n\n/** Reject a keyed snapshot before reconciliation can alias two rows in the DOM. */\nfunction assertUniqueListKeys<T>(\n items: readonly T[],\n key: (item: T) => ListKey,\n): void {\n const firstIndex = new Map<ListKey, number>();\n for (let index = 0; index < items.length; index++) {\n const rowKey = key(items[index]);\n const first = firstIndex.get(rowKey);\n if (first !== undefined) {\n const displayed =\n typeof rowKey === 'string' ? JSON.stringify(rowKey) : String(rowKey);\n throw new Error(\n `bindList: duplicate key ${displayed} at indices ${first} and ${index} — every row key must be unique.`,\n );\n }\n firstIndex.set(rowKey, index);\n }\n}\n\n/**\n * Bind a keyed, per-row-reactive list to `parent`, driven by `source` (a\n * `signal<readonly T[]>` or an `arraySignal<T>`). Returns a disposer that tears\n * down every row mount, the scroll listener (if virtualized), and the source\n * subscription.\n */\nexport function bindList<T>(\n parent: HTMLElement,\n source: ListSource<T>,\n options: BindListOptions<T>,\n): BindListHandle {\n const { key, render, tag = 'div', virtualize, before } = options;\n let firstRender = true;\n let forceSnapshot = false;\n\n const patchSource = source as {\n [ARRAY_SIGNAL_BRAND]?: boolean;\n _consumePatches?: () => ArrayPatch<T>[];\n };\n const granularEligible =\n virtualize === undefined && patchSource[ARRAY_SIGNAL_BRAND] === true;\n\n const container =\n virtualize === undefined ? parent : document.createElement('div');\n if (virtualize !== undefined) {\n if (virtualize.containerClass !== undefined)\n container.className = virtualize.containerClass;\n if (virtualize.containerId !== undefined)\n container.id = virtualize.containerId;\n }\n const endAnchor = (): Node | null => {\n if (virtualize !== undefined || before === undefined) return null;\n return (typeof before === 'function' ? before() : before) ?? null;\n };\n\n const rows = createListRowController({\n container,\n key,\n render,\n tag,\n endAnchor,\n });\n const virtualization =\n virtualize === undefined\n ? undefined\n : createListVirtualizationController({\n parent,\n container,\n virtualize,\n key,\n rows,\n });\n\n const renderItems = (items: readonly T[]): void => {\n if (virtualization !== undefined) {\n virtualization.render(items);\n return;\n }\n if (granularEligible) {\n const patches = patchSource._consumePatches!();\n if (\n !firstRender &&\n !forceSnapshot &&\n patches.length > 0 &&\n !patches.some((patch) => patch.type === 'replace')\n ) {\n try {\n rows.applyPatches(patches);\n } catch (error) {\n forceSnapshot = true;\n throw error;\n }\n return;\n }\n }\n rows.sync(items);\n firstRender = false;\n forceSnapshot = false;\n };\n\n const stopEffect = effect(() => {\n const items = source.value;\n try {\n assertUniqueListKeys(items, key);\n } catch (error) {\n if (granularEligible) {\n patchSource._consumePatches!();\n forceSnapshot = true;\n }\n throw error;\n }\n renderItems(items);\n });\n\n if (virtualization !== undefined) {\n parent.appendChild(container);\n virtualization.start();\n }\n\n const dispose = ((): void => {\n stopEffect();\n rows.dispose(virtualization === undefined);\n if (virtualization !== undefined) {\n virtualization.dispose();\n container.remove();\n VIRTUAL_INTERNALS.delete(handle);\n }\n }) as BindListHandle;\n const handle = dispose;\n handle.setHeight = virtualization?.setHeight ?? NOOP;\n\n if (virtualization !== undefined) {\n handle.container = container;\n if (!virtualization.contentVisibility) {\n VIRTUAL_INTERNALS.set(handle, {\n visibleRows: () =>\n rows.order.map((row) => ({ key: key(row.item), el: row.el })),\n onRender: virtualization.onRender,\n });\n }\n }\n\n return handle;\n}\n\n/** Internal coordination surface between {@link bindList} and {@link observeRowHeights}. */\ninterface VirtualInternals {\n /** The current visible rows, in order, with their keys. */\n visibleRows: () => Array<{ key: ListKey; el: HTMLElement }>;\n /** Subscribe to each window render; returns an unsubscribe. */\n onRender: (cb: () => void) => () => void;\n}\n\n// GC-tied (WeakMap) coordination store — a pure cache, not counted against\n// Design rule 5 (same class as `bindings.ts:insertedTextNodes`).\nconst VIRTUAL_INTERNALS = new WeakMap<object, VirtualInternals>();\n\n/**\n * Drive a **measured** virtualized `bindList` (`virtualize: { rowHeight: {\n * estimate } }`) from real layout: install ONE `ResizeObserver` over the visible\n * rows and forward each row's `offsetHeight` to `handle.setHeight`, re-observing\n * as the window shifts. Returns a disposer.\n *\n * This is the batteries-included measurement path; it is deliberately separate\n * from `bindList` (which never depends on `ResizeObserver`) — you can measure\n * however you like and call `handle.setHeight` yourself instead. A no-op for a\n * non-virtualized handle or where `ResizeObserver` is unavailable (SSR).\n *\n * const list = bindList(scrollEl, source, { key, render, virtualize: { rowHeight: { estimate: 64 } } });\n * const stopMeasuring = observeRowHeights(list);\n */\nexport function observeRowHeights(handle: BindListHandle): () => void {\n const internals = VIRTUAL_INTERNALS.get(handle);\n const RO = globalThis.ResizeObserver;\n if (internals === undefined || RO === undefined) return NOOP;\n\n const keyByEl = new WeakMap<Element, ListKey>();\n const observer = new RO((entries) => {\n for (const entry of entries) {\n const k = keyByEl.get(entry.target);\n if (k !== undefined)\n handle.setHeight(k, (entry.target as HTMLElement).offsetHeight);\n }\n });\n\n const resync = (): void => {\n observer.disconnect();\n for (const { key: k, el } of internals.visibleRows()) {\n keyByEl.set(el, k);\n observer.observe(el);\n }\n };\n\n const unsubscribe = internals.onRender(resync);\n resync(); // observe the initial window\n\n return () => {\n observer.disconnect();\n unsubscribe();\n };\n}\n"]}