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
|
@@ -0,0 +1,3106 @@
|
|
|
1
|
+
import { D as enableKittyKeyboard$1, E as disableMouse$1, F as TerminalCaps, O as enableMouse$1, T as disableKittyKeyboard$1, j as ColorLevel } from "./index-377zdPLP.mjs";
|
|
2
|
+
import { Ct as Decoration, Ht as LayoutNode$1, Q as UnderlineStyle, S as Term, Tt as EventSource, X as Style, Z as TerminalBuffer, _ as BoundTerm, gt as AgNodeType, ht as AgNode, jt as Rect, nt as FrameCell, ut as FocusManager, wt as Event, xt as CursorShape$1 } from "./mouse-events-B2z5tbsL.mjs";
|
|
3
|
+
import React, { ReactElement, ReactNode } from "react";
|
|
4
|
+
|
|
5
|
+
//#region packages/ag-term/src/unicode.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Run a function with a specific measurer as the active scope.
|
|
8
|
+
* Module-level convenience functions (graphemeWidth, displayWidth, etc.)
|
|
9
|
+
* will use this measurer instead of the lazy default for the duration.
|
|
10
|
+
*/
|
|
11
|
+
declare function runWithMeasurer<T>(measurer: Measurer, fn: () => T): T;
|
|
12
|
+
/**
|
|
13
|
+
* Check if text sizing mode is currently enabled.
|
|
14
|
+
* Returns the default (false) since globals have been removed.
|
|
15
|
+
* Use measurer.textSizing for scoped queries.
|
|
16
|
+
*/
|
|
17
|
+
declare function isTextSizingEnabled(): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Width measurement functions scoped to specific terminal capabilities.
|
|
20
|
+
* Created by createMeasurer() from TerminalCaps.
|
|
21
|
+
*/
|
|
22
|
+
interface Measurer {
|
|
23
|
+
readonly maybeWideEmojis: boolean;
|
|
24
|
+
readonly textSizing: boolean;
|
|
25
|
+
/** Height of one line in measurement units. Terminal: 1 (one cell row). Canvas pixel: e.g. 20. */
|
|
26
|
+
readonly lineHeight: number;
|
|
27
|
+
displayWidth(text: string): number;
|
|
28
|
+
displayWidthAnsi(text: string): number;
|
|
29
|
+
graphemeWidth(grapheme: string): number;
|
|
30
|
+
wrapText(text: string, width: number, trim?: boolean, hard?: boolean, truncateAtomicOverflow?: boolean): string[];
|
|
31
|
+
sliceByWidth(text: string, maxWidth: number): string;
|
|
32
|
+
sliceByWidthFromEnd(text: string, maxWidth: number): string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Create a width measurer scoped to terminal capabilities.
|
|
36
|
+
* Each measurer has its own caches (no shared global state).
|
|
37
|
+
*/
|
|
38
|
+
declare function createMeasurer(caps?: {
|
|
39
|
+
maybeWideEmojis?: boolean;
|
|
40
|
+
textSizing?: boolean;
|
|
41
|
+
}): Measurer;
|
|
42
|
+
/**
|
|
43
|
+
* Split a string into grapheme clusters.
|
|
44
|
+
* Each grapheme is a user-perceived character that may consist of
|
|
45
|
+
* multiple Unicode code points.
|
|
46
|
+
*
|
|
47
|
+
* Examples:
|
|
48
|
+
* - "cafe\u0301" (café with combining accent) -> ["c", "a", "f", "e\u0301"]
|
|
49
|
+
* - "👨👩👧" (family emoji) -> ["👨👩👧"]
|
|
50
|
+
* - "한국어" -> ["한", "국", "어"]
|
|
51
|
+
*/
|
|
52
|
+
declare function splitGraphemes(text: string): string[];
|
|
53
|
+
/**
|
|
54
|
+
* Count the number of graphemes in a string.
|
|
55
|
+
*/
|
|
56
|
+
declare function graphemeCount(text: string): number;
|
|
57
|
+
/**
|
|
58
|
+
* Append VS16 (U+FE0F) to emoji characters that have default text presentation.
|
|
59
|
+
*
|
|
60
|
+
* Use this to normalize icon characters for consistent terminal rendering.
|
|
61
|
+
* Characters that already have emoji presentation or VS16 are returned unchanged.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```ts
|
|
65
|
+
* ensureEmojiPresentation('⚠') // '⚠\uFE0F' (⚠️)
|
|
66
|
+
* ensureEmojiPresentation('☑') // '☑\uFE0F' (☑️)
|
|
67
|
+
* ensureEmojiPresentation('☐') // '☐' (unchanged, not an emoji)
|
|
68
|
+
* ensureEmojiPresentation('📁') // '📁' (unchanged, already emoji presentation)
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
declare function ensureEmojiPresentation(char: string): string;
|
|
72
|
+
/**
|
|
73
|
+
* Get the display width of a string (number of terminal columns).
|
|
74
|
+
* Uses string-width which handles:
|
|
75
|
+
* - Wide characters (CJK) -> 2 columns
|
|
76
|
+
* - Regular ASCII -> 1 column
|
|
77
|
+
* - Zero-width characters (combining, ZWJ) -> 0 columns
|
|
78
|
+
* - Emoji -> varies (1 or 2)
|
|
79
|
+
* - ANSI escape sequences -> 0 columns (stripped)
|
|
80
|
+
*
|
|
81
|
+
* Corrects string-width for text-presentation emoji characters
|
|
82
|
+
* (e.g., ⚠ U+26A0) that terminals render as 2 columns wide.
|
|
83
|
+
*
|
|
84
|
+
* Results are cached for performance.
|
|
85
|
+
*/
|
|
86
|
+
declare function displayWidth(text: string): number;
|
|
87
|
+
/**
|
|
88
|
+
* Get the display width of a single grapheme.
|
|
89
|
+
*
|
|
90
|
+
* Overrides string-width for characters that are Extended_Pictographic with
|
|
91
|
+
* default text presentation. These characters (e.g., ⚠ U+26A0, ☑ U+2611)
|
|
92
|
+
* are reported as width 1 by string-width (per Unicode EAW tables), but most
|
|
93
|
+
* modern terminals render them as 2 columns wide using emoji glyphs.
|
|
94
|
+
*
|
|
95
|
+
* The mismatch causes text after these characters to be placed at the wrong
|
|
96
|
+
* column, leading to truncation or overlap.
|
|
97
|
+
*/
|
|
98
|
+
declare function graphemeWidth(grapheme: string): number;
|
|
99
|
+
/**
|
|
100
|
+
* Check if a grapheme is a wide character (takes 2 columns).
|
|
101
|
+
*/
|
|
102
|
+
declare function isWideGrapheme(grapheme: string): boolean;
|
|
103
|
+
/**
|
|
104
|
+
* Check if a grapheme is zero-width (combining character, ZWJ, etc.).
|
|
105
|
+
*/
|
|
106
|
+
declare function isZeroWidthGrapheme(grapheme: string): boolean;
|
|
107
|
+
/**
|
|
108
|
+
* Truncate a string to fit within a given display width.
|
|
109
|
+
* Handles wide characters and ANSI escape sequences (including OSC 8 hyperlinks) correctly.
|
|
110
|
+
*
|
|
111
|
+
* @param text - The text to truncate (may contain ANSI escape sequences)
|
|
112
|
+
* @param maxWidth - Maximum display width
|
|
113
|
+
* @param ellipsis - Ellipsis to append if truncated (default: "...")
|
|
114
|
+
* @returns Truncated string
|
|
115
|
+
*/
|
|
116
|
+
declare function truncateText(text: string, maxWidth: number, ellipsis?: string): string;
|
|
117
|
+
/**
|
|
118
|
+
* Pad a string to a given display width.
|
|
119
|
+
*
|
|
120
|
+
* @param text - The text to pad
|
|
121
|
+
* @param width - Target display width
|
|
122
|
+
* @param align - Alignment: 'left', 'right', or 'center'
|
|
123
|
+
* @param padChar - Character to use for padding (default: space)
|
|
124
|
+
* @returns Padded string
|
|
125
|
+
*/
|
|
126
|
+
declare function padText(text: string, width: number, align?: "left" | "right" | "center", padChar?: string): string;
|
|
127
|
+
/**
|
|
128
|
+
* Constrain text to width and height limits.
|
|
129
|
+
* Combines wrapping and truncation to fit text in a box.
|
|
130
|
+
*
|
|
131
|
+
* @param text - Text to constrain (may contain ANSI codes)
|
|
132
|
+
* @param width - Maximum display width per line
|
|
133
|
+
* @param maxLines - Maximum number of lines
|
|
134
|
+
* @param pad - If true, pad lines to full width
|
|
135
|
+
* @param ellipsis - Custom ellipsis character (default: "…")
|
|
136
|
+
* @returns Object with lines array and truncated flag
|
|
137
|
+
*/
|
|
138
|
+
declare function constrainText(text: string, width: number, maxLines: number, pad?: boolean, ellipsis?: string): {
|
|
139
|
+
lines: string[];
|
|
140
|
+
truncated: boolean;
|
|
141
|
+
};
|
|
142
|
+
/**
|
|
143
|
+
* Wrap text to fit within a given width.
|
|
144
|
+
*
|
|
145
|
+
* Implements word-boundary wrapping:
|
|
146
|
+
* 1. Breaks at word boundaries (spaces, hyphens) when possible
|
|
147
|
+
* 2. Falls back to character wrap only when necessary (very long words)
|
|
148
|
+
* 3. Handles CJK text properly (can break anywhere since CJK has no word spaces)
|
|
149
|
+
* 4. Preserves intentional line breaks
|
|
150
|
+
*
|
|
151
|
+
* @param text - The text to wrap (may contain ANSI escape sequences)
|
|
152
|
+
* @param width - Maximum display width per line
|
|
153
|
+
* @param preserveNewlines - Whether to preserve existing newlines
|
|
154
|
+
* @param trim - Trim trailing spaces on broken lines and skip leading spaces on continuation lines (useful for rendering)
|
|
155
|
+
* @param truncateAtomicOverflow - When true, an unbreakable token wider than
|
|
156
|
+
* `width` (no soft-break separator) ends with a Unicode `…` ellipsis on
|
|
157
|
+
* the offending line and the rest of the token is dropped, instead of
|
|
158
|
+
* character-wrapping. Implements the `wrap="wrap-truncate"` mode —
|
|
159
|
+
* CSS-equivalent `overflow-wrap: break-word` + `text-overflow: ellipsis`.
|
|
160
|
+
* Default `false` preserves existing character-wrap fallback (`wrap="wrap"`).
|
|
161
|
+
* @returns Array of wrapped lines
|
|
162
|
+
*/
|
|
163
|
+
declare function wrapText(text: string, width: number, preserveNewlines?: boolean, trim?: boolean, truncateAtomicOverflow?: boolean): string[];
|
|
164
|
+
/**
|
|
165
|
+
* Slice text by display width (from start).
|
|
166
|
+
* Returns the first `maxWidth` columns of text.
|
|
167
|
+
* Uses the default measurer for width calculations.
|
|
168
|
+
* Handles both ANSI-styled and plain text.
|
|
169
|
+
*
|
|
170
|
+
* @param text - The text to slice
|
|
171
|
+
* @param maxWidth - Maximum display width to keep from the start
|
|
172
|
+
* @returns Sliced string from the start
|
|
173
|
+
*/
|
|
174
|
+
declare function sliceByWidth(text: string, maxWidth: number): string;
|
|
175
|
+
/**
|
|
176
|
+
* Slice a string by display width range.
|
|
177
|
+
* Like string.slice() but works with display columns.
|
|
178
|
+
*
|
|
179
|
+
* @param text - The text to slice
|
|
180
|
+
* @param start - Start display column (inclusive)
|
|
181
|
+
* @param end - End display column (exclusive)
|
|
182
|
+
* @returns Sliced string
|
|
183
|
+
*/
|
|
184
|
+
declare function sliceByWidthRange(text: string, start: number, end?: number): string;
|
|
185
|
+
/**
|
|
186
|
+
* Slice text by display width from the end.
|
|
187
|
+
* Returns the last `maxWidth` columns of text.
|
|
188
|
+
* Uses the default measurer for width calculations.
|
|
189
|
+
*
|
|
190
|
+
* @param text - The text to slice
|
|
191
|
+
* @param maxWidth - Maximum display width to keep from the end
|
|
192
|
+
* @returns Sliced string from the end
|
|
193
|
+
*/
|
|
194
|
+
declare function sliceByWidthFromEnd(text: string, maxWidth: number): string;
|
|
195
|
+
/**
|
|
196
|
+
* Write styled text to a terminal buffer.
|
|
197
|
+
*
|
|
198
|
+
* Handles:
|
|
199
|
+
* - Multi-byte graphemes (emoji, combining characters)
|
|
200
|
+
* - Wide characters (CJK) that take 2 cells
|
|
201
|
+
* - Zero-width characters (appended to previous cell)
|
|
202
|
+
*
|
|
203
|
+
* @param buffer - The buffer to write to
|
|
204
|
+
* @param x - Starting column
|
|
205
|
+
* @param y - Row
|
|
206
|
+
* @param text - Text to write
|
|
207
|
+
* @param style - Style to apply
|
|
208
|
+
* @returns The ending column (x + display_width)
|
|
209
|
+
*/
|
|
210
|
+
declare function writeTextToBuffer(buffer: TerminalBuffer, x: number, y: number, text: string, style?: Style): number;
|
|
211
|
+
/**
|
|
212
|
+
* Write styled text to a buffer with automatic truncation.
|
|
213
|
+
*
|
|
214
|
+
* @param buffer - The buffer to write to
|
|
215
|
+
* @param x - Starting column
|
|
216
|
+
* @param y - Row
|
|
217
|
+
* @param text - Text to write
|
|
218
|
+
* @param maxWidth - Maximum width (truncate if exceeded)
|
|
219
|
+
* @param style - Style to apply
|
|
220
|
+
* @param ellipsis - Ellipsis for truncated text
|
|
221
|
+
*/
|
|
222
|
+
declare function writeTextTruncated(buffer: TerminalBuffer, x: number, y: number, text: string, maxWidth: number, style?: Style, ellipsis?: string): void;
|
|
223
|
+
/**
|
|
224
|
+
* Write multiple lines of styled text to a buffer.
|
|
225
|
+
*
|
|
226
|
+
* @param buffer - The buffer to write to
|
|
227
|
+
* @param x - Starting column
|
|
228
|
+
* @param y - Starting row
|
|
229
|
+
* @param lines - Lines to write
|
|
230
|
+
* @param style - Style to apply
|
|
231
|
+
*/
|
|
232
|
+
declare function writeLinesToBuffer(buffer: TerminalBuffer, x: number, y: number, lines: string[], style?: Style): void;
|
|
233
|
+
/**
|
|
234
|
+
* Strip all ANSI escape codes from a string.
|
|
235
|
+
*
|
|
236
|
+
* Handles:
|
|
237
|
+
* - CSI sequences (cursor movement, colors, SGR, etc.)
|
|
238
|
+
* - OSC sequences (window titles, hyperlinks)
|
|
239
|
+
* - Single-character escape sequences
|
|
240
|
+
* - Character set selection
|
|
241
|
+
*/
|
|
242
|
+
declare function stripAnsi(text: string): string;
|
|
243
|
+
/**
|
|
244
|
+
* Get display width of text with ANSI sequences.
|
|
245
|
+
* ANSI sequences don't contribute to display width.
|
|
246
|
+
*/
|
|
247
|
+
declare function displayWidthAnsi(text: string): number;
|
|
248
|
+
/**
|
|
249
|
+
* Truncate text that may contain ANSI sequences.
|
|
250
|
+
* Preserves ANSI codes while truncating visible characters.
|
|
251
|
+
*
|
|
252
|
+
* Note: This is a simplified implementation that strips ANSI before
|
|
253
|
+
* truncation. For proper ANSI-aware truncation, consider using
|
|
254
|
+
* slice-ansi or similar library.
|
|
255
|
+
*/
|
|
256
|
+
declare function truncateAnsi(text: string, maxWidth: number, ellipsis?: string): string;
|
|
257
|
+
/** Styled text segment with associated ANSI colors/attributes */
|
|
258
|
+
interface StyledSegment {
|
|
259
|
+
text: string;
|
|
260
|
+
fg?: number | null;
|
|
261
|
+
bg?: number | null;
|
|
262
|
+
/**
|
|
263
|
+
* Underline color (SGR 58).
|
|
264
|
+
* Same format as fg/bg: packed RGB with 0x1000000 marker, or 256-color index.
|
|
265
|
+
*/
|
|
266
|
+
underlineColor?: number | null;
|
|
267
|
+
bold?: boolean;
|
|
268
|
+
dim?: boolean;
|
|
269
|
+
italic?: boolean;
|
|
270
|
+
underline?: boolean;
|
|
271
|
+
/**
|
|
272
|
+
* Underline style variant (SGR 4:x).
|
|
273
|
+
* Uses UnderlineStyle from buffer.ts.
|
|
274
|
+
*/
|
|
275
|
+
underlineStyle?: UnderlineStyle;
|
|
276
|
+
inverse?: boolean;
|
|
277
|
+
/** Strikethrough (SGR 9 on, SGR 29 off). */
|
|
278
|
+
strikethrough?: boolean;
|
|
279
|
+
/** Overline (SGR 53 on, SGR 55 off). */
|
|
280
|
+
overline?: boolean;
|
|
281
|
+
bgOverride?: boolean;
|
|
282
|
+
/**
|
|
283
|
+
* OSC 8 hyperlink URL.
|
|
284
|
+
* Set when the segment is inside an OSC 8 hyperlink sequence.
|
|
285
|
+
*/
|
|
286
|
+
hyperlink?: string;
|
|
287
|
+
/**
|
|
288
|
+
* True when the foreground color was specified using colon-separated SGR
|
|
289
|
+
* (e.g., `38:2::255:100:0m` instead of `38;2;255;100;0m`).
|
|
290
|
+
* Used to preserve the original format in round-trip output.
|
|
291
|
+
*/
|
|
292
|
+
colonFg?: boolean;
|
|
293
|
+
/**
|
|
294
|
+
* True when the background color was specified using colon-separated SGR.
|
|
295
|
+
*/
|
|
296
|
+
colonBg?: boolean;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Parse text with ANSI escape sequences into styled segments.
|
|
300
|
+
* Handles basic SGR (Select Graphic Rendition) codes including:
|
|
301
|
+
* - Standard colors (30-37, 40-47, 90-97, 100-107)
|
|
302
|
+
* - Extended colors (38;5;N, 48;5;N for 256-color, 38;2;r;g;b, 48;2;r;g;b for RGB)
|
|
303
|
+
* - Underline styles (4:x where x = 0-5)
|
|
304
|
+
* - Underline color (58;5;N for 256-color, 58;2;r;g;b for RGB)
|
|
305
|
+
*/
|
|
306
|
+
declare function parseAnsiText(text: string): StyledSegment[];
|
|
307
|
+
/**
|
|
308
|
+
* Check if text contains ANSI escape sequences (SGR or OSC).
|
|
309
|
+
* Detects both ESC-based (7-bit) and C1 (8-bit) forms:
|
|
310
|
+
* - ESC [ params final (CSI)
|
|
311
|
+
* - ESC ] (OSC)
|
|
312
|
+
* - U+009B params final (C1 CSI)
|
|
313
|
+
* - U+009D (C1 OSC)
|
|
314
|
+
*/
|
|
315
|
+
declare function hasAnsi(text: string): boolean;
|
|
316
|
+
/**
|
|
317
|
+
* Measure the dimensions of multi-line text.
|
|
318
|
+
*
|
|
319
|
+
* @param text - Text to measure (may contain newlines)
|
|
320
|
+
* @returns { width, height } in display columns and rows
|
|
321
|
+
*/
|
|
322
|
+
declare function measureText(text: string): {
|
|
323
|
+
width: number;
|
|
324
|
+
height: number;
|
|
325
|
+
};
|
|
326
|
+
/**
|
|
327
|
+
* Check if a string contains any wide characters.
|
|
328
|
+
*/
|
|
329
|
+
declare function hasWideCharacters(text: string): boolean;
|
|
330
|
+
/**
|
|
331
|
+
* Check if a string contains any combining/zero-width characters.
|
|
332
|
+
*/
|
|
333
|
+
declare function hasZeroWidthCharacters(text: string): boolean;
|
|
334
|
+
/**
|
|
335
|
+
* Normalize string for consistent handling.
|
|
336
|
+
* Applies Unicode NFC normalization.
|
|
337
|
+
*/
|
|
338
|
+
declare function normalizeText(text: string): string;
|
|
339
|
+
/**
|
|
340
|
+
* Get the first code point of a string.
|
|
341
|
+
*/
|
|
342
|
+
declare function getFirstCodePoint(str: string): number;
|
|
343
|
+
/**
|
|
344
|
+
* Check if a grapheme is likely an emoji.
|
|
345
|
+
* Note: This is a heuristic, not comprehensive.
|
|
346
|
+
*/
|
|
347
|
+
declare function isLikelyEmoji(grapheme: string): boolean;
|
|
348
|
+
/**
|
|
349
|
+
* Check if a grapheme is a CJK character.
|
|
350
|
+
*/
|
|
351
|
+
declare function isCJK(grapheme: string): boolean;
|
|
352
|
+
//#endregion
|
|
353
|
+
//#region packages/ag-term/src/clipboard.d.ts
|
|
354
|
+
/**
|
|
355
|
+
* Clipboard Backend Abstraction
|
|
356
|
+
*
|
|
357
|
+
* Pluggable clipboard system with support for multiple backends.
|
|
358
|
+
* The default backend uses OSC 52 for terminal clipboard access.
|
|
359
|
+
*
|
|
360
|
+
* Architecture:
|
|
361
|
+
* - ClipboardBackend: interface for clipboard read/write
|
|
362
|
+
* - ClipboardData: multi-format clipboard content (text, markdown, html, internal)
|
|
363
|
+
* - createOsc52Backend: OSC 52 terminal clipboard (default)
|
|
364
|
+
* - createInternalClipboardBackend: in-memory store for rich app-internal paste
|
|
365
|
+
* - createCompositeClipboard: fan-out writes to multiple backends
|
|
366
|
+
*
|
|
367
|
+
* OSC 52 Protocol:
|
|
368
|
+
* - Copy: ESC ] 52 ; c ; <base64> BEL
|
|
369
|
+
* - Query: ESC ] 52 ; c ; ? BEL
|
|
370
|
+
* - Response: ESC ] 52 ; c ; <base64> BEL (or ST terminator)
|
|
371
|
+
*
|
|
372
|
+
* Supported by: Ghostty, Kitty, WezTerm, iTerm2, xterm, foot, tmux
|
|
373
|
+
*/
|
|
374
|
+
/**
|
|
375
|
+
* Multi-format clipboard content.
|
|
376
|
+
*
|
|
377
|
+
* Plain text is always present. Optional rich formats allow applications
|
|
378
|
+
* to provide structured data for within-app paste without losing it
|
|
379
|
+
* through the plain-text-only system clipboard.
|
|
380
|
+
*/
|
|
381
|
+
interface ClipboardData {
|
|
382
|
+
/** Plain text content (always present) */
|
|
383
|
+
text: string;
|
|
384
|
+
/** Markdown representation */
|
|
385
|
+
markdown?: string;
|
|
386
|
+
/** HTML representation */
|
|
387
|
+
html?: string;
|
|
388
|
+
/** App-specific structured data (e.g., node tree for structured paste) */
|
|
389
|
+
internal?: unknown;
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Clipboard backend capabilities.
|
|
393
|
+
*
|
|
394
|
+
* `text` is always true — every backend supports plain text.
|
|
395
|
+
* Rich format support is backend-dependent.
|
|
396
|
+
*/
|
|
397
|
+
interface ClipboardCapabilities {
|
|
398
|
+
readonly text: true;
|
|
399
|
+
readonly html?: boolean;
|
|
400
|
+
readonly markdown?: boolean;
|
|
401
|
+
readonly internal?: boolean;
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Pluggable clipboard backend.
|
|
405
|
+
*
|
|
406
|
+
* Backends handle the transport of clipboard data to/from the system
|
|
407
|
+
* or an in-memory store. The framework writes ClipboardData; the backend
|
|
408
|
+
* decides what formats it can actually carry.
|
|
409
|
+
*/
|
|
410
|
+
interface ClipboardBackend {
|
|
411
|
+
/** Write clipboard data. Backends may ignore formats they don't support. */
|
|
412
|
+
write(data: ClipboardData): void | Promise<void>;
|
|
413
|
+
/** Read clipboard contents as plain text. Not all backends support read. */
|
|
414
|
+
read?(): Promise<string>;
|
|
415
|
+
/** What formats this backend supports */
|
|
416
|
+
readonly capabilities: ClipboardCapabilities;
|
|
417
|
+
}
|
|
418
|
+
/** Minimal writable interface for clipboard output */
|
|
419
|
+
interface Writable {
|
|
420
|
+
write(data: string): boolean | void;
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Upper bound on the decoded size of an OSC 52 clipboard payload.
|
|
424
|
+
*
|
|
425
|
+
* OSC 52 responses are attacker-influenceable: a hostile program on the far end
|
|
426
|
+
* of an SSH session, or a malicious terminal emulator, can emit an arbitrary
|
|
427
|
+
* base64 blob that arrives on stdin and is decoded here (see the
|
|
428
|
+
* `input-owner.ts` clipboard-response path that fires it as a paste). Node's
|
|
429
|
+
* base64 decoder never throws and imposes no size limit, so without a cap a
|
|
430
|
+
* single response could force an arbitrarily large allocation and then be fed
|
|
431
|
+
* into a focused guest as pasted input. Real clipboards are small — terminals
|
|
432
|
+
* themselves cap OSC 52 payloads around ~100 KB (see the `createOsc52Backend`
|
|
433
|
+
* quirks note) — so 1 MiB sits comfortably above any legitimate clipboard while
|
|
434
|
+
* keeping the decode bounded.
|
|
435
|
+
*/
|
|
436
|
+
/**
|
|
437
|
+
* Create an OSC 52 clipboard backend.
|
|
438
|
+
*
|
|
439
|
+
* Writes plain text to the system clipboard via the terminal's OSC 52 support.
|
|
440
|
+
* Works across SSH sessions. Rich formats (markdown, html, internal) are
|
|
441
|
+
* silently ignored — OSC 52 only carries plain text.
|
|
442
|
+
*
|
|
443
|
+
* Quirks:
|
|
444
|
+
* - Some terminals limit payload size (~100KB)
|
|
445
|
+
* - tmux requires `set -g set-clipboard on`
|
|
446
|
+
* - Some terminals only support BEL terminator (not ST)
|
|
447
|
+
*/
|
|
448
|
+
declare function createOsc52Backend(stdout: Writable): ClipboardBackend;
|
|
449
|
+
/**
|
|
450
|
+
* In-memory clipboard store for within-app paste.
|
|
451
|
+
*
|
|
452
|
+
* Stores the full ClipboardData including rich formats that OSC 52 can't carry.
|
|
453
|
+
* Used alongside OSC 52 so plain text goes to the system clipboard while
|
|
454
|
+
* rich data is available for internal paste operations.
|
|
455
|
+
*/
|
|
456
|
+
declare function createInternalClipboardBackend(): ClipboardBackend & {
|
|
457
|
+
/** Get the stored clipboard data, or null if empty */getData(): ClipboardData | null; /** Get the timestamp of the last write */
|
|
458
|
+
getTimestamp(): number;
|
|
459
|
+
};
|
|
460
|
+
/**
|
|
461
|
+
* Create a composite clipboard that writes to multiple backends.
|
|
462
|
+
*
|
|
463
|
+
* Writes fan out to all backends. Reads come from the first backend
|
|
464
|
+
* that supports read (in order). This lets you do OSC 52 + internal
|
|
465
|
+
* store simultaneously: plain text goes to system clipboard, rich
|
|
466
|
+
* data stays in memory for structured paste.
|
|
467
|
+
*/
|
|
468
|
+
declare function createCompositeClipboard(...backends: ClipboardBackend[]): ClipboardBackend;
|
|
469
|
+
/**
|
|
470
|
+
* Parse an OSC 52 clipboard response and decode the base64 content.
|
|
471
|
+
*
|
|
472
|
+
* Return semantics (see {@link ProtocolError} for the full contract):
|
|
473
|
+
* - `null` — input is NOT an OSC 52 clipboard response (no prefix, or a
|
|
474
|
+
* query marker `?` rather than a response). Callers in a discriminator
|
|
475
|
+
* chain treat this as "next parser please."
|
|
476
|
+
* - `throw ProtocolError` — input HAS the OSC 52 prefix (we committed to
|
|
477
|
+
* this protocol) but is malformed (e.g., missing terminator). Loud
|
|
478
|
+
* failure is required by bead 15127 acceptance line 22.
|
|
479
|
+
*
|
|
480
|
+
* Handles both BEL (\x07) and ST (ESC \) terminators.
|
|
481
|
+
*/
|
|
482
|
+
declare function parseClipboardResponse(input: string): string | null;
|
|
483
|
+
//#endregion
|
|
484
|
+
//#region packages/headless/src/pointer.d.ts
|
|
485
|
+
interface Position {
|
|
486
|
+
x: number;
|
|
487
|
+
y: number;
|
|
488
|
+
}
|
|
489
|
+
//#endregion
|
|
490
|
+
//#region packages/ag-term/src/drag-events.d.ts
|
|
491
|
+
/**
|
|
492
|
+
* Active drag session state.
|
|
493
|
+
* Created when pointer state machine emits startDrag, updated on updateDrag,
|
|
494
|
+
* cleared on finishDrag/cancelDrag.
|
|
495
|
+
*/
|
|
496
|
+
interface DragState {
|
|
497
|
+
/** Whether a drag is currently active */
|
|
498
|
+
active: boolean;
|
|
499
|
+
/** The node being dragged (has draggable=true) */
|
|
500
|
+
source: AgNode;
|
|
501
|
+
/** Terminal position where the drag started */
|
|
502
|
+
startPos: Position;
|
|
503
|
+
/** Current drag position (updated on every pointer move) */
|
|
504
|
+
currentPos: Position;
|
|
505
|
+
/** Node currently under the cursor that accepts drops (has onDrop handler), or null */
|
|
506
|
+
dropTarget: AgNode | null;
|
|
507
|
+
}
|
|
508
|
+
//#endregion
|
|
509
|
+
//#region packages/headless/src/copy-mode.d.ts
|
|
510
|
+
/**
|
|
511
|
+
* Copy-mode state machine — pure TEA `(action, state) → [state, effects[]]`.
|
|
512
|
+
*
|
|
513
|
+
* Keyboard-driven selection mode (vim-like visual mode).
|
|
514
|
+
* When active, h/j/k/l navigate a cursor, v/V toggle visual selection,
|
|
515
|
+
* y yanks (copies) the selection and exits.
|
|
516
|
+
*/
|
|
517
|
+
interface CopyModePosition {
|
|
518
|
+
col: number;
|
|
519
|
+
row: number;
|
|
520
|
+
}
|
|
521
|
+
interface CopyModeState {
|
|
522
|
+
/** Whether copy-mode is active */
|
|
523
|
+
active: boolean;
|
|
524
|
+
/** Current cursor position */
|
|
525
|
+
cursor: CopyModePosition;
|
|
526
|
+
/** Character-wise visual mode (v) */
|
|
527
|
+
visual: boolean;
|
|
528
|
+
/** Line-wise visual mode (V) */
|
|
529
|
+
visualLine: boolean;
|
|
530
|
+
/** Start of visual selection (set when visual mode is entered) */
|
|
531
|
+
anchor: CopyModePosition | null;
|
|
532
|
+
/** Buffer dimensions for boundary clamping */
|
|
533
|
+
bufferWidth: number;
|
|
534
|
+
bufferHeight: number;
|
|
535
|
+
}
|
|
536
|
+
//#endregion
|
|
537
|
+
//#region packages/ag-term/src/output.d.ts
|
|
538
|
+
/**
|
|
539
|
+
* Generate ANSI sequence to move cursor to position.
|
|
540
|
+
* Terminal positions are 1-indexed.
|
|
541
|
+
*/
|
|
542
|
+
declare function moveCursor$1(x: number, y: number): string;
|
|
543
|
+
/**
|
|
544
|
+
* Generate ANSI sequence to move cursor up N lines.
|
|
545
|
+
*/
|
|
546
|
+
declare function cursorUp(n: number): string;
|
|
547
|
+
/**
|
|
548
|
+
* Generate ANSI sequence to move cursor down N lines.
|
|
549
|
+
*/
|
|
550
|
+
declare function cursorDown(n: number): string;
|
|
551
|
+
/**
|
|
552
|
+
* Generate ANSI sequence to move cursor right N columns.
|
|
553
|
+
*/
|
|
554
|
+
declare function cursorRight(n: number): string;
|
|
555
|
+
/**
|
|
556
|
+
* Generate ANSI sequence to move cursor left N columns.
|
|
557
|
+
*/
|
|
558
|
+
declare function cursorLeft(n: number): string;
|
|
559
|
+
/**
|
|
560
|
+
* Generate ANSI sequence to move cursor to column.
|
|
561
|
+
*/
|
|
562
|
+
declare function cursorToColumn(x: number): string;
|
|
563
|
+
/**
|
|
564
|
+
* Terminal cursor shape. Combined with blink parameter in setCursorStyle().
|
|
565
|
+
*/
|
|
566
|
+
type CursorShape = "block" | "underline" | "bar";
|
|
567
|
+
/**
|
|
568
|
+
* Set the terminal cursor shape via DECSCUSR (CSI Ps SP q).
|
|
569
|
+
*
|
|
570
|
+
* Supported by: xterm, Ghostty, Kitty, WezTerm, iTerm2, Alacritty, foot.
|
|
571
|
+
* Terminals that don't support it safely ignore the sequence.
|
|
572
|
+
*
|
|
573
|
+
* @param shape - "block", "underline", or "bar"
|
|
574
|
+
* @param blink - Whether the cursor should blink (default: false)
|
|
575
|
+
*/
|
|
576
|
+
declare function setCursorStyle(shape: CursorShape, blink?: boolean): string;
|
|
577
|
+
/**
|
|
578
|
+
* Reset the terminal cursor style to the terminal's default (DECSCUSR 0).
|
|
579
|
+
*/
|
|
580
|
+
declare function resetCursorStyle(): string;
|
|
581
|
+
declare const enableMouse: typeof enableMouse$1;
|
|
582
|
+
declare const disableMouse: typeof disableMouse$1;
|
|
583
|
+
/**
|
|
584
|
+
* Kitty keyboard protocol flags (bitfield).
|
|
585
|
+
*
|
|
586
|
+
* | Flag | Bit | Description |
|
|
587
|
+
* | ---- | --- | ---------------------------------------------- |
|
|
588
|
+
* | 1 | 0 | Disambiguate escape codes |
|
|
589
|
+
* | 2 | 1 | Report event types (press/repeat/release) |
|
|
590
|
+
* | 4 | 2 | Report alternate keys |
|
|
591
|
+
* | 8 | 3 | Report all keys as escape codes |
|
|
592
|
+
* | 16 | 4 | Report associated text |
|
|
593
|
+
*/
|
|
594
|
+
declare const KittyFlags: {
|
|
595
|
+
readonly DISAMBIGUATE: 1;
|
|
596
|
+
readonly REPORT_EVENTS: 2;
|
|
597
|
+
readonly REPORT_ALTERNATE: 4;
|
|
598
|
+
readonly REPORT_ALL_KEYS: 8;
|
|
599
|
+
readonly REPORT_TEXT: 16;
|
|
600
|
+
};
|
|
601
|
+
/**
|
|
602
|
+
* Enable Kitty keyboard protocol (push mode).
|
|
603
|
+
* Sends CSI > flags u to opt into the specified modes.
|
|
604
|
+
* Default flags=11 (DISAMBIGUATE | REPORT_EVENTS | REPORT_ALL_KEYS) —
|
|
605
|
+
* enables modifier-only key reporting needed for useModifierKeys() Cmd tracking.
|
|
606
|
+
* Supported: Ghostty, Kitty, WezTerm, foot. Ignored by unsupported terminals.
|
|
607
|
+
*
|
|
608
|
+
* @param flags Bitfield of KittyFlags (default: DISAMBIGUATE)
|
|
609
|
+
*/
|
|
610
|
+
declare const enableKittyKeyboard: typeof enableKittyKeyboard$1;
|
|
611
|
+
declare function queryKittyKeyboard(): string;
|
|
612
|
+
declare const disableKittyKeyboard: typeof disableKittyKeyboard$1;
|
|
613
|
+
/** BEL character — audible bell and legacy OSC string terminator. */
|
|
614
|
+
declare const BEL = "\u0007";
|
|
615
|
+
/**
|
|
616
|
+
* Set the terminal window title using OSC 2 (window title only).
|
|
617
|
+
* Does not affect icon title (tab name in some terminals).
|
|
618
|
+
* Widely supported: xterm, Ghostty, iTerm2, Kitty, WezTerm, Alacritty, foot.
|
|
619
|
+
*/
|
|
620
|
+
declare function setWindowTitle(stdout: NodeJS.WriteStream, title: string): void;
|
|
621
|
+
/**
|
|
622
|
+
* Set both the window title and icon title using OSC 0.
|
|
623
|
+
* Some terminals treat OSC 0 as equivalent to OSC 2; others also change the
|
|
624
|
+
* dock/taskbar icon name.
|
|
625
|
+
*/
|
|
626
|
+
declare function setWindowAndIconTitle(stdout: NodeJS.WriteStream, title: string): void;
|
|
627
|
+
/**
|
|
628
|
+
* Reset the terminal window title by sending an empty OSC 2 sequence.
|
|
629
|
+
* The terminal typically reverts to its default title (shell command, etc.).
|
|
630
|
+
*/
|
|
631
|
+
declare function resetWindowTitle(stdout: NodeJS.WriteStream): void;
|
|
632
|
+
/** Report current working directory to the terminal via OSC 7.
|
|
633
|
+
* Used by terminals (iTerm2, Ghostty, WezTerm) for tab/split directory inheritance.
|
|
634
|
+
*/
|
|
635
|
+
declare function reportDirectory(stdout: NodeJS.WriteStream, path: string): void;
|
|
636
|
+
/**
|
|
637
|
+
* Mouse cursor shape names for OSC 22.
|
|
638
|
+
*
|
|
639
|
+
* Uses X11/CSS cursor names. Supported by: Ghostty, Kitty (>=0.33), foot,
|
|
640
|
+
* WezTerm (partial). Terminals that don't support OSC 22 safely ignore it.
|
|
641
|
+
*/
|
|
642
|
+
type MouseCursorShape = "default" | "text" | "pointer" | "crosshair" | "move" | "not-allowed" | "wait" | "help" | "grab" | "grabbing" | "col-resize" | "row-resize" | "ew-resize" | "ns-resize";
|
|
643
|
+
/**
|
|
644
|
+
* Generate OSC 22 sequence to set the mouse cursor shape.
|
|
645
|
+
*
|
|
646
|
+
* @param shape - X11/CSS cursor name
|
|
647
|
+
* @returns ANSI escape sequence string
|
|
648
|
+
*/
|
|
649
|
+
declare function setMouseCursorShape(shape: MouseCursorShape): string;
|
|
650
|
+
/**
|
|
651
|
+
* Generate OSC 22 sequence to reset mouse cursor to default.
|
|
652
|
+
*
|
|
653
|
+
* @returns ANSI escape sequence string
|
|
654
|
+
*/
|
|
655
|
+
declare function resetMouseCursorShape(): string;
|
|
656
|
+
declare const ANSI: {
|
|
657
|
+
readonly ESC: "\u001B";
|
|
658
|
+
readonly CSI: "\u001B[";
|
|
659
|
+
readonly CURSOR_HIDE: "\u001B[?25l";
|
|
660
|
+
readonly CURSOR_SHOW: "\u001B[?25h";
|
|
661
|
+
readonly CURSOR_HOME: "\u001B[H";
|
|
662
|
+
readonly SYNC_BEGIN: "\u001B[?2026h";
|
|
663
|
+
readonly SYNC_END: "\u001B[?2026l";
|
|
664
|
+
readonly RESET: "\u001B[0m";
|
|
665
|
+
readonly SGR: {
|
|
666
|
+
readonly bold: 1;
|
|
667
|
+
readonly dim: 2;
|
|
668
|
+
readonly italic: 3;
|
|
669
|
+
readonly underline: 4;
|
|
670
|
+
readonly blink: 5;
|
|
671
|
+
readonly inverse: 7;
|
|
672
|
+
readonly hidden: 8;
|
|
673
|
+
readonly strikethrough: 9;
|
|
674
|
+
readonly boldOff: 22;
|
|
675
|
+
readonly italicOff: 23;
|
|
676
|
+
readonly underlineOff: 24;
|
|
677
|
+
readonly blinkOff: 25;
|
|
678
|
+
readonly inverseOff: 27;
|
|
679
|
+
readonly hiddenOff: 28;
|
|
680
|
+
readonly strikethroughOff: 29;
|
|
681
|
+
readonly fgDefault: 39;
|
|
682
|
+
readonly fgBlack: 30;
|
|
683
|
+
readonly fgRed: 31;
|
|
684
|
+
readonly fgGreen: 32;
|
|
685
|
+
readonly fgYellow: 33;
|
|
686
|
+
readonly fgBlue: 34;
|
|
687
|
+
readonly fgMagenta: 35;
|
|
688
|
+
readonly fgCyan: 36;
|
|
689
|
+
readonly fgWhite: 37;
|
|
690
|
+
readonly fgBrightBlack: 90;
|
|
691
|
+
readonly fgBrightRed: 91;
|
|
692
|
+
readonly fgBrightGreen: 92;
|
|
693
|
+
readonly fgBrightYellow: 93;
|
|
694
|
+
readonly fgBrightBlue: 94;
|
|
695
|
+
readonly fgBrightMagenta: 95;
|
|
696
|
+
readonly fgBrightCyan: 96;
|
|
697
|
+
readonly fgBrightWhite: 97;
|
|
698
|
+
readonly bgDefault: 49;
|
|
699
|
+
readonly bgBlack: 40;
|
|
700
|
+
readonly bgRed: 41;
|
|
701
|
+
readonly bgGreen: 42;
|
|
702
|
+
readonly bgYellow: 43;
|
|
703
|
+
readonly bgBlue: 44;
|
|
704
|
+
readonly bgMagenta: 45;
|
|
705
|
+
readonly bgCyan: 46;
|
|
706
|
+
readonly bgWhite: 47;
|
|
707
|
+
readonly bgBrightBlack: 100;
|
|
708
|
+
readonly bgBrightRed: 101;
|
|
709
|
+
readonly bgBrightGreen: 102;
|
|
710
|
+
readonly bgBrightYellow: 103;
|
|
711
|
+
readonly bgBrightBlue: 104;
|
|
712
|
+
readonly bgBrightMagenta: 105;
|
|
713
|
+
readonly bgBrightCyan: 106;
|
|
714
|
+
readonly bgBrightWhite: 107;
|
|
715
|
+
};
|
|
716
|
+
readonly moveCursor: typeof moveCursor$1;
|
|
717
|
+
readonly cursorUp: typeof cursorUp;
|
|
718
|
+
readonly cursorDown: typeof cursorDown;
|
|
719
|
+
readonly cursorLeft: typeof cursorLeft;
|
|
720
|
+
readonly cursorRight: typeof cursorRight;
|
|
721
|
+
readonly cursorToColumn: typeof cursorToColumn;
|
|
722
|
+
};
|
|
723
|
+
//#endregion
|
|
724
|
+
//#region packages/ag-react/src/hooks/useCursor.d.ts
|
|
725
|
+
interface CursorPosition {
|
|
726
|
+
/** Column offset within the component (0-indexed) */
|
|
727
|
+
col: number;
|
|
728
|
+
/** Row offset within the component (0-indexed) */
|
|
729
|
+
row: number;
|
|
730
|
+
/** Whether the cursor should be visible. Default: true */
|
|
731
|
+
visible?: boolean;
|
|
732
|
+
/** Terminal cursor shape (DECSCUSR). Default: terminal default */
|
|
733
|
+
shape?: CursorShape;
|
|
734
|
+
}
|
|
735
|
+
interface CursorState {
|
|
736
|
+
/** Absolute terminal X position (0-indexed) */
|
|
737
|
+
x: number;
|
|
738
|
+
/** Absolute terminal Y position (0-indexed) */
|
|
739
|
+
y: number;
|
|
740
|
+
/** Whether cursor is visible */
|
|
741
|
+
visible: boolean;
|
|
742
|
+
/** Terminal cursor shape (DECSCUSR) */
|
|
743
|
+
shape?: CursorShape;
|
|
744
|
+
/**
|
|
745
|
+
* Identity of the `useCursor` instance that last wrote this state. Set
|
|
746
|
+
* automatically by the hook on each store write. Used by the
|
|
747
|
+
* clear-on-unmount + clear-on-hide branches to skip the clear when
|
|
748
|
+
* THIS instance no longer owns the state — preventing the last-writer-
|
|
749
|
+
* wins stomp where an inactive sibling instance clears the active
|
|
750
|
+
* one's cursor. See bead `@km/silvery/13011-usecursor-race`. Optional
|
|
751
|
+
* because legacy external writes (Ink compat, direct store pokes) may
|
|
752
|
+
* omit it.
|
|
753
|
+
*/
|
|
754
|
+
owner?: number;
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Imperative cursor accessors for non-React code (scheduler, output phase).
|
|
758
|
+
* Created by createCursorStore() and threaded to consumers that can't use hooks.
|
|
759
|
+
*/
|
|
760
|
+
interface CursorAccessors {
|
|
761
|
+
getCursorState(): CursorState | null;
|
|
762
|
+
subscribeCursor(listener: () => void): () => void;
|
|
763
|
+
}
|
|
764
|
+
interface CursorStore {
|
|
765
|
+
state: CursorState | null;
|
|
766
|
+
listeners: Set<() => void>;
|
|
767
|
+
accessors: CursorAccessors;
|
|
768
|
+
setCursorState(state: CursorState | null): void;
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
771
|
+
* Create an isolated cursor store. Each silvery instance gets one.
|
|
772
|
+
* Returns the store (for CursorProvider) and accessors (for scheduler/output).
|
|
773
|
+
*/
|
|
774
|
+
declare function createCursorStore(): CursorStore;
|
|
775
|
+
/**
|
|
776
|
+
* Provider that gives its subtree an isolated cursor store.
|
|
777
|
+
* Wrap your silvery app root in this to isolate cursor state per instance.
|
|
778
|
+
*/
|
|
779
|
+
declare function CursorProvider({
|
|
780
|
+
store,
|
|
781
|
+
children
|
|
782
|
+
}: {
|
|
783
|
+
store: CursorStore;
|
|
784
|
+
children?: ReactNode;
|
|
785
|
+
}): React.FunctionComponentElement<React.ProviderProps<CursorStore | null>>;
|
|
786
|
+
/** For testing -- reset global fallback state between tests. */
|
|
787
|
+
declare function resetCursorState(): void;
|
|
788
|
+
/**
|
|
789
|
+
* Show and position the terminal's blinking cursor within this component.
|
|
790
|
+
*
|
|
791
|
+
* The cursor position is relative to the component's screen position.
|
|
792
|
+
* Only one cursor can be active per silvery instance -- last caller with visible=true wins.
|
|
793
|
+
*
|
|
794
|
+
* Uses CursorContext if a CursorProvider is present (per-instance isolation).
|
|
795
|
+
* Falls back to module-level globals otherwise (backward compat).
|
|
796
|
+
*
|
|
797
|
+
* @deprecated Phase 2 of `km-silvery.view-as-layout-output` migrated cursor
|
|
798
|
+
* positioning from a React-effect-chain (`useCursor` → `useScrollRect` →
|
|
799
|
+
* `setCursorState`) to a layout-output prop (`<Box cursorOffset={…}>`). The
|
|
800
|
+
* prop path resolves absolute coordinates synchronously during the layout
|
|
801
|
+
* phase, so the very first frame after a conditional mount emits correct
|
|
802
|
+
* cursor ANSI — fixing `km-silvercode.cursor-startup-position` end-to-end.
|
|
803
|
+
*
|
|
804
|
+
* Migrate consumers by deleting the `useCursor({col, row, visible})` call
|
|
805
|
+
* and adding `cursorOffset={{col, row, visible}}` to the surrounding Box. The
|
|
806
|
+
* layout phase applies border + padding offsets automatically, so consumers
|
|
807
|
+
* no longer need to add `borderColOffset` / `borderRowOffset` manually.
|
|
808
|
+
*
|
|
809
|
+
* This hook remains as a back-compat wrapper that writes to the cursor
|
|
810
|
+
* store; the scheduler still reads the store as a fallback when no
|
|
811
|
+
* `cursorOffset` prop is set on any node. Slated for deletion once all
|
|
812
|
+
* in-tree consumers migrate (TextArea + TextInput already migrated; Ink
|
|
813
|
+
* compat goes through the store directly and is unaffected). See bead
|
|
814
|
+
* `km-silvery.delete-cursor-globals`.
|
|
815
|
+
*/
|
|
816
|
+
declare function useCursor(position: CursorPosition): void;
|
|
817
|
+
//#endregion
|
|
818
|
+
//#region packages/ag-term/src/pipeline/output-phase.d.ts
|
|
819
|
+
interface OutputPhaseDiagnostics {
|
|
820
|
+
reason: "first-render" | "inline-first-render" | "inline-resize-incremental" | "inline-incremental" | "forced-full-render" | "dimension-mismatch" | "zero-diff" | "diff" | "native-scroll-diff";
|
|
821
|
+
mode: "fullscreen" | "inline";
|
|
822
|
+
width: number;
|
|
823
|
+
height: number;
|
|
824
|
+
prevWidth: number;
|
|
825
|
+
prevHeight: number;
|
|
826
|
+
changedCells?: number;
|
|
827
|
+
rawChangedCells?: number;
|
|
828
|
+
dirtyRows?: number;
|
|
829
|
+
}
|
|
830
|
+
/**
|
|
831
|
+
* Output-phase capabilities type.
|
|
832
|
+
*
|
|
833
|
+
* Post km-silvery.text-box-attr-props (2026-04-23):
|
|
834
|
+
* `underlineStyles` is now the per-style set (not just a boolean), so the
|
|
835
|
+
* output phase can downgrade individual styles — e.g. `"curly"` requested on
|
|
836
|
+
* a terminal that only supports `"single"` emits plain `SGR 4` instead of
|
|
837
|
+
* the unsupported `4:3`.
|
|
838
|
+
*
|
|
839
|
+
* Accepts either form at construction for backwards compatibility:
|
|
840
|
+
* - `boolean` — back-compat shorthand. `true` = all styles supported
|
|
841
|
+
* (modern terminals); `false` = only plain `SGR 4` works.
|
|
842
|
+
* - `readonly UnderlineStyle[]` — per-style granular set, matches
|
|
843
|
+
* `TerminalCaps.underlineStyles` directly.
|
|
844
|
+
*
|
|
845
|
+
* Internally the output phase always sees the array form (resolved by the
|
|
846
|
+
* ctx factory). Consumers check per-style via the `supportsUnderlineStyle`
|
|
847
|
+
* helper, or check "any extended support" via `caps.underlineStyles.length > 0`.
|
|
848
|
+
*/
|
|
849
|
+
type OutputUnderlineStylesCap = boolean | readonly UnderlineStyle[];
|
|
850
|
+
interface OutputCaps {
|
|
851
|
+
/**
|
|
852
|
+
* Per-style underline support. Array form so the output phase can
|
|
853
|
+
* downgrade individual unsupported styles to plain SGR 4 (single).
|
|
854
|
+
* Empty array = plain SGR 4 only.
|
|
855
|
+
*/
|
|
856
|
+
readonly underlineStyles: readonly UnderlineStyle[];
|
|
857
|
+
readonly underlineColor: boolean;
|
|
858
|
+
/**
|
|
859
|
+
* SGR 53 (overline) support. When false, the output phase skips
|
|
860
|
+
* emitting SGR 53/55 — the cell attr is still packed/diffed, but no
|
|
861
|
+
* escape sequence reaches the terminal. Defaults to true for modern
|
|
862
|
+
* terminals (Ghostty, iTerm2, xterm with extended attrs enabled).
|
|
863
|
+
*/
|
|
864
|
+
readonly overline: boolean;
|
|
865
|
+
readonly colorLevel: TerminalCaps["colorLevel"];
|
|
866
|
+
}
|
|
867
|
+
/** Output phase function signature. */
|
|
868
|
+
interface OutputPhaseFn {
|
|
869
|
+
(prev: TerminalBuffer | null, next: TerminalBuffer, mode?: "fullscreen" | "inline", scrollbackOffset?: number, termRows?: number, cursorPos?: CursorState | null): string;
|
|
870
|
+
/** Reset inline cursor state. Used by useScrollback to clear cursor tracking on resize. */
|
|
871
|
+
resetInlineState?: () => void;
|
|
872
|
+
/** Get the current inline cursor row (relative to render region start). -1 if unknown. */
|
|
873
|
+
getInlineCursorRow?: () => number;
|
|
874
|
+
/** Promote frozen content to scrollback. Called by useScrollback to queue
|
|
875
|
+
* frozen content for the next render — the output phase writes frozen + live
|
|
876
|
+
* content in a single target.write() to avoid flicker. */
|
|
877
|
+
promoteScrollback?: (frozenContent: string, frozenLineCount: number) => void;
|
|
878
|
+
}
|
|
879
|
+
interface OutputMeasurer {
|
|
880
|
+
graphemeWidth(grapheme: string): number;
|
|
881
|
+
readonly textSizing: boolean;
|
|
882
|
+
}
|
|
883
|
+
/**
|
|
884
|
+
* Create a scoped output phase that uses specific terminal capabilities.
|
|
885
|
+
*
|
|
886
|
+
* @param caps - Terminal capabilities for SGR code generation. Accepts
|
|
887
|
+
* `underlineStyles` as either a boolean (back-compat) or the per-style
|
|
888
|
+
* array form; see {@link OutputUnderlineStylesCap}.
|
|
889
|
+
* @param measurer - Width measurer for graphemeWidth/textSizing (avoids dual-module-loading issues)
|
|
890
|
+
*/
|
|
891
|
+
declare function createOutputPhase(caps: {
|
|
892
|
+
underlineStyles?: OutputUnderlineStylesCap;
|
|
893
|
+
underlineColor?: boolean;
|
|
894
|
+
overline?: boolean;
|
|
895
|
+
colorLevel?: TerminalCaps["colorLevel"];
|
|
896
|
+
}, measurer?: OutputMeasurer): OutputPhaseFn;
|
|
897
|
+
//#endregion
|
|
898
|
+
//#region packages/ag-term/src/pipeline/types.d.ts
|
|
899
|
+
/**
|
|
900
|
+
* Context threaded through the render pipeline.
|
|
901
|
+
*
|
|
902
|
+
* Carries per-render resources that were previously accessed via module-level
|
|
903
|
+
* globals (e.g., `_scopedMeasurer` + `runWithMeasurer()`). Threading context
|
|
904
|
+
* explicitly eliminates save/restore patterns and makes the pipeline pure.
|
|
905
|
+
*
|
|
906
|
+
* Phase 1: measurer only.
|
|
907
|
+
* Phase 2: NodeRenderState for per-node params.
|
|
908
|
+
* Phase 3: instrumentation/diagnostics fields (optional — fall back to
|
|
909
|
+
* module-level globals when absent for backward compat).
|
|
910
|
+
*/
|
|
911
|
+
interface PipelineContext {
|
|
912
|
+
readonly measurer: Measurer;
|
|
913
|
+
readonly instrumentEnabled?: boolean;
|
|
914
|
+
readonly stats?: RenderPhaseStats;
|
|
915
|
+
readonly nodeTrace?: NodeTraceEntry[];
|
|
916
|
+
readonly nodeTraceEnabled?: boolean;
|
|
917
|
+
readonly bgConflictMode?: BgConflictMode;
|
|
918
|
+
readonly warnedBgConflicts?: Set<string>;
|
|
919
|
+
}
|
|
920
|
+
/**
|
|
921
|
+
* Background conflict detection mode.
|
|
922
|
+
* Set via SILVERY_BG_CONFLICT env var: 'ignore' | 'warn' | 'throw'
|
|
923
|
+
*/
|
|
924
|
+
type BgConflictMode = "ignore" | "warn" | "throw";
|
|
925
|
+
/**
|
|
926
|
+
* Per-node trace entry for SILVERY_STRICT diagnosis.
|
|
927
|
+
*/
|
|
928
|
+
interface NodeTraceEntry {
|
|
929
|
+
id: string;
|
|
930
|
+
type: string;
|
|
931
|
+
depth: number;
|
|
932
|
+
rect: string;
|
|
933
|
+
prevLayout: string;
|
|
934
|
+
hasPrev: boolean;
|
|
935
|
+
ancestorCleared: boolean;
|
|
936
|
+
flags: string;
|
|
937
|
+
decision: string;
|
|
938
|
+
layoutChanged: boolean;
|
|
939
|
+
contentAreaAffected?: boolean;
|
|
940
|
+
contentRegionCleared?: boolean;
|
|
941
|
+
childrenNeedFreshRender?: boolean;
|
|
942
|
+
childHasPrev?: boolean;
|
|
943
|
+
childAncestorCleared?: boolean;
|
|
944
|
+
skipBgFill?: boolean;
|
|
945
|
+
bgColor?: string;
|
|
946
|
+
}
|
|
947
|
+
/**
|
|
948
|
+
* Mutable stats counters for render phase instrumentation.
|
|
949
|
+
* Reset after each renderPhase call.
|
|
950
|
+
*/
|
|
951
|
+
interface RenderPhaseStats {
|
|
952
|
+
nodesVisited: number;
|
|
953
|
+
nodesRendered: number;
|
|
954
|
+
nodesSkipped: number;
|
|
955
|
+
textNodes: number;
|
|
956
|
+
boxNodes: number;
|
|
957
|
+
clearOps: number;
|
|
958
|
+
/**
|
|
959
|
+
* Count of text nodes that took the per-segment style-only restyle fast path
|
|
960
|
+
* (skips collectTextWithBg→formatTextLines→renderGraphemes; restyles existing
|
|
961
|
+
* cells in place per child span). See render-text.ts `restyleTextSegments`.
|
|
962
|
+
*/
|
|
963
|
+
textRestyleFastPath: number;
|
|
964
|
+
noPrevBuffer: number;
|
|
965
|
+
flagContentDirty: number;
|
|
966
|
+
flagStylePropsDirty: number;
|
|
967
|
+
flagLayoutChanged: number;
|
|
968
|
+
flagSubtreeDirty: number;
|
|
969
|
+
flagChildrenDirty: number;
|
|
970
|
+
flagChildPositionChanged: number;
|
|
971
|
+
flagAncestorLayoutChanged: number;
|
|
972
|
+
scrollContainerCount: number;
|
|
973
|
+
scrollViewportCleared: number;
|
|
974
|
+
scrollClearReason: string;
|
|
975
|
+
normalChildrenRepaint: number;
|
|
976
|
+
normalRepaintReason: string;
|
|
977
|
+
/**
|
|
978
|
+
* Count of children force-rendered because an earlier first-pass sibling's
|
|
979
|
+
* boxRect overlapped them. CSS paint order requires later siblings to win
|
|
980
|
+
* at any overlap; on incremental renders the earlier sibling's painting
|
|
981
|
+
* destroys the later sibling's pixels in the cloned buffer, so the later
|
|
982
|
+
* sibling must repaint even when its own dirty flags are clean.
|
|
983
|
+
*/
|
|
984
|
+
siblingOverlapForced: number;
|
|
985
|
+
cascadeMinDepth: number;
|
|
986
|
+
cascadeNodes: string;
|
|
987
|
+
_noopSkip: number;
|
|
988
|
+
_prevBufferNull: number;
|
|
989
|
+
_prevBufferDimMismatch: number;
|
|
990
|
+
_hasPrevBuffer: number;
|
|
991
|
+
_layoutW: number;
|
|
992
|
+
_layoutH: number;
|
|
993
|
+
_prevW: number;
|
|
994
|
+
_prevH: number;
|
|
995
|
+
_callCount: number;
|
|
996
|
+
}
|
|
997
|
+
//#endregion
|
|
998
|
+
//#region packages/ag-term/src/render-adapter.d.ts
|
|
999
|
+
/**
|
|
1000
|
+
* Render Adapter Abstraction
|
|
1001
|
+
*
|
|
1002
|
+
* This module defines the interfaces that allow silvery to render to different
|
|
1003
|
+
* targets (terminal, canvas, etc.) while keeping the core layout and
|
|
1004
|
+
* reconciliation logic portable.
|
|
1005
|
+
*/
|
|
1006
|
+
interface TextMeasureStyle {
|
|
1007
|
+
bold?: boolean;
|
|
1008
|
+
italic?: boolean;
|
|
1009
|
+
fontSize?: number;
|
|
1010
|
+
fontFamily?: string;
|
|
1011
|
+
}
|
|
1012
|
+
interface TextMeasureResult {
|
|
1013
|
+
width: number;
|
|
1014
|
+
height: number;
|
|
1015
|
+
}
|
|
1016
|
+
interface TextMeasurer {
|
|
1017
|
+
/**
|
|
1018
|
+
* Measure text dimensions.
|
|
1019
|
+
* Returns width in adapter units (cells for terminal, pixels for canvas).
|
|
1020
|
+
*/
|
|
1021
|
+
measureText(text: string, style?: TextMeasureStyle): TextMeasureResult;
|
|
1022
|
+
/**
|
|
1023
|
+
* Get the line height for the given style.
|
|
1024
|
+
*/
|
|
1025
|
+
getLineHeight(style?: TextMeasureStyle): number;
|
|
1026
|
+
}
|
|
1027
|
+
interface RenderStyle {
|
|
1028
|
+
/** Cross-target hyperlink carried by the rendered text cells. */
|
|
1029
|
+
hyperlink?: string;
|
|
1030
|
+
fg?: string;
|
|
1031
|
+
bg?: string;
|
|
1032
|
+
attrs?: {
|
|
1033
|
+
bold?: boolean;
|
|
1034
|
+
dim?: boolean;
|
|
1035
|
+
italic?: boolean;
|
|
1036
|
+
underline?: boolean;
|
|
1037
|
+
underlineStyle?: "single" | "double" | "curly" | "dotted" | "dashed";
|
|
1038
|
+
underlineColor?: string; /** Overline — SGR 53/55. Independent of underline. */
|
|
1039
|
+
overline?: boolean;
|
|
1040
|
+
strikethrough?: boolean;
|
|
1041
|
+
inverse?: boolean;
|
|
1042
|
+
};
|
|
1043
|
+
}
|
|
1044
|
+
interface RenderBuffer {
|
|
1045
|
+
readonly width: number;
|
|
1046
|
+
readonly height: number;
|
|
1047
|
+
/**
|
|
1048
|
+
* Fill a rectangle with a style.
|
|
1049
|
+
*/
|
|
1050
|
+
fillRect(x: number, y: number, width: number, height: number, style: RenderStyle): void;
|
|
1051
|
+
/**
|
|
1052
|
+
* Draw text at a position.
|
|
1053
|
+
*/
|
|
1054
|
+
drawText(x: number, y: number, text: string, style: RenderStyle): void;
|
|
1055
|
+
/**
|
|
1056
|
+
* Draw a single character at a position.
|
|
1057
|
+
*/
|
|
1058
|
+
drawChar(x: number, y: number, char: string, style: RenderStyle): void;
|
|
1059
|
+
/**
|
|
1060
|
+
* Check if coordinates are within bounds.
|
|
1061
|
+
*/
|
|
1062
|
+
inBounds(x: number, y: number): boolean;
|
|
1063
|
+
/**
|
|
1064
|
+
* Draw a filled rounded rectangle with optional border stroke.
|
|
1065
|
+
* Canvas-only — terminal adapters don't implement this.
|
|
1066
|
+
*/
|
|
1067
|
+
fillRoundedRect?(x: number, y: number, width: number, height: number, radius: number, fill: string | undefined, stroke: string | undefined, lineWidth?: number): void;
|
|
1068
|
+
}
|
|
1069
|
+
interface BorderChars {
|
|
1070
|
+
topLeft: string;
|
|
1071
|
+
topRight: string;
|
|
1072
|
+
bottomLeft: string;
|
|
1073
|
+
bottomRight: string;
|
|
1074
|
+
horizontal: string;
|
|
1075
|
+
vertical: string;
|
|
1076
|
+
/** Bottom horizontal character. When absent, falls back to `horizontal`. */
|
|
1077
|
+
bottomHorizontal?: string;
|
|
1078
|
+
/** Right vertical character. When absent, falls back to `vertical`. */
|
|
1079
|
+
rightVertical?: string;
|
|
1080
|
+
}
|
|
1081
|
+
interface RenderAdapter {
|
|
1082
|
+
/** Adapter name for debugging */
|
|
1083
|
+
name: string;
|
|
1084
|
+
/** Text measurement for this adapter */
|
|
1085
|
+
measurer: TextMeasurer;
|
|
1086
|
+
/**
|
|
1087
|
+
* Create a buffer for rendering.
|
|
1088
|
+
*/
|
|
1089
|
+
createBuffer(width: number, height: number): RenderBuffer;
|
|
1090
|
+
/**
|
|
1091
|
+
* Flush the buffer to the output (terminal, canvas, etc.).
|
|
1092
|
+
* For terminal: returns ANSI diff string.
|
|
1093
|
+
* For canvas: draws directly, returns void.
|
|
1094
|
+
*/
|
|
1095
|
+
flush(buffer: RenderBuffer, prevBuffer: RenderBuffer | null): string | void;
|
|
1096
|
+
/**
|
|
1097
|
+
* Get border characters for the given style.
|
|
1098
|
+
*/
|
|
1099
|
+
getBorderChars(style: string): BorderChars;
|
|
1100
|
+
}
|
|
1101
|
+
//#endregion
|
|
1102
|
+
//#region packages/ag/src/layout-signals.d.ts
|
|
1103
|
+
/**
|
|
1104
|
+
* Writable signal — call with no args to read, call with value to write.
|
|
1105
|
+
*/
|
|
1106
|
+
type WritableSignal<T> = {
|
|
1107
|
+
(): T;
|
|
1108
|
+
(value: T): void;
|
|
1109
|
+
};
|
|
1110
|
+
/**
|
|
1111
|
+
* Reactive projection of `AgNode.scrollState` — the layout-phase's pixel-space
|
|
1112
|
+
* truth about what's visible in an `overflow="scroll"` container.
|
|
1113
|
+
*
|
|
1114
|
+
* This is the **single source of truth** that virtualization consumers (like
|
|
1115
|
+
* `useVirtualizer` + `ListView`) read to decide which items to render. By
|
|
1116
|
+
* subscribing to this signal instead of independently computing their own
|
|
1117
|
+
* visible range, consumers cannot diverge from what layout-phase actually
|
|
1118
|
+
* laid out on screen.
|
|
1119
|
+
*
|
|
1120
|
+
* Fields are pixel-space integers already rounded by the layout engine —
|
|
1121
|
+
* re-using them (instead of recomputing via `sumHeights`) guarantees
|
|
1122
|
+
* `leadingHeight == scrollOffset` by construction.
|
|
1123
|
+
*
|
|
1124
|
+
* `null` for non-scroll containers and for scroll containers before the first
|
|
1125
|
+
* layout pass (bootstrap state — virtualizers must fall back to estimates).
|
|
1126
|
+
*/
|
|
1127
|
+
interface ScrollStateSnapshot {
|
|
1128
|
+
/** Current scroll offset in terminal rows (pixel-space, pre-rounded). */
|
|
1129
|
+
readonly offset: number;
|
|
1130
|
+
/** Total content height (all children) in rows. */
|
|
1131
|
+
readonly contentHeight: number;
|
|
1132
|
+
/** Visible height (container height minus borders/padding). */
|
|
1133
|
+
readonly viewportHeight: number;
|
|
1134
|
+
/** Index of the first visible child (flexbox-measured). */
|
|
1135
|
+
readonly firstVisibleChild: number;
|
|
1136
|
+
/** Index of the last visible child (flexbox-measured). */
|
|
1137
|
+
readonly lastVisibleChild: number;
|
|
1138
|
+
/** Count of items hidden above the viewport. */
|
|
1139
|
+
readonly hiddenAbove: number;
|
|
1140
|
+
/** Count of items hidden below the viewport. */
|
|
1141
|
+
readonly hiddenBelow: number;
|
|
1142
|
+
}
|
|
1143
|
+
/**
|
|
1144
|
+
* Caret rect — absolute terminal coordinates of the caret declared on a
|
|
1145
|
+
* Box via `cursorOffset`, computed during the layout phase as the peer of
|
|
1146
|
+
* `scrollRect` / `screenRect` / `boxRect` / `contentRect`.
|
|
1147
|
+
*
|
|
1148
|
+
* Width/height are always 1 (the caret occupies a single cell). The
|
|
1149
|
+
* `visible` flag is a separate property because layout still computes the
|
|
1150
|
+
* coordinates even when the caret is hidden — that lets toggling
|
|
1151
|
+
* `visible` re-emit the caret without re-running layout.
|
|
1152
|
+
*
|
|
1153
|
+
* `shape` is **deprecated** — the terminal layer now derives the shape
|
|
1154
|
+
* from focus + editable state via `resolveCaretStyle` in `@silvery/ag-term`.
|
|
1155
|
+
* The field is kept for one cycle so external readers that were already
|
|
1156
|
+
* forwarding `cursor.shape` to DECSCUSR keep working; new code MUST NOT
|
|
1157
|
+
* branch on this field. See bead `km-silvery.cursor-invariants` invariant 6.
|
|
1158
|
+
*/
|
|
1159
|
+
interface CursorRect {
|
|
1160
|
+
/** Absolute terminal X column (0-indexed) */
|
|
1161
|
+
readonly x: number;
|
|
1162
|
+
/** Absolute terminal Y row (0-indexed) */
|
|
1163
|
+
readonly y: number;
|
|
1164
|
+
/** Whether the caret should be visible on this frame. */
|
|
1165
|
+
readonly visible: boolean;
|
|
1166
|
+
/**
|
|
1167
|
+
* @deprecated Target-specific. Read focus state from the active cursor
|
|
1168
|
+
* node via `resolveCaretStyle` in `@silvery/ag-term` instead. Removed in
|
|
1169
|
+
* the next cycle.
|
|
1170
|
+
*/
|
|
1171
|
+
readonly shape?: CursorShape$1;
|
|
1172
|
+
}
|
|
1173
|
+
/**
|
|
1174
|
+
* All reactive signals for an AgNode.
|
|
1175
|
+
*
|
|
1176
|
+
* Combined rect signals (layout outputs) + node signals (content/state).
|
|
1177
|
+
* One interface, one WeakMap, one sync function.
|
|
1178
|
+
*/
|
|
1179
|
+
interface LayoutSignals {
|
|
1180
|
+
readonly boxRect: WritableSignal<Rect | null>;
|
|
1181
|
+
readonly scrollRect: WritableSignal<Rect | null>;
|
|
1182
|
+
readonly screenRect: WritableSignal<Rect | null>;
|
|
1183
|
+
readonly boxRectCommitted: WritableSignal<Rect | null>;
|
|
1184
|
+
readonly scrollRectCommitted: WritableSignal<Rect | null>;
|
|
1185
|
+
readonly screenRectCommitted: WritableSignal<Rect | null>;
|
|
1186
|
+
/**
|
|
1187
|
+
* Content-box rect — `boxRect` minus border and padding (CSS content area).
|
|
1188
|
+
* Peer of `boxRect`/`scrollRect`/`screenRect`, synced after layout. Null
|
|
1189
|
+
* when the node has no boxRect yet (pre-layout) or is not a Box.
|
|
1190
|
+
*
|
|
1191
|
+
* This is the canonical origin for caret positioning, popover anchors,
|
|
1192
|
+
* selection fragments, and any feature that needs to draw "inside" a Box's
|
|
1193
|
+
* content area without re-deriving the border/padding math at the call
|
|
1194
|
+
* site. `computeCursorRect` reads from this rect — Phase 4 / overlay-anchor
|
|
1195
|
+
* work will too. See `km-silvery.cursor-invariants` invariant 3.
|
|
1196
|
+
*/
|
|
1197
|
+
readonly contentRect: WritableSignal<Rect | null>;
|
|
1198
|
+
/**
|
|
1199
|
+
* Absolute terminal coordinates of the caret declared by this node's
|
|
1200
|
+
* `BoxProps.cursorOffset`. Null when the node has no cursorOffset prop, or
|
|
1201
|
+
* before the first layout pass populates `scrollRect`.
|
|
1202
|
+
*
|
|
1203
|
+
* Phase 2 of `km-silvery.view-as-layout-output` — the scheduler reads this
|
|
1204
|
+
* signal (rather than `cursorStore.getCursorState()`) to emit caret
|
|
1205
|
+
* positioning ANSI. Because layout phase runs synchronously before each
|
|
1206
|
+
* render, the very first frame after mount sees the correct caret — no
|
|
1207
|
+
* effect-chain stale-read on conditional mounts.
|
|
1208
|
+
*/
|
|
1209
|
+
readonly cursorRect: WritableSignal<CursorRect | null>;
|
|
1210
|
+
/**
|
|
1211
|
+
* Focused-node id declared by this node's `BoxProps.focused`. The value is
|
|
1212
|
+
* the node's `id` (preferred) or `testID` when `props.focused === true`,
|
|
1213
|
+
* else `null`. Phase 4a of `km-silvery.view-as-layout-output` — the
|
|
1214
|
+
* focus-renderer reads this signal (and/or `findActiveFocusedNodeId(root)`)
|
|
1215
|
+
* to paint focus styling without going through the `useFocus` →
|
|
1216
|
+
* `FocusManager` → `useSyncExternalStore` effect chain.
|
|
1217
|
+
*
|
|
1218
|
+
* Per-node by design (peer of `cursorRect`): each Box that participates in
|
|
1219
|
+
* focus carries its own declared id here, and the tree-walk lookup picks
|
|
1220
|
+
* the deepest visible declarer. Unmount is handled by WeakMap GC + the
|
|
1221
|
+
* per-frame recompute in `syncRectSignals` clearing back to null when the
|
|
1222
|
+
* prop is removed. See bead `km-silvery.phase4-split-focus-selection`.
|
|
1223
|
+
*/
|
|
1224
|
+
readonly focusedNodeId: WritableSignal<string | null>;
|
|
1225
|
+
/**
|
|
1226
|
+
* Geometric output of `BoxProps.selectionIntent` — the list of rectangles
|
|
1227
|
+
* (one per visual line spanned) that the selection renderer should paint
|
|
1228
|
+
* with highlight bg this frame. Empty array when the node declares no
|
|
1229
|
+
* `selectionIntent` or when the intent is collapsed (`from === to`).
|
|
1230
|
+
*
|
|
1231
|
+
* Phase 4b of `km-silvery.view-as-layout-output` — peer of `cursorRect`
|
|
1232
|
+
* (caret) and `focusedNodeId` (focus). The selection renderer reads this
|
|
1233
|
+
* signal (and/or `findActiveSelectionFragments(root)`) to paint highlight
|
|
1234
|
+
* styling without going through the legacy `SelectionFeature` capability
|
|
1235
|
+
* + `useSelection` `useSyncExternalStore` chain.
|
|
1236
|
+
*
|
|
1237
|
+
* Per-node by design: each Box that participates in selection carries its
|
|
1238
|
+
* own fragments here. The aggregator (`findActiveSelectionFragments`)
|
|
1239
|
+
* concatenates fragments across all mounted declarers — multi-node
|
|
1240
|
+
* selection composes for free. Stale-cleanup is handled by WeakMap GC +
|
|
1241
|
+
* the per-frame recompute in `syncRectSignals` clearing back to the empty
|
|
1242
|
+
* sentinel when the prop is removed (mirrors cursor invariant 5 + focus
|
|
1243
|
+
* invariant 3). See bead `km-silvery.phase4-split-focus-selection`.
|
|
1244
|
+
*/
|
|
1245
|
+
readonly selectionFragments: WritableSignal<readonly Rect[]>;
|
|
1246
|
+
readonly scrollState: WritableSignal<ScrollStateSnapshot | null>;
|
|
1247
|
+
/**
|
|
1248
|
+
* Geometric output of `BoxProps.anchorRef` — the rect that other Boxes'
|
|
1249
|
+
* decorations resolve to when they reference this anchor by id. The
|
|
1250
|
+
* registered rect is the Box's `contentRect` (border + padding excluded);
|
|
1251
|
+
* edge-specific rects are derived by `placeFloating` at consumption time.
|
|
1252
|
+
*
|
|
1253
|
+
* Phase 4c of `km-silvery.view-as-layout-output` (overlay-anchor v1) — peer
|
|
1254
|
+
* of `cursorRect`/`focusedNodeId`/`selectionFragments`. The tree-walk
|
|
1255
|
+
* lookup `findAnchor(root, id)` reads this signal (and falls back to
|
|
1256
|
+
* `computeAnchorRect` for nodes without signals).
|
|
1257
|
+
*
|
|
1258
|
+
* Null when:
|
|
1259
|
+
* - the node has no `anchorRef` BoxProp, OR
|
|
1260
|
+
* - `contentRect` is unavailable (pre-layout / clipped to zero size).
|
|
1261
|
+
*/
|
|
1262
|
+
readonly anchorRect: WritableSignal<Rect | null>;
|
|
1263
|
+
/**
|
|
1264
|
+
* Absolute terminal coordinates of the HARDWARE-PARK cell declared by this
|
|
1265
|
+
* node's `BoxProps.parkOffset` — where a managed frame parks (then hides) the
|
|
1266
|
+
* hardware cursor when this node owns the frame. Position-only (1×1 rect);
|
|
1267
|
+
* null when the node has no `parkOffset` prop or before the first layout pass.
|
|
1268
|
+
*
|
|
1269
|
+
* Peer of `cursorRect`, but NON-focus-gated: the tree-walk lookup
|
|
1270
|
+
* `findActiveParkRect(root)` picks the deepest declarer regardless of focus or
|
|
1271
|
+
* visibility, so a managed frame always has a benign park cell and never falls
|
|
1272
|
+
* back to the box origin / `home(0,0)` (@km/code/v0.2/19702).
|
|
1273
|
+
*/
|
|
1274
|
+
readonly parkRect: WritableSignal<Rect | null>;
|
|
1275
|
+
/**
|
|
1276
|
+
* Geometric output of `BoxProps.decorations` — the resolved per-decoration
|
|
1277
|
+
* rect list this frame. Each entry carries the source kind, id, and rects
|
|
1278
|
+
* (popover/tooltip → one rect at the placed position; highlight → one rect
|
|
1279
|
+
* within the owning Box's content area).
|
|
1280
|
+
*
|
|
1281
|
+
* v1 emits one rect per decoration. The list mirrors the BoxProp's order so
|
|
1282
|
+
* paint order is deterministic. When an anchor lookup fails (popover/tooltip
|
|
1283
|
+
* with no matching `anchorRef`), the entry's `rects` is empty — the
|
|
1284
|
+
* renderer skips it.
|
|
1285
|
+
*
|
|
1286
|
+
* Phase 4c of `km-silvery.view-as-layout-output` (overlay-anchor v1).
|
|
1287
|
+
*/
|
|
1288
|
+
readonly decorationRects: WritableSignal<readonly DecorationRect[]>;
|
|
1289
|
+
readonly textContent: WritableSignal<string | undefined>;
|
|
1290
|
+
readonly focused: WritableSignal<boolean>;
|
|
1291
|
+
}
|
|
1292
|
+
/**
|
|
1293
|
+
* Geometric output of one `Decoration` entry — the resolved rects for paint
|
|
1294
|
+
* time, plus the source kind/id so the renderer can dispatch by kind.
|
|
1295
|
+
*
|
|
1296
|
+
* Phase 4c of `km-silvery.view-as-layout-output` (overlay-anchor v1).
|
|
1297
|
+
*
|
|
1298
|
+
* v1 emits one rect per decoration. Soft-wrap aware highlight fragmentation
|
|
1299
|
+
* (multiple rects per highlight) is deferred to v2 — same path as
|
|
1300
|
+
* `selectionFragments` once the find/replace consumer ships.
|
|
1301
|
+
*
|
|
1302
|
+
* `rects` may be empty when an anchor lookup failed (popover/tooltip with a
|
|
1303
|
+
* missing `anchorId`); consumers SHOULD skip empty entries rather than
|
|
1304
|
+
* paint a 0×0 rect.
|
|
1305
|
+
*/
|
|
1306
|
+
interface DecorationRect {
|
|
1307
|
+
/** Decoration kind from the source `Decoration` entry. */
|
|
1308
|
+
readonly kind: Decoration["kind"];
|
|
1309
|
+
/** Stable id from the source `Decoration` entry. */
|
|
1310
|
+
readonly id: string;
|
|
1311
|
+
/** Resolved rects in absolute terminal cell space. May be empty. */
|
|
1312
|
+
readonly rects: readonly Rect[];
|
|
1313
|
+
}
|
|
1314
|
+
//#endregion
|
|
1315
|
+
//#region packages/ag-term/src/pipeline/index.d.ts
|
|
1316
|
+
/**
|
|
1317
|
+
* Options for render pipeline callers.
|
|
1318
|
+
*/
|
|
1319
|
+
interface ExecuteRenderOptions {
|
|
1320
|
+
/**
|
|
1321
|
+
* Render mode: fullscreen or inline.
|
|
1322
|
+
* Default: 'fullscreen'
|
|
1323
|
+
*/
|
|
1324
|
+
mode?: "fullscreen" | "inline";
|
|
1325
|
+
/**
|
|
1326
|
+
* Skip notifying layout subscribers.
|
|
1327
|
+
* Use for static/one-shot renders where layout feedback isn't needed.
|
|
1328
|
+
* Default: false
|
|
1329
|
+
*/
|
|
1330
|
+
skipLayoutNotifications?: boolean;
|
|
1331
|
+
/**
|
|
1332
|
+
* Skip scroll state updates.
|
|
1333
|
+
* Use for fresh render comparisons (SILVERY_STRICT) to avoid mutating state.
|
|
1334
|
+
* Default: false
|
|
1335
|
+
*/
|
|
1336
|
+
skipScrollStateUpdates?: boolean;
|
|
1337
|
+
/**
|
|
1338
|
+
* Number of lines written to stdout between renders (inline mode only).
|
|
1339
|
+
* Used to adjust cursor positioning when external code (e.g., useScrollback)
|
|
1340
|
+
* writes directly to stdout between renders.
|
|
1341
|
+
* Default: 0
|
|
1342
|
+
*/
|
|
1343
|
+
scrollbackOffset?: number;
|
|
1344
|
+
/**
|
|
1345
|
+
* Terminal height in rows (inline mode only).
|
|
1346
|
+
* Used to clamp cursor-up offset when content exceeds terminal height.
|
|
1347
|
+
* Without this, content taller than the terminal causes rendering corruption
|
|
1348
|
+
* because cursor-up can't reach lines that scrolled off screen.
|
|
1349
|
+
*/
|
|
1350
|
+
termRows?: number;
|
|
1351
|
+
/**
|
|
1352
|
+
* Cursor position from useCursor() (inline mode only).
|
|
1353
|
+
* When provided, the output phase positions the real terminal cursor
|
|
1354
|
+
* at this location instead of leaving it at the end of content.
|
|
1355
|
+
*/
|
|
1356
|
+
cursorPos?: CursorState | null;
|
|
1357
|
+
}
|
|
1358
|
+
/**
|
|
1359
|
+
* Pipeline configuration from withRender().
|
|
1360
|
+
* Carries term-scoped width measurer and output phase.
|
|
1361
|
+
*/
|
|
1362
|
+
interface PipelineConfig {
|
|
1363
|
+
/** Width measurer scoped to terminal capabilities */
|
|
1364
|
+
readonly measurer: Measurer;
|
|
1365
|
+
/** Output phase function scoped to terminal capabilities */
|
|
1366
|
+
readonly outputPhaseFn: OutputPhaseFn;
|
|
1367
|
+
}
|
|
1368
|
+
//#endregion
|
|
1369
|
+
//#region packages/ag/src/cls.d.ts
|
|
1370
|
+
/**
|
|
1371
|
+
* Why a layout shift happened. Used to filter "expected" shifts (the user
|
|
1372
|
+
* scrolled, content arrived in a streaming view) from "unexpected" shifts
|
|
1373
|
+
* (a code fence resized mid-stream, a status line bounced) — only the
|
|
1374
|
+
* latter fail CLS strict checks.
|
|
1375
|
+
*/
|
|
1376
|
+
type ReflowReason = "user-action" | "unexpected" | "animation" | "content-arrival";
|
|
1377
|
+
/**
|
|
1378
|
+
* Classifier function — given a block + rect transition, returns the
|
|
1379
|
+
* reflow reason. The capture API passes a classifier that inspects layout
|
|
1380
|
+
* context (user-action vs streamed content vs unsolicited reflow). Default
|
|
1381
|
+
* classifier returns "unexpected" — most pessimistic, captures the bug
|
|
1382
|
+
* class CLS is designed to detect.
|
|
1383
|
+
*/
|
|
1384
|
+
type ReasonClassifier = (blockId: string, fromRect: Rect, toRect: Rect, frameTimestamp: number) => ReflowReason;
|
|
1385
|
+
interface LayoutShift {
|
|
1386
|
+
/** Stable identifier for the shifted block. */
|
|
1387
|
+
blockId: string;
|
|
1388
|
+
/** Position + size in the previous frame. */
|
|
1389
|
+
fromRect: Rect;
|
|
1390
|
+
/** Position + size in the current frame. */
|
|
1391
|
+
toRect: Rect;
|
|
1392
|
+
/** Wall-clock timestamp at frame end (ms since epoch). */
|
|
1393
|
+
frameTimestamp: number;
|
|
1394
|
+
/** Why the shift happened — see ReflowReason for taxonomy. */
|
|
1395
|
+
reflowReason: ReflowReason;
|
|
1396
|
+
}
|
|
1397
|
+
interface CLSReport {
|
|
1398
|
+
/** All shifts observed in the capture window. */
|
|
1399
|
+
shifts: readonly LayoutShift[];
|
|
1400
|
+
/** Sum of (area × distance) over all shifts (CLS metric proper). */
|
|
1401
|
+
cumulativeScore: number;
|
|
1402
|
+
/** Subset of shifts with reflowReason="unexpected" — the actionable ones. */
|
|
1403
|
+
unexpectedShifts: readonly LayoutShift[];
|
|
1404
|
+
}
|
|
1405
|
+
//#endregion
|
|
1406
|
+
//#region packages/ag-react/src/debug/render-path.d.ts
|
|
1407
|
+
/**
|
|
1408
|
+
* A summary of one node in a render-path or mount-tree dump.
|
|
1409
|
+
* The shape is JSON-serializable for snapshot tests.
|
|
1410
|
+
*/
|
|
1411
|
+
interface RenderPathNode {
|
|
1412
|
+
/** Component name (data-component prop, or `${hostType}#${testID|id}`, or hostType) */
|
|
1413
|
+
name: string;
|
|
1414
|
+
/** AgNode host type (silvery-box, silvery-text, silvery-root) */
|
|
1415
|
+
type: AgNodeType;
|
|
1416
|
+
/** Optional content-relative position from layout phase */
|
|
1417
|
+
boxRect?: {
|
|
1418
|
+
x: number;
|
|
1419
|
+
y: number;
|
|
1420
|
+
width: number;
|
|
1421
|
+
height: number;
|
|
1422
|
+
};
|
|
1423
|
+
}
|
|
1424
|
+
/** Recursive mount tree — name + children for snapshot-friendly dumps. */
|
|
1425
|
+
interface MountTree extends RenderPathNode {
|
|
1426
|
+
children: MountTree[];
|
|
1427
|
+
}
|
|
1428
|
+
//#endregion
|
|
1429
|
+
//#region packages/test/src/auto-locator.d.ts
|
|
1430
|
+
/**
|
|
1431
|
+
* Filter options for locator narrowing
|
|
1432
|
+
*/
|
|
1433
|
+
interface FilterOptions {
|
|
1434
|
+
/** Match nodes containing this text */
|
|
1435
|
+
hasText?: string | RegExp;
|
|
1436
|
+
/** Match nodes with this testID */
|
|
1437
|
+
hasTestId?: string;
|
|
1438
|
+
/** Match nodes with this attribute value */
|
|
1439
|
+
has?: {
|
|
1440
|
+
attr: string;
|
|
1441
|
+
value?: string;
|
|
1442
|
+
};
|
|
1443
|
+
}
|
|
1444
|
+
/**
|
|
1445
|
+
* AutoLocator interface - lazy, self-refreshing reference to nodes
|
|
1446
|
+
*/
|
|
1447
|
+
interface AutoLocator {
|
|
1448
|
+
getByText(text: string | RegExp): AutoLocator;
|
|
1449
|
+
getByTestId(id: string): AutoLocator;
|
|
1450
|
+
locator(selector: string): AutoLocator;
|
|
1451
|
+
filter(options: FilterOptions): AutoLocator;
|
|
1452
|
+
filter(predicate: (node: AgNode) => boolean): AutoLocator;
|
|
1453
|
+
first(): AutoLocator;
|
|
1454
|
+
last(): AutoLocator;
|
|
1455
|
+
nth(index: number): AutoLocator;
|
|
1456
|
+
resolve(): AgNode | null;
|
|
1457
|
+
resolveAll(): AgNode[];
|
|
1458
|
+
count(): number;
|
|
1459
|
+
textContent(): string;
|
|
1460
|
+
getAttribute(name: string): string | undefined;
|
|
1461
|
+
boundingBox(): Rect | null;
|
|
1462
|
+
isVisible(): boolean;
|
|
1463
|
+
}
|
|
1464
|
+
/**
|
|
1465
|
+
* Create an AutoLocator from a container getter function.
|
|
1466
|
+
* The getter is called fresh on each resolution.
|
|
1467
|
+
*/
|
|
1468
|
+
declare function createAutoLocator(getContainer: () => AgNode, opts?: {
|
|
1469
|
+
strict?: boolean;
|
|
1470
|
+
}): AutoLocator;
|
|
1471
|
+
//#endregion
|
|
1472
|
+
//#region packages/ag-term/src/app.d.ts
|
|
1473
|
+
/**
|
|
1474
|
+
* App interface - unified return type from render()
|
|
1475
|
+
*/
|
|
1476
|
+
/**
|
|
1477
|
+
* Fluent chain of App actions. Each action method returns `ChainableApp`
|
|
1478
|
+
* — a `PromiseLike<App>` that exposes the same action methods so calls
|
|
1479
|
+
* can be composed without explicit `await` between every step.
|
|
1480
|
+
*
|
|
1481
|
+
* ```ts
|
|
1482
|
+
* await app.keyDown("Super").hover(x, y).keyUp("Super")
|
|
1483
|
+
* ```
|
|
1484
|
+
*
|
|
1485
|
+
* The returned chain `.then`-s into the App, so existing `await
|
|
1486
|
+
* app.action(...)` calls continue to receive the App instance — the
|
|
1487
|
+
* change is purely additive at the call site.
|
|
1488
|
+
*
|
|
1489
|
+
* Bead: @km/silvery/fluent-chain-actions.
|
|
1490
|
+
*/
|
|
1491
|
+
interface ChainableApp extends PromiseLike<App> {
|
|
1492
|
+
press(key: string): ChainableApp;
|
|
1493
|
+
keyDown(key: string): ChainableApp;
|
|
1494
|
+
keyUp(key: string): ChainableApp;
|
|
1495
|
+
pressSequence(...keys: string[]): ChainableApp;
|
|
1496
|
+
type(text: string): ChainableApp;
|
|
1497
|
+
click(x: number, y: number, options?: {
|
|
1498
|
+
button?: number;
|
|
1499
|
+
shift?: boolean;
|
|
1500
|
+
meta?: boolean;
|
|
1501
|
+
ctrl?: boolean;
|
|
1502
|
+
cmd?: boolean;
|
|
1503
|
+
}): ChainableApp;
|
|
1504
|
+
doubleClick(x: number, y: number, options?: {
|
|
1505
|
+
button?: number;
|
|
1506
|
+
shift?: boolean;
|
|
1507
|
+
meta?: boolean;
|
|
1508
|
+
ctrl?: boolean;
|
|
1509
|
+
cmd?: boolean;
|
|
1510
|
+
}): ChainableApp;
|
|
1511
|
+
hover(x: number, y: number, options?: {
|
|
1512
|
+
shift?: boolean;
|
|
1513
|
+
meta?: boolean;
|
|
1514
|
+
ctrl?: boolean;
|
|
1515
|
+
cmd?: boolean;
|
|
1516
|
+
}): ChainableApp;
|
|
1517
|
+
wheel(x: number, y: number, delta: number, options?: {
|
|
1518
|
+
shift?: boolean;
|
|
1519
|
+
meta?: boolean;
|
|
1520
|
+
ctrl?: boolean;
|
|
1521
|
+
cmd?: boolean;
|
|
1522
|
+
}): ChainableApp;
|
|
1523
|
+
}
|
|
1524
|
+
interface App {
|
|
1525
|
+
/** Full rendered text (no ANSI codes) */
|
|
1526
|
+
readonly text: string;
|
|
1527
|
+
/** Full rendered text with ANSI styling */
|
|
1528
|
+
readonly ansi: string;
|
|
1529
|
+
/** Per-line plain text array (no ANSI codes) */
|
|
1530
|
+
readonly lines: string[];
|
|
1531
|
+
/** Frame width in terminal columns */
|
|
1532
|
+
readonly width: number;
|
|
1533
|
+
/** Frame height in terminal rows */
|
|
1534
|
+
readonly height: number;
|
|
1535
|
+
/** Get the cell at the given column and row (resolved styling) */
|
|
1536
|
+
cell(col: number, row: number): FrameCell;
|
|
1537
|
+
/** Check whether the plain text contains the given substring */
|
|
1538
|
+
containsText(text: string): boolean;
|
|
1539
|
+
/** Get node at content coordinates */
|
|
1540
|
+
nodeAt(x: number, y: number): AgNode | null;
|
|
1541
|
+
/** Get locator by testID attribute */
|
|
1542
|
+
getByTestId(id: string): AutoLocator;
|
|
1543
|
+
/** Get locator by text content */
|
|
1544
|
+
getByText(text: string | RegExp): AutoLocator;
|
|
1545
|
+
/** Get locator by CSS-style selector */
|
|
1546
|
+
locator(selector: string): AutoLocator;
|
|
1547
|
+
/**
|
|
1548
|
+
* Send a single key press. Emits the bare Kitty CSI u shape
|
|
1549
|
+
* (no `:eventType` byte) — the parser defaults to "press" without
|
|
1550
|
+
* one. Equivalent to a full down+up keystroke for tests asserting
|
|
1551
|
+
* "user typed key X". For held-state scenarios (Cmd-hover popovers,
|
|
1552
|
+
* multi-key chords) use `keyDown` / `keyUp` which emit explicit
|
|
1553
|
+
* `:1` (press) and `:3` (release) event-type bytes.
|
|
1554
|
+
*
|
|
1555
|
+
* Both bare-press and explicit-`:1` shapes parse to
|
|
1556
|
+
* `{ eventType: "press" | undefined }` respectively; consumers that
|
|
1557
|
+
* test `eventType !== "release"` see them identically. The shape
|
|
1558
|
+
* difference is intentional: bare = transient single event, typed =
|
|
1559
|
+
* paired down/up lifecycle. Bead:
|
|
1560
|
+
* @km/silvery/keydown-keyup-test-primitives.
|
|
1561
|
+
*/
|
|
1562
|
+
press(key: string): ChainableApp;
|
|
1563
|
+
/**
|
|
1564
|
+
* Send a key DOWN event without auto-release. Useful for held-modifier
|
|
1565
|
+
* scenarios — e.g., `app.keyDown("Super")` then `app.hover(x, y)` then
|
|
1566
|
+
* `app.keyUp("Super")` to drive a Cmd-hover popover that opens after a
|
|
1567
|
+
* dwell timer reads the (still-held) modifier from the input store.
|
|
1568
|
+
*
|
|
1569
|
+
* Requires Kitty keyboard protocol (`kittyMode: true`) for modifier keys —
|
|
1570
|
+
* legacy ANSI cannot represent Super (Cmd) alone.
|
|
1571
|
+
*
|
|
1572
|
+
* For a single press+release event (the common case) use `press()`. Bead:
|
|
1573
|
+
* @km/silvery/keydown-keyup-test-primitives.
|
|
1574
|
+
*/
|
|
1575
|
+
keyDown(key: string): ChainableApp;
|
|
1576
|
+
/**
|
|
1577
|
+
* Send a key UP event. Pairs with `keyDown(key)`. Drops the implicit
|
|
1578
|
+
* modifier state (mouseState.keyboardModifiers + the input-store
|
|
1579
|
+
* modifier tracker) so subsequent events run without the modifier
|
|
1580
|
+
* asserted. Bead: @km/silvery/keydown-keyup-test-primitives.
|
|
1581
|
+
*/
|
|
1582
|
+
keyUp(key: string): ChainableApp;
|
|
1583
|
+
/** Send multiple key presses */
|
|
1584
|
+
pressSequence(...keys: string[]): ChainableApp;
|
|
1585
|
+
/** Type text input */
|
|
1586
|
+
type(text: string): ChainableApp;
|
|
1587
|
+
/** Simulate a mouse click at (x, y) terminal coordinates */
|
|
1588
|
+
click(x: number, y: number, options?: {
|
|
1589
|
+
button?: number;
|
|
1590
|
+
shift?: boolean;
|
|
1591
|
+
meta?: boolean;
|
|
1592
|
+
ctrl?: boolean;
|
|
1593
|
+
cmd?: boolean;
|
|
1594
|
+
}): ChainableApp;
|
|
1595
|
+
/** Simulate a double-click at (x, y) terminal coordinates */
|
|
1596
|
+
doubleClick(x: number, y: number, options?: {
|
|
1597
|
+
button?: number;
|
|
1598
|
+
shift?: boolean;
|
|
1599
|
+
meta?: boolean;
|
|
1600
|
+
ctrl?: boolean;
|
|
1601
|
+
cmd?: boolean;
|
|
1602
|
+
}): ChainableApp;
|
|
1603
|
+
/** Simulate a mouse move/hover at (x, y) terminal coordinates */
|
|
1604
|
+
hover(x: number, y: number, options?: {
|
|
1605
|
+
shift?: boolean;
|
|
1606
|
+
meta?: boolean;
|
|
1607
|
+
ctrl?: boolean;
|
|
1608
|
+
cmd?: boolean;
|
|
1609
|
+
}): ChainableApp;
|
|
1610
|
+
/** Simulate a mouse wheel event at (x, y) with delta (-1=up, +1=down) */
|
|
1611
|
+
wheel(x: number, y: number, delta: number, options?: {
|
|
1612
|
+
shift?: boolean;
|
|
1613
|
+
meta?: boolean;
|
|
1614
|
+
ctrl?: boolean;
|
|
1615
|
+
cmd?: boolean;
|
|
1616
|
+
}): ChainableApp;
|
|
1617
|
+
/** Resize the virtual terminal and re-render. Only available in test renderer. */
|
|
1618
|
+
resize(cols: number, rows: number): void;
|
|
1619
|
+
/** Wait until app exits */
|
|
1620
|
+
run(): Promise<void>;
|
|
1621
|
+
/** Bound terminal for screen-space access */
|
|
1622
|
+
readonly term: BoundTerm;
|
|
1623
|
+
/** Re-render with a new element */
|
|
1624
|
+
rerender(element: ReactNode): void;
|
|
1625
|
+
/** Unmount the component and clean up */
|
|
1626
|
+
unmount(): void;
|
|
1627
|
+
/** Dispose (alias for unmount) — enables `using` */
|
|
1628
|
+
[Symbol.dispose](): void;
|
|
1629
|
+
/** Promise that resolves when the app exits (alias for run()) */
|
|
1630
|
+
waitUntilExit(): Promise<void>;
|
|
1631
|
+
/**
|
|
1632
|
+
* Drain additional commit / layout cycles until layout reports stable
|
|
1633
|
+
* (no node dirty, no pending React commit) OR a budget cap is reached
|
|
1634
|
+
* (default 20 passes / 50ms wall clock).
|
|
1635
|
+
*
|
|
1636
|
+
* The default test harness exposes the post-`MAX_CONVERGENCE_PASSES`
|
|
1637
|
+
* frame — matching what production silvery commits on first paint.
|
|
1638
|
+
* Most tests don't need to wait further: keyboard / mouse input each
|
|
1639
|
+
* runs its own bounded-convergence loop, so an assertion after `press()`
|
|
1640
|
+
* already sees post-convergence state for that batch.
|
|
1641
|
+
*
|
|
1642
|
+
* This method is for assertions on layout state IMMEDIATELY after mount
|
|
1643
|
+
* that require chains needing more than `MAX_CONVERGENCE_PASSES` passes
|
|
1644
|
+
* to settle (rare with the layout-signals primitive, common with older
|
|
1645
|
+
* useState+onLayout chains). Returns once stable so tests can assert
|
|
1646
|
+
* post-convergence text/layout.
|
|
1647
|
+
*
|
|
1648
|
+
* Resolves without throwing even when the cap is hit — an infinitely
|
|
1649
|
+
* non-converging app is a structural bug surfaced by SILVERY_STRICT's
|
|
1650
|
+
* `assertBoundedConvergence`, not a test-author concern here.
|
|
1651
|
+
*
|
|
1652
|
+
* Bead: `@km/silvery/test-harness-convergence-cap-parity`.
|
|
1653
|
+
*/
|
|
1654
|
+
waitForLayoutStable(opts?: {
|
|
1655
|
+
timeoutMs?: number;
|
|
1656
|
+
maxPasses?: number;
|
|
1657
|
+
}): Promise<void>;
|
|
1658
|
+
/**
|
|
1659
|
+
* Begin a CLS capture window. Layout shifts that occur between this call
|
|
1660
|
+
* and the next `endCLSCapture()` are recorded with the supplied
|
|
1661
|
+
* `ReasonClassifier` (default: every shift labeled "unexpected"). Use
|
|
1662
|
+
* `waitForLayoutStable()` to drain pending layout passes before reading
|
|
1663
|
+
* the report.
|
|
1664
|
+
*
|
|
1665
|
+
* Throws if a capture is already active — call `endCLSCapture()` or
|
|
1666
|
+
* `cancelCLSCapture()` first. The capture is process-wide (single active
|
|
1667
|
+
* recorder per process); two parallel captures on different App
|
|
1668
|
+
* instances is not supported by design.
|
|
1669
|
+
*
|
|
1670
|
+
* Bead: km-silvery.cls-instrumentation-primitive
|
|
1671
|
+
*/
|
|
1672
|
+
beginCLSCapture(classifier?: ReasonClassifier): void;
|
|
1673
|
+
/**
|
|
1674
|
+
* End the active CLS capture and return the aggregated report. When
|
|
1675
|
+
* `SILVERY_STRICT=cls` (or tier 2+) is enabled, throws
|
|
1676
|
+
* `UnexpectedLayoutShiftError` if any shift has reflowReason="unexpected"
|
|
1677
|
+
* — failing-fast under STRICT lets close-gate tests skip explicit
|
|
1678
|
+
* assertions and rely on the umbrella env var.
|
|
1679
|
+
*
|
|
1680
|
+
* Throws if no capture is active.
|
|
1681
|
+
*
|
|
1682
|
+
* Bead: km-silvery.cls-instrumentation-primitive
|
|
1683
|
+
*/
|
|
1684
|
+
endCLSCapture(): CLSReport;
|
|
1685
|
+
/**
|
|
1686
|
+
* Cancel the active CLS capture without producing a report. Idempotent
|
|
1687
|
+
* — safe to call when no capture is active. Used by test-cleanup paths
|
|
1688
|
+
* that need to bail without asserting.
|
|
1689
|
+
*
|
|
1690
|
+
* Bead: km-silvery.cls-instrumentation-primitive
|
|
1691
|
+
*/
|
|
1692
|
+
cancelCLSCapture(): void;
|
|
1693
|
+
/** Clear the terminal output */
|
|
1694
|
+
clear(): void;
|
|
1695
|
+
/** Render current buffer to PNG. Requires Playwright (lazy-loaded on first call). */
|
|
1696
|
+
screenshot(outputPath?: string): Promise<Buffer>;
|
|
1697
|
+
/** Print component tree to console */
|
|
1698
|
+
debug(): void;
|
|
1699
|
+
/** Render the current tree from scratch (no incremental buffer reuse).
|
|
1700
|
+
* Returns the fresh buffer without updating incremental state.
|
|
1701
|
+
* Only available in test renderer - throws otherwise. */
|
|
1702
|
+
freshRender(): TerminalBuffer;
|
|
1703
|
+
/** Check if exit() was called */
|
|
1704
|
+
exitCalled(): boolean;
|
|
1705
|
+
/** Get error passed to exit() */
|
|
1706
|
+
exitError(): Error | undefined;
|
|
1707
|
+
/** Send raw stdin input (for sync test helpers; prefer app.press() for new code) */
|
|
1708
|
+
readonly stdin: {
|
|
1709
|
+
write: (data: string) => void;
|
|
1710
|
+
};
|
|
1711
|
+
/** All rendered frames (internal) */
|
|
1712
|
+
readonly frames: string[];
|
|
1713
|
+
/** Get last frame with ANSI codes (internal - use app.ansi instead) */
|
|
1714
|
+
lastFrame(): string | undefined;
|
|
1715
|
+
/** Get last buffer (internal - use app.term.buffer instead) */
|
|
1716
|
+
lastBuffer(): TerminalBuffer | undefined;
|
|
1717
|
+
/** Get last frame as plain text (internal - use app.text instead) */
|
|
1718
|
+
lastFrameText(): string | undefined;
|
|
1719
|
+
/** Get container root node (internal - use app.locator() instead) */
|
|
1720
|
+
getContainer(): AgNode;
|
|
1721
|
+
/** Focus a node by testID */
|
|
1722
|
+
focus(testID: string): void;
|
|
1723
|
+
/** Get the focus path from focused node to root (testID[]) */
|
|
1724
|
+
getFocusPath(): string[];
|
|
1725
|
+
/** Direct access to the FocusManager instance */
|
|
1726
|
+
readonly focusManager: FocusManager;
|
|
1727
|
+
/** Get the current cursor state for this silvery instance (per-instance, not global). */
|
|
1728
|
+
getCursorState(): CursorState | null;
|
|
1729
|
+
/**
|
|
1730
|
+
* Return the parent chain from the container root down to the first
|
|
1731
|
+
* AgNode whose component name matches `componentName`. Empty array if
|
|
1732
|
+
* not found. Useful for asserting structural invariants in tests and
|
|
1733
|
+
* for bead-investigation static traces ("does ToolBlock actually
|
|
1734
|
+
* render inside Content.Body[width=full]?").
|
|
1735
|
+
*
|
|
1736
|
+
* See `@silvery/ag-react/debug/render-path` for the underlying API.
|
|
1737
|
+
*/
|
|
1738
|
+
renderPath(componentName: string): RenderPathNode[];
|
|
1739
|
+
/**
|
|
1740
|
+
* Recursive JSON dump of the entire mount tree. Useful for snapshot
|
|
1741
|
+
* tests asserting on structural invariants.
|
|
1742
|
+
*/
|
|
1743
|
+
mountTree(): MountTree;
|
|
1744
|
+
}
|
|
1745
|
+
//#endregion
|
|
1746
|
+
//#region packages/ag-term/src/layout-engine.d.ts
|
|
1747
|
+
/**
|
|
1748
|
+
* Branded types prevent accidentally mixing up layout constant categories.
|
|
1749
|
+
* E.g., you can't pass an AlignValue where a FlexDirectionValue is expected.
|
|
1750
|
+
*/
|
|
1751
|
+
type FlexDirectionValue = number & {
|
|
1752
|
+
readonly __brand: "FlexDirection";
|
|
1753
|
+
};
|
|
1754
|
+
type WrapValue = number & {
|
|
1755
|
+
readonly __brand: "Wrap";
|
|
1756
|
+
};
|
|
1757
|
+
type AlignValue = number & {
|
|
1758
|
+
readonly __brand: "Align";
|
|
1759
|
+
};
|
|
1760
|
+
type JustifyValue = number & {
|
|
1761
|
+
readonly __brand: "Justify";
|
|
1762
|
+
};
|
|
1763
|
+
type EdgeValue = number & {
|
|
1764
|
+
readonly __brand: "Edge";
|
|
1765
|
+
};
|
|
1766
|
+
type GutterValue = number & {
|
|
1767
|
+
readonly __brand: "Gutter";
|
|
1768
|
+
};
|
|
1769
|
+
type DisplayValue = number & {
|
|
1770
|
+
readonly __brand: "Display";
|
|
1771
|
+
};
|
|
1772
|
+
type PositionTypeValue = number & {
|
|
1773
|
+
readonly __brand: "PositionType";
|
|
1774
|
+
};
|
|
1775
|
+
type OverflowValue = number & {
|
|
1776
|
+
readonly __brand: "Overflow";
|
|
1777
|
+
};
|
|
1778
|
+
type DirectionValue = number & {
|
|
1779
|
+
readonly __brand: "Direction";
|
|
1780
|
+
};
|
|
1781
|
+
type MeasureModeValue = number & {
|
|
1782
|
+
readonly __brand: "MeasureMode";
|
|
1783
|
+
};
|
|
1784
|
+
/**
|
|
1785
|
+
* Constants for layout configuration.
|
|
1786
|
+
* These are the same across Yoga and Flexily.
|
|
1787
|
+
* Uses branded types for compile-time safety.
|
|
1788
|
+
*/
|
|
1789
|
+
interface LayoutConstants {
|
|
1790
|
+
FLEX_DIRECTION_COLUMN: FlexDirectionValue;
|
|
1791
|
+
FLEX_DIRECTION_COLUMN_REVERSE: FlexDirectionValue;
|
|
1792
|
+
FLEX_DIRECTION_ROW: FlexDirectionValue;
|
|
1793
|
+
FLEX_DIRECTION_ROW_REVERSE: FlexDirectionValue;
|
|
1794
|
+
WRAP_NO_WRAP: WrapValue;
|
|
1795
|
+
WRAP_WRAP: WrapValue;
|
|
1796
|
+
WRAP_WRAP_REVERSE: WrapValue;
|
|
1797
|
+
ALIGN_AUTO: AlignValue;
|
|
1798
|
+
ALIGN_FLEX_START: AlignValue;
|
|
1799
|
+
ALIGN_CENTER: AlignValue;
|
|
1800
|
+
ALIGN_FLEX_END: AlignValue;
|
|
1801
|
+
ALIGN_STRETCH: AlignValue;
|
|
1802
|
+
ALIGN_BASELINE: AlignValue;
|
|
1803
|
+
ALIGN_SPACE_BETWEEN: AlignValue;
|
|
1804
|
+
ALIGN_SPACE_AROUND: AlignValue;
|
|
1805
|
+
ALIGN_SPACE_EVENLY: AlignValue;
|
|
1806
|
+
JUSTIFY_FLEX_START: JustifyValue;
|
|
1807
|
+
JUSTIFY_CENTER: JustifyValue;
|
|
1808
|
+
JUSTIFY_FLEX_END: JustifyValue;
|
|
1809
|
+
JUSTIFY_SPACE_BETWEEN: JustifyValue;
|
|
1810
|
+
JUSTIFY_SPACE_AROUND: JustifyValue;
|
|
1811
|
+
JUSTIFY_SPACE_EVENLY: JustifyValue;
|
|
1812
|
+
EDGE_LEFT: EdgeValue;
|
|
1813
|
+
EDGE_TOP: EdgeValue;
|
|
1814
|
+
EDGE_RIGHT: EdgeValue;
|
|
1815
|
+
EDGE_BOTTOM: EdgeValue;
|
|
1816
|
+
EDGE_HORIZONTAL: EdgeValue;
|
|
1817
|
+
EDGE_VERTICAL: EdgeValue;
|
|
1818
|
+
EDGE_ALL: EdgeValue;
|
|
1819
|
+
GUTTER_COLUMN: GutterValue;
|
|
1820
|
+
GUTTER_ROW: GutterValue;
|
|
1821
|
+
GUTTER_ALL: GutterValue;
|
|
1822
|
+
DISPLAY_FLEX: DisplayValue;
|
|
1823
|
+
DISPLAY_NONE: DisplayValue;
|
|
1824
|
+
POSITION_TYPE_STATIC: PositionTypeValue;
|
|
1825
|
+
POSITION_TYPE_RELATIVE: PositionTypeValue;
|
|
1826
|
+
POSITION_TYPE_ABSOLUTE: PositionTypeValue;
|
|
1827
|
+
OVERFLOW_VISIBLE: OverflowValue;
|
|
1828
|
+
OVERFLOW_HIDDEN: OverflowValue;
|
|
1829
|
+
OVERFLOW_SCROLL: OverflowValue;
|
|
1830
|
+
DIRECTION_LTR: DirectionValue;
|
|
1831
|
+
MEASURE_MODE_UNDEFINED: MeasureModeValue;
|
|
1832
|
+
MEASURE_MODE_EXACTLY: MeasureModeValue;
|
|
1833
|
+
MEASURE_MODE_AT_MOST: MeasureModeValue;
|
|
1834
|
+
}
|
|
1835
|
+
/**
|
|
1836
|
+
* Declares which engine-native primitives a `LayoutEngine` supports.
|
|
1837
|
+
*
|
|
1838
|
+
* silvery is engine-pluggable — `flexily` (pure JS) and `yoga` (WASM) ship as peer
|
|
1839
|
+
* adapters. The responsive-layout reframe (Phase A0.1+) adds engine-native container
|
|
1840
|
+
* queries, fitWidth, size containment, cq* units, and CSS math functions. These
|
|
1841
|
+
* primitives can only be implemented in `flexily` (yoga's WASM boundary doesn't expose
|
|
1842
|
+
* inter-pass child-style mutation).
|
|
1843
|
+
*
|
|
1844
|
+
* Each adapter declares which capabilities it supports. Consumers (`<Box fitWidth>`,
|
|
1845
|
+
* `<Box containerQueries>`, etc.) gate on capability presence via `requireCapability()`
|
|
1846
|
+
* — under yoga, those primitives THROW at first paint with a one-line fix instruction
|
|
1847
|
+
* (switch via `SILVERY_ENGINE=flexily`).
|
|
1848
|
+
*/
|
|
1849
|
+
interface EngineCapabilities {
|
|
1850
|
+
/** Container queries: `<Box containerQueries={...}>` + `containerType` / `containerName` */
|
|
1851
|
+
readonly containerQueries: boolean;
|
|
1852
|
+
/** Size containment: `<Box containSize>` */
|
|
1853
|
+
readonly containSize: boolean;
|
|
1854
|
+
/** Container-query units in style values: `cqi`, `cqmin`, `cqb`, `cqmax` */
|
|
1855
|
+
readonly containerQueryUnits: boolean;
|
|
1856
|
+
/** fitWidth Box prop: `<Box fitWidth={[80, 120, "100cqi"]}>` */
|
|
1857
|
+
readonly fitWidth: boolean;
|
|
1858
|
+
/** CSS math functions in style values: `min()`, `max()`, `clamp()` */
|
|
1859
|
+
readonly styleMathFunctions: boolean;
|
|
1860
|
+
/** Inter-pass child-style mutation hook (underlying mechanism for CQ + fitWidth) */
|
|
1861
|
+
readonly childStyleMutation: boolean;
|
|
1862
|
+
}
|
|
1863
|
+
/**
|
|
1864
|
+
* Abstract layout engine interface.
|
|
1865
|
+
* Implementations can wrap Yoga, Flexily, or other layout engines.
|
|
1866
|
+
*/
|
|
1867
|
+
interface LayoutEngine {
|
|
1868
|
+
/** Create a new layout node */
|
|
1869
|
+
createNode(): LayoutNode$1;
|
|
1870
|
+
/** Layout constants for this engine */
|
|
1871
|
+
readonly constants: LayoutConstants;
|
|
1872
|
+
/** Engine name for debugging */
|
|
1873
|
+
readonly name: string;
|
|
1874
|
+
/** Which engine-native primitives this adapter supports (Phase A0.0.5+). */
|
|
1875
|
+
readonly capabilities: EngineCapabilities;
|
|
1876
|
+
}
|
|
1877
|
+
/**
|
|
1878
|
+
* Set the global layout engine instance.
|
|
1879
|
+
* Must be called before rendering.
|
|
1880
|
+
*/
|
|
1881
|
+
declare function setLayoutEngine(engine: LayoutEngine): void;
|
|
1882
|
+
/**
|
|
1883
|
+
* Check if a layout engine is initialized.
|
|
1884
|
+
*/
|
|
1885
|
+
declare function isLayoutEngineInitialized(): boolean;
|
|
1886
|
+
/**
|
|
1887
|
+
* Layout engine type for configuration.
|
|
1888
|
+
*
|
|
1889
|
+
* - 'flexily': Zero-allocation Flexily (default, optimized for high-frequency layout)
|
|
1890
|
+
* - 'flexily-classic': Classic Flexily algorithm (for debugging/compatibility)
|
|
1891
|
+
* - 'yoga': Facebook's WASM-based flexbox (most mature)
|
|
1892
|
+
*/
|
|
1893
|
+
type LayoutEngineType = "flexily" | "yoga";
|
|
1894
|
+
//#endregion
|
|
1895
|
+
//#region packages/ag-term/src/adapters/canvas-adapter.d.ts
|
|
1896
|
+
interface CanvasAdapterConfig {
|
|
1897
|
+
/** Font size in pixels (default: 14) */
|
|
1898
|
+
fontSize?: number;
|
|
1899
|
+
/** Font family (default: 'monospace') */
|
|
1900
|
+
fontFamily?: string;
|
|
1901
|
+
/** Line height multiplier (default: 1.2) */
|
|
1902
|
+
lineHeight?: number;
|
|
1903
|
+
/** Background color (default: '#1e1e1e') */
|
|
1904
|
+
backgroundColor?: string;
|
|
1905
|
+
/** Default foreground color (default: '#d4d4d4') */
|
|
1906
|
+
foregroundColor?: string;
|
|
1907
|
+
/** Monospace mode (default: true). When false, uses proportional font measurement. */
|
|
1908
|
+
monospace?: boolean;
|
|
1909
|
+
/** Device pixel ratio for sharp rendering on HiDPI displays (default: 1) */
|
|
1910
|
+
dpr?: number;
|
|
1911
|
+
}
|
|
1912
|
+
declare class CanvasRenderBuffer implements RenderBuffer {
|
|
1913
|
+
readonly width: number;
|
|
1914
|
+
readonly height: number;
|
|
1915
|
+
readonly canvas: OffscreenCanvas | HTMLCanvasElement;
|
|
1916
|
+
private ctx;
|
|
1917
|
+
private config;
|
|
1918
|
+
private readonly charWidth;
|
|
1919
|
+
private readonly cellHeight;
|
|
1920
|
+
constructor(width: number, height: number, config: Required<CanvasAdapterConfig>);
|
|
1921
|
+
fillRect(x: number, y: number, width: number, height: number, style: RenderStyle): void;
|
|
1922
|
+
drawText(x: number, y: number, text: string, style: RenderStyle): void;
|
|
1923
|
+
/**
|
|
1924
|
+
* Draw underline decorations at pixel coordinates.
|
|
1925
|
+
* Note: px, py are already in pixel coordinates.
|
|
1926
|
+
*/
|
|
1927
|
+
private drawUnderline;
|
|
1928
|
+
drawChar(x: number, y: number, char: string, style: RenderStyle): void;
|
|
1929
|
+
inBounds(x: number, y: number): boolean;
|
|
1930
|
+
fillRoundedRect(x: number, y: number, width: number, height: number, radius: number, fill: string | undefined, stroke: string | undefined, lineWidth?: number): void;
|
|
1931
|
+
}
|
|
1932
|
+
declare function createCanvasAdapter(config?: CanvasAdapterConfig): RenderAdapter;
|
|
1933
|
+
//#endregion
|
|
1934
|
+
//#region packages/ag-term/src/adapters/dom-adapter.d.ts
|
|
1935
|
+
interface DOMAdapterConfig {
|
|
1936
|
+
/** Font size in pixels (default: 14) */
|
|
1937
|
+
fontSize?: number;
|
|
1938
|
+
/** Font family (default: 'monospace') */
|
|
1939
|
+
fontFamily?: string;
|
|
1940
|
+
/** Line height multiplier (default: 1.2) */
|
|
1941
|
+
lineHeight?: number;
|
|
1942
|
+
/** Background color (default: '#1e1e1e') */
|
|
1943
|
+
backgroundColor?: string;
|
|
1944
|
+
/** Default foreground color (default: '#d4d4d4') */
|
|
1945
|
+
foregroundColor?: string;
|
|
1946
|
+
/** CSS class prefix (default: 'silvery') */
|
|
1947
|
+
classPrefix?: string;
|
|
1948
|
+
}
|
|
1949
|
+
declare class DOMRenderBuffer implements RenderBuffer {
|
|
1950
|
+
readonly width: number;
|
|
1951
|
+
readonly height: number;
|
|
1952
|
+
private config;
|
|
1953
|
+
private lines;
|
|
1954
|
+
private backgrounds;
|
|
1955
|
+
private readonly charWidth;
|
|
1956
|
+
private readonly cellHeight;
|
|
1957
|
+
private container;
|
|
1958
|
+
constructor(width: number, height: number, config: Required<DOMAdapterConfig>);
|
|
1959
|
+
/**
|
|
1960
|
+
* Set the container element for rendering.
|
|
1961
|
+
*/
|
|
1962
|
+
setContainer(container: HTMLElement): void;
|
|
1963
|
+
/**
|
|
1964
|
+
* Get the container element.
|
|
1965
|
+
*/
|
|
1966
|
+
getContainer(): HTMLElement | null;
|
|
1967
|
+
fillRect(x: number, y: number, width: number, height: number, style: RenderStyle): void;
|
|
1968
|
+
drawText(x: number, y: number, text: string, style: RenderStyle): void;
|
|
1969
|
+
drawChar(x: number, y: number, char: string, style: RenderStyle): void;
|
|
1970
|
+
inBounds(x: number, y: number): boolean;
|
|
1971
|
+
/**
|
|
1972
|
+
* Render the buffer to the container element.
|
|
1973
|
+
* Coordinates in the buffer are in cell units (cols/rows).
|
|
1974
|
+
* This method converts them to pixel coordinates for DOM positioning.
|
|
1975
|
+
*/
|
|
1976
|
+
render(): void;
|
|
1977
|
+
/**
|
|
1978
|
+
* Clear the buffer.
|
|
1979
|
+
*/
|
|
1980
|
+
clear(): void;
|
|
1981
|
+
}
|
|
1982
|
+
declare function createDOMAdapter(config?: DOMAdapterConfig): RenderAdapter;
|
|
1983
|
+
/**
|
|
1984
|
+
* Inject global CSS styles for silvery DOM rendering.
|
|
1985
|
+
* Call once at application startup if you want default styling.
|
|
1986
|
+
*/
|
|
1987
|
+
declare function injectDOMStyles(classPrefix?: string): void;
|
|
1988
|
+
//#endregion
|
|
1989
|
+
//#region packages/ag-term/src/bracketed-paste.d.ts
|
|
1990
|
+
/**
|
|
1991
|
+
* Bracketed Paste Mode
|
|
1992
|
+
*
|
|
1993
|
+
* Enables bracketed paste so the terminal wraps pasted text with markers.
|
|
1994
|
+
* This lets the app distinguish pasted text from typed input and receive
|
|
1995
|
+
* it as a single event rather than individual keystrokes.
|
|
1996
|
+
*
|
|
1997
|
+
* Protocol: DEC private mode 2004
|
|
1998
|
+
* - Enable: CSI ? 2004 h
|
|
1999
|
+
* - Disable: CSI ? 2004 l
|
|
2000
|
+
* - Paste start marker: CSI 200 ~
|
|
2001
|
+
* - Paste end marker: CSI 201 ~
|
|
2002
|
+
*
|
|
2003
|
+
* Supported by: Ghostty, Kitty, WezTerm, iTerm2, Alacritty, xterm, tmux, foot
|
|
2004
|
+
*/
|
|
2005
|
+
/** Escape sequence that marks the beginning of pasted text */
|
|
2006
|
+
declare const PASTE_START = "\u001B[200~";
|
|
2007
|
+
/** Escape sequence that marks the end of pasted text */
|
|
2008
|
+
declare const PASTE_END = "\u001B[201~";
|
|
2009
|
+
/**
|
|
2010
|
+
* Enable bracketed paste mode.
|
|
2011
|
+
* Writes CSI ? 2004 h to the output stream.
|
|
2012
|
+
*/
|
|
2013
|
+
declare function enableBracketedPaste(stdout: NodeJS.WriteStream): void;
|
|
2014
|
+
/**
|
|
2015
|
+
* Disable bracketed paste mode.
|
|
2016
|
+
* Writes CSI ? 2004 l to the output stream.
|
|
2017
|
+
*/
|
|
2018
|
+
declare function disableBracketedPaste(stdout: NodeJS.WriteStream): void;
|
|
2019
|
+
/** Result of parsing a bracketed paste sequence */
|
|
2020
|
+
interface BracketedPasteResult {
|
|
2021
|
+
type: "paste";
|
|
2022
|
+
content: string;
|
|
2023
|
+
}
|
|
2024
|
+
/**
|
|
2025
|
+
* Detect and extract bracketed paste content from raw terminal input.
|
|
2026
|
+
*
|
|
2027
|
+
* Return semantics (see {@link ProtocolError} for the full contract):
|
|
2028
|
+
* - `null` — input contains no PASTE_START marker (this is not bracketed
|
|
2029
|
+
* paste input). Discriminator-chain "next parser please" signal.
|
|
2030
|
+
* - `throw ProtocolError` — input HAS PASTE_START (we committed to
|
|
2031
|
+
* bracketed paste) but no PASTE_END follows. This indicates either a
|
|
2032
|
+
* stream-split paste (caller should buffer and retry on the next chunk)
|
|
2033
|
+
* or a protocol violation. Loud failure surfaces the gap; the dispatch
|
|
2034
|
+
* layer catches and decides whether to buffer or log.
|
|
2035
|
+
*/
|
|
2036
|
+
declare function parseBracketedPaste(input: string): BracketedPasteResult | null;
|
|
2037
|
+
//#endregion
|
|
2038
|
+
//#region packages/ag-term/src/osc-markers.d.ts
|
|
2039
|
+
/**
|
|
2040
|
+
* OSC 133 Semantic Prompt Markers
|
|
2041
|
+
*
|
|
2042
|
+
* Shell integration protocol that marks prompts and commands in terminal output.
|
|
2043
|
+
* Terminals like iTerm2, Kitty, and WezTerm use these markers to provide
|
|
2044
|
+
* "jump to previous/next prompt" navigation (Cmd+Up/Cmd+Down in iTerm2).
|
|
2045
|
+
*
|
|
2046
|
+
* Protocol: OSC 133
|
|
2047
|
+
* - Prompt start: ESC ] 133 ; A BEL (before user input)
|
|
2048
|
+
* - Prompt end: ESC ] 133 ; B BEL (after user input, before command output)
|
|
2049
|
+
* - Command output start: ESC ] 133 ; C BEL (before command output)
|
|
2050
|
+
* - Command output end: ESC ] 133 ; D ; N BEL (after command output, N = exit code)
|
|
2051
|
+
*
|
|
2052
|
+
* For a chat-style app, each "exchange" (user prompt + assistant response) maps to:
|
|
2053
|
+
* - 133;A before the user's message
|
|
2054
|
+
* - 133;B after the user's message
|
|
2055
|
+
* - 133;C before the assistant's response
|
|
2056
|
+
* - 133;D;0 after the assistant's response
|
|
2057
|
+
*
|
|
2058
|
+
* Supported by: iTerm2, Kitty, WezTerm, foot, Ghostty
|
|
2059
|
+
*/
|
|
2060
|
+
declare const OSC133: {
|
|
2061
|
+
/** Mark prompt start (before user input) */readonly promptStart: "\u001B]133;A\u0007"; /** Mark prompt end (after user input, before command output) */
|
|
2062
|
+
readonly promptEnd: "\u001B]133;B\u0007"; /** Mark command output start */
|
|
2063
|
+
readonly commandStart: "\u001B]133;C\u0007"; /** Mark command output end (exit code defaults to 0 = success) */
|
|
2064
|
+
readonly commandEnd: (exitCode?: number) => string;
|
|
2065
|
+
};
|
|
2066
|
+
//#endregion
|
|
2067
|
+
//#region packages/ag-term/src/kitty-detect.d.ts
|
|
2068
|
+
interface KittyDetectResult {
|
|
2069
|
+
/** Whether the terminal responded to the Kitty protocol query */
|
|
2070
|
+
supported: boolean;
|
|
2071
|
+
/** Bitfield of KittyFlags the terminal reported supporting (0 if unsupported) */
|
|
2072
|
+
flags: number;
|
|
2073
|
+
/** Any non-response data that was read during detection (regular input that arrived) */
|
|
2074
|
+
buffered?: string;
|
|
2075
|
+
}
|
|
2076
|
+
/**
|
|
2077
|
+
* Detect Kitty keyboard protocol support.
|
|
2078
|
+
*
|
|
2079
|
+
* Sends CSI ? u to the terminal and waits for a response.
|
|
2080
|
+
* Supported terminals respond with CSI ? flags u.
|
|
2081
|
+
* Unsupported terminals either ignore the query or echo it.
|
|
2082
|
+
*
|
|
2083
|
+
* @param write Function to write to stdout
|
|
2084
|
+
* @param read Function to read a chunk from stdin (should resolve with data or null on timeout)
|
|
2085
|
+
* @param timeoutMs How long to wait for response (default: 200ms)
|
|
2086
|
+
*/
|
|
2087
|
+
declare function detectKittySupport(write: (data: string) => void, read: (timeoutMs: number) => Promise<string | null>, timeoutMs?: number): Promise<KittyDetectResult>;
|
|
2088
|
+
/**
|
|
2089
|
+
* Detect Kitty support using real stdin/stdout.
|
|
2090
|
+
* Convenience wrapper around detectKittySupport.
|
|
2091
|
+
*/
|
|
2092
|
+
declare function detectKittyFromStdio(stdout: {
|
|
2093
|
+
write: (s: string) => boolean | void;
|
|
2094
|
+
}, stdin: NodeJS.ReadStream, timeoutMs?: number): Promise<KittyDetectResult>;
|
|
2095
|
+
//#endregion
|
|
2096
|
+
//#region packages/ag-term/src/termtest.d.ts
|
|
2097
|
+
/**
|
|
2098
|
+
* Terminal Capability Test
|
|
2099
|
+
*
|
|
2100
|
+
* Renders labeled test patterns for each terminal feature.
|
|
2101
|
+
* Run in any terminal to visually verify what it supports.
|
|
2102
|
+
*
|
|
2103
|
+
* Usage:
|
|
2104
|
+
* import { runTermtest } from "@silvery/ag-react"
|
|
2105
|
+
* runTermtest() // all sections
|
|
2106
|
+
* runTermtest({ sections: ["emoji", "colors"] }) // specific sections
|
|
2107
|
+
*
|
|
2108
|
+
* Compare output across terminals to build/verify profiles.
|
|
2109
|
+
*/
|
|
2110
|
+
/** Available test sections */
|
|
2111
|
+
declare const TERMTEST_SECTIONS: readonly ["sgr", "underline", "colors", "256", "truecolor", "unicode", "emoji", "borders", "inverse", "profile"];
|
|
2112
|
+
type TermtestSection = (typeof TERMTEST_SECTIONS)[number];
|
|
2113
|
+
interface TermtestOptions {
|
|
2114
|
+
/** Writable stream (defaults to process.stdout) */
|
|
2115
|
+
output?: {
|
|
2116
|
+
write(s: string): boolean;
|
|
2117
|
+
};
|
|
2118
|
+
/** Show only these sections. Omit or empty = all sections. */
|
|
2119
|
+
sections?: TermtestSection[];
|
|
2120
|
+
}
|
|
2121
|
+
/**
|
|
2122
|
+
* Run the terminal capability test.
|
|
2123
|
+
* Pass section names to filter: `runTermtest({ sections: ["emoji"] })`
|
|
2124
|
+
*/
|
|
2125
|
+
declare function runTermtest(options?: TermtestOptions): void;
|
|
2126
|
+
//#endregion
|
|
2127
|
+
//#region packages/ag-term/src/text-sizing.d.ts
|
|
2128
|
+
/**
|
|
2129
|
+
* Text Sizing Protocol (OSC 66) -- Kitty v0.40+
|
|
2130
|
+
*
|
|
2131
|
+
* Lets the app specify how many cells a character should occupy.
|
|
2132
|
+
* This solves the measurement/rendering mismatch for Private Use Area (PUA)
|
|
2133
|
+
* characters (nerdfont icons, powerline symbols) that `string-width` reports
|
|
2134
|
+
* as 1-cell but terminals render as 2-cell.
|
|
2135
|
+
*
|
|
2136
|
+
* When OSC 66 is used with w=2, both the app's layout engine and the terminal
|
|
2137
|
+
* agree on the character width, eliminating truncation and misalignment.
|
|
2138
|
+
*
|
|
2139
|
+
* Protocol format:
|
|
2140
|
+
* ESC ] 66 ; w=<width> ; <text> BEL
|
|
2141
|
+
*
|
|
2142
|
+
* Post km-silvery.unicode-plateau Phase 2 (2026-04-23): this module reads
|
|
2143
|
+
* ZERO environment variables. Capability detection lives entirely in
|
|
2144
|
+
* `@silvery/ansi`'s `createTerminalProfile`. Consumers pass `TerminalCaps`
|
|
2145
|
+
* (or an explicit fingerprint string for the probe cache) so the module is
|
|
2146
|
+
* pure w.r.t. environment and browser/canvas targets aren't broken.
|
|
2147
|
+
*
|
|
2148
|
+
* @see https://sw.kovidgoyal.net/kitty/text-sizing-protocol/
|
|
2149
|
+
*/
|
|
2150
|
+
/**
|
|
2151
|
+
* Wrap text in an OSC 66 sequence that tells the terminal to render it
|
|
2152
|
+
* in exactly `width` cells.
|
|
2153
|
+
*/
|
|
2154
|
+
declare function textSized(text: string, width: number): string;
|
|
2155
|
+
/**
|
|
2156
|
+
* Check if a code point is in the Private Use Area (PUA).
|
|
2157
|
+
* Covers BMP PUA (U+E000-U+F8FF) and Supplementary PUA-A/B.
|
|
2158
|
+
*/
|
|
2159
|
+
declare function isPrivateUseArea(cp: number): boolean;
|
|
2160
|
+
/**
|
|
2161
|
+
* Structural subset of the terminal profile's emulator the fingerprint helper
|
|
2162
|
+
* needs. Accepts any object with `program` + `version` strings (typically
|
|
2163
|
+
* `profile.emulator` or `term.emulator`).
|
|
2164
|
+
*/
|
|
2165
|
+
interface FingerprintEmulator {
|
|
2166
|
+
readonly program: string;
|
|
2167
|
+
readonly version: string;
|
|
2168
|
+
}
|
|
2169
|
+
/**
|
|
2170
|
+
* Build a terminal fingerprint for cache keying. Combines `program` +
|
|
2171
|
+
* `version` from the supplied emulator to uniquely identify the terminal
|
|
2172
|
+
* type. Different versions may add/remove OSC 66 support, so version is part
|
|
2173
|
+
* of the key.
|
|
2174
|
+
*
|
|
2175
|
+
* Post unicode-plateau Phase 2: the emulator argument is required — the
|
|
2176
|
+
* legacy env-reading variant is gone. Callers building fingerprints from a
|
|
2177
|
+
* one-shot probe can use `createTerminalProfile().emulator` upstream.
|
|
2178
|
+
*/
|
|
2179
|
+
declare function getTerminalFingerprint(emulator: FingerprintEmulator): string;
|
|
2180
|
+
/** Result of text sizing probe */
|
|
2181
|
+
interface TextSizingProbeResult {
|
|
2182
|
+
supported: boolean;
|
|
2183
|
+
widthOnly: boolean;
|
|
2184
|
+
}
|
|
2185
|
+
/**
|
|
2186
|
+
* Detect terminal support for the text sizing protocol.
|
|
2187
|
+
* Uses cursor position reports (CPR) to check if OSC 66 advances the cursor
|
|
2188
|
+
* by the specified width.
|
|
2189
|
+
*
|
|
2190
|
+
* Results are cached by fingerprint so the probe only runs once per terminal
|
|
2191
|
+
* type per process.
|
|
2192
|
+
*
|
|
2193
|
+
* @param write - Writer function (TUI output)
|
|
2194
|
+
* @param read - Reader function (CPR response source)
|
|
2195
|
+
* @param fingerprint - Cache key. Derived via {@link getTerminalFingerprint}
|
|
2196
|
+
* from `TerminalCaps`; callers inside a running session typically compute
|
|
2197
|
+
* it once from `term.profile.caps`.
|
|
2198
|
+
* @param timeout - Per-probe timeout in ms (default 1000)
|
|
2199
|
+
* @returns Object with `supported` and `widthOnly` flags:
|
|
2200
|
+
* - supported=true, widthOnly=false: full support (scale + width)
|
|
2201
|
+
* - supported=true, widthOnly=true: width mode only
|
|
2202
|
+
* - supported=false: no support
|
|
2203
|
+
*/
|
|
2204
|
+
declare function detectTextSizingSupport(write: (data: string) => void, read: () => Promise<string>, fingerprint: string, timeout?: number): Promise<TextSizingProbeResult>;
|
|
2205
|
+
//#endregion
|
|
2206
|
+
//#region packages/ag-term/src/cursor-query.d.ts
|
|
2207
|
+
/**
|
|
2208
|
+
* CSI 6n Cursor Position Query
|
|
2209
|
+
*
|
|
2210
|
+
* Queries the terminal for the current cursor position using the standard
|
|
2211
|
+
* Device Status Report (DSR) mechanism.
|
|
2212
|
+
*
|
|
2213
|
+
* Protocol:
|
|
2214
|
+
* - Query: CSI 6 n (\x1b[6n)
|
|
2215
|
+
* - Response: CSI {row} ; {col} R (\x1b[{row};{col}R)
|
|
2216
|
+
*
|
|
2217
|
+
* Row and column are 1-indexed in the protocol response.
|
|
2218
|
+
*
|
|
2219
|
+
* Supported by: virtually all terminals (VT100+)
|
|
2220
|
+
*/
|
|
2221
|
+
/**
|
|
2222
|
+
* Query the terminal cursor position.
|
|
2223
|
+
*
|
|
2224
|
+
* Sends CSI 6n and parses the CPR response.
|
|
2225
|
+
* Returns 1-indexed row and column.
|
|
2226
|
+
*
|
|
2227
|
+
* @param write Function to write to stdout
|
|
2228
|
+
* @param read Function to read a chunk from stdin (resolves with data or null on timeout)
|
|
2229
|
+
* @param timeoutMs How long to wait for response (default: 200ms)
|
|
2230
|
+
*/
|
|
2231
|
+
declare function queryCursorPosition(write: (data: string) => void, read: (timeoutMs: number) => Promise<string | null>, timeoutMs?: number): Promise<{
|
|
2232
|
+
row: number;
|
|
2233
|
+
col: number;
|
|
2234
|
+
} | null>;
|
|
2235
|
+
/**
|
|
2236
|
+
* Query cursor position using real stdin/stdout.
|
|
2237
|
+
* Convenience wrapper around queryCursorPosition.
|
|
2238
|
+
*/
|
|
2239
|
+
declare function queryCursorFromStdio(stdout: {
|
|
2240
|
+
write: (s: string) => boolean | void;
|
|
2241
|
+
}, stdin: NodeJS.ReadStream, timeoutMs?: number): Promise<{
|
|
2242
|
+
row: number;
|
|
2243
|
+
col: number;
|
|
2244
|
+
} | null>;
|
|
2245
|
+
//#endregion
|
|
2246
|
+
//#region packages/ag-term/src/device-attrs.d.ts
|
|
2247
|
+
/**
|
|
2248
|
+
* Device Attributes (DA1/DA2/DA3) + XTVERSION Queries
|
|
2249
|
+
*
|
|
2250
|
+
* Provides functions to query terminal identity and capabilities using
|
|
2251
|
+
* the standard VT device attribute escape sequences.
|
|
2252
|
+
*
|
|
2253
|
+
* Protocols:
|
|
2254
|
+
*
|
|
2255
|
+
* DA1 (Primary Device Attributes):
|
|
2256
|
+
* Query: CSI c (\x1b[c)
|
|
2257
|
+
* Response: CSI ? Ps ; Ps ; ... c
|
|
2258
|
+
*
|
|
2259
|
+
* DA2 (Secondary Device Attributes):
|
|
2260
|
+
* Query: CSI > c (\x1b[>c)
|
|
2261
|
+
* Response: CSI > Pt ; Pv ; Pc c
|
|
2262
|
+
* Where Pt=terminal type, Pv=firmware version, Pc=ROM cartridge id
|
|
2263
|
+
*
|
|
2264
|
+
* DA3 (Tertiary Device Attributes):
|
|
2265
|
+
* Query: CSI = c (\x1b[=c)
|
|
2266
|
+
* Response: DCS ! | hex-encoded-id ST (\x1bP!|{hex}\x1b\\)
|
|
2267
|
+
*
|
|
2268
|
+
* XTVERSION (Terminal Name + Version):
|
|
2269
|
+
* Query: CSI > 0 q (\x1b[>0q)
|
|
2270
|
+
* Response: DCS > | name(version) ST (\x1bP>|{text}\x1b\\)
|
|
2271
|
+
*
|
|
2272
|
+
* Supported by: xterm, Ghostty, Kitty, WezTerm, foot, VTE-based terminals
|
|
2273
|
+
*/
|
|
2274
|
+
/**
|
|
2275
|
+
* Query primary device attributes (DA1).
|
|
2276
|
+
*
|
|
2277
|
+
* Returns the list of attribute parameters the terminal reports.
|
|
2278
|
+
* Common params: 1=132-cols, 4=sixel, 6=selective-erase, 22=ANSI-color
|
|
2279
|
+
*
|
|
2280
|
+
* @param write Function to write to stdout
|
|
2281
|
+
* @param read Function to read a chunk from stdin
|
|
2282
|
+
* @param timeoutMs How long to wait for response (default: 200ms)
|
|
2283
|
+
*/
|
|
2284
|
+
declare function queryPrimaryDA(write: (data: string) => void, read: (timeoutMs: number) => Promise<string | null>, timeoutMs?: number): Promise<{
|
|
2285
|
+
params: number[];
|
|
2286
|
+
} | null>;
|
|
2287
|
+
/**
|
|
2288
|
+
* Query secondary device attributes (DA2).
|
|
2289
|
+
*
|
|
2290
|
+
* Returns terminal type, firmware version, and ROM cartridge id.
|
|
2291
|
+
* Common type values: 0=VT100, 1=VT220, 41=xterm, 65=VT500
|
|
2292
|
+
*
|
|
2293
|
+
* @param write Function to write to stdout
|
|
2294
|
+
* @param read Function to read a chunk from stdin
|
|
2295
|
+
* @param timeoutMs How long to wait for response (default: 200ms)
|
|
2296
|
+
*/
|
|
2297
|
+
declare function querySecondaryDA(write: (data: string) => void, read: (timeoutMs: number) => Promise<string | null>, timeoutMs?: number): Promise<{
|
|
2298
|
+
type: number;
|
|
2299
|
+
version: number;
|
|
2300
|
+
id: number;
|
|
2301
|
+
} | null>;
|
|
2302
|
+
/**
|
|
2303
|
+
* Query tertiary device attributes (DA3).
|
|
2304
|
+
*
|
|
2305
|
+
* Returns a hex-encoded unit ID string. Decode with Buffer.from(hex, 'hex').
|
|
2306
|
+
*
|
|
2307
|
+
* @param write Function to write to stdout
|
|
2308
|
+
* @param read Function to read a chunk from stdin
|
|
2309
|
+
* @param timeoutMs How long to wait for response (default: 200ms)
|
|
2310
|
+
*/
|
|
2311
|
+
declare function queryTertiaryDA(write: (data: string) => void, read: (timeoutMs: number) => Promise<string | null>, timeoutMs?: number): Promise<string | null>;
|
|
2312
|
+
/**
|
|
2313
|
+
* Query the terminal name and version via XTVERSION.
|
|
2314
|
+
*
|
|
2315
|
+
* Returns the version string as reported by the terminal, e.g.:
|
|
2316
|
+
* - "xterm(388)"
|
|
2317
|
+
* - "tmux 3.4"
|
|
2318
|
+
* - "WezTerm 20230712-072601-f4abf8fd"
|
|
2319
|
+
*
|
|
2320
|
+
* @param write Function to write to stdout
|
|
2321
|
+
* @param read Function to read a chunk from stdin
|
|
2322
|
+
* @param timeoutMs How long to wait for response (default: 200ms)
|
|
2323
|
+
*/
|
|
2324
|
+
declare function queryTerminalVersion(write: (data: string) => void, read: (timeoutMs: number) => Promise<string | null>, timeoutMs?: number): Promise<string | null>;
|
|
2325
|
+
/** Combined device attributes result. */
|
|
2326
|
+
interface DeviceAttributes {
|
|
2327
|
+
da1: {
|
|
2328
|
+
params: number[];
|
|
2329
|
+
} | null;
|
|
2330
|
+
da2: {
|
|
2331
|
+
type: number;
|
|
2332
|
+
version: number;
|
|
2333
|
+
id: number;
|
|
2334
|
+
} | null;
|
|
2335
|
+
version: string | null;
|
|
2336
|
+
}
|
|
2337
|
+
/**
|
|
2338
|
+
* Query all device attributes: DA1, DA2, and XTVERSION.
|
|
2339
|
+
*
|
|
2340
|
+
* Convenience wrapper that queries all three sequentially.
|
|
2341
|
+
* DA3 is omitted from the combined query as it's rarely needed.
|
|
2342
|
+
*
|
|
2343
|
+
* @param stdout Writable stream (e.g., process.stdout)
|
|
2344
|
+
* @param stdin Readable stream (e.g., process.stdin)
|
|
2345
|
+
* @param timeoutMs Per-query timeout (default: 200ms)
|
|
2346
|
+
*/
|
|
2347
|
+
declare function queryDeviceAttributes(stdout: {
|
|
2348
|
+
write: (s: string) => boolean | void;
|
|
2349
|
+
}, stdin: NodeJS.ReadStream, timeoutMs?: number): Promise<DeviceAttributes>;
|
|
2350
|
+
//#endregion
|
|
2351
|
+
//#region packages/ag-term/src/focus-reporting.d.ts
|
|
2352
|
+
/**
|
|
2353
|
+
* Focus Reporting (CSI ?1004h)
|
|
2354
|
+
*
|
|
2355
|
+
* Enables/disables terminal focus-in/focus-out event reporting.
|
|
2356
|
+
* When enabled, the terminal sends CSI I on focus-in and CSI O on focus-out.
|
|
2357
|
+
*
|
|
2358
|
+
* Protocol:
|
|
2359
|
+
* - Enable: CSI ? 1004 h
|
|
2360
|
+
* - Disable: CSI ? 1004 l
|
|
2361
|
+
* - Focus In: CSI I (\x1b[I)
|
|
2362
|
+
* - Focus Out: CSI O (\x1b[O)
|
|
2363
|
+
*
|
|
2364
|
+
* Supported by: xterm (v282+), Ghostty, Kitty, WezTerm, iTerm2, foot, VTE
|
|
2365
|
+
*/
|
|
2366
|
+
/**
|
|
2367
|
+
* Enable terminal focus reporting.
|
|
2368
|
+
* After enabling, the terminal will send CSI I / CSI O sequences
|
|
2369
|
+
* when the terminal window gains or loses focus.
|
|
2370
|
+
*/
|
|
2371
|
+
declare function enableFocusReporting(write: (data: string) => void): void;
|
|
2372
|
+
/**
|
|
2373
|
+
* Disable terminal focus reporting.
|
|
2374
|
+
*/
|
|
2375
|
+
declare function disableFocusReporting(write: (data: string) => void): void;
|
|
2376
|
+
/**
|
|
2377
|
+
* Parse a focus event from terminal input.
|
|
2378
|
+
*
|
|
2379
|
+
* Return semantics (see ProtocolError in @silvery/ansi for the full contract):
|
|
2380
|
+
* - `null` — input contains neither `CSI I` nor `CSI O` markers (not a
|
|
2381
|
+
* focus event). Discriminator-chain "next parser please" signal.
|
|
2382
|
+
*
|
|
2383
|
+
* The bead 15127 audit listed this parser for review, but there is no
|
|
2384
|
+
* "committed but malformed" branch — the marker either appears in the
|
|
2385
|
+
* input or it doesn't. No ProtocolError throw needed.
|
|
2386
|
+
*
|
|
2387
|
+
* @param input Raw terminal input string
|
|
2388
|
+
* @returns Parsed focus event, or null if not a focus sequence
|
|
2389
|
+
*/
|
|
2390
|
+
declare function parseFocusEvent(input: string): {
|
|
2391
|
+
type: "focus-in" | "focus-out";
|
|
2392
|
+
} | null;
|
|
2393
|
+
//#endregion
|
|
2394
|
+
//#region packages/ag-term/src/mode-query.d.ts
|
|
2395
|
+
/**
|
|
2396
|
+
* DECRQM — DEC Private Mode Query
|
|
2397
|
+
*
|
|
2398
|
+
* Queries the terminal for the state of DEC private modes.
|
|
2399
|
+
*
|
|
2400
|
+
* Protocol:
|
|
2401
|
+
* - Query: CSI ? {mode} $ p
|
|
2402
|
+
* - Response: CSI ? {mode} ; {Ps} $ y
|
|
2403
|
+
*
|
|
2404
|
+
* Where Ps is:
|
|
2405
|
+
* 1 = set (mode is enabled)
|
|
2406
|
+
* 2 = reset (mode is disabled)
|
|
2407
|
+
* 0 = not recognized (unknown mode)
|
|
2408
|
+
* 3 = permanently set
|
|
2409
|
+
* 4 = permanently reset
|
|
2410
|
+
*
|
|
2411
|
+
* We normalize 3→"set" and 4→"reset" for simplicity.
|
|
2412
|
+
*
|
|
2413
|
+
* Supported by: xterm, Ghostty, Kitty, WezTerm, foot, VTE-based terminals
|
|
2414
|
+
*/
|
|
2415
|
+
/** Well-known DEC private mode constants. */
|
|
2416
|
+
declare const DecMode: {
|
|
2417
|
+
/** DEC cursor visible (DECTCEM) */readonly CURSOR_VISIBLE: 25; /** Alternate screen buffer (DECSET 1049) */
|
|
2418
|
+
readonly ALT_SCREEN: 1049; /** Normal mouse tracking (X10) */
|
|
2419
|
+
readonly MOUSE_TRACKING: 1000; /** Bracketed paste mode */
|
|
2420
|
+
readonly BRACKETED_PASTE: 2004; /** Synchronized output */
|
|
2421
|
+
readonly SYNC_OUTPUT: 2026; /** Focus reporting */
|
|
2422
|
+
readonly FOCUS_REPORTING: 1004;
|
|
2423
|
+
};
|
|
2424
|
+
type ModeState = "set" | "reset" | "unknown";
|
|
2425
|
+
/**
|
|
2426
|
+
* Query the state of a single DEC private mode.
|
|
2427
|
+
*
|
|
2428
|
+
* @param write Function to write to stdout
|
|
2429
|
+
* @param read Function to read a chunk from stdin
|
|
2430
|
+
* @param mode DEC private mode number (e.g., DecMode.ALT_SCREEN)
|
|
2431
|
+
* @param timeoutMs How long to wait for response (default: 200ms)
|
|
2432
|
+
* @returns "set", "reset", or "unknown"
|
|
2433
|
+
*/
|
|
2434
|
+
declare function queryMode(write: (data: string) => void, read: (timeoutMs: number) => Promise<string | null>, mode: number, timeoutMs?: number): Promise<ModeState>;
|
|
2435
|
+
/**
|
|
2436
|
+
* Query the state of multiple DEC private modes.
|
|
2437
|
+
*
|
|
2438
|
+
* Queries each mode sequentially and returns a Map of results.
|
|
2439
|
+
*
|
|
2440
|
+
* @param write Function to write to stdout
|
|
2441
|
+
* @param read Function to read a chunk from stdin
|
|
2442
|
+
* @param modes Array of DEC private mode numbers
|
|
2443
|
+
* @param timeoutMs Per-query timeout (default: 200ms)
|
|
2444
|
+
*/
|
|
2445
|
+
declare function queryModes(write: (data: string) => void, read: (timeoutMs: number) => Promise<string | null>, modes: number[], timeoutMs?: number): Promise<Map<number, ModeState>>;
|
|
2446
|
+
//#endregion
|
|
2447
|
+
//#region packages/ag-term/src/pixel-size.d.ts
|
|
2448
|
+
/**
|
|
2449
|
+
* CSI 14t/18t — Terminal Pixel and Text Area Size Queries
|
|
2450
|
+
*
|
|
2451
|
+
* Queries the terminal for window dimensions in pixels and characters.
|
|
2452
|
+
*
|
|
2453
|
+
* Protocols:
|
|
2454
|
+
*
|
|
2455
|
+
* Text Area Pixels (CSI 14t):
|
|
2456
|
+
* Query: CSI 14 t
|
|
2457
|
+
* Response: CSI 4 ; height ; width t
|
|
2458
|
+
*
|
|
2459
|
+
* Text Area Size in Characters (CSI 18t):
|
|
2460
|
+
* Query: CSI 18 t
|
|
2461
|
+
* Response: CSI 8 ; rows ; cols t
|
|
2462
|
+
*
|
|
2463
|
+
* Cell size can be derived by dividing pixel dimensions by character dimensions.
|
|
2464
|
+
*
|
|
2465
|
+
* Supported by: xterm, Ghostty, Kitty, WezTerm, foot, iTerm2
|
|
2466
|
+
*/
|
|
2467
|
+
/**
|
|
2468
|
+
* Query the terminal text area size in pixels.
|
|
2469
|
+
*
|
|
2470
|
+
* @param write Function to write to stdout
|
|
2471
|
+
* @param read Function to read a chunk from stdin
|
|
2472
|
+
* @param timeoutMs How long to wait for response (default: 200ms)
|
|
2473
|
+
* @returns Width and height in pixels, or null on timeout/unsupported
|
|
2474
|
+
*/
|
|
2475
|
+
declare function queryTextAreaPixels(write: (data: string) => void, read: (timeoutMs: number) => Promise<string | null>, timeoutMs?: number): Promise<{
|
|
2476
|
+
width: number;
|
|
2477
|
+
height: number;
|
|
2478
|
+
} | null>;
|
|
2479
|
+
/**
|
|
2480
|
+
* Query the terminal text area size in characters (rows x columns).
|
|
2481
|
+
*
|
|
2482
|
+
* @param write Function to write to stdout
|
|
2483
|
+
* @param read Function to read a chunk from stdin
|
|
2484
|
+
* @param timeoutMs How long to wait for response (default: 200ms)
|
|
2485
|
+
* @returns Rows and columns, or null on timeout/unsupported
|
|
2486
|
+
*/
|
|
2487
|
+
declare function queryTextAreaSize(write: (data: string) => void, read: (timeoutMs: number) => Promise<string | null>, timeoutMs?: number): Promise<{
|
|
2488
|
+
cols: number;
|
|
2489
|
+
rows: number;
|
|
2490
|
+
} | null>;
|
|
2491
|
+
/**
|
|
2492
|
+
* Query the terminal cell size in pixels by querying both pixel
|
|
2493
|
+
* dimensions and character dimensions, then dividing.
|
|
2494
|
+
*
|
|
2495
|
+
* @param write Function to write to stdout
|
|
2496
|
+
* @param read Function to read a chunk from stdin
|
|
2497
|
+
* @param timeoutMs Per-query timeout (default: 200ms)
|
|
2498
|
+
* @returns Cell width and height in pixels, or null if either query fails
|
|
2499
|
+
*/
|
|
2500
|
+
declare function queryCellSize(write: (data: string) => void, read: (timeoutMs: number) => Promise<string | null>, timeoutMs?: number): Promise<{
|
|
2501
|
+
width: number;
|
|
2502
|
+
height: number;
|
|
2503
|
+
} | null>;
|
|
2504
|
+
//#endregion
|
|
2505
|
+
//#region packages/ag-term/src/term-def.d.ts
|
|
2506
|
+
/**
|
|
2507
|
+
* Minimal surface for configuring render().
|
|
2508
|
+
*
|
|
2509
|
+
* TermDef provides a simple way to configure rendering without requiring
|
|
2510
|
+
* a full Term instance. It's useful for:
|
|
2511
|
+
* - Static rendering (just width/height, no events)
|
|
2512
|
+
* - Testing (mock dimensions and events)
|
|
2513
|
+
* - Quick scripts (auto-detect everything from stdin/stdout)
|
|
2514
|
+
*
|
|
2515
|
+
* The presence of `events` (or `stdin` which auto-creates events)
|
|
2516
|
+
* determines the render mode:
|
|
2517
|
+
* - No events → static mode (render until stable)
|
|
2518
|
+
* - Has events → interactive mode (render until exit() called)
|
|
2519
|
+
*
|
|
2520
|
+
* @example
|
|
2521
|
+
* ```tsx
|
|
2522
|
+
* // Static render with custom width
|
|
2523
|
+
* const output = await render(<App />, { width: 100 })
|
|
2524
|
+
*
|
|
2525
|
+
* // Interactive with stdin/stdout
|
|
2526
|
+
* await render(<App />, { stdin: process.stdin, stdout: process.stdout })
|
|
2527
|
+
*
|
|
2528
|
+
* // Custom events
|
|
2529
|
+
* await render(<App />, { events: myEventSource })
|
|
2530
|
+
* ```
|
|
2531
|
+
*/
|
|
2532
|
+
interface TermDef {
|
|
2533
|
+
/** Output stream (used for dimensions if not specified) */
|
|
2534
|
+
stdout?: NodeJS.WriteStream;
|
|
2535
|
+
/** Width in columns (default: stdout?.columns ?? 80) */
|
|
2536
|
+
width?: number;
|
|
2537
|
+
/** Height in rows (default: stdout?.rows ?? 24) */
|
|
2538
|
+
height?: number;
|
|
2539
|
+
/**
|
|
2540
|
+
* Color support (true=detect, false=mono, or specific {@link ColorLevel}).
|
|
2541
|
+
*
|
|
2542
|
+
* `null` is accepted as a compat alias for `"mono"` (pre-terminal-profile-plateau
|
|
2543
|
+
* no-color spelling).
|
|
2544
|
+
*/
|
|
2545
|
+
colors?: boolean | ColorLevel | null;
|
|
2546
|
+
/**
|
|
2547
|
+
* Event source for interactive mode.
|
|
2548
|
+
*
|
|
2549
|
+
* When present, render runs until exit() is called.
|
|
2550
|
+
* When absent, render completes when UI is stable.
|
|
2551
|
+
*/
|
|
2552
|
+
events?: AsyncIterable<Event> | EventSource;
|
|
2553
|
+
/**
|
|
2554
|
+
* Standard input stream.
|
|
2555
|
+
*
|
|
2556
|
+
* When provided (and events is not), automatically creates input events
|
|
2557
|
+
* from stdin, enabling interactive mode.
|
|
2558
|
+
*/
|
|
2559
|
+
stdin?: NodeJS.ReadStream;
|
|
2560
|
+
}
|
|
2561
|
+
/**
|
|
2562
|
+
* Check if a value is a Term instance (duck typing).
|
|
2563
|
+
*
|
|
2564
|
+
* Post km-silvery.caps-restructure Phase 7: the legacy hasCursor/hasInput/
|
|
2565
|
+
* hasColor methods were removed — caps/profile/write/size are the canonical
|
|
2566
|
+
* Term surface.
|
|
2567
|
+
*/
|
|
2568
|
+
declare function isTerm(value: unknown): value is Term;
|
|
2569
|
+
/**
|
|
2570
|
+
* Create an async iterable of input events from a stdin stream.
|
|
2571
|
+
*
|
|
2572
|
+
* This enables interactive mode by providing a source of keyboard events.
|
|
2573
|
+
*/
|
|
2574
|
+
declare function createInputEvents(stdin: NodeJS.ReadStream): AsyncIterable<Event>;
|
|
2575
|
+
//#endregion
|
|
2576
|
+
//#region packages/ag-term/src/non-tty.d.ts
|
|
2577
|
+
/**
|
|
2578
|
+
* Non-TTY Mode Support for Silvery
|
|
2579
|
+
*
|
|
2580
|
+
* Provides detection and rendering modes for non-interactive environments:
|
|
2581
|
+
* - Piped output (process.stdout.isTTY === false)
|
|
2582
|
+
* - CI environments
|
|
2583
|
+
* - TERM=dumb
|
|
2584
|
+
*
|
|
2585
|
+
* When in non-TTY mode, silvery avoids cursor positioning codes that garble
|
|
2586
|
+
* output in non-interactive environments.
|
|
2587
|
+
*
|
|
2588
|
+
* Modes:
|
|
2589
|
+
* - 'auto': Detect based on environment (default)
|
|
2590
|
+
* - 'tty': Force TTY mode (normal cursor positioning)
|
|
2591
|
+
* - 'line-by-line': Simple newline-separated output, no cursor movement
|
|
2592
|
+
* - 'static': Single output at end (no updates)
|
|
2593
|
+
* - 'plain': Strip all ANSI codes
|
|
2594
|
+
*/
|
|
2595
|
+
/**
|
|
2596
|
+
* Non-TTY rendering mode.
|
|
2597
|
+
*
|
|
2598
|
+
* - 'auto': Auto-detect based on environment
|
|
2599
|
+
* - 'tty': Force TTY mode with cursor positioning
|
|
2600
|
+
* - 'line-by-line': Output lines without cursor repositioning
|
|
2601
|
+
* - 'static': Single final output only
|
|
2602
|
+
* - 'plain': Strip all ANSI escape codes
|
|
2603
|
+
*/
|
|
2604
|
+
type NonTTYMode = "auto" | "tty" | "line-by-line" | "static" | "plain";
|
|
2605
|
+
/**
|
|
2606
|
+
* Options for non-TTY output.
|
|
2607
|
+
*/
|
|
2608
|
+
interface NonTTYOptions {
|
|
2609
|
+
/** The rendering mode. Default: 'auto' */
|
|
2610
|
+
mode?: NonTTYMode;
|
|
2611
|
+
/** Output stream to check for TTY status. Default: process.stdout */
|
|
2612
|
+
stdout?: NodeJS.WriteStream;
|
|
2613
|
+
}
|
|
2614
|
+
/**
|
|
2615
|
+
* Resolved non-TTY mode after auto-detection.
|
|
2616
|
+
*/
|
|
2617
|
+
type ResolvedNonTTYMode = Exclude<NonTTYMode, "auto">;
|
|
2618
|
+
/**
|
|
2619
|
+
* Check if the environment is a TTY.
|
|
2620
|
+
*
|
|
2621
|
+
* Returns false if:
|
|
2622
|
+
* - stdout.isTTY is false or undefined
|
|
2623
|
+
* - TERM=dumb
|
|
2624
|
+
* - CI environment variables are set
|
|
2625
|
+
*
|
|
2626
|
+
* Pass `emulator` (from `term.emulator` or a TerminalEmulator fixture) when
|
|
2627
|
+
* available to avoid a redundant env read. Without it, the TERM=dumb check
|
|
2628
|
+
* delegates to {@link createTerminalProfile} — the canonical single-source-
|
|
2629
|
+
* of-truth entry in `@silvery/ansi/profile`. CI env vars remain read
|
|
2630
|
+
* directly because they are orthogonal to terminal capabilities (a CI
|
|
2631
|
+
* runner's TTY status doesn't describe what the terminal can render).
|
|
2632
|
+
*/
|
|
2633
|
+
declare function isTTY(stdout?: NodeJS.WriteStream, /** Structural emulator — `{ TERM }` is the only field this helper reads. */
|
|
2634
|
+
|
|
2635
|
+
emulator?: {
|
|
2636
|
+
TERM: string;
|
|
2637
|
+
}): boolean;
|
|
2638
|
+
/**
|
|
2639
|
+
* Resolve the non-TTY mode based on options and environment.
|
|
2640
|
+
*
|
|
2641
|
+
* When mode is 'auto':
|
|
2642
|
+
* - If TTY detected: returns 'tty'
|
|
2643
|
+
* - If non-TTY detected: returns 'line-by-line'
|
|
2644
|
+
*/
|
|
2645
|
+
declare function resolveNonTTYMode(options?: NonTTYOptions): ResolvedNonTTYMode;
|
|
2646
|
+
//#endregion
|
|
2647
|
+
//#region packages/ag-term/src/errors.d.ts
|
|
2648
|
+
/** Structured mismatch data attached to the error (mirrors MismatchDebugContext shape) */
|
|
2649
|
+
interface MismatchErrorData {
|
|
2650
|
+
/** Render-phase instrumentation snapshot (nodes visited/rendered/skipped, per-flag breakdown) */
|
|
2651
|
+
renderPhaseStats?: RenderPhaseStats;
|
|
2652
|
+
/** Debug context for the mismatched cell (from debug-mismatch.ts) */
|
|
2653
|
+
mismatchContext?: unknown;
|
|
2654
|
+
}
|
|
2655
|
+
/**
|
|
2656
|
+
* Error thrown when SILVERY_STRICT detects a mismatch.
|
|
2657
|
+
* This error should NOT be caught by general error handlers - it indicates
|
|
2658
|
+
* a bug in incremental rendering that needs to be fixed.
|
|
2659
|
+
*
|
|
2660
|
+
* When SILVERY_STRICT fires, the error automatically includes:
|
|
2661
|
+
* - Render-phase instrumentation (nodes visited/rendered/skipped, per-flag breakdown)
|
|
2662
|
+
* - Cell attribution (which node owns the mismatched cell, dirty flags, scroll context)
|
|
2663
|
+
*/
|
|
2664
|
+
declare class IncrementalRenderMismatchError extends Error {
|
|
2665
|
+
/** Render-phase instrumentation snapshot */
|
|
2666
|
+
renderPhaseStats?: RenderPhaseStats;
|
|
2667
|
+
/** Debug context for the mismatched cell */
|
|
2668
|
+
mismatchContext?: unknown;
|
|
2669
|
+
constructor(message: string, data?: MismatchErrorData);
|
|
2670
|
+
}
|
|
2671
|
+
//#endregion
|
|
2672
|
+
//#region packages/ag-term/src/scheduler.d.ts
|
|
2673
|
+
interface RenderStats {
|
|
2674
|
+
/** Number of renders executed */
|
|
2675
|
+
renderCount: number;
|
|
2676
|
+
/** Number of renders skipped (batched) */
|
|
2677
|
+
skippedCount: number;
|
|
2678
|
+
/** Last render duration in ms */
|
|
2679
|
+
lastRenderTime: number;
|
|
2680
|
+
/** Average render time in ms */
|
|
2681
|
+
avgRenderTime: number;
|
|
2682
|
+
}
|
|
2683
|
+
//#endregion
|
|
2684
|
+
//#region packages/ag-term/src/inspector.d.ts
|
|
2685
|
+
interface InspectorOptions {
|
|
2686
|
+
/** Output stream (default: process.stderr) */
|
|
2687
|
+
output?: NodeJS.WritableStream;
|
|
2688
|
+
/** Log file path (overrides output stream) */
|
|
2689
|
+
logFile?: string;
|
|
2690
|
+
/** Include layout rects in tree dump */
|
|
2691
|
+
showLayout?: boolean;
|
|
2692
|
+
/** Include style info in tree dump */
|
|
2693
|
+
showStyles?: boolean;
|
|
2694
|
+
}
|
|
2695
|
+
/** Enable the silvery inspector. */
|
|
2696
|
+
declare function enableInspector(options?: InspectorOptions): void;
|
|
2697
|
+
/** Disable the inspector. */
|
|
2698
|
+
declare function disableInspector(): void;
|
|
2699
|
+
/** Check if inspector is active. */
|
|
2700
|
+
declare function isInspectorEnabled(): boolean;
|
|
2701
|
+
/**
|
|
2702
|
+
* Log render stats after each frame.
|
|
2703
|
+
*
|
|
2704
|
+
* Called by the scheduler after doRender completes. When the inspector
|
|
2705
|
+
* is disabled this is a no-op (zero overhead).
|
|
2706
|
+
*/
|
|
2707
|
+
declare function inspectFrame(stats: RenderStats): void;
|
|
2708
|
+
/**
|
|
2709
|
+
* Dump the component tree structure as indented text.
|
|
2710
|
+
*
|
|
2711
|
+
* Walks the SilveryNode tree and formats each node with its type, testID,
|
|
2712
|
+
* layout rect, and dirty flags.
|
|
2713
|
+
*/
|
|
2714
|
+
declare function inspectTree(rootNode: AgNode, options?: {
|
|
2715
|
+
depth?: number;
|
|
2716
|
+
showLayout?: boolean;
|
|
2717
|
+
}): string;
|
|
2718
|
+
/**
|
|
2719
|
+
* Auto-enable if SILVERY_DEV=1 is set.
|
|
2720
|
+
*
|
|
2721
|
+
* Call this at startup to respect the environment variable convention.
|
|
2722
|
+
*/
|
|
2723
|
+
declare function autoEnableInspector(): void;
|
|
2724
|
+
//#endregion
|
|
2725
|
+
//#region packages/ag-term/src/measurer.d.ts
|
|
2726
|
+
/**
|
|
2727
|
+
* Term extended with measurement capabilities.
|
|
2728
|
+
*/
|
|
2729
|
+
interface MeasuredTerm extends Term, Measurer {}
|
|
2730
|
+
/**
|
|
2731
|
+
* Extend a Term with measurement capabilities.
|
|
2732
|
+
*
|
|
2733
|
+
* Creates a width measurer from the term's caps and adds measurement
|
|
2734
|
+
* methods (displayWidth, graphemeWidth, wrapText, etc.) to the term.
|
|
2735
|
+
*/
|
|
2736
|
+
declare function withMeasurer(term: Term): MeasuredTerm;
|
|
2737
|
+
/**
|
|
2738
|
+
* Create a pipeline configuration from caps and/or measurer.
|
|
2739
|
+
*
|
|
2740
|
+
* This is the single factory for PipelineConfig -- use it instead of
|
|
2741
|
+
* manually constructing { measurer, outputPhaseFn }.
|
|
2742
|
+
*
|
|
2743
|
+
* @param options.caps - Terminal capabilities (for output phase SGR generation).
|
|
2744
|
+
* Reads `caps.maybeWideEmojis` (text-presentation emoji width) and
|
|
2745
|
+
* `caps.textSizing` for measurement.
|
|
2746
|
+
* @param options.measurer - Explicit measurer (if omitted, created from caps)
|
|
2747
|
+
*/
|
|
2748
|
+
declare function createPipeline(options?: {
|
|
2749
|
+
caps?: TerminalCaps;
|
|
2750
|
+
measurer?: Measurer;
|
|
2751
|
+
}): PipelineConfig;
|
|
2752
|
+
//#endregion
|
|
2753
|
+
//#region packages/ag-term/src/scroll-utils.d.ts
|
|
2754
|
+
/**
|
|
2755
|
+
* Scroll Utilities
|
|
2756
|
+
*
|
|
2757
|
+
* Shared functions for edge-based scrolling behavior across VirtualList,
|
|
2758
|
+
* HorizontalVirtualList, and other scroll-aware components.
|
|
2759
|
+
*/
|
|
2760
|
+
/**
|
|
2761
|
+
* Calculate edge-based scroll offset.
|
|
2762
|
+
*
|
|
2763
|
+
* Only scrolls when cursor approaches the edge of the visible area.
|
|
2764
|
+
* This provides smoother scrolling by starting to scroll before hitting
|
|
2765
|
+
* the absolute edge, maintaining context around the selected item.
|
|
2766
|
+
*
|
|
2767
|
+
* ## Algorithm
|
|
2768
|
+
*
|
|
2769
|
+
* The viewport is divided into zones:
|
|
2770
|
+
* ```
|
|
2771
|
+
* |<padding>|<------ safe zone ------>|<padding>|
|
|
2772
|
+
* | scroll | no scroll needed | scroll |
|
|
2773
|
+
* | if < | | if > |
|
|
2774
|
+
* ```
|
|
2775
|
+
*
|
|
2776
|
+
* When the selected item enters a padding zone, the viewport scrolls
|
|
2777
|
+
* to keep the item visible with margin.
|
|
2778
|
+
*
|
|
2779
|
+
* ## Asymmetry Note
|
|
2780
|
+
*
|
|
2781
|
+
* The +1 in the "scroll down/right" case is intentional:
|
|
2782
|
+
* - Offset points to the TOP/LEFT of the viewport
|
|
2783
|
+
* - We want the selected item to be `padding` items from the BOTTOM/RIGHT
|
|
2784
|
+
* - Formula: `selectedIndex - visibleCount + padding + 1`
|
|
2785
|
+
*
|
|
2786
|
+
* Example: visibleCount=10, padding=2, selectedIndex=15
|
|
2787
|
+
* offset = 15 - 10 + 2 + 1 = 8
|
|
2788
|
+
* viewport shows items 8-17, selected item 15 is at position 7 (2 from bottom)
|
|
2789
|
+
*
|
|
2790
|
+
* @param selectedIndex - Currently selected item index
|
|
2791
|
+
* @param currentOffset - Current scroll offset (topmost/leftmost visible item)
|
|
2792
|
+
* @param visibleCount - Number of items visible in viewport
|
|
2793
|
+
* @param totalCount - Total number of items
|
|
2794
|
+
* @param padding - Items to keep visible before/after cursor (default: 1)
|
|
2795
|
+
* @returns New scroll offset
|
|
2796
|
+
*/
|
|
2797
|
+
declare function calcEdgeBasedScrollOffset(selectedIndex: number, currentOffset: number, visibleCount: number, totalCount: number, padding?: number): number;
|
|
2798
|
+
//#endregion
|
|
2799
|
+
//#region packages/ag-term/src/scroll-region.d.ts
|
|
2800
|
+
/**
|
|
2801
|
+
* Terminal scroll region (DECSTBM) utilities.
|
|
2802
|
+
*
|
|
2803
|
+
* Scroll regions tell the terminal to natively scroll content within
|
|
2804
|
+
* a defined row range, which is faster than re-rendering all cells.
|
|
2805
|
+
*
|
|
2806
|
+
* DECSTBM (DEC Set Top and Bottom Margins) is supported by most modern
|
|
2807
|
+
* terminal emulators: xterm, iTerm2, Kitty, Ghostty, WezTerm, etc.
|
|
2808
|
+
*/
|
|
2809
|
+
/** Set terminal scroll region (1-indexed top and bottom rows). */
|
|
2810
|
+
declare function setScrollRegion(stdout: NodeJS.WriteStream, top: number, bottom: number): void;
|
|
2811
|
+
/** Reset scroll region to full terminal. */
|
|
2812
|
+
declare function resetScrollRegion(stdout: NodeJS.WriteStream): void;
|
|
2813
|
+
/** Scroll content up by N lines within the current scroll region. */
|
|
2814
|
+
declare function scrollUp(stdout: NodeJS.WriteStream, lines?: number): void;
|
|
2815
|
+
/** Scroll content down by N lines within the current scroll region. */
|
|
2816
|
+
declare function scrollDown(stdout: NodeJS.WriteStream, lines?: number): void;
|
|
2817
|
+
/** Move cursor to a specific position (1-indexed row and column). */
|
|
2818
|
+
declare function moveCursor(stdout: NodeJS.WriteStream, row: number, col: number): void;
|
|
2819
|
+
interface ScrollRegionConfig {
|
|
2820
|
+
/** Top row of the scrollable area (0-indexed). */
|
|
2821
|
+
top: number;
|
|
2822
|
+
/** Bottom row of the scrollable area (0-indexed). */
|
|
2823
|
+
bottom: number;
|
|
2824
|
+
/** Whether scroll region optimization is enabled. */
|
|
2825
|
+
enabled: boolean;
|
|
2826
|
+
}
|
|
2827
|
+
/**
|
|
2828
|
+
* Detect if the terminal likely supports DECSTBM scroll regions.
|
|
2829
|
+
*
|
|
2830
|
+
* Most modern terminals do (xterm, iTerm2, Kitty, Ghostty, WezTerm, etc.)
|
|
2831
|
+
* but some (e.g., Linux console) may not handle them correctly.
|
|
2832
|
+
*
|
|
2833
|
+
* Pass `emulator` (via `term.emulator` or a test fixture) when in scope.
|
|
2834
|
+
* Without it, this falls back to {@link createTerminalProfile} — the canonical
|
|
2835
|
+
* single-source-of-truth entry point in `@silvery/ansi/profile`. Direct
|
|
2836
|
+
* reads of terminal-signal env vars (TERM / TERM_PROGRAM / …) are banned
|
|
2837
|
+
* outside that module — see `scripts/lint-env-reads.ts`.
|
|
2838
|
+
*/
|
|
2839
|
+
declare function supportsScrollRegions(/** Structural emulator — `{ program, TERM }` is all this helper reads. */
|
|
2840
|
+
|
|
2841
|
+
emulator?: {
|
|
2842
|
+
program: string;
|
|
2843
|
+
TERM: string;
|
|
2844
|
+
}): boolean;
|
|
2845
|
+
//#endregion
|
|
2846
|
+
//#region packages/ag-term/src/pane-manager.d.ts
|
|
2847
|
+
/**
|
|
2848
|
+
* Pane Manager - Pure functions for manipulating split layout trees.
|
|
2849
|
+
*
|
|
2850
|
+
* All functions are pure: they return new layout trees, never mutate.
|
|
2851
|
+
* The layout tree is a binary tree where leaves are panes and internal
|
|
2852
|
+
* nodes are splits (horizontal or vertical) with a ratio.
|
|
2853
|
+
*/
|
|
2854
|
+
type LayoutNode = {
|
|
2855
|
+
type: "leaf";
|
|
2856
|
+
id: string;
|
|
2857
|
+
} | {
|
|
2858
|
+
type: "split";
|
|
2859
|
+
direction: "horizontal" | "vertical";
|
|
2860
|
+
ratio: number;
|
|
2861
|
+
first: LayoutNode;
|
|
2862
|
+
second: LayoutNode;
|
|
2863
|
+
};
|
|
2864
|
+
/** Create a single-pane layout */
|
|
2865
|
+
declare function createLeaf(id: string): LayoutNode;
|
|
2866
|
+
/** Get all leaf pane IDs in depth-first left-to-right order */
|
|
2867
|
+
declare function getPaneIds(layout: LayoutNode): string[];
|
|
2868
|
+
/** Find the next/previous pane in tab order (depth-first left-to-right) */
|
|
2869
|
+
declare function getTabOrder(layout: LayoutNode): string[];
|
|
2870
|
+
/**
|
|
2871
|
+
* Split a pane into two. Returns new layout tree with the target pane split.
|
|
2872
|
+
* The original pane becomes the first child; the new pane becomes the second.
|
|
2873
|
+
*/
|
|
2874
|
+
declare function splitPane(layout: LayoutNode, targetPaneId: string, direction: "horizontal" | "vertical", newPaneId: string, ratio?: number): LayoutNode;
|
|
2875
|
+
/**
|
|
2876
|
+
* Remove a pane from the layout. The sibling takes the full space.
|
|
2877
|
+
* Returns null if the removed pane was the last one.
|
|
2878
|
+
*/
|
|
2879
|
+
declare function removePane(layout: LayoutNode, paneId: string): LayoutNode | null;
|
|
2880
|
+
/** Swap two panes' positions in the layout */
|
|
2881
|
+
declare function swapPanes(layout: LayoutNode, paneId1: string, paneId2: string): LayoutNode;
|
|
2882
|
+
/**
|
|
2883
|
+
* Resize a split: adjust the ratio of the nearest ancestor split
|
|
2884
|
+
* that contains the target pane as its first child.
|
|
2885
|
+
* Positive delta = grow (first child gets more), negative = shrink.
|
|
2886
|
+
*/
|
|
2887
|
+
declare function resizeSplit(layout: LayoutNode, paneId: string, delta: number): LayoutNode;
|
|
2888
|
+
/**
|
|
2889
|
+
* Find the pane adjacent to the given pane in a direction.
|
|
2890
|
+
*
|
|
2891
|
+
* For left/right: looks for siblings in horizontal splits.
|
|
2892
|
+
* For up/down: looks for siblings in vertical splits.
|
|
2893
|
+
*
|
|
2894
|
+
* Returns null if no adjacent pane exists in that direction.
|
|
2895
|
+
*/
|
|
2896
|
+
declare function findAdjacentPane(layout: LayoutNode, paneId: string, direction: "left" | "right" | "up" | "down"): string | null;
|
|
2897
|
+
//#endregion
|
|
2898
|
+
//#region packages/headless/src/find.d.ts
|
|
2899
|
+
interface FindMatch {
|
|
2900
|
+
row: number;
|
|
2901
|
+
startCol: number;
|
|
2902
|
+
endCol: number;
|
|
2903
|
+
}
|
|
2904
|
+
/**
|
|
2905
|
+
* Result from a FindProvider's model-level search.
|
|
2906
|
+
* Represents a match within a virtual list item that may not be on screen.
|
|
2907
|
+
*/
|
|
2908
|
+
interface FindResult {
|
|
2909
|
+
/** Virtual list item identifier */
|
|
2910
|
+
itemId: string;
|
|
2911
|
+
/** Character offset within item text */
|
|
2912
|
+
offset: number;
|
|
2913
|
+
/** Match length */
|
|
2914
|
+
length: number;
|
|
2915
|
+
/** Screen row — set after reveal() makes the item visible */
|
|
2916
|
+
row?: number;
|
|
2917
|
+
/** Screen start column — set after reveal() */
|
|
2918
|
+
startCol?: number;
|
|
2919
|
+
}
|
|
2920
|
+
interface FindState {
|
|
2921
|
+
/** Current search query, or null if find is not active */
|
|
2922
|
+
query: string | null;
|
|
2923
|
+
/** All matches in the visible buffer */
|
|
2924
|
+
matches: FindMatch[];
|
|
2925
|
+
/** Index of the currently focused match (-1 if no matches) */
|
|
2926
|
+
currentIndex: number;
|
|
2927
|
+
/** Whether find mode is active */
|
|
2928
|
+
active: boolean;
|
|
2929
|
+
/** Provider-level results (when FindProvider is present) */
|
|
2930
|
+
providerResults: FindResult[];
|
|
2931
|
+
/** Whether provider search is in progress */
|
|
2932
|
+
providerSearching: boolean;
|
|
2933
|
+
}
|
|
2934
|
+
//#endregion
|
|
2935
|
+
//#region packages/ag-term/src/history-buffer.d.ts
|
|
2936
|
+
/**
|
|
2937
|
+
* Ring buffer for frozen scrollback items.
|
|
2938
|
+
*
|
|
2939
|
+
* Each item represents a rendered list entry (card, row, etc.) with its
|
|
2940
|
+
* ANSI snapshot, split rows, and plain-text rows for searching.
|
|
2941
|
+
* When total rows or cached ANSI bytes exceed capacity, the oldest items are
|
|
2942
|
+
* evicted. The byte count tracks the serialized string payload; it is a
|
|
2943
|
+
* deterministic cache budget, not an exact process heap measurement.
|
|
2944
|
+
*/
|
|
2945
|
+
interface HistoryItem {
|
|
2946
|
+
key: string | number;
|
|
2947
|
+
ansi: string;
|
|
2948
|
+
rows: string[];
|
|
2949
|
+
plainTextRows: string[];
|
|
2950
|
+
width: number;
|
|
2951
|
+
}
|
|
2952
|
+
interface HistoryBuffer {
|
|
2953
|
+
push(item: HistoryItem): void;
|
|
2954
|
+
readonly totalRows: number;
|
|
2955
|
+
readonly totalBytes: number;
|
|
2956
|
+
readonly itemCount: number;
|
|
2957
|
+
getRows(startRow: number, count: number): string[];
|
|
2958
|
+
getPlainTextRows(startRow: number, count: number): string[];
|
|
2959
|
+
search(query: string): number[];
|
|
2960
|
+
getItemAtRow(row: number): {
|
|
2961
|
+
item: HistoryItem;
|
|
2962
|
+
localRow: number;
|
|
2963
|
+
} | null;
|
|
2964
|
+
clear(): void;
|
|
2965
|
+
readonly capacity: number;
|
|
2966
|
+
readonly capacityBytes: number | null;
|
|
2967
|
+
}
|
|
2968
|
+
//#endregion
|
|
2969
|
+
//#region packages/ag-term/src/viewport-compositor.d.ts
|
|
2970
|
+
interface ComposedViewport {
|
|
2971
|
+
/** History rows to overlay at top of viewport */
|
|
2972
|
+
overlayRows: string[];
|
|
2973
|
+
/** Number of top rows occupied by history */
|
|
2974
|
+
overlayRowCount: number;
|
|
2975
|
+
/** Number of bottom rows for live content */
|
|
2976
|
+
liveRowsVisible: number;
|
|
2977
|
+
/** Whether viewing history */
|
|
2978
|
+
isScrolledUp: boolean;
|
|
2979
|
+
/** Total scrollable height */
|
|
2980
|
+
totalHeight: number;
|
|
2981
|
+
}
|
|
2982
|
+
//#endregion
|
|
2983
|
+
//#region packages/ag-term/src/search-overlay.d.ts
|
|
2984
|
+
/**
|
|
2985
|
+
* Search overlay state machine for virtual inline mode.
|
|
2986
|
+
*
|
|
2987
|
+
* Pure TEA: (action, state) -> [state, effects[]]
|
|
2988
|
+
* Provides incremental search-as-you-type with match navigation.
|
|
2989
|
+
*/
|
|
2990
|
+
interface SearchMatch {
|
|
2991
|
+
row: number;
|
|
2992
|
+
startCol: number;
|
|
2993
|
+
endCol: number;
|
|
2994
|
+
}
|
|
2995
|
+
/**
|
|
2996
|
+
* A single contiguous character range within a string that matched a search
|
|
2997
|
+
* query. `start` is inclusive, `end` is exclusive — same convention as
|
|
2998
|
+
* `String.prototype.slice(start, end)`.
|
|
2999
|
+
*/
|
|
3000
|
+
interface MatchRange {
|
|
3001
|
+
start: number;
|
|
3002
|
+
end: number;
|
|
3003
|
+
}
|
|
3004
|
+
/**
|
|
3005
|
+
* Find all case-insensitive occurrences of `query` inside `text` and return
|
|
3006
|
+
* their character offsets. Empty query or empty text returns `[]`.
|
|
3007
|
+
*
|
|
3008
|
+
* This is the canonical silvery search-match algorithm. The same logic runs
|
|
3009
|
+
* inside `ListView`'s registered Searchable (to find matches across items)
|
|
3010
|
+
* and is exposed to consumers that render multi-segment items — a LogRow
|
|
3011
|
+
* whose searchable text is a concatenation of field values, but whose visual
|
|
3012
|
+
* rendering splits those fields across separate Text nodes, needs per-segment
|
|
3013
|
+
* ranges to highlight without re-implementing the semantics.
|
|
3014
|
+
*
|
|
3015
|
+
* Ranges are returned in ascending `start` order; overlapping matches are
|
|
3016
|
+
* not produced (`indexOf(..., start = last.start + 1)` advances past each
|
|
3017
|
+
* match start, not past its full length, preserving overlapping runs of
|
|
3018
|
+
* short queries — e.g. `"aa"` in `"aaaa"` yields `[0..2], [1..3], [2..4]`).
|
|
3019
|
+
*
|
|
3020
|
+
* Offsets are CHARACTER offsets into the input string. They are not column
|
|
3021
|
+
* offsets — multi-column wide glyphs are counted as one character here, the
|
|
3022
|
+
* same way the SearchProvider's match-row computation does.
|
|
3023
|
+
*/
|
|
3024
|
+
declare function computeMatchRanges(text: string, query: string): MatchRange[];
|
|
3025
|
+
//#endregion
|
|
3026
|
+
//#region packages/ag-term/src/runtime/wrap-measurer-registration.d.ts
|
|
3027
|
+
/**
|
|
3028
|
+
* Wrap-measurer registration for `@silvery/ag-term`.
|
|
3029
|
+
*
|
|
3030
|
+
* `@silvery/ag` exposes a process-level wrap-measurer registry
|
|
3031
|
+
* (`setWrapMeasurer` / `getWrapMeasurer`) so geometry helpers like
|
|
3032
|
+
* `computeSelectionFragments` can ask the active terminal runtime for
|
|
3033
|
+
* grapheme-correct soft-wrap slices. Without registration, the geometry
|
|
3034
|
+
* layer falls back to `\n`-only line splitting — fine for pure-framework
|
|
3035
|
+
* unit tests, wrong for real apps where a 60-char paragraph at width 20
|
|
3036
|
+
* needs to emit 3 fragments.
|
|
3037
|
+
*
|
|
3038
|
+
* **Registration shape (Option B per `hub/silvery/design/overlay-anchor-system.md`
|
|
3039
|
+
* § 8)**: terminal-side `wrapTextWithOffsets` adapts to the registry's
|
|
3040
|
+
* `wrapText(text, maxWidth) → readonly WrapSlice[]` contract. The slice
|
|
3041
|
+
* shape is structurally identical (`text` + `startOffset` + `endOffset`),
|
|
3042
|
+
* so the adapter is a passthrough — no allocation, no reformatting.
|
|
3043
|
+
*
|
|
3044
|
+
* **Lifecycle**: registration happens at module load — see the side-effect
|
|
3045
|
+
* call below. The adapter reads the active scoped measurer (terminal caps,
|
|
3046
|
+
* wide-emoji heuristics, text-sizing) at call time, so caps changes during
|
|
3047
|
+
* a session (e.g., post-DA1 width-detection toggling `maybeWideEmojis`)
|
|
3048
|
+
* are picked up automatically.
|
|
3049
|
+
*
|
|
3050
|
+
* **Test isolation**: tests that need the `\n`-only fallback (pure-`ag`
|
|
3051
|
+
* pathway) call `setWrapMeasurer(null)` in `beforeEach`/`afterEach` and
|
|
3052
|
+
* `restoreDefaultWrapMeasurer()` in `afterAll`. Tests that exercise the
|
|
3053
|
+
* registered path do nothing — the import-time side effect already wired
|
|
3054
|
+
* it up.
|
|
3055
|
+
*
|
|
3056
|
+
* **Multi-Term**: v1 is module-level singleton — see `@silvery/ag/wrap-measurer.ts`
|
|
3057
|
+
* file header for the upgrade path. Single Term-per-process is the
|
|
3058
|
+
* production reality; multi-Term scenarios are research / future work.
|
|
3059
|
+
*
|
|
3060
|
+
* Tracking: bead `km-silvery.softwrap-selection-fragments` (closes Phase
|
|
3061
|
+
* 4b deferred wrap-spanning).
|
|
3062
|
+
*/
|
|
3063
|
+
/**
|
|
3064
|
+
* Install the terminal-runtime wrap measurer into `@silvery/ag`'s registry.
|
|
3065
|
+
*
|
|
3066
|
+
* Reads the live registry (`getWrapMeasurer()`) — not a local boolean — to
|
|
3067
|
+
* decide whether to install. This makes the function safe to call after a
|
|
3068
|
+
* `setWrapMeasurer(null)` teardown that bypassed `uninstallTerminalWrapMeasurer`
|
|
3069
|
+
* (e.g. tests that drop the registration through `@silvery/ag`'s own API).
|
|
3070
|
+
*
|
|
3071
|
+
* Idempotent: when our adapter is already the registered measurer, this
|
|
3072
|
+
* is a no-op. When a foreign measurer is registered (rare — only seen if
|
|
3073
|
+
* a test or alternate runtime registered its own), this overwrites it
|
|
3074
|
+
* with our adapter, matching v1's "single Term-per-process" assumption.
|
|
3075
|
+
*
|
|
3076
|
+
* Called automatically at module load (see side-effect below); exported
|
|
3077
|
+
* for tests that want to re-arm after a `setWrapMeasurer(null)` teardown.
|
|
3078
|
+
*/
|
|
3079
|
+
declare function installTerminalWrapMeasurer(): void;
|
|
3080
|
+
/**
|
|
3081
|
+
* Reset registration when our adapter is the active measurer. After
|
|
3082
|
+
* calling this, the `@silvery/ag` registry is empty (`getWrapMeasurer()`
|
|
3083
|
+
* returns null) and `computeSelectionFragments` falls back to `\n`-only
|
|
3084
|
+
* splitting. Tests use this to exercise the fallback path; runtime
|
|
3085
|
+
* teardown uses it on Term dispose.
|
|
3086
|
+
*
|
|
3087
|
+
* No-op when a foreign measurer is registered — clearing it would surprise
|
|
3088
|
+
* whoever installed it. Call `setWrapMeasurer(null)` directly if the goal
|
|
3089
|
+
* is to clear unconditionally.
|
|
3090
|
+
*/
|
|
3091
|
+
declare function uninstallTerminalWrapMeasurer(): void;
|
|
3092
|
+
/**
|
|
3093
|
+
* Test helper: restore the registry to its post-import-time default
|
|
3094
|
+
* state. Equivalent to `installTerminalWrapMeasurer()` but reads more
|
|
3095
|
+
* naturally in `afterEach` / `afterAll` blocks.
|
|
3096
|
+
*/
|
|
3097
|
+
declare function restoreDefaultWrapMeasurer(): void;
|
|
3098
|
+
/**
|
|
3099
|
+
* Read whether the terminal measurer is currently the active registration.
|
|
3100
|
+
* Sources truth from the live registry so a concurrent test that called
|
|
3101
|
+
* `setWrapMeasurer(null)` is observed correctly.
|
|
3102
|
+
*/
|
|
3103
|
+
declare function isTerminalWrapMeasurerInstalled(): boolean;
|
|
3104
|
+
//#endregion
|
|
3105
|
+
export { enableFocusReporting as $, hasWideCharacters as $n, RenderStyle as $t, withMeasurer as A, setCursorStyle as An, injectDOMStyles as At, ResolvedNonTTYMode as B, createOsc52Backend as Bn, App as Bt, scrollDown as C, enableKittyKeyboard as Cn, PASTE_START as Ct, calcEdgeBasedScrollOffset as D, resetCursorStyle as Dn, DOMAdapterConfig as Dt, supportsScrollRegions as E, reportDirectory as En, parseBracketedPaste as Et, inspectFrame as F, DragState as Fn, LayoutConstants as Ft, isTerm as G, createMeasurer as Gn, LayoutShift as Gt, resolveNonTTYMode as H, Measurer as Hn, FilterOptions as Ht, inspectTree as I, ClipboardBackend as In, LayoutEngine as It, queryTextAreaSize as J, ensureEmojiPresentation as Jn, ExecuteRenderOptions as Jt, queryCellSize as K, displayWidth as Kn, ReasonClassifier as Kt, isInspectorEnabled as L, ClipboardCapabilities as Ln, LayoutEngineType as Lt, autoEnableInspector as M, setWindowAndIconTitle as Mn, CanvasRenderBuffer as Mt, disableInspector as N, setWindowTitle as Nn, createCanvasAdapter as Nt, MeasuredTerm as O, resetMouseCursorShape as On, DOMRenderBuffer as Ot, enableInspector as P, CopyModeState as Pn, EngineCapabilities as Pt, disableFocusReporting as Q, hasAnsi as Qn, RenderBuffer as Qt, IncrementalRenderMismatchError as R, createCompositeClipboard as Rn, isLayoutEngineInitialized as Rt, resetScrollRegion as S, disableMouse as Sn, PASTE_END as St, setScrollRegion as T, queryKittyKeyboard as Tn, enableBracketedPaste as Tt, TermDef as U, StyledSegment as Un, createAutoLocator as Ut, isTTY as V, parseClipboardResponse as Vn, AutoLocator as Vt, createInputEvents as W, constrainText as Wn, CLSReport as Wt, queryMode as X, graphemeCount as Xn, LayoutSignals as Xt, DecMode as Y, getFirstCodePoint as Yn, PipelineConfig as Yt, queryModes as Z, graphemeWidth as Zn, BorderChars as Zt, resizeSplit as _, BEL as _n, truncateText as _r, KittyDetectResult as _t, MatchRange as a, OutputPhaseDiagnostics as an, isZeroWidthGrapheme as ar, queryTerminalVersion as at, ScrollRegionConfig as b, MouseCursorShape as bn, writeTextToBuffer as br, OSC133 as bt, ComposedViewport as c, CursorAccessors as cn, padText as cr, queryCursorPosition as ct, LayoutNode as d, CursorState as dn, sliceByWidth as dr, isPrivateUseArea as dt, TextMeasureResult as en, hasZeroWidthCharacters as er, parseFocusEvent as et, createLeaf as f, CursorStore as fn, sliceByWidthFromEnd as fr, textSized as ft, removePane as g, ANSI as gn, truncateAnsi as gr, runTermtest as gt, getTabOrder as h, useCursor as hn, stripAnsi as hr, TermtestSection as ht, uninstallTerminalWrapMeasurer as i, OutputCaps as in, isWideGrapheme as ir, querySecondaryDA as it, InspectorOptions as j, setMouseCursorShape as jn, CanvasAdapterConfig as jt, createPipeline as k, resetWindowTitle as kn, createDOMAdapter as kt, HistoryBuffer as l, CursorPosition as ln, parseAnsiText as lr, detectTextSizingSupport as lt, getPaneIds as m, resetCursorState as mn, splitGraphemes as mr, TermtestOptions as mt, isTerminalWrapMeasurerInstalled as n, TextMeasurer as nn, isLikelyEmoji as nr, queryDeviceAttributes as nt, SearchMatch as o, OutputPhaseFn as on, measureText as or, queryTertiaryDA as ot, findAdjacentPane as p, createCursorStore as pn, sliceByWidthRange as pr, TERMTEST_SECTIONS as pt, queryTextAreaPixels as q, displayWidthAnsi as qn, ReflowReason as qt, restoreDefaultWrapMeasurer as r, PipelineContext as rn, isTextSizingEnabled as rr, queryPrimaryDA as rt, computeMatchRanges as s, createOutputPhase as sn, normalizeText as sr, queryCursorFromStdio as st, installTerminalWrapMeasurer as t, TextMeasureStyle as tn, isCJK as tr, DeviceAttributes as tt, FindState as u, CursorProvider as un, runWithMeasurer as ur, getTerminalFingerprint as ut, splitPane as v, CursorShape as vn, wrapText as vr, detectKittyFromStdio as vt, scrollUp as w, enableMouse as wn, disableBracketedPaste as wt, moveCursor as x, disableKittyKeyboard as xn, writeTextTruncated as xr, BracketedPasteResult as xt, swapPanes as y, KittyFlags as yn, writeLinesToBuffer as yr, detectKittySupport as yt, NonTTYOptions as z, createInternalClipboardBackend as zn, setLayoutEngine as zt };
|
|
3106
|
+
//# sourceMappingURL=wrap-measurer-registration-IV2HtcCd.d.mts.map
|