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.
Files changed (218) hide show
  1. package/README.md +9 -4
  2. package/dist/Text-Lq0dmj8-.mjs +239 -0
  3. package/dist/Text-Lq0dmj8-.mjs.map +1 -0
  4. package/dist/UPNG-Bo33r8rA.mjs +3 -0
  5. package/dist/UPNG-DosRPdF4.mjs +5075 -0
  6. package/dist/UPNG-DosRPdF4.mjs.map +1 -0
  7. package/dist/__vite-browser-external-2447137e-D_JM6skp.mjs +6 -0
  8. package/dist/__vite-browser-external-2447137e-D_JM6skp.mjs.map +1 -0
  9. package/dist/{animation-Cn64yepo.mjs → animation-ZMN2_XKv.mjs} +2 -2
  10. package/dist/animation-ZMN2_XKv.mjs.map +1 -0
  11. package/dist/{ansi-Cc33mW54.d.mts → ansi-2Xn0yatP.d.mts} +1 -1
  12. package/dist/{ansi-Cc33mW54.d.mts.map → ansi-2Xn0yatP.d.mts.map} +1 -1
  13. package/dist/{ansi-CLOitHKx.mjs → ansi-D1KQMAbf.mjs} +1 -1
  14. package/dist/{ansi-CLOitHKx.mjs.map → ansi-D1KQMAbf.mjs.map} +1 -1
  15. package/dist/ansi-yC4RyBNY.mjs +22441 -0
  16. package/dist/ansi-yC4RyBNY.mjs.map +1 -0
  17. package/dist/apng-CR08rIaH.mjs +58 -0
  18. package/dist/apng-CR08rIaH.mjs.map +1 -0
  19. package/dist/apng-DaHfVaVI.mjs +3 -0
  20. package/dist/assets/resvgjs.darwin-arm64-BtufyGW1.node +0 -0
  21. package/dist/assets/skia.darwin-arm64-DQs5sT6N.node +0 -0
  22. package/dist/backend-B-WYLUib.mjs +13396 -0
  23. package/dist/backend-B-WYLUib.mjs.map +1 -0
  24. package/dist/backends-CUtan80W.mjs +3 -0
  25. package/dist/backends-DIVYzKqd.mjs +1083 -0
  26. package/dist/backends-DIVYzKqd.mjs.map +1 -0
  27. package/dist/bound-term-0sPrrzH1.d.mts +4640 -0
  28. package/dist/bound-term-0sPrrzH1.d.mts.map +1 -0
  29. package/dist/canvas-1v7dPT-_.mjs +3 -0
  30. package/dist/canvas-CSuPOMNt.mjs +1442 -0
  31. package/dist/canvas-CSuPOMNt.mjs.map +1 -0
  32. package/dist/{chunk-Vs_PY4HZ.mjs → chunk-BSw8zbkd.mjs} +1 -1
  33. package/dist/cli-dvo0r2fs.mjs +4 -0
  34. package/dist/compare-CQodSH4G.mjs +376 -0
  35. package/dist/compare-CQodSH4G.mjs.map +1 -0
  36. package/dist/compare-DHlcxEYA.mjs +3 -0
  37. package/dist/context-BU5LkkIy.mjs.map +1 -1
  38. package/dist/devtools-CJdt5H0X.mjs +2 -0
  39. package/dist/{devtools-DxkSLXDA.mjs → devtools-DcQjgyjL.mjs} +5 -4
  40. package/dist/{devtools-DxkSLXDA.mjs.map → devtools-DcQjgyjL.mjs.map} +1 -1
  41. package/dist/easing-BI-ASGMO.d.mts +24 -0
  42. package/dist/easing-BI-ASGMO.d.mts.map +1 -0
  43. package/dist/{eta-Bb3RH3wh.mjs → eta-CJlGH06n.mjs} +1 -1
  44. package/dist/{eta-Bb3RH3wh.mjs.map → eta-CJlGH06n.mjs.map} +1 -1
  45. package/dist/flexily-zero-adapter-C3Vj0fPt.mjs +306 -0
  46. package/dist/flexily-zero-adapter-C3Vj0fPt.mjs.map +1 -0
  47. package/dist/{flexily-zero-adapter-CMxXhdOL.mjs → flexily-zero-adapter-C4lW_Ov5.mjs} +1 -1
  48. package/dist/fonts-BFmhXDv7.mjs +88 -0
  49. package/dist/fonts-BFmhXDv7.mjs.map +1 -0
  50. package/dist/gif-C_AjaT9d.mjs +188 -0
  51. package/dist/gif-C_AjaT9d.mjs.map +1 -0
  52. package/dist/gif-DaC4XrxA.mjs +3 -0
  53. package/dist/gifenc-BOUT-KFB.mjs +730 -0
  54. package/dist/gifenc-BOUT-KFB.mjs.map +1 -0
  55. package/dist/image-C2Birh2x.mjs +1252 -0
  56. package/dist/image-C2Birh2x.mjs.map +1 -0
  57. package/dist/index-BUMxS65f.d.mts +453 -0
  58. package/dist/index-BUMxS65f.d.mts.map +1 -0
  59. package/dist/{index-D3saHouR.d.mts → index-CSQf13CI.d.mts} +1057 -1133
  60. package/dist/index-CSQf13CI.d.mts.map +1 -0
  61. package/dist/{index-BXslOebb.d.mts → index-Cl9KKjQ_.d.mts} +4919 -3921
  62. package/dist/index-Cl9KKjQ_.d.mts.map +1 -0
  63. package/dist/index-XbNrPhWl.d.mts +336 -0
  64. package/dist/index-XbNrPhWl.d.mts.map +1 -0
  65. package/dist/index.d.mts +8 -5
  66. package/dist/index.d.mts.map +1 -1
  67. package/dist/index.mjs +14 -12
  68. package/dist/index.mjs.map +1 -1
  69. package/dist/key-mapping-CS-YD_cD.mjs +132 -0
  70. package/dist/key-mapping-CS-YD_cD.mjs.map +1 -0
  71. package/dist/key-mapping-Yn-Jgrij.mjs +3 -0
  72. package/dist/{layout-engine-B6Cdz1yZ.mjs → layout-engine-C07LEXWT.mjs} +1 -1
  73. package/dist/layout-engine-C2px0RJE.mjs +67 -0
  74. package/dist/layout-engine-C2px0RJE.mjs.map +1 -0
  75. package/dist/layout-signals-Cnw6xk8Q.mjs +988 -0
  76. package/dist/layout-signals-Cnw6xk8Q.mjs.map +1 -0
  77. package/dist/mouse-events-Dki3ISIp.mjs +1044 -0
  78. package/dist/mouse-events-Dki3ISIp.mjs.map +1 -0
  79. package/dist/{multi-progress-Bq9Oi_WI.mjs → multi-progress-CIRjrzma.mjs} +3 -3
  80. package/dist/{multi-progress-Bq9Oi_WI.mjs.map → multi-progress-CIRjrzma.mjs.map} +1 -1
  81. package/dist/{multi-progress-DAQC7eap.d.mts → multi-progress-DHZ2xUT2.d.mts} +2 -2
  82. package/dist/{multi-progress-DAQC7eap.d.mts.map → multi-progress-DHZ2xUT2.d.mts.map} +1 -1
  83. package/dist/{node-BeWlnCPY.mjs → node-CjM5Rt-M.mjs} +4 -4
  84. package/dist/node-CjM5Rt-M.mjs.map +1 -0
  85. package/dist/playwright-D5YiZcNS.mjs +76397 -0
  86. package/dist/playwright-D5YiZcNS.mjs.map +1 -0
  87. package/dist/png-codec-Dp84742B.mjs +36 -0
  88. package/dist/png-codec-Dp84742B.mjs.map +1 -0
  89. package/dist/png-codec-QwOtJ8Zs.mjs +3 -0
  90. package/dist/progress-DB_Xo071.mjs +675 -0
  91. package/dist/progress-DB_Xo071.mjs.map +1 -0
  92. package/dist/{progress-bar-CXE5Qfkd.mjs → progress-bar-oJwq22CR.mjs} +4 -4
  93. package/dist/{progress-bar-CXE5Qfkd.mjs.map → progress-bar-oJwq22CR.mjs.map} +1 -1
  94. package/dist/rasterizer-BRXrDdWx.mjs +3 -0
  95. package/dist/rasterizer-CpEhJvdR.mjs +296 -0
  96. package/dist/rasterizer-CpEhJvdR.mjs.map +1 -0
  97. package/dist/reconciler-DldIJB93.mjs +2083 -0
  98. package/dist/reconciler-DldIJB93.mjs.map +1 -0
  99. package/dist/{render-string-CDCeYkS3.mjs → render-string-BcoCpjCB.mjs} +1 -1
  100. package/dist/{render-string-Darrg7ku.mjs → render-string-DkQacASz.mjs} +2707 -549
  101. package/dist/render-string-DkQacASz.mjs.map +1 -0
  102. package/dist/resvg-js-DkOndZI3.mjs +203 -0
  103. package/dist/resvg-js-DkOndZI3.mjs.map +1 -0
  104. package/dist/runtime.d.mts +3 -2
  105. package/dist/runtime.mjs +3 -3
  106. package/dist/schemes-JjNp4aSl.mjs +2611 -0
  107. package/dist/schemes-JjNp4aSl.mjs.map +1 -0
  108. package/dist/{spinner-CGo34vyR.d.mts → spinner-CZINHpkV.d.mts} +2 -2
  109. package/dist/{spinner-CGo34vyR.d.mts.map → spinner-CZINHpkV.d.mts.map} +1 -1
  110. package/dist/{spinner-CeOmcuw_.mjs → spinner-D9lrHr8s.mjs} +7 -7
  111. package/dist/spinner-D9lrHr8s.mjs.map +1 -0
  112. package/dist/src-5w9QR6_8.mjs +1071 -0
  113. package/dist/src-5w9QR6_8.mjs.map +1 -0
  114. package/dist/src-BNTToU7l.mjs +4387 -0
  115. package/dist/src-BNTToU7l.mjs.map +1 -0
  116. package/dist/{src-CF-6UN01.mjs → src-BR4xNwdG.mjs} +10436 -2622
  117. package/dist/src-BR4xNwdG.mjs.map +1 -0
  118. package/dist/{types-Bk2yw9Qj.mjs → src-DKp-_OFG.mjs} +34 -94
  119. package/dist/src-DKp-_OFG.mjs.map +1 -0
  120. package/dist/src-bt8wSrfJ.mjs +258 -0
  121. package/dist/src-bt8wSrfJ.mjs.map +1 -0
  122. package/dist/src-e33Y6kNJ.mjs +3 -0
  123. package/dist/src-iDwu25UD.mjs +1814 -0
  124. package/dist/src-iDwu25UD.mjs.map +1 -0
  125. package/dist/steps-Bp2uNqnn.d.mts +202 -0
  126. package/dist/steps-Bp2uNqnn.d.mts.map +1 -0
  127. package/dist/svg-15lZZzxq.mjs +486 -0
  128. package/dist/svg-15lZZzxq.mjs.map +1 -0
  129. package/dist/svg-Cz0UXcDj.mjs +255 -0
  130. package/dist/svg-Cz0UXcDj.mjs.map +1 -0
  131. package/dist/svg-DY72a4HK.mjs +3 -0
  132. package/dist/svg-g1D6ErwR.d.mts +82 -0
  133. package/dist/svg-g1D6ErwR.d.mts.map +1 -0
  134. package/dist/term.d.mts +3 -0
  135. package/dist/term.mjs +9 -0
  136. package/dist/term.mjs.map +1 -0
  137. package/dist/theme.d.mts +95 -2
  138. package/dist/theme.d.mts.map +1 -0
  139. package/dist/theme.mjs +9 -3
  140. package/dist/theme.mjs.map +1 -0
  141. package/dist/{types-BH_v3iMT.d.mts → types-kt_fKR37.d.mts} +2 -15
  142. package/dist/types-kt_fKR37.d.mts.map +1 -0
  143. package/dist/ui/animation.d.mts +2 -1
  144. package/dist/ui/animation.mjs +1 -1
  145. package/dist/ui/ansi.d.mts +1 -1
  146. package/dist/ui/ansi.mjs +1 -1
  147. package/dist/ui/cli.d.mts +3 -3
  148. package/dist/ui/cli.mjs +5 -5
  149. package/dist/ui/display.d.mts +1 -1
  150. package/dist/ui/image.d.mts +2 -2
  151. package/dist/ui/image.mjs +2 -2
  152. package/dist/ui/input.d.mts +1 -1
  153. package/dist/ui/input.mjs +4 -2
  154. package/dist/ui/input.mjs.map +1 -1
  155. package/dist/ui/progress.d.mts +5 -249
  156. package/dist/ui/progress.mjs +5 -858
  157. package/dist/ui/react.d.mts +1 -1
  158. package/dist/ui/react.mjs +2 -2
  159. package/dist/ui/recording-chrome-react.d.mts +21 -0
  160. package/dist/ui/recording-chrome-react.d.mts.map +1 -0
  161. package/dist/ui/recording-chrome-react.mjs +105 -0
  162. package/dist/ui/recording-chrome-react.mjs.map +1 -0
  163. package/dist/ui/recording-chrome.d.mts +2 -0
  164. package/dist/ui/recording-chrome.mjs +2 -0
  165. package/dist/ui/utils.mjs +1 -1
  166. package/dist/ui/wrappers.d.mts +3 -3
  167. package/dist/ui/wrappers.mjs +2 -2
  168. package/dist/ui.d.mts +7 -6
  169. package/dist/ui.mjs +8 -7
  170. package/dist/{useLatest-Bg2x4bfP.d.mts → useLatest-DRDDVwjh.d.mts} +5 -25
  171. package/dist/useLatest-DRDDVwjh.d.mts.map +1 -0
  172. package/dist/{with-text-input-CRfoiFFG.d.mts → with-text-input-YeohVLeo.d.mts} +4 -55
  173. package/dist/with-text-input-YeohVLeo.d.mts.map +1 -0
  174. package/dist/wrapper-C70ATkVv.mjs +3527 -0
  175. package/dist/wrapper-C70ATkVv.mjs.map +1 -0
  176. package/dist/{wrappers-UTADQkSY.mjs → wrappers-BCUYITrY.mjs} +5 -157
  177. package/dist/wrappers-BCUYITrY.mjs.map +1 -0
  178. package/dist/{yoga-adapter-8oRGRw8V.mjs → yoga-adapter-BnZX1PAY.mjs} +28 -2
  179. package/dist/yoga-adapter-BnZX1PAY.mjs.map +1 -0
  180. package/dist/yoga-adapter-DxgsQ_gg.mjs +2 -0
  181. package/dist/zipBundle-3nqeDRtm.mjs +3 -0
  182. package/dist/zipBundle-VNAYFmqJ.mjs +2003 -0
  183. package/dist/zipBundle-VNAYFmqJ.mjs.map +1 -0
  184. package/package.json +20 -9
  185. package/dist/animation-Cn64yepo.mjs.map +0 -1
  186. package/dist/cli-BKp0YtBD.mjs +0 -4
  187. package/dist/devtools-9QY4teqI.mjs +0 -2
  188. package/dist/flexily-zero-adapter-BlQa46nr.mjs +0 -3385
  189. package/dist/flexily-zero-adapter-BlQa46nr.mjs.map +0 -1
  190. package/dist/image-CTII5QWI.mjs +0 -477
  191. package/dist/image-CTII5QWI.mjs.map +0 -1
  192. package/dist/index-BXslOebb.d.mts.map +0 -1
  193. package/dist/index-BnA7mNpo.d.mts +0 -175
  194. package/dist/index-BnA7mNpo.d.mts.map +0 -1
  195. package/dist/index-D3saHouR.d.mts.map +0 -1
  196. package/dist/layout-engine-ClUgv6jB.mjs +0 -50
  197. package/dist/layout-engine-ClUgv6jB.mjs.map +0 -1
  198. package/dist/node-BeWlnCPY.mjs.map +0 -1
  199. package/dist/reconciler-Cwgm8hRR.mjs +0 -8459
  200. package/dist/reconciler-Cwgm8hRR.mjs.map +0 -1
  201. package/dist/render-string-Darrg7ku.mjs.map +0 -1
  202. package/dist/spinner-CeOmcuw_.mjs.map +0 -1
  203. package/dist/src-B5GjfG7g.mjs +0 -4305
  204. package/dist/src-B5GjfG7g.mjs.map +0 -1
  205. package/dist/src-CChwjk0Z.mjs +0 -738
  206. package/dist/src-CChwjk0Z.mjs.map +0 -1
  207. package/dist/src-CF-6UN01.mjs.map +0 -1
  208. package/dist/src-NCKb8kE5.mjs +0 -2660
  209. package/dist/src-NCKb8kE5.mjs.map +0 -1
  210. package/dist/types-BH_v3iMT.d.mts.map +0 -1
  211. package/dist/types-Bk2yw9Qj.mjs.map +0 -1
  212. package/dist/ui/progress.d.mts.map +0 -1
  213. package/dist/ui/progress.mjs.map +0 -1
  214. package/dist/useLatest-Bg2x4bfP.d.mts.map +0 -1
  215. package/dist/with-text-input-CRfoiFFG.d.mts.map +0 -1
  216. package/dist/wrappers-UTADQkSY.mjs.map +0 -1
  217. package/dist/yoga-adapter-8oRGRw8V.mjs.map +0 -1
  218. package/dist/yoga-adapter-D_CcxSt5.mjs +0 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flexily-zero-adapter-C3Vj0fPt.mjs","names":["FlexilyNode"],"sources":["../packages/ag-term/src/adapters/flexily-zero-adapter.ts"],"sourcesContent":["/**\n * Flexily Layout Engine Adapter\n *\n * Wraps Flexily to implement the LayoutEngine interface.\n * Uses the default zero-allocation algorithm from flexily.\n */\n\nimport {\n ALIGN_AUTO,\n ALIGN_BASELINE,\n ALIGN_CENTER,\n ALIGN_FLEX_END,\n ALIGN_FLEX_START,\n ALIGN_SPACE_AROUND,\n ALIGN_SPACE_BETWEEN,\n ALIGN_SPACE_EVENLY,\n ALIGN_STRETCH,\n DIRECTION_LTR,\n DISPLAY_FLEX,\n DISPLAY_NONE,\n EDGE_ALL,\n EDGE_BOTTOM,\n EDGE_HORIZONTAL,\n EDGE_LEFT,\n EDGE_RIGHT,\n EDGE_TOP,\n EDGE_VERTICAL,\n // Constants\n FLEX_DIRECTION_COLUMN,\n FLEX_DIRECTION_COLUMN_REVERSE,\n FLEX_DIRECTION_ROW,\n FLEX_DIRECTION_ROW_REVERSE,\n Node as FlexilyNode,\n GUTTER_ALL,\n GUTTER_COLUMN,\n GUTTER_ROW,\n JUSTIFY_CENTER,\n JUSTIFY_FLEX_END,\n JUSTIFY_FLEX_START,\n JUSTIFY_SPACE_AROUND,\n JUSTIFY_SPACE_BETWEEN,\n JUSTIFY_SPACE_EVENLY,\n MEASURE_MODE_AT_MOST,\n MEASURE_MODE_EXACTLY,\n MEASURE_MODE_MIN_CONTENT,\n MEASURE_MODE_UNDEFINED,\n OVERFLOW_HIDDEN,\n OVERFLOW_SCROLL,\n OVERFLOW_VISIBLE,\n POSITION_TYPE_ABSOLUTE,\n POSITION_TYPE_RELATIVE,\n POSITION_TYPE_STATIC,\n WRAP_NO_WRAP,\n WRAP_WRAP,\n WRAP_WRAP_REVERSE,\n} from \"flexily\"\n\n// Flexily UNIT_CQI / UNIT_CQMIN constants. Hardcoded at this seam rather than\n// imported from \"flexily\" because the published flexily@0.7.1 does not export\n// them yet — A0.2's export-bump (vendor/flexily commit fb61a79) hasn't shipped\n// to npm. Constants are stable per Phase 1 of @km/silvery/responsive-layout-\n// architecture-reframe; same shape as the CONTAINER_TYPE_INLINE_SIZE=1 hardcode\n// in applyBoxProps. When flexily 0.7.2+ ships with the new exports, this can be\n// replaced with a named import.\nconst UNIT_CQI = 6\nconst UNIT_CQMIN = 7\n\nimport type { FitWidthLane, LayoutNode, MeasureFunc, MeasureMode } from \"@silvery/ag/layout-types\"\nimport type {\n AlignValue,\n DirectionValue,\n DisplayValue,\n EdgeValue,\n EngineCapabilities,\n FlexDirectionValue,\n GutterValue,\n JustifyValue,\n LayoutConstants,\n LayoutEngine,\n MeasureModeValue,\n OverflowValue,\n PositionTypeValue,\n WrapValue,\n} from \"../layout-engine\"\n\n/**\n * Capabilities advertised by the flexily-zero adapter.\n *\n * All six A0 primitives shipped: four at A0.1 (engine-native CQ resolution at\n * vendor/flexily `46141e9`), one at A0.2 (fitWidth single-pass lane snap at\n * vendor/flexily `4b44bf9`), one at A0.3 (CSS math functions at vendor/flexily\n * `0aebb95`).\n *\n * - `containerQueries` → A0.1 ✓ — Pass 1 freezeQuerySize + Pass 2 ancestor walk\n * - `containSize` → A0.1 ✓ — Phase 9 inline-axis shrink-wrap gate\n * - `containerQueryUnits` → A0.1 ✓ — UNIT_CQI / UNIT_CQMIN parsed and resolved\n * - `childStyleMutation` → A0.1 ✓ — setContainerQueryStyle hook (A0.0 substrate)\n * - `fitWidth` → A0.2 ✓ — Phase 3 lane-select prefix; single-pass lane snap\n * - `styleMathFunctions` → A0.3 ✓ — UNIT_CALC + MathExpr; late-bound per\n * vendor/flexily/docs/two-phase-layout.md\n */\nconst FLEXILY_CAPABILITIES: EngineCapabilities = Object.freeze({\n containerQueries: true, // ✓ A0.1\n containSize: true, // ✓ A0.1\n containerQueryUnits: true, // ✓ A0.1\n fitWidth: true, // ✓ A0.2\n styleMathFunctions: true, // ✓ A0.3\n childStyleMutation: true, // ✓ A0.1\n})\n\n// ============================================================================\n// Flexily Zero Node Adapter\n// ============================================================================\n\n/**\n * Wraps a Flexily zero-alloc node to implement LayoutNode interface.\n * Since Flexily already has a Yoga-compatible API, this is mostly delegation.\n */\nclass FlexilyZeroNodeAdapter implements LayoutNode {\n private node: FlexilyNode\n\n constructor(node: FlexilyNode) {\n this.node = node\n }\n\n /** Get the underlying Flexily node (for tree operations) */\n getFlexilyNode(): FlexilyNode {\n return this.node\n }\n\n // Tree operations\n insertChild(child: LayoutNode, index: number): void {\n const flexilyChild = (child as FlexilyZeroNodeAdapter).getFlexilyNode()\n this.node.insertChild(flexilyChild, index)\n }\n\n removeChild(child: LayoutNode): void {\n const flexilyChild = (child as FlexilyZeroNodeAdapter).getFlexilyNode()\n this.node.removeChild(flexilyChild)\n }\n\n free(): void {\n this.node.free()\n }\n\n // Measure function\n setMeasureFunc(measureFunc: MeasureFunc): void {\n this.node.setMeasureFunc((width, widthMode, height, heightMode) => {\n const widthModeStr = this.measureModeToString(widthMode)\n const heightModeStr = this.measureModeToString(heightMode)\n return measureFunc(width, widthModeStr, height, heightModeStr)\n })\n }\n\n // Dirty tracking - forces layout recalculation\n markDirty(): void {\n this.node.markDirty()\n }\n isDirty(): boolean {\n return this.node.isDirty()\n }\n\n private measureModeToString(mode: number): MeasureMode {\n if (mode === MEASURE_MODE_EXACTLY) return \"exactly\"\n if (mode === MEASURE_MODE_AT_MOST) return \"at-most\"\n if (mode === MEASURE_MODE_MIN_CONTENT) return \"min-content\"\n return \"undefined\"\n }\n\n // Dimension setters\n setWidth(value: number): void {\n this.node.setWidth(value)\n }\n setWidthPercent(value: number): void {\n this.node.setWidthPercent(value)\n }\n setWidthAuto(): void {\n this.node.setWidthAuto()\n }\n setWidthFitContent(): void {\n this.node.setWidthFitContent()\n }\n setWidthSnugContent(): void {\n this.node.setWidthSnugContent()\n }\n setHeight(value: number): void {\n this.node.setHeight(value)\n }\n setHeightPercent(value: number): void {\n this.node.setHeightPercent(value)\n }\n setHeightAuto(): void {\n this.node.setHeightAuto()\n }\n setMinWidth(value: number): void {\n this.node.setMinWidth(value)\n }\n setMinWidthPercent(value: number): void {\n this.node.setMinWidthPercent(value)\n }\n setMinHeight(value: number): void {\n this.node.setMinHeight(value)\n }\n setMinHeightPercent(value: number): void {\n this.node.setMinHeightPercent(value)\n }\n setMaxWidth(value: number): void {\n this.node.setMaxWidth(value)\n }\n setMaxWidthPercent(value: number): void {\n this.node.setMaxWidthPercent(value)\n }\n setMaxHeight(value: number): void {\n this.node.setMaxHeight(value)\n }\n setMaxHeightPercent(value: number): void {\n this.node.setMaxHeightPercent(value)\n }\n\n // Flex properties\n setFlexGrow(value: number): void {\n this.node.setFlexGrow(value)\n }\n setFlexShrink(value: number): void {\n this.node.setFlexShrink(value)\n }\n setFlexBasis(value: number): void {\n this.node.setFlexBasis(value)\n }\n setFlexBasisPercent(value: number): void {\n this.node.setFlexBasisPercent(value)\n }\n setFlexBasisAuto(): void {\n this.node.setFlexBasisAuto()\n }\n setFlexDirection(direction: number): void {\n this.node.setFlexDirection(direction)\n }\n setFlexWrap(wrap: number): void {\n this.node.setFlexWrap(wrap)\n }\n\n // Alignment\n setAlignItems(align: number): void {\n this.node.setAlignItems(align)\n }\n setAlignSelf(align: number): void {\n this.node.setAlignSelf(align)\n }\n setAlignContent(align: number): void {\n this.node.setAlignContent(align)\n }\n setJustifyContent(justify: number): void {\n this.node.setJustifyContent(justify)\n }\n\n // Spacing\n setPadding(edge: number, value: number): void {\n this.node.setPadding(edge, value)\n }\n setMargin(edge: number, value: number): void {\n this.node.setMargin(edge, value)\n }\n setBorder(edge: number, value: number): void {\n this.node.setBorder(edge, value)\n }\n setGap(gutter: number, value: number): void {\n this.node.setGap(gutter, value)\n }\n\n // Display & Position\n setDisplay(display: number): void {\n this.node.setDisplay(display)\n }\n setPositionType(positionType: number): void {\n this.node.setPositionType(positionType)\n }\n setPosition(edge: number, value: number): void {\n this.node.setPosition(edge, value)\n }\n setPositionPercent(edge: number, value: number): void {\n this.node.setPositionPercent(edge, value)\n }\n setOverflow(overflow: number): void {\n this.node.setOverflow(overflow)\n }\n\n // Container queries (A0.1) — flexily supports both; advertised via FLEXILY_CAPABILITIES.\n setContainerType(containerType: number): void {\n this.node.setContainerType(containerType)\n }\n setContainSize(value: boolean): void {\n this.node.setContainSize(value)\n }\n\n // Fit-width (A0.2) — translate FitWidthLane to flexily's { value, unit: UNIT_* }\n // shape. Lane entries can be plain numbers (treated as UNIT_POINT) or objects\n // with a \"cqi\"/\"cqmin\" unit string.\n setFitWidth(lanes: readonly FitWidthLane[] | undefined): void {\n if (lanes === undefined || lanes.length === 0) {\n this.node.setFitWidth(undefined)\n return\n }\n this.node.setFitWidth(\n lanes.map((entry) => {\n if (typeof entry === \"number\") return entry\n const unit = entry.unit === \"cqi\" ? UNIT_CQI : UNIT_CQMIN\n return { value: entry.value, unit }\n }),\n )\n }\n\n // Aspect Ratio\n setAspectRatio(value: number): void {\n this.node.setAspectRatio(value)\n }\n\n // Layout calculation\n calculateLayout(width: number, height: number, direction?: number): void {\n this.node.calculateLayout(width, height, direction ?? DIRECTION_LTR)\n }\n\n // Layout results\n getComputedLeft(): number {\n return this.node.getComputedLeft()\n }\n getComputedTop(): number {\n return this.node.getComputedTop()\n }\n getComputedWidth(): number {\n return this.node.getComputedWidth()\n }\n getComputedHeight(): number {\n return this.node.getComputedHeight()\n }\n}\n\n// ============================================================================\n// Flexily Zero Layout Engine\n// ============================================================================\n\n/**\n * Layout engine implementation using Flexily zero-allocation variant.\n * Optimized for high-frequency layout with reduced GC pressure.\n */\nexport class FlexilyZeroLayoutEngine implements LayoutEngine {\n private _constants: LayoutConstants = {\n // Flex Direction (cast from Flexily's plain numbers to branded types)\n FLEX_DIRECTION_COLUMN: FLEX_DIRECTION_COLUMN as FlexDirectionValue,\n FLEX_DIRECTION_COLUMN_REVERSE: FLEX_DIRECTION_COLUMN_REVERSE as FlexDirectionValue,\n FLEX_DIRECTION_ROW: FLEX_DIRECTION_ROW as FlexDirectionValue,\n FLEX_DIRECTION_ROW_REVERSE: FLEX_DIRECTION_ROW_REVERSE as FlexDirectionValue,\n\n // Wrap\n WRAP_NO_WRAP: WRAP_NO_WRAP as WrapValue,\n WRAP_WRAP: WRAP_WRAP as WrapValue,\n WRAP_WRAP_REVERSE: WRAP_WRAP_REVERSE as WrapValue,\n\n // Align\n ALIGN_AUTO: ALIGN_AUTO as AlignValue,\n ALIGN_FLEX_START: ALIGN_FLEX_START as AlignValue,\n ALIGN_CENTER: ALIGN_CENTER as AlignValue,\n ALIGN_FLEX_END: ALIGN_FLEX_END as AlignValue,\n ALIGN_STRETCH: ALIGN_STRETCH as AlignValue,\n ALIGN_BASELINE: ALIGN_BASELINE as AlignValue,\n ALIGN_SPACE_BETWEEN: ALIGN_SPACE_BETWEEN as AlignValue,\n ALIGN_SPACE_AROUND: ALIGN_SPACE_AROUND as AlignValue,\n ALIGN_SPACE_EVENLY: ALIGN_SPACE_EVENLY as AlignValue,\n\n // Justify\n JUSTIFY_FLEX_START: JUSTIFY_FLEX_START as JustifyValue,\n JUSTIFY_CENTER: JUSTIFY_CENTER as JustifyValue,\n JUSTIFY_FLEX_END: JUSTIFY_FLEX_END as JustifyValue,\n JUSTIFY_SPACE_BETWEEN: JUSTIFY_SPACE_BETWEEN as JustifyValue,\n JUSTIFY_SPACE_AROUND: JUSTIFY_SPACE_AROUND as JustifyValue,\n JUSTIFY_SPACE_EVENLY: JUSTIFY_SPACE_EVENLY as JustifyValue,\n\n // Edge\n EDGE_LEFT: EDGE_LEFT as EdgeValue,\n EDGE_TOP: EDGE_TOP as EdgeValue,\n EDGE_RIGHT: EDGE_RIGHT as EdgeValue,\n EDGE_BOTTOM: EDGE_BOTTOM as EdgeValue,\n EDGE_HORIZONTAL: EDGE_HORIZONTAL as EdgeValue,\n EDGE_VERTICAL: EDGE_VERTICAL as EdgeValue,\n EDGE_ALL: EDGE_ALL as EdgeValue,\n\n // Gutter\n GUTTER_COLUMN: GUTTER_COLUMN as GutterValue,\n GUTTER_ROW: GUTTER_ROW as GutterValue,\n GUTTER_ALL: GUTTER_ALL as GutterValue,\n\n // Display\n DISPLAY_FLEX: DISPLAY_FLEX as DisplayValue,\n DISPLAY_NONE: DISPLAY_NONE as DisplayValue,\n\n // Position Type\n POSITION_TYPE_STATIC: POSITION_TYPE_STATIC as PositionTypeValue,\n POSITION_TYPE_RELATIVE: POSITION_TYPE_RELATIVE as PositionTypeValue,\n POSITION_TYPE_ABSOLUTE: POSITION_TYPE_ABSOLUTE as PositionTypeValue,\n\n // Overflow\n OVERFLOW_VISIBLE: OVERFLOW_VISIBLE as OverflowValue,\n OVERFLOW_HIDDEN: OVERFLOW_HIDDEN as OverflowValue,\n OVERFLOW_SCROLL: OVERFLOW_SCROLL as OverflowValue,\n\n // Direction\n DIRECTION_LTR: DIRECTION_LTR as DirectionValue,\n\n // Measure Mode\n MEASURE_MODE_UNDEFINED: MEASURE_MODE_UNDEFINED as MeasureModeValue,\n MEASURE_MODE_EXACTLY: MEASURE_MODE_EXACTLY as MeasureModeValue,\n MEASURE_MODE_AT_MOST: MEASURE_MODE_AT_MOST as MeasureModeValue,\n }\n\n createNode(): LayoutNode {\n return new FlexilyZeroNodeAdapter(FlexilyNode.create({ defaults: \"css\" }))\n }\n\n get constants(): LayoutConstants {\n return this._constants\n }\n\n get name(): string {\n return \"flexily-zero\"\n }\n\n get capabilities(): EngineCapabilities {\n return FLEXILY_CAPABILITIES\n }\n}\n\n/**\n * Internal-only Yoga-preset variant of FlexilyZeroLayoutEngine.\n *\n * Production silvery uses CSS-correct defaults via the standard\n * {@link FlexilyZeroLayoutEngine}. The Ink-compat layer (which mimics\n * Ink/Yoga semantics — `flexShrink:0`, `alignContent:flex-start`, no auto\n * min-size on flex items) needs Yoga-preset Nodes. This subclass is the\n * only sanctioned way to opt into Yoga semantics from inside silvery; do\n * not export from the public barrel and do not use in app code.\n */\nclass FlexilyZeroLayoutEngineYoga extends FlexilyZeroLayoutEngine {\n override createNode(): LayoutNode {\n return new FlexilyZeroNodeAdapter(FlexilyNode.create({ defaults: \"yoga\" }))\n }\n}\n\n// ============================================================================\n// Initialization Helpers\n// ============================================================================\n\n/**\n * Create a Flexily zero-allocation layout engine with CSS-correct defaults.\n *\n * silvery's standard layout engine. Sets `flexShrink: 1`,\n * `alignContent: stretch`, and CSS §4.5 flex-item auto min-size on every\n * Node, matching browser flexbox semantics. Unlike Yoga, Flexily doesn't\n * require async initialization.\n *\n * Yoga-flavored semantics are deliberately not exposed here — consumers\n * needing Yoga compat should use `createFlexily({ defaults: \"yoga\" })` from\n * flexily directly. The Ink-compat layer is the single internal user that\n * stays on Yoga; it imports the `@internal` {@link createFlexilyZeroEngineForInkCompat}.\n */\nexport function createFlexilyZeroEngine(): FlexilyZeroLayoutEngine {\n return new FlexilyZeroLayoutEngine()\n}\n\n/**\n * Yoga-preset variant for Ink-compat tests only.\n *\n * Do not use in production silvery code. The Ink-compat layer (`packages/ink`)\n * mimics Ink semantics, which match Yoga (`flexShrink:0`, `alignContent:flex-start`,\n * no auto min-size). When silvery's auto-generated Ink-compat tests render\n * through silvery's layout pipeline, they need Yoga-preset Nodes — that's\n * the one and only purpose of this factory.\n *\n * @internal Visible to silvery tests/compat/ink/helpers and packages/ink internals.\n */\nexport function createFlexilyZeroEngineForInkCompat(): FlexilyZeroLayoutEngine {\n return new FlexilyZeroLayoutEngineYoga()\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import { n as createFlexilyZeroEngine } from "./flexily-zero-adapter-BlQa46nr.mjs";
1
+ import { n as createFlexilyZeroEngine } from "./flexily-zero-adapter-C3Vj0fPt.mjs";
2
2
  export { createFlexilyZeroEngine };
@@ -0,0 +1,88 @@
1
+ import { n as __esmMin } from "./chunk-BSw8zbkd.mjs";
2
+ import { existsSync } from "node:fs";
3
+ import { dirname, join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ //#region ../termless/src/render/fonts.ts
6
+ /**
7
+ * Bundled fallback fonts — the canonical termless font assets.
8
+ *
9
+ * Four OFL-licensed faces ship inside `@termless/core` under `assets/fonts`:
10
+ *
11
+ * - JetBrains Mono — primary monospace face (broad Latin + box-drawing)
12
+ * - Noto Sans Symbols 2 — terminal symbol glyphs JetBrains Mono lacks
13
+ * - Symbols Nerd Font — Nerd Font private-use icons (powerline, devicons,
14
+ * the U+F0xx/U+E0xx glyphs TUIs like km use)
15
+ * - Noto Emoji (mono) — emoji code points (📁 📋 📄, status emoji)
16
+ *
17
+ * Two render paths consume these:
18
+ *
19
+ * - The `@resvg/resvg-js` SVG→raster path (`./view/gif.ts`, `./view/apng.ts`,
20
+ * `record --screenshot *.png`) — passes the font files to resvg's
21
+ * `font.fontFiles` so emoji/symbol code points resolve instead of tofu.
22
+ * - `@termless/ghostty`'s `@napi-rs/canvas` renderer — registers them
23
+ * process-wide via `GlobalFonts.registerFromPath`.
24
+ *
25
+ * Both packages resolve the directory through this module so there is a
26
+ * single bundled copy, owned by `@termless/core` (the foundational package).
27
+ */
28
+ /**
29
+ * Resolve the bundled `assets/fonts` directory in both layouts:
30
+ * - dev: `<pkg>/src/render/fonts.ts` → `<pkg>/assets/fonts`
31
+ * - published: `<pkg>/dist/index.mjs` → `<pkg>/assets/fonts`
32
+ *
33
+ * In dev the file sits two levels deep (`src/render/`), in a published build
34
+ * one level deep (`dist/`). We probe upward for the first ancestor that has
35
+ * an `assets/fonts` child so both layouts resolve without a build-time guess.
36
+ */
37
+ function bundledFontsDir() {
38
+ let here = dirname(fileURLToPath(import.meta.url));
39
+ for (let i = 0; i < 4; i++) {
40
+ const candidate = join(here, "assets", "fonts");
41
+ if (existsSync(candidate)) return candidate;
42
+ here = dirname(here);
43
+ }
44
+ return join(dirname(fileURLToPath(import.meta.url)), "..", "..", "assets", "fonts");
45
+ }
46
+ /**
47
+ * Absolute paths of the bundled font files that actually exist on disk.
48
+ * Missing files are skipped silently — a partial fallback chain still beats
49
+ * a hard failure, and the bundled-font test pins the happy path.
50
+ */
51
+ function bundledFontFiles() {
52
+ const dir = bundledFontsDir();
53
+ const files = [];
54
+ for (const { file } of BUNDLED_FONTS) {
55
+ const path = join(dir, file);
56
+ if (existsSync(path)) files.push(path);
57
+ }
58
+ return files;
59
+ }
60
+ var BUNDLED_PRIMARY_FAMILY, BUNDLED_SYMBOL_FAMILY, BUNDLED_NERD_FAMILY, BUNDLED_EMOJI_FAMILY, BUNDLED_FONTS;
61
+ var init_fonts = __esmMin((() => {
62
+ BUNDLED_PRIMARY_FAMILY = "TermlessMono";
63
+ BUNDLED_SYMBOL_FAMILY = "TermlessSymbols";
64
+ BUNDLED_NERD_FAMILY = "TermlessNerd";
65
+ BUNDLED_EMOJI_FAMILY = "TermlessEmoji";
66
+ BUNDLED_FONTS = [
67
+ {
68
+ file: "JetBrainsMono-Regular.ttf",
69
+ family: BUNDLED_PRIMARY_FAMILY
70
+ },
71
+ {
72
+ file: "NotoSansSymbols2-Regular.ttf",
73
+ family: BUNDLED_SYMBOL_FAMILY
74
+ },
75
+ {
76
+ file: "SymbolsNerdFontMono-Regular.ttf",
77
+ family: BUNDLED_NERD_FAMILY
78
+ },
79
+ {
80
+ file: "NotoEmoji-Regular.ttf",
81
+ family: BUNDLED_EMOJI_FAMILY
82
+ }
83
+ ];
84
+ }));
85
+ //#endregion
86
+ export { BUNDLED_SYMBOL_FAMILY as a, init_fonts as c, BUNDLED_PRIMARY_FAMILY as i, BUNDLED_FONTS as n, bundledFontFiles as o, BUNDLED_NERD_FAMILY as r, bundledFontsDir as s, BUNDLED_EMOJI_FAMILY as t };
87
+
88
+ //# sourceMappingURL=fonts-BFmhXDv7.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fonts-BFmhXDv7.mjs","names":[],"sources":["../../termless/src/render/fonts.ts"],"sourcesContent":["/**\n * Bundled fallback fonts — the canonical termless font assets.\n *\n * Four OFL-licensed faces ship inside `@termless/core` under `assets/fonts`:\n *\n * - JetBrains Mono — primary monospace face (broad Latin + box-drawing)\n * - Noto Sans Symbols 2 — terminal symbol glyphs JetBrains Mono lacks\n * - Symbols Nerd Font — Nerd Font private-use icons (powerline, devicons,\n * the U+F0xx/U+E0xx glyphs TUIs like km use)\n * - Noto Emoji (mono) — emoji code points (📁 📋 📄, status emoji)\n *\n * Two render paths consume these:\n *\n * - The `@resvg/resvg-js` SVG→raster path (`./view/gif.ts`, `./view/apng.ts`,\n * `record --screenshot *.png`) — passes the font files to resvg's\n * `font.fontFiles` so emoji/symbol code points resolve instead of tofu.\n * - `@termless/ghostty`'s `@napi-rs/canvas` renderer — registers them\n * process-wide via `GlobalFonts.registerFromPath`.\n *\n * Both packages resolve the directory through this module so there is a\n * single bundled copy, owned by `@termless/core` (the foundational package).\n */\n\nimport { dirname, join } from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\nimport { existsSync } from \"node:fs\"\n\n/** Family names the bundled fonts are registered under (CSS `font-family`). */\nexport const BUNDLED_PRIMARY_FAMILY = \"TermlessMono\"\nexport const BUNDLED_SYMBOL_FAMILY = \"TermlessSymbols\"\nexport const BUNDLED_NERD_FAMILY = \"TermlessNerd\"\nexport const BUNDLED_EMOJI_FAMILY = \"TermlessEmoji\"\n\n/** One bundled font: a file name (relative to the fonts dir) + its family. */\nexport interface BundledFont {\n file: string\n family: string\n}\n\n/**\n * The bundled faces, in fallback order. The primary face is first; the\n * symbol + emoji faces follow so per-glyph fallback resolves in that order.\n */\nexport const BUNDLED_FONTS: readonly BundledFont[] = [\n { file: \"JetBrainsMono-Regular.ttf\", family: BUNDLED_PRIMARY_FAMILY },\n { file: \"NotoSansSymbols2-Regular.ttf\", family: BUNDLED_SYMBOL_FAMILY },\n { file: \"SymbolsNerdFontMono-Regular.ttf\", family: BUNDLED_NERD_FAMILY },\n { file: \"NotoEmoji-Regular.ttf\", family: BUNDLED_EMOJI_FAMILY },\n]\n\n/**\n * Resolve the bundled `assets/fonts` directory in both layouts:\n * - dev: `<pkg>/src/render/fonts.ts` → `<pkg>/assets/fonts`\n * - published: `<pkg>/dist/index.mjs` → `<pkg>/assets/fonts`\n *\n * In dev the file sits two levels deep (`src/render/`), in a published build\n * one level deep (`dist/`). We probe upward for the first ancestor that has\n * an `assets/fonts` child so both layouts resolve without a build-time guess.\n */\nexport function bundledFontsDir(): string {\n let here = dirname(fileURLToPath(import.meta.url))\n for (let i = 0; i < 4; i++) {\n const candidate = join(here, \"assets\", \"fonts\")\n if (existsSync(candidate)) return candidate\n here = dirname(here)\n }\n // Fall back to the dev layout (`src/render/` → two levels up) so callers\n // get a deterministic path even when the assets are absent.\n return join(dirname(fileURLToPath(import.meta.url)), \"..\", \"..\", \"assets\", \"fonts\")\n}\n\n/**\n * Absolute paths of the bundled font files that actually exist on disk.\n * Missing files are skipped silently — a partial fallback chain still beats\n * a hard failure, and the bundled-font test pins the happy path.\n */\nexport function bundledFontFiles(): string[] {\n const dir = bundledFontsDir()\n const files: string[] = []\n for (const { file } of BUNDLED_FONTS) {\n const path = join(dir, file)\n if (existsSync(path)) files.push(path)\n }\n return files\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2DA,SAAgB,kBAA0B;CACxC,IAAI,OAAO,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;AAClD,MAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK;EAC1B,MAAM,YAAY,KAAK,MAAM,UAAU,QAAQ;AAC/C,MAAI,WAAW,UAAU,CAAE,QAAO;AAClC,SAAO,QAAQ,KAAK;;AAItB,QAAO,KAAK,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC,EAAE,MAAM,MAAM,UAAU,QAAQ;;;;;;;AAQrF,SAAgB,mBAA6B;CAC3C,MAAM,MAAM,iBAAiB;CAC7B,MAAM,QAAkB,EAAE;AAC1B,MAAK,MAAM,EAAE,UAAU,eAAe;EACpC,MAAM,OAAO,KAAK,KAAK,KAAK;AAC5B,MAAI,WAAW,KAAK,CAAE,OAAM,KAAK,KAAK;;AAExC,QAAO;;;;AAvDI,0BAAyB;AACzB,yBAAwB;AACxB,uBAAsB;AACtB,wBAAuB;AAYvB,iBAAwC;EACnD;GAAE,MAAM;GAA6B,QAAQ;GAAwB;EACrE;GAAE,MAAM;GAAgC,QAAQ;GAAuB;EACvE;GAAE,MAAM;GAAmC,QAAQ;GAAqB;EACxE;GAAE,MAAM;GAAyB,QAAQ;GAAsB;EAChE"}
@@ -0,0 +1,188 @@
1
+ import { n as __esmMin, o as __toESM } from "./chunk-BSw8zbkd.mjs";
2
+ import { n as selectRasterizer, t as init_rasterizer } from "./rasterizer-CpEhJvdR.mjs";
3
+ //#region ../termless/src/view/gif.ts
4
+ async function loadGifenc() {
5
+ if (gifencModule) return gifencModule;
6
+ try {
7
+ gifencModule = await import("./gifenc-BOUT-KFB.mjs").then((m) => /* @__PURE__ */ __toESM(m.default, 1));
8
+ return gifencModule;
9
+ } catch {
10
+ throw new Error("createGif() requires gifenc. Install it:\n bun add gifenc");
11
+ }
12
+ }
13
+ function svgHasChrome(svg) {
14
+ return /windowBar|windowTitle|<rect[^>]*rx="\d+"/.test(svg);
15
+ }
16
+ function svgContentOffset(svg, scale) {
17
+ const contentTransform = [...svg.matchAll(/<g[^>]*\btransform="translate\(\s*([\d.-]+)[,\s]+([\d.-]+)\s*\)"/g)].at(-1);
18
+ if (!contentTransform) return {
19
+ x: 0,
20
+ y: 0
21
+ };
22
+ return {
23
+ x: Math.round(Number(contentTransform[1]) * scale),
24
+ y: Math.round(Number(contentTransform[2]) * scale)
25
+ };
26
+ }
27
+ function compositeBitmap(base, overlay, offset) {
28
+ const pixels = new Uint8Array(base.pixels);
29
+ for (let y = 0; y < overlay.height; y++) {
30
+ const dy = y + offset.y;
31
+ if (dy < 0 || dy >= base.height) continue;
32
+ for (let x = 0; x < overlay.width; x++) {
33
+ const dx = x + offset.x;
34
+ if (dx < 0 || dx >= base.width) continue;
35
+ const si = (y * overlay.width + x) * 4;
36
+ const di = (dy * base.width + dx) * 4;
37
+ const srcAlpha = overlay.pixels[si + 3] / 255;
38
+ if (srcAlpha <= 0) continue;
39
+ if (srcAlpha >= 1) {
40
+ pixels[di] = overlay.pixels[si];
41
+ pixels[di + 1] = overlay.pixels[si + 1];
42
+ pixels[di + 2] = overlay.pixels[si + 2];
43
+ pixels[di + 3] = overlay.pixels[si + 3];
44
+ continue;
45
+ }
46
+ const dstAlpha = pixels[di + 3] / 255;
47
+ const outAlpha = srcAlpha + dstAlpha * (1 - srcAlpha);
48
+ if (outAlpha <= 0) continue;
49
+ const dstWeight = dstAlpha * (1 - srcAlpha) / outAlpha;
50
+ const srcWeight = srcAlpha / outAlpha;
51
+ pixels[di] = Math.round(overlay.pixels[si] * srcWeight + pixels[di] * dstWeight);
52
+ pixels[di + 1] = Math.round(overlay.pixels[si + 1] * srcWeight + pixels[di + 1] * dstWeight);
53
+ pixels[di + 2] = Math.round(overlay.pixels[si + 2] * srcWeight + pixels[di + 2] * dstWeight);
54
+ pixels[di + 3] = Math.round(outAlpha * 255);
55
+ }
56
+ }
57
+ return {
58
+ ...base,
59
+ pixels
60
+ };
61
+ }
62
+ /**
63
+ * Encode animation frames as an animated GIF.
64
+ *
65
+ * Each SVG frame is rasterized to RGBA pixels via the selected renderer
66
+ * (`options.renderer`, default `auto`), then quantized to 256 colors and
67
+ * written to a GIF stream via gifenc.
68
+ *
69
+ * @param frames - SVG frames with durations
70
+ * @param options - Animation options plus optional `scale` and `renderer`
71
+ * @returns GIF file as Uint8Array
72
+ */
73
+ async function createGif(frames, options) {
74
+ if (frames.length === 0) throw new Error("createGif requires at least one frame");
75
+ const [{ GIFEncoder, quantize, applyPalette }, rasterizer] = await Promise.all([loadGifenc(), selectRasterizer(options?.renderer ?? "auto")]);
76
+ const defaultDuration = options?.defaultDuration ?? 100;
77
+ const loop = options?.loop ?? 0;
78
+ const scale = options?.scale ?? 2;
79
+ const forceSvg = options?.forceSvg === true;
80
+ const gif = GIFEncoder();
81
+ const rasterize = async (frame) => {
82
+ if (forceSvg || !frame.snapshot || !rasterizer.rasterizeCells) return rasterizer.rasterize(frame.svg, scale);
83
+ const cells = await rasterizer.rasterizeCells(frame.snapshot, scale);
84
+ if (!svgHasChrome(frame.svg)) return cells;
85
+ return compositeBitmap(await rasterizer.rasterize(frame.svg, scale), cells, svgContentOffset(frame.svg, scale));
86
+ };
87
+ try {
88
+ const sampleIdx = Math.floor(frames.length / 2);
89
+ const sample = await rasterize(frames[sampleIdx]);
90
+ const palette = quantize(sample.pixels, 255);
91
+ const TRANSPARENT_IDX = 255;
92
+ let prevIndexed = null;
93
+ for (let i = 0; i < frames.length; i++) {
94
+ const frame = frames[i];
95
+ const duration = frame.duration || defaultDuration;
96
+ const { pixels: rgba, width, height } = i === sampleIdx ? sample : await rasterize(frame);
97
+ const indexed = applyPalette(rgba, palette);
98
+ const canDiff = prevIndexed !== null && prevIndexed.length === indexed.length;
99
+ let frameData = indexed;
100
+ if (canDiff) {
101
+ const delta = new Uint8Array(indexed.length);
102
+ for (let p = 0; p < indexed.length; p++) delta[p] = indexed[p] === prevIndexed[p] ? TRANSPARENT_IDX : indexed[p];
103
+ frameData = delta;
104
+ }
105
+ gif.writeFrame(frameData, width, height, {
106
+ palette: i === 0 ? palette : void 0,
107
+ delay: duration,
108
+ repeat: i === 0 ? loop === 0 ? 0 : loop : void 0,
109
+ transparent: canDiff,
110
+ transparentIndex: TRANSPARENT_IDX,
111
+ dispose: 1
112
+ });
113
+ if ((i & 3) === 3) await new Promise((r) => setImmediate(r));
114
+ prevIndexed = indexed;
115
+ }
116
+ } finally {
117
+ await rasterizer.dispose?.();
118
+ }
119
+ gif.finish();
120
+ return gif.bytesView();
121
+ }
122
+ /**
123
+ * Encode already-rasterized PNG frames as an animated GIF.
124
+ *
125
+ * Unlike {@link createGif}, the frames are pre-rendered PNGs — no SVG round-
126
+ * trip, no `@resvg/resvg-js`. Used by the cross-backend compositor
127
+ * (`../tape/compare-canvas.ts`), whose frames are composed PNGs.
128
+ *
129
+ * All frames are expected to share dimensions (the compositor guarantees this
130
+ * by time-syncing the panel layout); a mismatched frame is letterboxed onto
131
+ * the first frame's canvas size.
132
+ */
133
+ async function createGifFromPngs(frames, options) {
134
+ if (frames.length === 0) throw new Error("createGifFromPngs requires at least one frame");
135
+ const { GIFEncoder, quantize, applyPalette } = await loadGifenc();
136
+ const defaultDuration = options?.defaultDuration ?? 100;
137
+ const loop = options?.loop ?? 0;
138
+ const decode = options?.decodePng ?? await (async () => {
139
+ const { decodePngRgba } = await import("./png-codec-QwOtJ8Zs.mjs");
140
+ return decodePngRgba;
141
+ })();
142
+ const gif = GIFEncoder();
143
+ let baseW = 0;
144
+ let baseH = 0;
145
+ for (let i = 0; i < frames.length; i++) {
146
+ const frame = frames[i];
147
+ const decoded = decode(frame.png);
148
+ if (i === 0) {
149
+ baseW = decoded.width;
150
+ baseH = decoded.height;
151
+ }
152
+ let rgba = decoded.data;
153
+ let width = decoded.width;
154
+ let height = decoded.height;
155
+ if (width !== baseW || height !== baseH) {
156
+ const padded = new Uint8Array(baseW * baseH * 4);
157
+ for (let y = 0; y < Math.min(height, baseH); y++) for (let x = 0; x < Math.min(width, baseW); x++) {
158
+ const si = (y * width + x) * 4;
159
+ const di = (y * baseW + x) * 4;
160
+ padded[di] = rgba[si];
161
+ padded[di + 1] = rgba[si + 1];
162
+ padded[di + 2] = rgba[si + 2];
163
+ padded[di + 3] = rgba[si + 3];
164
+ }
165
+ rgba = padded;
166
+ width = baseW;
167
+ height = baseH;
168
+ }
169
+ const palette = quantize(rgba, 256);
170
+ const indexed = applyPalette(rgba, palette);
171
+ gif.writeFrame(indexed, width, height, {
172
+ palette,
173
+ delay: frame.duration || defaultDuration,
174
+ repeat: i === 0 ? loop === 0 ? 0 : loop : void 0
175
+ });
176
+ }
177
+ gif.finish();
178
+ return gif.bytesView();
179
+ }
180
+ var gifencModule;
181
+ var init_gif = __esmMin((() => {
182
+ init_rasterizer();
183
+ gifencModule = null;
184
+ }));
185
+ //#endregion
186
+ export { createGifFromPngs as n, init_gif as r, createGif as t };
187
+
188
+ //# sourceMappingURL=gif-C_AjaT9d.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gif-C_AjaT9d.mjs","names":[],"sources":["../../termless/src/view/gif.ts"],"sourcesContent":["/**\n * Animated GIF encoder for termless.\n *\n * Converts SVG frames to an animated GIF using gifenc (pure JS, no binary deps)\n * for GIF encoding. SVG → pixel rasterization goes through the **renderer**\n * (`./rasterizer.ts`): `canvas` (`@napi-rs/canvas`, high fidelity) when its\n * native binding loads, `resvg` (`@resvg/resvg-js`) as the cross-platform\n * fallback. The renderer is *not* hardwired — a default `out.gif` gets\n * `canvas` fidelity when the binding is available.\n *\n * `gifenc` is an optional/lazy-loaded dependency — throws a clear error if\n * missing.\n */\n\n// Module declarations live in ./gifenc.d.ts (picked up via tsconfig `include` glob).\nimport type { AnimationFrame, AnimationOptions } from \"./animation-types.ts\"\nimport { selectRasterizer, type RasterBitmap, type RendererKind } from \"./rasterizer.ts\"\n\n// Lazy-cached imports\nlet gifencModule: typeof import(\"gifenc\") | null = null\n\nasync function loadGifenc() {\n if (gifencModule) return gifencModule\n try {\n gifencModule = await import(\"gifenc\")\n return gifencModule\n } catch {\n throw new Error(\"createGif() requires gifenc. Install it:\\n bun add gifenc\")\n }\n}\n\nfunction svgHasChrome(svg: string): boolean {\n return /windowBar|windowTitle|<rect[^>]*rx=\"\\d+\"/.test(svg)\n}\n\nfunction svgContentOffset(svg: string, scale: number): { x: number; y: number } {\n const transforms = [...svg.matchAll(/<g[^>]*\\btransform=\"translate\\(\\s*([\\d.-]+)[,\\s]+([\\d.-]+)\\s*\\)\"/g)]\n const contentTransform = transforms.at(-1)\n if (!contentTransform) return { x: 0, y: 0 }\n return {\n x: Math.round(Number(contentTransform[1]) * scale),\n y: Math.round(Number(contentTransform[2]) * scale),\n }\n}\n\nfunction compositeBitmap(base: RasterBitmap, overlay: RasterBitmap, offset: { x: number; y: number }): RasterBitmap {\n const pixels = new Uint8Array(base.pixels)\n for (let y = 0; y < overlay.height; y++) {\n const dy = y + offset.y\n if (dy < 0 || dy >= base.height) continue\n for (let x = 0; x < overlay.width; x++) {\n const dx = x + offset.x\n if (dx < 0 || dx >= base.width) continue\n\n const si = (y * overlay.width + x) * 4\n const di = (dy * base.width + dx) * 4\n const srcAlpha = overlay.pixels[si + 3]! / 255\n if (srcAlpha <= 0) continue\n if (srcAlpha >= 1) {\n pixels[di] = overlay.pixels[si]!\n pixels[di + 1] = overlay.pixels[si + 1]!\n pixels[di + 2] = overlay.pixels[si + 2]!\n pixels[di + 3] = overlay.pixels[si + 3]!\n continue\n }\n\n const dstAlpha = pixels[di + 3]! / 255\n const outAlpha = srcAlpha + dstAlpha * (1 - srcAlpha)\n if (outAlpha <= 0) continue\n\n const dstWeight = (dstAlpha * (1 - srcAlpha)) / outAlpha\n const srcWeight = srcAlpha / outAlpha\n pixels[di] = Math.round(overlay.pixels[si]! * srcWeight + pixels[di]! * dstWeight)\n pixels[di + 1] = Math.round(overlay.pixels[si + 1]! * srcWeight + pixels[di + 1]! * dstWeight)\n pixels[di + 2] = Math.round(overlay.pixels[si + 2]! * srcWeight + pixels[di + 2]! * dstWeight)\n pixels[di + 3] = Math.round(outAlpha * 255)\n }\n }\n return { ...base, pixels }\n}\n\n/**\n * Encode animation frames as an animated GIF.\n *\n * Each SVG frame is rasterized to RGBA pixels via the selected renderer\n * (`options.renderer`, default `auto`), then quantized to 256 colors and\n * written to a GIF stream via gifenc.\n *\n * @param frames - SVG frames with durations\n * @param options - Animation options plus optional `scale` and `renderer`\n * @returns GIF file as Uint8Array\n */\nexport async function createGif(\n frames: AnimationFrame[],\n options?: AnimationOptions & { scale?: number; renderer?: RendererKind; forceSvg?: boolean },\n): Promise<Uint8Array> {\n if (frames.length === 0) {\n throw new Error(\"createGif requires at least one frame\")\n }\n\n const [{ GIFEncoder, quantize, applyPalette }, rasterizer] = await Promise.all([\n loadGifenc(),\n selectRasterizer(options?.renderer ?? \"auto\"),\n ])\n\n const defaultDuration = options?.defaultDuration ?? 100\n const loop = options?.loop ?? 0\n const scale = options?.scale ?? 2\n const forceSvg = options?.forceSvg === true\n\n const gif = GIFEncoder()\n\n // Cell-native path: a renderer that exposes `rasterizeCells` (swash) skips\n // the SVG round-trip when the frame carries a snapshot — that is what makes\n // color emoji and exact glyph coverage survive into the GIF. If the SVG\n // also carries chrome, rasterize the chrome SVG and overwrite only the\n // terminal content rectangle with the cell-native bitmap.\n const rasterize = async (frame: AnimationFrame) => {\n if (forceSvg || !frame.snapshot || !rasterizer.rasterizeCells) {\n return rasterizer.rasterize(frame.svg, scale)\n }\n\n const cells = await rasterizer.rasterizeCells(frame.snapshot, scale)\n if (!svgHasChrome(frame.svg)) return cells\n\n const chrome = await rasterizer.rasterize(frame.svg, scale)\n return compositeBitmap(chrome, cells, svgContentOffset(frame.svg, scale))\n }\n\n // Shared global palette. A terminal recording's colour set barely changes\n // frame to frame (theme + ANSI + anti-alias blends), so quantizing a fresh\n // palette per frame — and re-embedding it in every frame — is pure bloat.\n // Quantize ONCE from a representative content-bearing frame and reuse it as\n // the GIF's global colour table. 255 colours, not 256: index 255 is\n // reserved as the inter-frame transparency marker (below).\n //\n // The whole batch runs inside try/finally so the `browser` renderer's\n // headless-Chromium instance is released even if encoding throws.\n try {\n const sampleIdx = Math.floor(frames.length / 2)\n const sample = await rasterize(frames[sampleIdx]!)\n const palette = quantize(sample.pixels, 255)\n const TRANSPARENT_IDX = 255\n\n // Inter-frame diffing. Successive terminal frames differ in only a handful\n // of cells, so after frame 0 each frame is written as a *delta*: pixels\n // unchanged from the previous frame become the transparent index, and the\n // frame is composited over its predecessor (`dispose: 1` — do not dispose).\n // Long runs of the transparent index compress near-free under LZW — the\n // dominant GIF-size win for a recording.\n let prevIndexed: Uint8Array | null = null\n\n for (let i = 0; i < frames.length; i++) {\n const frame = frames[i]!\n const duration = frame.duration || defaultDuration\n\n const { pixels: rgba, width, height } = i === sampleIdx ? sample : await rasterize(frame)\n const indexed = applyPalette(rgba, palette)\n\n // Delta-encode against the previous frame when dimensions match.\n const canDiff = prevIndexed !== null && prevIndexed.length === indexed.length\n let frameData = indexed\n if (canDiff) {\n const delta = new Uint8Array(indexed.length)\n for (let p = 0; p < indexed.length; p++) {\n delta[p] = indexed[p] === prevIndexed![p] ? TRANSPARENT_IDX : indexed[p]!\n }\n frameData = delta\n }\n\n gif.writeFrame(frameData, width, height, {\n // Only the first frame carries the palette — it becomes the global\n // colour table; later frames reference it (no per-frame local table).\n palette: i === 0 ? palette : undefined,\n delay: duration,\n repeat: i === 0 ? (loop === 0 ? 0 : loop) : undefined,\n // Frames after the first are deltas: transparent pixels show the\n // composited prior frame through. `dispose: 1` keeps each frame in\n // place so the next delta lands on top of it.\n transparent: canDiff,\n transparentIndex: TRANSPARENT_IDX,\n dispose: 1,\n })\n\n // Yield to the event loop every frame so external progress UIs\n // (spinners, ProgressBar) can tick. `gif.writeFrame` is CPU-bound\n // (LZW encoding); without a yield the encoding loop monopolises\n // the loop for the entire run and any silvery/react Spinner timer\n // in the host process freezes mid-animation.\n if ((i & 3) === 3) await new Promise<void>((r) => setImmediate(r))\n\n // Compare the NEXT frame against this frame's full (un-delta'd) content.\n prevIndexed = indexed\n }\n } finally {\n // Release the headless-Chromium instance the `browser` renderer holds.\n await rasterizer.dispose?.()\n }\n\n gif.finish()\n return gif.bytesView()\n}\n\n/** One PNG-backed animation frame: pre-rasterized bytes + a display duration. */\nexport interface PngFrame {\n /** PNG bytes of this frame. */\n png: Uint8Array\n /** Display duration in milliseconds. */\n duration: number\n}\n\n/**\n * Encode already-rasterized PNG frames as an animated GIF.\n *\n * Unlike {@link createGif}, the frames are pre-rendered PNGs — no SVG round-\n * trip, no `@resvg/resvg-js`. Used by the cross-backend compositor\n * (`../tape/compare-canvas.ts`), whose frames are composed PNGs.\n *\n * All frames are expected to share dimensions (the compositor guarantees this\n * by time-syncing the panel layout); a mismatched frame is letterboxed onto\n * the first frame's canvas size.\n */\nexport async function createGifFromPngs(\n frames: PngFrame[],\n options?: AnimationOptions & { decodePng?: (png: Uint8Array) => { width: number; height: number; data: Uint8Array } },\n): Promise<Uint8Array> {\n if (frames.length === 0) {\n throw new Error(\"createGifFromPngs requires at least one frame\")\n }\n const { GIFEncoder, quantize, applyPalette } = await loadGifenc()\n const defaultDuration = options?.defaultDuration ?? 100\n const loop = options?.loop ?? 0\n\n // Decoder: caller-supplied (the compositor passes its upng codec) or upng.\n const decode =\n options?.decodePng ??\n (await (async () => {\n const { decodePngRgba } = await import(\"../recording/tape/png-codec.ts\")\n return decodePngRgba\n })())\n\n const gif = GIFEncoder()\n let baseW = 0\n let baseH = 0\n\n for (let i = 0; i < frames.length; i++) {\n const frame = frames[i]!\n const decoded = decode(frame.png)\n if (i === 0) {\n baseW = decoded.width\n baseH = decoded.height\n }\n let rgba = decoded.data\n let width = decoded.width\n let height = decoded.height\n if (width !== baseW || height !== baseH) {\n // Letterbox onto the base canvas (top-left aligned).\n const padded = new Uint8Array(baseW * baseH * 4)\n for (let y = 0; y < Math.min(height, baseH); y++) {\n for (let x = 0; x < Math.min(width, baseW); x++) {\n const si = (y * width + x) * 4\n const di = (y * baseW + x) * 4\n padded[di] = rgba[si]!\n padded[di + 1] = rgba[si + 1]!\n padded[di + 2] = rgba[si + 2]!\n padded[di + 3] = rgba[si + 3]!\n }\n }\n rgba = padded\n width = baseW\n height = baseH\n }\n const palette = quantize(rgba, 256)\n const indexed = applyPalette(rgba, palette)\n gif.writeFrame(indexed, width, height, {\n palette,\n delay: frame.duration || defaultDuration,\n repeat: i === 0 ? (loop === 0 ? 0 : loop) : undefined,\n })\n }\n\n gif.finish()\n return gif.bytesView()\n}\n"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { r as init_gif, t as createGif } from "./gif-C_AjaT9d.mjs";
2
+ init_gif();
3
+ export { createGif };