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.
- package/README.md +1 -1
- package/dist/{Text-Lq0dmj8-.mjs → Text-Ci6UCSTL.mjs} +46 -48
- package/dist/Text-Ci6UCSTL.mjs.map +1 -0
- package/dist/{render-string-DkQacASz.mjs → ag-q9lRlKlt.mjs} +5322 -4259
- package/dist/ag-q9lRlKlt.mjs.map +1 -0
- package/dist/{animation-ZMN2_XKv.mjs → animation-CZ4x2R7C.mjs} +1 -1
- package/dist/{animation-ZMN2_XKv.mjs.map → animation-CZ4x2R7C.mjs.map} +1 -1
- package/dist/{ansi-D1KQMAbf.mjs → ansi-CLmIMnop.mjs} +1 -1
- package/dist/{ansi-D1KQMAbf.mjs.map → ansi-CLmIMnop.mjs.map} +1 -1
- package/dist/{ansi-2Xn0yatP.d.mts → ansi-zmNzgkPB.d.mts} +1 -1
- package/dist/{ansi-2Xn0yatP.d.mts.map → ansi-zmNzgkPB.d.mts.map} +1 -1
- package/dist/{backend-B-WYLUib.mjs → backend-DSiSIc-h.mjs} +2 -2
- package/dist/{backend-B-WYLUib.mjs.map → backend-DSiSIc-h.mjs.map} +1 -1
- package/dist/chain-bridge-CkYcMxlq.mjs +4149 -0
- package/dist/chain-bridge-CkYcMxlq.mjs.map +1 -0
- package/dist/{chunk-BSw8zbkd.mjs → chunk-BEJ448es.mjs} +1 -3
- package/dist/cli-VNqUKlXw.mjs +4 -0
- package/dist/{context-BU5LkkIy.mjs → context-CEhuD1R_.mjs} +1 -1
- package/dist/{context-BU5LkkIy.mjs.map → context-CEhuD1R_.mjs.map} +1 -1
- package/dist/devtools-BF4IMumr.mjs +2 -0
- package/dist/{devtools-DcQjgyjL.mjs → devtools-td2AjFwc.mjs} +5 -5
- package/dist/{devtools-DcQjgyjL.mjs.map → devtools-td2AjFwc.mjs.map} +1 -1
- package/dist/{easing-BI-ASGMO.d.mts → easing-C5MrdO7V.d.mts} +1 -1
- package/dist/{easing-BI-ASGMO.d.mts.map → easing-C5MrdO7V.d.mts.map} +1 -1
- package/dist/{eta-CJlGH06n.mjs → eta-BwYWEjfS.mjs} +1 -1
- package/dist/{eta-CJlGH06n.mjs.map → eta-BwYWEjfS.mjs.map} +1 -1
- package/dist/{flexily-zero-adapter-C4lW_Ov5.mjs → flexily-zero-adapter-DktX3HsX.mjs} +1 -1
- package/dist/{flexily-zero-adapter-C3Vj0fPt.mjs → flexily-zero-adapter-VoBGHG14.mjs} +1 -1
- package/dist/{flexily-zero-adapter-C3Vj0fPt.mjs.map → flexily-zero-adapter-VoBGHG14.mjs.map} +1 -1
- package/dist/hit-registry-D_nxOivS.d.mts +71 -0
- package/dist/hit-registry-D_nxOivS.d.mts.map +1 -0
- package/dist/{UPNG-DosRPdF4.mjs → image-CIF75xMr.mjs} +891 -6
- package/dist/image-CIF75xMr.mjs.map +1 -0
- package/dist/{index-CSQf13CI.d.mts → index-377zdPLP.d.mts} +35 -12
- package/dist/index-377zdPLP.d.mts.map +1 -0
- package/dist/{index-Cl9KKjQ_.d.mts → index-BcSr0RAQ.d.mts} +8670 -10659
- package/dist/index-BcSr0RAQ.d.mts.map +1 -0
- package/dist/{index-XbNrPhWl.d.mts → index-DLnpxDAm.d.mts} +2 -2
- package/dist/{index-XbNrPhWl.d.mts.map → index-DLnpxDAm.d.mts.map} +1 -1
- package/dist/{index-BUMxS65f.d.mts → index-DlNmckTq.d.mts} +2 -2
- package/dist/{index-BUMxS65f.d.mts.map → index-DlNmckTq.d.mts.map} +1 -1
- package/dist/index.d.mts +12 -8
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +18 -15
- package/dist/index.mjs.map +1 -1
- package/dist/{layout-engine-C2px0RJE.mjs → layout-engine-BgR58vL0.mjs} +3 -3
- package/dist/{layout-engine-C2px0RJE.mjs.map → layout-engine-BgR58vL0.mjs.map} +1 -1
- package/dist/{layout-engine-C07LEXWT.mjs → layout-engine-DNjTha8f.mjs} +1 -1
- package/dist/{layout-signals-Cnw6xk8Q.mjs → layout-signals-CGYbHmxy.mjs} +146 -23
- package/dist/layout-signals-CGYbHmxy.mjs.map +1 -0
- package/dist/{bound-term-0sPrrzH1.d.mts → mouse-events-B2z5tbsL.d.mts} +780 -379
- package/dist/mouse-events-B2z5tbsL.d.mts.map +1 -0
- package/dist/{mouse-events-Dki3ISIp.mjs → mouse-events-WQ2h-uqG.mjs} +45 -10
- package/dist/mouse-events-WQ2h-uqG.mjs.map +1 -0
- package/dist/{multi-progress-DHZ2xUT2.d.mts → multi-progress-BaEFfHKP.d.mts} +2 -2
- package/dist/{multi-progress-DHZ2xUT2.d.mts.map → multi-progress-BaEFfHKP.d.mts.map} +1 -1
- package/dist/{multi-progress-CIRjrzma.mjs → multi-progress-zCWHEm9P.mjs} +3 -3
- package/dist/{multi-progress-CIRjrzma.mjs.map → multi-progress-zCWHEm9P.mjs.map} +1 -1
- package/dist/{node-CjM5Rt-M.mjs → node-CJ3J_rW-.mjs} +1 -1
- package/dist/{node-CjM5Rt-M.mjs.map → node-CJ3J_rW-.mjs.map} +1 -1
- package/dist/{progress-DB_Xo071.mjs → progress-6vJ7nxUg.mjs} +4 -4
- package/dist/{progress-DB_Xo071.mjs.map → progress-6vJ7nxUg.mjs.map} +1 -1
- package/dist/{progress-bar-oJwq22CR.mjs → progress-bar-BlFNsZL8.mjs} +4 -4
- package/dist/{progress-bar-oJwq22CR.mjs.map → progress-bar-BlFNsZL8.mjs.map} +1 -1
- package/dist/{reconciler-DldIJB93.mjs → reconciler--_-biN23.mjs} +190 -134
- package/dist/reconciler--_-biN23.mjs.map +1 -0
- package/dist/{render-string-BcoCpjCB.mjs → render-string-DaghHpLN.mjs} +1 -1
- package/dist/render-string-DtHZP0cU.mjs +215 -0
- package/dist/render-string-DtHZP0cU.mjs.map +1 -0
- package/dist/runtime.d.mts +4 -3
- package/dist/runtime.mjs +4 -3
- package/dist/schemes-DWQPxXAi.mjs +2516 -0
- package/dist/{schemes-JjNp4aSl.mjs.map → schemes-DWQPxXAi.mjs.map} +1 -1
- package/dist/{spinner-D9lrHr8s.mjs → spinner-B50hILqx.mjs} +19 -4
- package/dist/{spinner-D9lrHr8s.mjs.map → spinner-B50hILqx.mjs.map} +1 -1
- package/dist/{spinner-CZINHpkV.d.mts → spinner-DN10UfF0.d.mts} +2 -2
- package/dist/{spinner-CZINHpkV.d.mts.map → spinner-DN10UfF0.d.mts.map} +1 -1
- package/dist/{src-BNTToU7l.mjs → src-B8lf1kiR.mjs} +1507 -1166
- package/dist/src-B8lf1kiR.mjs.map +1 -0
- package/dist/{src-5w9QR6_8.mjs → src-BOLdi3WG.mjs} +105 -237
- package/dist/src-BOLdi3WG.mjs.map +1 -0
- package/dist/{src-BR4xNwdG.mjs → src-BXeTHHlL.mjs} +20240 -25437
- package/dist/src-BXeTHHlL.mjs.map +1 -0
- package/dist/{src-DKp-_OFG.mjs → src-BqeBFB3X.mjs} +90 -47
- package/dist/src-BqeBFB3X.mjs.map +1 -0
- package/dist/src-DbqcSrhy.mjs +3901 -0
- package/dist/src-DbqcSrhy.mjs.map +1 -0
- package/dist/{steps-Bp2uNqnn.d.mts → steps-B2kBmFpb.d.mts} +1 -1
- package/dist/{steps-Bp2uNqnn.d.mts.map → steps-B2kBmFpb.d.mts.map} +1 -1
- package/dist/{svg-Cz0UXcDj.mjs → svg-NmV9dgB9.mjs} +1 -1
- package/dist/{svg-Cz0UXcDj.mjs.map → svg-NmV9dgB9.mjs.map} +1 -1
- package/dist/{svg-g1D6ErwR.d.mts → svg-VUr78l5d.d.mts} +1 -1
- package/dist/{svg-g1D6ErwR.d.mts.map → svg-VUr78l5d.d.mts.map} +1 -1
- package/dist/term.d.mts +2 -2
- package/dist/term.mjs +3 -8
- package/dist/test.d.mts +673 -0
- package/dist/test.d.mts.map +1 -0
- package/dist/test.mjs +2829 -0
- package/dist/test.mjs.map +1 -0
- package/dist/theme.d.mts +2 -2
- package/dist/theme.d.mts.map +1 -1
- package/dist/theme.mjs +3 -8
- package/dist/{types-kt_fKR37.d.mts → types-D_E0FbqA.d.mts} +1 -1
- package/dist/{types-kt_fKR37.d.mts.map → types-D_E0FbqA.d.mts.map} +1 -1
- package/dist/ui/animation.d.mts +2 -2
- package/dist/ui/animation.mjs +1 -1
- package/dist/ui/ansi.d.mts +1 -1
- package/dist/ui/ansi.mjs +1 -1
- package/dist/ui/cli.d.mts +3 -3
- package/dist/ui/cli.mjs +5 -5
- package/dist/ui/image.d.mts +1 -1
- package/dist/ui/image.mjs +1 -1
- package/dist/ui/progress.d.mts +4 -4
- package/dist/ui/progress.mjs +4 -4
- package/dist/ui/react.d.mts +1 -1
- package/dist/ui/react.mjs +2 -2
- package/dist/ui/recording-chrome-react.d.mts +1 -1
- package/dist/ui/recording-chrome-react.mjs +2 -2
- package/dist/ui/recording-chrome.d.mts +1 -1
- package/dist/ui/recording-chrome.mjs +1 -1
- package/dist/ui/utils.mjs +1 -1
- package/dist/ui/wrappers.d.mts +2 -2
- package/dist/ui/wrappers.mjs +1 -1
- package/dist/ui.d.mts +6 -6
- package/dist/ui.mjs +7 -7
- package/dist/unicode-CxyxVGmB.mjs +11077 -0
- package/dist/unicode-CxyxVGmB.mjs.map +1 -0
- package/dist/{useLatest-DRDDVwjh.d.mts → useLatest-UOvWy0ZW.d.mts} +2 -2
- package/dist/{useLatest-DRDDVwjh.d.mts.map → useLatest-UOvWy0ZW.d.mts.map} +1 -1
- package/dist/useLayout-D5cE8OCI.mjs +424 -0
- package/dist/useLayout-D5cE8OCI.mjs.map +1 -0
- package/dist/{with-text-input-YeohVLeo.d.mts → with-text-input-DLoSCBrm.d.mts} +3 -3
- package/dist/{with-text-input-YeohVLeo.d.mts.map → with-text-input-DLoSCBrm.d.mts.map} +1 -1
- package/dist/wrap-measurer-registration-IV2HtcCd.d.mts +3106 -0
- package/dist/wrap-measurer-registration-IV2HtcCd.d.mts.map +1 -0
- package/dist/{wrapper-C70ATkVv.mjs → wrapper-CD29afOs.mjs} +59 -17
- package/dist/wrapper-CD29afOs.mjs.map +1 -0
- package/dist/{wrappers-BCUYITrY.mjs → wrappers-CInBJm44.mjs} +18 -8
- package/dist/wrappers-CInBJm44.mjs.map +1 -0
- package/dist/{yoga-adapter-BnZX1PAY.mjs → yoga-adapter-9uab5Qn-.mjs} +2 -2
- package/dist/{yoga-adapter-BnZX1PAY.mjs.map → yoga-adapter-9uab5Qn-.mjs.map} +1 -1
- package/dist/yoga-adapter-yqTl1aXK.mjs +2 -0
- package/package.json +59 -14
- package/dist/Text-Lq0dmj8-.mjs.map +0 -1
- package/dist/UPNG-Bo33r8rA.mjs +0 -3
- package/dist/UPNG-DosRPdF4.mjs.map +0 -1
- package/dist/__vite-browser-external-2447137e-D_JM6skp.mjs +0 -6
- package/dist/__vite-browser-external-2447137e-D_JM6skp.mjs.map +0 -1
- package/dist/ansi-yC4RyBNY.mjs +0 -22441
- package/dist/ansi-yC4RyBNY.mjs.map +0 -1
- package/dist/apng-CR08rIaH.mjs +0 -58
- package/dist/apng-CR08rIaH.mjs.map +0 -1
- package/dist/apng-DaHfVaVI.mjs +0 -3
- package/dist/assets/resvgjs.darwin-arm64-BtufyGW1.node +0 -0
- package/dist/assets/skia.darwin-arm64-DQs5sT6N.node +0 -0
- package/dist/backends-CUtan80W.mjs +0 -3
- package/dist/backends-DIVYzKqd.mjs +0 -1083
- package/dist/backends-DIVYzKqd.mjs.map +0 -1
- package/dist/bound-term-0sPrrzH1.d.mts.map +0 -1
- package/dist/canvas-1v7dPT-_.mjs +0 -3
- package/dist/canvas-CSuPOMNt.mjs +0 -1442
- package/dist/canvas-CSuPOMNt.mjs.map +0 -1
- package/dist/cli-dvo0r2fs.mjs +0 -4
- package/dist/compare-CQodSH4G.mjs +0 -376
- package/dist/compare-CQodSH4G.mjs.map +0 -1
- package/dist/compare-DHlcxEYA.mjs +0 -3
- package/dist/devtools-CJdt5H0X.mjs +0 -2
- package/dist/fonts-BFmhXDv7.mjs +0 -88
- package/dist/fonts-BFmhXDv7.mjs.map +0 -1
- package/dist/gif-C_AjaT9d.mjs +0 -188
- package/dist/gif-C_AjaT9d.mjs.map +0 -1
- package/dist/gif-DaC4XrxA.mjs +0 -3
- package/dist/gifenc-BOUT-KFB.mjs +0 -730
- package/dist/gifenc-BOUT-KFB.mjs.map +0 -1
- package/dist/image-C2Birh2x.mjs +0 -1252
- package/dist/image-C2Birh2x.mjs.map +0 -1
- package/dist/index-CSQf13CI.d.mts.map +0 -1
- package/dist/index-Cl9KKjQ_.d.mts.map +0 -1
- package/dist/key-mapping-CS-YD_cD.mjs +0 -132
- package/dist/key-mapping-CS-YD_cD.mjs.map +0 -1
- package/dist/key-mapping-Yn-Jgrij.mjs +0 -3
- package/dist/layout-signals-Cnw6xk8Q.mjs.map +0 -1
- package/dist/mouse-events-Dki3ISIp.mjs.map +0 -1
- package/dist/playwright-D5YiZcNS.mjs +0 -76397
- package/dist/playwright-D5YiZcNS.mjs.map +0 -1
- package/dist/png-codec-Dp84742B.mjs +0 -36
- package/dist/png-codec-Dp84742B.mjs.map +0 -1
- package/dist/png-codec-QwOtJ8Zs.mjs +0 -3
- package/dist/rasterizer-BRXrDdWx.mjs +0 -3
- package/dist/rasterizer-CpEhJvdR.mjs +0 -296
- package/dist/rasterizer-CpEhJvdR.mjs.map +0 -1
- package/dist/reconciler-DldIJB93.mjs.map +0 -1
- package/dist/render-string-DkQacASz.mjs.map +0 -1
- package/dist/resvg-js-DkOndZI3.mjs +0 -203
- package/dist/resvg-js-DkOndZI3.mjs.map +0 -1
- package/dist/schemes-JjNp4aSl.mjs +0 -2611
- package/dist/src-5w9QR6_8.mjs.map +0 -1
- package/dist/src-BNTToU7l.mjs.map +0 -1
- package/dist/src-BR4xNwdG.mjs.map +0 -1
- package/dist/src-DKp-_OFG.mjs.map +0 -1
- package/dist/src-bt8wSrfJ.mjs +0 -258
- package/dist/src-bt8wSrfJ.mjs.map +0 -1
- package/dist/src-e33Y6kNJ.mjs +0 -3
- package/dist/src-iDwu25UD.mjs +0 -1814
- package/dist/src-iDwu25UD.mjs.map +0 -1
- package/dist/svg-15lZZzxq.mjs +0 -486
- package/dist/svg-15lZZzxq.mjs.map +0 -1
- package/dist/svg-DY72a4HK.mjs +0 -3
- package/dist/term.mjs.map +0 -1
- package/dist/theme.mjs.map +0 -1
- package/dist/ui/display.d.mts +0 -35
- package/dist/ui/display.d.mts.map +0 -1
- package/dist/ui/display.mjs +0 -123
- package/dist/ui/display.mjs.map +0 -1
- package/dist/ui/input.d.mts +0 -184
- package/dist/ui/input.d.mts.map +0 -1
- package/dist/ui/input.mjs +0 -285
- package/dist/ui/input.mjs.map +0 -1
- package/dist/wrapper-C70ATkVv.mjs.map +0 -1
- package/dist/wrappers-BCUYITrY.mjs.map +0 -1
- package/dist/yoga-adapter-DxgsQ_gg.mjs +0 -2
- package/dist/zipBundle-3nqeDRtm.mjs +0 -3
- package/dist/zipBundle-VNAYFmqJ.mjs +0 -2003
- package/dist/zipBundle-VNAYFmqJ.mjs.map +0 -1
|
@@ -1,4 +1,302 @@
|
|
|
1
|
-
import { i as __require, t as __commonJSMin } from "./chunk-
|
|
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
|
|
3917
|
-
|
|
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 {
|
|
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=
|
|
5960
|
+
//# sourceMappingURL=image-CIF75xMr.mjs.map
|