@llui/dom 0.0.20 → 0.0.21

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 (105) hide show
  1. package/README.md +16 -14
  2. package/dist/addressed.d.ts +1 -1
  3. package/dist/addressed.d.ts.map +1 -1
  4. package/dist/addressed.js.map +1 -1
  5. package/dist/binding.d.ts +2 -2
  6. package/dist/binding.d.ts.map +1 -1
  7. package/dist/binding.js +2 -2
  8. package/dist/binding.js.map +1 -1
  9. package/dist/devtools.d.ts +3 -3
  10. package/dist/devtools.d.ts.map +1 -1
  11. package/dist/devtools.js +14 -12
  12. package/dist/devtools.js.map +1 -1
  13. package/dist/el-split.js +3 -3
  14. package/dist/el-split.js.map +1 -1
  15. package/dist/el-template.js +3 -3
  16. package/dist/el-template.js.map +1 -1
  17. package/dist/elements.js +1 -1
  18. package/dist/elements.js.map +1 -1
  19. package/dist/hmr.d.ts +1 -1
  20. package/dist/hmr.d.ts.map +1 -1
  21. package/dist/hmr.js +6 -6
  22. package/dist/hmr.js.map +1 -1
  23. package/dist/index.d.ts +5 -2
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +2 -0
  26. package/dist/index.js.map +1 -1
  27. package/dist/internal.d.ts +1 -1
  28. package/dist/internal.d.ts.map +1 -1
  29. package/dist/internal.js +1 -1
  30. package/dist/internal.js.map +1 -1
  31. package/dist/lifetime.d.ts +39 -0
  32. package/dist/lifetime.d.ts.map +1 -0
  33. package/dist/lifetime.js +250 -0
  34. package/dist/lifetime.js.map +1 -0
  35. package/dist/mathml-elements.js +1 -1
  36. package/dist/mathml-elements.js.map +1 -1
  37. package/dist/mount.d.ts +11 -9
  38. package/dist/mount.d.ts.map +1 -1
  39. package/dist/mount.js +15 -31
  40. package/dist/mount.js.map +1 -1
  41. package/dist/primitives/branch.d.ts.map +1 -1
  42. package/dist/primitives/branch.js +30 -20
  43. package/dist/primitives/branch.js.map +1 -1
  44. package/dist/primitives/child.js +12 -12
  45. package/dist/primitives/child.js.map +1 -1
  46. package/dist/primitives/context.js +6 -6
  47. package/dist/primitives/context.js.map +1 -1
  48. package/dist/primitives/each.d.ts.map +1 -1
  49. package/dist/primitives/each.js +37 -32
  50. package/dist/primitives/each.js.map +1 -1
  51. package/dist/primitives/error-boundary.js +7 -7
  52. package/dist/primitives/error-boundary.js.map +1 -1
  53. package/dist/primitives/foreign.js +3 -3
  54. package/dist/primitives/foreign.js.map +1 -1
  55. package/dist/primitives/lazy.js +11 -11
  56. package/dist/primitives/lazy.js.map +1 -1
  57. package/dist/primitives/on-mount.js +2 -2
  58. package/dist/primitives/on-mount.js.map +1 -1
  59. package/dist/primitives/portal.js +4 -4
  60. package/dist/primitives/portal.js.map +1 -1
  61. package/dist/primitives/sample.d.ts +18 -0
  62. package/dist/primitives/sample.d.ts.map +1 -0
  63. package/dist/primitives/sample.js +22 -0
  64. package/dist/primitives/sample.js.map +1 -0
  65. package/dist/primitives/scope.d.ts +25 -0
  66. package/dist/primitives/scope.d.ts.map +1 -0
  67. package/dist/primitives/scope.js +36 -0
  68. package/dist/primitives/scope.js.map +1 -0
  69. package/dist/primitives/selector.js +4 -4
  70. package/dist/primitives/selector.js.map +1 -1
  71. package/dist/primitives/show.d.ts.map +1 -1
  72. package/dist/primitives/show.js +4 -1
  73. package/dist/primitives/show.js.map +1 -1
  74. package/dist/primitives/slice.d.ts.map +1 -1
  75. package/dist/primitives/slice.js +10 -1
  76. package/dist/primitives/slice.js.map +1 -1
  77. package/dist/primitives/text.js +3 -3
  78. package/dist/primitives/text.js.map +1 -1
  79. package/dist/primitives/virtual-each.d.ts.map +1 -1
  80. package/dist/primitives/virtual-each.js +9 -9
  81. package/dist/primitives/virtual-each.js.map +1 -1
  82. package/dist/render-context.d.ts +2 -2
  83. package/dist/render-context.d.ts.map +1 -1
  84. package/dist/render-context.js.map +1 -1
  85. package/dist/ssr.d.ts +4 -4
  86. package/dist/ssr.d.ts.map +1 -1
  87. package/dist/ssr.js +3 -3
  88. package/dist/ssr.js.map +1 -1
  89. package/dist/svg-elements.js +1 -1
  90. package/dist/svg-elements.js.map +1 -1
  91. package/dist/tracking/disposer-log.d.ts +4 -4
  92. package/dist/tracking/disposer-log.d.ts.map +1 -1
  93. package/dist/tracking/disposer-log.js.map +1 -1
  94. package/dist/types.d.ts +50 -9
  95. package/dist/types.d.ts.map +1 -1
  96. package/dist/types.js.map +1 -1
  97. package/dist/update-loop.d.ts +3 -3
  98. package/dist/update-loop.d.ts.map +1 -1
  99. package/dist/update-loop.js +12 -8
  100. package/dist/update-loop.js.map +1 -1
  101. package/dist/view-helpers.d.ts +9 -1
  102. package/dist/view-helpers.d.ts.map +1 -1
  103. package/dist/view-helpers.js +4 -0
  104. package/dist/view-helpers.js.map +1 -1
  105. package/package.json +1 -1
@@ -0,0 +1,25 @@
1
+ import type { ScopeOptions } from '../types.js';
2
+ /**
3
+ * Rebuild a subtree when `on(state)` changes.
4
+ *
5
+ * The `render` callback runs once at mount and once again every time
6
+ * `on(state)` returns a new value (compared with `Object.is`). Each run
7
+ * gets a fresh `Lifetime`, so bindings, onMount callbacks, and any
8
+ * per-arm state are recreated cleanly. Use `sample()` inside `render`
9
+ * when you need to read a whole-state snapshot at rebuild time without
10
+ * creating a reactive binding.
11
+ *
12
+ * Sugar over `branch({ on, cases: {}, default: render, __disposalCause: 'scope-rebuild' })`.
13
+ *
14
+ * ```ts
15
+ * scope({
16
+ * on: (s) => String(s.chartEpoch),
17
+ * render: (h) => {
18
+ * const stats = sample<State, Stats>((s) => s.stats)
19
+ * return [chartView(h, stats)]
20
+ * },
21
+ * })
22
+ * ```
23
+ */
24
+ export declare function scope<S, M = unknown>(opts: ScopeOptions<S, M>): Node[];
25
+ //# sourceMappingURL=scope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../../src/primitives/scope.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAG/C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,CAWtE"}
@@ -0,0 +1,36 @@
1
+ import { branch } from './branch.js';
2
+ /**
3
+ * Rebuild a subtree when `on(state)` changes.
4
+ *
5
+ * The `render` callback runs once at mount and once again every time
6
+ * `on(state)` returns a new value (compared with `Object.is`). Each run
7
+ * gets a fresh `Lifetime`, so bindings, onMount callbacks, and any
8
+ * per-arm state are recreated cleanly. Use `sample()` inside `render`
9
+ * when you need to read a whole-state snapshot at rebuild time without
10
+ * creating a reactive binding.
11
+ *
12
+ * Sugar over `branch({ on, cases: {}, default: render, __disposalCause: 'scope-rebuild' })`.
13
+ *
14
+ * ```ts
15
+ * scope({
16
+ * on: (s) => String(s.chartEpoch),
17
+ * render: (h) => {
18
+ * const stats = sample<State, Stats>((s) => s.stats)
19
+ * return [chartView(h, stats)]
20
+ * },
21
+ * })
22
+ * ```
23
+ */
24
+ export function scope(opts) {
25
+ return branch({
26
+ on: opts.on,
27
+ cases: {},
28
+ default: opts.render,
29
+ enter: opts.enter,
30
+ leave: opts.leave,
31
+ onTransition: opts.onTransition,
32
+ __disposalCause: 'scope-rebuild',
33
+ __mask: opts.__mask,
34
+ });
35
+ }
36
+ //# sourceMappingURL=scope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope.js","sourceRoot":"","sources":["../../src/primitives/scope.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEpC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,KAAK,CAAiB,IAAwB;IAC5D,OAAO,MAAM,CAAO;QAClB,EAAE,EAAE,IAAI,CAAC,EAAE;QACX,KAAK,EAAE,EAAE;QACT,OAAO,EAAE,IAAI,CAAC,MAAM;QACpB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,YAAY,EAAE,IAAI,CAAC,YAAY;QAC/B,eAAe,EAAE,eAAe;QAChC,MAAM,EAAE,IAAI,CAAC,MAAM;KACpB,CAAC,CAAA;AACJ,CAAC","sourcesContent":["import type { ScopeOptions } from '../types.js'\nimport { branch } from './branch.js'\n\n/**\n * Rebuild a subtree when `on(state)` changes.\n *\n * The `render` callback runs once at mount and once again every time\n * `on(state)` returns a new value (compared with `Object.is`). Each run\n * gets a fresh `Lifetime`, so bindings, onMount callbacks, and any\n * per-arm state are recreated cleanly. Use `sample()` inside `render`\n * when you need to read a whole-state snapshot at rebuild time without\n * creating a reactive binding.\n *\n * Sugar over `branch({ on, cases: {}, default: render, __disposalCause: 'scope-rebuild' })`.\n *\n * ```ts\n * scope({\n * on: (s) => String(s.chartEpoch),\n * render: (h) => {\n * const stats = sample<State, Stats>((s) => s.stats)\n * return [chartView(h, stats)]\n * },\n * })\n * ```\n */\nexport function scope<S, M = unknown>(opts: ScopeOptions<S, M>): Node[] {\n return branch<S, M>({\n on: opts.on,\n cases: {},\n default: opts.render,\n enter: opts.enter,\n leave: opts.leave,\n onTransition: opts.onTransition,\n __disposalCause: 'scope-rebuild',\n __mask: opts.__mask,\n })\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  import { getRenderContext } from '../render-context.js';
2
2
  import { createBinding, applyBinding } from '../binding.js';
3
- import { addDisposer } from '../scope.js';
3
+ import { addDisposer } from '../lifetime.js';
4
4
  import { FULL_MASK } from '../update-loop.js';
5
5
  import { registerOnClear } from './each.js';
6
6
  /**
@@ -16,7 +16,7 @@ import { registerOnClear } from './each.js';
16
16
  */
17
17
  export function selector(field) {
18
18
  const ctx = getRenderContext('selector');
19
- const scope = ctx.rootScope;
19
+ const scope = ctx.rootLifetime;
20
20
  const registry = new Map();
21
21
  let lastValue = field(ctx.state);
22
22
  let generation = 0;
@@ -97,8 +97,8 @@ export function selector(field) {
97
97
  // Per-row disposer for generic reconcile paths (scope disposal).
98
98
  // Uses generation check to skip work if already bulk-cleared.
99
99
  const gen = generation;
100
- const itemScope = getRenderContext('selector').rootScope;
101
- addDisposer(itemScope, () => {
100
+ const itemLifetime = getRenderContext('selector').rootLifetime;
101
+ addDisposer(itemLifetime, () => {
102
102
  if (gen !== generation)
103
103
  return; // already bulk-cleared, no-op
104
104
  const idx = bucket.indexOf(entry);
@@ -1 +1 @@
1
- {"version":3,"file":"selector.js","sourceRoot":"","sources":["../../src/primitives/selector.ts"],"names":[],"mappings":"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","sourcesContent":["import { getRenderContext } from '../render-context.js'\nimport { createBinding, applyBinding } from '../binding.js'\nimport { addDisposer } from '../scope.js'\nimport { FULL_MASK } from '../update-loop.js'\nimport { registerOnClear } from './each.js'\nimport type { BindingKind } from '../types.js'\n\ninterface SelectorEntry {\n node: Node\n kind: BindingKind\n key: string | undefined\n lastValue: unknown\n transform: (match: boolean) => unknown\n gen: number\n}\n\n/**\n * Optimized \"one-of-N\" reactive binding — O(1) updates instead of O(n).\n *\n * Uses a generation counter instead of per-row disposers:\n * - bind() tags entries with the current generation\n * - Individual row removal bumps the generation for that entry (set gen = -1)\n * - Bulk clear bumps the generation and clears the registry via registerOnClear\n * - updateSelector() skips stale entries (gen !== current) and compacts lazily\n *\n * Zero per-row closure allocations. Zero Set.delete calls on disposal.\n */\nexport function selector<S, V>(field: (s: S) => V): SelectorInstance<V> {\n const ctx = getRenderContext('selector')\n const scope = ctx.rootScope\n\n const registry = new Map<V, SelectorEntry[]>()\n let lastValue: V = field(ctx.state as S)\n let generation = 0\n let registeredOnClear = false\n\n function updateBucket(bucket: SelectorEntry[], match: boolean, gen: number): void {\n let hasStale = false\n for (let i = 0; i < bucket.length; i++) {\n const entry = bucket[i]!\n if (entry.gen !== gen) {\n hasStale = true\n continue\n }\n const v = entry.transform(match)\n if (!Object.is(v, entry.lastValue)) {\n entry.lastValue = v\n applyBinding({ kind: entry.kind, node: entry.node, key: entry.key }, v)\n }\n }\n // Lazy compaction — remove stale entries when encountered\n if (hasStale) {\n let w = 0\n for (let r = 0; r < bucket.length; r++) {\n if (bucket[r]!.gen === gen) bucket[w++] = bucket[r]!\n }\n bucket.length = w\n }\n }\n\n function updateSelector(state: S): V {\n const newValue = field(state)\n if (Object.is(newValue, lastValue)) return lastValue\n const gen = generation\n\n const oldBucket = registry.get(lastValue)\n if (oldBucket) updateBucket(oldBucket, false, gen)\n\n const newBucket = registry.get(newValue)\n if (newBucket) updateBucket(newBucket, true, gen)\n\n lastValue = newValue\n return newValue\n }\n\n createBinding(scope, {\n mask: FULL_MASK,\n accessor: ((state: S) => updateSelector(state)) as (state: never) => unknown,\n kind: 'text',\n node: document.createComment('selector'),\n perItem: false,\n })\n\n return {\n bind(\n node: Node,\n key: V | (() => V),\n kind: BindingKind,\n propKey: string | undefined,\n transform: (match: boolean) => unknown,\n ): void {\n const currentKey = typeof key === 'function' ? (key as () => V)() : key\n const initialMatch = Object.is(lastValue, currentKey)\n const initialValue = transform(initialMatch)\n\n applyBinding({ kind, node, key: propKey }, initialValue)\n\n const entry: SelectorEntry = {\n node,\n kind,\n key: propKey,\n lastValue: initialValue,\n transform,\n gen: generation,\n }\n\n let bucket = registry.get(currentKey)\n if (!bucket) {\n bucket = []\n registry.set(currentKey, bucket)\n }\n bucket.push(entry)\n\n // Register bulk clear callback (once per selector per each() block)\n if (!registeredOnClear) {\n registerOnClear(() => {\n generation++\n registry.clear()\n })\n registeredOnClear = true\n }\n\n // Per-row disposer for generic reconcile paths (scope disposal).\n // Uses generation check to skip work if already bulk-cleared.\n const gen = generation\n const itemScope = getRenderContext('selector').rootScope\n addDisposer(itemScope, () => {\n if (gen !== generation) return // already bulk-cleared, no-op\n const idx = bucket!.indexOf(entry)\n if (idx !== -1) bucket!.splice(idx, 1)\n if (bucket!.length === 0) registry.delete(currentKey)\n })\n },\n\n __directUpdate(state: unknown): void {\n updateSelector(state as S)\n },\n }\n}\n\nexport interface SelectorInstance<V> {\n bind(\n node: Node,\n key: V | (() => V),\n kind: BindingKind,\n propKey: string | undefined,\n transform: (match: boolean) => unknown,\n ): void\n __directUpdate(state: unknown): void\n}\n"]}
1
+ {"version":3,"file":"selector.js","sourceRoot":"","sources":["../../src/primitives/selector.ts"],"names":[],"mappings":"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","sourcesContent":["import { getRenderContext } from '../render-context.js'\nimport { createBinding, applyBinding } from '../binding.js'\nimport { addDisposer } from '../lifetime.js'\nimport { FULL_MASK } from '../update-loop.js'\nimport { registerOnClear } from './each.js'\nimport type { BindingKind } from '../types.js'\n\ninterface SelectorEntry {\n node: Node\n kind: BindingKind\n key: string | undefined\n lastValue: unknown\n transform: (match: boolean) => unknown\n gen: number\n}\n\n/**\n * Optimized \"one-of-N\" reactive binding — O(1) updates instead of O(n).\n *\n * Uses a generation counter instead of per-row disposers:\n * - bind() tags entries with the current generation\n * - Individual row removal bumps the generation for that entry (set gen = -1)\n * - Bulk clear bumps the generation and clears the registry via registerOnClear\n * - updateSelector() skips stale entries (gen !== current) and compacts lazily\n *\n * Zero per-row closure allocations. Zero Set.delete calls on disposal.\n */\nexport function selector<S, V>(field: (s: S) => V): SelectorInstance<V> {\n const ctx = getRenderContext('selector')\n const scope = ctx.rootLifetime\n\n const registry = new Map<V, SelectorEntry[]>()\n let lastValue: V = field(ctx.state as S)\n let generation = 0\n let registeredOnClear = false\n\n function updateBucket(bucket: SelectorEntry[], match: boolean, gen: number): void {\n let hasStale = false\n for (let i = 0; i < bucket.length; i++) {\n const entry = bucket[i]!\n if (entry.gen !== gen) {\n hasStale = true\n continue\n }\n const v = entry.transform(match)\n if (!Object.is(v, entry.lastValue)) {\n entry.lastValue = v\n applyBinding({ kind: entry.kind, node: entry.node, key: entry.key }, v)\n }\n }\n // Lazy compaction — remove stale entries when encountered\n if (hasStale) {\n let w = 0\n for (let r = 0; r < bucket.length; r++) {\n if (bucket[r]!.gen === gen) bucket[w++] = bucket[r]!\n }\n bucket.length = w\n }\n }\n\n function updateSelector(state: S): V {\n const newValue = field(state)\n if (Object.is(newValue, lastValue)) return lastValue\n const gen = generation\n\n const oldBucket = registry.get(lastValue)\n if (oldBucket) updateBucket(oldBucket, false, gen)\n\n const newBucket = registry.get(newValue)\n if (newBucket) updateBucket(newBucket, true, gen)\n\n lastValue = newValue\n return newValue\n }\n\n createBinding(scope, {\n mask: FULL_MASK,\n accessor: ((state: S) => updateSelector(state)) as (state: never) => unknown,\n kind: 'text',\n node: document.createComment('selector'),\n perItem: false,\n })\n\n return {\n bind(\n node: Node,\n key: V | (() => V),\n kind: BindingKind,\n propKey: string | undefined,\n transform: (match: boolean) => unknown,\n ): void {\n const currentKey = typeof key === 'function' ? (key as () => V)() : key\n const initialMatch = Object.is(lastValue, currentKey)\n const initialValue = transform(initialMatch)\n\n applyBinding({ kind, node, key: propKey }, initialValue)\n\n const entry: SelectorEntry = {\n node,\n kind,\n key: propKey,\n lastValue: initialValue,\n transform,\n gen: generation,\n }\n\n let bucket = registry.get(currentKey)\n if (!bucket) {\n bucket = []\n registry.set(currentKey, bucket)\n }\n bucket.push(entry)\n\n // Register bulk clear callback (once per selector per each() block)\n if (!registeredOnClear) {\n registerOnClear(() => {\n generation++\n registry.clear()\n })\n registeredOnClear = true\n }\n\n // Per-row disposer for generic reconcile paths (scope disposal).\n // Uses generation check to skip work if already bulk-cleared.\n const gen = generation\n const itemLifetime = getRenderContext('selector').rootLifetime\n addDisposer(itemLifetime, () => {\n if (gen !== generation) return // already bulk-cleared, no-op\n const idx = bucket!.indexOf(entry)\n if (idx !== -1) bucket!.splice(idx, 1)\n if (bucket!.length === 0) registry.delete(currentKey)\n })\n },\n\n __directUpdate(state: unknown): void {\n updateSelector(state as S)\n },\n }\n}\n\nexport interface SelectorInstance<V> {\n bind(\n node: Node,\n key: V | (() => V),\n kind: BindingKind,\n propKey: string | undefined,\n transform: (match: boolean) => unknown,\n ): void\n __directUpdate(state: unknown): void\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"show.d.ts","sourceRoot":"","sources":["../../src/primitives/show.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAK9C,wBAAgB,IAAI,CAAC,CAAC,EAAE,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,CAYpE"}
1
+ {"version":3,"file":"show.d.ts","sourceRoot":"","sources":["../../src/primitives/show.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAK9C,wBAAgB,IAAI,CAAC,CAAC,EAAE,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,CAepE"}
@@ -5,7 +5,10 @@ export function show(opts) {
5
5
  // arm — it lets the disposer log distinguish show/hide transitions from
6
6
  // multi-case branch swaps.
7
7
  return branch({
8
- on: opts.when,
8
+ // branch.on is string-only; stringify the boolean for the case lookup.
9
+ // JS object literals stringify boolean keys, so `cases.{true, false}`
10
+ // matches `String(true)` / `String(false)`.
11
+ on: (s) => String(opts.when(s)),
9
12
  cases: { true: opts.render, false: opts.fallback ?? EMPTY },
10
13
  enter: opts.enter,
11
14
  leave: opts.leave,
@@ -1 +1 @@
1
- {"version":3,"file":"show.js","sourceRoot":"","sources":["../../src/primitives/show.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEpC,MAAM,KAAK,GAAG,GAAG,EAAE,CAAC,EAAY,CAAA;AAEhC,MAAM,UAAU,IAAI,CAAiB,IAAuB;IAC1D,sEAAsE;IACtE,wEAAwE;IACxE,2BAA2B;IAC3B,OAAO,MAAM,CAAO;QAClB,EAAE,EAAE,IAAI,CAAC,IAAI;QACb,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,QAAQ,IAAI,KAAK,EAAE;QAC3D,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,YAAY,EAAE,IAAI,CAAC,YAAY;QAC/B,eAAe,EAAE,WAAW;KAC7B,CAAC,CAAA;AACJ,CAAC","sourcesContent":["import type { ShowOptions } from '../types.js'\nimport { branch } from './branch.js'\n\nconst EMPTY = () => [] as Node[]\n\nexport function show<S, M = unknown>(opts: ShowOptions<S, M>): Node[] {\n // `__disposalCause` is read by branch.ts when it disposes the leaving\n // arm — it lets the disposer log distinguish show/hide transitions from\n // multi-case branch swaps.\n return branch<S, M>({\n on: opts.when,\n cases: { true: opts.render, false: opts.fallback ?? EMPTY },\n enter: opts.enter,\n leave: opts.leave,\n onTransition: opts.onTransition,\n __disposalCause: 'show-hide',\n })\n}\n"]}
1
+ {"version":3,"file":"show.js","sourceRoot":"","sources":["../../src/primitives/show.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEpC,MAAM,KAAK,GAAG,GAAG,EAAE,CAAC,EAAY,CAAA;AAEhC,MAAM,UAAU,IAAI,CAAiB,IAAuB;IAC1D,sEAAsE;IACtE,wEAAwE;IACxE,2BAA2B;IAC3B,OAAO,MAAM,CAAO;QAClB,uEAAuE;QACvE,sEAAsE;QACtE,4CAA4C;QAC5C,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC/B,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,QAAQ,IAAI,KAAK,EAAE;QAC3D,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,YAAY,EAAE,IAAI,CAAC,YAAY;QAC/B,eAAe,EAAE,WAAW;KAC7B,CAAC,CAAA;AACJ,CAAC","sourcesContent":["import type { ShowOptions } from '../types.js'\nimport { branch } from './branch.js'\n\nconst EMPTY = () => [] as Node[]\n\nexport function show<S, M = unknown>(opts: ShowOptions<S, M>): Node[] {\n // `__disposalCause` is read by branch.ts when it disposes the leaving\n // arm — it lets the disposer log distinguish show/hide transitions from\n // multi-case branch swaps.\n return branch<S, M>({\n // branch.on is string-only; stringify the boolean for the case lookup.\n // JS object literals stringify boolean keys, so `cases.{true, false}`\n // matches `String(true)` / `String(false)`.\n on: (s) => String(opts.when(s)),\n cases: { true: opts.render, false: opts.fallback ?? EMPTY },\n enter: opts.enter,\n leave: opts.leave,\n onTransition: opts.onTransition,\n __disposalCause: 'show-hide',\n })\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"slice.d.ts","sourceRoot":"","sources":["../../src/primitives/slice.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAA2C,MAAM,aAAa,CAAA;AAChF,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAA;AAU9C;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,EAChC,CAAC,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG;IAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAA;CAAE,EACpC,IAAI,EAAE,CAAC,CAAC,EAAE,IAAI,KAAK,GAAG,GACrB,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CA2Dd"}
1
+ {"version":3,"file":"slice.d.ts","sourceRoot":"","sources":["../../src/primitives/slice.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAyD,MAAM,aAAa,CAAA;AAC9F,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAA;AAY9C;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,EAChC,CAAC,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG;IAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAA;CAAE,EACpC,IAAI,EAAE,CAAC,CAAC,EAAE,IAAI,KAAK,GAAG,GACrB,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAmEd"}
@@ -1,9 +1,11 @@
1
1
  import { show as _show } from './show.js';
2
2
  import { branch as _branch } from './branch.js';
3
+ import { scope as _scope } from './scope.js';
3
4
  import { each as _each } from './each.js';
4
5
  import { text as _text } from './text.js';
5
6
  import { unsafeHtml as _unsafeHtml } from './unsafe-html.js';
6
7
  import { memo as _memo } from './memo.js';
8
+ import { sample as _sample } from './sample.js';
7
9
  import { selector as _selector } from './selector.js';
8
10
  import { useContext } from './context.js';
9
11
  /**
@@ -47,7 +49,13 @@ export function slice(h, lift) {
47
49
  branch: (opts) => _branch({
48
50
  ...opts,
49
51
  on: (r) => opts.on(lift(r)),
50
- cases: wrapCases(opts.cases),
52
+ cases: opts.cases ? wrapCases(opts.cases) : undefined,
53
+ default: opts.default ? wrapCase(opts.default) : undefined,
54
+ }),
55
+ scope: (opts) => _scope({
56
+ ...opts,
57
+ on: (r) => opts.on(lift(r)),
58
+ render: wrapCase(opts.render),
51
59
  }),
52
60
  each: (opts) => _each({
53
61
  ...opts,
@@ -72,6 +80,7 @@ export function slice(h, lift) {
72
80
  const root = useContext(c);
73
81
  return (s) => root(s);
74
82
  },
83
+ sample: (selector) => _sample((r) => selector(lift(r))),
75
84
  };
76
85
  }
77
86
  //# sourceMappingURL=slice.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"slice.js","sourceRoot":"","sources":["../../src/primitives/slice.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Send, EachOptions, ShowOptions, BranchOptions } from '../types.js'\nimport type { View } from '../view-helpers.js'\nimport { show as _show } from './show.js'\nimport { branch as _branch } from './branch.js'\nimport { each as _each } from './each.js'\nimport { text as _text } from './text.js'\nimport { unsafeHtml as _unsafeHtml } from './unsafe-html.js'\nimport { memo as _memo } from './memo.js'\nimport { selector as _selector } from './selector.js'\nimport { useContext, type Context } from './context.js'\n\n/**\n * Build a `View<Sub, M>` that composes a selector into every state-bound\n * accessor. Used to write view-functions over a sub-slice of parent state:\n *\n * ```ts\n * import { slice } from '@llui/dom'\n *\n * view: (h) => {\n * const formView = slice(h, (s) => s.form)\n * return [...formView.show({ when: f => f.valid, render: (h) => [...] })]\n * }\n * ```\n *\n * Kept as a standalone function rather than a method on the View bundle so\n * apps that don't use it don't pay for its bundle cost — tree-shaken when\n * unused.\n */\nexport function slice<Root, Sub, M>(\n h: View<Root, M> | { send: Send<M> },\n lift: (r: Root) => Sub,\n): View<Sub, M> {\n const send = h.send\n\n // Wrap a Sub-typed case callback to work with the Root-typed primitive.\n // The inner callback receives a View<Root, M> from the primitive, and we\n // narrow it to View<Sub, M> via a recursive slice() call.\n const wrapCase =\n (fn: (h: View<Sub, M>) => Node[]) =>\n (rootH: View<Root, M>): Node[] =>\n fn(slice(rootH, lift))\n\n const wrapCases = (\n cases: Record<string | number, (h: View<Sub, M>) => Node[]>,\n ): Record<string | number, (h: View<Root, M>) => Node[]> => {\n const out: Record<string | number, (h: View<Root, M>) => Node[]> = {}\n for (const key of Object.keys(cases)) {\n out[key] = wrapCase(cases[key]!)\n }\n return out\n }\n\n return {\n send,\n show: (opts: ShowOptions<Sub, M>) =>\n _show<Root, M>({\n ...opts,\n when: (r) => opts.when(lift(r)),\n render: wrapCase(opts.render),\n fallback: opts.fallback ? wrapCase(opts.fallback) : undefined,\n }),\n branch: (opts: BranchOptions<Sub, M>) =>\n _branch<Root, M>({\n ...opts,\n on: (r) => opts.on(lift(r)),\n cases: wrapCases(opts.cases),\n }),\n each: <T>(opts: EachOptions<Sub, T, M>) =>\n _each<Root, T, M>({\n ...opts,\n items: (r) => opts.items(lift(r)),\n }),\n text: (accessor, mask) => {\n if (typeof accessor === 'string') return _text(accessor)\n return _text<Root>((r) => accessor(lift(r)), mask)\n },\n unsafeHtml: (accessor, mask) => {\n if (typeof accessor === 'string') return _unsafeHtml(accessor)\n return _unsafeHtml<Root>((r) => accessor(lift(r)), mask)\n },\n memo: <T>(accessor: (s: Sub) => T) => {\n const m = _memo<Root, T>((r) => accessor(lift(r)))\n return (s: Sub) => m(s as unknown as Root)\n },\n selector: <V>(field: (s: Sub) => V) => _selector<Root, V>((r) => field(lift(r))),\n ctx: <T>(c: Context<T>) => {\n const root = useContext<Root, T>(c)\n return (s: Sub) => root(s as unknown as Root)\n },\n }\n}\n"]}
1
+ {"version":3,"file":"slice.js","sourceRoot":"","sources":["../../src/primitives/slice.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Send, EachOptions, ShowOptions, BranchOptions, ScopeOptions } from '../types.js'\nimport type { View } from '../view-helpers.js'\nimport { show as _show } from './show.js'\nimport { branch as _branch } from './branch.js'\nimport { scope as _scope } from './scope.js'\nimport { each as _each } from './each.js'\nimport { text as _text } from './text.js'\nimport { unsafeHtml as _unsafeHtml } from './unsafe-html.js'\nimport { memo as _memo } from './memo.js'\nimport { sample as _sample } from './sample.js'\nimport { selector as _selector } from './selector.js'\nimport { useContext, type Context } from './context.js'\n\n/**\n * Build a `View<Sub, M>` that composes a selector into every state-bound\n * accessor. Used to write view-functions over a sub-slice of parent state:\n *\n * ```ts\n * import { slice } from '@llui/dom'\n *\n * view: (h) => {\n * const formView = slice(h, (s) => s.form)\n * return [...formView.show({ when: f => f.valid, render: (h) => [...] })]\n * }\n * ```\n *\n * Kept as a standalone function rather than a method on the View bundle so\n * apps that don't use it don't pay for its bundle cost — tree-shaken when\n * unused.\n */\nexport function slice<Root, Sub, M>(\n h: View<Root, M> | { send: Send<M> },\n lift: (r: Root) => Sub,\n): View<Sub, M> {\n const send = h.send\n\n // Wrap a Sub-typed case callback to work with the Root-typed primitive.\n // The inner callback receives a View<Root, M> from the primitive, and we\n // narrow it to View<Sub, M> via a recursive slice() call.\n const wrapCase =\n (fn: (h: View<Sub, M>) => Node[]) =>\n (rootH: View<Root, M>): Node[] =>\n fn(slice(rootH, lift))\n\n const wrapCases = (\n cases: Record<string, (h: View<Sub, M>) => Node[]>,\n ): Record<string, (h: View<Root, M>) => Node[]> => {\n const out: Record<string, (h: View<Root, M>) => Node[]> = {}\n for (const key of Object.keys(cases)) {\n out[key] = wrapCase(cases[key]!)\n }\n return out\n }\n\n return {\n send,\n show: (opts: ShowOptions<Sub, M>) =>\n _show<Root, M>({\n ...opts,\n when: (r) => opts.when(lift(r)),\n render: wrapCase(opts.render),\n fallback: opts.fallback ? wrapCase(opts.fallback) : undefined,\n }),\n branch: (opts: BranchOptions<Sub, M>) =>\n _branch<Root, M>({\n ...opts,\n on: (r) => opts.on(lift(r)),\n cases: opts.cases ? wrapCases(opts.cases) : undefined,\n default: opts.default ? wrapCase(opts.default) : undefined,\n }),\n scope: (opts: ScopeOptions<Sub, M>) =>\n _scope<Root, M>({\n ...opts,\n on: (r) => opts.on(lift(r)),\n render: wrapCase(opts.render),\n }),\n each: <T>(opts: EachOptions<Sub, T, M>) =>\n _each<Root, T, M>({\n ...opts,\n items: (r) => opts.items(lift(r)),\n }),\n text: (accessor, mask) => {\n if (typeof accessor === 'string') return _text(accessor)\n return _text<Root>((r) => accessor(lift(r)), mask)\n },\n unsafeHtml: (accessor, mask) => {\n if (typeof accessor === 'string') return _unsafeHtml(accessor)\n return _unsafeHtml<Root>((r) => accessor(lift(r)), mask)\n },\n memo: <T>(accessor: (s: Sub) => T) => {\n const m = _memo<Root, T>((r) => accessor(lift(r)))\n return (s: Sub) => m(s as unknown as Root)\n },\n selector: <V>(field: (s: Sub) => V) => _selector<Root, V>((r) => field(lift(r))),\n ctx: <T>(c: Context<T>) => {\n const root = useContext<Root, T>(c)\n return (s: Sub) => root(s as unknown as Root)\n },\n sample: <R>(selector: (s: Sub) => R) => _sample<Root, R>((r) => selector(lift(r))),\n }\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  import { getRenderContext } from '../render-context.js';
2
2
  import { createBinding } from '../binding.js';
3
- import { addCheckedItemUpdater } from '../scope.js';
3
+ import { addCheckedItemUpdater } from '../lifetime.js';
4
4
  import { FULL_MASK } from '../update-loop.js';
5
5
  export function text(accessor, mask) {
6
6
  if (typeof accessor === 'string') {
@@ -12,7 +12,7 @@ export function text(accessor, mask) {
12
12
  // Register as direct updater, bypassing Phase 2 binding scan
13
13
  if (accessor.length === 0) {
14
14
  const get = accessor;
15
- const initial = addCheckedItemUpdater(ctx.rootScope, () => String(get()), (v) => {
15
+ const initial = addCheckedItemUpdater(ctx.rootLifetime, () => String(get()), (v) => {
16
16
  node.nodeValue = v;
17
17
  });
18
18
  node.nodeValue = initial;
@@ -20,7 +20,7 @@ export function text(accessor, mask) {
20
20
  }
21
21
  // Component-level state accessor
22
22
  const bindingMask = mask ?? FULL_MASK;
23
- const binding = createBinding(ctx.rootScope, {
23
+ const binding = createBinding(ctx.rootLifetime, {
24
24
  mask: bindingMask,
25
25
  accessor: accessor,
26
26
  kind: 'text',
@@ -1 +1 @@
1
- {"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/primitives/text.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAE7C,MAAM,UAAU,IAAI,CAClB,QAAsD,EACtD,IAAa;IAEb,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,OAAO,QAAQ,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAA;IAC1C,CAAC;IAED,MAAM,GAAG,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAA;IACpC,MAAM,IAAI,GAAG,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,CAAA;IAExC,mEAAmE;IACnE,6DAA6D;IAC7D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,GAAG,GAAG,QAAwB,CAAA;QACpC,MAAM,OAAO,GAAG,qBAAqB,CACnC,GAAG,CAAC,SAAS,EACb,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,EACnB,CAAC,CAAC,EAAE,EAAE;YACJ,IAAI,CAAC,SAAS,GAAG,CAAC,CAAA;QACpB,CAAC,CACF,CAAA;QACD,IAAI,CAAC,SAAS,GAAG,OAAO,CAAA;QACxB,OAAO,IAAI,CAAA;IACb,CAAC;IAED,iCAAiC;IACjC,MAAM,WAAW,GAAG,IAAI,IAAI,SAAS,CAAA;IACrC,MAAM,OAAO,GAAG,aAAa,CAAC,GAAG,CAAC,SAAS,EAAE;QAC3C,IAAI,EAAE,WAAW;QACjB,QAAQ,EAAE,QAAqC;QAC/C,IAAI,EAAE,MAAM;QACZ,IAAI;QACJ,OAAO,EAAE,KAAK;KACf,CAAC,CAAA;IACF,MAAM,YAAY,GAAI,QAA6B,CAAC,GAAG,CAAC,KAAU,CAAC,CAAA;IACnE,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,YAAY,CAAC,CAAA;IACrC,OAAO,CAAC,SAAS,GAAG,YAAY,CAAA;IAEhC,OAAO,IAAI,CAAA;AACb,CAAC","sourcesContent":["import { getRenderContext } from '../render-context.js'\nimport { createBinding } from '../binding.js'\nimport { addCheckedItemUpdater } from '../scope.js'\nimport { FULL_MASK } from '../update-loop.js'\n\nexport function text<S>(\n accessor: ((s: S) => string) | (() => string) | string,\n mask?: number,\n): Text {\n if (typeof accessor === 'string') {\n return document.createTextNode(accessor)\n }\n\n const ctx = getRenderContext('text')\n const node = document.createTextNode('')\n\n // Per-item accessor from each() — zero-arg function (length === 0)\n // Register as direct updater, bypassing Phase 2 binding scan\n if (accessor.length === 0) {\n const get = accessor as () => string\n const initial = addCheckedItemUpdater(\n ctx.rootScope,\n () => String(get()),\n (v) => {\n node.nodeValue = v\n },\n )\n node.nodeValue = initial\n return node\n }\n\n // Component-level state accessor\n const bindingMask = mask ?? FULL_MASK\n const binding = createBinding(ctx.rootScope, {\n mask: bindingMask,\n accessor: accessor as (state: never) => unknown,\n kind: 'text',\n node,\n perItem: false,\n })\n const initialValue = (accessor as (s: S) => string)(ctx.state as S)\n node.nodeValue = String(initialValue)\n binding.lastValue = initialValue\n\n return node\n}\n"]}
1
+ {"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/primitives/text.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAE7C,MAAM,UAAU,IAAI,CAClB,QAAsD,EACtD,IAAa;IAEb,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,OAAO,QAAQ,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAA;IAC1C,CAAC;IAED,MAAM,GAAG,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAA;IACpC,MAAM,IAAI,GAAG,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,CAAA;IAExC,mEAAmE;IACnE,6DAA6D;IAC7D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,GAAG,GAAG,QAAwB,CAAA;QACpC,MAAM,OAAO,GAAG,qBAAqB,CACnC,GAAG,CAAC,YAAY,EAChB,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,EACnB,CAAC,CAAC,EAAE,EAAE;YACJ,IAAI,CAAC,SAAS,GAAG,CAAC,CAAA;QACpB,CAAC,CACF,CAAA;QACD,IAAI,CAAC,SAAS,GAAG,OAAO,CAAA;QACxB,OAAO,IAAI,CAAA;IACb,CAAC;IAED,iCAAiC;IACjC,MAAM,WAAW,GAAG,IAAI,IAAI,SAAS,CAAA;IACrC,MAAM,OAAO,GAAG,aAAa,CAAC,GAAG,CAAC,YAAY,EAAE;QAC9C,IAAI,EAAE,WAAW;QACjB,QAAQ,EAAE,QAAqC;QAC/C,IAAI,EAAE,MAAM;QACZ,IAAI;QACJ,OAAO,EAAE,KAAK;KACf,CAAC,CAAA;IACF,MAAM,YAAY,GAAI,QAA6B,CAAC,GAAG,CAAC,KAAU,CAAC,CAAA;IACnE,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,YAAY,CAAC,CAAA;IACrC,OAAO,CAAC,SAAS,GAAG,YAAY,CAAA;IAEhC,OAAO,IAAI,CAAA;AACb,CAAC","sourcesContent":["import { getRenderContext } from '../render-context.js'\nimport { createBinding } from '../binding.js'\nimport { addCheckedItemUpdater } from '../lifetime.js'\nimport { FULL_MASK } from '../update-loop.js'\n\nexport function text<S>(\n accessor: ((s: S) => string) | (() => string) | string,\n mask?: number,\n): Text {\n if (typeof accessor === 'string') {\n return document.createTextNode(accessor)\n }\n\n const ctx = getRenderContext('text')\n const node = document.createTextNode('')\n\n // Per-item accessor from each() — zero-arg function (length === 0)\n // Register as direct updater, bypassing Phase 2 binding scan\n if (accessor.length === 0) {\n const get = accessor as () => string\n const initial = addCheckedItemUpdater(\n ctx.rootLifetime,\n () => String(get()),\n (v) => {\n node.nodeValue = v\n },\n )\n node.nodeValue = initial\n return node\n }\n\n // Component-level state accessor\n const bindingMask = mask ?? FULL_MASK\n const binding = createBinding(ctx.rootLifetime, {\n mask: bindingMask,\n accessor: accessor as (state: never) => unknown,\n kind: 'text',\n node,\n perItem: false,\n })\n const initialValue = (accessor as (s: S) => string)(ctx.state as S)\n node.nodeValue = String(initialValue)\n binding.lastValue = initialValue\n\n return node\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"virtual-each.d.ts","sourceRoot":"","sources":["../../src/primitives/virtual-each.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAS,IAAI,EAAE,MAAM,aAAa,CAAA;AAY5D,MAAM,WAAW,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,OAAO;IACnD,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAA;IACpB,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,GAAG,MAAM,CAAA;IACjC,qFAAqF;IACrF,UAAU,EAAE,MAAM,CAAA;IAClB,6CAA6C;IAC7C,eAAe,EAAE,MAAM,CAAA;IACvB,sFAAsF;IACtF,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,CAAC,IAAI,EAAE;QACb,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAA;QACb,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,CAAA;QACrB,GAAG,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,MAAM,CAAC,CAAA;QAC1C,KAAK,EAAE,MAAM,MAAM,CAAA;KACpB,KAAK,IAAI,EAAE,CAAA;CACb;AAiBD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,CAkMxF"}
1
+ {"version":3,"file":"virtual-each.d.ts","sourceRoot":"","sources":["../../src/primitives/virtual-each.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAY,IAAI,EAAE,MAAM,aAAa,CAAA;AAY/D,MAAM,WAAW,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,OAAO;IACnD,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAA;IACpB,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,GAAG,MAAM,CAAA;IACjC,qFAAqF;IACrF,UAAU,EAAE,MAAM,CAAA;IAClB,6CAA6C;IAC7C,eAAe,EAAE,MAAM,CAAA;IACvB,sFAAsF;IACtF,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,CAAC,IAAI,EAAE;QACb,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAA;QACb,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,CAAA;QACrB,GAAG,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,MAAM,CAAC,CAAA;QAC1C,KAAK,EAAE,MAAM,MAAM,CAAA;KACpB,KAAK,IAAI,EAAE,CAAA;CACb;AAiBD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,CAkMxF"}
@@ -1,9 +1,9 @@
1
1
  import { getRenderContext, setRenderContext, clearRenderContext, } from '../render-context.js';
2
- import { createScope, disposeScope, addDisposer } from '../scope.js';
2
+ import { createLifetime, disposeLifetime, addDisposer } from '../lifetime.js';
3
3
  import { getFlatBindings, setFlatBindings } from '../binding.js';
4
4
  import { FULL_MASK } from '../update-loop.js';
5
5
  const buildCtx = {
6
- rootScope: null,
6
+ rootLifetime: null,
7
7
  state: null,
8
8
  allBindings: [],
9
9
  structuralBlocks: [],
@@ -33,7 +33,7 @@ const buildCtx = {
33
33
  */
34
34
  export function virtualEach(opts) {
35
35
  const ctx = getRenderContext('virtualEach');
36
- const parentScope = ctx.rootScope;
36
+ const parentLifetime = ctx.rootLifetime;
37
37
  const blocks = ctx.structuralBlocks;
38
38
  const send = ctx.send;
39
39
  const overscan = opts.overscan ?? 3;
@@ -64,7 +64,7 @@ export function virtualEach(opts) {
64
64
  };
65
65
  const buildEntry = (item, index, state) => {
66
66
  const key = opts.key(item);
67
- const scope = createScope(parentScope);
67
+ const scope = createLifetime(parentLifetime);
68
68
  const wrapper = document.createElement('div');
69
69
  wrapper.style.position = 'absolute';
70
70
  wrapper.style.top = `${index * opts.itemHeight}px`;
@@ -103,7 +103,7 @@ export function virtualEach(opts) {
103
103
  return itemProxy;
104
104
  };
105
105
  const indexAccessor = () => entry.index;
106
- buildCtx.rootScope = scope;
106
+ buildCtx.rootLifetime = scope;
107
107
  buildCtx.state = state;
108
108
  buildCtx.allBindings = ctx.allBindings;
109
109
  buildCtx.structuralBlocks = ctx.structuralBlocks;
@@ -133,7 +133,7 @@ export function virtualEach(opts) {
133
133
  // Dispose all entries
134
134
  for (const entry of entries.values()) {
135
135
  entry.scope.disposalCause = 'each-remove';
136
- disposeScope(entry.scope);
136
+ disposeLifetime(entry.scope);
137
137
  if (entry.wrapper.parentNode)
138
138
  entry.wrapper.parentNode.removeChild(entry.wrapper);
139
139
  }
@@ -151,7 +151,7 @@ export function virtualEach(opts) {
151
151
  for (const [key, entry] of entries) {
152
152
  if (!visibleKeys.has(key)) {
153
153
  entry.scope.disposalCause = 'each-remove';
154
- disposeScope(entry.scope);
154
+ disposeLifetime(entry.scope);
155
155
  if (entry.wrapper.parentNode)
156
156
  entry.wrapper.parentNode.removeChild(entry.wrapper);
157
157
  entries.delete(key);
@@ -196,10 +196,10 @@ export function virtualEach(opts) {
196
196
  // Initial render
197
197
  reconcile(ctx.state);
198
198
  // Cleanup on parent disposal
199
- addDisposer(parentScope, () => {
199
+ addDisposer(parentLifetime, () => {
200
200
  scroll.removeEventListener('scroll', onScroll);
201
201
  for (const entry of entries.values()) {
202
- disposeScope(entry.scope);
202
+ disposeLifetime(entry.scope);
203
203
  }
204
204
  entries.clear();
205
205
  });
@@ -1 +1 @@
1
- {"version":3,"file":"virtual-each.js","sourceRoot":"","sources":["../../src/primitives/virtual-each.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ItemAccessor, Scope, Send } from '../types.js'\nimport {\n getRenderContext,\n setRenderContext,\n clearRenderContext,\n type RenderContext,\n} from '../render-context.js'\nimport { createScope, disposeScope, addDisposer } from '../scope.js'\nimport { getFlatBindings, setFlatBindings } from '../binding.js'\nimport { FULL_MASK } from '../update-loop.js'\nimport type { StructuralBlock } from '../structural.js'\n\nexport interface VirtualEachOptions<S, T, M = unknown> {\n items: (s: S) => T[]\n key: (item: T) => string | number\n /** Fixed pixel height per item. Required — dynamic heights are not supported yet. */\n itemHeight: number\n /** Scrollable container height in pixels. */\n containerHeight: number\n /** Extra rows to render above/below the viewport for smooth scrolling. Default: 3. */\n overscan?: number\n /** Optional class for the scroll container. */\n class?: string\n render: (opts: {\n send: Send<M>\n item: ItemAccessor<T>\n acc: <R>(selector: (t: T) => R) => () => R\n index: () => number\n }) => Node[]\n}\n\ninterface VirtualEntry<T> {\n key: string | number\n current: T\n index: number\n scope: Scope\n wrapper: HTMLElement\n}\n\nconst buildCtx: RenderContext = {\n rootScope: null as unknown as Scope,\n state: null,\n allBindings: [],\n structuralBlocks: [],\n}\n\n/**\n * Virtualized list — renders only the rows visible in the scroll viewport.\n * Use for lists with 1k+ items where a regular `each()` would be too slow.\n *\n * Current limitations:\n * - Fixed row height (`itemHeight`) — dynamic heights not supported\n * - No transitions / animations\n * - No cross-container reuse (items outside view are fully disposed)\n *\n * ```ts\n * view: ({ text }) => [\n * ...virtualEach({\n * items: (s) => s.rows,\n * key: (r) => r.id,\n * itemHeight: 40,\n * containerHeight: 600,\n * render: ({ item }) => [\n * div({ class: 'row' }, [text(item.label)]),\n * ],\n * }),\n * ]\n * ```\n */\nexport function virtualEach<S, T, M = unknown>(opts: VirtualEachOptions<S, T, M>): Node[] {\n const ctx = getRenderContext('virtualEach')\n const parentScope = ctx.rootScope\n const blocks = ctx.structuralBlocks\n const send = ctx.send as (msg: M) => void\n\n const overscan = opts.overscan ?? 3\n\n // Scroll container\n const scroll = document.createElement('div')\n scroll.style.overflow = 'auto'\n scroll.style.position = 'relative'\n scroll.style.height = `${opts.containerHeight}px`\n scroll.dataset.virtualContainer = ''\n if (opts.class) scroll.className = opts.class\n\n // Inner spacer sized to full content height\n const spacer = document.createElement('div')\n spacer.style.position = 'relative'\n spacer.style.width = '100%'\n spacer.dataset.virtualSpacer = ''\n scroll.appendChild(spacer)\n\n // Map of key → entry\n const entries = new Map<string | number, VirtualEntry<T>>()\n let lastItems: T[] = []\n let scrollTop = 0\n\n const computeRange = (length: number): [number, number] => {\n if (length === 0) return [0, 0]\n const start = Math.max(0, Math.floor(scrollTop / opts.itemHeight) - overscan)\n const end = Math.min(\n length,\n Math.ceil((scrollTop + opts.containerHeight) / opts.itemHeight) + overscan,\n )\n return [start, end]\n }\n\n const buildEntry = (item: T, index: number, state: S): VirtualEntry<T> => {\n const key = opts.key(item)\n const scope = createScope(parentScope)\n\n const wrapper = document.createElement('div')\n wrapper.style.position = 'absolute'\n wrapper.style.top = `${index * opts.itemHeight}px`\n wrapper.style.left = '0'\n wrapper.style.right = '0'\n wrapper.style.height = `${opts.itemHeight}px`\n wrapper.dataset.virtualItem = ''\n wrapper.dataset.virtualKey = String(key)\n\n const entry: VirtualEntry<T> = { key, current: item, index, scope, wrapper }\n\n // Item accessor: item(selector) and item.field\n const itemFn = <R>(selector: (t: T) => R): (() => R) => {\n const accessor = (): R => selector(entry.current)\n ;(accessor as unknown as { __perItem: true }).__perItem = true\n return accessor\n }\n\n let itemProxy: ItemAccessor<T> | null = null\n const getItemProxy = (): ItemAccessor<T> => {\n if (itemProxy) return itemProxy\n const fieldCache = new Map<string, () => unknown>()\n itemProxy = new Proxy(itemFn as object, {\n get(target, prop) {\n if (typeof prop === 'symbol' || prop === 'then' || prop === 'prototype') {\n return Reflect.get(target, prop)\n }\n const k = prop as string\n const cached = fieldCache.get(k)\n if (cached) return cached\n const accessor = (): unknown => (entry.current as Record<string, unknown>)[k]\n ;(accessor as unknown as { __perItem: true }).__perItem = true\n fieldCache.set(k, accessor)\n return accessor\n },\n }) as ItemAccessor<T>\n return itemProxy\n }\n\n const indexAccessor = (): number => entry.index\n\n buildCtx.rootScope = scope\n buildCtx.state = state\n buildCtx.allBindings = ctx.allBindings\n buildCtx.structuralBlocks = ctx.structuralBlocks\n buildCtx.instance = ctx.instance\n const prevFlat = getFlatBindings()\n setFlatBindings(ctx.allBindings)\n setRenderContext(buildCtx)\n\n const nodes = opts.render({\n send,\n item: getItemProxy(),\n acc: itemFn,\n index: indexAccessor,\n })\n\n clearRenderContext()\n setFlatBindings(prevFlat)\n setRenderContext(ctx)\n\n for (const node of nodes) wrapper.appendChild(node)\n return entry\n }\n\n const reconcile = (state: S): void => {\n const items = opts.items(state)\n lastItems = items\n\n // Update spacer total height\n spacer.style.height = `${items.length * opts.itemHeight}px`\n\n if (items.length === 0) {\n // Dispose all entries\n for (const entry of entries.values()) {\n entry.scope.disposalCause = 'each-remove'\n disposeScope(entry.scope)\n if (entry.wrapper.parentNode) entry.wrapper.parentNode.removeChild(entry.wrapper)\n }\n entries.clear()\n return\n }\n\n const [start, end] = computeRange(items.length)\n\n // Build a map of key → {item, index} for the visible range\n const visibleKeys = new Map<string | number, { item: T; index: number }>()\n for (let i = start; i < end; i++) {\n const item = items[i]!\n visibleKeys.set(opts.key(item), { item, index: i })\n }\n\n // Dispose entries no longer visible\n for (const [key, entry] of entries) {\n if (!visibleKeys.has(key)) {\n entry.scope.disposalCause = 'each-remove'\n disposeScope(entry.scope)\n if (entry.wrapper.parentNode) entry.wrapper.parentNode.removeChild(entry.wrapper)\n entries.delete(key)\n }\n }\n\n // Create new entries + update existing\n for (const [key, { item, index }] of visibleKeys) {\n const existing = entries.get(key)\n if (existing) {\n existing.current = item\n if (existing.index !== index) {\n existing.index = index\n existing.wrapper.style.top = `${index * opts.itemHeight}px`\n }\n } else {\n const entry = buildEntry(item, index, state)\n entries.set(key, entry)\n spacer.appendChild(entry.wrapper)\n }\n }\n }\n\n // Scroll handler — reconcile visible range without touching component state\n const onScroll = (): void => {\n scrollTop = scroll.scrollTop\n reconcile(ctx.state as S)\n }\n scroll.addEventListener('scroll', onScroll, { passive: true })\n\n // Register as a structural block BEFORE initial render so this block\n // precedes any nested blocks its rows register. See branch.ts for the\n // full rationale (Phase 1 iteration safety).\n const block: StructuralBlock = {\n mask: FULL_MASK,\n reconcile(state: unknown) {\n const newItems = opts.items(state as S)\n if (newItems === lastItems) return\n reconcile(state as S)\n },\n }\n blocks.push(block)\n\n // Initial render\n reconcile(ctx.state as S)\n\n // Cleanup on parent disposal\n addDisposer(parentScope, () => {\n scroll.removeEventListener('scroll', onScroll)\n for (const entry of entries.values()) {\n disposeScope(entry.scope)\n }\n entries.clear()\n })\n\n return [scroll]\n}\n"]}
1
+ {"version":3,"file":"virtual-each.js","sourceRoot":"","sources":["../../src/primitives/virtual-each.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ItemAccessor, Lifetime, Send } from '../types.js'\nimport {\n getRenderContext,\n setRenderContext,\n clearRenderContext,\n type RenderContext,\n} from '../render-context.js'\nimport { createLifetime, disposeLifetime, addDisposer } from '../lifetime.js'\nimport { getFlatBindings, setFlatBindings } from '../binding.js'\nimport { FULL_MASK } from '../update-loop.js'\nimport type { StructuralBlock } from '../structural.js'\n\nexport interface VirtualEachOptions<S, T, M = unknown> {\n items: (s: S) => T[]\n key: (item: T) => string | number\n /** Fixed pixel height per item. Required — dynamic heights are not supported yet. */\n itemHeight: number\n /** Scrollable container height in pixels. */\n containerHeight: number\n /** Extra rows to render above/below the viewport for smooth scrolling. Default: 3. */\n overscan?: number\n /** Optional class for the scroll container. */\n class?: string\n render: (opts: {\n send: Send<M>\n item: ItemAccessor<T>\n acc: <R>(selector: (t: T) => R) => () => R\n index: () => number\n }) => Node[]\n}\n\ninterface VirtualEntry<T> {\n key: string | number\n current: T\n index: number\n scope: Lifetime\n wrapper: HTMLElement\n}\n\nconst buildCtx: RenderContext = {\n rootLifetime: null as unknown as Lifetime,\n state: null,\n allBindings: [],\n structuralBlocks: [],\n}\n\n/**\n * Virtualized list — renders only the rows visible in the scroll viewport.\n * Use for lists with 1k+ items where a regular `each()` would be too slow.\n *\n * Current limitations:\n * - Fixed row height (`itemHeight`) — dynamic heights not supported\n * - No transitions / animations\n * - No cross-container reuse (items outside view are fully disposed)\n *\n * ```ts\n * view: ({ text }) => [\n * ...virtualEach({\n * items: (s) => s.rows,\n * key: (r) => r.id,\n * itemHeight: 40,\n * containerHeight: 600,\n * render: ({ item }) => [\n * div({ class: 'row' }, [text(item.label)]),\n * ],\n * }),\n * ]\n * ```\n */\nexport function virtualEach<S, T, M = unknown>(opts: VirtualEachOptions<S, T, M>): Node[] {\n const ctx = getRenderContext('virtualEach')\n const parentLifetime = ctx.rootLifetime\n const blocks = ctx.structuralBlocks\n const send = ctx.send as (msg: M) => void\n\n const overscan = opts.overscan ?? 3\n\n // Scroll container\n const scroll = document.createElement('div')\n scroll.style.overflow = 'auto'\n scroll.style.position = 'relative'\n scroll.style.height = `${opts.containerHeight}px`\n scroll.dataset.virtualContainer = ''\n if (opts.class) scroll.className = opts.class\n\n // Inner spacer sized to full content height\n const spacer = document.createElement('div')\n spacer.style.position = 'relative'\n spacer.style.width = '100%'\n spacer.dataset.virtualSpacer = ''\n scroll.appendChild(spacer)\n\n // Map of key → entry\n const entries = new Map<string | number, VirtualEntry<T>>()\n let lastItems: T[] = []\n let scrollTop = 0\n\n const computeRange = (length: number): [number, number] => {\n if (length === 0) return [0, 0]\n const start = Math.max(0, Math.floor(scrollTop / opts.itemHeight) - overscan)\n const end = Math.min(\n length,\n Math.ceil((scrollTop + opts.containerHeight) / opts.itemHeight) + overscan,\n )\n return [start, end]\n }\n\n const buildEntry = (item: T, index: number, state: S): VirtualEntry<T> => {\n const key = opts.key(item)\n const scope = createLifetime(parentLifetime)\n\n const wrapper = document.createElement('div')\n wrapper.style.position = 'absolute'\n wrapper.style.top = `${index * opts.itemHeight}px`\n wrapper.style.left = '0'\n wrapper.style.right = '0'\n wrapper.style.height = `${opts.itemHeight}px`\n wrapper.dataset.virtualItem = ''\n wrapper.dataset.virtualKey = String(key)\n\n const entry: VirtualEntry<T> = { key, current: item, index, scope, wrapper }\n\n // Item accessor: item(selector) and item.field\n const itemFn = <R>(selector: (t: T) => R): (() => R) => {\n const accessor = (): R => selector(entry.current)\n ;(accessor as unknown as { __perItem: true }).__perItem = true\n return accessor\n }\n\n let itemProxy: ItemAccessor<T> | null = null\n const getItemProxy = (): ItemAccessor<T> => {\n if (itemProxy) return itemProxy\n const fieldCache = new Map<string, () => unknown>()\n itemProxy = new Proxy(itemFn as object, {\n get(target, prop) {\n if (typeof prop === 'symbol' || prop === 'then' || prop === 'prototype') {\n return Reflect.get(target, prop)\n }\n const k = prop as string\n const cached = fieldCache.get(k)\n if (cached) return cached\n const accessor = (): unknown => (entry.current as Record<string, unknown>)[k]\n ;(accessor as unknown as { __perItem: true }).__perItem = true\n fieldCache.set(k, accessor)\n return accessor\n },\n }) as ItemAccessor<T>\n return itemProxy\n }\n\n const indexAccessor = (): number => entry.index\n\n buildCtx.rootLifetime = scope\n buildCtx.state = state\n buildCtx.allBindings = ctx.allBindings\n buildCtx.structuralBlocks = ctx.structuralBlocks\n buildCtx.instance = ctx.instance\n const prevFlat = getFlatBindings()\n setFlatBindings(ctx.allBindings)\n setRenderContext(buildCtx)\n\n const nodes = opts.render({\n send,\n item: getItemProxy(),\n acc: itemFn,\n index: indexAccessor,\n })\n\n clearRenderContext()\n setFlatBindings(prevFlat)\n setRenderContext(ctx)\n\n for (const node of nodes) wrapper.appendChild(node)\n return entry\n }\n\n const reconcile = (state: S): void => {\n const items = opts.items(state)\n lastItems = items\n\n // Update spacer total height\n spacer.style.height = `${items.length * opts.itemHeight}px`\n\n if (items.length === 0) {\n // Dispose all entries\n for (const entry of entries.values()) {\n entry.scope.disposalCause = 'each-remove'\n disposeLifetime(entry.scope)\n if (entry.wrapper.parentNode) entry.wrapper.parentNode.removeChild(entry.wrapper)\n }\n entries.clear()\n return\n }\n\n const [start, end] = computeRange(items.length)\n\n // Build a map of key → {item, index} for the visible range\n const visibleKeys = new Map<string | number, { item: T; index: number }>()\n for (let i = start; i < end; i++) {\n const item = items[i]!\n visibleKeys.set(opts.key(item), { item, index: i })\n }\n\n // Dispose entries no longer visible\n for (const [key, entry] of entries) {\n if (!visibleKeys.has(key)) {\n entry.scope.disposalCause = 'each-remove'\n disposeLifetime(entry.scope)\n if (entry.wrapper.parentNode) entry.wrapper.parentNode.removeChild(entry.wrapper)\n entries.delete(key)\n }\n }\n\n // Create new entries + update existing\n for (const [key, { item, index }] of visibleKeys) {\n const existing = entries.get(key)\n if (existing) {\n existing.current = item\n if (existing.index !== index) {\n existing.index = index\n existing.wrapper.style.top = `${index * opts.itemHeight}px`\n }\n } else {\n const entry = buildEntry(item, index, state)\n entries.set(key, entry)\n spacer.appendChild(entry.wrapper)\n }\n }\n }\n\n // Scroll handler — reconcile visible range without touching component state\n const onScroll = (): void => {\n scrollTop = scroll.scrollTop\n reconcile(ctx.state as S)\n }\n scroll.addEventListener('scroll', onScroll, { passive: true })\n\n // Register as a structural block BEFORE initial render so this block\n // precedes any nested blocks its rows register. See branch.ts for the\n // full rationale (Phase 1 iteration safety).\n const block: StructuralBlock = {\n mask: FULL_MASK,\n reconcile(state: unknown) {\n const newItems = opts.items(state as S)\n if (newItems === lastItems) return\n reconcile(state as S)\n },\n }\n blocks.push(block)\n\n // Initial render\n reconcile(ctx.state as S)\n\n // Cleanup on parent disposal\n addDisposer(parentLifetime, () => {\n scroll.removeEventListener('scroll', onScroll)\n for (const entry of entries.values()) {\n disposeLifetime(entry.scope)\n }\n entries.clear()\n })\n\n return [scroll]\n}\n"]}
@@ -1,7 +1,7 @@
1
- import type { Scope, Binding } from './types.js';
1
+ import type { Lifetime, Binding } from './types.js';
2
2
  import type { StructuralBlock } from './structural.js';
3
3
  export interface RenderContext {
4
- rootScope: Scope;
4
+ rootLifetime: Lifetime;
5
5
  state: unknown;
6
6
  allBindings: Binding[];
7
7
  structuralBlocks: StructuralBlock[];
@@ -1 +1 @@
1
- {"version":3,"file":"render-context.d.ts","sourceRoot":"","sources":["../src/render-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAChD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAGtD,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,KAAK,CAAA;IAChB,KAAK,EAAE,OAAO,CAAA;IACd,WAAW,EAAE,OAAO,EAAE,CAAA;IACtB,gBAAgB,EAAE,eAAe,EAAE,CAAA;IACnC,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAA;CAM9B;AAID,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAEzD;AAED,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC;AAED,wBAAgB,gBAAgB,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,aAAa,CAgBtE"}
1
+ {"version":3,"file":"render-context.d.ts","sourceRoot":"","sources":["../src/render-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAGtD,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,QAAQ,CAAA;IACtB,KAAK,EAAE,OAAO,CAAA;IACd,WAAW,EAAE,OAAO,EAAE,CAAA;IACtB,gBAAgB,EAAE,eAAe,EAAE,CAAA;IACnC,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAA;CAM9B;AAID,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAEzD;AAED,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC;AAED,wBAAgB,gBAAgB,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,aAAa,CAgBtE"}
@@ -1 +1 @@
1
- {"version":3,"file":"render-context.js","sourceRoot":"","sources":["../src/render-context.ts"],"names":[],"mappings":"AAkBA,IAAI,cAAc,GAAyB,IAAI,CAAA;AAE/C,MAAM,UAAU,gBAAgB,CAAC,GAAkB;IACjD,cAAc,GAAG,GAAG,CAAA;AACtB,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,cAAc,GAAG,IAAI,CAAA;AACvB,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,aAAsB;IACrD,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,MAAM,IAAI,GAAG,aAAa,CAAC,CAAC,CAAC,GAAG,aAAa,IAAI,CAAC,CAAC,CAAC,YAAY,CAAA;QAChE,MAAM,IAAI,KAAK,CACb,UAAU,IAAI,4DAA4D;YACxE,0DAA0D;YAC1D,sEAAsE;YACtE,yEAAyE;YACzE,0EAA0E;YAC1E,0EAA0E;YAC1E,gCAAgC;YAChC,yEAAyE;YACzE,sEAAsE,CACzE,CAAA;IACH,CAAC;IACD,OAAO,cAAc,CAAA;AACvB,CAAC","sourcesContent":["import type { Scope, Binding } from './types.js'\nimport type { StructuralBlock } from './structural.js'\nimport type { ComponentInstance } from './update-loop.js'\n\nexport interface RenderContext {\n rootScope: Scope\n state: unknown\n allBindings: Binding[]\n structuralBlocks: StructuralBlock[]\n container?: Element\n send?: (msg: unknown) => void\n /** @internal dev-only — the owning ComponentInstance. Set by mount /\n * hydrate / child to let primitives (currently `each`) emit tracker\n * data to `inst._eachDiffLog`. Nested contexts pass through via\n * spread (e.g. `{ ...ctx, rootScope }`). Undefined outside dev. */\n instance?: ComponentInstance\n}\n\nlet currentContext: RenderContext | null = null\n\nexport function setRenderContext(ctx: RenderContext): void {\n currentContext = ctx\n}\n\nexport function clearRenderContext(): void {\n currentContext = null\n}\n\nexport function getRenderContext(primitiveName?: string): RenderContext {\n if (!currentContext) {\n const name = primitiveName ? `${primitiveName}()` : 'primitives'\n throw new Error(\n `[LLui] ${name} can only be called inside a component's view() function. ` +\n `It was called outside a render context. Common causes:\\n` +\n ` 1. Calling a primitive at module scope instead of inside view().\\n` +\n ` 2. Calling an overlay helper (dialog.overlay, popover.overlay, …) at ` +\n `module scope — these internally use show()/branch() and must be invoked ` +\n `from inside the component's view callback so their result can be spread ` +\n `into the returned node tree.\\n` +\n ` 3. Calling a primitive from a setTimeout / Promise / event handler — ` +\n `the render context only persists during the synchronous view() call.`,\n )\n }\n return currentContext\n}\n"]}
1
+ {"version":3,"file":"render-context.js","sourceRoot":"","sources":["../src/render-context.ts"],"names":[],"mappings":"AAkBA,IAAI,cAAc,GAAyB,IAAI,CAAA;AAE/C,MAAM,UAAU,gBAAgB,CAAC,GAAkB;IACjD,cAAc,GAAG,GAAG,CAAA;AACtB,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,cAAc,GAAG,IAAI,CAAA;AACvB,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,aAAsB;IACrD,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,MAAM,IAAI,GAAG,aAAa,CAAC,CAAC,CAAC,GAAG,aAAa,IAAI,CAAC,CAAC,CAAC,YAAY,CAAA;QAChE,MAAM,IAAI,KAAK,CACb,UAAU,IAAI,4DAA4D;YACxE,0DAA0D;YAC1D,sEAAsE;YACtE,yEAAyE;YACzE,0EAA0E;YAC1E,0EAA0E;YAC1E,gCAAgC;YAChC,yEAAyE;YACzE,sEAAsE,CACzE,CAAA;IACH,CAAC;IACD,OAAO,cAAc,CAAA;AACvB,CAAC","sourcesContent":["import type { Lifetime, Binding } from './types.js'\nimport type { StructuralBlock } from './structural.js'\nimport type { ComponentInstance } from './update-loop.js'\n\nexport interface RenderContext {\n rootLifetime: Lifetime\n state: unknown\n allBindings: Binding[]\n structuralBlocks: StructuralBlock[]\n container?: Element\n send?: (msg: unknown) => void\n /** @internal dev-only — the owning ComponentInstance. Set by mount /\n * hydrate / child to let primitives (currently `each`) emit tracker\n * data to `inst._eachDiffLog`. Nested contexts pass through via\n * spread (e.g. `{ ...ctx, rootLifetime }`). Undefined outside dev. */\n instance?: ComponentInstance\n}\n\nlet currentContext: RenderContext | null = null\n\nexport function setRenderContext(ctx: RenderContext): void {\n currentContext = ctx\n}\n\nexport function clearRenderContext(): void {\n currentContext = null\n}\n\nexport function getRenderContext(primitiveName?: string): RenderContext {\n if (!currentContext) {\n const name = primitiveName ? `${primitiveName}()` : 'primitives'\n throw new Error(\n `[LLui] ${name} can only be called inside a component's view() function. ` +\n `It was called outside a render context. Common causes:\\n` +\n ` 1. Calling a primitive at module scope instead of inside view().\\n` +\n ` 2. Calling an overlay helper (dialog.overlay, popover.overlay, …) at ` +\n `module scope — these internally use show()/branch() and must be invoked ` +\n `from inside the component's view callback so their result can be spread ` +\n `into the returned node tree.\\n` +\n ` 3. Calling a primitive from a setTimeout / Promise / event handler — ` +\n `the render context only persists during the synchronous view() call.`,\n )\n }\n return currentContext\n}\n"]}
package/dist/ssr.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ComponentDef, Scope, Binding } from './types.js';
1
+ import type { ComponentDef, Lifetime, Binding } from './types.js';
2
2
  import { type ComponentInstance } from './update-loop.js';
3
3
  /**
4
4
  * Render a component to DOM nodes for SSR, returning both the produced
@@ -6,14 +6,14 @@ import { type ComponentInstance } from './update-loop.js';
6
6
  * serializing — e.g. `@llui/vike` stitches layout + page nodes at the
7
7
  * `pageSlot()` marker position before one final serialization pass).
8
8
  *
9
- * Accepts an optional `parentScope` so the rendered instance's rootScope
9
+ * Accepts an optional `parentLifetime` so the rendered instance's rootLifetime
10
10
  * becomes a child of an existing scope tree — used by persistent layouts
11
11
  * so contexts provided by an outer layout are reachable from an inner
12
12
  * page via `useContext`.
13
13
  *
14
14
  * Call `initSsrDom()` once before using this on the server.
15
15
  */
16
- export declare function renderNodes<S, M, E>(def: ComponentDef<S, M, E>, initialState?: S, parentScope?: Scope): {
16
+ export declare function renderNodes<S, M, E, D = void>(def: ComponentDef<S, M, E, D>, initialState?: S, parentLifetime?: Lifetime): {
17
17
  nodes: Node[];
18
18
  inst: ComponentInstance<S, M, E>;
19
19
  };
@@ -38,5 +38,5 @@ export declare function serializeNodes(nodes: Node[], bindings: Binding[]): stri
38
38
  * For persistent layouts, use `renderNodes` + `serializeNodes` directly
39
39
  * so layout and page nodes can be composed before serialization.
40
40
  */
41
- export declare function renderToString<S, M, E>(def: ComponentDef<S, M, E>, initialState?: S): string;
41
+ export declare function renderToString<S, M, E, D = void>(def: ComponentDef<S, M, E, D>, initialState?: S): string;
42
42
  //# sourceMappingURL=ssr.d.ts.map
package/dist/ssr.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ssr.d.ts","sourceRoot":"","sources":["../src/ssr.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAC9D,OAAO,EAA2B,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAKlF;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EACjC,GAAG,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC1B,YAAY,CAAC,EAAE,CAAC,EAChB,WAAW,CAAC,EAAE,KAAK,GAClB;IAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;CAAE,CAiBrD;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAezE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,YAAY,CAAC,EAAE,CAAC,GAAG,MAAM,CAG5F"}
1
+ {"version":3,"file":"ssr.d.ts","sourceRoot":"","sources":["../src/ssr.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AACjE,OAAO,EAA2B,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAKlF;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,IAAI,EAC3C,GAAG,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC7B,YAAY,CAAC,EAAE,CAAC,EAChB,cAAc,CAAC,EAAE,QAAQ,GACxB;IAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;CAAE,CAiBrD;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAezE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,IAAI,EAC9C,GAAG,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC7B,YAAY,CAAC,EAAE,CAAC,GACf,MAAM,CAGR"}
package/dist/ssr.js CHANGED
@@ -8,15 +8,15 @@ import { createView } from './view-helpers.js';
8
8
  * serializing — e.g. `@llui/vike` stitches layout + page nodes at the
9
9
  * `pageSlot()` marker position before one final serialization pass).
10
10
  *
11
- * Accepts an optional `parentScope` so the rendered instance's rootScope
11
+ * Accepts an optional `parentLifetime` so the rendered instance's rootLifetime
12
12
  * becomes a child of an existing scope tree — used by persistent layouts
13
13
  * so contexts provided by an outer layout are reachable from an inner
14
14
  * page via `useContext`.
15
15
  *
16
16
  * Call `initSsrDom()` once before using this on the server.
17
17
  */
18
- export function renderNodes(def, initialState, parentScope) {
19
- const inst = createComponentInstance(def, undefined, parentScope ?? null);
18
+ export function renderNodes(def, initialState, parentLifetime) {
19
+ const inst = createComponentInstance(def, undefined, parentLifetime ?? null);
20
20
  if (initialState !== undefined) {
21
21
  inst.state = initialState;
22
22
  }
package/dist/ssr.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ssr.js","sourceRoot":"","sources":["../src/ssr.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ComponentDef, Scope, Binding } from './types.js'\nimport { createComponentInstance, type ComponentInstance } from './update-loop.js'\nimport { setRenderContext, clearRenderContext } from './render-context.js'\nimport { setFlatBindings } from './binding.js'\nimport { createView } from './view-helpers.js'\n\n/**\n * Render a component to DOM nodes for SSR, returning both the produced\n * nodes and the component instance (so callers can compose trees before\n * serializing — e.g. `@llui/vike` stitches layout + page nodes at the\n * `pageSlot()` marker position before one final serialization pass).\n *\n * Accepts an optional `parentScope` so the rendered instance's rootScope\n * becomes a child of an existing scope tree — used by persistent layouts\n * so contexts provided by an outer layout are reachable from an inner\n * page via `useContext`.\n *\n * Call `initSsrDom()` once before using this on the server.\n */\nexport function renderNodes<S, M, E>(\n def: ComponentDef<S, M, E>,\n initialState?: S,\n parentScope?: Scope,\n): { nodes: Node[]; inst: ComponentInstance<S, M, E> } {\n const inst = createComponentInstance(def, undefined, parentScope ?? null)\n if (initialState !== undefined) {\n inst.state = initialState\n }\n\n setFlatBindings(inst.allBindings)\n setRenderContext({\n ...inst,\n send: inst.send as (msg: unknown) => void,\n instance: inst as ComponentInstance,\n })\n const nodes = def.view(createView<S, M>(inst.send))\n clearRenderContext()\n setFlatBindings(null)\n\n return { nodes, inst }\n}\n\n/**\n * Serialize an array of DOM nodes to an HTML string, adding\n * `data-llui-hydrate` markers on elements that own reactive bindings.\n *\n * Accepts a flat binding list so compositions of multiple instances\n * (layout + page, for persistent-layout SSR) produce correct markers\n * across the whole tree. Pass the union of every composed instance's\n * `allBindings`.\n */\nexport function serializeNodes(nodes: Node[], bindings: Binding[]): string {\n const hydrateElements = new Set<Node>()\n for (const binding of bindings) {\n const node = binding.node\n if (node.nodeType === 1) {\n hydrateElements.add(node)\n } else if (node.parentNode && node.parentNode.nodeType === 1) {\n hydrateElements.add(node.parentNode)\n }\n }\n let html = ''\n for (const node of nodes) {\n html += nodeToStringWithMarkers(node, hydrateElements)\n }\n return html\n}\n\n/**\n * Render a component to an HTML string for SSR.\n * Evaluates view() against the initial state (or provided data),\n * serializes the DOM to HTML, and adds data-llui-hydrate markers\n * on nodes with reactive bindings.\n *\n * Call initSsrDom() once before using this on the server.\n *\n * For persistent layouts, use `renderNodes` + `serializeNodes` directly\n * so layout and page nodes can be composed before serialization.\n */\nexport function renderToString<S, M, E>(def: ComponentDef<S, M, E>, initialState?: S): string {\n const { nodes, inst } = renderNodes(def, initialState)\n return serializeNodes(nodes, inst.allBindings)\n}\n\nfunction nodeToStringWithMarkers(node: Node, bindingNodes: Set<Node>): string {\n if (node.nodeType === 3) {\n return escapeHtml(node.textContent ?? '')\n }\n if (node.nodeType === 8) {\n return `<!--${node.textContent ?? ''}-->`\n }\n if (node.nodeType === 1) {\n const el = node as Element\n return elementToString(el, bindingNodes)\n }\n return ''\n}\n\nfunction elementToString(el: Element, bindingNodes: Set<Node>): string {\n const tag = el.tagName.toLowerCase()\n let attrs = ''\n\n for (let i = 0; i < el.attributes.length; i++) {\n const attr = el.attributes[i]!\n // Skip event handler attributes\n if (attr.name.startsWith('on')) continue\n attrs += ` ${attr.name}=\"${escapeAttr(attr.value)}\"`\n }\n\n // Add hydrate marker if this element or any of its text children have bindings\n if (bindingNodes.has(el)) {\n attrs += ' data-llui-hydrate'\n }\n\n // Void elements\n if (isVoidElement(tag)) {\n return `<${tag}${attrs} />`\n }\n\n let children = ''\n for (let i = 0; i < el.childNodes.length; i++) {\n children += nodeToStringWithMarkers(el.childNodes[i]!, bindingNodes)\n }\n\n return `<${tag}${attrs}>${children}</${tag}>`\n}\n\nfunction escapeHtml(s: string): string {\n return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')\n}\n\nfunction escapeAttr(s: string): string {\n return s.replace(/&/g, '&amp;').replace(/\"/g, '&quot;')\n}\n\nconst VOID_ELEMENTS = new Set([\n 'area',\n 'base',\n 'br',\n 'col',\n 'embed',\n 'hr',\n 'img',\n 'input',\n 'link',\n 'meta',\n 'param',\n 'source',\n 'track',\n 'wbr',\n])\n\nfunction isVoidElement(tag: string): boolean {\n return VOID_ELEMENTS.has(tag)\n}\n"]}
1
+ {"version":3,"file":"ssr.js","sourceRoot":"","sources":["../src/ssr.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ComponentDef, Lifetime, Binding } from './types.js'\nimport { createComponentInstance, type ComponentInstance } from './update-loop.js'\nimport { setRenderContext, clearRenderContext } from './render-context.js'\nimport { setFlatBindings } from './binding.js'\nimport { createView } from './view-helpers.js'\n\n/**\n * Render a component to DOM nodes for SSR, returning both the produced\n * nodes and the component instance (so callers can compose trees before\n * serializing — e.g. `@llui/vike` stitches layout + page nodes at the\n * `pageSlot()` marker position before one final serialization pass).\n *\n * Accepts an optional `parentLifetime` so the rendered instance's rootLifetime\n * becomes a child of an existing scope tree — used by persistent layouts\n * so contexts provided by an outer layout are reachable from an inner\n * page via `useContext`.\n *\n * Call `initSsrDom()` once before using this on the server.\n */\nexport function renderNodes<S, M, E, D = void>(\n def: ComponentDef<S, M, E, D>,\n initialState?: S,\n parentLifetime?: Lifetime,\n): { nodes: Node[]; inst: ComponentInstance<S, M, E> } {\n const inst = createComponentInstance(def, undefined, parentLifetime ?? null)\n if (initialState !== undefined) {\n inst.state = initialState\n }\n\n setFlatBindings(inst.allBindings)\n setRenderContext({\n ...inst,\n send: inst.send as (msg: unknown) => void,\n instance: inst as ComponentInstance,\n })\n const nodes = def.view(createView<S, M>(inst.send))\n clearRenderContext()\n setFlatBindings(null)\n\n return { nodes, inst }\n}\n\n/**\n * Serialize an array of DOM nodes to an HTML string, adding\n * `data-llui-hydrate` markers on elements that own reactive bindings.\n *\n * Accepts a flat binding list so compositions of multiple instances\n * (layout + page, for persistent-layout SSR) produce correct markers\n * across the whole tree. Pass the union of every composed instance's\n * `allBindings`.\n */\nexport function serializeNodes(nodes: Node[], bindings: Binding[]): string {\n const hydrateElements = new Set<Node>()\n for (const binding of bindings) {\n const node = binding.node\n if (node.nodeType === 1) {\n hydrateElements.add(node)\n } else if (node.parentNode && node.parentNode.nodeType === 1) {\n hydrateElements.add(node.parentNode)\n }\n }\n let html = ''\n for (const node of nodes) {\n html += nodeToStringWithMarkers(node, hydrateElements)\n }\n return html\n}\n\n/**\n * Render a component to an HTML string for SSR.\n * Evaluates view() against the initial state (or provided data),\n * serializes the DOM to HTML, and adds data-llui-hydrate markers\n * on nodes with reactive bindings.\n *\n * Call initSsrDom() once before using this on the server.\n *\n * For persistent layouts, use `renderNodes` + `serializeNodes` directly\n * so layout and page nodes can be composed before serialization.\n */\nexport function renderToString<S, M, E, D = void>(\n def: ComponentDef<S, M, E, D>,\n initialState?: S,\n): string {\n const { nodes, inst } = renderNodes(def, initialState)\n return serializeNodes(nodes, inst.allBindings)\n}\n\nfunction nodeToStringWithMarkers(node: Node, bindingNodes: Set<Node>): string {\n if (node.nodeType === 3) {\n return escapeHtml(node.textContent ?? '')\n }\n if (node.nodeType === 8) {\n return `<!--${node.textContent ?? ''}-->`\n }\n if (node.nodeType === 1) {\n const el = node as Element\n return elementToString(el, bindingNodes)\n }\n return ''\n}\n\nfunction elementToString(el: Element, bindingNodes: Set<Node>): string {\n const tag = el.tagName.toLowerCase()\n let attrs = ''\n\n for (let i = 0; i < el.attributes.length; i++) {\n const attr = el.attributes[i]!\n // Skip event handler attributes\n if (attr.name.startsWith('on')) continue\n attrs += ` ${attr.name}=\"${escapeAttr(attr.value)}\"`\n }\n\n // Add hydrate marker if this element or any of its text children have bindings\n if (bindingNodes.has(el)) {\n attrs += ' data-llui-hydrate'\n }\n\n // Void elements\n if (isVoidElement(tag)) {\n return `<${tag}${attrs} />`\n }\n\n let children = ''\n for (let i = 0; i < el.childNodes.length; i++) {\n children += nodeToStringWithMarkers(el.childNodes[i]!, bindingNodes)\n }\n\n return `<${tag}${attrs}>${children}</${tag}>`\n}\n\nfunction escapeHtml(s: string): string {\n return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')\n}\n\nfunction escapeAttr(s: string): string {\n return s.replace(/&/g, '&amp;').replace(/\"/g, '&quot;')\n}\n\nconst VOID_ELEMENTS = new Set([\n 'area',\n 'base',\n 'br',\n 'col',\n 'embed',\n 'hr',\n 'img',\n 'input',\n 'link',\n 'meta',\n 'param',\n 'source',\n 'track',\n 'wbr',\n])\n\nfunction isVoidElement(tag: string): boolean {\n return VOID_ELEMENTS.has(tag)\n}\n"]}
@@ -25,7 +25,7 @@ function createSvgElement(tag, propsOrChildren, maybeChildren) {
25
25
  const key = kind === 'class' ? undefined : rawKey;
26
26
  const accessor = value;
27
27
  const perItem = value.length === 0;
28
- const binding = createBinding(ctx.rootScope, {
28
+ const binding = createBinding(ctx.rootLifetime, {
29
29
  mask: FULL_MASK,
30
30
  accessor,
31
31
  kind,