@zakmandhro/bunti 0.1.4 → 0.2.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/AGENTS.md +65 -0
- package/README.md +145 -34
- package/dist/cli.d.ts +44 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +147 -0
- package/dist/cli.js.map +1 -0
- package/dist/colors.d.ts +144 -0
- package/dist/colors.d.ts.map +1 -0
- package/dist/colors.js +426 -0
- package/dist/colors.js.map +1 -0
- package/dist/components/Box.d.ts +11 -0
- package/dist/components/Box.d.ts.map +1 -0
- package/dist/components/Box.js +9 -0
- package/dist/components/Box.js.map +1 -0
- package/dist/components/Button.d.ts +24 -0
- package/dist/components/Button.d.ts.map +1 -0
- package/dist/components/Button.js +102 -0
- package/dist/components/Button.js.map +1 -0
- package/dist/components/Card.d.ts +33 -0
- package/dist/components/Card.d.ts.map +1 -0
- package/dist/components/Card.js +79 -0
- package/dist/components/Card.js.map +1 -0
- package/dist/components/Header.d.ts +23 -0
- package/dist/components/Header.d.ts.map +1 -0
- package/dist/components/Header.js +58 -0
- package/dist/components/Header.js.map +1 -0
- package/dist/components/Input.d.ts +25 -0
- package/dist/components/Input.d.ts.map +1 -0
- package/dist/components/Input.js +232 -0
- package/dist/components/Input.js.map +1 -0
- package/dist/components/Link.d.ts +21 -0
- package/dist/components/Link.d.ts.map +1 -0
- package/dist/components/Link.js +29 -0
- package/dist/components/Link.js.map +1 -0
- package/dist/components/Modal.d.ts +38 -0
- package/dist/components/Modal.d.ts.map +1 -0
- package/dist/components/Modal.js +87 -0
- package/dist/components/Modal.js.map +1 -0
- package/dist/components/Progress.d.ts +27 -0
- package/dist/components/Progress.d.ts.map +1 -0
- package/dist/components/Progress.js +39 -0
- package/dist/components/Progress.js.map +1 -0
- package/dist/components/Spinner.d.ts +19 -0
- package/dist/components/Spinner.d.ts.map +1 -0
- package/dist/components/Spinner.js +27 -0
- package/dist/components/Spinner.js.map +1 -0
- package/dist/components/index.d.ts +10 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +10 -0
- package/dist/components/index.js.map +1 -0
- package/dist/data/nf-glyphs.d.ts +11 -0
- package/dist/data/nf-glyphs.d.ts.map +1 -0
- package/dist/data/nf-glyphs.js +535 -0
- package/dist/data/nf-glyphs.js.map +1 -0
- package/dist/data/nf-names.d.ts +8 -0
- package/dist/data/nf-names.d.ts.map +1 -0
- package/dist/data/nf-names.js +2 -0
- package/dist/data/nf-names.js.map +1 -0
- package/dist/demo-registry.d.ts +27 -0
- package/dist/demo-registry.d.ts.map +1 -0
- package/dist/demo-registry.js +57 -0
- package/dist/demo-registry.js.map +1 -0
- package/dist/demos/2048.ts +743 -0
- package/dist/demos/animation.ts +246 -0
- package/dist/demos/demo-layout.ts +124 -0
- package/dist/demos/engine.ts +374 -0
- package/dist/demos/interaction.ts +91 -0
- package/dist/demos/login.ts +203 -0
- package/dist/demos/mission-control.ts +558 -0
- package/dist/demos/showcase.ts +224 -0
- package/dist/detect.d.ts +104 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/detect.js +227 -0
- package/dist/detect.js.map +1 -0
- package/dist/diagnostics.d.ts +73 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics.js +225 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/dsl/context.d.ts +11 -0
- package/dist/dsl/context.d.ts.map +1 -0
- package/dist/dsl/context.js +563 -0
- package/dist/dsl/context.js.map +1 -0
- package/dist/dsl/hooks.d.ts +12 -0
- package/dist/dsl/hooks.d.ts.map +1 -0
- package/dist/dsl/hooks.js +148 -0
- package/dist/dsl/hooks.js.map +1 -0
- package/dist/dsl/interaction.d.ts +16 -0
- package/dist/dsl/interaction.d.ts.map +1 -0
- package/dist/dsl/interaction.js +101 -0
- package/dist/dsl/interaction.js.map +1 -0
- package/dist/dsl/layers.d.ts +14 -0
- package/dist/dsl/layers.d.ts.map +1 -0
- package/dist/dsl/layers.js +87 -0
- package/dist/dsl/layers.js.map +1 -0
- package/dist/dsl/motion.d.ts +10 -0
- package/dist/dsl/motion.d.ts.map +1 -0
- package/dist/dsl/motion.js +150 -0
- package/dist/dsl/motion.js.map +1 -0
- package/dist/dsl/render.d.ts +22 -0
- package/dist/dsl/render.d.ts.map +1 -0
- package/dist/dsl/render.js +69 -0
- package/dist/dsl/render.js.map +1 -0
- package/dist/dsl/types.d.ts +510 -0
- package/dist/dsl/types.d.ts.map +1 -0
- package/dist/dsl/types.js +33 -0
- package/dist/dsl/types.js.map +1 -0
- package/dist/dsl.d.ts +18 -0
- package/dist/dsl.d.ts.map +1 -0
- package/dist/dsl.js +17 -0
- package/dist/dsl.js.map +1 -0
- package/dist/easing.d.ts +45 -0
- package/dist/easing.d.ts.map +1 -0
- package/dist/easing.js +121 -0
- package/dist/easing.js.map +1 -0
- package/dist/geometry.d.ts +77 -0
- package/dist/geometry.d.ts.map +1 -0
- package/dist/geometry.js +130 -0
- package/dist/geometry.js.map +1 -0
- package/dist/icons-full.d.ts +5 -0
- package/dist/icons-full.d.ts.map +1 -0
- package/dist/icons-full.js +31 -0
- package/dist/icons-full.js.map +1 -0
- package/dist/icons.d.ts +442 -0
- package/dist/icons.d.ts.map +1 -0
- package/dist/icons.js +314 -0
- package/dist/icons.js.map +1 -0
- package/dist/index.d.ts +228 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +46 -0
- package/dist/index.js.map +1 -0
- package/dist/input.d.ts +177 -0
- package/dist/input.d.ts.map +1 -0
- package/dist/input.js +533 -0
- package/dist/input.js.map +1 -0
- package/dist/layout.d.ts +194 -0
- package/dist/layout.d.ts.map +1 -0
- package/dist/layout.js +673 -0
- package/dist/layout.js.map +1 -0
- package/dist/render.d.ts +49 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +650 -0
- package/dist/render.js.map +1 -0
- package/dist/state.d.ts +226 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +130 -0
- package/dist/state.js.map +1 -0
- package/dist/theme.d.ts +129 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +167 -0
- package/dist/theme.js.map +1 -0
- package/dist/themes/catppuccin-mocha.d.ts +19 -0
- package/dist/themes/catppuccin-mocha.d.ts.map +1 -0
- package/dist/themes/catppuccin-mocha.js +36 -0
- package/dist/themes/catppuccin-mocha.js.map +1 -0
- package/dist/themes/dracula.d.ts +21 -0
- package/dist/themes/dracula.d.ts.map +1 -0
- package/dist/themes/dracula.js +37 -0
- package/dist/themes/dracula.js.map +1 -0
- package/dist/themes/github-light.d.ts +21 -0
- package/dist/themes/github-light.d.ts.map +1 -0
- package/dist/themes/github-light.js +36 -0
- package/dist/themes/github-light.js.map +1 -0
- package/dist/themes/index.d.ts +19 -0
- package/dist/themes/index.d.ts.map +1 -0
- package/dist/themes/index.js +17 -0
- package/dist/themes/index.js.map +1 -0
- package/dist/themes/nord.d.ts +23 -0
- package/dist/themes/nord.d.ts.map +1 -0
- package/dist/themes/nord.js +38 -0
- package/dist/themes/nord.js.map +1 -0
- package/dist/themes/one-dark-pro.d.ts +21 -0
- package/dist/themes/one-dark-pro.d.ts.map +1 -0
- package/dist/themes/one-dark-pro.js +36 -0
- package/dist/themes/one-dark-pro.js.map +1 -0
- package/dist/themes/tokyo-night.d.ts +20 -0
- package/dist/themes/tokyo-night.d.ts.map +1 -0
- package/dist/themes/tokyo-night.js +37 -0
- package/dist/themes/tokyo-night.js.map +1 -0
- package/dist/utils.d.ts +28 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +158 -0
- package/dist/utils.js.map +1 -0
- package/dist/vendor/colors.d.ts +45 -0
- package/dist/vendor/colors.d.ts.map +1 -0
- package/dist/vendor/colors.js +72 -0
- package/dist/vendor/colors.js.map +1 -0
- package/llms.txt +79 -0
- package/package.json +52 -11
- package/src/colors.ts +0 -255
- package/src/components/Button.ts +0 -104
- package/src/components/Card.ts +0 -53
- package/src/components/Header.ts +0 -65
- package/src/components/Input.ts +0 -124
- package/src/components/index.ts +0 -4
- package/src/data/glyphs.ts +0 -30
- package/src/detect.ts +0 -60
- package/src/dsl.ts +0 -661
- package/src/icons.ts +0 -186
- package/src/index.ts +0 -72
- package/src/layout.ts +0 -640
- package/src/render.ts +0 -302
- package/src/state.ts +0 -148
- package/src/utils.ts +0 -165
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { Box } from '../components/index.js';
|
|
2
|
+
import {
|
|
3
|
+
ICON_MAP,
|
|
4
|
+
joinHorizontal,
|
|
5
|
+
box as renderBox,
|
|
6
|
+
splitRect,
|
|
7
|
+
truncate,
|
|
8
|
+
} from '../index.js';
|
|
9
|
+
import { demo } from './demo-layout';
|
|
10
|
+
|
|
11
|
+
const PALETTE = [
|
|
12
|
+
'bunti-blue',
|
|
13
|
+
'sky',
|
|
14
|
+
'plasma',
|
|
15
|
+
'rose',
|
|
16
|
+
'gold',
|
|
17
|
+
'mint',
|
|
18
|
+
'success',
|
|
19
|
+
'warning',
|
|
20
|
+
'error',
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
const BORDERS = ['default', 'rounded', 'double', 'dashed', 'frame'] as const;
|
|
24
|
+
|
|
25
|
+
demo('SHOWCASE', (ctx, bounds) => {
|
|
26
|
+
const { color, gradient, icon } = ctx;
|
|
27
|
+
const width = Math.min(bounds.w - 4, 104);
|
|
28
|
+
const shell = bounds.place({
|
|
29
|
+
y: 0,
|
|
30
|
+
width,
|
|
31
|
+
height: Math.min(bounds.h, 27),
|
|
32
|
+
});
|
|
33
|
+
const [left, right] = splitRect(shell, {
|
|
34
|
+
direction: 'horizontal',
|
|
35
|
+
constraints: ['44%', '1fr'],
|
|
36
|
+
gap: 2,
|
|
37
|
+
});
|
|
38
|
+
const [palette, layout] = splitRect(left!, {
|
|
39
|
+
direction: 'vertical',
|
|
40
|
+
constraints: [11, '1fr'],
|
|
41
|
+
gap: 1,
|
|
42
|
+
});
|
|
43
|
+
const [frames, textLab, iconLab] = splitRect(right!, {
|
|
44
|
+
direction: 'vertical',
|
|
45
|
+
constraints: [8, 9, '1fr'],
|
|
46
|
+
gap: 1,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
Box(
|
|
50
|
+
ctx,
|
|
51
|
+
{
|
|
52
|
+
x: palette?.x,
|
|
53
|
+
y: palette?.y,
|
|
54
|
+
width: palette?.width,
|
|
55
|
+
height: palette?.height,
|
|
56
|
+
border: 'rounded',
|
|
57
|
+
borderColor: 'bunti-blue',
|
|
58
|
+
padding: [1, 2],
|
|
59
|
+
},
|
|
60
|
+
({ text }) => {
|
|
61
|
+
text(color.fg('bunti-blue', color.bold('COLOR SYSTEM\n')));
|
|
62
|
+
for (const [index, name] of PALETTE.entries()) {
|
|
63
|
+
if (index > 0 && index % 3 === 0) text('\n');
|
|
64
|
+
text(color.dim(name.padEnd(12)));
|
|
65
|
+
text(color.fg(name, '████'));
|
|
66
|
+
text(' ');
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
Box(
|
|
72
|
+
ctx,
|
|
73
|
+
{
|
|
74
|
+
x: layout?.x,
|
|
75
|
+
y: layout?.y,
|
|
76
|
+
width: layout?.width,
|
|
77
|
+
height: layout?.height,
|
|
78
|
+
border: 'rounded',
|
|
79
|
+
borderColor: 'mint',
|
|
80
|
+
padding: [1, 2],
|
|
81
|
+
},
|
|
82
|
+
({ text }) => {
|
|
83
|
+
text(color.fg('mint', color.bold('RECT LAYOUT\n\n')));
|
|
84
|
+
text(color.dim('splitRect: fixed + percent + fill tracks\n\n'));
|
|
85
|
+
|
|
86
|
+
const tracks = splitRect(
|
|
87
|
+
{ x: 0, y: 0, width: Math.max(1, (layout?.width ?? 8) - 6), height: 3 },
|
|
88
|
+
{
|
|
89
|
+
direction: 'horizontal',
|
|
90
|
+
constraints: [8, '35%', '1fr'],
|
|
91
|
+
gap: 1,
|
|
92
|
+
},
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
const blocks = tracks.map((track, index) =>
|
|
96
|
+
renderBox(
|
|
97
|
+
color.fg(
|
|
98
|
+
'white',
|
|
99
|
+
index === 0 ? '8 cols' : index === 1 ? '35%' : 'fill',
|
|
100
|
+
),
|
|
101
|
+
{
|
|
102
|
+
width: track.width,
|
|
103
|
+
height: track.height,
|
|
104
|
+
border: 'none',
|
|
105
|
+
bgColor: index === 0 ? 'ocean' : index === 1 ? 'nebula' : 'plasma',
|
|
106
|
+
align: 'center',
|
|
107
|
+
valign: 'middle',
|
|
108
|
+
},
|
|
109
|
+
),
|
|
110
|
+
);
|
|
111
|
+
text(joinHorizontal(...blocks));
|
|
112
|
+
text('\n\n');
|
|
113
|
+
text(color.dim('Content stays inside the assigned Rect.'));
|
|
114
|
+
},
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
Box(
|
|
118
|
+
ctx,
|
|
119
|
+
{
|
|
120
|
+
x: frames?.x,
|
|
121
|
+
y: frames?.y,
|
|
122
|
+
width: frames?.width,
|
|
123
|
+
height: frames?.height,
|
|
124
|
+
border: 'rounded',
|
|
125
|
+
borderColor: 'plasma',
|
|
126
|
+
padding: [1, 2],
|
|
127
|
+
},
|
|
128
|
+
({ text }) => {
|
|
129
|
+
text(color.fg('plasma', color.bold('BORDERS + DEPTH\n\n')));
|
|
130
|
+
const cells = splitRect(
|
|
131
|
+
{ x: 0, y: 3, width: Math.max(1, (frames?.width ?? 8) - 6), height: 3 },
|
|
132
|
+
{
|
|
133
|
+
direction: 'horizontal',
|
|
134
|
+
constraints: BORDERS.map(() => '1fr'),
|
|
135
|
+
gap: 1,
|
|
136
|
+
},
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
const blocks = BORDERS.map((border, index) => {
|
|
140
|
+
const cell = cells[index]!;
|
|
141
|
+
return renderBox(
|
|
142
|
+
color.fg('silver', truncate(border, Math.max(1, cell.width - 2))),
|
|
143
|
+
{
|
|
144
|
+
width: cell.width,
|
|
145
|
+
height: cell.height,
|
|
146
|
+
border,
|
|
147
|
+
borderColor: index % 2 === 0 ? 'silver' : 'gold',
|
|
148
|
+
align: 'center',
|
|
149
|
+
valign: 'middle',
|
|
150
|
+
},
|
|
151
|
+
);
|
|
152
|
+
});
|
|
153
|
+
text(joinHorizontal(...blocks));
|
|
154
|
+
},
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
Box(
|
|
158
|
+
ctx,
|
|
159
|
+
{
|
|
160
|
+
x: textLab?.x,
|
|
161
|
+
y: textLab?.y,
|
|
162
|
+
width: textLab?.width,
|
|
163
|
+
height: textLab?.height,
|
|
164
|
+
border: 'rounded',
|
|
165
|
+
borderColor: 'gold',
|
|
166
|
+
padding: [1, 2],
|
|
167
|
+
},
|
|
168
|
+
({ text, box }) => {
|
|
169
|
+
text(color.fg('gold', color.bold('TEXT + GRADIENTS\n\n')));
|
|
170
|
+
const bar = gradient({
|
|
171
|
+
colors: ['sky', 'plasma', 'rose', 'gold', 'mint'],
|
|
172
|
+
direction: 'horizontal',
|
|
173
|
+
steps: Math.max(1, (textLab?.width ?? 8) - 6),
|
|
174
|
+
});
|
|
175
|
+
box(
|
|
176
|
+
{
|
|
177
|
+
width: '100%',
|
|
178
|
+
height: 1,
|
|
179
|
+
border: 'none',
|
|
180
|
+
bgColor: bar,
|
|
181
|
+
},
|
|
182
|
+
() => {},
|
|
183
|
+
);
|
|
184
|
+
text('\n');
|
|
185
|
+
text(
|
|
186
|
+
color.fg(
|
|
187
|
+
'silver',
|
|
188
|
+
'Wrapped text respects ANSI colors and terminal width without escape damage.',
|
|
189
|
+
),
|
|
190
|
+
);
|
|
191
|
+
},
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
Box(
|
|
195
|
+
ctx,
|
|
196
|
+
{
|
|
197
|
+
x: iconLab?.x,
|
|
198
|
+
y: iconLab?.y,
|
|
199
|
+
width: iconLab?.width,
|
|
200
|
+
height: iconLab?.height,
|
|
201
|
+
border: 'rounded',
|
|
202
|
+
borderColor: 'silver',
|
|
203
|
+
padding: [1, 2],
|
|
204
|
+
},
|
|
205
|
+
({ text, table }) => {
|
|
206
|
+
text(color.fg('silver', color.bold('ICONS + TABLES\n\n')));
|
|
207
|
+
const rows = Object.keys(ICON_MAP)
|
|
208
|
+
.slice(0, 12)
|
|
209
|
+
.reduce<string[][]>((acc, name, index) => {
|
|
210
|
+
const row = Math.floor(index / 3);
|
|
211
|
+
acc[row] ??= [];
|
|
212
|
+
acc[row]!.push(`${icon(name)} ${color.dim(name)}`);
|
|
213
|
+
return acc;
|
|
214
|
+
}, []);
|
|
215
|
+
|
|
216
|
+
table(rows, {
|
|
217
|
+
width: Math.max(1, (iconLab?.width ?? 8) - 6),
|
|
218
|
+
border: 'none',
|
|
219
|
+
padding: [0, 1],
|
|
220
|
+
columns: [{ align: 'left' }, { align: 'left' }, { align: 'left' }],
|
|
221
|
+
});
|
|
222
|
+
},
|
|
223
|
+
);
|
|
224
|
+
});
|
package/dist/detect.d.ts
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bunti Terminal Capability Detection
|
|
3
|
+
*
|
|
4
|
+
* Env-first and synchronous: `identifyTerminal()` is a pure function from an
|
|
5
|
+
* environment map to a `TerminalProfile` (app, version, multiplexer,
|
|
6
|
+
* truecolor, sync-output support, Nerd Font policy). No escape-sequence
|
|
7
|
+
* probes are sent; async handshakes are deliberately deferred.
|
|
8
|
+
*/
|
|
9
|
+
/** Legacy capability shape (kept for back-compat; see TerminalProfile). */
|
|
10
|
+
export interface TerminalCapabilities {
|
|
11
|
+
nerdFont: boolean;
|
|
12
|
+
glyphProtocol: boolean;
|
|
13
|
+
unicode: boolean;
|
|
14
|
+
color: boolean;
|
|
15
|
+
}
|
|
16
|
+
/** Terminal emulators Bunti identifies from environment variables. */
|
|
17
|
+
export type TerminalApp = 'ghostty' | 'kitty' | 'wezterm' | 'iterm2' | 'alacritty' | 'vscode' | 'apple-terminal' | 'warp' | 'unknown';
|
|
18
|
+
/**
|
|
19
|
+
* Nerd Font availability policy:
|
|
20
|
+
* - 'yes': the terminal embeds Nerd Font symbols (Ghostty >= 1.2).
|
|
21
|
+
* - 'assumed-yes': power-user terminals whose users overwhelmingly install
|
|
22
|
+
* patched fonts (kitty, WezTerm, iTerm2, Warp, Alacritty).
|
|
23
|
+
* - 'assumed-no': stock fonts, no embedded symbols (VS Code, Apple Terminal,
|
|
24
|
+
* unknown terminals) — Bunti falls back to the ASCII icon tier.
|
|
25
|
+
* - 'no': explicitly disabled via BUNTI_NF/NERD_FONTS.
|
|
26
|
+
*/
|
|
27
|
+
export type NerdFontPolicy = 'yes' | 'no' | 'assumed-yes' | 'assumed-no';
|
|
28
|
+
/**
|
|
29
|
+
* Everything Bunti knows about the hosting terminal, derived purely from
|
|
30
|
+
* environment variables. Exposed as `ctx.terminal` (detected once per
|
|
31
|
+
* render()).
|
|
32
|
+
*/
|
|
33
|
+
export interface TerminalProfile {
|
|
34
|
+
/** Identified terminal emulator ('unknown' when no signal matches). */
|
|
35
|
+
app: TerminalApp;
|
|
36
|
+
/** TERM_PROGRAM_VERSION / LC_TERMINAL_VERSION when available. */
|
|
37
|
+
version?: string;
|
|
38
|
+
/** Terminal multiplexer sitting between Bunti and the emulator. */
|
|
39
|
+
multiplexer?: 'tmux' | 'screen';
|
|
40
|
+
/** 24-bit color support (mirrors detectColorTier() === 'truecolor'). */
|
|
41
|
+
truecolor: boolean;
|
|
42
|
+
/** Known support for synchronized output (DEC private mode 2026). */
|
|
43
|
+
syncOutput: boolean;
|
|
44
|
+
/** Nerd Font glyph policy for the icon engine. */
|
|
45
|
+
nerdFont: NerdFontPolicy;
|
|
46
|
+
/** 'override' when BUNTI_NF/NERD_FONTS forced the nerdFont field. */
|
|
47
|
+
source: 'override' | 'env';
|
|
48
|
+
}
|
|
49
|
+
type Env = Record<string, string | undefined>;
|
|
50
|
+
/**
|
|
51
|
+
* Color capability tiers, from full 24-bit RGB down to no color at all.
|
|
52
|
+
* `resolveColor()` quantizes RGB values to the active tier automatically.
|
|
53
|
+
*/
|
|
54
|
+
export type ColorTier = 'truecolor' | '256' | '16' | 'mono';
|
|
55
|
+
/**
|
|
56
|
+
* Pure env-to-profile detection (defaults to process.env). Never mutates the
|
|
57
|
+
* environment and sends no escape sequences, so it is safe to call anywhere.
|
|
58
|
+
*
|
|
59
|
+
* Detection order:
|
|
60
|
+
* 1. BUNTI_NF/NERD_FONTS/NERD_FONT force the nerdFont field on or off
|
|
61
|
+
* (source: 'override'); app identification still runs.
|
|
62
|
+
* 2. TMUX/screen are recorded as `multiplexer`; app identification then
|
|
63
|
+
* relies on variables that pass through the multiplexer.
|
|
64
|
+
* 3. App signals, in order: Ghostty (GHOSTTY_RESOURCES_DIR, lowercase
|
|
65
|
+
* TERM_PROGRAM=ghostty, TERM=xterm-ghostty), kitty (KITTY_WINDOW_ID,
|
|
66
|
+
* TERM=xterm-kitty), WezTerm (WEZTERM_PANE, TERM_PROGRAM), iTerm2
|
|
67
|
+
* (TERM_PROGRAM=iTerm.app, LC_TERMINAL=iTerm2), Alacritty
|
|
68
|
+
* (ALACRITTY_WINDOW_ID, TERM=alacritty), VS Code, Apple Terminal, Warp.
|
|
69
|
+
*/
|
|
70
|
+
export declare function identifyTerminal(env?: Env): TerminalProfile;
|
|
71
|
+
/**
|
|
72
|
+
* Pure tier detection from an environment map (defaults to process.env).
|
|
73
|
+
*
|
|
74
|
+
* Rules, in order:
|
|
75
|
+
* 1. NO_COLOR set to a non-empty value -> 'mono' (https://no-color.org spec).
|
|
76
|
+
* 2. TERM=dumb -> 'mono'.
|
|
77
|
+
* 3. COLORTERM=truecolor|24bit -> 'truecolor'.
|
|
78
|
+
* 4. An identified truecolor-capable app (ghostty, kitty, wezterm, iterm2,
|
|
79
|
+
* alacritty, vscode, warp) -> 'truecolor'. This also catches TERM values
|
|
80
|
+
* like xterm-kitty that the legacy TERM families would misclassify.
|
|
81
|
+
* 5. Known truecolor TERM_PROGRAM, or a truecolor/direct TERM -> 'truecolor'.
|
|
82
|
+
* 6. TERM containing "256color" -> '256'.
|
|
83
|
+
* 7. Legacy TERM families (xterm, screen, vt100, linux, ...) -> '16'.
|
|
84
|
+
* 8. Otherwise -> 'truecolor' (optimistic: modern terminals rarely set hints).
|
|
85
|
+
*/
|
|
86
|
+
export declare function detectColorTier(env?: Env): ColorTier;
|
|
87
|
+
/**
|
|
88
|
+
* Returns the active color tier. Detection runs lazily on first call and is
|
|
89
|
+
* cached; `setColorTier()` overrides it (used by ScreenOptions.colorTier and
|
|
90
|
+
* tests).
|
|
91
|
+
*/
|
|
92
|
+
export declare function colorTier(): ColorTier;
|
|
93
|
+
/**
|
|
94
|
+
* Forces a color tier (pass undefined to clear the override and re-detect
|
|
95
|
+
* lazily on the next `colorTier()` call).
|
|
96
|
+
*/
|
|
97
|
+
export declare function setColorTier(tier?: ColorTier): void;
|
|
98
|
+
/**
|
|
99
|
+
* Back-compat wrapper over `identifyTerminal()` returning the legacy
|
|
100
|
+
* TerminalCapabilities shape. Prefer `identifyTerminal()` / `ctx.terminal`.
|
|
101
|
+
*/
|
|
102
|
+
export declare function detectCapabilities(env?: Env): Promise<TerminalCapabilities>;
|
|
103
|
+
export {};
|
|
104
|
+
//# sourceMappingURL=detect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,2EAA2E;AAC3E,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,OAAO,CAAC;IAClB,aAAa,EAAE,OAAO,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,sEAAsE;AACtE,MAAM,MAAM,WAAW,GACnB,SAAS,GACT,OAAO,GACP,SAAS,GACT,QAAQ,GACR,WAAW,GACX,QAAQ,GACR,gBAAgB,GAChB,MAAM,GACN,SAAS,CAAC;AAEd;;;;;;;;GAQG;AACH,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,IAAI,GAAG,aAAa,GAAG,YAAY,CAAC;AAEzE;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,uEAAuE;IACvE,GAAG,EAAE,WAAW,CAAC;IACjB,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAChC,wEAAwE;IACxE,SAAS,EAAE,OAAO,CAAC;IACnB,qEAAqE;IACrE,UAAU,EAAE,OAAO,CAAC;IACpB,kDAAkD;IAClD,QAAQ,EAAE,cAAc,CAAC;IACzB,qEAAqE;IACrE,MAAM,EAAE,UAAU,GAAG,KAAK,CAAC;CAC5B;AAED,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAE9C;;;GAGG;AACH,MAAM,MAAM,SAAS,GAAG,WAAW,GAAG,KAAK,GAAG,IAAI,GAAG,MAAM,CAAC;AAwH5D;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,GAAE,GAAwB,GAC5B,eAAe,CAcjB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAAC,GAAG,GAAE,GAAwB,GAAG,SAAS,CAwBxE;AAKD;;;;GAIG;AACH,wBAAgB,SAAS,IAAI,SAAS,CAIrC;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,CAAC,EAAE,SAAS,QAG5C;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,GAAE,GAAwB,GAC5B,OAAO,CAAC,oBAAoB,CAAC,CAQ/B"}
|
package/dist/detect.js
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bunti Terminal Capability Detection
|
|
3
|
+
*
|
|
4
|
+
* Env-first and synchronous: `identifyTerminal()` is a pure function from an
|
|
5
|
+
* environment map to a `TerminalProfile` (app, version, multiplexer,
|
|
6
|
+
* truecolor, sync-output support, Nerd Font policy). No escape-sequence
|
|
7
|
+
* probes are sent; async handshakes are deliberately deferred.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* TERM_PROGRAM values known to render 24-bit color but NOT part of the
|
|
11
|
+
* TerminalApp union (those are covered by app identification).
|
|
12
|
+
*/
|
|
13
|
+
const EXTRA_TRUECOLOR_PROGRAMS = ['Hyper', 'Rio', 'Term7'];
|
|
14
|
+
/** Apps with known 24-bit color support (all identified apps but Apple Terminal). */
|
|
15
|
+
const TRUECOLOR_APPS = [
|
|
16
|
+
'ghostty',
|
|
17
|
+
'kitty',
|
|
18
|
+
'wezterm',
|
|
19
|
+
'iterm2',
|
|
20
|
+
'alacritty',
|
|
21
|
+
'vscode',
|
|
22
|
+
'warp',
|
|
23
|
+
];
|
|
24
|
+
/** Apps with known synchronized-output (mode 2026) support. */
|
|
25
|
+
const SYNC_OUTPUT_APPS = [
|
|
26
|
+
'ghostty',
|
|
27
|
+
'kitty',
|
|
28
|
+
'wezterm',
|
|
29
|
+
'iterm2',
|
|
30
|
+
'alacritty',
|
|
31
|
+
'warp',
|
|
32
|
+
];
|
|
33
|
+
/** Nerd Font policy per identified app (see NerdFontPolicy). */
|
|
34
|
+
const NERD_FONT_POLICY = {
|
|
35
|
+
ghostty: 'yes', // embeds Symbols Nerd Font since 1.2
|
|
36
|
+
kitty: 'assumed-yes',
|
|
37
|
+
wezterm: 'assumed-yes',
|
|
38
|
+
iterm2: 'assumed-yes',
|
|
39
|
+
warp: 'assumed-yes',
|
|
40
|
+
alacritty: 'assumed-yes',
|
|
41
|
+
vscode: 'assumed-no',
|
|
42
|
+
'apple-terminal': 'assumed-no',
|
|
43
|
+
unknown: 'assumed-no',
|
|
44
|
+
};
|
|
45
|
+
/** The TERM_PROGRAM value each app announces (when it sets one at all). */
|
|
46
|
+
const TERM_PROGRAM_BY_APP = {
|
|
47
|
+
ghostty: 'ghostty', // lowercase — Ghostty never capitalizes it
|
|
48
|
+
wezterm: 'WezTerm',
|
|
49
|
+
iterm2: 'iTerm.app',
|
|
50
|
+
vscode: 'vscode',
|
|
51
|
+
'apple-terminal': 'Apple_Terminal',
|
|
52
|
+
warp: 'WarpTerminal',
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Identifies the terminal app, version, and multiplexer from env vars.
|
|
56
|
+
* App-specific variables (GHOSTTY_RESOURCES_DIR, KITTY_WINDOW_ID, ...) are
|
|
57
|
+
* checked alongside TERM_PROGRAM because they survive tmux/screen, whose
|
|
58
|
+
* inner TERM rewrites would otherwise mask the outer emulator.
|
|
59
|
+
*/
|
|
60
|
+
function detectApp(env) {
|
|
61
|
+
const term = env.TERM || '';
|
|
62
|
+
const termProgram = env.TERM_PROGRAM || '';
|
|
63
|
+
const multiplexer = env.TMUX
|
|
64
|
+
? 'tmux'
|
|
65
|
+
: term.startsWith('tmux')
|
|
66
|
+
? 'tmux'
|
|
67
|
+
: env.STY || term.startsWith('screen')
|
|
68
|
+
? 'screen'
|
|
69
|
+
: undefined;
|
|
70
|
+
let app = 'unknown';
|
|
71
|
+
if (env.GHOSTTY_RESOURCES_DIR ||
|
|
72
|
+
termProgram === 'ghostty' ||
|
|
73
|
+
term === 'xterm-ghostty') {
|
|
74
|
+
app = 'ghostty';
|
|
75
|
+
}
|
|
76
|
+
else if (env.KITTY_WINDOW_ID || term === 'xterm-kitty') {
|
|
77
|
+
app = 'kitty';
|
|
78
|
+
}
|
|
79
|
+
else if (env.WEZTERM_PANE || termProgram === 'WezTerm') {
|
|
80
|
+
app = 'wezterm';
|
|
81
|
+
}
|
|
82
|
+
else if (termProgram === 'iTerm.app' || env.LC_TERMINAL === 'iTerm2') {
|
|
83
|
+
app = 'iterm2';
|
|
84
|
+
}
|
|
85
|
+
else if (env.ALACRITTY_WINDOW_ID || term === 'alacritty') {
|
|
86
|
+
app = 'alacritty';
|
|
87
|
+
}
|
|
88
|
+
else if (termProgram === 'vscode') {
|
|
89
|
+
app = 'vscode';
|
|
90
|
+
}
|
|
91
|
+
else if (termProgram === 'Apple_Terminal') {
|
|
92
|
+
app = 'apple-terminal';
|
|
93
|
+
}
|
|
94
|
+
else if (termProgram === 'WarpTerminal') {
|
|
95
|
+
app = 'warp';
|
|
96
|
+
}
|
|
97
|
+
let version;
|
|
98
|
+
if (termProgram !== '' && termProgram === TERM_PROGRAM_BY_APP[app]) {
|
|
99
|
+
version = env.TERM_PROGRAM_VERSION || undefined;
|
|
100
|
+
}
|
|
101
|
+
else if (app === 'iterm2' && env.LC_TERMINAL === 'iTerm2') {
|
|
102
|
+
version = env.LC_TERMINAL_VERSION || undefined;
|
|
103
|
+
}
|
|
104
|
+
return { app, version, multiplexer };
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Parses the BUNTI_NF / NERD_FONTS / NERD_FONT override variables.
|
|
108
|
+
* Returns 'yes'/'no' when forced, undefined when unset or unrecognized.
|
|
109
|
+
*/
|
|
110
|
+
function nerdFontOverride(env) {
|
|
111
|
+
const value = env.NERD_FONTS || env.NERD_FONT || env.BUNTI_NF;
|
|
112
|
+
if (value === '1' || value === 'true' || value === 'yes')
|
|
113
|
+
return 'yes';
|
|
114
|
+
if (value === '0' || value === 'false' || value === 'no')
|
|
115
|
+
return 'no';
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Pure env-to-profile detection (defaults to process.env). Never mutates the
|
|
120
|
+
* environment and sends no escape sequences, so it is safe to call anywhere.
|
|
121
|
+
*
|
|
122
|
+
* Detection order:
|
|
123
|
+
* 1. BUNTI_NF/NERD_FONTS/NERD_FONT force the nerdFont field on or off
|
|
124
|
+
* (source: 'override'); app identification still runs.
|
|
125
|
+
* 2. TMUX/screen are recorded as `multiplexer`; app identification then
|
|
126
|
+
* relies on variables that pass through the multiplexer.
|
|
127
|
+
* 3. App signals, in order: Ghostty (GHOSTTY_RESOURCES_DIR, lowercase
|
|
128
|
+
* TERM_PROGRAM=ghostty, TERM=xterm-ghostty), kitty (KITTY_WINDOW_ID,
|
|
129
|
+
* TERM=xterm-kitty), WezTerm (WEZTERM_PANE, TERM_PROGRAM), iTerm2
|
|
130
|
+
* (TERM_PROGRAM=iTerm.app, LC_TERMINAL=iTerm2), Alacritty
|
|
131
|
+
* (ALACRITTY_WINDOW_ID, TERM=alacritty), VS Code, Apple Terminal, Warp.
|
|
132
|
+
*/
|
|
133
|
+
export function identifyTerminal(env = process.env) {
|
|
134
|
+
const { app, version, multiplexer } = detectApp(env);
|
|
135
|
+
const override = nerdFontOverride(env);
|
|
136
|
+
const profile = {
|
|
137
|
+
app,
|
|
138
|
+
truecolor: detectColorTier(env) === 'truecolor',
|
|
139
|
+
syncOutput: SYNC_OUTPUT_APPS.includes(app),
|
|
140
|
+
nerdFont: override ?? NERD_FONT_POLICY[app],
|
|
141
|
+
source: override !== undefined ? 'override' : 'env',
|
|
142
|
+
};
|
|
143
|
+
if (version !== undefined)
|
|
144
|
+
profile.version = version;
|
|
145
|
+
if (multiplexer !== undefined)
|
|
146
|
+
profile.multiplexer = multiplexer;
|
|
147
|
+
return profile;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Pure tier detection from an environment map (defaults to process.env).
|
|
151
|
+
*
|
|
152
|
+
* Rules, in order:
|
|
153
|
+
* 1. NO_COLOR set to a non-empty value -> 'mono' (https://no-color.org spec).
|
|
154
|
+
* 2. TERM=dumb -> 'mono'.
|
|
155
|
+
* 3. COLORTERM=truecolor|24bit -> 'truecolor'.
|
|
156
|
+
* 4. An identified truecolor-capable app (ghostty, kitty, wezterm, iterm2,
|
|
157
|
+
* alacritty, vscode, warp) -> 'truecolor'. This also catches TERM values
|
|
158
|
+
* like xterm-kitty that the legacy TERM families would misclassify.
|
|
159
|
+
* 5. Known truecolor TERM_PROGRAM, or a truecolor/direct TERM -> 'truecolor'.
|
|
160
|
+
* 6. TERM containing "256color" -> '256'.
|
|
161
|
+
* 7. Legacy TERM families (xterm, screen, vt100, linux, ...) -> '16'.
|
|
162
|
+
* 8. Otherwise -> 'truecolor' (optimistic: modern terminals rarely set hints).
|
|
163
|
+
*/
|
|
164
|
+
export function detectColorTier(env = process.env) {
|
|
165
|
+
if (env.NO_COLOR !== undefined && env.NO_COLOR !== '')
|
|
166
|
+
return 'mono';
|
|
167
|
+
const term = env.TERM || '';
|
|
168
|
+
if (term === 'dumb')
|
|
169
|
+
return 'mono';
|
|
170
|
+
const colorterm = (env.COLORTERM || '').toLowerCase();
|
|
171
|
+
if (colorterm === 'truecolor' || colorterm === '24bit')
|
|
172
|
+
return 'truecolor';
|
|
173
|
+
const { app } = detectApp(env);
|
|
174
|
+
if (TRUECOLOR_APPS.includes(app))
|
|
175
|
+
return 'truecolor';
|
|
176
|
+
if (EXTRA_TRUECOLOR_PROGRAMS.includes(env.TERM_PROGRAM || '')) {
|
|
177
|
+
return 'truecolor';
|
|
178
|
+
}
|
|
179
|
+
if (term.includes('truecolor') ||
|
|
180
|
+
term.includes('24bit') ||
|
|
181
|
+
term.includes('direct')) {
|
|
182
|
+
return 'truecolor';
|
|
183
|
+
}
|
|
184
|
+
if (term.includes('256color'))
|
|
185
|
+
return '256';
|
|
186
|
+
if (/^(xterm|screen|tmux|vt(1|2)\d\d|rxvt|linux|ansi|cygwin)/.test(term)) {
|
|
187
|
+
return '16';
|
|
188
|
+
}
|
|
189
|
+
return 'truecolor';
|
|
190
|
+
}
|
|
191
|
+
let tierOverride;
|
|
192
|
+
let detectedTier;
|
|
193
|
+
/**
|
|
194
|
+
* Returns the active color tier. Detection runs lazily on first call and is
|
|
195
|
+
* cached; `setColorTier()` overrides it (used by ScreenOptions.colorTier and
|
|
196
|
+
* tests).
|
|
197
|
+
*/
|
|
198
|
+
export function colorTier() {
|
|
199
|
+
if (tierOverride !== undefined)
|
|
200
|
+
return tierOverride;
|
|
201
|
+
if (detectedTier === undefined)
|
|
202
|
+
detectedTier = detectColorTier();
|
|
203
|
+
return detectedTier;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Forces a color tier (pass undefined to clear the override and re-detect
|
|
207
|
+
* lazily on the next `colorTier()` call).
|
|
208
|
+
*/
|
|
209
|
+
export function setColorTier(tier) {
|
|
210
|
+
tierOverride = tier;
|
|
211
|
+
if (tier === undefined)
|
|
212
|
+
detectedTier = undefined;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Back-compat wrapper over `identifyTerminal()` returning the legacy
|
|
216
|
+
* TerminalCapabilities shape. Prefer `identifyTerminal()` / `ctx.terminal`.
|
|
217
|
+
*/
|
|
218
|
+
export async function detectCapabilities(env = process.env) {
|
|
219
|
+
const profile = identifyTerminal(env);
|
|
220
|
+
return {
|
|
221
|
+
nerdFont: profile.nerdFont === 'yes' || profile.nerdFont === 'assumed-yes',
|
|
222
|
+
glyphProtocol: profile.app === 'ghostty',
|
|
223
|
+
unicode: true,
|
|
224
|
+
color: detectColorTier(env) !== 'mono',
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
//# sourceMappingURL=detect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect.js","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bunti Dev Diagnostics
|
|
3
|
+
*
|
|
4
|
+
* Non-fatal "you probably meant" hints that teach the API while you build.
|
|
5
|
+
* Hints are buffered (deduplicated) and flushed ONCE to stderr when the
|
|
6
|
+
* process exits — never mid-frame, so a warning can never tear an active
|
|
7
|
+
* render. All hints are suppressed when NODE_ENV=production or
|
|
8
|
+
* BUNTI_NO_HINTS=1.
|
|
9
|
+
*
|
|
10
|
+
* The icon engine's unknown-name warnings flow through the same buffer via
|
|
11
|
+
* registerHintFlusher(), so a session produces at most one hint block.
|
|
12
|
+
*/
|
|
13
|
+
import type { Rect } from './geometry';
|
|
14
|
+
import type { ScreenOptions } from './state';
|
|
15
|
+
/** Lazily produces extra hint lines at flush time (used by the icon engine). */
|
|
16
|
+
type HintFlusher = () => string[];
|
|
17
|
+
/** True when dev hints are disabled (NODE_ENV=production or BUNTI_NO_HINTS=1). */
|
|
18
|
+
export declare function hintsSuppressed(): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Buffers a one-line dev hint (deduplicated by message). Never writes to
|
|
21
|
+
* the terminal directly — everything drains on process exit.
|
|
22
|
+
*/
|
|
23
|
+
export declare function recordHint(message: string): void;
|
|
24
|
+
/**
|
|
25
|
+
* Registers a lazy hint-line producer drained at flush time. Lets modules
|
|
26
|
+
* with their own buffers (icons) share the single exit flush.
|
|
27
|
+
*/
|
|
28
|
+
export declare function registerHintFlusher(flusher: HintFlusher): void;
|
|
29
|
+
/** Renders and clears every buffered hint; '' when there are none. */
|
|
30
|
+
export declare function drainHints(): string;
|
|
31
|
+
/** Exit-hook target: writes all buffered hints to stderr, once. */
|
|
32
|
+
export declare function flushHints(): void;
|
|
33
|
+
/** Capped two-row Levenshtein; returns cap+1 as soon as it can bail out. */
|
|
34
|
+
export declare function editDistance(a: string, b: string, cap: number): number;
|
|
35
|
+
/**
|
|
36
|
+
* Best "did you mean" candidate for a name: exact match wins, then
|
|
37
|
+
* prefix relationships, then capped edit distance (cap scales with
|
|
38
|
+
* length). Returns undefined when nothing is close enough.
|
|
39
|
+
*/
|
|
40
|
+
export declare function nearestMatch(target: string, candidates: Iterable<string>): string | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* Called on reads of ctx.lastKey / ctx.keys / keyPressed / isKeyHeld:
|
|
43
|
+
* hints once when neither keyboard nor mouse input was enabled, so the
|
|
44
|
+
* app can never receive what it is polling for.
|
|
45
|
+
*/
|
|
46
|
+
export declare function hintInputReadWithoutKeyboard(options: ScreenOptions): void;
|
|
47
|
+
/**
|
|
48
|
+
* Called by the render loop: keyboard input only works on a real TTY —
|
|
49
|
+
* piped stdin (CI, `echo | bun app.ts`, agent harnesses) is ignored.
|
|
50
|
+
*/
|
|
51
|
+
export declare function hintKeyboardStdinNotTTY(keyboardRequested: boolean, stdinIsTTY: boolean): void;
|
|
52
|
+
/**
|
|
53
|
+
* Marks a frame boundary for keyless-hook tracking: compares the two most
|
|
54
|
+
* recent completed frames and hints once when the positional hook count
|
|
55
|
+
* shrank or a slot changed type (useState <-> useAsync) — the signature of
|
|
56
|
+
* conditionally-called keyless hooks returning another call's state.
|
|
57
|
+
*/
|
|
58
|
+
export declare function beginHookFrame(state: object): void;
|
|
59
|
+
/** Records one keyless hook call (positional index + hook kind). */
|
|
60
|
+
export declare function recordKeylessHook(state: object, index: number, kind: 'useState' | 'useAsync'): void;
|
|
61
|
+
/**
|
|
62
|
+
* Cheap per-frame heuristic run by flushFlow: top-level boxes paint
|
|
63
|
+
* directly to the screen buffer, so two intersecting rects mean one box
|
|
64
|
+
* painted over another with nothing controlling the stacking. Hints once,
|
|
65
|
+
* then stops checking. O(n²) over at most 50 rects per frame.
|
|
66
|
+
*/
|
|
67
|
+
export declare function hintOverlappingBoxes(rects: Rect[] | undefined): void;
|
|
68
|
+
/** @internal Test hook: currently buffered hint messages. */
|
|
69
|
+
export declare function __bufferedHints(): string[];
|
|
70
|
+
/** @internal Test hook: clears buffered hints and one-shot latches. */
|
|
71
|
+
export declare function __resetHints(): void;
|
|
72
|
+
export {};
|
|
73
|
+
//# sourceMappingURL=diagnostics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../src/diagnostics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C,gFAAgF;AAChF,KAAK,WAAW,GAAG,MAAM,MAAM,EAAE,CAAC;AAMlC,kFAAkF;AAClF,wBAAgB,eAAe,IAAI,OAAO,CAIzC;AASD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAIhD;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CAG9D;AAED,sEAAsE;AACtE,wBAAgB,UAAU,IAAI,MAAM,CAOnC;AAED,mEAAmE;AACnE,wBAAgB,UAAU,IAAI,IAAI,CAIjC;AAID,4EAA4E;AAC5E,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAiBtE;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,GAC3B,MAAM,GAAG,SAAS,CAqBpB;AAID;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,CAKzE;AAID;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,iBAAiB,EAAE,OAAO,EAC1B,UAAU,EAAE,OAAO,GAClB,IAAI,CAMN;AAgBD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAsBlD;AAED,oEAAoE;AACpE,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,UAAU,GAAG,UAAU,GAC5B,IAAI,CAKN;AAQD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,SAAS,GAAG,IAAI,CAwBpE;AAID,6DAA6D;AAC7D,wBAAgB,eAAe,IAAI,MAAM,EAAE,CAE1C;AAED,uEAAuE;AACvE,wBAAgB,YAAY,IAAI,IAAI,CAGnC"}
|