silvery 0.19.2 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -4
- package/dist/Text-Lq0dmj8-.mjs +239 -0
- package/dist/Text-Lq0dmj8-.mjs.map +1 -0
- package/dist/UPNG-Bo33r8rA.mjs +3 -0
- package/dist/UPNG-DosRPdF4.mjs +5075 -0
- package/dist/UPNG-DosRPdF4.mjs.map +1 -0
- package/dist/__vite-browser-external-2447137e-D_JM6skp.mjs +6 -0
- package/dist/__vite-browser-external-2447137e-D_JM6skp.mjs.map +1 -0
- package/dist/{animation-Cn64yepo.mjs → animation-ZMN2_XKv.mjs} +2 -2
- package/dist/animation-ZMN2_XKv.mjs.map +1 -0
- package/dist/{ansi-Cc33mW54.d.mts → ansi-2Xn0yatP.d.mts} +1 -1
- package/dist/{ansi-Cc33mW54.d.mts.map → ansi-2Xn0yatP.d.mts.map} +1 -1
- package/dist/{ansi-CLOitHKx.mjs → ansi-D1KQMAbf.mjs} +1 -1
- package/dist/{ansi-CLOitHKx.mjs.map → ansi-D1KQMAbf.mjs.map} +1 -1
- package/dist/ansi-yC4RyBNY.mjs +22441 -0
- package/dist/ansi-yC4RyBNY.mjs.map +1 -0
- package/dist/apng-CR08rIaH.mjs +58 -0
- package/dist/apng-CR08rIaH.mjs.map +1 -0
- package/dist/apng-DaHfVaVI.mjs +3 -0
- package/dist/assets/resvgjs.darwin-arm64-BtufyGW1.node +0 -0
- package/dist/assets/skia.darwin-arm64-DQs5sT6N.node +0 -0
- package/dist/backend-B-WYLUib.mjs +13396 -0
- package/dist/backend-B-WYLUib.mjs.map +1 -0
- package/dist/backends-CUtan80W.mjs +3 -0
- package/dist/backends-DIVYzKqd.mjs +1083 -0
- package/dist/backends-DIVYzKqd.mjs.map +1 -0
- package/dist/bound-term-0sPrrzH1.d.mts +4640 -0
- package/dist/bound-term-0sPrrzH1.d.mts.map +1 -0
- package/dist/canvas-1v7dPT-_.mjs +3 -0
- package/dist/canvas-CSuPOMNt.mjs +1442 -0
- package/dist/canvas-CSuPOMNt.mjs.map +1 -0
- package/dist/{chunk-Vs_PY4HZ.mjs → chunk-BSw8zbkd.mjs} +1 -1
- package/dist/cli-dvo0r2fs.mjs +4 -0
- package/dist/compare-CQodSH4G.mjs +376 -0
- package/dist/compare-CQodSH4G.mjs.map +1 -0
- package/dist/compare-DHlcxEYA.mjs +3 -0
- package/dist/context-BU5LkkIy.mjs.map +1 -1
- package/dist/devtools-CJdt5H0X.mjs +2 -0
- package/dist/{devtools-DxkSLXDA.mjs → devtools-DcQjgyjL.mjs} +5 -4
- package/dist/{devtools-DxkSLXDA.mjs.map → devtools-DcQjgyjL.mjs.map} +1 -1
- package/dist/easing-BI-ASGMO.d.mts +24 -0
- package/dist/easing-BI-ASGMO.d.mts.map +1 -0
- package/dist/{eta-Bb3RH3wh.mjs → eta-CJlGH06n.mjs} +1 -1
- package/dist/{eta-Bb3RH3wh.mjs.map → eta-CJlGH06n.mjs.map} +1 -1
- package/dist/flexily-zero-adapter-C3Vj0fPt.mjs +306 -0
- package/dist/flexily-zero-adapter-C3Vj0fPt.mjs.map +1 -0
- package/dist/{flexily-zero-adapter-CMxXhdOL.mjs → flexily-zero-adapter-C4lW_Ov5.mjs} +1 -1
- package/dist/fonts-BFmhXDv7.mjs +88 -0
- package/dist/fonts-BFmhXDv7.mjs.map +1 -0
- package/dist/gif-C_AjaT9d.mjs +188 -0
- package/dist/gif-C_AjaT9d.mjs.map +1 -0
- package/dist/gif-DaC4XrxA.mjs +3 -0
- package/dist/gifenc-BOUT-KFB.mjs +730 -0
- package/dist/gifenc-BOUT-KFB.mjs.map +1 -0
- package/dist/image-C2Birh2x.mjs +1252 -0
- package/dist/image-C2Birh2x.mjs.map +1 -0
- package/dist/index-BUMxS65f.d.mts +453 -0
- package/dist/index-BUMxS65f.d.mts.map +1 -0
- package/dist/{index-D3saHouR.d.mts → index-CSQf13CI.d.mts} +1057 -1133
- package/dist/index-CSQf13CI.d.mts.map +1 -0
- package/dist/{index-BXslOebb.d.mts → index-Cl9KKjQ_.d.mts} +4919 -3921
- package/dist/index-Cl9KKjQ_.d.mts.map +1 -0
- package/dist/index-XbNrPhWl.d.mts +336 -0
- package/dist/index-XbNrPhWl.d.mts.map +1 -0
- package/dist/index.d.mts +8 -5
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +14 -12
- package/dist/index.mjs.map +1 -1
- package/dist/key-mapping-CS-YD_cD.mjs +132 -0
- package/dist/key-mapping-CS-YD_cD.mjs.map +1 -0
- package/dist/key-mapping-Yn-Jgrij.mjs +3 -0
- package/dist/{layout-engine-B6Cdz1yZ.mjs → layout-engine-C07LEXWT.mjs} +1 -1
- package/dist/layout-engine-C2px0RJE.mjs +67 -0
- package/dist/layout-engine-C2px0RJE.mjs.map +1 -0
- package/dist/layout-signals-Cnw6xk8Q.mjs +988 -0
- package/dist/layout-signals-Cnw6xk8Q.mjs.map +1 -0
- package/dist/mouse-events-Dki3ISIp.mjs +1044 -0
- package/dist/mouse-events-Dki3ISIp.mjs.map +1 -0
- package/dist/{multi-progress-Bq9Oi_WI.mjs → multi-progress-CIRjrzma.mjs} +3 -3
- package/dist/{multi-progress-Bq9Oi_WI.mjs.map → multi-progress-CIRjrzma.mjs.map} +1 -1
- package/dist/{multi-progress-DAQC7eap.d.mts → multi-progress-DHZ2xUT2.d.mts} +2 -2
- package/dist/{multi-progress-DAQC7eap.d.mts.map → multi-progress-DHZ2xUT2.d.mts.map} +1 -1
- package/dist/{node-BeWlnCPY.mjs → node-CjM5Rt-M.mjs} +4 -4
- package/dist/node-CjM5Rt-M.mjs.map +1 -0
- package/dist/playwright-D5YiZcNS.mjs +76397 -0
- package/dist/playwright-D5YiZcNS.mjs.map +1 -0
- package/dist/png-codec-Dp84742B.mjs +36 -0
- package/dist/png-codec-Dp84742B.mjs.map +1 -0
- package/dist/png-codec-QwOtJ8Zs.mjs +3 -0
- package/dist/progress-DB_Xo071.mjs +675 -0
- package/dist/progress-DB_Xo071.mjs.map +1 -0
- package/dist/{progress-bar-CXE5Qfkd.mjs → progress-bar-oJwq22CR.mjs} +4 -4
- package/dist/{progress-bar-CXE5Qfkd.mjs.map → progress-bar-oJwq22CR.mjs.map} +1 -1
- package/dist/rasterizer-BRXrDdWx.mjs +3 -0
- package/dist/rasterizer-CpEhJvdR.mjs +296 -0
- package/dist/rasterizer-CpEhJvdR.mjs.map +1 -0
- package/dist/reconciler-DldIJB93.mjs +2083 -0
- package/dist/reconciler-DldIJB93.mjs.map +1 -0
- package/dist/{render-string-CDCeYkS3.mjs → render-string-BcoCpjCB.mjs} +1 -1
- package/dist/{render-string-Darrg7ku.mjs → render-string-DkQacASz.mjs} +2707 -549
- package/dist/render-string-DkQacASz.mjs.map +1 -0
- package/dist/resvg-js-DkOndZI3.mjs +203 -0
- package/dist/resvg-js-DkOndZI3.mjs.map +1 -0
- package/dist/runtime.d.mts +3 -2
- package/dist/runtime.mjs +3 -3
- package/dist/schemes-JjNp4aSl.mjs +2611 -0
- package/dist/schemes-JjNp4aSl.mjs.map +1 -0
- package/dist/{spinner-CGo34vyR.d.mts → spinner-CZINHpkV.d.mts} +2 -2
- package/dist/{spinner-CGo34vyR.d.mts.map → spinner-CZINHpkV.d.mts.map} +1 -1
- package/dist/{spinner-CeOmcuw_.mjs → spinner-D9lrHr8s.mjs} +7 -7
- package/dist/spinner-D9lrHr8s.mjs.map +1 -0
- package/dist/src-5w9QR6_8.mjs +1071 -0
- package/dist/src-5w9QR6_8.mjs.map +1 -0
- package/dist/src-BNTToU7l.mjs +4387 -0
- package/dist/src-BNTToU7l.mjs.map +1 -0
- package/dist/{src-CF-6UN01.mjs → src-BR4xNwdG.mjs} +10436 -2622
- package/dist/src-BR4xNwdG.mjs.map +1 -0
- package/dist/{types-Bk2yw9Qj.mjs → src-DKp-_OFG.mjs} +34 -94
- package/dist/src-DKp-_OFG.mjs.map +1 -0
- package/dist/src-bt8wSrfJ.mjs +258 -0
- package/dist/src-bt8wSrfJ.mjs.map +1 -0
- package/dist/src-e33Y6kNJ.mjs +3 -0
- package/dist/src-iDwu25UD.mjs +1814 -0
- package/dist/src-iDwu25UD.mjs.map +1 -0
- package/dist/steps-Bp2uNqnn.d.mts +202 -0
- package/dist/steps-Bp2uNqnn.d.mts.map +1 -0
- package/dist/svg-15lZZzxq.mjs +486 -0
- package/dist/svg-15lZZzxq.mjs.map +1 -0
- package/dist/svg-Cz0UXcDj.mjs +255 -0
- package/dist/svg-Cz0UXcDj.mjs.map +1 -0
- package/dist/svg-DY72a4HK.mjs +3 -0
- package/dist/svg-g1D6ErwR.d.mts +82 -0
- package/dist/svg-g1D6ErwR.d.mts.map +1 -0
- package/dist/term.d.mts +3 -0
- package/dist/term.mjs +9 -0
- package/dist/term.mjs.map +1 -0
- package/dist/theme.d.mts +95 -2
- package/dist/theme.d.mts.map +1 -0
- package/dist/theme.mjs +9 -3
- package/dist/theme.mjs.map +1 -0
- package/dist/{types-BH_v3iMT.d.mts → types-kt_fKR37.d.mts} +2 -15
- package/dist/types-kt_fKR37.d.mts.map +1 -0
- package/dist/ui/animation.d.mts +2 -1
- package/dist/ui/animation.mjs +1 -1
- package/dist/ui/ansi.d.mts +1 -1
- package/dist/ui/ansi.mjs +1 -1
- package/dist/ui/cli.d.mts +3 -3
- package/dist/ui/cli.mjs +5 -5
- package/dist/ui/display.d.mts +1 -1
- package/dist/ui/image.d.mts +2 -2
- package/dist/ui/image.mjs +2 -2
- package/dist/ui/input.d.mts +1 -1
- package/dist/ui/input.mjs +4 -2
- package/dist/ui/input.mjs.map +1 -1
- package/dist/ui/progress.d.mts +5 -249
- package/dist/ui/progress.mjs +5 -858
- package/dist/ui/react.d.mts +1 -1
- package/dist/ui/react.mjs +2 -2
- package/dist/ui/recording-chrome-react.d.mts +21 -0
- package/dist/ui/recording-chrome-react.d.mts.map +1 -0
- package/dist/ui/recording-chrome-react.mjs +105 -0
- package/dist/ui/recording-chrome-react.mjs.map +1 -0
- package/dist/ui/recording-chrome.d.mts +2 -0
- package/dist/ui/recording-chrome.mjs +2 -0
- package/dist/ui/utils.mjs +1 -1
- package/dist/ui/wrappers.d.mts +3 -3
- package/dist/ui/wrappers.mjs +2 -2
- package/dist/ui.d.mts +7 -6
- package/dist/ui.mjs +8 -7
- package/dist/{useLatest-Bg2x4bfP.d.mts → useLatest-DRDDVwjh.d.mts} +5 -25
- package/dist/useLatest-DRDDVwjh.d.mts.map +1 -0
- package/dist/{with-text-input-CRfoiFFG.d.mts → with-text-input-YeohVLeo.d.mts} +4 -55
- package/dist/with-text-input-YeohVLeo.d.mts.map +1 -0
- package/dist/wrapper-C70ATkVv.mjs +3527 -0
- package/dist/wrapper-C70ATkVv.mjs.map +1 -0
- package/dist/{wrappers-UTADQkSY.mjs → wrappers-BCUYITrY.mjs} +5 -157
- package/dist/wrappers-BCUYITrY.mjs.map +1 -0
- package/dist/{yoga-adapter-8oRGRw8V.mjs → yoga-adapter-BnZX1PAY.mjs} +28 -2
- package/dist/yoga-adapter-BnZX1PAY.mjs.map +1 -0
- package/dist/yoga-adapter-DxgsQ_gg.mjs +2 -0
- package/dist/zipBundle-3nqeDRtm.mjs +3 -0
- package/dist/zipBundle-VNAYFmqJ.mjs +2003 -0
- package/dist/zipBundle-VNAYFmqJ.mjs.map +1 -0
- package/package.json +20 -9
- package/dist/animation-Cn64yepo.mjs.map +0 -1
- package/dist/cli-BKp0YtBD.mjs +0 -4
- package/dist/devtools-9QY4teqI.mjs +0 -2
- package/dist/flexily-zero-adapter-BlQa46nr.mjs +0 -3385
- package/dist/flexily-zero-adapter-BlQa46nr.mjs.map +0 -1
- package/dist/image-CTII5QWI.mjs +0 -477
- package/dist/image-CTII5QWI.mjs.map +0 -1
- package/dist/index-BXslOebb.d.mts.map +0 -1
- package/dist/index-BnA7mNpo.d.mts +0 -175
- package/dist/index-BnA7mNpo.d.mts.map +0 -1
- package/dist/index-D3saHouR.d.mts.map +0 -1
- package/dist/layout-engine-ClUgv6jB.mjs +0 -50
- package/dist/layout-engine-ClUgv6jB.mjs.map +0 -1
- package/dist/node-BeWlnCPY.mjs.map +0 -1
- package/dist/reconciler-Cwgm8hRR.mjs +0 -8459
- package/dist/reconciler-Cwgm8hRR.mjs.map +0 -1
- package/dist/render-string-Darrg7ku.mjs.map +0 -1
- package/dist/spinner-CeOmcuw_.mjs.map +0 -1
- package/dist/src-B5GjfG7g.mjs +0 -4305
- package/dist/src-B5GjfG7g.mjs.map +0 -1
- package/dist/src-CChwjk0Z.mjs +0 -738
- package/dist/src-CChwjk0Z.mjs.map +0 -1
- package/dist/src-CF-6UN01.mjs.map +0 -1
- package/dist/src-NCKb8kE5.mjs +0 -2660
- package/dist/src-NCKb8kE5.mjs.map +0 -1
- package/dist/types-BH_v3iMT.d.mts.map +0 -1
- package/dist/types-Bk2yw9Qj.mjs.map +0 -1
- package/dist/ui/progress.d.mts.map +0 -1
- package/dist/ui/progress.mjs.map +0 -1
- package/dist/useLatest-Bg2x4bfP.d.mts.map +0 -1
- package/dist/with-text-input-CRfoiFFG.d.mts.map +0 -1
- package/dist/wrappers-UTADQkSY.mjs.map +0 -1
- package/dist/yoga-adapter-8oRGRw8V.mjs.map +0 -1
- package/dist/yoga-adapter-D_CcxSt5.mjs +0 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reconciler-DldIJB93.mjs","names":["#children","#parent","_isBrandedHandle","_getAdoptedHandles","collectNodeTextContent"],"sources":["../packages/ag/src/dirty-tracking.ts","../packages/ag/src/epoch.ts","../packages/scope/src/trace.ts","../packages/scope/src/handle.ts","../packages/scope/src/index.ts","../packages/ag-term/src/pipeline/collect-text.ts","../packages/ag-term/src/pipeline/measure-stats.ts","../packages/ag-react/src/reconciler/helpers.ts","../packages/ag-react/src/reconciler/nodes.ts","../packages/ag-react/src/reconciler/host-config.ts","../packages/ag-react/src/reconciler/index.ts"],"sourcesContent":["/**\n * Dirty Node Tracking\n *\n * Module-level Set<AgNode> that enables O(1) dirty checks for pipeline phases.\n * The reconciler adds nodes when dirty flags are set; pipeline phases query\n * the set to skip unnecessary work; the set is cleared after each render pass.\n *\n * Two categories tracked:\n * - contentDirtyNodes: nodes with any content/style dirty flag (need re-render)\n * - styleOnlyDirtyNodes: nodes where ONLY style changed (no content, no layout,\n * no children) — eligible for the style-only fast path\n *\n * Layout dirty tracking is NOT here — Flexily owns it via isDirty() propagation.\n * The layout gate in ag.ts / layout-phase.ts checks root.layoutNode.isDirty().\n */\n\nimport type { AgNode } from \"./types\"\n\n/**\n * Nodes with any content/style dirty flag. Written by reconciler,\n * read by render phase for targeted subtree entry.\n */\nconst contentDirtyNodes: Set<AgNode> = new Set()\n\n/**\n * Nodes where ONLY style props changed (no content, layout, or children changes).\n * These are eligible for the style-only fast path in the render phase, which\n * updates cell styles without re-collecting text or re-computing layout.\n *\n * A node is style-only when commitUpdate classifies contentChanged=\"style\"\n * AND layoutChanged=false. The render phase checks this set to decide whether\n * to use restyleRegion() instead of full renderText()/renderBox().\n */\nconst styleOnlyDirtyNodes: Set<AgNode> = new Set()\n\n/**\n * Nodes where scrollTo/scrollOffset changed. These don't affect Flexily layout\n * dimensions, but the scroll, sticky, scrollRect, and notify phases must still\n * run to update visible children positions.\n *\n * Written by reconciler (host-config.ts commitUpdate), read by ag.ts\n * layout-on-demand gate.\n */\nconst scrollDirtyNodes: Set<AgNode> = new Set()\n\n// ---------------------------------------------------------------------------\n// Write API (reconciler)\n// ---------------------------------------------------------------------------\n\n/** Mark a node as content-dirty. Called when content/style flags are set. */\nexport function trackContentDirty(node: AgNode): void {\n contentDirtyNodes.add(node)\n}\n\n/**\n * Mark a node as style-only dirty. Called when commitUpdate sees\n * contentChanged=\"style\" AND layoutChanged=false.\n * If a node is later marked with contentDirty, the render phase ignores\n * the style-only flag (full path takes precedence).\n */\nexport function trackStyleOnlyDirty(node: AgNode): void {\n styleOnlyDirtyNodes.add(node)\n}\n\n/** Mark a node as scroll-dirty. Called when scrollTo/scrollOffset props change. */\nexport function trackScrollDirty(node: AgNode): void {\n scrollDirtyNodes.add(node)\n}\n\n// ---------------------------------------------------------------------------\n// Read API (pipeline phases)\n// ---------------------------------------------------------------------------\n\n/** O(1) check: are there any content-dirty nodes? */\nexport function hasContentDirty(): boolean {\n return contentDirtyNodes.size > 0\n}\n\n/** O(1) check: are there any scroll-dirty nodes? */\nexport function hasScrollDirty(): boolean {\n return scrollDirtyNodes.size > 0\n}\n\n/** O(1) check: is this node style-only dirty (eligible for fast path)? */\nexport function isStyleOnlyDirty(node: AgNode): boolean {\n return styleOnlyDirtyNodes.has(node)\n}\n\n/** Get the set of content-dirty nodes (for iteration). */\nexport function getContentDirtyNodes(): ReadonlySet<AgNode> {\n return contentDirtyNodes\n}\n\n// ---------------------------------------------------------------------------\n// Clear API (after render pass)\n// ---------------------------------------------------------------------------\n\n/** Clear all dirty tracking. Called after each render pass completes. */\nexport function clearDirtyTracking(): void {\n contentDirtyNodes.clear()\n styleOnlyDirtyNodes.clear()\n scrollDirtyNodes.clear()\n}\n","/**\n * Render Epoch + Bit-Packed Dirty Flags\n *\n * A monotonically increasing counter that replaces boolean dirty flags.\n * Instead of setting `node.contentDirty = true` and later clearing with\n * `node.contentDirty = false`, the reconciler stamps `node.dirtyEpoch = renderEpoch`\n * and sets the appropriate bit in `node.dirtyBits`. The render phase checks\n * `node.dirtyEpoch === renderEpoch && (node.dirtyBits & BIT) !== 0`.\n *\n * Clearing all flags is O(1): just `renderEpoch++`. The old O(N) tree walk\n * in clearDirtyFlags becomes unnecessary — stale epoch stamps automatically\n * read as \"not dirty\" once the epoch advances.\n *\n * INITIAL_EPOCH (-1) is the sentinel for \"never dirty\". New nodes use the\n * current epoch so they appear dirty on first render.\n *\n * ## Bit-Packed Dirty Flags (S-MEM)\n *\n * Seven dirty flags are packed into a single `dirtyBits` number field:\n * bit 0: content (text content or content-affecting props changed)\n * bit 1: styleProps (visual props changed: color, bg, border, etc.)\n * bit 2: bg (backgroundColor specifically changed)\n * bit 3: children (direct children added/removed/reordered)\n * bit 4: subtree (this node or any descendant has dirty content/layout)\n * bit 5: absoluteChildMutated (absolute child had structural changes)\n * bit 6: descendantOverflow (descendant overflow changed)\n *\n * Note: outlines do NOT get a dirty bit — they're handled by the separate\n * decoration phase (see pipeline/decoration-phase.ts) which redraws them\n * every frame using per-cell snapshots.\n *\n * Combined with `dirtyEpoch`, this reduces per-node memory from 56 bytes\n * (7 separate epoch fields × 8 bytes) to 16 bytes (2 fields × 8 bytes).\n */\n\n/** Sentinel value: node has never been marked dirty for this flag. */\nexport const INITIAL_EPOCH = -1\n\n// ============================================================================\n// Dirty Bit Constants\n// ============================================================================\n\n/** Content changed (text content or content-affecting props). */\nexport const CONTENT_BIT = 1 << 0 // 0b0000001\n/** Visual style props changed (color, bg, border, etc.). */\nexport const STYLE_PROPS_BIT = 1 << 1 // 0b0000010\n/** backgroundColor specifically changed. */\nexport const BG_BIT = 1 << 2 // 0b0000100\n/** Direct children added, removed, or reordered. */\nexport const CHILDREN_BIT = 1 << 3 // 0b0001000\n/** This node or any descendant has dirty content/layout. */\nexport const SUBTREE_BIT = 1 << 4 // 0b0010000\n/** Absolute-positioned child had structural changes. */\nexport const ABS_CHILD_BIT = 1 << 5 // 0b0100000\n/** Descendant overflow changed. */\nexport const DESC_OVERFLOW_BIT = 1 << 6 // 0b1000000\n\n/** All reconciler-owned bits (content + styleProps + bg + children + subtree). */\nexport const ALL_RECONCILER_BITS =\n CONTENT_BIT | STYLE_PROPS_BIT | BG_BIT | CHILDREN_BIT | SUBTREE_BIT\n\n/** All bits combined. */\nexport const ALL_BITS =\n CONTENT_BIT |\n STYLE_PROPS_BIT |\n BG_BIT |\n CHILDREN_BIT |\n SUBTREE_BIT |\n ABS_CHILD_BIT |\n DESC_OVERFLOW_BIT\n\n/**\n * The current render epoch. Incremented after each render pass.\n * Reconciler stamps dirty nodes with this value; render phase checks equality.\n */\nlet renderEpoch = 0\n\n/** Get the current render epoch value. */\nexport function getRenderEpoch(): number {\n return renderEpoch\n}\n\n/**\n * Advance the render epoch. Called once at the end of each render pass.\n * All nodes stamped with the old epoch instantly become \"not dirty\".\n */\nexport function advanceRenderEpoch(): void {\n renderEpoch++\n}\n\n/**\n * Check if an epoch stamp matches the current render epoch (i.e., \"is dirty\").\n */\nexport function isCurrentEpoch(epoch: number): boolean {\n return epoch === renderEpoch\n}\n\n// ============================================================================\n// Bit-Packed Dirty Flag Helpers\n// ============================================================================\n\n/**\n * Check if a specific dirty bit is set for the current epoch.\n * Returns true if dirtyEpoch matches the current render epoch AND the bit is set.\n */\nexport function isDirty(dirtyBits: number, dirtyEpoch: number, bit: number): boolean {\n return dirtyEpoch === renderEpoch && (dirtyBits & bit) !== 0\n}\n\n/**\n * Check if ANY dirty bit is set for the current epoch.\n */\nexport function isAnyDirty(dirtyBits: number, dirtyEpoch: number): boolean {\n return dirtyEpoch === renderEpoch && dirtyBits !== 0\n}\n\n/**\n * Set a dirty bit on a node. If the epoch has changed since last write,\n * resets all bits and starts fresh with only the new bit.\n *\n * @returns The new dirtyBits value (caller must assign to node.dirtyBits).\n */\nexport function setDirtyBit(dirtyBits: number, dirtyEpoch: number, bit: number): number {\n if (dirtyEpoch !== renderEpoch) {\n return bit // new epoch — reset to just this bit\n }\n return dirtyBits | bit // same epoch — add bit\n}\n","/**\n * @silvery/scope/trace — opt-in leak detector for scopes and disposables.\n *\n * Gated by `SILVERY_SCOPE_TRACE=1`. When enabled, every `createScope()`\n * and `disposable()` call is recorded with its creation stack; dispose\n * unregisters; at process exit any remaining entries are logged.\n *\n * Zero overhead when disabled — the public functions are no-ops behind\n * an early-return guard. The trace registry isn't even allocated.\n *\n * Why: silvery's `Scope` (Phase 0/1/2 of `km-silvery.lifecycle-scope`)\n * makes resource ownership explicit, but adoption is opt-in. This is the\n * runtime backstop for what the ESLint `no-raw-lifecycle` rule can't see\n * (dynamic call sites, third-party paths). Together they make\n * convention-driven leaks structurally impossible.\n *\n * Usage in tests / CI:\n * ```bash\n * SILVERY_SCOPE_TRACE=1 bun run test\n * ```\n *\n * The detector logs to stderr at process exit (or `getTraceSnapshot()`\n * is callable any time for in-test assertions). Production builds — no\n * env var set — skip every code path.\n *\n * @packageDocumentation\n */\n\nconst TRACE_ENV = \"SILVERY_SCOPE_TRACE\"\n\nfunction envEnabled(): boolean {\n try {\n return !!(globalThis as { process?: { env?: Record<string, string | undefined> } }).process\n ?.env?.[TRACE_ENV]\n } catch {\n return false\n }\n}\n\nconst traceEnabled = envEnabled()\n\nexport interface TraceEntry {\n /** \"scope\" for Scope instances, \"disposable\" for disposable()-wrapped values. */\n readonly kind: \"scope\" | \"disposable\"\n /** Optional human label (Scope.name, or a tag passed by the caller). */\n readonly name?: string\n /** Creation site — stack trace captured at creation time. */\n readonly createdAt: string\n}\n\n// Allocated only when tracing is enabled. WeakMap so disposed handles\n// don't pin themselves once the GC reclaims them — but we still untrack\n// on explicit dispose to surface real leaks (held references that never\n// got disposed).\nconst live: Map<object, TraceEntry> | null = traceEnabled ? new Map() : null\n\n/** Returns true if `SILVERY_SCOPE_TRACE` is set. Useful for skipping\n * expensive trace-only diagnostics. */\nexport function isTraceEnabled(): boolean {\n return traceEnabled\n}\n\n/** Internal — register a handle on creation. No-op when tracing is off. */\nexport function _trackCreate(handle: object, kind: \"scope\" | \"disposable\", name?: string): void {\n if (!live) return\n // `new Error().stack` includes this function as the top frame; that's\n // fine for skim-reading. Slice off the first two frames if you want\n // less noise — kept simple for now.\n const stack = new Error(\"(creation stack)\").stack ?? \"\"\n live.set(handle, { kind, name, createdAt: stack })\n}\n\n/** Internal — unregister a handle on dispose. No-op when tracing is off. */\nexport function _trackDispose(handle: object): void {\n if (!live) return\n live.delete(handle)\n}\n\n/** Snapshot of currently-undisposed tracked handles. Empty when tracing\n * is off. Useful for in-test leak assertions:\n * ```ts\n * await app.dispose()\n * expect(getTraceSnapshot()).toHaveLength(0)\n * ```\n */\nexport function getTraceSnapshot(): readonly TraceEntry[] {\n if (!live) return []\n return [...live.values()]\n}\n\n/** Force the at-exit report to fire now (for tests / manual diagnostics).\n * Always logs the count, even if zero. No-op when tracing is off. */\nexport function reportTraceLeaks(): number {\n if (!live) return 0\n const count = live.size\n if (count === 0) {\n console.error(\"[silvery:scope:trace] no undisposed handles\")\n return 0\n }\n console.error(`[silvery:scope:trace] ${count} undisposed handle(s):`)\n for (const entry of live.values()) {\n const label = entry.kind + (entry.name ? `(${entry.name})` : \"\")\n console.error(` - ${label}`)\n console.error(entry.createdAt)\n }\n return count\n}\n\n/**\n * Print a per-scope handle-delta diagnostic at scope close. Called from\n * `Scope[Symbol.asyncDispose]()` AFTER the inherited stack drains. Per\n * km-silvery.lifecycle-leak-detection Phase 2: previously the trace only\n * fired at process exit, which left in-test scope-close leaks invisible\n * until the worker terminated.\n *\n * `pre` and `post` are snapshot counts of `getAdoptedHandles(scope).length`\n * before and after scope-close. The delta is reported to stderr when\n * tracing is enabled and the scope did NOT balance.\n */\nexport function reportScopeDelta(scopeName: string | undefined, pre: number, post: number): void {\n if (!traceEnabled) return\n if (post === 0) return // balanced — no signal\n const label = scopeName ? `Scope(${scopeName})` : \"Scope\"\n console.error(\n `[silvery:scope:trace] ${label} close-delta: ${pre} adopted → ${post} undisposed (${pre - post} disposed cleanly)`,\n )\n}\n\n// At-exit hook — fire-and-log. Only when tracing is enabled.\n// Note: `process.on(\"exit\", …)` is the one signal handler that escapes\n// `term.signals` — at exit time the term-owner is already disposed, so\n// we go direct. This file is in `@silvery/scope` (allowlisted by\n// check-no-raw-lifecycle.sh).\nif (traceEnabled) {\n const proc = (globalThis as { process?: { on?: (e: string, fn: () => void) => void } }).process\n if (proc?.on) {\n proc.on(\"exit\", () => {\n reportTraceLeaks()\n })\n }\n}\n","/**\n * @silvery/scope/handle — opaque branded handles + per-scope ownership accounting.\n *\n * Two-layer defense per pro/Kimi review of km-silvery.scope-resource-ownership\n * Phase 1 design (2026-04-26):\n *\n * - **Compile-time** layer: `Handle<B>` with a `[handleBrand]: B` phantom\n * property. Stops accidental literal construction. Does NOT stop `as`\n * escapes (TypeScript permits assertions between overlapping types).\n *\n * - **Runtime** layer: a module-private `WeakSet<object>` records every\n * handle the factory has produced. `adoptHandle()` rejects values that\n * aren't in the set, so `as`-forged handles fail at the library\n * boundary. The handle object is `Object.freeze`d, so `iterable` /\n * `emitted` / `[Symbol.asyncDispose]` cannot be overwritten by callers.\n * Internal metadata (kind, value, owner) lives in WeakMaps off the\n * object — nothing leaks via `Object.getOwnPropertySymbols`.\n *\n * Per-scope (NOT global) accounting addresses the pro/Kimi warning that\n * ambient handles in unrelated scopes can flake CI. Tests in this package\n * pin \"ambient handles in scope A do not leak detection in scope B.\"\n *\n * What this still doesn't block:\n * 1. `const x = anything as TickHandle` — TypeScript permits the cast at\n * compile time. Lint (an ESLint rule banning `as <Handle-typed-name>`)\n * is the pragmatic remedy. Documented in §\"Limits\" below.\n * 2. Manual `scope.use(handle)` (bypass of `adoptHandle`) — the handle\n * still disposes (good), but per-scope accounting doesn't see it. We\n * route `Scope.use()` through `adoptHandle()` for genuine handles to\n * close this hole. See `index.ts`.\n *\n * Usage from a factory module:\n *\n * ```ts\n * // packages/foo/src/widget.ts\n * import { defineHandle, type Scope } from \"@silvery/scope\"\n *\n * const Widget = defineHandle(\"Widget\")\n * export type WidgetHandle = ReturnType<typeof Widget.create>\n *\n * export function createWidget(scope: Scope): WidgetHandle {\n * const impl = ...\n * const handle = Widget.create(impl, () => impl.close())\n * scope.adoptHandle(handle)\n * return handle\n * }\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { Scope } from \"./index.js\"\n\n// =============================================================================\n// Module-private brand registry — runtime authenticity layer\n// =============================================================================\n\n/**\n * Module-private set of every legitimate handle the factory has produced.\n * `WeakSet` keys by reference, so disposed-and-GC'd handles drop out\n * automatically; this is the canonical source of truth for \"is this a real\n * silvery handle?\" Forged values (created via `as TickHandle` or by hand)\n * are NOT in this set, so `adoptHandle()` and `Scope.use()` reject them.\n */\nconst branded = new WeakSet<object>()\n\n/** Internal storage off the handle so the public surface stays opaque. */\ninterface HandleMetadata {\n readonly kind: string\n /** Approximate creation site — captured at adoption time, not handle birth. */\n createdAt?: string\n}\nconst metadata = new WeakMap<object, HandleMetadata>()\n\n// =============================================================================\n// Deterministic handle counter — C1 L5 invariant\n// =============================================================================\n\n/**\n * Global count of handles currently alive (created but not yet disposed).\n * Incremented in `defineHandle().create()`, decremented once on first\n * dispose (idempotent). WeakSet alone cannot give a count; this integer\n * counter is the parallel deterministic accounting layer.\n *\n * Used by tests to assert structural lifecycle invariants without GC:\n * - After dispose-all: `getActiveHandleCount() === 0`\n * - During N creates: `getActiveHandleCount() === N`\n *\n * Not per-scope (use `getAdoptedHandles(scope)` for per-scope accounting).\n */\nlet _activeHandleCount = 0\n\n/**\n * Return the number of handles currently alive (created but not disposed).\n * Deterministic — no GC required. Zero after all handles in a scope have\n * been disposed.\n *\n * @example\n * ```ts\n * const scope = createScope(\"test\")\n * const tick = createScopedTick(scope, 16)\n * expect(getActiveHandleCount()).toBeGreaterThanOrEqual(1)\n * await scope[Symbol.asyncDispose]()\n * expect(getActiveHandleCount()).toBe(0)\n * ```\n */\nexport function getActiveHandleCount(): number {\n return _activeHandleCount\n}\n\n/** True iff `value` was minted by `defineHandle(...).create(...)`. */\nexport function isBrandedHandle(value: unknown): value is object {\n return typeof value === \"object\" && value !== null && branded.has(value)\n}\n\n// =============================================================================\n// Type-level brand (compile-time layer)\n// =============================================================================\n\n/**\n * Phantom brand used to make `Handle<B>` distinct per resource kind. The\n * brand is never assigned at runtime — it is purely a compile-time fence\n * to prevent accidental object-literal construction. Runtime authenticity\n * is enforced by `branded` (the WeakSet above).\n */\ndeclare const handleBrand: unique symbol\n\n/**\n * Opaque branded handle. The runtime shape is `AsyncDisposable`; the\n * compile-time shape additionally carries `[handleBrand]: B` so an external\n * module cannot satisfy the type from an object literal.\n *\n * Treat as opaque outside its factory module — read no fields, only pass\n * to functions that accept `Handle<B>` and to `scope.adoptHandle(...)`.\n *\n * Limit: `as Handle<B>` and `as unknown as Handle<B>` casts compile, by\n * design of TypeScript's structural assertions. The runtime layer\n * (`branded` WeakSet) catches forged handles when they re-enter the\n * library via `adoptHandle()` or the wrapped `Scope.use()`.\n */\nexport interface Handle<B extends symbol> extends AsyncDisposable {\n readonly [handleBrand]: B\n}\n\n// =============================================================================\n// Handle definition\n// =============================================================================\n\n/**\n * Brand a handle factory. Returns an object with a `create(impl, dispose)`\n * method that produces opaque `Handle<Brand>` values.\n *\n * The returned brand is module-private — only the file that called\n * `defineHandle(\"Foo\")` can construct `Handle<typeof Foo.brand>` values.\n * Other modules can refer to the type (via `ReturnType<typeof Foo.create>`)\n * but cannot forge values.\n *\n * Each call to `defineHandle` produces a fresh `unique symbol` typed brand\n * and a fresh runtime `Symbol`. Two calls with the same `kind` string are\n * still typed-distinct (the `unique symbol` is per-call-site).\n *\n * @param kind Human-readable label for diagnostics (leak reports, traces).\n *\n * @example\n * ```ts\n * const Tick = defineHandle(\"Tick\")\n * export type TickHandle = ReturnType<typeof Tick.create>\n *\n * export function createScopedTick(scope: Scope, intervalMs: number): TickHandle {\n * const id = setTimeout(...)\n * const handle = Tick.create({ id }, () => clearTimeout(id))\n * scope.adoptHandle(handle)\n * return handle\n * }\n * ```\n */\n/**\n * Public return shape of `defineHandle`. Annotated explicitly so tsdown's\n * dts generator can emit a type for the function — without this the\n * inferred return references the per-call-site `unique symbol` `Brand`\n * which is not nameable in the .d.mts (TS2527). The brand is preserved\n * at runtime via the `branded` WeakSet; on the type side we surface\n * `Handle<symbol>` (loses compile-time per-kind distinction but\n * preserves the runtime guarantee + the structural Handle shape).\n */\nexport interface DefinedHandle<K extends string> {\n readonly kind: K\n create<V extends object>(_value: V, dispose: (() => void) | (() => Promise<void>)): Handle<symbol>\n}\n\nexport function defineHandle<K extends string>(kind: K): DefinedHandle<K> {\n // Per-call-site `unique symbol`. The runtime symbol exists only to give\n // the compile-time brand a unique-symbol-typed shape; we never store it\n // on the handle object, so reflection cannot extract it.\n const brand: unique symbol = Symbol(`silvery.handle:${kind}`) as never\n type Brand = typeof brand\n\n return {\n /** Diagnostic label for this handle kind. */\n kind,\n /**\n * Create an opaque, frozen handle that wraps `value` with `dispose`\n * cleanup. The returned value is registered in the module-private\n * `branded` WeakSet so the library can authenticate it later.\n *\n * The handle is `Object.freeze`d before return so callers cannot\n * overwrite `[Symbol.asyncDispose]`, `[Symbol.dispose]`, or any\n * surface property the consuming factory adds via `Object.assign`.\n * Add public surface BEFORE returning from your factory.\n */\n create<V extends object>(\n _value: V,\n dispose: (() => void) | (() => Promise<void>),\n ): Handle<Brand> {\n // `Object.create(null)` — no prototype, so no `toString` / `valueOf`\n // / `hasOwnProperty` surface to spoof.\n const handle = Object.create(null) as Record<PropertyKey, unknown>\n\n // Idempotent dispose guard — prevents the counter from double-decrementing\n // if both the scope wrapper AND a manual [Symbol.asyncDispose]() call fire.\n let _disposed = false\n\n // Disposal symbols are non-writable / non-configurable / non-enumerable\n // so a consumer cannot overwrite or delete them after `freeze`. The\n // `freeze` at the bottom is belt-and-braces — both layers refuse\n // overwrite.\n Object.defineProperty(handle, Symbol.asyncDispose, {\n value: async () => {\n if (!_disposed) {\n _disposed = true\n _activeHandleCount--\n }\n await dispose()\n },\n enumerable: false,\n writable: false,\n configurable: false,\n })\n Object.defineProperty(handle, Symbol.dispose, {\n value: () => {\n if (!_disposed) {\n _disposed = true\n _activeHandleCount--\n }\n void dispose()\n },\n enumerable: false,\n writable: false,\n configurable: false,\n })\n\n branded.add(handle)\n metadata.set(handle, { kind })\n _activeHandleCount++\n\n // NOTE: factory-supplied surface (e.g. TickHandle.iterable) must be\n // attached by the consuming factory BEFORE Object.freeze runs at the\n // call site. The factory is the one that knows the surface; we don't\n // freeze here so the factory can `Object.assign(handle, surface)`\n // and then call `Object.freeze(handle)` itself.\n //\n // Rationale: freezing here would force surface to live behind extra\n // accessors. Better: factories use the helper below.\n return handle as unknown as Handle<Brand>\n },\n }\n}\n\n/**\n * Finalise a handle's public surface and freeze it. Call this from the\n * resource factory AFTER attaching all public properties (e.g. `iterable`,\n * `emitted`) and BEFORE returning to the consumer.\n *\n * @param handle The branded handle returned by `<defineHandle().create>`.\n * @param surface Public properties to attach (each becomes non-enumerable,\n * non-writable, non-configurable so the freeze is real).\n *\n * @example\n * ```ts\n * const handle = Tick.create(internal, stop)\n * finaliseHandle(handle, { iterable, emitted: () => count })\n * return handle as TickHandle\n * ```\n */\nexport function finaliseHandle<H extends object, S extends object>(handle: H, surface: S): H & S {\n if (!branded.has(handle)) {\n throw new TypeError(\"finaliseHandle: not a branded handle (call defineHandle().create first)\")\n }\n for (const key of Object.keys(surface) as (keyof S)[]) {\n Object.defineProperty(handle, key, {\n value: surface[key],\n enumerable: true,\n writable: false,\n configurable: false,\n })\n }\n // Symbol-keyed properties on the surface (rare) — also lock them down.\n for (const sym of Object.getOwnPropertySymbols(surface)) {\n Object.defineProperty(handle, sym, {\n value: (surface as Record<symbol, unknown>)[sym],\n enumerable: false,\n writable: false,\n configurable: false,\n })\n }\n Object.freeze(handle)\n return handle as H & S\n}\n\n// =============================================================================\n// Per-scope ownership registry\n// =============================================================================\n\n/**\n * Internal — readable shape used by `Scope.adoptHandle()` to register handles\n * without depending on `Handle<B>`'s opaque type. `Handle<B>` extends\n * `AsyncDisposable` so it satisfies this shape.\n */\nexport type RegistrableHandle = AsyncDisposable\n\n/** Read the kind label off a branded handle. Throws for non-branded values. */\nexport function getHandleKind(handle: object): string {\n const meta = metadata.get(handle)\n if (!meta) return \"unknown\"\n return meta.kind\n}\n\n/**\n * Diagnostic info about a leaked handle. Surfaced via `LeakedHandlesError`\n * when scope close detects unbalanced accounting.\n */\nexport interface LeakedHandle {\n readonly kind: string\n readonly createdAt?: string\n}\n\n/**\n * Thrown by `assertScopeBalance(scope)` when handles adopted into the scope\n * were not disposed before close. Carries the leak inventory so callers /\n * tests can assert which kinds leaked and how many.\n */\nexport class LeakedHandlesError extends Error {\n readonly leaks: readonly LeakedHandle[]\n readonly scopeName?: string\n\n constructor(leaks: readonly LeakedHandle[], scopeName?: string) {\n const counts = new Map<string, number>()\n for (const leak of leaks) {\n counts.set(leak.kind, (counts.get(leak.kind) ?? 0) + 1)\n }\n const summary = [...counts.entries()].map(([k, n]) => `${k}×${n}`).join(\", \")\n super(\n `Scope${scopeName ? `(${scopeName})` : \"\"} closed with ${leaks.length} undisposed handle(s): ${summary}`,\n )\n this.name = \"LeakedHandlesError\"\n this.leaks = leaks\n this.scopeName = scopeName\n }\n}\n\n// =============================================================================\n// Scope augmentation\n// =============================================================================\n\n/**\n * Per-scope handle accounting. Keyed by `Scope` instance via WeakMap so we\n * don't have to modify the Scope class signature for the minimum-invasive\n * Phase 1 prototype.\n */\nconst ownedHandles = new WeakMap<Scope, Set<RegistrableHandle>>()\nconst handleOrigins = new WeakMap<RegistrableHandle, Scope>()\n\n/**\n * Adopt a `Handle` into the given scope's ownership registry. The handle:\n *\n * - is rejected if it isn't in the module-private `branded` WeakSet\n * (forged values fail here)\n * - is rejected if already owned by a different scope\n * - is registered with `scope.use(...)` for LIFO disposal\n * - subscribes to early manual disposal so the registry stays accurate\n * if the consumer calls `handle[Symbol.asyncDispose]()` directly\n *\n * Idempotent: adopting the same handle twice into the same scope is a no-op.\n */\nexport function adoptHandle(scope: Scope, handle: RegistrableHandle): void {\n if (scope.disposed) {\n throw new ReferenceError(\"Cannot adopt handle into a disposed scope\")\n }\n if (!branded.has(handle as object)) {\n throw new TypeError(\n \"adoptHandle: value is not a silvery handle (forged via 'as' or wrong factory). \" +\n \"Use the resource's createX(scope, ...) factory.\",\n )\n }\n\n let owned = ownedHandles.get(scope)\n if (!owned) {\n owned = new Set()\n ownedHandles.set(scope, owned)\n }\n\n // Cross-scope adoption — second owner would double-dispose.\n const existingOwner = handleOrigins.get(handle)\n if (existingOwner && existingOwner !== scope) {\n throw new TypeError(\n \"Handle already owned by another scope; create a new handle for this scope instead\",\n )\n }\n if (owned.has(handle)) return // idempotent\n\n owned.add(handle)\n handleOrigins.set(handle, scope)\n\n // Update the metadata's createdAt (best-effort) so leak diagnostics carry\n // the adoption stack.\n const existing = metadata.get(handle as object)\n if (existing && !existing.createdAt) {\n metadata.set(handle as object, { ...existing, createdAt: captureCreationStack() })\n }\n\n // Wrap dispose in an idempotent guard so the early-dispose path and the\n // scope-close path don't double-call the underlying cleanup.\n let disposedFlag = false\n const removeFromRegistry = () => {\n if (disposedFlag) return\n disposedFlag = true\n owned.delete(handle)\n handleOrigins.delete(handle)\n }\n\n scope.use({\n [Symbol.asyncDispose]: async () => {\n try {\n if (!disposedFlag) await handle[Symbol.asyncDispose]()\n } finally {\n removeFromRegistry()\n }\n },\n })\n}\n\n/**\n * Snapshot the current set of handles still adopted into this scope.\n * Empty when the scope was never used or all handles are disposed.\n */\nexport function getAdoptedHandles(scope: Scope): readonly LeakedHandle[] {\n const owned = ownedHandles.get(scope)\n if (!owned || owned.size === 0) return []\n const result: LeakedHandle[] = []\n for (const h of owned) {\n const meta = metadata.get(h as object)\n result.push({\n kind: meta?.kind ?? \"unknown\",\n createdAt: meta?.createdAt,\n })\n }\n return result\n}\n\n/**\n * Throw `LeakedHandlesError` if any handles adopted into `scope` remain\n * undisposed. Per-scope, not global — ambient handles in unrelated scopes\n * never trigger this.\n */\nexport function assertScopeBalance(scope: Scope): void {\n const leaks = getAdoptedHandles(scope)\n if (leaks.length === 0) return\n throw new LeakedHandlesError(leaks, scope.name)\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\nfunction captureCreationStack(): string {\n const stack = new Error(\"(handle adoption)\").stack ?? \"\"\n const lines = stack.split(\"\\n\")\n return lines.slice(3).join(\"\\n\")\n}\n","/**\n * @silvery/scope — Structured concurrency scopes for silvery apps.\n *\n * `Scope` is a subclass of TC39's `AsyncDisposableStack` that adds:\n * - an `AbortSignal` that aborts on disposal (and links to a parent's signal)\n * - a `child(name?)` method that creates child scopes with cascade disposal\n * - an overridden `[Symbol.asyncDispose]()` that disposes children before the\n * inherited user disposer stack\n *\n * All disposer-stack semantics (LIFO, async-await, idempotent dispose,\n * `SuppressedError` on multi-throw, post-dispose `ReferenceError`) come\n * from `AsyncDisposableStack` directly.\n *\n * @example\n * ```ts\n * using scope = createScope(\"app\")\n * const proc = scope.use(disposable(\n * child_process.spawn(\"claude\"),\n * p => p.kill(\"SIGTERM\"),\n * ))\n * ```\n *\n * @packageDocumentation\n */\n\nimport { _trackCreate, _trackDispose, reportScopeDelta } from \"./trace.js\"\nimport {\n adoptHandle as _adoptHandle,\n getAdoptedHandles as _getAdoptedHandles,\n isBrandedHandle as _isBrandedHandle,\n type RegistrableHandle,\n} from \"./handle.js\"\n\nexport interface ScopeTimerOptions {\n /** Call `.unref()` on Node/Bun timer handles when available. */\n readonly unref?: boolean\n}\n\n// =============================================================================\n// Scope\n// =============================================================================\n\nexport class Scope extends AsyncDisposableStack {\n readonly signal: AbortSignal\n readonly name?: string\n readonly #children = new Set<Scope>()\n readonly #parent?: Scope\n\n constructor(parent?: Scope, name?: string) {\n super()\n this.name = name\n this.#parent = parent\n _trackCreate(this, \"scope\", name)\n\n const controller = new AbortController()\n this.signal = controller.signal\n this.defer(() => controller.abort())\n\n if (parent) {\n if (parent.disposed) {\n throw new ReferenceError(\"Cannot create child of disposed scope\")\n }\n if (parent.signal.aborted) {\n controller.abort()\n } else {\n const onAbort = () => controller.abort()\n parent.signal.addEventListener(\"abort\", onAbort, { once: true })\n this.defer(() => parent.signal.removeEventListener(\"abort\", onAbort))\n }\n parent.#children.add(this)\n }\n }\n\n /** Create a child scope. Child's signal aborts when this scope's signal does. */\n child(name?: string): Scope {\n return new Scope(this, name)\n }\n\n /**\n * Schedule a one-shot callback owned by this scope. Disposing the scope\n * clears the timer if it has not fired yet. The returned function cancels\n * the timer early and is idempotent.\n */\n timeout(callback: () => void, ms: number, opts?: ScopeTimerOptions): () => void {\n let active = true\n const timer = setTimeout(() => {\n if (!active) return\n active = false\n callback()\n }, ms) as ReturnType<typeof setTimeout> & { unref?: () => void }\n if (opts?.unref === true) timer.unref?.()\n\n const cancel = () => {\n if (!active) return\n active = false\n clearTimeout(timer)\n }\n this.defer(cancel)\n return cancel\n }\n\n /**\n * Schedule a repeating callback owned by this scope. Disposing the scope\n * clears the interval. The returned function cancels the interval early\n * and is idempotent.\n */\n interval(callback: () => void, ms: number, opts?: ScopeTimerOptions): () => void {\n let active = true\n const timer = setInterval(() => {\n if (active) callback()\n }, ms) as ReturnType<typeof setInterval> & { unref?: () => void }\n if (opts?.unref === true) timer.unref?.()\n\n const cancel = () => {\n if (!active) return\n active = false\n clearInterval(timer)\n }\n this.defer(cancel)\n return cancel\n }\n\n /**\n * Cancellation-aware sleep. Resolves when the timer fires, when the scope\n * is disposed, or immediately if the scope is already aborted.\n */\n sleep(ms: number, opts?: ScopeTimerOptions): Promise<void> {\n return new Promise((resolve) => {\n if (this.signal.aborted) {\n resolve()\n return\n }\n\n let done = false\n let cancelTimer: (() => void) | null = null\n const finish = () => {\n if (done) return\n done = true\n cancelTimer?.()\n this.signal.removeEventListener(\"abort\", finish)\n resolve()\n }\n\n this.signal.addEventListener(\"abort\", finish, { once: true })\n cancelTimer = this.timeout(finish, ms, opts)\n this.defer(finish)\n })\n }\n\n /**\n * A debounced wrapper around `fn`, owned by this scope. Each call cancels the\n * pending invocation and reschedules `ms` out, so only the last call in a\n * burst fires. Uses a SINGLE timer slot — no per-call defer accumulation\n * (unlike repeatedly calling {@link timeout}), which is what makes it the\n * right primitive for keystroke/hover debounces. Disposing the scope cancels\n * any pending call; the returned function exposes `.cancel()` to drop a\n * pending call early. Resolves the structured-concurrency adoption need for\n * cancel-and-reschedule timers (@km/all/structured-concurrency).\n */\n debounce<A extends unknown[]>(\n fn: (...args: A) => void,\n ms: number,\n opts?: ScopeTimerOptions,\n ): ((...args: A) => void) & { cancel: () => void } {\n let timer: (ReturnType<typeof setTimeout> & { unref?: () => void }) | undefined\n const cancel = () => {\n if (timer !== undefined) {\n clearTimeout(timer)\n timer = undefined\n }\n }\n this.defer(cancel)\n const debounced = (...args: A) => {\n if (this.signal.aborted) return\n cancel()\n timer = setTimeout(() => {\n timer = undefined\n fn(...args)\n }, ms)\n if (opts?.unref === true) timer.unref?.()\n }\n debounced.cancel = cancel\n return debounced\n }\n\n /**\n * Adopt an opaque {@link Handle} (from `defineHandle()`) into this scope's\n * ownership registry. The handle is added to the inherited disposer stack\n * (LIFO disposal) and tracked separately so {@link assertScopeBalance}\n * can detect leaked handles per-scope without depending on global GC.\n *\n * See `./handle.ts` for the brand-+-registry pattern.\n */\n adoptHandle(handle: RegistrableHandle): void {\n _adoptHandle(this, handle)\n }\n\n /**\n * Adopt an `AsyncDisposable` for LIFO teardown.\n *\n * Branded handles (from `defineHandle()`) are routed through\n * {@link adoptHandle} so per-scope accounting catches them.\n * Non-branded `AsyncDisposable` values use the inherited stack directly.\n *\n * This closes the pro/Kimi-flagged \"scope.use(handle) bypasses ownership\"\n * hole — a forged or hand-rolled `AsyncDisposable` claiming to be a\n * branded handle still goes through the runtime authenticity gate in\n * `adoptHandle`.\n */\n override use<T extends AsyncDisposable | Disposable | null | undefined>(value: T): T {\n if (\n value !== null &&\n value !== undefined &&\n typeof value === \"object\" &&\n _isBrandedHandle(value)\n ) {\n _adoptHandle(this, value as unknown as RegistrableHandle)\n return value\n }\n return super.use(value)\n }\n\n /**\n * Dispose children first, then the inherited user disposer stack.\n * Collects errors across the tree and surfaces them as `SuppressedError`.\n *\n * When `SILVERY_SCOPE_TRACE=1`, prints a per-scope handle-delta to\n * stderr if any adopted handles remained undisposed after the inherited\n * stack ran (km-silvery.lifecycle-leak-detection Phase 2).\n */\n override async [Symbol.asyncDispose](): Promise<void> {\n if (this.disposed) return\n const errors: unknown[] = []\n\n // Snapshot pre-close handle count for the trace diagnostic. WeakMap\n // lookup is O(1) and only allocates a list on demand, so this is\n // ~free even when tracing is off.\n const preCount = _getAdoptedHandles(this).length\n\n // 1. Dispose children first, most-recent first\n const children = [...this.#children].reverse()\n this.#children.clear()\n for (const c of children) {\n try {\n await c[Symbol.asyncDispose]()\n } catch (e) {\n errors.push(e)\n }\n }\n\n // 2. Inherited user disposer stack (LIFO over defer + use + adopt)\n try {\n await super[Symbol.asyncDispose]()\n } catch (e) {\n errors.push(e)\n }\n\n // Handle-delta diagnostic — prints when SILVERY_SCOPE_TRACE=1 AND\n // adopted handles remain undisposed. No output on balanced close.\n const postCount = _getAdoptedHandles(this).length\n reportScopeDelta(this.name, preCount, postCount)\n\n // 3. Remove self from parent so early disposal releases the reference\n if (this.#parent) this.#parent.#children.delete(this)\n\n _trackDispose(this)\n\n if (errors.length === 1) throw errors[0]\n if (errors.length > 1) {\n throw errors.reduce(\n (suppressed, e) => new SuppressedError(e, suppressed, \"multiple dispose errors\"),\n )\n }\n }\n\n /**\n * `AsyncDisposableStack.move()` returns a plain stack that loses Scope's\n * `signal`, `name`, and child registry. Throw rather than silently\n * corrupting invariants. Create a new scope and re-register resources\n * explicitly if you need to relocate ownership.\n */\n override move(): never {\n throw new TypeError(\n \"Scope.move() is not supported — create a new scope and re-register resources explicitly\",\n )\n }\n}\n\n// =============================================================================\n// Factories\n// =============================================================================\n\n/** Create a root scope. Use `scope.child(name?)` for descendants. */\nexport function createScope(name?: string): Scope {\n return new Scope(undefined, name)\n}\n\n/**\n * Wrap a value with a `Symbol.dispose` / `Symbol.asyncDispose` so it can be\n * passed to `scope.use(...)`. Both sync and async disposers supported.\n *\n * @example\n * ```ts\n * const proc = scope.use(disposable(child_process.spawn(\"claude\"), p => p.kill()))\n * ```\n */\n// Async overload listed first: TS overload resolution picks the first matching\n// signature, and async functions (returning Promise<void>) match this one but\n// not the sync `() => void` below.\nexport function disposable<T extends object>(\n value: T,\n dispose: (v: T) => Promise<void>,\n): T & AsyncDisposable\nexport function disposable<T extends object>(value: T, dispose: (v: T) => void): T & Disposable\nexport function disposable(value: object, dispose: (v: object) => void | Promise<void>): object {\n _trackCreate(value, \"disposable\")\n // Attach both symbol methods so either `using` or `await using` works.\n // The caller's overload selects the static type; runtime accepts both.\n return Object.assign(value, {\n [Symbol.dispose]() {\n _trackDispose(value)\n void dispose(value)\n },\n [Symbol.asyncDispose]() {\n _trackDispose(value)\n return Promise.resolve(dispose(value))\n },\n })\n}\n\n// =============================================================================\n// Error reporting\n// =============================================================================\n\n/** Context passed to `reportDisposeError`. */\nexport interface DisposeErrorContext {\n /** Where the fire-and-forget disposal originated. */\n readonly phase: \"react-unmount\" | \"signal\" | \"app-exit\" | \"manual\"\n /** The scope that was being disposed, if known. */\n readonly scope?: Scope\n}\n\nexport type DisposeErrorSink = (error: unknown, context: DisposeErrorContext) => void\n\n// Sink stored on globalThis so duplicate @silvery/scope module copies (e.g.\n// when a test imports the package via a relative path while the runtime\n// imports it via the symlinked node_modules path) share the same sink. Without\n// this, `setDisposeErrorSink(testSink)` from the test only mutates one copy's\n// `currentSink`; the renderer's `reportDisposeError` resolves the other\n// copy's `currentSink` (still the default console-error sink) and the test\n// captures nothing. Bead: km-silvery.scope-phase-1.\nconst SINK_KEY = Symbol.for(\"@silvery/scope/disposeErrorSink\")\nconst defaultSink: DisposeErrorSink = (error, context) => {\n const name = context.scope?.name ?? \"?\"\n // eslint-disable-next-line no-console\n console.error(`[scope dispose error] phase=${context.phase} scope=${name}`, error)\n}\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- globalThis registry\nconst sinkHost: any = globalThis\nif (!sinkHost[SINK_KEY]) sinkHost[SINK_KEY] = { sink: defaultSink }\n\n/**\n * Report a disposal failure from a fire-and-forget context (React unmount,\n * signal handler, app-exit hook). Best-effort; never throws.\n */\nexport function reportDisposeError(error: unknown, context: DisposeErrorContext): void {\n try {\n sinkHost[SINK_KEY].sink(error, context)\n } catch {\n // sink must never throw — swallow to keep the teardown path alive\n }\n}\n\n/** Override the global disposal-error sink (e.g. fail-fast in tests). */\nexport function setDisposeErrorSink(sink: DisposeErrorSink): void {\n sinkHost[SINK_KEY].sink = sink\n}\n\n// =============================================================================\n// withScope plugin (host-level wiring)\n// =============================================================================\n\n/**\n * Minimal duck-typed shape `withScope` looks for to auto-wire host\n * cancellation. Matches `term.signals.on(signal, fn)`, whose return value\n * is both `Disposable` and `AsyncDisposable` so it can be registered\n * directly with `Scope.use(...)`. Kept as a structural type so\n * `@silvery/scope` doesn't have to depend on `@silvery/ag-term`.\n */\ninterface CancelSignalSource {\n on(signal: \"SIGINT\" | \"SIGTERM\", fn: () => void): Disposable & AsyncDisposable\n}\n\ninterface WithScopeAppShape {\n defer(fn: () => void): void\n /** Optional — when present (e.g. composed after `withTerminal`),\n * withScope auto-wires SIGINT/SIGTERM to root-scope dispose. */\n term?: { signals?: CancelSignalSource }\n}\n\n/**\n * Plugin that adds a root scope to the app. The scope is disposed when the\n * app exits. If the app already has a `term.signals` source (i.e. composed\n * after `withTerminal`), SIGINT and SIGTERM also start root disposal —\n * disposal failures flow through `reportDisposeError({ phase: \"signal\" })`.\n *\n * Web-host cancellation (`pagehide` / `beforeunload`) lives in the web\n * runtime, not here.\n */\nexport function withScope(name?: string) {\n return <A extends WithScopeAppShape>(app: A) => {\n const scope = createScope(name ?? \"app\")\n\n const sigSrc = app.term?.signals\n if (sigSrc) {\n const onSignal = () => {\n scope[Symbol.asyncDispose]().catch((error) =>\n reportDisposeError(error, { phase: \"signal\", scope }),\n )\n }\n scope.use(sigSrc.on(\"SIGINT\", onSignal))\n scope.use(sigSrc.on(\"SIGTERM\", onSignal))\n }\n\n app.defer(() => {\n scope[Symbol.asyncDispose]().catch((error) =>\n reportDisposeError(error, { phase: \"app-exit\", scope }),\n )\n })\n return { ...app, scope } as A & { readonly scope: Scope }\n }\n}\n\n// =============================================================================\n// Handle re-exports — opaque branded handles + per-scope ownership registry\n// =============================================================================\n\nexport {\n defineHandle,\n finaliseHandle,\n isBrandedHandle,\n getActiveHandleCount,\n type Handle,\n type RegistrableHandle,\n type LeakedHandle,\n LeakedHandlesError,\n getAdoptedHandles,\n getHandleKind,\n assertScopeBalance,\n} from \"./handle.js\"\n\n// =============================================================================\n// Trace re-exports — diagnostic helpers for SILVERY_SCOPE_TRACE\n// =============================================================================\n\nexport {\n isTraceEnabled,\n getTraceSnapshot,\n reportTraceLeaks,\n reportScopeDelta,\n type TraceEntry,\n} from \"./trace.js\"\n","/**\n * Shared text content collection primitives.\n *\n * Multiple pipeline phases need to collect plain text from node trees:\n * - Measure phase: to compute intrinsic size for fit-content nodes\n * - Render phase: to compute DOM-level truncation budget before ANSI serialization\n * - Reconciler: to feed text to the Yoga measure function\n *\n * All share the same traversal logic: walk children, apply internal_transform,\n * concatenate text. The only difference is whether hidden nodes are skipped\n * (the reconciler skips them because Suspense hides the primary tree;\n * the render phases don't because hidden nodes are already laid out with 0 size).\n *\n * The ANSI-styled variants (collectTextContent in render-text.ts) and the\n * styled-segment variants (collectStyledSegments in render-phase-adapter.ts)\n * have fundamentally different output shapes and child processing logic,\n * so they remain separate.\n */\n\nimport type { AgNode } from \"@silvery/ag/types\"\n\n/**\n * Collect plain text from a node tree, applying internal_transform.\n *\n * This is the base traversal used by:\n * - measure-phase.ts (fit-content measurement)\n * - render-text.ts (DOM-level truncation budget)\n *\n * Does NOT filter hidden or display:none nodes — those are handled by\n * the layout engine (display:none gets 0x0 size) or by other phases.\n */\nexport function collectPlainText(node: AgNode): string {\n if (node.textContent !== undefined) return node.textContent\n let result = \"\"\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i]!\n let childText = collectPlainText(child)\n if (childText.length > 0 && (child.props as any).internal_transform) {\n childText = (child.props as any).internal_transform(childText, i)\n }\n result += childText\n }\n return result\n}\n\n/**\n * Collect plain text from a node tree, skipping hidden children.\n *\n * Used by the reconciler's Yoga measure function where hidden nodes\n * (from Suspense) must not contribute to measured size.\n *\n * Identical to collectPlainText except for the hidden check.\n */\nexport function collectPlainTextSkipHidden(node: AgNode): string {\n if (node.textContent !== undefined) return node.textContent\n let result = \"\"\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i]!\n if (child.hidden) continue\n let childText = collectPlainTextSkipHidden(child)\n if (childText.length > 0 && (child.props as any).internal_transform) {\n childText = (child.props as any).internal_transform(childText, i)\n }\n result += childText\n }\n return result\n}\n","/**\n * Profiling counters for measure function performance analysis (dev only).\n *\n * Shared between @silvery/ag-react/reconciler/nodes (where measure happens)\n * and @silvery/ag-term/pipeline/layout-phase (where stats are logged).\n *\n * Lives in @silvery/ag-term to keep the @silvery/ag-term barrel React-free.\n */\n\nexport const measureStats = {\n calls: 0,\n cacheHits: 0,\n textCollects: 0,\n displayWidthCalls: 0,\n reset() {\n this.calls = 0\n this.cacheHits = 0\n this.textCollects = 0\n this.displayWidthCalls = 0\n },\n}\n","/**\n * Reconciler Helper Functions\n *\n * Utility functions for props comparison and change detection\n * used by the React reconciler during updates.\n */\n\n/**\n * Set of layout-affecting props.\n */\nexport const LAYOUT_PROPS = new Set([\n \"width\",\n \"height\",\n \"minWidth\",\n \"minHeight\",\n \"maxWidth\",\n \"maxHeight\",\n \"flexDirection\",\n \"flexWrap\",\n \"justifyContent\",\n \"alignItems\",\n \"alignContent\",\n \"alignSelf\",\n \"flexGrow\",\n \"flexShrink\",\n \"flexBasis\",\n \"padding\",\n \"paddingX\",\n \"paddingY\",\n \"paddingTop\",\n \"paddingBottom\",\n \"paddingLeft\",\n \"paddingRight\",\n \"margin\",\n \"marginX\",\n \"marginY\",\n \"marginTop\",\n \"marginBottom\",\n \"marginLeft\",\n \"marginRight\",\n \"gap\",\n \"columnGap\",\n \"rowGap\",\n \"borderStyle\",\n \"borderTop\",\n \"borderBottom\",\n \"borderLeft\",\n \"borderRight\",\n \"display\",\n \"position\",\n \"top\",\n \"left\",\n \"bottom\",\n \"right\",\n \"aspectRatio\",\n \"overflow\",\n \"overflowX\",\n \"overflowY\",\n // Viewport leaf dimensions (silvery-viewport host element — see bead\n // @km/silvery/15513). cols/rows pin the layout node's width/height; their\n // changes must mark the layout node dirty so the parent re-flows.\n \"cols\",\n \"rows\",\n // Note: scrollTo intentionally excluded - it doesn't affect layout dimensions,\n // only scroll offset which is handled in scrollPhase (reads props.scrollTo directly)\n])\n\n/**\n * Set of content props that affect layout dimensions (trigger contentDirty + Flexily markDirty()).\n * wrap changes text line count; internal_transform changes text width.\n */\nconst TEXT_CONTENT_PROPS = new Set([\"wrap\", \"internal_transform\"])\n\n/**\n * Set of style props that affect content (paint) but NOT layout dimensions.\n * borderColor, color, bold, etc. don't change how much space a node takes.\n * borderStyle is also a layout prop (affects border widths), but it's included\n * here so stylePropsDirty is set — otherwise border add/remove doesn't trigger\n * renderBox to draw/clear border characters.\n */\nconst STYLE_PROPS = new Set([\n \"color\",\n \"backgroundColor\",\n \"bold\",\n \"italic\",\n \"underline\",\n \"underlineStyle\",\n \"underlineColor\",\n \"overline\",\n \"strikethrough\",\n \"inverse\",\n \"borderColor\",\n \"borderBackgroundColor\",\n \"borderTopBackgroundColor\",\n \"borderBottomBackgroundColor\",\n \"borderLeftBackgroundColor\",\n \"borderRightBackgroundColor\",\n \"borderStyle\",\n \"outlineStyle\",\n \"outlineColor\",\n \"outlineDimColor\",\n \"outlineTop\",\n \"outlineBottom\",\n \"outlineLeft\",\n \"outlineRight\",\n \"theme\",\n])\n\n// ============================================================================\n// Single-pass prop change classification\n// ============================================================================\n\n/**\n * Result of classifying prop changes in a single pass.\n */\nexport interface PropChangeResult {\n /** Whether any prop changed (replaces propsEqual) */\n anyChanged: boolean\n /** Whether layout-affecting props changed (replaces layoutPropsChanged) */\n layoutChanged: boolean\n /**\n * Content change type (replaces contentPropsChanged):\n * - \"text\": text content changed (affects layout dimensions)\n * - \"style\": style-only change (affects paint but not layout)\n * - false: no content change\n */\n contentChanged: \"text\" | \"style\" | false\n}\n\n/** Shared singleton for the no-changes fast path (identity check). */\nconst NO_CHANGES: PropChangeResult = {\n anyChanged: false,\n layoutChanged: false,\n contentChanged: false,\n}\n\n/**\n * Classify all prop changes in a single pass over the union of old and new keys.\n *\n * Replaces the previous 3-pass approach (propsEqual + layoutPropsChanged +\n * contentPropsChanged) with one iteration. Includes an identity fast path\n * and early exit when both layout and content flags are fully determined.\n */\nexport function classifyPropChanges(\n oldProps: Record<string, unknown>,\n newProps: Record<string, unknown>,\n): PropChangeResult {\n // Identity check — fastest path\n if (oldProps === newProps) return NO_CHANGES\n\n const keysA = Object.keys(oldProps)\n const keysB = Object.keys(newProps)\n\n // Different key count means something definitely changed\n const sameKeyCount = keysA.length === keysB.length\n\n let layoutChanged = false\n let contentChanged: \"text\" | \"style\" | false = false\n let anyChanged = false\n\n // Iterate old keys — covers changed values and keys removed in newProps\n for (const key of keysA) {\n if (oldProps[key] !== newProps[key]) {\n anyChanged = true\n if (LAYOUT_PROPS.has(key)) layoutChanged = true\n // Classify content change (text > style priority)\n if (contentChanged !== \"text\") {\n if (key === \"children\") {\n // Only primitive children (string, number) affect text rendering.\n // Array/object children are React elements reconciled separately.\n const oldIsPrimitive =\n typeof oldProps[key] === \"string\" || typeof oldProps[key] === \"number\"\n const newIsPrimitive =\n typeof newProps[key] === \"string\" || typeof newProps[key] === \"number\"\n if (oldIsPrimitive || newIsPrimitive) {\n contentChanged = \"text\"\n }\n } else if (TEXT_CONTENT_PROPS.has(key)) {\n contentChanged = \"text\"\n } else if (contentChanged !== \"style\" && STYLE_PROPS.has(key)) {\n contentChanged = \"style\"\n }\n }\n // Early exit: both layout and text-content detected — can't escalate further\n if (layoutChanged && contentChanged === \"text\") break\n }\n }\n\n // Check for keys added in newProps (present in newProps but absent in oldProps).\n // If key counts are the same and we found changes above, all keys that exist in\n // newProps also exist in oldProps (no new keys were added). Skip this loop.\n if (!sameKeyCount) {\n for (const key of keysB) {\n if (!(key in oldProps)) {\n anyChanged = true\n if (LAYOUT_PROPS.has(key)) layoutChanged = true\n if (contentChanged !== \"text\") {\n if (key === \"children\") {\n const newIsPrimitive =\n typeof newProps[key] === \"string\" || typeof newProps[key] === \"number\"\n if (newIsPrimitive) {\n contentChanged = \"text\"\n }\n } else if (TEXT_CONTENT_PROPS.has(key)) {\n contentChanged = \"text\"\n } else if (contentChanged !== \"style\" && STYLE_PROPS.has(key)) {\n contentChanged = \"style\"\n }\n }\n if (layoutChanged && contentChanged === \"text\") break\n }\n }\n }\n\n // If key counts differ, something definitely changed even if no individual\n // key comparison flagged it (e.g., a key was removed from newProps and its\n // old value was undefined — oldProps[key] === newProps[key] === undefined).\n if (!anyChanged && !sameKeyCount) {\n anyChanged = true\n }\n\n if (!anyChanged) return NO_CHANGES\n return { anyChanged, layoutChanged, contentChanged }\n}\n","/**\n * Node Creation and Layout Application\n *\n * Functions for creating SilveryNodes and applying layout properties.\n */\n\nimport { createLogger } from \"loggily\"\nimport type { LayoutNode } from \"@silvery/ag/layout-types\"\nimport { getConstants, getLayoutEngine, requireCapability } from \"@silvery/ag-term/layout-engine\"\nimport { collectPlainTextSkipHidden as collectNodeTextContent } from \"@silvery/ag-term/pipeline/collect-text\"\nimport {\n type BoxProps,\n type AgNode,\n type AgNodeType,\n type TextProps,\n rectEqual,\n} from \"@silvery/ag/types\"\nimport type { ViewportProps } from \"@silvery/ag/viewport-types\"\nimport {\n type Measurer,\n displayWidth,\n isSoftBreakPoint,\n wrapText,\n getActiveLineHeight,\n} from \"@silvery/ag-term/unicode\"\nimport {\n getRenderEpoch,\n INITIAL_EPOCH,\n isDirty,\n CONTENT_BIT,\n STYLE_PROPS_BIT,\n BG_BIT,\n CHILDREN_BIT,\n SUBTREE_BIT,\n ABS_CHILD_BIT,\n DESC_OVERFLOW_BIT,\n ALL_RECONCILER_BITS,\n} from \"@silvery/ag/epoch\"\n\nconst measureLog = createLogger(\"silvery:measure\")\n\n// Import from shared module (lives in @silvery/ag-term to keep barrel React-free)\n// Re-exported for consumers that imported from here previously\nimport { measureStats } from \"@silvery/ag-term/pipeline/measure-stats\"\nexport { measureStats }\n\nimport { syncRectSignals } from \"@silvery/ag/layout-signals\"\n\n// ============================================================================\n// Ink-compat Text measureFunc shim\n// ============================================================================\n//\n// Silvery's Text measureFunc reports CSS-correct intrinsic sizes:\n// - non-wrappable Text (wrap=truncate*|clip|false) reports natural width\n// as both min-content and max-content. Truncation is a paint-phase\n// concern (handled by render-text.ts:formatTextLines reading layout.width).\n//\n// Ink's historical behavior conflates intrinsic measurement with render-time\n// clipping: <Box width={N}><Text wrap=\"truncate\">...</Text></Box> measures\n// the Text as `min(naturalWidth, N)` so the parent Box \"feels\" like it\n// constrained the child. Yoga-preset flex layout (Ink-compat) has\n// flexShrink:0 + min:0, so without this clamp, Text overflows the Box.\n//\n// We preserve Ink semantics behind a module-level flag toggled by\n// `initInkCompat()` (see `@silvery/ink/ink-render.ts`). When enabled, the\n// non-wrappable branch falls back to the old `min(lineWidth, maxWidth)`\n// clamp at intrinsic-sizing time. Spec-correct silvery code (the default)\n// stays untouched.\n//\n// Tracking bead: km-silvery.text-intrinsic-vs-render.\nlet inkCompatTextMeasure = false\n\n/**\n * Enable the Ink-compat Text measureFunc shim. Called by `initInkCompat()`\n * when consumers import from `@silvery/ink`. Idempotent.\n *\n * @internal\n */\nexport function setInkCompatTextMeasure(enabled: boolean): void {\n inkCompatTextMeasure = enabled\n}\n\n// ============================================================================\n// Node Creation\n// ============================================================================\n\n/**\n * Create a new SilveryNode with a fresh layout node.\n */\nexport function createNode(\n type: AgNodeType,\n props: BoxProps | TextProps | Record<string, unknown>,\n measurer?: Measurer,\n): AgNode {\n const layoutNode = getLayoutEngine().createNode()\n const epoch = getRenderEpoch()\n\n const node: AgNode = {\n type,\n props,\n children: [],\n parent: null,\n layoutNode,\n boxRect: null,\n scrollRect: null,\n screenRect: null,\n prevLayout: null,\n prevScrollRect: null,\n prevScreenRect: null,\n layoutChangedThisFrame: epoch,\n dirtyBits: ALL_RECONCILER_BITS,\n dirtyEpoch: epoch,\n }\n\n // Apply initial flexbox props to layout node\n if (type === \"silvery-box\") {\n applyBoxProps(layoutNode, props as BoxProps)\n } else if (type === \"silvery-text\") {\n // Text is a leaf flex item — apply the FlexboxProps subset declared by\n // TextFlexItemProps (flexGrow/flexShrink/flexBasis/alignSelf + min/max\n // dimensions). See `TextFlexItemProps` in @silvery/ag/types.\n applyTextFlexItemProps(layoutNode, props as TextProps)\n } else if (type === \"silvery-viewport\") {\n // Viewport is a leaf with fixed cols × rows. No flex children, no measure\n // function — the foreign cell buffer is blitted by the render phase at\n // boxRect. See bead @km/silvery/15513.\n applyViewportProps(layoutNode, props as unknown as ViewportProps)\n } else if (type === \"silvery-island\") {\n // Island is a leaf with intrinsic cols × rows hints — parent flex/layout\n // overrides at the layout phase. The guest's cell buffer is blitted by\n // the render phase at boxRect. See bead @km/silvery/15646.\n applyIslandProps(layoutNode, props as IslandLayoutProps)\n }\n\n // Set up measure function for text nodes\n // This tells the layout engine how to calculate the text's intrinsic size\n if (type === \"silvery-text\") {\n // Cache for measure results - avoid recalculating if text and constraints unchanged\n // Cache multiple (width, widthMode) -> result entries since layout calls measure with different widths\n let cachedText: string | null = null\n const measureCache = new Map<string, { width: number; height: number }>()\n\n layoutNode.setMeasureFunc((width, widthMode, height, heightMode) => {\n measureStats.calls++\n // Log measure calls through loggily (zero-cost when disabled via optional chaining)\n measureLog.debug?.(\n `measure \"${collectNodeTextContent(node).slice(0, 40)}\" width=${width} widthMode=${widthMode} height=${height} heightMode=${heightMode}`,\n )\n\n // Fast path: check if we have a cached result for this exact constraint\n // This avoids text collection entirely if we've measured this before\n const cacheKey = `${width}|${widthMode}|${height}|${heightMode}`\n const cached = measureCache.get(cacheKey)\n if (cached && cachedText !== null && !isDirty(node.dirtyBits, node.dirtyEpoch, CONTENT_BIT)) {\n measureStats.cacheHits++\n return cached\n }\n\n // Collect text content from this node and its raw text children\n // Use cached text if node hasn't been marked dirty (contentDirty)\n let text: string\n if (cachedText !== null && !isDirty(node.dirtyBits, node.dirtyEpoch, CONTENT_BIT)) {\n text = cachedText\n } else {\n measureStats.textCollects++\n const newText = collectNodeTextContent(node)\n // Only clear measurement cache if text actually changed\n if (newText !== cachedText) {\n measureCache.clear()\n }\n text = newText\n cachedText = text\n // Clear CONTENT_BIT so subsequent measure calls in same layout pass use cache.\n // NOTE: This means the render phase won't see contentDirty=true for text nodes\n // whose content changed. The render phase uses stylePropsDirty (which survives the\n // measure phase) combined with the node type check to correctly identify text\n // nodes that need region clearing. See contentAreaAffected in render-phase.ts.\n node.dirtyBits &= ~CONTENT_BIT\n }\n if (!text) {\n return { width: 0, height: 0 }\n }\n\n // Check cache again (may have been preserved if text unchanged)\n const cachedAfterCollect = measureCache.get(cacheKey)\n if (cachedAfterCollect) {\n measureStats.cacheHits++\n return cachedAfterCollect\n }\n\n // Calculate text dimensions\n const lines = text.split(\"\\n\")\n // Treat NaN width the same as unconstrained (can happen with auto-sized parents).\n // The flexily-only \"min-content\" mode is treated as \"AT_MOST 0\" for the\n // wrap branch: any wrap opportunity floors at the longest unbreakable\n // word; non-wrappable text returns naturalWidth (the isTruncate branch\n // ignores maxWidth at intrinsic-sizing time).\n const isMinContentQuery = widthMode === \"min-content\"\n const maxWidth =\n widthMode === \"undefined\" || Number.isNaN(width)\n ? Number.POSITIVE_INFINITY\n : isMinContentQuery\n ? 0\n : width\n\n // Wrap mode classification — separates intrinsic-sizing semantics\n // (CSS-aligned) from paint-time clipping (handled by\n // render-text.ts:formatTextLines reading layout.width).\n //\n // CSS analogy for non-wrappable text (wrap=\"truncate*\" / \"clip\" / false):\n // white-space: nowrap → min-content == max-content == naturalWidth\n // (no wrap opportunities; the line is one unbreakable token at\n // intrinsic-sizing time). Truncation/clipping moves to the paint\n // phase: render-text.ts applies the ellipsis or hard-clip at\n // layout.width.\n //\n // CSS analogy for wrappable text (wrap=\"wrap\" / \"even\" / undefined)\n // and word-break: break-all (wrap=\"hard\"):\n // min-content < max-content. Today this measureFunc reports the\n // AT_MOST-constrained wrap result for both queries, which matches\n // max-content for unconstrained queries and the post-wrap width\n // under per-child constraints. A future Phase 2 introduces a\n // distinct min-content protocol — see bead\n // km-silvery.text-intrinsic-vs-render.\n //\n // Reference: pro review 2026-04-26 (GPT-5.4 Pro + Kimi K2.6) and\n // bead km-silvery.text-intrinsic-vs-render.\n const { wrap } = node.props as TextProps\n const isTruncate =\n wrap === \"truncate\" ||\n wrap === \"truncate-start\" ||\n wrap === \"truncate-middle\" ||\n wrap === \"truncate-end\" ||\n wrap === \"clip\" ||\n wrap === false\n // Hard wrap: character-level wrapping, not word-aware. Each line that\n // exceeds maxWidth is sliced into chunks of exactly maxWidth.\n const isHardWrap = wrap === \"hard\"\n // wrap-truncate: word-aware wrap with ellipsis fallback for atomic\n // tokens that can't break. Measure path threads the flag into the\n // measurer so intrinsic line count matches the render-time output —\n // otherwise layout reserves char-wrapped row count but render emits\n // one truncated line, leaving blank rows below.\n const isWrapTruncate = wrap === \"wrap-truncate\"\n\n // internal_transform (set by <Transform>) is applied per-rendered-line\n // and can change the line's display width. fit-content and other\n // intrinsic-sizing queries must reflect the post-transform width — see\n // pipeline-bugfixes.test.tsx \"measure-fit-transform\".\n const internalTransform = (node.props as TextProps).internal_transform\n\n // Calculate actual dimensions based on wrapping\n // Use wrapText() for accurate line count — must match the render phase\n // (render-text.ts formatTextLines) which also uses wrapText()\n let totalHeight = 0\n let actualWidth = 0\n\n // Use explicit measurer when available, fall back to module-level convenience functions\n const dw = measurer ? measurer.displayWidth.bind(measurer) : displayWidth\n const wt = measurer ? measurer.wrapText.bind(measurer) : wrapText\n\n const lh = getActiveLineHeight() // 1 in cell mode, lineHeightPx in pixel mode\n\n // Track the index of each rendered line (post-wrap, pre-transform) so\n // internal_transform receives the same (line, index) pairs the render\n // phase will produce. Reset per measure call.\n let renderedLineIndex = 0\n const widthFor = (line: string): number => {\n if (!internalTransform) return dw(line)\n const transformed = internalTransform(line, renderedLineIndex)\n return dw(transformed)\n }\n\n for (const line of lines) {\n measureStats.displayWidthCalls++\n const lineWidth = dw(line)\n if (isTruncate) {\n // Non-wrappable text. CSS analogue: `text-overflow: ellipsis`\n // (or `clip`) with `white-space: nowrap`. The element implicitly\n // declares \"I'll fit in any width — overflow becomes ellipsis.\"\n //\n // min-content = 1 cell (one for the ellipsis) so flexbox CAN\n // shrink the Text below natural width. Without this, a\n // `<Text wrap=\"truncate\">` in a narrow container reports\n // min-content = max-content = full natural width, the parent\n // gets pinned at natural width, the parent's overflow=\"hidden\"\n // hard-clips with no ellipsis. User report 2026-05-11\n // (@km/silvery/text-truncation-mid-word-no-ellipsis):\n // \"ProjectedMap auto-stabilization + render watc\" — the \"watc\"\n // is a hard-clip at the parent's render-time width, with the\n // truncateText ellipsis path never reached because flex never\n // shrank the Text.\n //\n // max-content stays at natural width — fit-content / shrink-\n // wrap queries still see the full text so an unconstrained\n // parent sizes to the natural content width.\n totalHeight += lh\n if (isMinContentQuery) {\n actualWidth = Math.max(actualWidth, lineWidth > 0 ? 1 : 0)\n } else {\n actualWidth = Math.max(actualWidth, widthFor(line))\n }\n renderedLineIndex++\n } else if (isHardWrap) {\n if (isMinContentQuery) {\n // Hard-wrap min-content: any character can break, so min = 1\n // cell (or 0 for an empty line). Height is 1 — actual layout\n // computes wrapped height at the allocated width.\n totalHeight += lh\n actualWidth = Math.max(actualWidth, lineWidth > 0 ? 1 : 0)\n renderedLineIndex++\n } else if (lineWidth <= maxWidth) {\n totalHeight += lh\n actualWidth = Math.max(actualWidth, widthFor(line))\n renderedLineIndex++\n } else if (Number.isFinite(maxWidth) && maxWidth > 0) {\n // Character-level hard wrap: ceil(lineWidth / maxWidth) lines.\n const wrappedCount = Math.ceil(lineWidth / maxWidth)\n totalHeight += wrappedCount * lh\n actualWidth = Math.max(actualWidth, maxWidth)\n renderedLineIndex += wrappedCount\n } else {\n totalHeight += lh\n actualWidth = Math.max(actualWidth, widthFor(line))\n renderedLineIndex++\n }\n } else if (isMinContentQuery) {\n // Wrappable min-content: longest unbreakable segment between\n // ALL break opportunities the wrap algorithm would actually\n // honor. CSS min-content is the longest run of characters that\n // cannot break — for our wrap algorithm that includes both hard\n // boundaries (whitespace, hyphens) AND soft separators (`/`,\n // `\\`, `.`, `_`, `:`) per `isSoftBreakPoint`.\n //\n // Without the soft-break aware split, paths like\n // `.claude/skills/{claim,do}/SKILL.md` reported their full\n // width as min-content, pinning parent Boxes wider than the\n // assigned cell budget — content overflowed the card border\n // when soft-wrap would have fit it just fine.\n //\n // Strategy: split on whitespace (primary) and on soft-break\n // points (secondary). The widest segment between any two\n // break opportunities is the true min-content for wrap.\n let longestSegment = 0\n let segmentWidth = 0\n for (let pos = 0; pos < line.length; pos++) {\n const ch = line[pos]!\n if (ch === \" \" || ch === \"\\t\" || ch === \"-\") {\n // Hard break: segment ends BEFORE this char (next segment\n // starts after).\n if (segmentWidth > longestSegment) longestSegment = segmentWidth\n segmentWidth = 0\n continue\n }\n // Account for the grapheme width (not just `1` — wide chars,\n // CJK etc.). For ASCII this is 1; for multi-codepoint\n // graphemes the loop step is conservative (per-char) but\n // the width contribution comes from `dw(ch)`.\n segmentWidth += dw(ch)\n if (isSoftBreakPoint(ch)) {\n // Soft break: segment INCLUDES the separator (we break\n // AFTER it). Reset for the next segment.\n if (segmentWidth > longestSegment) longestSegment = segmentWidth\n segmentWidth = 0\n }\n }\n if (segmentWidth > longestSegment) longestSegment = segmentWidth\n // Height at min-content: a single line per source line. Actual\n // wrapped height is computed when the layout pass measures with\n // the assigned width.\n totalHeight += lh\n actualWidth = Math.max(actualWidth, longestSegment)\n renderedLineIndex++\n } else {\n // Wrappable text (wrap, wrap-truncate, even, undefined) under\n // exact/at-most/undefined.\n if (lineWidth <= maxWidth) {\n totalHeight += lh\n actualWidth = Math.max(actualWidth, widthFor(line))\n renderedLineIndex++\n } else {\n // wrap-truncate: pass the truncate-atomic-overflow flag so the\n // measured line count matches what render emits.\n const wrapped = wt(line, maxWidth, false, true, isWrapTruncate)\n totalHeight += wrapped.length * lh\n for (const wl of wrapped) {\n actualWidth = Math.max(actualWidth, widthFor(wl))\n renderedLineIndex++\n }\n }\n }\n }\n\n // Respect height constraint from layout engine.\n // When heightMode is \"at-most\", the text should not exceed the available height.\n // When heightMode is \"exactly\", the text should be exactly that height.\n // This prevents text from overflowing into parent border rows.\n let resultHeight = Math.max(lh, totalHeight)\n if (heightMode === \"exactly\" && Number.isFinite(height)) {\n resultHeight = height\n } else if (heightMode === \"at-most\" && Number.isFinite(height)) {\n resultHeight = Math.min(resultHeight, height)\n }\n\n // Final width:\n // - min-content query: return actualWidth as-is — the maxWidth\n // clamp would zero out the longest-word answer (we set\n // maxWidth=0 above for the min-content protocol).\n // - non-wrappable: natural width as-is (no maxWidth clamp — render\n // phase clips at paint time). Under the Ink-compat shim, fall\n // back to `min(actualWidth, maxWidth)` to preserve Ink's\n // conflated intrinsic-vs-render semantics.\n // - wrappable / hard: clamp to maxWidth so flex distribution sees\n // post-wrap width (e.g. wrapped \"Hello world\" at width=6 reports\n // width=5, the longest wrapped line).\n const resultWidth = isMinContentQuery\n ? actualWidth\n : isTruncate && !inkCompatTextMeasure\n ? actualWidth\n : Math.min(actualWidth, maxWidth)\n const result = {\n width: resultWidth,\n height: resultHeight,\n }\n measureCache.set(cacheKey, result)\n return result\n })\n }\n\n return node\n}\n\n// collectNodeTextContent is imported from @silvery/ag-term/pipeline/collect-text\n// as collectPlainTextSkipHidden. Previously duplicated here; now shared with\n// measure-phase.ts and render-text.ts via the shared collect-text module.\n\n/**\n * Create the root node for the Silvery tree.\n * Root is always column (document flow is top-to-bottom), regardless of\n * flexily's default flexDirection.\n */\nexport function createRootNode(): AgNode {\n const node = createNode(\"silvery-root\", {})\n const c = getConstants()\n node.layoutNode!.setFlexDirection(c.FLEX_DIRECTION_COLUMN)\n return node\n}\n\n/**\n * Create a virtual text node (for nested text elements).\n * Virtual text nodes don't have layout nodes and don't participate in layout.\n * They're used when Text is nested inside another Text.\n */\nexport function createVirtualTextNode(props: TextProps): AgNode {\n const epoch = getRenderEpoch()\n return {\n type: \"silvery-text\",\n props,\n children: [],\n parent: null,\n layoutNode: null, // No layout node for virtual text\n boxRect: null,\n scrollRect: null,\n screenRect: null,\n prevLayout: null,\n prevScrollRect: null,\n prevScreenRect: null,\n layoutChangedThisFrame: INITIAL_EPOCH,\n dirtyBits: CONTENT_BIT | STYLE_PROPS_BIT | BG_BIT | SUBTREE_BIT,\n dirtyEpoch: epoch,\n isRawText: false, // Not raw text, but virtual (nested) text\n inlineRects: null,\n }\n}\n\n// ============================================================================\n// Layout Property Application\n// ============================================================================\n\n/**\n * Apply ViewportProps to a viewport node's layout node.\n *\n * A `<Viewport>` is a leaf with fixed `cols`×`rows` — no flex children, no\n * measure function. We pin width/height from the props so the parent layout\n * positions the viewport rect deterministically; if the parent rect is\n * narrower, flexbox + parent overflow=\"hidden\" clips at paint time.\n *\n * See bead `@km/silvery/15513-surface-nested-composition-primitive`.\n */\nexport function applyViewportProps(\n layoutNode: LayoutNode,\n props: ViewportProps,\n oldProps?: ViewportProps,\n): void {\n if (props.cols !== undefined) {\n layoutNode.setWidth(props.cols)\n } else if (oldProps?.cols !== undefined) {\n layoutNode.setWidthAuto()\n }\n if (props.rows !== undefined) {\n layoutNode.setHeight(props.rows)\n } else if (oldProps?.rows !== undefined) {\n layoutNode.setHeightAuto()\n }\n}\n\n/**\n * Layout-relevant slice of <Island> props.\n *\n * Islands are leaf flex items that ALSO carry guest-contract dims. The two\n * are intentionally decoupled:\n *\n * - `cols` / `rows` — initial dimensions for the **guest's cell grid**. PTY\n * guests need them to spawn the child process; snapshot guests use them\n * as the frame size. Live for the guest's first paint; subsequent resizes\n * flow through `IslandSizeOwner.requestResize` (two-phase ack).\n *\n * - `width` / `height` / `flexGrow` / `flexShrink` / `flexBasis` / `alignSelf` /\n * `minWidth` / `minHeight` / `maxWidth` / `maxHeight` — control the **layout\n * slot** in flexily, exactly like any other flex item (Box, Text). When\n * present, they override `cols` / `rows` for layout purposes. When the\n * layout-derived dim differs from what the guest is rendering at, the host\n * calls `handle.size.requestResize(layoutCols, layoutRows)` so the guest can\n * acknowledge.\n *\n * Default behavior:\n * - `<Island cols=80 rows=24 />` — fixed 80×24 slot, guest renders at 80×24.\n * - `<Island cols=80 rows=24 flexGrow=1 />` — guest spawns at 80×24, flex\n * grows the slot, host immediately requests resize to the new dims.\n * - `<Island cols=80 rows=24 width=120 />` — guest spawns at 80×24, slot is\n * 120 wide, host requests resize to 120.\n *\n * Container-query units (`\"100cqi\"`, `\"50cqmin\"`) on `width` / `height` are not\n * wired yet (Phase 1 limitation); the cq plumbing lives in `applyBoxProps`\n * and will land here in a follow-up. See `@km/silvery/15646-islands`.\n */\nexport interface IslandLayoutProps {\n /** Initial guest cell-grid width (cells). Required at the <Island> surface;\n * optional here because oldProps may have already pinned it on a re-apply. */\n cols?: number\n /** Initial guest cell-grid height (cells). */\n rows?: number\n /** Whether this island participates in tree focus navigation. */\n focusable?: boolean\n /** Explicit layout slot width (cells or \"N%\"). Overrides `cols` for layout. */\n width?: number | string\n /** Explicit layout slot height. Overrides `rows` for layout. */\n height?: number | string\n /** CSS `flex-grow` — proportion of free positive space along the main axis. */\n flexGrow?: number\n /** CSS `flex-shrink`. */\n flexShrink?: number\n /** CSS `flex-basis` — initial main-size before grow/shrink distribution. */\n flexBasis?: number | string\n /** Cross-axis self-alignment override. */\n alignSelf?: \"auto\" | \"flex-start\" | \"flex-end\" | \"center\" | \"stretch\" | \"baseline\"\n /** CSS `min-width` — floor for shrink distribution. */\n minWidth?: number | string\n /** CSS `min-height`. */\n minHeight?: number | string\n /** CSS `max-width` — ceiling for grow distribution. */\n maxWidth?: number | string\n /** CSS `max-height`. */\n maxHeight?: number | string\n}\n\n/**\n * Apply IslandProps to an island node's layout node.\n *\n * `<Island>` is a leaf — guest content lives off the AgNode's `islandState`\n * slot, blitted by the render phase at boxRect. Layout-dim resolution:\n * explicit `width` / `height` wins; otherwise fall back to `cols` / `rows`.\n *\n * See bead `@km/silvery/15646-islands` for the two-phase resize protocol\n * (guest acks via `IslandSizeOwner` after host writes new dims).\n */\nexport function applyIslandProps(\n layoutNode: LayoutNode,\n props: IslandLayoutProps,\n oldProps?: IslandLayoutProps,\n): void {\n const c = getConstants()\n const wasRemoved = (prop: keyof IslandLayoutProps): boolean =>\n oldProps?.[prop] !== undefined && props[prop] === undefined\n\n // ─── Width: explicit `width` wins; fall back to `cols` ─────────────────\n if (props.width !== undefined) {\n if (typeof props.width === \"string\" && props.width.endsWith(\"%\")) {\n layoutNode.setWidthPercent(Number.parseFloat(props.width))\n } else if (typeof props.width === \"number\") {\n layoutNode.setWidth(props.width)\n }\n } else if (props.cols !== undefined) {\n layoutNode.setWidth(props.cols)\n } else if (wasRemoved(\"width\") || wasRemoved(\"cols\")) {\n layoutNode.setWidthAuto()\n }\n\n // ─── Height: explicit `height` wins; fall back to `rows` ───────────────\n if (props.height !== undefined) {\n if (typeof props.height === \"string\" && props.height.endsWith(\"%\")) {\n layoutNode.setHeightPercent(Number.parseFloat(props.height))\n } else if (typeof props.height === \"number\") {\n layoutNode.setHeight(props.height)\n }\n } else if (props.rows !== undefined) {\n layoutNode.setHeight(props.rows)\n } else if (wasRemoved(\"height\") || wasRemoved(\"rows\")) {\n layoutNode.setHeightAuto()\n }\n\n // ─── Flex item props (mirror applyTextFlexItemProps semantics) ─────────\n if (props.flexGrow !== undefined) {\n layoutNode.setFlexGrow(props.flexGrow)\n } else if (wasRemoved(\"flexGrow\")) {\n layoutNode.setFlexGrow(0)\n }\n\n if (props.flexShrink !== undefined) {\n layoutNode.setFlexShrink(props.flexShrink)\n } else if (wasRemoved(\"flexShrink\")) {\n layoutNode.setFlexShrink(1)\n }\n\n if (props.flexBasis !== undefined) {\n if (typeof props.flexBasis === \"string\" && props.flexBasis.endsWith(\"%\")) {\n layoutNode.setFlexBasisPercent(Number.parseFloat(props.flexBasis))\n } else if (props.flexBasis === \"auto\") {\n layoutNode.setFlexBasisAuto()\n } else if (typeof props.flexBasis === \"number\") {\n layoutNode.setFlexBasis(props.flexBasis)\n }\n } else if (wasRemoved(\"flexBasis\")) {\n layoutNode.setFlexBasisAuto()\n }\n\n if (props.alignSelf !== undefined) {\n if (props.alignSelf === \"auto\") {\n layoutNode.setAlignSelf(c.ALIGN_AUTO)\n } else {\n layoutNode.setAlignSelf(alignToConstant(props.alignSelf))\n }\n } else if (wasRemoved(\"alignSelf\")) {\n layoutNode.setAlignSelf(c.ALIGN_AUTO)\n }\n\n // ─── Min / max dimensions ──────────────────────────────────────────────\n if (props.minWidth !== undefined) {\n if (typeof props.minWidth === \"string\" && props.minWidth.endsWith(\"%\")) {\n layoutNode.setMinWidthPercent(Number.parseFloat(props.minWidth))\n } else if (typeof props.minWidth === \"number\") {\n layoutNode.setMinWidth(props.minWidth)\n }\n } else if (wasRemoved(\"minWidth\")) {\n layoutNode.setMinWidth(0)\n }\n\n if (props.minHeight !== undefined) {\n if (typeof props.minHeight === \"string\" && props.minHeight.endsWith(\"%\")) {\n layoutNode.setMinHeightPercent(Number.parseFloat(props.minHeight))\n } else if (typeof props.minHeight === \"number\") {\n layoutNode.setMinHeight(props.minHeight)\n }\n } else if (wasRemoved(\"minHeight\")) {\n layoutNode.setMinHeight(0)\n }\n\n if (props.maxWidth !== undefined) {\n if (typeof props.maxWidth === \"string\" && props.maxWidth.endsWith(\"%\")) {\n layoutNode.setMaxWidthPercent(Number.parseFloat(props.maxWidth))\n } else if (typeof props.maxWidth === \"number\") {\n layoutNode.setMaxWidth(props.maxWidth)\n }\n } else if (wasRemoved(\"maxWidth\")) {\n layoutNode.setMaxWidth(Number.POSITIVE_INFINITY)\n }\n\n if (props.maxHeight !== undefined) {\n if (typeof props.maxHeight === \"string\" && props.maxHeight.endsWith(\"%\")) {\n layoutNode.setMaxHeightPercent(Number.parseFloat(props.maxHeight))\n } else if (typeof props.maxHeight === \"number\") {\n layoutNode.setMaxHeight(props.maxHeight)\n }\n } else if (wasRemoved(\"maxHeight\")) {\n layoutNode.setMaxHeight(Number.POSITIVE_INFINITY)\n }\n}\n\n/**\n * Apply TextFlexItemProps to a Text node's layout node.\n *\n * Text is a leaf flex item — it accepts the subset of FlexboxProps that\n * affect how it participates as a flex item (sizing, growth, shrink),\n * not the props that affect how it lays out children. This is the\n * canonical CSS escape hatch: use `flexShrink={0}` to keep a Text rigid,\n * or `minWidth={0}` to let it shrink fully under a tight parent.\n *\n * See `TextFlexItemProps` in @silvery/ag/types.\n */\nexport function applyTextFlexItemProps(\n layoutNode: LayoutNode,\n props: TextProps,\n oldProps?: TextProps,\n): void {\n const c = getConstants()\n const wasRemoved = (prop: keyof TextProps): boolean =>\n oldProps?.[prop] !== undefined && props[prop] === undefined\n\n if (props.flexGrow !== undefined) {\n layoutNode.setFlexGrow(props.flexGrow)\n } else if (wasRemoved(\"flexGrow\")) {\n layoutNode.setFlexGrow(0)\n }\n\n if (props.flexShrink !== undefined) {\n layoutNode.setFlexShrink(props.flexShrink)\n } else if (wasRemoved(\"flexShrink\")) {\n layoutNode.setFlexShrink(1)\n }\n\n if (props.flexBasis !== undefined) {\n if (typeof props.flexBasis === \"string\" && props.flexBasis.endsWith(\"%\")) {\n layoutNode.setFlexBasisPercent(Number.parseFloat(props.flexBasis))\n } else if (props.flexBasis === \"auto\") {\n layoutNode.setFlexBasisAuto()\n } else if (typeof props.flexBasis === \"number\") {\n layoutNode.setFlexBasis(props.flexBasis)\n }\n } else if (wasRemoved(\"flexBasis\")) {\n layoutNode.setFlexBasisAuto()\n }\n\n if (props.alignSelf !== undefined) {\n if (props.alignSelf === \"auto\") {\n layoutNode.setAlignSelf(c.ALIGN_AUTO)\n } else {\n layoutNode.setAlignSelf(alignToConstant(props.alignSelf))\n }\n } else if (wasRemoved(\"alignSelf\")) {\n layoutNode.setAlignSelf(c.ALIGN_AUTO)\n }\n\n if (props.minWidth !== undefined) {\n if (typeof props.minWidth === \"string\" && props.minWidth.endsWith(\"%\")) {\n layoutNode.setMinWidthPercent(Number.parseFloat(props.minWidth))\n } else if (typeof props.minWidth === \"number\") {\n layoutNode.setMinWidth(props.minWidth)\n }\n } else if (wasRemoved(\"minWidth\")) {\n layoutNode.setMinWidth(0)\n }\n\n if (props.minHeight !== undefined) {\n if (typeof props.minHeight === \"string\" && props.minHeight.endsWith(\"%\")) {\n layoutNode.setMinHeightPercent(Number.parseFloat(props.minHeight))\n } else if (typeof props.minHeight === \"number\") {\n layoutNode.setMinHeight(props.minHeight)\n }\n } else if (wasRemoved(\"minHeight\")) {\n layoutNode.setMinHeight(0)\n }\n\n if (props.maxWidth !== undefined) {\n if (typeof props.maxWidth === \"string\" && props.maxWidth.endsWith(\"%\")) {\n layoutNode.setMaxWidthPercent(Number.parseFloat(props.maxWidth))\n } else if (typeof props.maxWidth === \"number\") {\n layoutNode.setMaxWidth(props.maxWidth)\n }\n } else if (wasRemoved(\"maxWidth\")) {\n layoutNode.setMaxWidth(Number.POSITIVE_INFINITY)\n }\n\n if (props.maxHeight !== undefined) {\n if (typeof props.maxHeight === \"string\" && props.maxHeight.endsWith(\"%\")) {\n layoutNode.setMaxHeightPercent(Number.parseFloat(props.maxHeight))\n } else if (typeof props.maxHeight === \"number\") {\n layoutNode.setMaxHeight(props.maxHeight)\n }\n } else if (wasRemoved(\"maxHeight\")) {\n layoutNode.setMaxHeight(Number.POSITIVE_INFINITY)\n }\n}\n\n/**\n * Parse a `fitWidth` entry from user-facing form (number | string) to the\n * engine's FitWidthLane shape (number | { value, unit: \"cqi\" | \"cqmin\" }).\n *\n * Accepted strings: `\"100cqi\"`, `\"50cqmin\"` — decimal and integer values both\n * fine. Numbers pass through unchanged. Invalid strings throw with a clear\n * pointer to the bug — the layout engine consumes only the parsed form, so\n * parse-fail at the React seam is the right place.\n */\nfunction parseFitWidthEntry(\n entry: number | string,\n): number | { value: number; unit: \"cqi\" | \"cqmin\" } {\n if (typeof entry === \"number\") return entry\n const cqiMatch = entry.match(/^(\\d+(?:\\.\\d+)?)cqi$/)\n if (cqiMatch) return { value: Number.parseFloat(cqiMatch[1]!), unit: \"cqi\" }\n const cqminMatch = entry.match(/^(\\d+(?:\\.\\d+)?)cqmin$/)\n if (cqminMatch) return { value: Number.parseFloat(cqminMatch[1]!), unit: \"cqmin\" }\n throw new Error(\n `<Box fitWidth>: invalid lane entry ${JSON.stringify(entry)}. ` +\n `Expected a number (cells) or a string like \"100cqi\" / \"50cqmin\".`,\n )\n}\n\n/**\n * Apply BoxProps to a layout node.\n * This maps Ink/Silvery props to the layout engine API.\n */\nexport function applyBoxProps(layoutNode: LayoutNode, props: BoxProps, oldProps?: BoxProps): void {\n const c = getConstants()\n // Helper: true when a prop was set in oldProps but not in newProps (prop removed on rerender)\n const wasRemoved = (prop: keyof BoxProps): boolean =>\n oldProps?.[prop] !== undefined && props[prop] === undefined\n\n // Dimensions\n if (props.width !== undefined) {\n if (typeof props.width === \"string\" && props.width.endsWith(\"%\")) {\n layoutNode.setWidthPercent(Number.parseFloat(props.width))\n } else if (typeof props.width === \"number\") {\n layoutNode.setWidth(props.width)\n } else if (props.width === \"auto\") {\n layoutNode.setWidthAuto()\n } else if (props.width === \"fit-content\") {\n layoutNode.setWidthFitContent()\n } else if (props.width === \"snug-content\") {\n layoutNode.setWidthSnugContent()\n }\n } else if (wasRemoved(\"width\")) {\n layoutNode.setWidthAuto()\n }\n\n if (props.height !== undefined) {\n if (typeof props.height === \"string\" && props.height.endsWith(\"%\")) {\n layoutNode.setHeightPercent(Number.parseFloat(props.height))\n } else if (typeof props.height === \"number\") {\n layoutNode.setHeight(props.height)\n } else if (props.height === \"auto\") {\n layoutNode.setHeightAuto()\n }\n } else if (wasRemoved(\"height\")) {\n layoutNode.setHeightAuto()\n }\n\n // Min/Max dimensions\n if (props.minWidth !== undefined) {\n if (typeof props.minWidth === \"string\" && props.minWidth.endsWith(\"%\")) {\n layoutNode.setMinWidthPercent(Number.parseFloat(props.minWidth))\n } else if (typeof props.minWidth === \"number\") {\n layoutNode.setMinWidth(props.minWidth)\n }\n } else if (wasRemoved(\"minWidth\")) {\n layoutNode.setMinWidth(0)\n }\n\n if (props.minHeight !== undefined) {\n if (typeof props.minHeight === \"string\" && props.minHeight.endsWith(\"%\")) {\n layoutNode.setMinHeightPercent(Number.parseFloat(props.minHeight))\n } else if (typeof props.minHeight === \"number\") {\n layoutNode.setMinHeight(props.minHeight)\n }\n } else if (wasRemoved(\"minHeight\")) {\n layoutNode.setMinHeight(0)\n }\n\n if (props.maxWidth !== undefined) {\n if (typeof props.maxWidth === \"string\" && props.maxWidth.endsWith(\"%\")) {\n layoutNode.setMaxWidthPercent(Number.parseFloat(props.maxWidth))\n } else if (typeof props.maxWidth === \"number\") {\n layoutNode.setMaxWidth(props.maxWidth)\n }\n } else if (wasRemoved(\"maxWidth\")) {\n layoutNode.setMaxWidth(Number.POSITIVE_INFINITY)\n }\n\n if (props.maxHeight !== undefined) {\n if (typeof props.maxHeight === \"string\" && props.maxHeight.endsWith(\"%\")) {\n layoutNode.setMaxHeightPercent(Number.parseFloat(props.maxHeight))\n } else if (typeof props.maxHeight === \"number\") {\n layoutNode.setMaxHeight(props.maxHeight)\n }\n } else if (wasRemoved(\"maxHeight\")) {\n layoutNode.setMaxHeight(Number.POSITIVE_INFINITY)\n }\n\n // Flex properties\n if (props.flexGrow !== undefined) {\n layoutNode.setFlexGrow(props.flexGrow)\n } else if (wasRemoved(\"flexGrow\")) {\n layoutNode.setFlexGrow(0)\n }\n\n if (props.flexShrink !== undefined) {\n layoutNode.setFlexShrink(props.flexShrink)\n } else if (wasRemoved(\"flexShrink\")) {\n layoutNode.setFlexShrink(1)\n }\n\n if (props.flexBasis !== undefined) {\n if (typeof props.flexBasis === \"string\" && props.flexBasis.endsWith(\"%\")) {\n layoutNode.setFlexBasisPercent(Number.parseFloat(props.flexBasis))\n } else if (props.flexBasis === \"auto\") {\n layoutNode.setFlexBasisAuto()\n } else if (typeof props.flexBasis === \"number\") {\n layoutNode.setFlexBasis(props.flexBasis)\n }\n } else if (wasRemoved(\"flexBasis\")) {\n layoutNode.setFlexBasisAuto()\n }\n\n // Flex direction\n if (props.flexDirection !== undefined) {\n const directionMap: Record<string, number> = {\n row: c.FLEX_DIRECTION_ROW,\n column: c.FLEX_DIRECTION_COLUMN,\n \"row-reverse\": c.FLEX_DIRECTION_ROW_REVERSE,\n \"column-reverse\": c.FLEX_DIRECTION_COLUMN_REVERSE,\n }\n layoutNode.setFlexDirection(directionMap[props.flexDirection] ?? c.FLEX_DIRECTION_ROW)\n } else if (wasRemoved(\"flexDirection\")) {\n layoutNode.setFlexDirection(c.FLEX_DIRECTION_ROW)\n }\n\n // Flex wrap\n if (props.flexWrap !== undefined) {\n const wrapMap: Record<string, number> = {\n nowrap: c.WRAP_NO_WRAP,\n wrap: c.WRAP_WRAP,\n \"wrap-reverse\": c.WRAP_WRAP_REVERSE,\n }\n layoutNode.setFlexWrap(wrapMap[props.flexWrap] ?? c.WRAP_NO_WRAP)\n } else if (wasRemoved(\"flexWrap\")) {\n layoutNode.setFlexWrap(c.WRAP_NO_WRAP)\n }\n\n // Alignment\n if (props.alignItems !== undefined) {\n layoutNode.setAlignItems(alignToConstant(props.alignItems))\n } else if (wasRemoved(\"alignItems\")) {\n layoutNode.setAlignItems(c.ALIGN_STRETCH)\n }\n\n if (props.alignSelf !== undefined) {\n if (props.alignSelf === \"auto\") {\n layoutNode.setAlignSelf(c.ALIGN_AUTO)\n } else {\n layoutNode.setAlignSelf(alignToConstant(props.alignSelf))\n }\n } else if (wasRemoved(\"alignSelf\")) {\n layoutNode.setAlignSelf(c.ALIGN_AUTO)\n }\n\n if (props.alignContent !== undefined) {\n layoutNode.setAlignContent(alignToConstant(props.alignContent))\n } else if (wasRemoved(\"alignContent\")) {\n layoutNode.setAlignContent(c.ALIGN_FLEX_START)\n }\n\n if (props.justifyContent !== undefined) {\n layoutNode.setJustifyContent(justifyToConstant(props.justifyContent))\n } else if (wasRemoved(\"justifyContent\")) {\n layoutNode.setJustifyContent(c.JUSTIFY_FLEX_START)\n }\n\n // Padding\n applySpacing(layoutNode, \"padding\", props)\n\n // Margin\n applySpacing(layoutNode, \"margin\", props)\n\n // Gap\n if (props.gap !== undefined) {\n layoutNode.setGap(c.GUTTER_ALL, props.gap)\n } else if (wasRemoved(\"gap\")) {\n layoutNode.setGap(c.GUTTER_ALL, 0)\n }\n\n if (props.columnGap !== undefined) {\n layoutNode.setGap(c.GUTTER_COLUMN, props.columnGap)\n } else if (wasRemoved(\"columnGap\")) {\n layoutNode.setGap(c.GUTTER_COLUMN, 0)\n }\n\n if (props.rowGap !== undefined) {\n layoutNode.setGap(c.GUTTER_ROW, props.rowGap)\n } else if (wasRemoved(\"rowGap\")) {\n layoutNode.setGap(c.GUTTER_ROW, 0)\n }\n\n // Display\n if (props.display !== undefined) {\n layoutNode.setDisplay(props.display === \"none\" ? c.DISPLAY_NONE : c.DISPLAY_FLEX)\n } else if (wasRemoved(\"display\")) {\n layoutNode.setDisplay(c.DISPLAY_FLEX)\n }\n\n // Position\n // Note: 'sticky' is handled at render-time, not by layout engine. For layout purposes, treat as relative.\n if (props.position !== undefined) {\n if (props.position === \"absolute\") {\n layoutNode.setPositionType(c.POSITION_TYPE_ABSOLUTE)\n } else if (props.position === \"static\") {\n layoutNode.setPositionType(c.POSITION_TYPE_STATIC)\n } else {\n layoutNode.setPositionType(c.POSITION_TYPE_RELATIVE)\n }\n } else if (wasRemoved(\"position\")) {\n layoutNode.setPositionType(c.POSITION_TYPE_RELATIVE)\n }\n\n // Position offsets (top, left, bottom, right)\n // Skip offsets for position=\"static\" — static positioning ignores offsets (CSS spec).\n if (props.position !== \"static\") {\n applyPositionOffset(layoutNode, c.EDGE_TOP, props.top)\n applyPositionOffset(layoutNode, c.EDGE_LEFT, props.left)\n applyPositionOffset(layoutNode, c.EDGE_BOTTOM, props.bottom)\n applyPositionOffset(layoutNode, c.EDGE_RIGHT, props.right)\n }\n\n // Aspect ratio\n if (props.aspectRatio !== undefined) {\n layoutNode.setAspectRatio(props.aspectRatio)\n } else if (wasRemoved(\"aspectRatio\")) {\n layoutNode.setAspectRatio(NaN)\n }\n\n // Overflow\n // Derive effective overflow: explicit overflow takes precedence, then per-axis (hidden if either axis is hidden)\n const effectiveOverflow =\n props.overflow ??\n (props.overflowX === \"hidden\" || props.overflowY === \"hidden\" ? \"hidden\" : undefined)\n if (effectiveOverflow !== undefined) {\n if (effectiveOverflow === \"hidden\") {\n layoutNode.setOverflow(c.OVERFLOW_HIDDEN)\n } else if (effectiveOverflow === \"scroll\") {\n // Use OVERFLOW_HIDDEN for layout so children are constrained to parent width\n // (text wraps instead of overflowing). The render phase reads props.overflow\n // to enable vertical scroll behavior independently of the layout constraint.\n layoutNode.setOverflow(c.OVERFLOW_HIDDEN)\n } else {\n layoutNode.setOverflow(c.OVERFLOW_VISIBLE)\n }\n } else if (wasRemoved(\"overflow\") || wasRemoved(\"overflowX\") || wasRemoved(\"overflowY\")) {\n layoutNode.setOverflow(c.OVERFLOW_VISIBLE)\n }\n\n // Container queries (A0.1) — wired only when the active engine advertises the\n // capability. Under yoga, `requireCapability` throws at first paint with the\n // one-line fix (SILVERY_ENGINE=flexily). Phase 1 contains inline-size only;\n // `containerName` is reserved for forward-compat (matcher arrives in Phase A).\n //\n // The numeric mapping mirrors flexily's CONTAINER_TYPE_NORMAL=0 / INLINE_SIZE=1.\n // Hardcoded at this seam to avoid widening LayoutConstants for two values that\n // don't roundtrip through yoga (yoga has no equivalent).\n if (props.containerType !== undefined) {\n requireCapability(\"containerQueries\", \"<Box containerType>\")\n layoutNode.setContainerType(props.containerType === \"inline-size\" ? 1 : 0)\n } else if (wasRemoved(\"containerType\")) {\n layoutNode.setContainerType(0)\n }\n if (props.containSize !== undefined) {\n requireCapability(\"containSize\", \"<Box containSize>\")\n layoutNode.setContainSize(props.containSize)\n } else if (wasRemoved(\"containSize\")) {\n layoutNode.setContainSize(false)\n }\n // containerQueries — substrate prop, no engine wiring yet. The Phase A\n // silvery-layer matcher will read this prop on the React side, not push it\n // into the layout engine. We DO surface a capability check so consumers under\n // yoga get the same one-line-fix error shape as the other CQ primitives.\n if (props.containerQueries !== undefined) {\n requireCapability(\"containerQueries\", \"<Box containerQueries>\")\n }\n\n // fitWidth (A0.2). Parse string entries like \"100cqi\" / \"50cqmin\" into the\n // engine's FitWidthLane shape. Plain numbers pass through. Invalid strings\n // throw with a clear message — silvery's prop layer is the authoritative\n // place to do this validation (the layout engine just consumes the parsed form).\n if (props.fitWidth !== undefined) {\n requireCapability(\"fitWidth\", \"<Box fitWidth>\")\n layoutNode.setFitWidth(props.fitWidth.map(parseFitWidthEntry))\n } else if (wasRemoved(\"fitWidth\")) {\n layoutNode.setFitWidth(undefined)\n }\n\n // Border (affects layout - 1 cell per border side in terminal mode).\n // In pixel/canvas mode (lineHeight > 1), borders are visual-only — drawn by\n // fillRoundedRect, not layout-affecting box-drawing characters. Set width to 0\n // so flexily doesn't steal space from content for invisible character borders.\n if (props.borderStyle) {\n const borderWidth = getActiveLineHeight() > 1 ? 0 : 1\n if (props.borderTop !== false) {\n layoutNode.setBorder(c.EDGE_TOP, borderWidth)\n } else {\n layoutNode.setBorder(c.EDGE_TOP, 0)\n }\n if (props.borderBottom !== false) {\n layoutNode.setBorder(c.EDGE_BOTTOM, borderWidth)\n } else {\n layoutNode.setBorder(c.EDGE_BOTTOM, 0)\n }\n if (props.borderLeft !== false) {\n layoutNode.setBorder(c.EDGE_LEFT, borderWidth)\n } else {\n layoutNode.setBorder(c.EDGE_LEFT, 0)\n }\n if (props.borderRight !== false) {\n layoutNode.setBorder(c.EDGE_RIGHT, borderWidth)\n } else {\n layoutNode.setBorder(c.EDGE_RIGHT, 0)\n }\n } else {\n // Reset all border widths when borderStyle is removed\n layoutNode.setBorder(c.EDGE_TOP, 0)\n layoutNode.setBorder(c.EDGE_BOTTOM, 0)\n layoutNode.setBorder(c.EDGE_LEFT, 0)\n layoutNode.setBorder(c.EDGE_RIGHT, 0)\n }\n}\n\n/**\n * Apply padding or margin to a layout node.\n */\nfunction applySpacing(layoutNode: LayoutNode, type: \"padding\" | \"margin\", props: BoxProps): void {\n const c = getConstants()\n const set =\n type === \"padding\"\n ? layoutNode.setPadding.bind(layoutNode)\n : layoutNode.setMargin.bind(layoutNode)\n\n const all = props[type] as number | undefined\n const x = props[`${type}X` as keyof BoxProps] as number | undefined\n const yy = props[`${type}Y` as keyof BoxProps] as number | undefined\n const top = props[`${type}Top` as keyof BoxProps] as number | undefined\n const bottom = props[`${type}Bottom` as keyof BoxProps] as number | undefined\n const left = props[`${type}Left` as keyof BoxProps] as number | undefined\n const right = props[`${type}Right` as keyof BoxProps] as number | undefined\n\n // Compute effective value per edge, resolving CSS-like specificity cascade:\n // individual > axis (X/Y) > all > 0\n // This handles the case where props are REMOVED (e.g., paddingLeft: 1 → undefined):\n // the edge is reset to 0 instead of retaining the stale Yoga value.\n set(c.EDGE_TOP, top ?? yy ?? all ?? 0)\n set(c.EDGE_BOTTOM, bottom ?? yy ?? all ?? 0)\n set(c.EDGE_LEFT, left ?? x ?? all ?? 0)\n set(c.EDGE_RIGHT, right ?? x ?? all ?? 0)\n}\n\n/**\n * Apply a position offset (top/left/bottom/right) to a layout node.\n * Supports both numeric (absolute) and percentage string values.\n */\nfunction applyPositionOffset(\n layoutNode: LayoutNode,\n edge: number,\n value: number | string | undefined,\n): void {\n if (value === undefined) {\n // Unset stale position offset when prop is removed on rerender\n layoutNode.setPosition(edge, NaN)\n return\n }\n if (typeof value === \"string\" && value.endsWith(\"%\")) {\n layoutNode.setPositionPercent(edge, Number.parseFloat(value))\n } else if (typeof value === \"number\") {\n layoutNode.setPosition(edge, value)\n }\n}\n\n/**\n * Convert align value to layout constant.\n */\nfunction alignToConstant(align: string): number {\n const c = getConstants()\n const map: Record<string, number> = {\n \"flex-start\": c.ALIGN_FLEX_START,\n \"flex-end\": c.ALIGN_FLEX_END,\n center: c.ALIGN_CENTER,\n stretch: c.ALIGN_STRETCH,\n baseline: c.ALIGN_BASELINE,\n \"space-between\": c.ALIGN_SPACE_BETWEEN,\n \"space-around\": c.ALIGN_SPACE_AROUND,\n \"space-evenly\": c.ALIGN_SPACE_EVENLY,\n }\n return map[align] ?? c.ALIGN_STRETCH\n}\n\n/**\n * Convert justify value to layout constant.\n */\nfunction justifyToConstant(justify: string): number {\n const c = getConstants()\n const map: Record<string, number> = {\n \"flex-start\": c.JUSTIFY_FLEX_START,\n \"flex-end\": c.JUSTIFY_FLEX_END,\n center: c.JUSTIFY_CENTER,\n \"space-between\": c.JUSTIFY_SPACE_BETWEEN,\n \"space-around\": c.JUSTIFY_SPACE_AROUND,\n \"space-evenly\": c.JUSTIFY_SPACE_EVENLY,\n }\n return map[justify] ?? c.JUSTIFY_FLEX_START\n}\n\n// ============================================================================\n// Layout Calculation\n// ============================================================================\n\n/**\n * Calculate layout for the entire tree starting from root.\n */\nexport function calculateLayout(root: AgNode, width: number, height: number): void {\n const c = getConstants()\n if (!root.layoutNode) {\n throw new Error(\"Root node must have a layout node\")\n }\n root.layoutNode.calculateLayout(width, height, c.DIRECTION_LTR)\n propagateLayout(root, 0, 0)\n notifyLayoutSubscribers(root)\n}\n\n/**\n * Propagate computed layout from layout nodes to SilveryNodes.\n */\nfunction propagateLayout(node: AgNode, parentX: number, parentY: number): void {\n // Save previous layout for change detection\n node.prevLayout = node.boxRect\n\n // Get computed layout from layout node\n if (!node.layoutNode) {\n // Virtual nodes (raw text, nested text) inherit parent layout\n return\n }\n const left = node.layoutNode.getComputedLeft()\n const top = node.layoutNode.getComputedTop()\n const width = node.layoutNode.getComputedWidth()\n const height = node.layoutNode.getComputedHeight()\n\n node.boxRect = {\n x: parentX + left,\n y: parentY + top,\n width,\n height,\n }\n\n // If dimensions changed, content needs re-render\n if (!rectEqual(node.prevLayout, node.boxRect)) {\n const epoch = getRenderEpoch()\n if (node.dirtyEpoch !== epoch) {\n node.dirtyBits = CONTENT_BIT\n node.dirtyEpoch = epoch\n } else {\n node.dirtyBits |= CONTENT_BIT\n }\n }\n\n // Recursively propagate to children\n for (const child of node.children) {\n propagateLayout(child, node.boxRect.x, node.boxRect.y)\n }\n}\n\n/**\n * Sync layout signals for nodes whose rects changed.\n */\nfunction notifyLayoutSubscribers(node: AgNode): void {\n // Sync rect values into alien-signals (for signal-based hooks)\n syncRectSignals(node)\n\n for (const child of node.children) {\n notifyLayoutSubscribers(child)\n }\n}\n","/**\n * React Reconciler Host Config\n *\n * Defines how React creates, updates, and manages SilveryNodes.\n * This is the bridge between React's reconciliation algorithm\n * and our custom terminal node tree.\n */\n\n// Module declarations for \"react-reconciler/constants.js\" live in ../react-reconciler.d.ts\n// (picked up via tsconfig `include` glob).\n\nimport { createContext } from \"react\"\nimport {\n DefaultEventPriority,\n DiscreteEventPriority,\n NoEventPriority,\n} from \"react-reconciler/constants.js\"\nimport { reportDisposeError, type Scope } from \"@silvery/scope\"\nimport type { BoxProps, AgNode, AgNodeType, TextProps } from \"@silvery/ag/types\"\nimport {\n trackContentDirty,\n trackStyleOnlyDirty,\n trackScrollDirty,\n} from \"@silvery/ag/dirty-tracking\"\nimport { syncTextContentSignal } from \"@silvery/ag/layout-signals\"\nimport {\n getRenderEpoch,\n INITIAL_EPOCH,\n isDirty,\n setDirtyBit,\n CONTENT_BIT,\n STYLE_PROPS_BIT,\n BG_BIT,\n CHILDREN_BIT,\n SUBTREE_BIT,\n ALL_RECONCILER_BITS,\n} from \"@silvery/ag/epoch\"\nimport type { ViewportProps } from \"@silvery/ag/viewport-types\"\nimport { classifyPropChanges } from \"./helpers\"\nimport {\n applyBoxProps,\n applyIslandProps,\n applyTextFlexItemProps,\n applyViewportProps,\n createNode,\n createVirtualTextNode,\n type IslandLayoutProps,\n} from \"./nodes\"\nimport { createLogger } from \"loggily\"\nimport { warnOnce, _resetWarnOnceForTesting } from \"@silvery/ansi\"\n\nconst log = createLogger(\"silvery:reconciler\")\nconst mountLog = createLogger(\"silvery:mount\")\n\nconst DEBUG_PROP_NAMES = [\n \"id\",\n \"testID\",\n \"data-component\",\n \"display\",\n \"position\",\n \"flexDirection\",\n \"width\",\n \"height\",\n \"minWidth\",\n \"minHeight\",\n \"flexGrow\",\n \"flexShrink\",\n \"overflow\",\n \"overflowX\",\n \"overflowY\",\n \"overflowIndicator\",\n \"scrollTo\",\n \"scrollOffset\",\n \"scrollbar\",\n \"scrollbarVisibility\",\n \"follow\",\n \"onWheel\",\n \"onClick\",\n \"onMouseDown\",\n \"onMouseUp\",\n \"onMouseMove\",\n] as const\n\nfunction hostTypeLabel(type: AgNodeType): string {\n if (type === \"silvery-box\") return \"Box\"\n if (type === \"silvery-text\") return \"Text\"\n if (type === \"silvery-viewport\") return \"Viewport\"\n if (type === \"silvery-island\") return \"Island\"\n return type\n}\n\nfunction getDebugComponentName(node: AgNode): string {\n const props = node.props as Record<string, unknown>\n const explicitName = props[\"data-component\"]\n if (typeof explicitName === \"string\" && explicitName.length > 0) return explicitName\n\n const base = hostTypeLabel(node.type)\n const testID = props.testID\n if (typeof testID === \"string\" && testID.length > 0) return `${base}#${testID}`\n\n const id = props.id\n if (typeof id === \"string\" && id.length > 0) return `${base}#${id}`\n\n return base\n}\n\nfunction summarizeDebugProp(value: unknown): unknown {\n if (typeof value === \"function\") return true\n if (\n value === null ||\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\"\n ) {\n return value\n }\n return undefined\n}\n\nfunction summarizeDebugProps(props: Record<string, unknown>): Record<string, unknown> {\n const summary: Record<string, unknown> = {}\n for (const key of DEBUG_PROP_NAMES) {\n const value = summarizeDebugProp(props[key])\n if (value !== undefined) summary[key] = value\n }\n for (const key of Object.keys(props)) {\n if (!key.startsWith(\"data-\") || key === \"data-component\") continue\n const value = summarizeDebugProp(props[key])\n if (value !== undefined) summary[key] = value\n }\n return summary\n}\n\nfunction logNodeLifecycle(event: \"mount\" | \"update\" | \"unmount\", node: AgNode): void {\n const props = node.props as Record<string, unknown>\n const component = getDebugComponentName(node)\n const text = node.textContent\n mountLog.debug?.(`${event} ${component}`, {\n event,\n component,\n type: node.type,\n props: summarizeDebugProps(props),\n propKeys: Object.keys(props)\n .filter((key) => key !== \"children\")\n .sort(),\n ...(text ? { text: text.length > 80 ? `${text.slice(0, 77)}...` : text } : {}),\n })\n}\n\nfunction logUnmountSubtree(node: AgNode): void {\n logNodeLifecycle(\"unmount\", node)\n for (const child of node.children) {\n logUnmountSubtree(child)\n }\n}\n\n/**\n * Normalize Ink intrinsic element types to Silvery equivalents.\n * Ink uses `ink-box` / `ink-text` as intrinsic element names;\n * Silvery uses `silvery-box` / `silvery-text`.\n */\nfunction normalizeNodeType(type: string): AgNodeType {\n if (type === \"ink-box\") return \"silvery-box\"\n if (type === \"ink-text\") return \"silvery-text\"\n return type as AgNodeType\n}\n\n// ============================================================================\n// Node Removal Hook\n// ============================================================================\n\n/**\n * Callback invoked when a node is removed from the tree.\n * Used by the app layer to coordinate focus cleanup — if the focused element\n * is within a removed subtree, focus must be cleared to prevent dangling\n * references and broken navigation (indexOf → -1, hasFocusWithin lies).\n */\nlet onNodeRemovedCallback: ((removedNode: AgNode) => void) | null = null\n\n/**\n * Register a callback to be called when any node is removed from the tree.\n * Returns a cleanup function to unregister. Only one callback at a time.\n */\nexport function setOnNodeRemoved(callback: ((removedNode: AgNode) => void) | null): void {\n onNodeRemovedCallback = callback\n}\n\n// ============================================================================\n// Fiber-Local Scope Slot\n// ============================================================================\n//\n// Every host instance (AgNode) gets an optional Scope slot — kept off the\n// AgNode shape itself so that @silvery/ag stays free of an upward\n// dependency on @silvery/scope. The slot is owned by the reconciler:\n//\n// - hooks (useScope) attach a fiber-local scope on first access via\n// `attachNodeScope`,\n// - the unmount paths below (removeChild, removeChildFromContainer,\n// clearContainer, detachDeletedInstance) walk the doomed subtree and\n// fire-and-forget `scope[Symbol.asyncDispose]()`,\n// - any rejection routes through `reportDisposeError(error, { phase:\n// \"react-unmount\", scope })`. Disposal is unavoidable — there is no\n// path that swallows the slot without disposing.\n//\n// A WeakMap means a node that's eligible for GC drops its scope reference\n// even if the dispose was already kicked off; the dispose itself keeps the\n// scope alive for the duration of the teardown via its own closures.\n\n// Module-instance-shared via globalThis so duplicate module copies (e.g. when\n// tests import host-config relatively while the renderer imports it through\n// the @silvery/ag-react/reconciler symlink) all see the same per-node scope\n// table. Without this, two module copies hold two independent WeakMaps and\n// fiber-scope disposal silently no-ops because the dispose path's WeakMap is\n// not the one the consumer attached to.\nconst NODE_SCOPES_KEY = Symbol.for(\"@silvery/ag-react/reconciler/nodeScopes\")\ntype NodeScopesRegistry = WeakMap<AgNode, Scope>\nconst nodeScopes: NodeScopesRegistry =\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- globalThis registry\n (globalThis as any)[NODE_SCOPES_KEY] ??\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ((globalThis as any)[NODE_SCOPES_KEY] = new WeakMap<AgNode, Scope>())\n\n/**\n * Attach a fiber-local scope to a host instance. Called from `useScope` /\n * `useScopeEffect` when a component first asks for a scope. Idempotent\n * within a single mount: replacing an attached scope without first\n * disposing it would leak the predecessor, so this throws instead.\n */\nexport function attachNodeScope(node: AgNode, scope: Scope): void {\n const existing = nodeScopes.get(node)\n if (existing && existing !== scope) {\n throw new Error(\n \"attachNodeScope: node already has a different scope attached. \" +\n \"Detach (or dispose) the existing scope before attaching another.\",\n )\n }\n nodeScopes.set(node, scope)\n}\n\n/** Read the fiber-local scope (or `undefined`) for a host instance. */\nexport function getNodeScope(node: AgNode): Scope | undefined {\n return nodeScopes.get(node)\n}\n\n/**\n * Detach the slot without disposing — used by hooks whose own\n * `useEffect` cleanup ran first (so the scope is already disposed and the\n * unmount path must not double-dispose).\n */\nexport function detachNodeScope(node: AgNode): Scope | undefined {\n const scope = nodeScopes.get(node)\n if (scope) nodeScopes.delete(node)\n return scope\n}\n\n/**\n * Dispose any scope attached to `node` and to every descendant. Called\n * from the reconciler's unmount paths. Fire-and-forget per the design\n * contract: react commit is synchronous, scope dispose is async, so we\n * kick off the promise and route rejections through `reportDisposeError`.\n *\n * Walks the subtree synchronously so all slots are detached *before* any\n * dispose promise resolves — this prevents a re-entrant render from\n * observing a partially torn-down tree with live scope slots.\n */\nexport function disposeSubtreeScopes(node: AgNode): void {\n // Detach first, dispose second — so an exception in dispose doesn't\n // leave the slot pointing at a half-disposed scope.\n const scope = nodeScopes.get(node)\n if (scope) {\n nodeScopes.delete(node)\n void scope[Symbol.asyncDispose]().catch((error) =>\n reportDisposeError(error, { phase: \"react-unmount\", scope }),\n )\n }\n for (const child of node.children) {\n disposeSubtreeScopes(child)\n }\n}\n\n// ============================================================================\n// Subtree Dirty Propagation\n// ============================================================================\n\n/**\n * Mark this node and all ancestors as having dirty content/layout.\n * Used to enable fast-path subtree skipping in renderPhase.\n */\nfunction markSubtreeDirty(node: AgNode | null): void {\n const epoch = getRenderEpoch()\n while (node && !isDirty(node.dirtyBits, node.dirtyEpoch, SUBTREE_BIT)) {\n if (node.dirtyEpoch !== epoch) {\n node.dirtyBits = SUBTREE_BIT\n node.dirtyEpoch = epoch\n } else {\n node.dirtyBits |= SUBTREE_BIT\n }\n node = node.parent\n }\n}\n\n/**\n * When a child change (append/remove/insert/text-update) occurs inside a\n * virtual text subtree (no layoutNode), the nearest layout ancestor must be\n * notified so its measure function re-collects descendant text and the layout\n * engine recalculates dimensions. Without this, the measure cache stays stale\n * and renderPhase renders at the wrong size / doesn't clear old content.\n *\n * No-op when the node already has a layoutNode (normal path handles it).\n */\nfunction markLayoutAncestorDirty(node: AgNode): void {\n if (node.layoutNode) return\n let ancestor: AgNode | null = node.parent\n while (ancestor && !ancestor.layoutNode) {\n ancestor = ancestor.parent\n }\n if (ancestor?.layoutNode) {\n const epoch = getRenderEpoch()\n if (ancestor.dirtyEpoch !== epoch) {\n ancestor.dirtyBits = CONTENT_BIT | STYLE_PROPS_BIT\n ancestor.dirtyEpoch = epoch\n } else {\n ancestor.dirtyBits |= CONTENT_BIT | STYLE_PROPS_BIT\n }\n ancestor.layoutNode.markDirty()\n trackContentDirty(ancestor)\n }\n}\n\n// ============================================================================\n// Dev Warnings\n// ============================================================================\n//\n// Box-inside-Text warning uses the shared `warnOnce` latch from @silvery/ansi\n// (see km-silvery.latch-consolidation). Tests reset via\n// `_resetWarnOnceForTesting(\"silvery/ag-react:box-in-text\")`.\n\nconst BOX_INSIDE_TEXT_WARNING_ID = \"silvery/ag-react:box-in-text\"\n\n/**\n * Reset the box-inside-text warning latch (for testing).\n * Thin wrapper over `_resetWarnOnceForTesting` that pins the warning ID —\n * call sites don't need to remember the exact key.\n */\nexport function _resetBoxInsideTextWarning(): void {\n _resetWarnOnceForTesting(BOX_INSIDE_TEXT_WARNING_ID)\n}\n\n/**\n * Ink-compatible strict validation mode.\n * When enabled, the reconciler throws errors instead of warnings for:\n * - Raw text directly inside a Box (must be inside Text)\n * - Box nested inside Text\n */\nlet inkStrictValidation = false\n\n/** Enable/disable Ink-compatible strict validation. */\nexport function setInkStrictValidation(enabled: boolean): void {\n inkStrictValidation = enabled\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Container type - the root of our Silvery tree\n */\nexport interface Container {\n root: AgNode\n onRender: () => void\n}\n\n/**\n * Host context tracks whether we're inside a Text component\n */\ninterface HostContext {\n isInsideText: boolean\n}\n\n// ============================================================================\n// Update Priority Management (for react-reconciler 0.33+)\n// ============================================================================\n\nlet currentUpdatePriority = NoEventPriority\n\n/**\n * Run a callback with DiscreteEventPriority so React treats state\n * updates inside it as user-interaction priority (synchronous commit).\n * Use this for keyboard input handling to prevent React's concurrent\n * scheduler from deferring the commit.\n */\nexport function runWithDiscreteEvent(fn: () => void): void {\n const prev = currentUpdatePriority\n currentUpdatePriority = DiscreteEventPriority\n try {\n fn()\n } finally {\n currentUpdatePriority = prev\n }\n}\n\n// ============================================================================\n// Host Config\n// ============================================================================\n\n/**\n * The React Reconciler host config.\n * This defines how React creates, updates, and manages our custom SilveryNodes.\n */\nexport const hostConfig = {\n // Renderer identity (used by React DevTools to identify this renderer)\n rendererPackageName: \"@silvery/ag-react\",\n rendererVersion: \"0.0.1\",\n\n // Feature flags\n supportsMutation: true,\n supportsPersistence: false,\n supportsHydration: false,\n isPrimaryRenderer: true,\n\n // Scheduling\n scheduleTimeout: setTimeout,\n cancelTimeout: clearTimeout,\n noTimeout: -1,\n supportsMicrotasks: true,\n scheduleMicrotask: queueMicrotask,\n\n // Context - tracks whether we're inside a Text component\n getRootHostContext(): HostContext {\n return { isInsideText: false }\n },\n\n getChildHostContext(parentHostContext: HostContext, type: AgNodeType): HostContext {\n // Normalize Ink intrinsic types (ink-box → silvery-box, ink-text → silvery-text)\n const normalizedType = normalizeNodeType(type)\n // Once inside a text node, stay inside\n const isInsideText = parentHostContext.isInsideText || normalizedType === \"silvery-text\"\n if (isInsideText === parentHostContext.isInsideText) {\n return parentHostContext\n }\n return { isInsideText }\n },\n\n // Instance creation\n createInstance(\n type: AgNodeType,\n props: BoxProps | TextProps,\n _rootContainer: unknown,\n hostContext: HostContext,\n ): AgNode {\n // Normalize Ink intrinsic types (ink-box → silvery-box, ink-text → silvery-text)\n type = normalizeNodeType(type)\n // Ink-compat: flatten `style` prop from intrinsic ink-box/ink-text elements.\n // Ink's intrinsic elements use `<ink-box style={{marginLeft: 1}}>` where the\n // style object contains layout props. Silvery expects them as top-level props.\n if (\"style\" in props && props.style && typeof props.style === \"object\") {\n props = { ...props.style, ...props } as BoxProps | TextProps\n }\n // Ink-compat: throw when a Box is nested inside a Text\n if (type === \"silvery-box\" && hostContext.isInsideText) {\n if (inkStrictValidation) {\n throw new Error(\"<Box> can\\u2019t be nested inside <Text> component\")\n }\n if (process.env.NODE_ENV !== \"production\") {\n warnOnce(BOX_INSIDE_TEXT_WARNING_ID, () =>\n log.warn?.(\n \"<Box> cannot be nested inside <Text>. This produces undefined layout behavior.\",\n ),\n )\n }\n }\n\n // Nested text nodes become \"virtual\" - no layout node\n if (type === \"silvery-text\" && hostContext.isInsideText) {\n const node = createVirtualTextNode(props as TextProps)\n logNodeLifecycle(\"mount\", node)\n return node\n }\n const node = createNode(type, props)\n logNodeLifecycle(\"mount\", node)\n return node\n },\n\n createTextInstance(text: string, _rootContainer: unknown, hostContext: HostContext): AgNode {\n // Ink-compat: throw when text appears directly in a Box (outside Text)\n if (inkStrictValidation && !hostContext.isInsideText && text.trim().length > 0) {\n throw new Error(`Text string \"${text}\" must be rendered inside <Text> component`)\n }\n // Raw text nodes don't have layout nodes - they're just data nodes\n // Their content is rendered by their parent silvery-text element\n const epoch = getRenderEpoch()\n const node: AgNode = {\n type: \"silvery-text\",\n props: { children: text } as TextProps,\n children: [],\n parent: null,\n layoutNode: null, // No layout node for raw text\n boxRect: null,\n scrollRect: null,\n screenRect: null,\n prevLayout: null,\n prevScrollRect: null,\n prevScreenRect: null,\n layoutChangedThisFrame: INITIAL_EPOCH,\n dirtyBits: CONTENT_BIT | STYLE_PROPS_BIT | BG_BIT | SUBTREE_BIT,\n dirtyEpoch: epoch,\n textContent: text,\n isRawText: true,\n }\n logNodeLifecycle(\"mount\", node)\n return node\n },\n\n // Tree operations\n appendChild(parentInstance: AgNode, child: AgNode) {\n // React calls appendChild to move an existing child during keyed reorder.\n // Remove from old position first to avoid duplicating in the children array.\n const existingIndex = parentInstance.children.indexOf(child)\n if (existingIndex !== -1) {\n parentInstance.children.splice(existingIndex, 1)\n if (parentInstance.layoutNode && child.layoutNode) {\n parentInstance.layoutNode.removeChild(child.layoutNode)\n }\n }\n child.parent = parentInstance\n parentInstance.children.push(child)\n // Only add to layout tree if both nodes have layout nodes\n if (parentInstance.layoutNode && child.layoutNode) {\n // Count non-raw-text children for proper layout index\n const layoutIndex = parentInstance.children.filter((c) => c.layoutNode !== null).length - 1\n parentInstance.layoutNode.insertChild(child.layoutNode, layoutIndex)\n }\n {\n const epoch = getRenderEpoch()\n const bits = CHILDREN_BIT | CONTENT_BIT\n parentInstance.dirtyBits =\n parentInstance.dirtyEpoch !== epoch ? bits : parentInstance.dirtyBits | bits\n parentInstance.dirtyEpoch = epoch\n }\n parentInstance.layoutNode?.markDirty()\n trackContentDirty(parentInstance)\n markLayoutAncestorDirty(parentInstance)\n markSubtreeDirty(parentInstance)\n },\n\n appendInitialChild(parentInstance: AgNode, child: AgNode) {\n child.parent = parentInstance\n parentInstance.children.push(child)\n // Only add to layout tree if both nodes have layout nodes\n if (parentInstance.layoutNode && child.layoutNode) {\n const layoutIndex = parentInstance.children.filter((c) => c.layoutNode !== null).length - 1\n parentInstance.layoutNode.insertChild(child.layoutNode, layoutIndex)\n }\n },\n\n appendChildToContainer(container: Container, child: AgNode) {\n // Remove from old position if already a child (keyed reorder)\n const existingIndex = container.root.children.indexOf(child)\n if (existingIndex !== -1) {\n container.root.children.splice(existingIndex, 1)\n if (container.root.layoutNode && child.layoutNode) {\n container.root.layoutNode.removeChild(child.layoutNode)\n }\n }\n child.parent = container.root\n container.root.children.push(child)\n if (container.root.layoutNode && child.layoutNode) {\n const layoutIndex = container.root.children.filter((c) => c.layoutNode !== null).length - 1\n container.root.layoutNode.insertChild(child.layoutNode, layoutIndex)\n }\n {\n const epoch = getRenderEpoch()\n const bits = CHILDREN_BIT | CONTENT_BIT\n container.root.dirtyBits =\n container.root.dirtyEpoch !== epoch ? bits : container.root.dirtyBits | bits\n container.root.dirtyEpoch = epoch\n }\n container.root.layoutNode?.markDirty()\n trackContentDirty(container.root)\n markSubtreeDirty(container.root)\n },\n\n removeChild(parentInstance: AgNode, child: AgNode) {\n const index = parentInstance.children.indexOf(child)\n if (index !== -1) {\n // Notify focus manager before detaching (needs parent chain intact for subtree check)\n onNodeRemovedCallback?.(child)\n logUnmountSubtree(child)\n // Dispose any fiber-local scopes in the doomed subtree. Must happen\n // before we splice — disposeSubtreeScopes walks `child.children`,\n // and we want the walk to see the same tree the focus manager just\n // observed.\n disposeSubtreeScopes(child)\n parentInstance.children.splice(index, 1)\n if (parentInstance.layoutNode && child.layoutNode) {\n parentInstance.layoutNode.removeChild(child.layoutNode)\n child.layoutNode.free()\n }\n child.parent = null\n {\n const epoch = getRenderEpoch()\n const bits = CHILDREN_BIT | CONTENT_BIT\n parentInstance.dirtyBits =\n parentInstance.dirtyEpoch !== epoch ? bits : parentInstance.dirtyBits | bits\n parentInstance.dirtyEpoch = epoch\n }\n parentInstance.layoutNode?.markDirty()\n trackContentDirty(parentInstance)\n markLayoutAncestorDirty(parentInstance)\n markSubtreeDirty(parentInstance)\n }\n },\n\n removeChildFromContainer(container: Container, child: AgNode) {\n const index = container.root.children.indexOf(child)\n if (index !== -1) {\n // Notify focus manager before detaching\n onNodeRemovedCallback?.(child)\n logUnmountSubtree(child)\n disposeSubtreeScopes(child)\n container.root.children.splice(index, 1)\n if (container.root.layoutNode && child.layoutNode) {\n container.root.layoutNode.removeChild(child.layoutNode)\n child.layoutNode.free()\n }\n child.parent = null\n {\n const epoch = getRenderEpoch()\n const bits = CHILDREN_BIT | CONTENT_BIT\n container.root.dirtyBits =\n container.root.dirtyEpoch !== epoch ? bits : container.root.dirtyBits | bits\n container.root.dirtyEpoch = epoch\n }\n container.root.layoutNode?.markDirty()\n trackContentDirty(container.root)\n markSubtreeDirty(container.root)\n }\n },\n\n insertBefore(parentInstance: AgNode, child: AgNode, beforeChild: AgNode) {\n // React calls insertBefore to move an existing child during keyed reorder.\n // Remove from old position first to avoid duplicating in the children array.\n const existingIndex = parentInstance.children.indexOf(child)\n if (existingIndex !== -1) {\n parentInstance.children.splice(existingIndex, 1)\n if (parentInstance.layoutNode && child.layoutNode) {\n parentInstance.layoutNode.removeChild(child.layoutNode)\n }\n }\n const beforeIndex = parentInstance.children.indexOf(beforeChild)\n if (beforeIndex !== -1) {\n child.parent = parentInstance\n parentInstance.children.splice(beforeIndex, 0, child)\n if (parentInstance.layoutNode && child.layoutNode) {\n // Count non-raw-text children before this position for proper layout index\n const layoutIndex = parentInstance.children\n .slice(0, beforeIndex)\n .filter((c) => c.layoutNode !== null).length\n parentInstance.layoutNode.insertChild(child.layoutNode, layoutIndex)\n }\n {\n const epoch = getRenderEpoch()\n const bits = CHILDREN_BIT | CONTENT_BIT\n parentInstance.dirtyBits =\n parentInstance.dirtyEpoch !== epoch ? bits : parentInstance.dirtyBits | bits\n parentInstance.dirtyEpoch = epoch\n }\n parentInstance.layoutNode?.markDirty()\n trackContentDirty(parentInstance)\n markLayoutAncestorDirty(parentInstance)\n markSubtreeDirty(parentInstance)\n }\n },\n\n insertInContainerBefore(container: Container, child: AgNode, beforeChild: AgNode) {\n // Remove from old position if already a child (keyed reorder)\n const existingIndex = container.root.children.indexOf(child)\n if (existingIndex !== -1) {\n container.root.children.splice(existingIndex, 1)\n if (container.root.layoutNode && child.layoutNode) {\n container.root.layoutNode.removeChild(child.layoutNode)\n }\n }\n const beforeIndex = container.root.children.indexOf(beforeChild)\n if (beforeIndex !== -1) {\n child.parent = container.root\n container.root.children.splice(beforeIndex, 0, child)\n if (container.root.layoutNode && child.layoutNode) {\n const layoutIndex = container.root.children\n .slice(0, beforeIndex)\n .filter((c) => c.layoutNode !== null).length\n container.root.layoutNode.insertChild(child.layoutNode, layoutIndex)\n }\n {\n const epoch = getRenderEpoch()\n const bits = CHILDREN_BIT | CONTENT_BIT\n container.root.dirtyBits =\n container.root.dirtyEpoch !== epoch ? bits : container.root.dirtyBits | bits\n container.root.dirtyEpoch = epoch\n }\n container.root.layoutNode?.markDirty()\n trackContentDirty(container.root)\n markSubtreeDirty(container.root)\n }\n },\n\n // Updates\n prepareUpdate(\n _instance: AgNode,\n _type: AgNodeType,\n oldProps: BoxProps | TextProps,\n newProps: BoxProps | TextProps,\n ): boolean | null {\n // Return true if we need to update\n return classifyPropChanges(\n oldProps as Record<string, unknown>,\n newProps as Record<string, unknown>,\n ).anyChanged\n },\n\n // Note: react-reconciler 0.33+ changed the signature from\n // commitUpdate(instance, updatePayload, type, oldProps, newProps) to\n // commitUpdate(instance, type, oldProps, newProps, finishedWork)\n commitUpdate(\n instance: AgNode,\n _type: AgNodeType,\n oldProps: BoxProps | TextProps,\n newProps: BoxProps | TextProps,\n _finishedWork: unknown,\n ) {\n // Ink-compat: flatten `style` prop from intrinsic ink-box/ink-text elements\n if (\"style\" in oldProps && oldProps.style && typeof oldProps.style === \"object\") {\n oldProps = { ...oldProps.style, ...oldProps } as BoxProps | TextProps\n }\n if (\"style\" in newProps && newProps.style && typeof newProps.style === \"object\") {\n newProps = { ...newProps.style, ...newProps } as BoxProps | TextProps\n }\n\n // Single-pass prop classification — replaces 3 separate iterations\n // (propsEqual + layoutPropsChanged + contentPropsChanged)\n const { anyChanged, layoutChanged, contentChanged } = classifyPropChanges(\n oldProps as Record<string, unknown>,\n newProps as Record<string, unknown>,\n )\n\n // Early exit if props are equal (React may call commitUpdate even when nothing changed)\n if (!anyChanged) {\n instance.props = newProps\n return\n }\n\n // Apply layout-affecting prop changes\n if (layoutChanged) {\n if (instance.layoutNode) {\n if (instance.type === \"silvery-text\") {\n applyTextFlexItemProps(instance.layoutNode, newProps as TextProps, oldProps as TextProps)\n } else if (instance.type === \"silvery-viewport\") {\n applyViewportProps(\n instance.layoutNode,\n newProps as unknown as ViewportProps,\n oldProps as unknown as ViewportProps,\n )\n } else if (instance.type === \"silvery-island\") {\n applyIslandProps(\n instance.layoutNode,\n newProps as unknown as IslandLayoutProps,\n oldProps as unknown as IslandLayoutProps,\n )\n } else {\n applyBoxProps(instance.layoutNode, newProps as BoxProps, oldProps as BoxProps)\n }\n instance.layoutNode.markDirty()\n }\n }\n if (contentChanged) {\n const epoch = getRenderEpoch()\n // stylePropsDirty: always set for any visual change. Render phase uses this\n // to know the node needs re-rendering (border, text style, bg, etc.).\n let bits = STYLE_PROPS_BIT\n // contentDirty: only for text content changes (not style-only changes).\n // Style-only changes (borderColor, color, bold) set stylePropsDirty but NOT\n // contentDirty, so render phase won't cascade to children for border-only\n // changes where the content area is unchanged.\n if (contentChanged === \"text\") {\n bits |= CONTENT_BIT\n if (instance.layoutNode) {\n instance.layoutNode.markDirty()\n }\n }\n // bgDirty: specifically track backgroundColor changes (added/changed/removed).\n // Render phase uses this to cascade re-renders only when the content area\n // was actually affected (not for border-only paint changes).\n if (\n (oldProps as Record<string, unknown>).backgroundColor !==\n (newProps as Record<string, unknown>).backgroundColor\n ) {\n bits |= BG_BIT\n }\n // Border removal: when borderStyle goes from truthy to falsy, stale border\n // characters (╭╮╰╯│─) persist in the cloned buffer because renderBox doesn't\n // draw anything at those positions. Setting bgDirty makes contentAreaAffected\n // true, triggering clearNodeRegion to fill the area with inherited bg.\n // Border *addition* doesn't need this — renderBorder overwrites the old cells.\n if (\n (oldProps as Record<string, unknown>).borderStyle &&\n !(newProps as Record<string, unknown>).borderStyle\n ) {\n bits |= BG_BIT\n }\n // NOTE: outline removal does NOT need a dirty bit here — the decoration\n // phase walks every frame and clears previous outline cells from\n // per-cell snapshots. See pipeline/decoration-phase.ts.\n // Theme change: all descendants need re-rendering with new token values.\n // We set both CONTENT_BIT and BG_BIT so that bgOnlyAffected remains false\n // (bgOnlyAffected = bgDirty && !contentDirty && ...). Without CONTENT_BIT,\n // bgOnlyChange fires when the ThemeProvider Box has a theme.bg value\n // (hasBgColor=true via getEffectiveBg), and bgOnlyChange sets\n // childrenNeedFreshRender=false — children skip re-render and use stale\n // $token-resolved colors from the clone. CONTENT_BIT disables bgOnlyChange\n // and ensures childrenNeedFreshRender=true so children re-render with the\n // new pushContextTheme(newTheme) context in the render phase.\n // NOTE: CONTENT_BIT here does NOT call layoutNode.markDirty() — that is\n // only done when contentChanged === \"text\" (not for theme-only changes).\n if (\n (oldProps as Record<string, unknown>).theme !== (newProps as Record<string, unknown>).theme\n ) {\n bits |= BG_BIT | CONTENT_BIT\n }\n instance.dirtyBits = instance.dirtyEpoch !== epoch ? bits : instance.dirtyBits | bits\n instance.dirtyEpoch = epoch\n }\n\n // Track dirty node in module-level set for O(1) pipeline phase checks\n if (contentChanged) {\n trackContentDirty(instance)\n }\n\n // Track style-only dirty nodes for the fast path.\n // A node is style-only when: contentChanged is \"style\" (not \"text\"),\n // layoutChanged is false, bgDirty is false, AND the node doesn't already\n // have contentDirty or childrenDirty (which may have been set by\n // commitTextUpdate on a child BEFORE this commitUpdate runs — React\n // processes children before parents in the commit phase).\n if (\n contentChanged === \"style\" &&\n !layoutChanged &&\n !isDirty(instance.dirtyBits, instance.dirtyEpoch, BG_BIT) &&\n !isDirty(instance.dirtyBits, instance.dirtyEpoch, CONTENT_BIT) &&\n !isDirty(instance.dirtyBits, instance.dirtyEpoch, CHILDREN_BIT)\n ) {\n trackStyleOnlyDirty(instance)\n }\n\n instance.props = newProps\n logNodeLifecycle(\"update\", instance)\n\n // Only mark subtree/ancestor dirty when visual changes were detected.\n // Data attributes (data-*), event handlers, and other non-visual props\n // don't affect rendering, so propagating dirty flags wastes render phase\n // time traversing unchanged subtrees.\n //\n // scrollTo/scrollOffset changes affect rendering via scroll phase (children\n // shift position), so they must propagate subtreeDirty for render phase\n // traversal. Without this, the render phase fast-path skips ancestors of\n // the scroll container, never reaching the container to re-render at the\n // new scroll position.\n const scrollToChanged =\n (oldProps as Record<string, unknown>).scrollTo !==\n (newProps as Record<string, unknown>).scrollTo\n const scrollOffsetChanged =\n (oldProps as Record<string, unknown>).scrollOffset !==\n (newProps as Record<string, unknown>).scrollOffset\n if (scrollToChanged || scrollOffsetChanged) {\n trackScrollDirty(instance)\n }\n if (layoutChanged || contentChanged || scrollToChanged || scrollOffsetChanged) {\n markLayoutAncestorDirty(instance)\n markSubtreeDirty(instance)\n }\n },\n\n commitTextUpdate(textInstance: AgNode, _oldText: string, newText: string) {\n textInstance.textContent = newText\n syncTextContentSignal(textInstance)\n textInstance.props = { children: newText } as TextProps\n const epoch = getRenderEpoch()\n const bits = CONTENT_BIT | STYLE_PROPS_BIT\n textInstance.dirtyBits =\n textInstance.dirtyEpoch !== epoch ? bits : textInstance.dirtyBits | bits\n textInstance.dirtyEpoch = epoch\n trackContentDirty(textInstance)\n // Text content change affects layout (measure function will return different size)\n // Walk up to the nearest layout ancestor so its measure cache is invalidated\n markLayoutAncestorDirty(textInstance)\n markSubtreeDirty(textInstance)\n },\n\n // Finalization\n finalizeInitialChildren() {\n return false\n },\n\n prepareForCommit() {\n return null\n },\n\n resetAfterCommit(container: Container) {\n // Trigger render after React finishes committing.\n //\n // Render-budget watchdog (zero-overhead when disabled): if the commit\n // exceeds `SILVERY_RENDER_BUDGET_MS` (default 500ms), emit a console\n // warning with the duration. Helps catch cascading-render regressions\n // before they manifest as user-visible freezes (the symptom that\n // motivated @km/board/projection-stability-improvements).\n //\n // Disabled by default in production by setting the env var to 0 or\n // unsetting it; budget is opt-in. Set `SILVERY_RENDER_BUDGET_MS=500`\n // (or any positive number) during dev to enable.\n const budgetEnv =\n typeof process !== \"undefined\" ? process.env.SILVERY_RENDER_BUDGET_MS : undefined\n const budget = budgetEnv !== undefined ? Number(budgetEnv) : 0\n if (budget > 0 && Number.isFinite(budget)) {\n const start = performance.now()\n container.onRender()\n const elapsed = performance.now() - start\n if (elapsed > budget) {\n // Use console.warn here (not loggily) so the warning surfaces in\n // the dev console even when no DEBUG namespace is set. Stays out\n // of the rendered TUI surface — this fires after React's commit\n // boundary and writes via the console-sink the host installs.\n // eslint-disable-next-line no-console\n console.warn(\n `[silvery] render commit exceeded budget: ${elapsed.toFixed(0)}ms > ${budget}ms — see @km/board/projection-stability-improvements for cascade-prevention guidance`,\n )\n }\n } else {\n container.onRender()\n }\n },\n\n // Misc\n getPublicInstance(instance: AgNode) {\n return instance\n },\n\n shouldSetTextContent() {\n return false\n },\n\n clearContainer(container: Container) {\n // Notify focus manager before clearing — any child subtree may contain focus\n for (const child of container.root.children) {\n onNodeRemovedCallback?.(child)\n logUnmountSubtree(child)\n }\n // Dispose any fiber-local scopes in the cleared subtrees, plus any\n // attached to the root itself (withScope-style root scopes attach\n // here). The root's slot is detached first; descendants follow.\n disposeSubtreeScopes(container.root)\n for (const child of container.root.children) {\n if (container.root.layoutNode && child.layoutNode) {\n container.root.layoutNode.removeChild(child.layoutNode)\n child.layoutNode.free()\n }\n }\n container.root.children = []\n // Must invalidate dirty flags — same as removeChildFromContainer.\n // Without this, the pipeline can skip re-rendering after a root clear,\n // leaving stale buffer content (tree/buffer mismatch).\n {\n const epoch = getRenderEpoch()\n const bits = CHILDREN_BIT | CONTENT_BIT\n container.root.dirtyBits =\n container.root.dirtyEpoch !== epoch ? bits : container.root.dirtyBits | bits\n container.root.dirtyEpoch = epoch\n }\n container.root.layoutNode?.markDirty()\n trackContentDirty(container.root)\n markSubtreeDirty(container.root)\n },\n\n preparePortalMount() {\n // No-op for terminal\n },\n\n getCurrentEventPriority() {\n if (currentUpdatePriority !== NoEventPriority) {\n return currentUpdatePriority\n }\n return DefaultEventPriority\n },\n\n getInstanceFromNode() {\n return null\n },\n\n beforeActiveInstanceBlur() {\n // No-op\n },\n\n afterActiveInstanceBlur() {\n // No-op\n },\n\n prepareScopeUpdate() {\n // No-op\n },\n\n getInstanceFromScope() {\n return null\n },\n\n detachDeletedInstance(node: AgNode) {\n // Final-cleanup hook fired after React commits a deletion. The\n // per-subtree disposal already happened in removeChild /\n // removeChildFromContainer / clearContainer (those run during commit\n // with the parent chain intact). This catches any fiber-local scope\n // still attached at this point — a re-entrant attach during dispose,\n // or a fiber path that bypassed the structural removeChild flow.\n // Idempotent: disposeSubtreeScopes detaches before disposing, so a\n // node that's already been processed becomes a no-op.\n disposeSubtreeScopes(node)\n },\n\n // React 19 / react-reconciler 0.33+ required methods\n setCurrentUpdatePriority(newPriority: number) {\n currentUpdatePriority = newPriority\n },\n\n getCurrentUpdatePriority() {\n return currentUpdatePriority\n },\n\n resolveUpdatePriority() {\n if (currentUpdatePriority !== NoEventPriority) {\n return currentUpdatePriority\n }\n return DefaultEventPriority\n },\n\n maySuspendCommit() {\n return false\n },\n\n NotPendingTransition: null,\n HostTransitionContext: createContext(null),\n\n resetFormInstance() {\n // No-op\n },\n\n requestPostPaintCallback() {\n // No-op\n },\n\n shouldAttemptEagerTransition() {\n return false\n },\n\n trackSchedulerEvent() {\n // No-op\n },\n\n resolveEventType() {\n return null\n },\n\n resolveEventTimeStamp() {\n return -1.1\n },\n\n preloadInstance() {\n return true\n },\n\n startSuspendingCommit() {\n // No-op\n },\n\n suspendInstance() {\n // No-op\n },\n\n waitForCommitToBeReady() {\n return null\n },\n\n // ========================================================================\n // Suspense Support (hide/unhide)\n // ========================================================================\n\n /**\n * Hide an instance during Suspense.\n * Called when React needs to hide content while showing a fallback.\n *\n * Must set stylePropsDirty (render phase fast-path skip includes stylePropsDirty check),\n * layoutNode.markDirty() (hiding changes measured content — the layout engine\n * must recalculate dimensions), and markLayoutAncestorDirty (virtual text nodes\n * without layoutNode need the nearest layout ancestor dirty).\n */\n hideInstance(instance: AgNode) {\n instance.hidden = true\n const epoch = getRenderEpoch()\n const bits = CONTENT_BIT | STYLE_PROPS_BIT\n instance.dirtyBits = instance.dirtyEpoch !== epoch ? bits : instance.dirtyBits | bits\n instance.dirtyEpoch = epoch\n if (instance.layoutNode) {\n instance.layoutNode.markDirty()\n }\n trackContentDirty(instance)\n // Mark parent dirty to trigger re-render\n if (instance.parent) {\n if (instance.parent.dirtyEpoch !== epoch) {\n instance.parent.dirtyBits = CONTENT_BIT\n instance.parent.dirtyEpoch = epoch\n } else {\n instance.parent.dirtyBits |= CONTENT_BIT\n }\n trackContentDirty(instance.parent)\n }\n markLayoutAncestorDirty(instance)\n markSubtreeDirty(instance)\n },\n\n /**\n * Unhide an instance after Suspense resolves.\n * Called when the suspended content is ready to show.\n *\n * Same invalidation as hideInstance — the node's visibility change affects\n * layout (measured content changes) and paint (content must be re-rendered).\n */\n unhideInstance(instance: AgNode, _props: BoxProps | TextProps) {\n instance.hidden = false\n const epoch = getRenderEpoch()\n const bits = CONTENT_BIT | STYLE_PROPS_BIT\n instance.dirtyBits = instance.dirtyEpoch !== epoch ? bits : instance.dirtyBits | bits\n instance.dirtyEpoch = epoch\n if (instance.layoutNode) {\n instance.layoutNode.markDirty()\n }\n trackContentDirty(instance)\n // Mark parent dirty to trigger re-render\n if (instance.parent) {\n if (instance.parent.dirtyEpoch !== epoch) {\n instance.parent.dirtyBits = CONTENT_BIT\n instance.parent.dirtyEpoch = epoch\n } else {\n instance.parent.dirtyBits |= CONTENT_BIT\n }\n trackContentDirty(instance.parent)\n }\n markLayoutAncestorDirty(instance)\n markSubtreeDirty(instance)\n },\n\n /**\n * Hide a text instance during Suspense.\n *\n * Text instances don't have layout nodes. markLayoutAncestorDirty walks up\n * to the nearest layout ancestor and marks it dirty so the measure function\n * re-collects descendant text (collectNodeTextContent skips hidden children).\n */\n hideTextInstance(textInstance: AgNode) {\n textInstance.hidden = true\n const epoch = getRenderEpoch()\n const bits = CONTENT_BIT | STYLE_PROPS_BIT\n textInstance.dirtyBits =\n textInstance.dirtyEpoch !== epoch ? bits : textInstance.dirtyBits | bits\n textInstance.dirtyEpoch = epoch\n trackContentDirty(textInstance)\n if (textInstance.parent) {\n if (textInstance.parent.dirtyEpoch !== epoch) {\n textInstance.parent.dirtyBits = CONTENT_BIT\n textInstance.parent.dirtyEpoch = epoch\n } else {\n textInstance.parent.dirtyBits |= CONTENT_BIT\n }\n trackContentDirty(textInstance.parent)\n }\n markLayoutAncestorDirty(textInstance)\n markSubtreeDirty(textInstance)\n },\n\n /**\n * Unhide a text instance after Suspense resolves.\n *\n * Same invalidation as hideTextInstance — the text content changes when\n * hidden children become visible again.\n */\n unhideTextInstance(textInstance: AgNode, _text: string) {\n textInstance.hidden = false\n const epoch = getRenderEpoch()\n const bits = CONTENT_BIT | STYLE_PROPS_BIT\n textInstance.dirtyBits =\n textInstance.dirtyEpoch !== epoch ? bits : textInstance.dirtyBits | bits\n textInstance.dirtyEpoch = epoch\n trackContentDirty(textInstance)\n if (textInstance.parent) {\n if (textInstance.parent.dirtyEpoch !== epoch) {\n textInstance.parent.dirtyBits = CONTENT_BIT\n textInstance.parent.dirtyEpoch = epoch\n } else {\n textInstance.parent.dirtyBits |= CONTENT_BIT\n }\n trackContentDirty(textInstance.parent)\n }\n markLayoutAncestorDirty(textInstance)\n markSubtreeDirty(textInstance)\n },\n}\n","/**\n * Silvery React Reconciler\n *\n * Custom React reconciler that builds a tree of SilveryNodes, each with a Yoga layout node.\n * This is the core of Silvery's architecture - separating structure (React reconciliation)\n * from content (terminal rendering).\n *\n * The reconciler creates SilveryNodes during React's reconciliation phase,\n * but actual terminal content is rendered later after Yoga computes layout.\n */\n\n// @ts-expect-error - react-reconciler has no type declarations\nimport Reconciler from \"react-reconciler\"\nimport type { AgNode } from \"@silvery/ag/types\"\nimport { type Container, disposeSubtreeScopes, hostConfig } from \"./host-config\"\nimport { createRootNode } from \"./nodes\"\n\n// Re-export only what's needed by render.tsx and testing/index.tsx\nexport type { Container } from \"./host-config\"\nexport {\n runWithDiscreteEvent,\n _resetBoxInsideTextWarning,\n setInkStrictValidation,\n setOnNodeRemoved,\n attachNodeScope,\n detachNodeScope,\n getNodeScope,\n disposeSubtreeScopes,\n} from \"./host-config\"\n\n// ============================================================================\n// Reconciler Export\n// ============================================================================\n\n/**\n * Create the React reconciler instance.\n */\nexport const reconciler = Reconciler(hostConfig)\n\n/**\n * Create a container for rendering.\n */\nexport function createContainer(onRender: () => void): Container {\n const root = createRootNode()\n return { root, onRender }\n}\n\n/**\n * Optional callbacks passed to `createFiberRoot` so the React reconciler can\n * surface render/effect errors back to the host runtime instead of swallowing\n * them. Each callback is invoked with the unwrapped `Error` (React passes the\n * raw thrown value; we adapt to `Error` for the callback shape).\n *\n * - `onUncaughtError` — render or effect error that React could not recover\n * from. The host should panic: restore the terminal, dump the stack to\n * stderr (on the real screen, not the altscreen overlay), and exit non-zero.\n * - `onCaughtError` — error caught by an `<ErrorBoundary>`. Usually the host\n * leaves these alone; useful for telemetry.\n * - `onRecoverableError` — React was able to recover (e.g. fell back to a\n * non-concurrent render). Worth a debug log.\n *\n * @see `@silvery/ag-term`'s `runtime/create-app.tsx` — wires `onUncaughtError`\n * to `panicApp` so React render errors panic cleanly.\n */\nexport interface FiberRootOptions {\n onUncaughtError?: (error: Error) => void\n onCaughtError?: (error: Error) => void\n onRecoverableError?: (error: Error) => void\n}\n\n/**\n * Create a React fiber root for a container (wraps the 10-argument reconciler call).\n *\n * Pass `options.onUncaughtError` to route React-thrown render errors back to\n * the host's panic path. Without it, render errors are silently swallowed\n * (the default `() => {}` callback) and the only surface for them is whatever\n * the host's `console.error` capture decides to do — typically an altscreen\n * overlay the user can't copy-paste or screenshot reliably.\n */\nexport function createFiberRoot(container: Container, options: FiberRootOptions = {}) {\n return reconciler.createContainer(\n container,\n 1, // ConcurrentRoot\n null, // hydrationCallbacks\n false, // isStrictMode\n null, // concurrentUpdatesByDefaultOverride\n \"\", // identifierPrefix\n options.onUncaughtError ?? (() => {}), // onUncaughtError\n options.onCaughtError ?? (() => {}), // onCaughtError\n options.onRecoverableError ?? (() => {}), // onRecoverableError\n null, // onDefaultTransitionIndicator\n )\n}\n\n/**\n * Get the root SilveryNode from a container.\n */\nexport function getContainerRoot(container: Container): AgNode {\n return container.root\n}\n\n/**\n * Synchronously unmount a fiber root and scrub the container so it can't\n * keep its closure-captured RenderInstance alive afterward.\n *\n * Why both steps are needed:\n *\n * 1. `createFiberRoot` uses `ConcurrentRoot` (mode 1). React's async\n * `updateContainer(null, fiberRoot, ...)` does NOT run layout-effect\n * cleanups before returning — useLayoutEffect / useBoxRect /\n * useBoxMetrics / signal-effect disposers are scheduled but may not\n * fire promptly. That keeps signal subscriptions alive past unmount,\n * which keeps the React tree reachable, which keeps the host\n * `RenderInstance` reachable. `updateContainerSync` + `flushSyncWork`\n * forces all cleanups to run inline.\n *\n * 2. Even after the React tree is detached, the `FiberRoot` keeps a\n * pointer to its `containerInfo` (our `Container`) for some time, and\n * `Container.onRender` typically closes over the entire enclosing\n * `RenderInstance`. Without nulling `onRender` and scrubbing the root\n * AgNode, the instance graph is still reachable through the FiberRoot's\n * container pointer.\n *\n * Call this in every unmount path that uses ConcurrentRoot. The previous\n * (async) pattern leaked across mount/unmount cycles in tests and likely\n * in production long-lived host applications too.\n *\n * Safe to call multiple times — `releaseContainer` is idempotent (the\n * scrub fields are nulled and `layoutNode.free()` is best-effort).\n *\n * @param fiberRoot — opaque React FiberRoot returned by `createFiberRoot`\n * @param container — the `Container` paired with that fiberRoot\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- React reconciler internal type\nexport function unmountFiberRoot(fiberRoot: any, container: Container): void {\n reconciler.updateContainerSync(null, fiberRoot, null, null)\n reconciler.flushSyncWork()\n releaseContainer(container)\n}\n\n/**\n * Scrub a Container so it can't retain its enclosing render state after\n * the React tree has been unmounted. See {@link unmountFiberRoot} for the\n * full rationale; call this directly only if you've already run a sync\n * unmount through the reconciler and just need the post-commit scrub.\n */\nexport function releaseContainer(container: Container): void {\n // Dispose any fiber-local scopes still attached to nodes in the tree.\n // Required because `updateContainerSync(null, fiberRoot, …)` on a\n // ConcurrentRoot does NOT route the unmount through the host-config\n // `removeChild*` / `clearContainer` paths in current react-reconciler\n // (0.33+) — those fire only for keyed-deletion reconciliations during a\n // mounted re-render, not for full-tree unmount. Without this walk, scopes\n // attached via `useScope()` / `useScopeEffect()` survive the unmount\n // because the per-node WeakMap entry stays reachable from\n // `container.root.children` until the line below clears it. Per the\n // design contract (hub/silvery/design/lifecycle-scope.md): \"Disposal is\n // unavoidable — there is no path that swallows the slot without\n // disposing.\" Bead: km-silvery.scope-phase-1.\n disposeSubtreeScopes(container.root)\n\n // Break FiberRoot → containerInfo → onRender → enclosing-instance retention.\n container.onRender = () => {}\n\n const root = container.root\n root.children = []\n root.parent = null\n root.boxRect = null\n root.scrollRect = null\n root.screenRect = null\n root.prevLayout = null\n root.prevScrollRect = null\n root.prevScreenRect = null\n\n if (root.layoutNode) {\n try {\n root.layoutNode.free()\n } catch {\n // best-effort; the layout node may already have been released by\n // the host-config clearContainer / removeChild paths during commit.\n }\n root.layoutNode = null\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as renderStringSync, t as renderString } from "./render-string-
|
|
1
|
+
import { n as renderStringSync, t as renderString } from "./render-string-DkQacASz.mjs";
|
|
2
2
|
export { renderString, renderStringSync };
|