silvery 0.21.0 → 0.23.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 (224) hide show
  1. package/README.md +1 -1
  2. package/dist/{Text-Lq0dmj8-.mjs → Text-Ci6UCSTL.mjs} +46 -48
  3. package/dist/Text-Ci6UCSTL.mjs.map +1 -0
  4. package/dist/{render-string-DkQacASz.mjs → ag-q9lRlKlt.mjs} +5322 -4259
  5. package/dist/ag-q9lRlKlt.mjs.map +1 -0
  6. package/dist/{animation-ZMN2_XKv.mjs → animation-CZ4x2R7C.mjs} +1 -1
  7. package/dist/{animation-ZMN2_XKv.mjs.map → animation-CZ4x2R7C.mjs.map} +1 -1
  8. package/dist/{ansi-D1KQMAbf.mjs → ansi-CLmIMnop.mjs} +1 -1
  9. package/dist/{ansi-D1KQMAbf.mjs.map → ansi-CLmIMnop.mjs.map} +1 -1
  10. package/dist/{ansi-2Xn0yatP.d.mts → ansi-zmNzgkPB.d.mts} +1 -1
  11. package/dist/{ansi-2Xn0yatP.d.mts.map → ansi-zmNzgkPB.d.mts.map} +1 -1
  12. package/dist/{backend-B-WYLUib.mjs → backend-DSiSIc-h.mjs} +2 -2
  13. package/dist/{backend-B-WYLUib.mjs.map → backend-DSiSIc-h.mjs.map} +1 -1
  14. package/dist/chain-bridge-CkYcMxlq.mjs +4149 -0
  15. package/dist/chain-bridge-CkYcMxlq.mjs.map +1 -0
  16. package/dist/{chunk-BSw8zbkd.mjs → chunk-BEJ448es.mjs} +1 -3
  17. package/dist/cli-VNqUKlXw.mjs +4 -0
  18. package/dist/{context-BU5LkkIy.mjs → context-CEhuD1R_.mjs} +1 -1
  19. package/dist/{context-BU5LkkIy.mjs.map → context-CEhuD1R_.mjs.map} +1 -1
  20. package/dist/devtools-BF4IMumr.mjs +2 -0
  21. package/dist/{devtools-DcQjgyjL.mjs → devtools-td2AjFwc.mjs} +5 -5
  22. package/dist/{devtools-DcQjgyjL.mjs.map → devtools-td2AjFwc.mjs.map} +1 -1
  23. package/dist/{easing-BI-ASGMO.d.mts → easing-C5MrdO7V.d.mts} +1 -1
  24. package/dist/{easing-BI-ASGMO.d.mts.map → easing-C5MrdO7V.d.mts.map} +1 -1
  25. package/dist/{eta-CJlGH06n.mjs → eta-BwYWEjfS.mjs} +1 -1
  26. package/dist/{eta-CJlGH06n.mjs.map → eta-BwYWEjfS.mjs.map} +1 -1
  27. package/dist/{flexily-zero-adapter-C4lW_Ov5.mjs → flexily-zero-adapter-DktX3HsX.mjs} +1 -1
  28. package/dist/{flexily-zero-adapter-C3Vj0fPt.mjs → flexily-zero-adapter-VoBGHG14.mjs} +1 -1
  29. package/dist/{flexily-zero-adapter-C3Vj0fPt.mjs.map → flexily-zero-adapter-VoBGHG14.mjs.map} +1 -1
  30. package/dist/hit-registry-D_nxOivS.d.mts +71 -0
  31. package/dist/hit-registry-D_nxOivS.d.mts.map +1 -0
  32. package/dist/{UPNG-DosRPdF4.mjs → image-CIF75xMr.mjs} +891 -6
  33. package/dist/image-CIF75xMr.mjs.map +1 -0
  34. package/dist/{index-CSQf13CI.d.mts → index-377zdPLP.d.mts} +35 -12
  35. package/dist/index-377zdPLP.d.mts.map +1 -0
  36. package/dist/{index-Cl9KKjQ_.d.mts → index-BcSr0RAQ.d.mts} +8670 -10659
  37. package/dist/index-BcSr0RAQ.d.mts.map +1 -0
  38. package/dist/{index-XbNrPhWl.d.mts → index-DLnpxDAm.d.mts} +2 -2
  39. package/dist/{index-XbNrPhWl.d.mts.map → index-DLnpxDAm.d.mts.map} +1 -1
  40. package/dist/{index-BUMxS65f.d.mts → index-DlNmckTq.d.mts} +2 -2
  41. package/dist/{index-BUMxS65f.d.mts.map → index-DlNmckTq.d.mts.map} +1 -1
  42. package/dist/index.d.mts +12 -8
  43. package/dist/index.d.mts.map +1 -1
  44. package/dist/index.mjs +18 -15
  45. package/dist/index.mjs.map +1 -1
  46. package/dist/{layout-engine-C2px0RJE.mjs → layout-engine-BgR58vL0.mjs} +3 -3
  47. package/dist/{layout-engine-C2px0RJE.mjs.map → layout-engine-BgR58vL0.mjs.map} +1 -1
  48. package/dist/{layout-engine-C07LEXWT.mjs → layout-engine-DNjTha8f.mjs} +1 -1
  49. package/dist/{layout-signals-Cnw6xk8Q.mjs → layout-signals-CGYbHmxy.mjs} +146 -23
  50. package/dist/layout-signals-CGYbHmxy.mjs.map +1 -0
  51. package/dist/{bound-term-0sPrrzH1.d.mts → mouse-events-B2z5tbsL.d.mts} +780 -379
  52. package/dist/mouse-events-B2z5tbsL.d.mts.map +1 -0
  53. package/dist/{mouse-events-Dki3ISIp.mjs → mouse-events-WQ2h-uqG.mjs} +45 -10
  54. package/dist/mouse-events-WQ2h-uqG.mjs.map +1 -0
  55. package/dist/{multi-progress-DHZ2xUT2.d.mts → multi-progress-BaEFfHKP.d.mts} +2 -2
  56. package/dist/{multi-progress-DHZ2xUT2.d.mts.map → multi-progress-BaEFfHKP.d.mts.map} +1 -1
  57. package/dist/{multi-progress-CIRjrzma.mjs → multi-progress-zCWHEm9P.mjs} +3 -3
  58. package/dist/{multi-progress-CIRjrzma.mjs.map → multi-progress-zCWHEm9P.mjs.map} +1 -1
  59. package/dist/{node-CjM5Rt-M.mjs → node-CJ3J_rW-.mjs} +1 -1
  60. package/dist/{node-CjM5Rt-M.mjs.map → node-CJ3J_rW-.mjs.map} +1 -1
  61. package/dist/{progress-DB_Xo071.mjs → progress-6vJ7nxUg.mjs} +4 -4
  62. package/dist/{progress-DB_Xo071.mjs.map → progress-6vJ7nxUg.mjs.map} +1 -1
  63. package/dist/{progress-bar-oJwq22CR.mjs → progress-bar-BlFNsZL8.mjs} +4 -4
  64. package/dist/{progress-bar-oJwq22CR.mjs.map → progress-bar-BlFNsZL8.mjs.map} +1 -1
  65. package/dist/{reconciler-DldIJB93.mjs → reconciler--_-biN23.mjs} +190 -134
  66. package/dist/reconciler--_-biN23.mjs.map +1 -0
  67. package/dist/{render-string-BcoCpjCB.mjs → render-string-DaghHpLN.mjs} +1 -1
  68. package/dist/render-string-DtHZP0cU.mjs +215 -0
  69. package/dist/render-string-DtHZP0cU.mjs.map +1 -0
  70. package/dist/runtime.d.mts +4 -3
  71. package/dist/runtime.mjs +4 -3
  72. package/dist/schemes-DWQPxXAi.mjs +2516 -0
  73. package/dist/{schemes-JjNp4aSl.mjs.map → schemes-DWQPxXAi.mjs.map} +1 -1
  74. package/dist/{spinner-D9lrHr8s.mjs → spinner-B50hILqx.mjs} +19 -4
  75. package/dist/{spinner-D9lrHr8s.mjs.map → spinner-B50hILqx.mjs.map} +1 -1
  76. package/dist/{spinner-CZINHpkV.d.mts → spinner-DN10UfF0.d.mts} +2 -2
  77. package/dist/{spinner-CZINHpkV.d.mts.map → spinner-DN10UfF0.d.mts.map} +1 -1
  78. package/dist/{src-BNTToU7l.mjs → src-B8lf1kiR.mjs} +1507 -1166
  79. package/dist/src-B8lf1kiR.mjs.map +1 -0
  80. package/dist/{src-5w9QR6_8.mjs → src-BOLdi3WG.mjs} +105 -237
  81. package/dist/src-BOLdi3WG.mjs.map +1 -0
  82. package/dist/{src-BR4xNwdG.mjs → src-BXeTHHlL.mjs} +20240 -25437
  83. package/dist/src-BXeTHHlL.mjs.map +1 -0
  84. package/dist/{src-DKp-_OFG.mjs → src-BqeBFB3X.mjs} +90 -47
  85. package/dist/src-BqeBFB3X.mjs.map +1 -0
  86. package/dist/src-DbqcSrhy.mjs +3901 -0
  87. package/dist/src-DbqcSrhy.mjs.map +1 -0
  88. package/dist/{steps-Bp2uNqnn.d.mts → steps-B2kBmFpb.d.mts} +1 -1
  89. package/dist/{steps-Bp2uNqnn.d.mts.map → steps-B2kBmFpb.d.mts.map} +1 -1
  90. package/dist/{svg-Cz0UXcDj.mjs → svg-NmV9dgB9.mjs} +1 -1
  91. package/dist/{svg-Cz0UXcDj.mjs.map → svg-NmV9dgB9.mjs.map} +1 -1
  92. package/dist/{svg-g1D6ErwR.d.mts → svg-VUr78l5d.d.mts} +1 -1
  93. package/dist/{svg-g1D6ErwR.d.mts.map → svg-VUr78l5d.d.mts.map} +1 -1
  94. package/dist/term.d.mts +2 -2
  95. package/dist/term.mjs +3 -8
  96. package/dist/test.d.mts +673 -0
  97. package/dist/test.d.mts.map +1 -0
  98. package/dist/test.mjs +2829 -0
  99. package/dist/test.mjs.map +1 -0
  100. package/dist/theme.d.mts +2 -2
  101. package/dist/theme.d.mts.map +1 -1
  102. package/dist/theme.mjs +3 -8
  103. package/dist/{types-kt_fKR37.d.mts → types-D_E0FbqA.d.mts} +1 -1
  104. package/dist/{types-kt_fKR37.d.mts.map → types-D_E0FbqA.d.mts.map} +1 -1
  105. package/dist/ui/animation.d.mts +2 -2
  106. package/dist/ui/animation.mjs +1 -1
  107. package/dist/ui/ansi.d.mts +1 -1
  108. package/dist/ui/ansi.mjs +1 -1
  109. package/dist/ui/cli.d.mts +3 -3
  110. package/dist/ui/cli.mjs +5 -5
  111. package/dist/ui/image.d.mts +1 -1
  112. package/dist/ui/image.mjs +1 -1
  113. package/dist/ui/progress.d.mts +4 -4
  114. package/dist/ui/progress.mjs +4 -4
  115. package/dist/ui/react.d.mts +1 -1
  116. package/dist/ui/react.mjs +2 -2
  117. package/dist/ui/recording-chrome-react.d.mts +1 -1
  118. package/dist/ui/recording-chrome-react.mjs +2 -2
  119. package/dist/ui/recording-chrome.d.mts +1 -1
  120. package/dist/ui/recording-chrome.mjs +1 -1
  121. package/dist/ui/utils.mjs +1 -1
  122. package/dist/ui/wrappers.d.mts +2 -2
  123. package/dist/ui/wrappers.mjs +1 -1
  124. package/dist/ui.d.mts +6 -6
  125. package/dist/ui.mjs +7 -7
  126. package/dist/unicode-CxyxVGmB.mjs +11077 -0
  127. package/dist/unicode-CxyxVGmB.mjs.map +1 -0
  128. package/dist/{useLatest-DRDDVwjh.d.mts → useLatest-UOvWy0ZW.d.mts} +2 -2
  129. package/dist/{useLatest-DRDDVwjh.d.mts.map → useLatest-UOvWy0ZW.d.mts.map} +1 -1
  130. package/dist/useLayout-D5cE8OCI.mjs +424 -0
  131. package/dist/useLayout-D5cE8OCI.mjs.map +1 -0
  132. package/dist/{with-text-input-YeohVLeo.d.mts → with-text-input-DLoSCBrm.d.mts} +3 -3
  133. package/dist/{with-text-input-YeohVLeo.d.mts.map → with-text-input-DLoSCBrm.d.mts.map} +1 -1
  134. package/dist/wrap-measurer-registration-IV2HtcCd.d.mts +3106 -0
  135. package/dist/wrap-measurer-registration-IV2HtcCd.d.mts.map +1 -0
  136. package/dist/{wrapper-C70ATkVv.mjs → wrapper-CD29afOs.mjs} +59 -17
  137. package/dist/wrapper-CD29afOs.mjs.map +1 -0
  138. package/dist/{wrappers-BCUYITrY.mjs → wrappers-CInBJm44.mjs} +18 -8
  139. package/dist/wrappers-CInBJm44.mjs.map +1 -0
  140. package/dist/{yoga-adapter-BnZX1PAY.mjs → yoga-adapter-9uab5Qn-.mjs} +2 -2
  141. package/dist/{yoga-adapter-BnZX1PAY.mjs.map → yoga-adapter-9uab5Qn-.mjs.map} +1 -1
  142. package/dist/yoga-adapter-yqTl1aXK.mjs +2 -0
  143. package/package.json +59 -14
  144. package/dist/Text-Lq0dmj8-.mjs.map +0 -1
  145. package/dist/UPNG-Bo33r8rA.mjs +0 -3
  146. package/dist/UPNG-DosRPdF4.mjs.map +0 -1
  147. package/dist/__vite-browser-external-2447137e-D_JM6skp.mjs +0 -6
  148. package/dist/__vite-browser-external-2447137e-D_JM6skp.mjs.map +0 -1
  149. package/dist/ansi-yC4RyBNY.mjs +0 -22441
  150. package/dist/ansi-yC4RyBNY.mjs.map +0 -1
  151. package/dist/apng-CR08rIaH.mjs +0 -58
  152. package/dist/apng-CR08rIaH.mjs.map +0 -1
  153. package/dist/apng-DaHfVaVI.mjs +0 -3
  154. package/dist/assets/resvgjs.darwin-arm64-BtufyGW1.node +0 -0
  155. package/dist/assets/skia.darwin-arm64-DQs5sT6N.node +0 -0
  156. package/dist/backends-CUtan80W.mjs +0 -3
  157. package/dist/backends-DIVYzKqd.mjs +0 -1083
  158. package/dist/backends-DIVYzKqd.mjs.map +0 -1
  159. package/dist/bound-term-0sPrrzH1.d.mts.map +0 -1
  160. package/dist/canvas-1v7dPT-_.mjs +0 -3
  161. package/dist/canvas-CSuPOMNt.mjs +0 -1442
  162. package/dist/canvas-CSuPOMNt.mjs.map +0 -1
  163. package/dist/cli-dvo0r2fs.mjs +0 -4
  164. package/dist/compare-CQodSH4G.mjs +0 -376
  165. package/dist/compare-CQodSH4G.mjs.map +0 -1
  166. package/dist/compare-DHlcxEYA.mjs +0 -3
  167. package/dist/devtools-CJdt5H0X.mjs +0 -2
  168. package/dist/fonts-BFmhXDv7.mjs +0 -88
  169. package/dist/fonts-BFmhXDv7.mjs.map +0 -1
  170. package/dist/gif-C_AjaT9d.mjs +0 -188
  171. package/dist/gif-C_AjaT9d.mjs.map +0 -1
  172. package/dist/gif-DaC4XrxA.mjs +0 -3
  173. package/dist/gifenc-BOUT-KFB.mjs +0 -730
  174. package/dist/gifenc-BOUT-KFB.mjs.map +0 -1
  175. package/dist/image-C2Birh2x.mjs +0 -1252
  176. package/dist/image-C2Birh2x.mjs.map +0 -1
  177. package/dist/index-CSQf13CI.d.mts.map +0 -1
  178. package/dist/index-Cl9KKjQ_.d.mts.map +0 -1
  179. package/dist/key-mapping-CS-YD_cD.mjs +0 -132
  180. package/dist/key-mapping-CS-YD_cD.mjs.map +0 -1
  181. package/dist/key-mapping-Yn-Jgrij.mjs +0 -3
  182. package/dist/layout-signals-Cnw6xk8Q.mjs.map +0 -1
  183. package/dist/mouse-events-Dki3ISIp.mjs.map +0 -1
  184. package/dist/playwright-D5YiZcNS.mjs +0 -76397
  185. package/dist/playwright-D5YiZcNS.mjs.map +0 -1
  186. package/dist/png-codec-Dp84742B.mjs +0 -36
  187. package/dist/png-codec-Dp84742B.mjs.map +0 -1
  188. package/dist/png-codec-QwOtJ8Zs.mjs +0 -3
  189. package/dist/rasterizer-BRXrDdWx.mjs +0 -3
  190. package/dist/rasterizer-CpEhJvdR.mjs +0 -296
  191. package/dist/rasterizer-CpEhJvdR.mjs.map +0 -1
  192. package/dist/reconciler-DldIJB93.mjs.map +0 -1
  193. package/dist/render-string-DkQacASz.mjs.map +0 -1
  194. package/dist/resvg-js-DkOndZI3.mjs +0 -203
  195. package/dist/resvg-js-DkOndZI3.mjs.map +0 -1
  196. package/dist/schemes-JjNp4aSl.mjs +0 -2611
  197. package/dist/src-5w9QR6_8.mjs.map +0 -1
  198. package/dist/src-BNTToU7l.mjs.map +0 -1
  199. package/dist/src-BR4xNwdG.mjs.map +0 -1
  200. package/dist/src-DKp-_OFG.mjs.map +0 -1
  201. package/dist/src-bt8wSrfJ.mjs +0 -258
  202. package/dist/src-bt8wSrfJ.mjs.map +0 -1
  203. package/dist/src-e33Y6kNJ.mjs +0 -3
  204. package/dist/src-iDwu25UD.mjs +0 -1814
  205. package/dist/src-iDwu25UD.mjs.map +0 -1
  206. package/dist/svg-15lZZzxq.mjs +0 -486
  207. package/dist/svg-15lZZzxq.mjs.map +0 -1
  208. package/dist/svg-DY72a4HK.mjs +0 -3
  209. package/dist/term.mjs.map +0 -1
  210. package/dist/theme.mjs.map +0 -1
  211. package/dist/ui/display.d.mts +0 -35
  212. package/dist/ui/display.d.mts.map +0 -1
  213. package/dist/ui/display.mjs +0 -123
  214. package/dist/ui/display.mjs.map +0 -1
  215. package/dist/ui/input.d.mts +0 -184
  216. package/dist/ui/input.d.mts.map +0 -1
  217. package/dist/ui/input.mjs +0 -285
  218. package/dist/ui/input.mjs.map +0 -1
  219. package/dist/wrapper-C70ATkVv.mjs.map +0 -1
  220. package/dist/wrappers-BCUYITrY.mjs.map +0 -1
  221. package/dist/yoga-adapter-DxgsQ_gg.mjs +0 -2
  222. package/dist/zipBundle-3nqeDRtm.mjs +0 -3
  223. package/dist/zipBundle-VNAYFmqJ.mjs +0 -2003
  224. package/dist/zipBundle-VNAYFmqJ.mjs.map +0 -1
@@ -1,4 +1,302 @@
1
- import { i as __require, t as __commonJSMin } from "./chunk-BSw8zbkd.mjs";
1
+ import { i as __toESM, r as __require, t as __commonJSMin } from "./chunk-BEJ448es.mjs";
2
+ import { r as effect } from "./src-BqeBFB3X.mjs";
3
+ import { j as createTerminalProfile } from "./src-B8lf1kiR.mjs";
4
+ import { c as StdoutContext, l as TermContext } from "./context-CEhuD1R_.mjs";
5
+ import { c as useScreenRect, i as useBoxSize } from "./useLayout-D5cE8OCI.mjs";
6
+ import { n as Box, t as Text } from "./Text-Ci6UCSTL.mjs";
7
+ import { readFileSync } from "node:fs";
8
+ import { useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useSyncExternalStore } from "react";
9
+ import { jsx } from "react/jsx-runtime";
10
+ //#region packages/ag-react/src/hooks/useTerm.ts
11
+ /**
12
+ * Shallow equality comparison for object selectors.
13
+ *
14
+ * @example
15
+ * ```tsx
16
+ * const { cols, rows } = useTerm(t => ({ cols: t.cols, rows: t.rows }), shallow)
17
+ * ```
18
+ */
19
+ function shallow(a, b) {
20
+ if (Object.is(a, b)) return true;
21
+ if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return false;
22
+ const keysA = Object.keys(a);
23
+ const keysB = Object.keys(b);
24
+ if (keysA.length !== keysB.length) return false;
25
+ for (const key of keysA) if (!Object.is(a[key], b[key])) return false;
26
+ return true;
27
+ }
28
+ function useTerm(selector, equalityFn) {
29
+ const term = useContext(TermContext);
30
+ if (!term) throw new Error("useTerm must be used within a render(element, term) context");
31
+ if (!selector) return term;
32
+ return useTermSelector(term, selector, equalityFn);
33
+ }
34
+ function useTermSelector(term, selector, equalityFn) {
35
+ const prevRef = useRef(void 0);
36
+ const isEqual = equalityFn ?? Object.is;
37
+ const subscribe = useCallback((listener) => {
38
+ return effect(() => {
39
+ term.size.snapshot();
40
+ listener();
41
+ });
42
+ }, [term]);
43
+ const getSnapshot = useCallback(() => {
44
+ const next = selector(term);
45
+ if (prevRef.current !== void 0 && isEqual(prevRef.current, next)) return prevRef.current;
46
+ prevRef.current = next;
47
+ return next;
48
+ }, [
49
+ term,
50
+ selector,
51
+ isEqual
52
+ ]);
53
+ return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
54
+ }
55
+ //#endregion
56
+ //#region packages/ag-react/src/hooks/useWindowSize.ts
57
+ /**
58
+ * Hook to get the current terminal window size.
59
+ * Re-renders when the terminal is resized.
60
+ *
61
+ * Reads from `term.size` (the Size sub-owner) — always returns defined values
62
+ * via the Size owner's default (80x24 for non-TTY streams).
63
+ *
64
+ * @example
65
+ * ```tsx
66
+ * import { useWindowSize, Box, Text } from '@silvery/ag-react'
67
+ *
68
+ * function StatusBar() {
69
+ * const { columns, rows } = useWindowSize()
70
+ * return <Text>{`${columns}x${rows}`}</Text>
71
+ * }
72
+ * ```
73
+ */
74
+ function useWindowSize() {
75
+ return useTerm((t) => ({
76
+ columns: t.size.cols(),
77
+ rows: t.size.rows()
78
+ }), shallow);
79
+ }
80
+ //#endregion
81
+ //#region packages/ag-react/src/ui/image/kitty-graphics.ts
82
+ /**
83
+ * Kitty Graphics Protocol
84
+ *
85
+ * Encodes and manages images using the Kitty terminal graphics protocol.
86
+ * Images are transmitted as base64-encoded PNG data via APC (Application
87
+ * Program Command) escape sequences.
88
+ *
89
+ * Protocol reference: https://sw.kovidgoyal.net/kitty/graphics-protocol/
90
+ *
91
+ * Key concepts:
92
+ * - `a=T` — transmit and display the image
93
+ * - `f=100` — format is PNG (raw PNG data, terminal decodes it)
94
+ * - `m=0|1` — 0 = last/only chunk, 1 = more chunks follow
95
+ * - Chunks should be <= 4096 bytes of base64 to avoid overwhelming the terminal
96
+ * - Images can be assigned an `i=<id>` for later deletion
97
+ */
98
+ const APC_START = "\x1B_G";
99
+ const ST$1 = "\x1B\\";
100
+ /** Maximum base64 bytes per chunk (Kitty recommendation) */
101
+ const MAX_CHUNK_SIZE = 4096;
102
+ /**
103
+ * Encode a PNG image into Kitty graphics protocol escape sequences.
104
+ *
105
+ * The image data is base64-encoded and split into chunks of <= 4096 bytes.
106
+ * Each chunk is wrapped in an APC escape sequence. The first chunk carries
107
+ * the image metadata (action, format, dimensions, ID). Subsequent chunks
108
+ * only carry `m=1` or `m=0` to indicate continuation.
109
+ *
110
+ * @param pngData - Raw PNG image data
111
+ * @param opts - Optional dimensions and ID
112
+ * @returns A string containing the complete escape sequence(s)
113
+ *
114
+ * @example
115
+ * ```ts
116
+ * import { readFileSync } from "fs"
117
+ * import { encodeKittyImage } from "@silvery/ag-react"
118
+ *
119
+ * const png = readFileSync("photo.png")
120
+ * const seq = encodeKittyImage(png, { width: 40, height: 20 })
121
+ * process.stdout.write(seq)
122
+ * ```
123
+ */
124
+ function encodeKittyImage(pngData, opts) {
125
+ const chunks = splitIntoChunks(pngData.toString("base64"), MAX_CHUNK_SIZE);
126
+ if (chunks.length === 0) return `${APC_START}${buildParams(opts, 0)};${ST$1}`;
127
+ if (chunks.length === 1) return `${APC_START}${buildParams(opts, 0)};${chunks[0]}${ST$1}`;
128
+ const parts = [];
129
+ parts.push(`${APC_START}${buildParams(opts, 1)};${chunks[0]}${ST$1}`);
130
+ for (let i = 1; i < chunks.length - 1; i++) parts.push(`${APC_START}m=1;${chunks[i]}${ST$1}`);
131
+ parts.push(`${APC_START}m=0;${chunks[chunks.length - 1]}${ST$1}`);
132
+ return parts.join("");
133
+ }
134
+ /**
135
+ * Generate an escape sequence to delete a Kitty image by ID.
136
+ *
137
+ * Uses `a=d` (delete) with `d=i` (delete by image ID). Removes both the
138
+ * stored image bytes AND every placement of it. Use
139
+ * {@link deleteKittyPlacement} to remove a single placement while keeping
140
+ * the image stored for later re-placement.
141
+ *
142
+ * @param id - The image ID to delete
143
+ * @returns The delete escape sequence
144
+ *
145
+ * @example
146
+ * ```ts
147
+ * process.stdout.write(deleteKittyImage(42))
148
+ * ```
149
+ */
150
+ function deleteKittyImage(id) {
151
+ return `${APC_START}a=d,d=i,i=${id},q=2${ST$1}`;
152
+ }
153
+ /**
154
+ * Delete a single placement of a stored image, keeping the image bytes.
155
+ *
156
+ * Uses `a=d` with `d=i` (image id) and `p=` (placement id). The image
157
+ * remains stored on the terminal — re-place via {@link placeKittyImage}
158
+ * without re-transmitting the PNG.
159
+ *
160
+ * @param id - Image ID
161
+ * @param placementId - Placement ID (defaults to 1)
162
+ */
163
+ function deleteKittyPlacement(id, placementId = 1) {
164
+ return `${APC_START}a=d,d=i,i=${id},p=${placementId},q=2${ST$1}`;
165
+ }
166
+ /**
167
+ * Place an already-transmitted image at the current cursor position.
168
+ *
169
+ * Uses `a=p` (place existing image). The image must have been previously
170
+ * transmitted with `transmitOnly: true` (or `a=T` — which transmits AND
171
+ * places, but you can still re-place separately afterwards). This is the
172
+ * fast path for a moving image: transmit the PNG once, then emit a tiny
173
+ * APC packet for each position update — no re-encoding of base64 bytes.
174
+ *
175
+ * Pair with {@link deleteKittyPlacement} to clear the prior placement
176
+ * before placing at a new cursor position. Skipping the delete leaves a
177
+ * stacked copy at the old position.
178
+ *
179
+ * @example
180
+ * ```ts
181
+ * // Transmit once
182
+ * write(encodeKittyImage(png, { id: 42, transmitOnly: true }))
183
+ * // Place at cursor (which the caller positions via CSI ;H)
184
+ * write(placeKittyImage({ id: 42, width: 40, height: 20 }))
185
+ * // Move: clear old placement, position cursor, place again
186
+ * write(deleteKittyPlacement(42))
187
+ * write(`\x1b[10;5H`) // move cursor
188
+ * write(placeKittyImage({ id: 42, width: 40, height: 20 }))
189
+ * ```
190
+ */
191
+ function placeKittyImage(opts) {
192
+ const placementId = opts.placementId ?? 1;
193
+ const parts = [
194
+ `a=p`,
195
+ `i=${opts.id}`,
196
+ `p=${placementId}`,
197
+ `z=${formatIntParam("zIndex", opts.zIndex ?? 1)}`,
198
+ `C=1`,
199
+ `q=2`
200
+ ];
201
+ if (opts.width != null) parts.push(`c=${opts.width}`);
202
+ if (opts.height != null) parts.push(`r=${opts.height}`);
203
+ appendPlacementParams(parts, opts);
204
+ return `${APC_START}${parts.join(",")};${ST$1}`;
205
+ }
206
+ /**
207
+ * Check if the current terminal likely supports the Kitty graphics protocol.
208
+ *
209
+ * Pass a profile or caps fixture when available. Without one, this falls
210
+ * back to {@link createTerminalProfile} — the canonical single-source-of-
211
+ * truth entry point in `@silvery/ansi/profile`. Direct reads of terminal-
212
+ * signal env vars (TERM / TERM_PROGRAM / …) are banned outside that module
213
+ * — see `scripts/lint-env-reads.ts`.
214
+ *
215
+ * For definitive detection, use a terminal query (send the graphics protocol
216
+ * query and check for a response), but that requires async I/O.
217
+ *
218
+ * Known supporting terminals: Kitty, WezTerm, Ghostty (partial), Konsole (partial).
219
+ *
220
+ * @returns `true` if the terminal likely supports Kitty graphics
221
+ */
222
+ function isKittyGraphicsSupported(profile) {
223
+ if (profile === void 0) return createTerminalProfile().caps.kittyGraphics;
224
+ if ("caps" in profile && profile.caps) return profile.caps.kittyGraphics;
225
+ const resolved = isEmulator(profile) ? profile : profile.emulator;
226
+ if (!resolved) return false;
227
+ const term = resolved.TERM;
228
+ const termProgram = resolved.program;
229
+ if (term === "dumb") return false;
230
+ if (term === "xterm-kitty" || termProgram === "kitty") return true;
231
+ if (termProgram === "WezTerm") return true;
232
+ if (termProgram === "ghostty" || termProgram === "Ghostty") return true;
233
+ if (termProgram === "konsole") return true;
234
+ return false;
235
+ }
236
+ function isEmulator(value) {
237
+ if (value === null || typeof value !== "object") return false;
238
+ const maybe = value;
239
+ return typeof maybe.program === "string" && typeof maybe.TERM === "string";
240
+ }
241
+ /**
242
+ * Build the Kitty graphics protocol parameter string for the first chunk.
243
+ *
244
+ * Important: `KittyImageOptions.width` / `.height` are documented as
245
+ * "terminal columns" / "terminal rows" — i.e. CELL counts. The Kitty
246
+ * protocol uses `c=N` / `r=M` for cell-based display sizing, NOT `s=` /
247
+ * `v=` (those are SOURCE PIXEL dimensions, used only for raw RGB
248
+ * uploads with f=24/32). Sending `s=`/`v=` with f=100 (PNG) leaves
249
+ * display sizing to the PNG's native pixel dimensions, which on a
250
+ * 1536×1024 asset blows up to ~192×64 cells and effectively disappears
251
+ * off-screen. Use `c=`/`r=` so the terminal scales the PNG into the
252
+ * reserved cell viewport.
253
+ */
254
+ function buildParams(opts, more) {
255
+ const parts = [
256
+ opts?.transmitOnly ? `a=t` : `a=T`,
257
+ `f=100`,
258
+ `m=${more}`,
259
+ `z=${formatIntParam("zIndex", opts?.zIndex ?? 1)}`,
260
+ `C=1`,
261
+ `q=2`
262
+ ];
263
+ if (opts?.width != null) parts.push(`c=${opts.width}`);
264
+ if (opts?.height != null) parts.push(`r=${opts.height}`);
265
+ if (opts?.id != null) parts.push(`i=${opts.id}`);
266
+ if (opts) appendPlacementParams(parts, opts);
267
+ return parts.join(",");
268
+ }
269
+ function appendPlacementParams(parts, opts) {
270
+ if (opts.pixelOffset?.x != null) parts.push(`X=${formatNonNegativeIntParam("pixelOffset.x", opts.pixelOffset.x)}`);
271
+ if (opts.pixelOffset?.y != null) parts.push(`Y=${formatNonNegativeIntParam("pixelOffset.y", opts.pixelOffset.y)}`);
272
+ if (opts.sourceRect?.x != null) parts.push(`x=${formatNonNegativeIntParam("sourceRect.x", opts.sourceRect.x)}`);
273
+ if (opts.sourceRect?.y != null) parts.push(`y=${formatNonNegativeIntParam("sourceRect.y", opts.sourceRect.y)}`);
274
+ if (opts.sourceRect?.width != null) parts.push(`w=${formatPositiveIntParam("sourceRect.width", opts.sourceRect.width)}`);
275
+ if (opts.sourceRect?.height != null) parts.push(`h=${formatPositiveIntParam("sourceRect.height", opts.sourceRect.height)}`);
276
+ if (opts.virtualPlacement) parts.push("U=1");
277
+ }
278
+ function formatIntParam(name, value) {
279
+ if (!Number.isInteger(value)) throw new Error(`kitty graphics ${name} must be an integer`);
280
+ return value;
281
+ }
282
+ function formatNonNegativeIntParam(name, value) {
283
+ if (!Number.isInteger(value) || value < 0) throw new Error(`kitty graphics ${name} must be a non-negative integer`);
284
+ return value;
285
+ }
286
+ function formatPositiveIntParam(name, value) {
287
+ if (!Number.isInteger(value) || value <= 0) throw new Error(`kitty graphics ${name} must be a positive integer`);
288
+ return value;
289
+ }
290
+ /**
291
+ * Split a string into chunks of at most `size` characters.
292
+ */
293
+ function splitIntoChunks(str, size) {
294
+ if (str.length === 0) return [];
295
+ const chunks = [];
296
+ for (let i = 0; i < str.length; i += size) chunks.push(str.slice(i, i + size));
297
+ return chunks;
298
+ }
299
+ //#endregion
2
300
  //#region ../../node_modules/.bun/pako@1.0.11/node_modules/pako/lib/utils/common.js
3
301
  var require_common = /* @__PURE__ */ __commonJSMin(((exports) => {
4
302
  var TYPED_OK = typeof Uint8Array !== "undefined" && typeof Uint16Array !== "undefined" && typeof Int32Array !== "undefined";
@@ -3913,8 +4211,27 @@ var require_pako = /* @__PURE__ */ __commonJSMin(((exports, module) => {
3913
4211
  module.exports = pako;
3914
4212
  }));
3915
4213
  //#endregion
3916
- //#region ../../node_modules/.bun/upng-js@2.1.0/node_modules/upng-js/UPNG.js
3917
- var require_UPNG = /* @__PURE__ */ __commonJSMin(((exports, module) => {
4214
+ //#region packages/ag-react/src/ui/image/sixel-encoder.ts
4215
+ /**
4216
+ * Sixel Encoder (Minimal Implementation)
4217
+ *
4218
+ * Sixel is an older image protocol supported by terminals like xterm, mlterm,
4219
+ * foot, and some others. Images are encoded as DCS (Device Control String)
4220
+ * sequences where each character encodes 6 vertical pixels.
4221
+ *
4222
+ * DCS format: `ESC P <params> q <sixel-data> ESC \`
4223
+ *
4224
+ * This is a minimal implementation that produces valid Sixel output for
4225
+ * simple images. For production use with complex images, consider using
4226
+ * a dedicated Sixel library that handles color quantization and dithering.
4227
+ *
4228
+ * Protocol reference: https://en.wikipedia.org/wiki/Sixel
4229
+ *
4230
+ * TODO: Full Sixel encoding with proper color quantization, dithering,
4231
+ * and compression. The current implementation handles basic RGBA image data
4232
+ * with a simple nearest-color palette approach.
4233
+ */
4234
+ var import_UPNG = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
3918
4235
  (function() {
3919
4236
  var UPNG = {};
3920
4237
  var pako;
@@ -5068,8 +5385,576 @@ var require_UPNG = /* @__PURE__ */ __commonJSMin(((exports, module) => {
5068
5385
  };
5069
5386
  })(UPNG, pako);
5070
5387
  })();
5071
- }));
5388
+ })))(), 1);
5389
+ const DCS_START = "\x1BP";
5390
+ const ST = "\x1B\\";
5391
+ /** Sixel introduces a color with `#<index>;2;<r>;<g>;<b>` (RGB percentages 0-100) */
5392
+ const SIXEL_NEWLINE = "-";
5393
+ /**
5394
+ * Decode a PNG buffer into RGBA pixel data suitable for {@link encodeSixel}.
5395
+ *
5396
+ * The Sixel protocol cannot transmit PNG directly (unlike Kitty's `f=100`),
5397
+ * so terminals that fall back to Sixel need raw RGBA pixels. This helper
5398
+ * uses upng-js — a small (~30 KB) pure-JS PNG decoder with one tiny
5399
+ * dependency (pako) — to bridge PNG → RGBA.
5400
+ *
5401
+ * Returns `null` if the buffer is not a valid PNG. Callers should fall back
5402
+ * to a text placeholder in that case.
5403
+ *
5404
+ * Supports all PNG flavors UPNG handles: 8/16-bit depth, RGB/RGBA/grayscale/
5405
+ * palette, interlaced. Animated PNGs (APNG) collapse to the first frame —
5406
+ * Sixel itself is single-frame.
5407
+ *
5408
+ * @example
5409
+ * ```ts
5410
+ * import { readFileSync } from "fs"
5411
+ * import { decodePngToRgba, encodeSixel } from "./sixel-encoder"
5412
+ *
5413
+ * const png = readFileSync("photo.png")
5414
+ * const rgba = decodePngToRgba(png)
5415
+ * if (rgba) {
5416
+ * process.stdout.write(encodeSixel(rgba))
5417
+ * }
5418
+ * ```
5419
+ */
5420
+ function decodePngToRgba(pngData) {
5421
+ try {
5422
+ const view = pngData instanceof Uint8Array ? pngData : new Uint8Array(pngData);
5423
+ const ab = view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength);
5424
+ const decoded = import_UPNG.default.decode(ab);
5425
+ const frames = import_UPNG.default.toRGBA8(decoded);
5426
+ if (frames.length === 0) return null;
5427
+ return {
5428
+ width: decoded.width,
5429
+ height: decoded.height,
5430
+ data: new Uint8Array(frames[0])
5431
+ };
5432
+ } catch {
5433
+ return null;
5434
+ }
5435
+ }
5436
+ /**
5437
+ * Encode RGBA image data as a Sixel escape sequence.
5438
+ *
5439
+ * This is a basic implementation that:
5440
+ * 1. Quantizes colors to a small palette (up to 256 colors)
5441
+ * 2. Encodes 6-row bands as Sixel characters
5442
+ * 3. Wraps in a DCS escape sequence
5443
+ *
5444
+ * For transparent pixels (alpha < 128), the background shows through.
5445
+ *
5446
+ * @param imageData - Image dimensions and RGBA pixel data
5447
+ * @returns A DCS escape sequence containing the Sixel-encoded image
5448
+ *
5449
+ * @example
5450
+ * ```ts
5451
+ * const img = { width: 10, height: 12, data: new Uint8Array(10 * 12 * 4) }
5452
+ * const seq = encodeSixel(img)
5453
+ * process.stdout.write(seq)
5454
+ * ```
5455
+ */
5456
+ function encodeSixel(imageData) {
5457
+ const { width, height, data } = imageData;
5458
+ if (width === 0 || height === 0 || data.length === 0) return `${DCS_START}q${ST}`;
5459
+ const palette = /* @__PURE__ */ new Map();
5460
+ const pixelColors = new Uint16Array(width * height);
5461
+ let nextColorIndex = 1;
5462
+ for (let y = 0; y < height; y++) for (let x = 0; x < width; x++) {
5463
+ const offset = (y * width + x) * 4;
5464
+ const r = data[offset];
5465
+ const g = data[offset + 1];
5466
+ const b = data[offset + 2];
5467
+ if (data[offset + 3] < 128) continue;
5468
+ const key = `${r >> 2 & 63},${g >> 2 & 63},${b >> 2 & 63}`;
5469
+ let idx = palette.get(key);
5470
+ if (idx == null) if (nextColorIndex >= 256) idx = 1;
5471
+ else {
5472
+ idx = nextColorIndex++;
5473
+ palette.set(key, idx);
5474
+ }
5475
+ pixelColors[y * width + x] = idx;
5476
+ }
5477
+ const parts = [];
5478
+ parts.push(`"1;1;${width};${height}`);
5479
+ for (const [key, idx] of palette) {
5480
+ const [qr, qg, qb] = key.split(",").map(Number);
5481
+ const rPct = Math.round(qr / 63 * 100);
5482
+ const gPct = Math.round(qg / 63 * 100);
5483
+ const bPct = Math.round(qb / 63 * 100);
5484
+ parts.push(`#${idx};2;${rPct};${gPct};${bPct}`);
5485
+ }
5486
+ for (let bandY = 0; bandY < height; bandY += 6) {
5487
+ if (bandY > 0) parts.push(SIXEL_NEWLINE);
5488
+ const bandColors = /* @__PURE__ */ new Set();
5489
+ for (let dy = 0; dy < 6 && bandY + dy < height; dy++) for (let x = 0; x < width; x++) {
5490
+ const ci = pixelColors[(bandY + dy) * width + x];
5491
+ if (ci > 0) bandColors.add(ci);
5492
+ }
5493
+ let first = true;
5494
+ for (const colorIdx of bandColors) {
5495
+ if (!first) parts.push("$");
5496
+ first = false;
5497
+ parts.push(`#${colorIdx}`);
5498
+ for (let x = 0; x < width; x++) {
5499
+ let sixelBits = 0;
5500
+ for (let dy = 0; dy < 6; dy++) {
5501
+ const y = bandY + dy;
5502
+ if (y < height && pixelColors[y * width + x] === colorIdx) sixelBits |= 1 << dy;
5503
+ }
5504
+ parts.push(String.fromCharCode(sixelBits + 63));
5505
+ }
5506
+ }
5507
+ }
5508
+ return `${DCS_START}q${parts.join("")}${ST}`;
5509
+ }
5510
+ /**
5511
+ * Check if the current terminal likely supports the Sixel protocol.
5512
+ *
5513
+ * Pass `caps` (from `term.caps` or a {@link TerminalCaps} fixture) when
5514
+ * available. Without caps, this falls back to {@link createTerminalProfile}
5515
+ * — the canonical single-source-of-truth entry point in
5516
+ * `@silvery/ansi/profile`. Direct reads of terminal-signal env vars
5517
+ * (TERM / TERM_PROGRAM / …) are banned outside that module — see
5518
+ * `scripts/lint-env-reads.ts`.
5519
+ *
5520
+ * For definitive detection, send a DA1 (Device Attributes) query and check
5521
+ * for "4" in the response, but that requires async I/O.
5522
+ *
5523
+ * Known supporting terminals: xterm (with +sixel), mlterm, foot, mintty,
5524
+ * WezTerm, Contour, Sixel-enabled builds of various terminals.
5525
+ *
5526
+ * @returns `true` if the terminal likely supports Sixel
5527
+ */
5528
+ function isSixelSupported(emulator) {
5529
+ const resolved = emulator ?? createTerminalProfile().emulator;
5530
+ const term = resolved.TERM;
5531
+ const termProgram = resolved.program;
5532
+ if (termProgram === "mlterm" || term.startsWith("mlterm")) return true;
5533
+ if (termProgram === "foot" || term === "foot" || term === "foot-extra") return true;
5534
+ if (termProgram === "WezTerm") return true;
5535
+ if (termProgram === "mintty") return true;
5536
+ return false;
5537
+ }
5538
+ //#endregion
5539
+ //#region packages/ag-react/src/ui/image/image-placement.ts
5540
+ const CURSOR_HIDE = "\x1B[?25l";
5541
+ const CURSOR_SHOW = "\x1B[?25h";
5542
+ const CURSOR_SAVE = "\x1B7";
5543
+ const CURSOR_RESTORE = "\x1B8";
5544
+ function withCursorPreserved(seq) {
5545
+ return `${CURSOR_HIDE}${CURSOR_SAVE}${seq}${CURSOR_RESTORE}${CURSOR_SHOW}`;
5546
+ }
5547
+ function computeVisibleImagePlacement({ rect, imagePixels, sourceRect, pixelOffset, viewport }) {
5548
+ if (rect.width <= 0 || rect.height <= 0) return null;
5549
+ if (rect.x + rect.width <= 0 || rect.y + rect.height <= 0) return null;
5550
+ if (viewport && (rect.x >= viewport.width || rect.y >= viewport.height)) return null;
5551
+ const leftClip = Math.max(0, -rect.x);
5552
+ const topClip = Math.max(0, -rect.y);
5553
+ const rightClip = viewport ? Math.max(0, rect.x + rect.width - viewport.width) : 0;
5554
+ const bottomClip = viewport ? Math.max(0, rect.y + rect.height - viewport.height) : 0;
5555
+ const visibleWidth = rect.width - leftClip - rightClip;
5556
+ const visibleHeight = rect.height - topClip - bottomClip;
5557
+ if (visibleWidth <= 0 || visibleHeight <= 0) return null;
5558
+ const placement = {
5559
+ x: Math.max(0, rect.x),
5560
+ y: Math.max(0, rect.y),
5561
+ width: visibleWidth,
5562
+ height: visibleHeight,
5563
+ ...pixelOffset ? { pixelOffset } : {}
5564
+ };
5565
+ if (!imagePixels || topClip === 0 && leftClip === 0 && rightClip === 0 && bottomClip === 0) return sourceRect ? {
5566
+ ...placement,
5567
+ sourceRect
5568
+ } : placement;
5569
+ const srcX = sourceRect?.x ?? 0;
5570
+ const srcY = sourceRect?.y ?? 0;
5571
+ const srcWidth = sourceRect?.width ?? imagePixels.width;
5572
+ const srcHeight = sourceRect?.height ?? imagePixels.height;
5573
+ const pixelsPerCol = srcWidth / Math.max(1, rect.width);
5574
+ const pixelsPerRow = srcHeight / Math.max(1, rect.height);
5575
+ return {
5576
+ ...placement,
5577
+ sourceRect: {
5578
+ x: Math.round(srcX + leftClip * pixelsPerCol),
5579
+ y: Math.round(srcY + topClip * pixelsPerRow),
5580
+ width: Math.max(1, Math.round(visibleWidth * pixelsPerCol)),
5581
+ height: Math.max(1, Math.round(visibleHeight * pixelsPerRow))
5582
+ }
5583
+ };
5584
+ }
5585
+ function imagePlacementKey({ placementId, zIndex, pixelOffset, sourceRect, virtualPlacement }) {
5586
+ return JSON.stringify({
5587
+ placementId,
5588
+ zIndex,
5589
+ pixelOffset,
5590
+ sourceRect,
5591
+ virtualPlacement
5592
+ });
5593
+ }
5594
+ function planKittyImagePlacement({ rect, imagePixels, sourceRect, pixelOffset, viewport, placementId, zIndex, virtualPlacement, previousPlacement, srcChanged }) {
5595
+ const placement = computeVisibleImagePlacement({
5596
+ rect,
5597
+ imagePixels,
5598
+ sourceRect,
5599
+ pixelOffset,
5600
+ viewport
5601
+ });
5602
+ if (!placement) return previousPlacement ? { kind: "delete-placement" } : { kind: "noop" };
5603
+ const placementKey = imagePlacementKey({
5604
+ placementId,
5605
+ zIndex,
5606
+ pixelOffset: placement.pixelOffset,
5607
+ sourceRect: placement.sourceRect,
5608
+ virtualPlacement
5609
+ });
5610
+ if (previousPlacement !== null && previousPlacement.x === placement.x && previousPlacement.y === placement.y && previousPlacement.width === placement.width && previousPlacement.height === placement.height && previousPlacement.placementKey === placementKey && !srcChanged) return { kind: "noop" };
5611
+ return {
5612
+ kind: "place",
5613
+ placement,
5614
+ placementKey,
5615
+ transmit: srcChanged,
5616
+ deleteImageBeforeTransmit: srcChanged && previousPlacement !== null
5617
+ };
5618
+ }
5619
+ //#endregion
5620
+ //#region packages/ag-react/src/ui/image/Image.tsx
5621
+ /**
5622
+ * Image Component
5623
+ *
5624
+ * Renders bitmap images in supported terminals using the Kitty graphics
5625
+ * protocol (primary) or Sixel (fallback). When neither is supported,
5626
+ * displays a text placeholder.
5627
+ *
5628
+ * Since terminal images are escape-sequence-based and don't fit the cell
5629
+ * buffer model, the component reserves visual space with a Box of the
5630
+ * requested dimensions and queues typed terminal artifacts after layout.
5631
+ *
5632
+ * @example
5633
+ * ```tsx
5634
+ * import { readFileSync } from "fs"
5635
+ * import { Image } from "@silvery/ag-react"
5636
+ *
5637
+ * const png = readFileSync("photo.png")
5638
+ * <Image src={png} width={40} height={20} />
5639
+ *
5640
+ * // With file path
5641
+ * <Image src="/path/to/image.png" width={40} height={20} />
5642
+ *
5643
+ * // Auto-detect protocol, fall back to text
5644
+ * <Image src={png} width={40} height={20} fallback="[photo]" />
5645
+ * ```
5646
+ */
5647
+ /**
5648
+ * Determine the best available image protocol.
5649
+ *
5650
+ * When `caps` is supplied (the caps of the Term we are actually rendering INTO,
5651
+ * read from {@link TermContext}), the decision is gated on THOSE — a terminal
5652
+ * whose own caps report no Kitty/Sixel support must never receive graphics
5653
+ * escape bytes, even if the ambient `process.stdout`/`TERM_PROGRAM` claims
5654
+ * support (a piped sink, a non-Kitty emulator, a proxied/cmux PTY). km bead
5655
+ * 19668: the welcome-bitmap escape-flood came from consulting ambient stdout
5656
+ * instead of the render term.
5657
+ *
5658
+ * When `caps` is absent (no TermContext — a standalone/unit render), it falls
5659
+ * back to the ambient profile, preserving the historical behavior.
5660
+ *
5661
+ * Returns null if no image protocol is available.
5662
+ */
5663
+ function detectProtocol(preferred, caps) {
5664
+ const kitty = caps ? caps.kittyGraphics : isKittyGraphicsSupported();
5665
+ const sixel = caps ? caps.sixel : isSixelSupported();
5666
+ if (preferred === "kitty") return kitty ? "kitty" : null;
5667
+ if (preferred === "sixel") return sixel ? "sixel" : null;
5668
+ if (kitty) return "kitty";
5669
+ if (sixel) return "sixel";
5670
+ return null;
5671
+ }
5672
+ /** Incrementing image ID counter for Kitty protocol */
5673
+ let nextImageId = 1;
5674
+ const KITTY_PLACEHOLDER = String.fromCodePoint(1109742);
5675
+ const KITTY_PLACEHOLDER_DIACRITICS = [
5676
+ 773,
5677
+ 781,
5678
+ 782,
5679
+ 784,
5680
+ 786,
5681
+ 829,
5682
+ 830,
5683
+ 831,
5684
+ 838,
5685
+ 842,
5686
+ 843,
5687
+ 844,
5688
+ 848,
5689
+ 849,
5690
+ 850,
5691
+ 855,
5692
+ 859,
5693
+ 867,
5694
+ 868,
5695
+ 869,
5696
+ 870,
5697
+ 871,
5698
+ 872,
5699
+ 873,
5700
+ 874,
5701
+ 875,
5702
+ 876,
5703
+ 877,
5704
+ 878,
5705
+ 879,
5706
+ 1155,
5707
+ 1156,
5708
+ 1157,
5709
+ 1158,
5710
+ 1159,
5711
+ 1426,
5712
+ 1427,
5713
+ 1428,
5714
+ 1429,
5715
+ 1431,
5716
+ 1432,
5717
+ 1433,
5718
+ 1436,
5719
+ 1437,
5720
+ 1438,
5721
+ 1439,
5722
+ 1440,
5723
+ 1441,
5724
+ 1448,
5725
+ 1449,
5726
+ 1451,
5727
+ 1452,
5728
+ 1455,
5729
+ 1476,
5730
+ 1552,
5731
+ 1553,
5732
+ 1554,
5733
+ 1555,
5734
+ 1556,
5735
+ 1557,
5736
+ 1558,
5737
+ 1559,
5738
+ 1623,
5739
+ 1624
5740
+ ].map((codepoint) => String.fromCodePoint(codepoint));
5741
+ function canRenderKittyPlaceholder({ id, width, height }) {
5742
+ return id !== null && id > 0 && id <= 255 && width > 0 && height > 0 && width <= KITTY_PLACEHOLDER_DIACRITICS.length && height <= KITTY_PLACEHOLDER_DIACRITICS.length;
5743
+ }
5744
+ function kittyPlaceholderText(width, height) {
5745
+ return Array.from({ length: height }, (_, row) => Array.from({ length: width }, (_, col) => KITTY_PLACEHOLDER + KITTY_PLACEHOLDER_DIACRITICS[row] + KITTY_PLACEHOLDER_DIACRITICS[col]).join("")).join("\n");
5746
+ }
5747
+ /**
5748
+ * Renders a bitmap image in the terminal.
5749
+ *
5750
+ * The component operates in two phases:
5751
+ * 1. **Layout phase**: Renders a Box that reserves the visual space
5752
+ * (filled with spaces so the cell buffer has the right dimensions).
5753
+ * 2. **Commit phase**: Queues terminal image artifacts positioned over the
5754
+ * reserved space.
5755
+ *
5756
+ * When image protocols are not available, the fallback text is shown instead.
5757
+ */
5758
+ function Image({ src, width: requestedWidth, height: requestedHeight, fallback = "[image]", protocol: preferredProtocol = "auto", placementId, zIndex, pixelOffset, sourceRect, virtualPlacement }) {
5759
+ const parentSize = useBoxSize();
5760
+ const effectiveWidth = requestedWidth ?? parentSize.width;
5761
+ const effectiveHeight = requestedHeight ?? Math.max(1, Math.floor(effectiveWidth / 2));
5762
+ return /* @__PURE__ */ jsx(Box, {
5763
+ width: effectiveWidth,
5764
+ height: effectiveHeight,
5765
+ children: /* @__PURE__ */ jsx(ImagePlacement, {
5766
+ src,
5767
+ width: effectiveWidth,
5768
+ height: effectiveHeight,
5769
+ fallback,
5770
+ protocol: preferredProtocol,
5771
+ placementId,
5772
+ zIndex,
5773
+ pixelOffset,
5774
+ sourceRect,
5775
+ virtualPlacement
5776
+ })
5777
+ });
5778
+ }
5779
+ function ImagePlacement({ src, width: effectiveWidth, height: effectiveHeight, fallback, protocol: preferredProtocol, placementId, zIndex, pixelOffset, sourceRect, virtualPlacement }) {
5780
+ const boxRect = useScreenRect();
5781
+ const stdoutCtx = useContext(StdoutContext);
5782
+ const term = useContext(TermContext);
5783
+ const termKittyGraphics = term?.caps.kittyGraphics;
5784
+ const termSixel = term?.caps.sixel;
5785
+ const { columns: viewportWidth, rows: viewportHeight } = useWindowSize();
5786
+ const viewport = {
5787
+ width: viewportWidth,
5788
+ height: viewportHeight
5789
+ };
5790
+ const imageIdRef = useRef(null);
5791
+ const transmittedSrcRef = useRef(null);
5792
+ const lastEmittedRef = useRef(null);
5793
+ const pngData = useMemo(() => {
5794
+ if (Buffer.isBuffer(src)) return src;
5795
+ try {
5796
+ return readFileSync(src);
5797
+ } catch {
5798
+ return null;
5799
+ }
5800
+ }, [src]);
5801
+ const decodedImage = useMemo(() => pngData ? decodePngToRgba(pngData) : null, [pngData]);
5802
+ const activeProtocol = useMemo(() => detectProtocol(preferredProtocol, term ? {
5803
+ kittyGraphics: term.caps.kittyGraphics,
5804
+ sixel: term.caps.sixel
5805
+ } : void 0), [
5806
+ preferredProtocol,
5807
+ term,
5808
+ termKittyGraphics,
5809
+ termSixel
5810
+ ]);
5811
+ if (activeProtocol === "kitty" && imageIdRef.current == null) imageIdRef.current = nextImageId++;
5812
+ useLayoutEffect(() => {
5813
+ if (!pngData || !stdoutCtx || !activeProtocol) return;
5814
+ if (effectiveWidth <= 0 || effectiveHeight <= 0) return;
5815
+ if (boxRect.width <= 0) return;
5816
+ const write = (data, artifact) => {
5817
+ if (stdoutCtx.queueFrameArtifact && artifact) {
5818
+ stdoutCtx.queueFrameArtifact({
5819
+ kind: "terminal-sequence",
5820
+ owner: artifact.owner,
5821
+ zIndex: artifact.zIndex,
5822
+ sequence: data
5823
+ });
5824
+ return;
5825
+ }
5826
+ (stdoutCtx.writeAfterFrame ?? stdoutCtx.write)(data);
5827
+ };
5828
+ if (activeProtocol === "kitty") {
5829
+ const id = imageIdRef.current;
5830
+ if (id == null) return;
5831
+ const srcChanged = transmittedSrcRef.current !== pngData;
5832
+ const plan = planKittyImagePlacement({
5833
+ rect: {
5834
+ x: boxRect.x,
5835
+ y: boxRect.y,
5836
+ width: effectiveWidth,
5837
+ height: effectiveHeight
5838
+ },
5839
+ imagePixels: decodedImage,
5840
+ sourceRect,
5841
+ pixelOffset,
5842
+ viewport,
5843
+ placementId,
5844
+ zIndex,
5845
+ virtualPlacement,
5846
+ previousPlacement: lastEmittedRef.current,
5847
+ srcChanged
5848
+ });
5849
+ const placeVisible = (visible, placementKey) => {
5850
+ write(withCursorPreserved(`\x1b[${visible.y + 1};${visible.x + 1}H` + placeKittyImage({
5851
+ id,
5852
+ width: visible.width,
5853
+ height: visible.height,
5854
+ placementId,
5855
+ zIndex,
5856
+ pixelOffset: visible.pixelOffset,
5857
+ sourceRect: visible.sourceRect,
5858
+ virtualPlacement
5859
+ })), {
5860
+ owner: "image:kitty:place",
5861
+ zIndex
5862
+ });
5863
+ lastEmittedRef.current = {
5864
+ x: visible.x,
5865
+ y: visible.y,
5866
+ width: visible.width,
5867
+ height: visible.height,
5868
+ placementKey
5869
+ };
5870
+ };
5871
+ if (plan.kind === "noop") return;
5872
+ if (plan.kind === "delete-placement") {
5873
+ const id = imageIdRef.current;
5874
+ if (id != null) write(withCursorPreserved(deleteKittyPlacement(id, placementId)), {
5875
+ owner: "image:kitty:delete-placement",
5876
+ zIndex
5877
+ });
5878
+ lastEmittedRef.current = null;
5879
+ return;
5880
+ }
5881
+ if (plan.transmit) {
5882
+ if (plan.deleteImageBeforeTransmit) write(deleteKittyImage(id), {
5883
+ owner: "image:kitty:delete-image",
5884
+ zIndex
5885
+ });
5886
+ write(encodeKittyImage(pngData, {
5887
+ id,
5888
+ transmitOnly: true
5889
+ }), {
5890
+ owner: "image:kitty:transmit",
5891
+ zIndex
5892
+ });
5893
+ transmittedSrcRef.current = pngData;
5894
+ }
5895
+ const visible = plan.placement;
5896
+ placeVisible(visible, plan.placementKey);
5897
+ } else if (activeProtocol === "sixel") {
5898
+ const visible = computeVisibleImagePlacement({
5899
+ rect: {
5900
+ x: boxRect.x,
5901
+ y: boxRect.y,
5902
+ width: effectiveWidth,
5903
+ height: effectiveHeight
5904
+ },
5905
+ imagePixels: decodedImage,
5906
+ sourceRect,
5907
+ pixelOffset,
5908
+ viewport
5909
+ });
5910
+ if (!visible) return;
5911
+ const moveCursor = `\x1b[${visible.y + 1};${visible.x + 1}H`;
5912
+ const rgba = decodePngToRgba(pngData);
5913
+ if (rgba) write(withCursorPreserved(moveCursor + encodeSixel(rgba)), {
5914
+ owner: "image:sixel:place",
5915
+ zIndex
5916
+ });
5917
+ }
5918
+ }, [
5919
+ pngData,
5920
+ stdoutCtx,
5921
+ activeProtocol,
5922
+ effectiveWidth,
5923
+ effectiveHeight,
5924
+ placementId,
5925
+ zIndex,
5926
+ pixelOffset,
5927
+ sourceRect,
5928
+ virtualPlacement,
5929
+ boxRect.x,
5930
+ boxRect.y,
5931
+ boxRect.width,
5932
+ boxRect.height,
5933
+ viewportWidth,
5934
+ viewportHeight,
5935
+ decodedImage
5936
+ ]);
5937
+ useEffect(() => {
5938
+ const id = imageIdRef.current;
5939
+ if (activeProtocol !== "kitty" || id == null || !stdoutCtx) return;
5940
+ return () => {
5941
+ stdoutCtx.write(withCursorPreserved(deleteKittyImage(id)));
5942
+ };
5943
+ }, [activeProtocol, stdoutCtx]);
5944
+ if (!activeProtocol || !pngData) return /* @__PURE__ */ jsx(Text, { children: fallback });
5945
+ const placeholderImageId = imageIdRef.current;
5946
+ if (activeProtocol === "kitty" && virtualPlacement && placeholderImageId !== null && canRenderKittyPlaceholder({
5947
+ id: placeholderImageId,
5948
+ width: effectiveWidth,
5949
+ height: effectiveHeight
5950
+ })) return /* @__PURE__ */ jsx(Text, {
5951
+ color: `ansi256(${placeholderImageId})`,
5952
+ children: kittyPlaceholderText(effectiveWidth, effectiveHeight)
5953
+ });
5954
+ const spaceLine = " ".repeat(Math.max(0, effectiveWidth));
5955
+ return /* @__PURE__ */ jsx(Text, { children: Array.from({ length: Math.max(0, effectiveHeight) }, () => spaceLine).join("\n") });
5956
+ }
5072
5957
  //#endregion
5073
- export { require_UPNG as t };
5958
+ export { deleteKittyImage as a, isKittyGraphicsSupported as c, shallow as d, useTerm as f, isSixelSupported as i, placeKittyImage as l, decodePngToRgba as n, deleteKittyPlacement as o, encodeSixel as r, encodeKittyImage as s, Image as t, useWindowSize as u };
5074
5959
 
5075
- //# sourceMappingURL=UPNG-DosRPdF4.mjs.map
5960
+ //# sourceMappingURL=image-CIF75xMr.mjs.map