@henryavila/blink-tui 0.1.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/LICENSE +21 -0
- package/README.md +367 -0
- package/dist/index.d.ts +2429 -0
- package/dist/index.js +1901 -0
- package/package.json +71 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,2429 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { BoxProps } from 'ink';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* blink palettes — the raw colour layer (LAYER 1 of the theming contract).
|
|
6
|
+
*
|
|
7
|
+
* A palette is 26 raw colours. It is the **only** thing a theme changes; every
|
|
8
|
+
* semantic token (`--fg`, `--accent`, `--state-ok`, `--domain-*`, …) maps a
|
|
9
|
+
* *role* onto one of these slots, and that role→slot grammar is identical for
|
|
10
|
+
* every theme (see `tokens.ts`). Components never reach for a palette colour;
|
|
11
|
+
* they consume {@link SemanticTokens}.
|
|
12
|
+
*
|
|
13
|
+
* Hex values are a 1:1 port of the `[data-theme]` blocks in the design system's
|
|
14
|
+
* `colors_and_type.css`. This is the one file allowed to carry raw hex (the
|
|
15
|
+
* contract checker enforces that).
|
|
16
|
+
*
|
|
17
|
+
* Slots run darkest → lightest for surfaces / overlays / text, then the 14
|
|
18
|
+
* accent hues. Adding a theme = add one palette here and one entry in
|
|
19
|
+
* `theme.ts` — every component and screen inherits it for free.
|
|
20
|
+
*/
|
|
21
|
+
/** A raw palette colour name — the 26 Catppuccin-shaped slots. */
|
|
22
|
+
type PaletteColor = 'crust' | 'mantle' | 'base' | 'surface0' | 'surface1' | 'surface2' | 'overlay0' | 'overlay1' | 'overlay2' | 'subtext0' | 'subtext1' | 'text' | 'rosewater' | 'flamingo' | 'pink' | 'mauve' | 'red' | 'maroon' | 'peach' | 'yellow' | 'green' | 'teal' | 'sky' | 'sapphire' | 'blue' | 'lavender';
|
|
23
|
+
/** The raw palette shape: every colour name mapped to a hex string. */
|
|
24
|
+
type Palette = Record<PaletteColor, string>;
|
|
25
|
+
/** The slot order — darkest→lightest surfaces, then text tiers, then 14 hues. */
|
|
26
|
+
declare const PALETTE_SLOTS: readonly PaletteColor[];
|
|
27
|
+
/**
|
|
28
|
+
* neutral — Catppuccin Mocha. blink's calm dark base palette. `contrast`
|
|
29
|
+
* (neutral+) and `vivid` reuse this exact palette and differ only in how the
|
|
30
|
+
* intent layer spends it (see `theme.ts`).
|
|
31
|
+
*/
|
|
32
|
+
declare const neutral: Palette;
|
|
33
|
+
/** nord — frost & polar night · cool, low-chroma. */
|
|
34
|
+
declare const nord: Palette;
|
|
35
|
+
/** gruvbox — retro · warm & earthy. */
|
|
36
|
+
declare const gruvbox: Palette;
|
|
37
|
+
/**
|
|
38
|
+
* tokyonight — saturated indigo night, deepened: a darker canvas so the bright
|
|
39
|
+
* indigo text & accents pop harder. blink's default theme.
|
|
40
|
+
*/
|
|
41
|
+
declare const tokyonight: Palette;
|
|
42
|
+
/** latte — Catppuccin Latte · the light theme (same roles, inverted luminance). */
|
|
43
|
+
declare const latte: Palette;
|
|
44
|
+
/** Every named base palette, keyed by id — used by the theme registry. */
|
|
45
|
+
declare const palettes: {
|
|
46
|
+
readonly neutral: Palette;
|
|
47
|
+
readonly nord: Palette;
|
|
48
|
+
readonly gruvbox: Palette;
|
|
49
|
+
readonly tokyonight: Palette;
|
|
50
|
+
readonly latte: Palette;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Catppuccin Mocha — kept as a named export for back-compat. Identical to
|
|
54
|
+
* {@link neutral}; it is the escape hatch for the rare one-off hue.
|
|
55
|
+
*/
|
|
56
|
+
declare const catppuccinMocha: Palette;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Semantic tokens — the layer components actually consume (LAYER 2 of the
|
|
60
|
+
* theming contract).
|
|
61
|
+
*
|
|
62
|
+
* A 1:1 port of the intent `--*` custom properties in the design system's
|
|
63
|
+
* `colors_and_type.css`. Each token maps a *role* ("focus", "an error", "a
|
|
64
|
+
* postgres row") onto a palette slot. This grammar is **identical for every
|
|
65
|
+
* theme** — only the palette underneath it changes — which is what lets a
|
|
66
|
+
* component recolour for free when the surface theme flips. A component never
|
|
67
|
+
* sees a palette name and has no colour prop.
|
|
68
|
+
*
|
|
69
|
+
* In a terminal there is no CSS background; `bg*` tokens are applied via Ink's
|
|
70
|
+
* `backgroundColor` prop (used sparingly — selection fills, inverse hotkeys)
|
|
71
|
+
* and `fg*` tokens via `color`.
|
|
72
|
+
*/
|
|
73
|
+
interface SemanticTokens {
|
|
74
|
+
bg: string;
|
|
75
|
+
bgElevated: string;
|
|
76
|
+
bgSunken: string;
|
|
77
|
+
bgPanel: string;
|
|
78
|
+
bgSelected: string;
|
|
79
|
+
bgFocused: string;
|
|
80
|
+
bgInverse: string;
|
|
81
|
+
fg: string;
|
|
82
|
+
fgMuted: string;
|
|
83
|
+
fgDim: string;
|
|
84
|
+
fgFaint: string;
|
|
85
|
+
fgDisabled: string;
|
|
86
|
+
fgInverse: string;
|
|
87
|
+
border: string;
|
|
88
|
+
borderFocus: string;
|
|
89
|
+
borderStrong: string;
|
|
90
|
+
accent: string;
|
|
91
|
+
accentAlt: string;
|
|
92
|
+
link: string;
|
|
93
|
+
highlight: string;
|
|
94
|
+
stateOk: string;
|
|
95
|
+
stateErr: string;
|
|
96
|
+
stateWarn: string;
|
|
97
|
+
statePending: string;
|
|
98
|
+
stateDrift: string;
|
|
99
|
+
stateInfo: string;
|
|
100
|
+
domainBlue: string;
|
|
101
|
+
domainAzure: string;
|
|
102
|
+
domainCyan: string;
|
|
103
|
+
domainGreen: string;
|
|
104
|
+
domainRed: string;
|
|
105
|
+
domainAmber: string;
|
|
106
|
+
domainYellow: string;
|
|
107
|
+
domainViolet: string;
|
|
108
|
+
domainNeutral: string;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Build the default intent mapping for a palette. This is LAYER 2 of the
|
|
112
|
+
* contract — the role→slot grammar that never moves between themes. A theme is
|
|
113
|
+
* just `buildTokens(itsPalette)`, optionally with a few intent overrides layered
|
|
114
|
+
* on top (see `theme.ts`).
|
|
115
|
+
*/
|
|
116
|
+
declare function buildTokens(p: Palette): SemanticTokens;
|
|
117
|
+
/** Catppuccin Mocha (neutral) semantic tokens — kept as a named export. */
|
|
118
|
+
declare const mochaTokens: SemanticTokens;
|
|
119
|
+
|
|
120
|
+
/** Dark or light surface — drives a theme picker's grouping, nothing else. */
|
|
121
|
+
type ThemeMode = 'dark' | 'light';
|
|
122
|
+
/**
|
|
123
|
+
* A blink theme bundles a raw {@link Palette} with its {@link SemanticTokens}
|
|
124
|
+
* mapping plus picker metadata. The colour scheme is a property of the terminal
|
|
125
|
+
* *surface*, exactly like a real terminal emulator: a component emits a semantic
|
|
126
|
+
* role and the surface decides the pixels. Switching themes re-renders every
|
|
127
|
+
* component from the new tokens — a component cannot diverge because it owns no
|
|
128
|
+
* colour. The surface is owned in exactly one place (the `ThemeProvider`).
|
|
129
|
+
*/
|
|
130
|
+
interface Theme {
|
|
131
|
+
/** Stable id, e.g. `"tokyonight"`. */
|
|
132
|
+
id: string;
|
|
133
|
+
/** Human label for a picker, e.g. `"tokyo night"`. */
|
|
134
|
+
label: string;
|
|
135
|
+
/** Dark or light. */
|
|
136
|
+
mode: ThemeMode;
|
|
137
|
+
/** One-line description for a picker. */
|
|
138
|
+
blurb: string;
|
|
139
|
+
/** Raw palette (escape hatch for one-off hues). */
|
|
140
|
+
palette: Palette;
|
|
141
|
+
/** Role-mapped tokens — what components consume. */
|
|
142
|
+
tokens: SemanticTokens;
|
|
143
|
+
/** @deprecated alias of {@link id}, kept for back-compat. */
|
|
144
|
+
name: string;
|
|
145
|
+
}
|
|
146
|
+
/** Just the picker-facing fields of a {@link Theme}. */
|
|
147
|
+
type ThemeMeta = Pick<Theme, 'id' | 'label' | 'mode' | 'blurb'>;
|
|
148
|
+
/** neutral — Catppuccin Mocha · the calm dark default palette. */
|
|
149
|
+
declare const mocha: Theme;
|
|
150
|
+
/** blink's default theme — `tokyonight`. Used when no theme is selected. */
|
|
151
|
+
declare const defaultTheme: Theme;
|
|
152
|
+
/** Resolve a theme by id (falls back to {@link defaultTheme} for an unknown id). */
|
|
153
|
+
declare function getTheme(id: string): Theme;
|
|
154
|
+
/** True if a theme id is registered. */
|
|
155
|
+
declare function hasTheme(id: string): boolean;
|
|
156
|
+
/** Every registered theme, in picker order. */
|
|
157
|
+
declare function allThemes(): Theme[];
|
|
158
|
+
/** Picker-facing metadata for every theme, in order. */
|
|
159
|
+
declare function listThemes(): ThemeMeta[];
|
|
160
|
+
/** A runtime theme definition — mirrors the design system's `registerTheme()`. */
|
|
161
|
+
interface ThemeDefinition {
|
|
162
|
+
/** Stable id (required). */
|
|
163
|
+
id: string;
|
|
164
|
+
/** Picker label. Defaults to `id`. */
|
|
165
|
+
label?: string;
|
|
166
|
+
/** Dark or light. Defaults to `'dark'`. */
|
|
167
|
+
mode?: ThemeMode;
|
|
168
|
+
/** One-line picker description. */
|
|
169
|
+
blurb?: string;
|
|
170
|
+
/** Base theme id to inherit the palette from. Defaults to `'neutral'`. */
|
|
171
|
+
extends?: string;
|
|
172
|
+
/** Palette slots to override — only list what you change; the rest inherit. */
|
|
173
|
+
palette?: Partial<Palette>;
|
|
174
|
+
/** Intent-token overrides (the "vivid"/"neutral+" trick). */
|
|
175
|
+
intent?: Partial<SemanticTokens>;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Register (or replace) a theme at runtime, from an app's own code — no
|
|
179
|
+
* framework edits, mirroring `registerGlyphs`. Only list the slots you want to
|
|
180
|
+
* change; everything else inherits from `extends` (default `neutral`), so a
|
|
181
|
+
* handful of accent overrides is already a complete theme:
|
|
182
|
+
*
|
|
183
|
+
* ```ts
|
|
184
|
+
* registerTheme({
|
|
185
|
+
* id: 'dracula', label: 'dracula', blurb: 'classic purple dark',
|
|
186
|
+
* extends: 'neutral',
|
|
187
|
+
* // hex strings for just the slots you change — the rest inherit from `extends`
|
|
188
|
+
* palette: { base, surface1, text, lavender, red, green },
|
|
189
|
+
* });
|
|
190
|
+
* // then drive it through the ThemeProvider / useThemeControls().setTheme('dracula')
|
|
191
|
+
* ```
|
|
192
|
+
*
|
|
193
|
+
* Returns the assembled {@link Theme}. Unlike the web engine there is no global
|
|
194
|
+
* "current theme" to `select` — the active theme is owned by the surface (the
|
|
195
|
+
* `ThemeProvider`); switch with `useThemeControls().setTheme(id)`.
|
|
196
|
+
*/
|
|
197
|
+
declare function registerTheme(def: ThemeDefinition): Theme;
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* The three rendering modes every blink glyph supports.
|
|
201
|
+
*
|
|
202
|
+
* - `nerd` — full Nerd Font vocabulary, incl. private-use domain logos.
|
|
203
|
+
* - `unicode` — safe Unicode glyphs (✓ ✗ ◯ …); domain glyphs degrade to text.
|
|
204
|
+
* - `ascii` — `[x] [!] [ ]` etc. for CI, `TERM=dumb`, and fontless terminals.
|
|
205
|
+
*
|
|
206
|
+
* An app never breaks: if the font is missing, the worst case is text-shaped
|
|
207
|
+
* fallbacks, never tofu boxes (□). Resolved once at startup by
|
|
208
|
+
* `detectIconSet()` and carried in the theme context.
|
|
209
|
+
*/
|
|
210
|
+
type IconSet = 'nerd' | 'unicode' | 'ascii';
|
|
211
|
+
/**
|
|
212
|
+
* A glyph's colour, expressed as **intent** — a semantic token key, never a raw
|
|
213
|
+
* hue. A domain glyph paints through a hue family (`'domainBlue'`,
|
|
214
|
+
* `'domainGreen'`, …) so it keeps its relative identity *and* recolours with the
|
|
215
|
+
* active theme. State / nav contract glyphs leave this unset — their colour
|
|
216
|
+
* comes from the state-intent map, not the glyph entry.
|
|
217
|
+
*/
|
|
218
|
+
type GlyphColor = keyof SemanticTokens;
|
|
219
|
+
/** A single glyph's three rendering variants, plus an optional owned colour. */
|
|
220
|
+
interface GlyphVariants {
|
|
221
|
+
nerd: string;
|
|
222
|
+
unicode: string;
|
|
223
|
+
ascii: string;
|
|
224
|
+
/**
|
|
225
|
+
* The colour this glyph renders in, owned at registration (the "intent, not
|
|
226
|
+
* style" rule for domain glyphs) and given as a {@link SemanticTokens} key.
|
|
227
|
+
* Components resolve it through the active theme via `glyphColor(name)` and
|
|
228
|
+
* fall back to a muted token when absent.
|
|
229
|
+
*/
|
|
230
|
+
color?: GlyphColor;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* What every blink app reads from context: the active {@link Theme}, the
|
|
235
|
+
* resolved {@link IconSet}, and the controls to switch theme. The theme is the
|
|
236
|
+
* one mutable piece of the surface — exactly like a terminal emulator's scheme.
|
|
237
|
+
* The icon set is decided once at boot.
|
|
238
|
+
*/
|
|
239
|
+
interface BlinkContextValue {
|
|
240
|
+
theme: Theme;
|
|
241
|
+
iconSet: IconSet;
|
|
242
|
+
/** Switch the active theme by id or by a {@link Theme} object. */
|
|
243
|
+
setTheme: (theme: string | Theme) => void;
|
|
244
|
+
/** Every registered theme's picker metadata, in order. */
|
|
245
|
+
themes: ThemeMeta[];
|
|
246
|
+
}
|
|
247
|
+
interface ThemeProviderProps {
|
|
248
|
+
/**
|
|
249
|
+
* The initial theme — a {@link Theme} object or a registered theme id. When
|
|
250
|
+
* omitted, blink starts on its default (`tokyonight`). Components read the
|
|
251
|
+
* *active* theme, which a picker can switch via `useThemeControls()`.
|
|
252
|
+
*/
|
|
253
|
+
theme?: Theme | string;
|
|
254
|
+
/** @deprecated alias of {@link theme} when passing an id. */
|
|
255
|
+
initialTheme?: string;
|
|
256
|
+
/**
|
|
257
|
+
* Resolved icon set. Pass the result of `detectIconSet()` (awaited at boot).
|
|
258
|
+
* Defaults to `'unicode'` — the safe mode that renders everywhere.
|
|
259
|
+
*/
|
|
260
|
+
iconSet?: IconSet;
|
|
261
|
+
children: React.ReactNode;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Wrap a blink app once, at the root, above `<App/>` — the surface that owns the
|
|
265
|
+
* colour scheme:
|
|
266
|
+
*
|
|
267
|
+
* ```tsx
|
|
268
|
+
* const iconSet = await detectIconSet();
|
|
269
|
+
* render(
|
|
270
|
+
* <ThemeProvider iconSet={iconSet} theme="tokyonight">
|
|
271
|
+
* <App />
|
|
272
|
+
* </ThemeProvider>
|
|
273
|
+
* );
|
|
274
|
+
* ```
|
|
275
|
+
*
|
|
276
|
+
* The provider holds the active theme in state; a theme picker calls
|
|
277
|
+
* `useThemeControls().setTheme(id)` and the whole tree recolours from the new
|
|
278
|
+
* tokens. No component sets, reads, or branches on the theme — consistency is
|
|
279
|
+
* structural, not a matter of discipline.
|
|
280
|
+
*/
|
|
281
|
+
declare function ThemeProvider({ theme, initialTheme, iconSet, children, }: ThemeProviderProps): React.ReactElement;
|
|
282
|
+
/** Full context: `{ theme, iconSet, setTheme, themes }`. */
|
|
283
|
+
declare function useBlink$1(): BlinkContextValue;
|
|
284
|
+
/** The active theme. */
|
|
285
|
+
declare function useTheme(): Theme;
|
|
286
|
+
/** Just the semantic tokens — the common case in components. */
|
|
287
|
+
declare function useTokens(): SemanticTokens;
|
|
288
|
+
/** The resolved icon set (`'nerd' | 'unicode' | 'ascii'`). */
|
|
289
|
+
declare function useIconSet(): IconSet;
|
|
290
|
+
/** The theme switch + list — what a theme picker drives. */
|
|
291
|
+
interface ThemeControls {
|
|
292
|
+
/** The active theme. */
|
|
293
|
+
theme: Theme;
|
|
294
|
+
/** The active theme's id. */
|
|
295
|
+
themeId: string;
|
|
296
|
+
/** Switch theme by id or {@link Theme}. */
|
|
297
|
+
setTheme: (theme: string | Theme) => void;
|
|
298
|
+
/** Every registered theme's metadata, in picker order. */
|
|
299
|
+
themes: ThemeMeta[];
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* The theme controls for a picker — the TUI analogue of the design system's
|
|
303
|
+
* `BlinkTheme.setTheme / getTheme / THEMES`. The picker is the *only* place that
|
|
304
|
+
* should switch the theme; ordinary components just read {@link useTokens}.
|
|
305
|
+
*/
|
|
306
|
+
declare function useThemeControls(): ThemeControls;
|
|
307
|
+
|
|
308
|
+
/** Tier 1 · COMMON_DOMAINS — the dev-tool domains most TUIs reuse. */
|
|
309
|
+
declare const COMMON_DOMAINS: {
|
|
310
|
+
database: {
|
|
311
|
+
nerd: string;
|
|
312
|
+
unicode: string;
|
|
313
|
+
ascii: string;
|
|
314
|
+
color: "domainNeutral";
|
|
315
|
+
};
|
|
316
|
+
mysql: {
|
|
317
|
+
nerd: string;
|
|
318
|
+
unicode: string;
|
|
319
|
+
ascii: string;
|
|
320
|
+
color: "domainAzure";
|
|
321
|
+
};
|
|
322
|
+
postgresql: {
|
|
323
|
+
nerd: string;
|
|
324
|
+
unicode: string;
|
|
325
|
+
ascii: string;
|
|
326
|
+
color: "domainBlue";
|
|
327
|
+
};
|
|
328
|
+
redis: {
|
|
329
|
+
nerd: string;
|
|
330
|
+
unicode: string;
|
|
331
|
+
ascii: string;
|
|
332
|
+
color: "domainRed";
|
|
333
|
+
};
|
|
334
|
+
docker: {
|
|
335
|
+
nerd: string;
|
|
336
|
+
unicode: string;
|
|
337
|
+
ascii: string;
|
|
338
|
+
color: "domainCyan";
|
|
339
|
+
};
|
|
340
|
+
github: {
|
|
341
|
+
nerd: string;
|
|
342
|
+
unicode: string;
|
|
343
|
+
ascii: string;
|
|
344
|
+
color: "domainNeutral";
|
|
345
|
+
};
|
|
346
|
+
git: {
|
|
347
|
+
nerd: string;
|
|
348
|
+
unicode: string;
|
|
349
|
+
ascii: string;
|
|
350
|
+
color: "domainAmber";
|
|
351
|
+
};
|
|
352
|
+
ssh: {
|
|
353
|
+
nerd: string;
|
|
354
|
+
unicode: string;
|
|
355
|
+
ascii: string;
|
|
356
|
+
color: "domainYellow";
|
|
357
|
+
};
|
|
358
|
+
nodejs: {
|
|
359
|
+
nerd: string;
|
|
360
|
+
unicode: string;
|
|
361
|
+
ascii: string;
|
|
362
|
+
color: "domainGreen";
|
|
363
|
+
};
|
|
364
|
+
php: {
|
|
365
|
+
nerd: string;
|
|
366
|
+
unicode: string;
|
|
367
|
+
ascii: string;
|
|
368
|
+
color: "domainViolet";
|
|
369
|
+
};
|
|
370
|
+
python: {
|
|
371
|
+
nerd: string;
|
|
372
|
+
unicode: string;
|
|
373
|
+
ascii: string;
|
|
374
|
+
color: "domainYellow";
|
|
375
|
+
};
|
|
376
|
+
vim: {
|
|
377
|
+
nerd: string;
|
|
378
|
+
unicode: string;
|
|
379
|
+
ascii: string;
|
|
380
|
+
color: "domainGreen";
|
|
381
|
+
};
|
|
382
|
+
apple: {
|
|
383
|
+
nerd: string;
|
|
384
|
+
unicode: string;
|
|
385
|
+
ascii: string;
|
|
386
|
+
color: "domainNeutral";
|
|
387
|
+
};
|
|
388
|
+
linux: {
|
|
389
|
+
nerd: string;
|
|
390
|
+
unicode: string;
|
|
391
|
+
ascii: string;
|
|
392
|
+
color: "domainYellow";
|
|
393
|
+
};
|
|
394
|
+
ubuntu: {
|
|
395
|
+
nerd: string;
|
|
396
|
+
unicode: string;
|
|
397
|
+
ascii: string;
|
|
398
|
+
color: "domainAmber";
|
|
399
|
+
};
|
|
400
|
+
font: {
|
|
401
|
+
nerd: string;
|
|
402
|
+
unicode: string;
|
|
403
|
+
ascii: string;
|
|
404
|
+
color: "domainNeutral";
|
|
405
|
+
};
|
|
406
|
+
ai: {
|
|
407
|
+
nerd: string;
|
|
408
|
+
unicode: string;
|
|
409
|
+
ascii: string;
|
|
410
|
+
color: "accent";
|
|
411
|
+
};
|
|
412
|
+
bolt: {
|
|
413
|
+
nerd: string;
|
|
414
|
+
unicode: string;
|
|
415
|
+
ascii: string;
|
|
416
|
+
color: "domainAmber";
|
|
417
|
+
};
|
|
418
|
+
};
|
|
419
|
+
/** A domain name from the {@link COMMON_DOMAINS} (Tier 1) pack. */
|
|
420
|
+
type CommonDomainName = keyof typeof COMMON_DOMAINS;
|
|
421
|
+
/** languages — programming languages / runtimes. */
|
|
422
|
+
declare const LANGUAGES: {
|
|
423
|
+
javascript: {
|
|
424
|
+
nerd: string;
|
|
425
|
+
unicode: string;
|
|
426
|
+
ascii: string;
|
|
427
|
+
color: "domainYellow";
|
|
428
|
+
};
|
|
429
|
+
typescript: {
|
|
430
|
+
nerd: string;
|
|
431
|
+
unicode: string;
|
|
432
|
+
ascii: string;
|
|
433
|
+
color: "domainBlue";
|
|
434
|
+
};
|
|
435
|
+
python: {
|
|
436
|
+
nerd: string;
|
|
437
|
+
unicode: string;
|
|
438
|
+
ascii: string;
|
|
439
|
+
color: "domainYellow";
|
|
440
|
+
};
|
|
441
|
+
php: {
|
|
442
|
+
nerd: string;
|
|
443
|
+
unicode: string;
|
|
444
|
+
ascii: string;
|
|
445
|
+
color: "domainViolet";
|
|
446
|
+
};
|
|
447
|
+
ruby: {
|
|
448
|
+
nerd: string;
|
|
449
|
+
unicode: string;
|
|
450
|
+
ascii: string;
|
|
451
|
+
color: "domainRed";
|
|
452
|
+
};
|
|
453
|
+
rust: {
|
|
454
|
+
nerd: string;
|
|
455
|
+
unicode: string;
|
|
456
|
+
ascii: string;
|
|
457
|
+
color: "domainAmber";
|
|
458
|
+
};
|
|
459
|
+
go: {
|
|
460
|
+
nerd: string;
|
|
461
|
+
unicode: string;
|
|
462
|
+
ascii: string;
|
|
463
|
+
color: "domainCyan";
|
|
464
|
+
};
|
|
465
|
+
java: {
|
|
466
|
+
nerd: string;
|
|
467
|
+
unicode: string;
|
|
468
|
+
ascii: string;
|
|
469
|
+
color: "domainAmber";
|
|
470
|
+
};
|
|
471
|
+
nodejs: {
|
|
472
|
+
nerd: string;
|
|
473
|
+
unicode: string;
|
|
474
|
+
ascii: string;
|
|
475
|
+
color: "domainGreen";
|
|
476
|
+
};
|
|
477
|
+
cpp: {
|
|
478
|
+
nerd: string;
|
|
479
|
+
unicode: string;
|
|
480
|
+
ascii: string;
|
|
481
|
+
color: "domainBlue";
|
|
482
|
+
};
|
|
483
|
+
c: {
|
|
484
|
+
nerd: string;
|
|
485
|
+
unicode: string;
|
|
486
|
+
ascii: string;
|
|
487
|
+
color: "domainBlue";
|
|
488
|
+
};
|
|
489
|
+
csharp: {
|
|
490
|
+
nerd: string;
|
|
491
|
+
unicode: string;
|
|
492
|
+
ascii: string;
|
|
493
|
+
color: "domainViolet";
|
|
494
|
+
};
|
|
495
|
+
html: {
|
|
496
|
+
nerd: string;
|
|
497
|
+
unicode: string;
|
|
498
|
+
ascii: string;
|
|
499
|
+
color: "domainAmber";
|
|
500
|
+
};
|
|
501
|
+
css: {
|
|
502
|
+
nerd: string;
|
|
503
|
+
unicode: string;
|
|
504
|
+
ascii: string;
|
|
505
|
+
color: "domainBlue";
|
|
506
|
+
};
|
|
507
|
+
react: {
|
|
508
|
+
nerd: string;
|
|
509
|
+
unicode: string;
|
|
510
|
+
ascii: string;
|
|
511
|
+
color: "domainCyan";
|
|
512
|
+
};
|
|
513
|
+
vue: {
|
|
514
|
+
nerd: string;
|
|
515
|
+
unicode: string;
|
|
516
|
+
ascii: string;
|
|
517
|
+
color: "domainGreen";
|
|
518
|
+
};
|
|
519
|
+
};
|
|
520
|
+
/** databases — engines & stores. */
|
|
521
|
+
declare const DATABASES: {
|
|
522
|
+
database: {
|
|
523
|
+
nerd: string;
|
|
524
|
+
unicode: string;
|
|
525
|
+
ascii: string;
|
|
526
|
+
color: "domainNeutral";
|
|
527
|
+
};
|
|
528
|
+
postgresql: {
|
|
529
|
+
nerd: string;
|
|
530
|
+
unicode: string;
|
|
531
|
+
ascii: string;
|
|
532
|
+
color: "domainBlue";
|
|
533
|
+
};
|
|
534
|
+
mysql: {
|
|
535
|
+
nerd: string;
|
|
536
|
+
unicode: string;
|
|
537
|
+
ascii: string;
|
|
538
|
+
color: "domainAzure";
|
|
539
|
+
};
|
|
540
|
+
mariadb: {
|
|
541
|
+
nerd: string;
|
|
542
|
+
unicode: string;
|
|
543
|
+
ascii: string;
|
|
544
|
+
color: "domainAzure";
|
|
545
|
+
};
|
|
546
|
+
redis: {
|
|
547
|
+
nerd: string;
|
|
548
|
+
unicode: string;
|
|
549
|
+
ascii: string;
|
|
550
|
+
color: "domainRed";
|
|
551
|
+
};
|
|
552
|
+
mongodb: {
|
|
553
|
+
nerd: string;
|
|
554
|
+
unicode: string;
|
|
555
|
+
ascii: string;
|
|
556
|
+
color: "domainGreen";
|
|
557
|
+
};
|
|
558
|
+
sqlite: {
|
|
559
|
+
nerd: string;
|
|
560
|
+
unicode: string;
|
|
561
|
+
ascii: string;
|
|
562
|
+
color: "domainBlue";
|
|
563
|
+
};
|
|
564
|
+
};
|
|
565
|
+
/** cloud — cloud / devops / infra. */
|
|
566
|
+
declare const CLOUD: {
|
|
567
|
+
aws: {
|
|
568
|
+
nerd: string;
|
|
569
|
+
unicode: string;
|
|
570
|
+
ascii: string;
|
|
571
|
+
color: "domainAmber";
|
|
572
|
+
};
|
|
573
|
+
cloud: {
|
|
574
|
+
nerd: string;
|
|
575
|
+
unicode: string;
|
|
576
|
+
ascii: string;
|
|
577
|
+
color: "domainCyan";
|
|
578
|
+
};
|
|
579
|
+
server: {
|
|
580
|
+
nerd: string;
|
|
581
|
+
unicode: string;
|
|
582
|
+
ascii: string;
|
|
583
|
+
color: "domainNeutral";
|
|
584
|
+
};
|
|
585
|
+
docker: {
|
|
586
|
+
nerd: string;
|
|
587
|
+
unicode: string;
|
|
588
|
+
ascii: string;
|
|
589
|
+
color: "domainCyan";
|
|
590
|
+
};
|
|
591
|
+
kubernetes: {
|
|
592
|
+
nerd: string;
|
|
593
|
+
unicode: string;
|
|
594
|
+
ascii: string;
|
|
595
|
+
color: "domainBlue";
|
|
596
|
+
};
|
|
597
|
+
nginx: {
|
|
598
|
+
nerd: string;
|
|
599
|
+
unicode: string;
|
|
600
|
+
ascii: string;
|
|
601
|
+
color: "domainGreen";
|
|
602
|
+
};
|
|
603
|
+
};
|
|
604
|
+
/** editors — editors / IDEs. */
|
|
605
|
+
declare const EDITORS: {
|
|
606
|
+
vim: {
|
|
607
|
+
nerd: string;
|
|
608
|
+
unicode: string;
|
|
609
|
+
ascii: string;
|
|
610
|
+
color: "domainGreen";
|
|
611
|
+
};
|
|
612
|
+
neovim: {
|
|
613
|
+
nerd: string;
|
|
614
|
+
unicode: string;
|
|
615
|
+
ascii: string;
|
|
616
|
+
color: "domainGreen";
|
|
617
|
+
};
|
|
618
|
+
vscode: {
|
|
619
|
+
nerd: string;
|
|
620
|
+
unicode: string;
|
|
621
|
+
ascii: string;
|
|
622
|
+
color: "domainBlue";
|
|
623
|
+
};
|
|
624
|
+
sublime: {
|
|
625
|
+
nerd: string;
|
|
626
|
+
unicode: string;
|
|
627
|
+
ascii: string;
|
|
628
|
+
color: "domainAmber";
|
|
629
|
+
};
|
|
630
|
+
emacs: {
|
|
631
|
+
nerd: string;
|
|
632
|
+
unicode: string;
|
|
633
|
+
ascii: string;
|
|
634
|
+
color: "domainViolet";
|
|
635
|
+
};
|
|
636
|
+
};
|
|
637
|
+
/** os — operating systems / distros. */
|
|
638
|
+
declare const OS: {
|
|
639
|
+
apple: {
|
|
640
|
+
nerd: string;
|
|
641
|
+
unicode: string;
|
|
642
|
+
ascii: string;
|
|
643
|
+
color: "domainNeutral";
|
|
644
|
+
};
|
|
645
|
+
linux: {
|
|
646
|
+
nerd: string;
|
|
647
|
+
unicode: string;
|
|
648
|
+
ascii: string;
|
|
649
|
+
color: "domainYellow";
|
|
650
|
+
};
|
|
651
|
+
ubuntu: {
|
|
652
|
+
nerd: string;
|
|
653
|
+
unicode: string;
|
|
654
|
+
ascii: string;
|
|
655
|
+
color: "domainAmber";
|
|
656
|
+
};
|
|
657
|
+
debian: {
|
|
658
|
+
nerd: string;
|
|
659
|
+
unicode: string;
|
|
660
|
+
ascii: string;
|
|
661
|
+
color: "domainRed";
|
|
662
|
+
};
|
|
663
|
+
arch: {
|
|
664
|
+
nerd: string;
|
|
665
|
+
unicode: string;
|
|
666
|
+
ascii: string;
|
|
667
|
+
color: "domainBlue";
|
|
668
|
+
};
|
|
669
|
+
fedora: {
|
|
670
|
+
nerd: string;
|
|
671
|
+
unicode: string;
|
|
672
|
+
ascii: string;
|
|
673
|
+
color: "domainBlue";
|
|
674
|
+
};
|
|
675
|
+
windows: {
|
|
676
|
+
nerd: string;
|
|
677
|
+
unicode: string;
|
|
678
|
+
ascii: string;
|
|
679
|
+
color: "domainCyan";
|
|
680
|
+
};
|
|
681
|
+
android: {
|
|
682
|
+
nerd: string;
|
|
683
|
+
unicode: string;
|
|
684
|
+
ascii: string;
|
|
685
|
+
color: "domainGreen";
|
|
686
|
+
};
|
|
687
|
+
};
|
|
688
|
+
/** companies — brands / vendors. */
|
|
689
|
+
declare const COMPANIES: {
|
|
690
|
+
github: {
|
|
691
|
+
nerd: string;
|
|
692
|
+
unicode: string;
|
|
693
|
+
ascii: string;
|
|
694
|
+
color: "domainNeutral";
|
|
695
|
+
};
|
|
696
|
+
gitlab: {
|
|
697
|
+
nerd: string;
|
|
698
|
+
unicode: string;
|
|
699
|
+
ascii: string;
|
|
700
|
+
color: "domainAmber";
|
|
701
|
+
};
|
|
702
|
+
bitbucket: {
|
|
703
|
+
nerd: string;
|
|
704
|
+
unicode: string;
|
|
705
|
+
ascii: string;
|
|
706
|
+
color: "domainBlue";
|
|
707
|
+
};
|
|
708
|
+
google: {
|
|
709
|
+
nerd: string;
|
|
710
|
+
unicode: string;
|
|
711
|
+
ascii: string;
|
|
712
|
+
color: "domainBlue";
|
|
713
|
+
};
|
|
714
|
+
microsoft: {
|
|
715
|
+
nerd: string;
|
|
716
|
+
unicode: string;
|
|
717
|
+
ascii: string;
|
|
718
|
+
color: "domainCyan";
|
|
719
|
+
};
|
|
720
|
+
apple: {
|
|
721
|
+
nerd: string;
|
|
722
|
+
unicode: string;
|
|
723
|
+
ascii: string;
|
|
724
|
+
color: "domainNeutral";
|
|
725
|
+
};
|
|
726
|
+
slack: {
|
|
727
|
+
nerd: string;
|
|
728
|
+
unicode: string;
|
|
729
|
+
ascii: string;
|
|
730
|
+
color: "domainViolet";
|
|
731
|
+
};
|
|
732
|
+
npm: {
|
|
733
|
+
nerd: string;
|
|
734
|
+
unicode: string;
|
|
735
|
+
ascii: string;
|
|
736
|
+
color: "domainRed";
|
|
737
|
+
};
|
|
738
|
+
git: {
|
|
739
|
+
nerd: string;
|
|
740
|
+
unicode: string;
|
|
741
|
+
ascii: string;
|
|
742
|
+
color: "domainAmber";
|
|
743
|
+
};
|
|
744
|
+
claude: {
|
|
745
|
+
nerd: string;
|
|
746
|
+
unicode: string;
|
|
747
|
+
ascii: string;
|
|
748
|
+
color: "accent";
|
|
749
|
+
};
|
|
750
|
+
};
|
|
751
|
+
/** frameworks — web / app frameworks. */
|
|
752
|
+
declare const FRAMEWORKS: {
|
|
753
|
+
react: {
|
|
754
|
+
nerd: string;
|
|
755
|
+
unicode: string;
|
|
756
|
+
ascii: string;
|
|
757
|
+
color: "domainCyan";
|
|
758
|
+
};
|
|
759
|
+
vue: {
|
|
760
|
+
nerd: string;
|
|
761
|
+
unicode: string;
|
|
762
|
+
ascii: string;
|
|
763
|
+
color: "domainGreen";
|
|
764
|
+
};
|
|
765
|
+
angular: {
|
|
766
|
+
nerd: string;
|
|
767
|
+
unicode: string;
|
|
768
|
+
ascii: string;
|
|
769
|
+
color: "domainRed";
|
|
770
|
+
};
|
|
771
|
+
svelte: {
|
|
772
|
+
nerd: string;
|
|
773
|
+
unicode: string;
|
|
774
|
+
ascii: string;
|
|
775
|
+
color: "domainAmber";
|
|
776
|
+
};
|
|
777
|
+
laravel: {
|
|
778
|
+
nerd: string;
|
|
779
|
+
unicode: string;
|
|
780
|
+
ascii: string;
|
|
781
|
+
color: "domainRed";
|
|
782
|
+
};
|
|
783
|
+
django: {
|
|
784
|
+
nerd: string;
|
|
785
|
+
unicode: string;
|
|
786
|
+
ascii: string;
|
|
787
|
+
color: "domainGreen";
|
|
788
|
+
};
|
|
789
|
+
rails: {
|
|
790
|
+
nerd: string;
|
|
791
|
+
unicode: string;
|
|
792
|
+
ascii: string;
|
|
793
|
+
color: "domainRed";
|
|
794
|
+
};
|
|
795
|
+
dotnet: {
|
|
796
|
+
nerd: string;
|
|
797
|
+
unicode: string;
|
|
798
|
+
ascii: string;
|
|
799
|
+
color: "domainViolet";
|
|
800
|
+
};
|
|
801
|
+
bootstrap: {
|
|
802
|
+
nerd: string;
|
|
803
|
+
unicode: string;
|
|
804
|
+
ascii: string;
|
|
805
|
+
color: "domainViolet";
|
|
806
|
+
};
|
|
807
|
+
jquery: {
|
|
808
|
+
nerd: string;
|
|
809
|
+
unicode: string;
|
|
810
|
+
ascii: string;
|
|
811
|
+
color: "domainBlue";
|
|
812
|
+
};
|
|
813
|
+
};
|
|
814
|
+
/** files — files & formats. */
|
|
815
|
+
declare const FILES: {
|
|
816
|
+
file: {
|
|
817
|
+
nerd: string;
|
|
818
|
+
unicode: string;
|
|
819
|
+
ascii: string;
|
|
820
|
+
color: "domainNeutral";
|
|
821
|
+
};
|
|
822
|
+
folder: {
|
|
823
|
+
nerd: string;
|
|
824
|
+
unicode: string;
|
|
825
|
+
ascii: string;
|
|
826
|
+
color: "domainBlue";
|
|
827
|
+
};
|
|
828
|
+
folder_open: {
|
|
829
|
+
nerd: string;
|
|
830
|
+
unicode: string;
|
|
831
|
+
ascii: string;
|
|
832
|
+
color: "domainBlue";
|
|
833
|
+
};
|
|
834
|
+
json: {
|
|
835
|
+
nerd: string;
|
|
836
|
+
unicode: string;
|
|
837
|
+
ascii: string;
|
|
838
|
+
color: "domainYellow";
|
|
839
|
+
};
|
|
840
|
+
yaml: {
|
|
841
|
+
nerd: string;
|
|
842
|
+
unicode: string;
|
|
843
|
+
ascii: string;
|
|
844
|
+
color: "domainAmber";
|
|
845
|
+
};
|
|
846
|
+
markdown: {
|
|
847
|
+
nerd: string;
|
|
848
|
+
unicode: string;
|
|
849
|
+
ascii: string;
|
|
850
|
+
color: "domainNeutral";
|
|
851
|
+
};
|
|
852
|
+
pdf: {
|
|
853
|
+
nerd: string;
|
|
854
|
+
unicode: string;
|
|
855
|
+
ascii: string;
|
|
856
|
+
color: "domainRed";
|
|
857
|
+
};
|
|
858
|
+
image: {
|
|
859
|
+
nerd: string;
|
|
860
|
+
unicode: string;
|
|
861
|
+
ascii: string;
|
|
862
|
+
color: "domainGreen";
|
|
863
|
+
};
|
|
864
|
+
archive: {
|
|
865
|
+
nerd: string;
|
|
866
|
+
unicode: string;
|
|
867
|
+
ascii: string;
|
|
868
|
+
color: "domainAmber";
|
|
869
|
+
};
|
|
870
|
+
lock: {
|
|
871
|
+
nerd: string;
|
|
872
|
+
unicode: string;
|
|
873
|
+
ascii: string;
|
|
874
|
+
color: "domainYellow";
|
|
875
|
+
};
|
|
876
|
+
};
|
|
877
|
+
/** social — social & messaging brands. */
|
|
878
|
+
declare const SOCIAL: {
|
|
879
|
+
slack: {
|
|
880
|
+
nerd: string;
|
|
881
|
+
unicode: string;
|
|
882
|
+
ascii: string;
|
|
883
|
+
color: "domainViolet";
|
|
884
|
+
};
|
|
885
|
+
discord: {
|
|
886
|
+
nerd: string;
|
|
887
|
+
unicode: string;
|
|
888
|
+
ascii: string;
|
|
889
|
+
color: "domainBlue";
|
|
890
|
+
};
|
|
891
|
+
telegram: {
|
|
892
|
+
nerd: string;
|
|
893
|
+
unicode: string;
|
|
894
|
+
ascii: string;
|
|
895
|
+
color: "domainCyan";
|
|
896
|
+
};
|
|
897
|
+
twitter: {
|
|
898
|
+
nerd: string;
|
|
899
|
+
unicode: string;
|
|
900
|
+
ascii: string;
|
|
901
|
+
color: "domainCyan";
|
|
902
|
+
};
|
|
903
|
+
youtube: {
|
|
904
|
+
nerd: string;
|
|
905
|
+
unicode: string;
|
|
906
|
+
ascii: string;
|
|
907
|
+
color: "domainRed";
|
|
908
|
+
};
|
|
909
|
+
linkedin: {
|
|
910
|
+
nerd: string;
|
|
911
|
+
unicode: string;
|
|
912
|
+
ascii: string;
|
|
913
|
+
color: "domainBlue";
|
|
914
|
+
};
|
|
915
|
+
reddit: {
|
|
916
|
+
nerd: string;
|
|
917
|
+
unicode: string;
|
|
918
|
+
ascii: string;
|
|
919
|
+
color: "domainAmber";
|
|
920
|
+
};
|
|
921
|
+
mastodon: {
|
|
922
|
+
nerd: string;
|
|
923
|
+
unicode: string;
|
|
924
|
+
ascii: string;
|
|
925
|
+
color: "domainViolet";
|
|
926
|
+
};
|
|
927
|
+
};
|
|
928
|
+
/** actions — generic UI action glyphs (not in the Tier 0 contract). */
|
|
929
|
+
declare const ACTIONS: {
|
|
930
|
+
search: {
|
|
931
|
+
nerd: string;
|
|
932
|
+
unicode: string;
|
|
933
|
+
ascii: string;
|
|
934
|
+
color: "domainNeutral";
|
|
935
|
+
};
|
|
936
|
+
filter: {
|
|
937
|
+
nerd: string;
|
|
938
|
+
unicode: string;
|
|
939
|
+
ascii: string;
|
|
940
|
+
color: "domainNeutral";
|
|
941
|
+
};
|
|
942
|
+
settings: {
|
|
943
|
+
nerd: string;
|
|
944
|
+
unicode: string;
|
|
945
|
+
ascii: string;
|
|
946
|
+
color: "domainNeutral";
|
|
947
|
+
};
|
|
948
|
+
trash: {
|
|
949
|
+
nerd: string;
|
|
950
|
+
unicode: string;
|
|
951
|
+
ascii: string;
|
|
952
|
+
color: "stateErr";
|
|
953
|
+
};
|
|
954
|
+
download: {
|
|
955
|
+
nerd: string;
|
|
956
|
+
unicode: string;
|
|
957
|
+
ascii: string;
|
|
958
|
+
color: "domainBlue";
|
|
959
|
+
};
|
|
960
|
+
upload: {
|
|
961
|
+
nerd: string;
|
|
962
|
+
unicode: string;
|
|
963
|
+
ascii: string;
|
|
964
|
+
color: "domainBlue";
|
|
965
|
+
};
|
|
966
|
+
refresh: {
|
|
967
|
+
nerd: string;
|
|
968
|
+
unicode: string;
|
|
969
|
+
ascii: string;
|
|
970
|
+
color: "stateInfo";
|
|
971
|
+
};
|
|
972
|
+
edit: {
|
|
973
|
+
nerd: string;
|
|
974
|
+
unicode: string;
|
|
975
|
+
ascii: string;
|
|
976
|
+
color: "domainNeutral";
|
|
977
|
+
};
|
|
978
|
+
bell: {
|
|
979
|
+
nerd: string;
|
|
980
|
+
unicode: string;
|
|
981
|
+
ascii: string;
|
|
982
|
+
color: "domainYellow";
|
|
983
|
+
};
|
|
984
|
+
link: {
|
|
985
|
+
nerd: string;
|
|
986
|
+
unicode: string;
|
|
987
|
+
ascii: string;
|
|
988
|
+
color: "link";
|
|
989
|
+
};
|
|
990
|
+
};
|
|
991
|
+
/**
|
|
992
|
+
* system — general-purpose system / OS / UI domain glyphs.
|
|
993
|
+
*
|
|
994
|
+
* The recurring "thing" glyphs that aren't a brand (→ {@link COMPANIES}), an
|
|
995
|
+
* action verb (→ {@link ACTIONS}), or a file format (→ {@link FILES}): a
|
|
996
|
+
* terminal, a globe, a home, a key, an envelope. Every `nerd` codepoint is a
|
|
997
|
+
* classic Font Awesome value; each carries a width-1 `unicode` fallback
|
|
998
|
+
* (verified via `string-width`) so it never tofus on a non–Nerd-Font terminal.
|
|
999
|
+
*/
|
|
1000
|
+
declare const SYSTEM: {
|
|
1001
|
+
terminal: {
|
|
1002
|
+
nerd: string;
|
|
1003
|
+
unicode: string;
|
|
1004
|
+
ascii: string;
|
|
1005
|
+
color: "domainGreen";
|
|
1006
|
+
};
|
|
1007
|
+
code: {
|
|
1008
|
+
nerd: string;
|
|
1009
|
+
unicode: string;
|
|
1010
|
+
ascii: string;
|
|
1011
|
+
color: "domainBlue";
|
|
1012
|
+
};
|
|
1013
|
+
globe: {
|
|
1014
|
+
nerd: string;
|
|
1015
|
+
unicode: string;
|
|
1016
|
+
ascii: string;
|
|
1017
|
+
color: "domainCyan";
|
|
1018
|
+
};
|
|
1019
|
+
home: {
|
|
1020
|
+
nerd: string;
|
|
1021
|
+
unicode: string;
|
|
1022
|
+
ascii: string;
|
|
1023
|
+
color: "domainNeutral";
|
|
1024
|
+
};
|
|
1025
|
+
key: {
|
|
1026
|
+
nerd: string;
|
|
1027
|
+
unicode: string;
|
|
1028
|
+
ascii: string;
|
|
1029
|
+
color: "domainYellow";
|
|
1030
|
+
};
|
|
1031
|
+
mail: {
|
|
1032
|
+
nerd: string;
|
|
1033
|
+
unicode: string;
|
|
1034
|
+
ascii: string;
|
|
1035
|
+
color: "domainBlue";
|
|
1036
|
+
};
|
|
1037
|
+
phone: {
|
|
1038
|
+
nerd: string;
|
|
1039
|
+
unicode: string;
|
|
1040
|
+
ascii: string;
|
|
1041
|
+
color: "domainGreen";
|
|
1042
|
+
};
|
|
1043
|
+
package: {
|
|
1044
|
+
nerd: string;
|
|
1045
|
+
unicode: string;
|
|
1046
|
+
ascii: string;
|
|
1047
|
+
color: "domainAmber";
|
|
1048
|
+
};
|
|
1049
|
+
sync: {
|
|
1050
|
+
nerd: string;
|
|
1051
|
+
unicode: string;
|
|
1052
|
+
ascii: string;
|
|
1053
|
+
color: "domainCyan";
|
|
1054
|
+
};
|
|
1055
|
+
text: {
|
|
1056
|
+
nerd: string;
|
|
1057
|
+
unicode: string;
|
|
1058
|
+
ascii: string;
|
|
1059
|
+
color: "domainNeutral";
|
|
1060
|
+
};
|
|
1061
|
+
tools: {
|
|
1062
|
+
nerd: string;
|
|
1063
|
+
unicode: string;
|
|
1064
|
+
ascii: string;
|
|
1065
|
+
color: "domainNeutral";
|
|
1066
|
+
};
|
|
1067
|
+
};
|
|
1068
|
+
/** packages — package managers. */
|
|
1069
|
+
declare const PACKAGES: {
|
|
1070
|
+
npm: {
|
|
1071
|
+
nerd: string;
|
|
1072
|
+
unicode: string;
|
|
1073
|
+
ascii: string;
|
|
1074
|
+
color: "domainRed";
|
|
1075
|
+
};
|
|
1076
|
+
yarn: {
|
|
1077
|
+
nerd: string;
|
|
1078
|
+
unicode: string;
|
|
1079
|
+
ascii: string;
|
|
1080
|
+
color: "domainBlue";
|
|
1081
|
+
};
|
|
1082
|
+
cargo: {
|
|
1083
|
+
nerd: string;
|
|
1084
|
+
unicode: string;
|
|
1085
|
+
ascii: string;
|
|
1086
|
+
color: "domainAmber";
|
|
1087
|
+
};
|
|
1088
|
+
pip: {
|
|
1089
|
+
nerd: string;
|
|
1090
|
+
unicode: string;
|
|
1091
|
+
ascii: string;
|
|
1092
|
+
color: "domainYellow";
|
|
1093
|
+
};
|
|
1094
|
+
composer: {
|
|
1095
|
+
nerd: string;
|
|
1096
|
+
unicode: string;
|
|
1097
|
+
ascii: string;
|
|
1098
|
+
color: "domainYellow";
|
|
1099
|
+
};
|
|
1100
|
+
homebrew: {
|
|
1101
|
+
nerd: string;
|
|
1102
|
+
unicode: string;
|
|
1103
|
+
ascii: string;
|
|
1104
|
+
color: "domainYellow";
|
|
1105
|
+
};
|
|
1106
|
+
apt: {
|
|
1107
|
+
nerd: string;
|
|
1108
|
+
unicode: string;
|
|
1109
|
+
ascii: string;
|
|
1110
|
+
color: "domainRed";
|
|
1111
|
+
};
|
|
1112
|
+
};
|
|
1113
|
+
/**
|
|
1114
|
+
* devinfra — recurring local dev-infrastructure tools. These show up often
|
|
1115
|
+
* enough as raw `nf()` registrations in apps to earn CURATED entries (the bar
|
|
1116
|
+
* for Tier 2). `laravel` is promoted from {@link FRAMEWORKS} and `valet` aliases
|
|
1117
|
+
* its glyph; `code-server` aliases the vscode family from {@link EDITORS}. The
|
|
1118
|
+
* genuine newcomers — tailscale / syncthing / mosh — carry safe `{ unicode,
|
|
1119
|
+
* ascii }` fallbacks so they never tofu without a Nerd Font; their `nerd`
|
|
1120
|
+
* codepoint is left empty (a curator must verify it) and the glyph degrades to
|
|
1121
|
+
* its unicode form until then.
|
|
1122
|
+
*
|
|
1123
|
+
* PRIME DIRECTIVE still holds: this pack is OPT-IN content, registered by no app
|
|
1124
|
+
* automatically. A one-app-only glyph stays in that app — these are here because
|
|
1125
|
+
* they recur, not to make every app carry them.
|
|
1126
|
+
*
|
|
1127
|
+
* Two single-cell divergences from the design system, for the same reason as
|
|
1128
|
+
* {@link COMMON_DOMAINS} `bolt` (see also `glyphs/glyphs.ts`): the DS unicode
|
|
1129
|
+
* fallbacks `⚡` (ngrok) and `✉` (mailpit) are emoji-presentation and
|
|
1130
|
+
* double-wide — they break the one-glyph-one-cell grid in unicode mode. blink
|
|
1131
|
+
* substitutes the width-1 `↯` and `⊠`; `string-width` is the arbiter.
|
|
1132
|
+
*/
|
|
1133
|
+
declare const DEVINFRA: {
|
|
1134
|
+
laravel: {
|
|
1135
|
+
nerd: string;
|
|
1136
|
+
unicode: string;
|
|
1137
|
+
ascii: string;
|
|
1138
|
+
color: "domainRed";
|
|
1139
|
+
};
|
|
1140
|
+
valet: {
|
|
1141
|
+
nerd: string;
|
|
1142
|
+
unicode: string;
|
|
1143
|
+
ascii: string;
|
|
1144
|
+
color: "domainRed";
|
|
1145
|
+
};
|
|
1146
|
+
'code-server': {
|
|
1147
|
+
nerd: string;
|
|
1148
|
+
unicode: string;
|
|
1149
|
+
ascii: string;
|
|
1150
|
+
color: "domainBlue";
|
|
1151
|
+
};
|
|
1152
|
+
ngrok: {
|
|
1153
|
+
nerd: string;
|
|
1154
|
+
unicode: string;
|
|
1155
|
+
ascii: string;
|
|
1156
|
+
color: "domainAmber";
|
|
1157
|
+
};
|
|
1158
|
+
mailpit: {
|
|
1159
|
+
nerd: string;
|
|
1160
|
+
unicode: string;
|
|
1161
|
+
ascii: string;
|
|
1162
|
+
color: "domainAmber";
|
|
1163
|
+
};
|
|
1164
|
+
tailscale: {
|
|
1165
|
+
nerd: string;
|
|
1166
|
+
unicode: string;
|
|
1167
|
+
ascii: string;
|
|
1168
|
+
color: "domainCyan";
|
|
1169
|
+
};
|
|
1170
|
+
syncthing: {
|
|
1171
|
+
nerd: string;
|
|
1172
|
+
unicode: string;
|
|
1173
|
+
ascii: string;
|
|
1174
|
+
color: "domainViolet";
|
|
1175
|
+
};
|
|
1176
|
+
mosh: {
|
|
1177
|
+
nerd: string;
|
|
1178
|
+
unicode: string;
|
|
1179
|
+
ascii: string;
|
|
1180
|
+
color: "domainAmber";
|
|
1181
|
+
};
|
|
1182
|
+
};
|
|
1183
|
+
/** Tier-2 pack directory — used by pickers / docs to label & iterate packs. */
|
|
1184
|
+
declare const GLYPH_PACKS: Record<string, Record<string, GlyphVariants>>;
|
|
1185
|
+
|
|
1186
|
+
/**
|
|
1187
|
+
* Tier 3 — the raw Nerd Font index (the escape hatch).
|
|
1188
|
+
*
|
|
1189
|
+
* A flat map of canonical Nerd Font names → codepoint (hex string), following
|
|
1190
|
+
* the upstream `glyphnames.json` convention (source-prefixed: `fa-`, `dev-`,
|
|
1191
|
+
* `seti-`, `pl-`, …). It carries **no curation**: no unicode/ascii fallback, no
|
|
1192
|
+
* semantic colour. `nf(name)` returns the Nerd Font char only; on a non–Nerd-Font
|
|
1193
|
+
* terminal it has nothing to degrade to. Use it as a deliberate escape hatch —
|
|
1194
|
+
*
|
|
1195
|
+
* ```ts
|
|
1196
|
+
* nf('fa-rocket'); // → the glyph, or '' if unknown
|
|
1197
|
+
* registerGlyphs({ deploy: { nf: 'fa-rocket' } }); // muted, ascii derived
|
|
1198
|
+
* ```
|
|
1199
|
+
*
|
|
1200
|
+
* — and for anything an app shows often, promote it to a curated Tier 1/2 entry
|
|
1201
|
+
* (give it a colour + fallbacks) instead of leaning on the raw index.
|
|
1202
|
+
*
|
|
1203
|
+
* Shipped here is a **verified seed** (~130 of the most-used glyphs). The full
|
|
1204
|
+
* production index (~10k) is generated from upstream `glyphnames.json`; merge it
|
|
1205
|
+
* at boot with {@link registerNerdIndex} so the bundle stays lean (the seed
|
|
1206
|
+
* always wins — nothing curated is clobbered).
|
|
1207
|
+
*/
|
|
1208
|
+
/** name → hex codepoint. Mutable so a generated full index can be merged in. */
|
|
1209
|
+
declare const NERD_INDEX: Record<string, string>;
|
|
1210
|
+
/** Source prefixes in the seed → human label (for a glyph picker's filter). */
|
|
1211
|
+
declare const NERD_INDEX_SOURCES: Record<string, string>;
|
|
1212
|
+
/**
|
|
1213
|
+
* Merge a generated full index into {@link NERD_INDEX}. The seed always wins, so
|
|
1214
|
+
* curated entries are never clobbered; keys starting with `_` (e.g. `_meta`) are
|
|
1215
|
+
* skipped. This is the Node-side analogue of the web engine's lazy fetch — the
|
|
1216
|
+
* app loads the generated `glyph-index.json` (~10k) and hands it over once at boot.
|
|
1217
|
+
*/
|
|
1218
|
+
declare function registerNerdIndex(full: Record<string, string>): void;
|
|
1219
|
+
/**
|
|
1220
|
+
* Resolve a raw codepoint to a char — by index name (`'dev-laravel'`) or by a
|
|
1221
|
+
* bare hex codepoint (`'e73f'`). Returns `''` for an unknown name (never throws,
|
|
1222
|
+
* never tofu-by-surprise on lookup).
|
|
1223
|
+
*/
|
|
1224
|
+
declare function nfChar(nameOrHex: string | null | undefined): string;
|
|
1225
|
+
/** `nf('fa-rocket')` → the Nerd Font char (nerd-only, no fallback), or `''`. */
|
|
1226
|
+
declare function nf(name: string): string;
|
|
1227
|
+
/** True if a raw index name is known. */
|
|
1228
|
+
declare function nfHas(name: string): boolean;
|
|
1229
|
+
|
|
1230
|
+
/**
|
|
1231
|
+
* The blink glyph ENGINE — the Tier 0 contract (state · nav · box · spinner ·
|
|
1232
|
+
* blocks) plus the extensible registry that powers Tiers 1–3.
|
|
1233
|
+
*
|
|
1234
|
+
* ─ Fonts vs. registry ─ The *drawing* of every Nerd Font glyph already lives in
|
|
1235
|
+
* the user's terminal **font** (CaskaydiaMono ships ~10k icons in the Private
|
|
1236
|
+
* Use Area); printing a codepoint is enough. What blink owns is the **registry**:
|
|
1237
|
+
* the map from a semantic NAME to a glyph plus its curated `{ unicode, ascii }`
|
|
1238
|
+
* fallbacks and its semantic `color` (a {@link SemanticTokens} key). Those three
|
|
1239
|
+
* are curation — they can't be auto-derived for 10k icons — which is why blink
|
|
1240
|
+
* *degrades* a glyph (nerd → unicode → ascii) rather than rendering tofu.
|
|
1241
|
+
*
|
|
1242
|
+
* ─ Contract vs. content ─ Tier 0 (this file) is the contract: it appears in
|
|
1243
|
+
* every blink app and never changes, so it's the only thing seeded into the
|
|
1244
|
+
* registry at import. Domain glyphs (Tiers 1–3) are CONTENT: the app opts in via
|
|
1245
|
+
* {@link registerGlyphs} (`packs.ts`) or the raw index (`nerdIndex.ts`, `nf()`).
|
|
1246
|
+
* blink core ships **zero** domain glyphs registered.
|
|
1247
|
+
*/
|
|
1248
|
+
declare const stateGlyphs: {
|
|
1249
|
+
check: {
|
|
1250
|
+
nerd: string;
|
|
1251
|
+
unicode: string;
|
|
1252
|
+
ascii: string;
|
|
1253
|
+
};
|
|
1254
|
+
cross: {
|
|
1255
|
+
nerd: string;
|
|
1256
|
+
unicode: string;
|
|
1257
|
+
ascii: string;
|
|
1258
|
+
};
|
|
1259
|
+
circle: {
|
|
1260
|
+
nerd: string;
|
|
1261
|
+
unicode: string;
|
|
1262
|
+
ascii: string;
|
|
1263
|
+
};
|
|
1264
|
+
half: {
|
|
1265
|
+
nerd: string;
|
|
1266
|
+
unicode: string;
|
|
1267
|
+
ascii: string;
|
|
1268
|
+
};
|
|
1269
|
+
checkboxOn: {
|
|
1270
|
+
nerd: string;
|
|
1271
|
+
unicode: string;
|
|
1272
|
+
ascii: string;
|
|
1273
|
+
};
|
|
1274
|
+
checkboxOff: {
|
|
1275
|
+
nerd: string;
|
|
1276
|
+
unicode: string;
|
|
1277
|
+
ascii: string;
|
|
1278
|
+
};
|
|
1279
|
+
checkboxLock: {
|
|
1280
|
+
nerd: string;
|
|
1281
|
+
unicode: string;
|
|
1282
|
+
ascii: string;
|
|
1283
|
+
};
|
|
1284
|
+
warn: {
|
|
1285
|
+
nerd: string;
|
|
1286
|
+
unicode: string;
|
|
1287
|
+
ascii: string;
|
|
1288
|
+
};
|
|
1289
|
+
rerun: {
|
|
1290
|
+
nerd: string;
|
|
1291
|
+
unicode: string;
|
|
1292
|
+
ascii: string;
|
|
1293
|
+
};
|
|
1294
|
+
};
|
|
1295
|
+
/** Navigation glyphs — focus carets, expand chevrons, relation arrows. */
|
|
1296
|
+
declare const navGlyphs: {
|
|
1297
|
+
focus: {
|
|
1298
|
+
nerd: string;
|
|
1299
|
+
unicode: string;
|
|
1300
|
+
ascii: string;
|
|
1301
|
+
};
|
|
1302
|
+
collapsed: {
|
|
1303
|
+
nerd: string;
|
|
1304
|
+
unicode: string;
|
|
1305
|
+
ascii: string;
|
|
1306
|
+
};
|
|
1307
|
+
expanded: {
|
|
1308
|
+
nerd: string;
|
|
1309
|
+
unicode: string;
|
|
1310
|
+
ascii: string;
|
|
1311
|
+
};
|
|
1312
|
+
depends: {
|
|
1313
|
+
nerd: string;
|
|
1314
|
+
unicode: string;
|
|
1315
|
+
ascii: string;
|
|
1316
|
+
};
|
|
1317
|
+
flow: {
|
|
1318
|
+
nerd: string;
|
|
1319
|
+
unicode: string;
|
|
1320
|
+
ascii: string;
|
|
1321
|
+
};
|
|
1322
|
+
back: {
|
|
1323
|
+
nerd: string;
|
|
1324
|
+
unicode: string;
|
|
1325
|
+
ascii: string;
|
|
1326
|
+
};
|
|
1327
|
+
moreAbove: {
|
|
1328
|
+
nerd: string;
|
|
1329
|
+
unicode: string;
|
|
1330
|
+
ascii: string;
|
|
1331
|
+
};
|
|
1332
|
+
moreBelow: {
|
|
1333
|
+
nerd: string;
|
|
1334
|
+
unicode: string;
|
|
1335
|
+
ascii: string;
|
|
1336
|
+
};
|
|
1337
|
+
};
|
|
1338
|
+
/** Built-in glyph names — the contract set, before any app registrations. */
|
|
1339
|
+
type BuiltinGlyphName = keyof typeof stateGlyphs | keyof typeof navGlyphs;
|
|
1340
|
+
/** A raw glyph with no curated colour renders muted — neutral, never semantic. */
|
|
1341
|
+
declare const DEFAULT_GLYPH_COLOR: GlyphColor;
|
|
1342
|
+
/** A registered glyph with no curated unicode fallback degrades to this mark. */
|
|
1343
|
+
declare const DEFAULT_UNICODE = "\u25C6";
|
|
1344
|
+
/** `deriveAscii('postgresql')` → `'[po]'` — used when an entry omits an ascii fallback. */
|
|
1345
|
+
declare function deriveAscii(name: string): string;
|
|
1346
|
+
/**
|
|
1347
|
+
* What {@link registerGlyphs} accepts per entry — three shapes, all coexisting:
|
|
1348
|
+
* - verbose : `{ nerd:'', unicode:'◆', ascii:'[la]', color:'domainRed' }`
|
|
1349
|
+
* - easy : `{ nf:'dev-laravel', color:'domainRed' }` ← codepoint from the index
|
|
1350
|
+
* - raw cp : `{ cp:'e73f', color:'domainRed' }` ← codepoint from hex
|
|
1351
|
+
* - string : `''` ← nerd only, all defaults
|
|
1352
|
+
* Anything omitted is filled in: `unicode → ◆`, `ascii → derived`, `color → muted`.
|
|
1353
|
+
*/
|
|
1354
|
+
type GlyphInput = string | (Partial<GlyphVariants> & {
|
|
1355
|
+
nf?: string;
|
|
1356
|
+
cp?: string;
|
|
1357
|
+
});
|
|
1358
|
+
/**
|
|
1359
|
+
* Register app-domain glyphs (or override built-ins). Call once at startup,
|
|
1360
|
+
* before the first render. Accepts one or more maps — later wins — so an app can
|
|
1361
|
+
* take a pack then override the few entries it cares about. Each entry's `color`
|
|
1362
|
+
* is owned here, at REGISTRATION, never per render site, so a row says "this is a
|
|
1363
|
+
* postgres row", not "paint this blue":
|
|
1364
|
+
*
|
|
1365
|
+
* ```ts
|
|
1366
|
+
* registerGlyphs(COMMON_DOMAINS); // Tier 1 pack
|
|
1367
|
+
* registerGlyphs(LANGUAGES, DATABASES); // many Tier 2 packs
|
|
1368
|
+
* registerGlyphs({ deploy: { nf: 'fa-rocket' } }); // easy form, from the index
|
|
1369
|
+
* registerGlyphs({ database: { color: 'domainCyan' } }); // override one
|
|
1370
|
+
* ```
|
|
1371
|
+
*/
|
|
1372
|
+
declare function registerGlyphs(...maps: Array<Record<string, GlyphInput> | undefined>): void;
|
|
1373
|
+
/** Single-entry convenience for {@link registerGlyphs}. */
|
|
1374
|
+
declare function registerGlyph(name: string, entry: GlyphInput): void;
|
|
1375
|
+
/** True if a glyph name is registered. */
|
|
1376
|
+
declare function hasGlyph(name: string): boolean;
|
|
1377
|
+
/** Every registered glyph name, sorted — for pickers & tests. */
|
|
1378
|
+
declare function registeredNames(): string[];
|
|
1379
|
+
/**
|
|
1380
|
+
* Resolve a glyph name to a string for the given icon set, with the
|
|
1381
|
+
* `nerd → unicode → ascii` fallback. Unknown names return the name itself (so a
|
|
1382
|
+
* typo or an unregistered domain renders visibly as text rather than tofu).
|
|
1383
|
+
* Prefer the {@link useGlyph} hook inside components — it reads the icon set from
|
|
1384
|
+
* context.
|
|
1385
|
+
*/
|
|
1386
|
+
declare function glyph(name: string, set: IconSet): string;
|
|
1387
|
+
/**
|
|
1388
|
+
* The colour a registered glyph renders in — a {@link SemanticTokens} key, owned
|
|
1389
|
+
* at registration. Resolve it through the active theme: `tokens[glyphColor(name)
|
|
1390
|
+
* ?? 'fgMuted']`. Returns `undefined` for an unregistered name or a contract
|
|
1391
|
+
* glyph (whose colour comes from the state-intent map, not the entry).
|
|
1392
|
+
*/
|
|
1393
|
+
declare function glyphColor(name: string): GlyphColor | undefined;
|
|
1394
|
+
/** A resolved intent: a glyph NAME (resolve via {@link glyph}) + a colour token key. */
|
|
1395
|
+
interface StateIntent {
|
|
1396
|
+
/** Registry glyph name — resolve with `useGlyph()` / `glyph(name, set)`. */
|
|
1397
|
+
glyph: string;
|
|
1398
|
+
/** Semantic token key for the colour — read `tokens[token]`. */
|
|
1399
|
+
token: keyof SemanticTokens;
|
|
1400
|
+
}
|
|
1401
|
+
/** Row / detail status intents → glyph + colour. The house mapping, central. */
|
|
1402
|
+
declare const stateIntents: {
|
|
1403
|
+
installed: {
|
|
1404
|
+
glyph: string;
|
|
1405
|
+
token: "stateOk";
|
|
1406
|
+
};
|
|
1407
|
+
ok: {
|
|
1408
|
+
glyph: string;
|
|
1409
|
+
token: "stateOk";
|
|
1410
|
+
};
|
|
1411
|
+
done: {
|
|
1412
|
+
glyph: string;
|
|
1413
|
+
token: "stateOk";
|
|
1414
|
+
};
|
|
1415
|
+
missing: {
|
|
1416
|
+
glyph: string;
|
|
1417
|
+
token: "stateErr";
|
|
1418
|
+
};
|
|
1419
|
+
error: {
|
|
1420
|
+
glyph: string;
|
|
1421
|
+
token: "stateErr";
|
|
1422
|
+
};
|
|
1423
|
+
err: {
|
|
1424
|
+
glyph: string;
|
|
1425
|
+
token: "stateErr";
|
|
1426
|
+
};
|
|
1427
|
+
failed: {
|
|
1428
|
+
glyph: string;
|
|
1429
|
+
token: "stateErr";
|
|
1430
|
+
};
|
|
1431
|
+
drift: {
|
|
1432
|
+
glyph: string;
|
|
1433
|
+
token: "stateDrift";
|
|
1434
|
+
};
|
|
1435
|
+
partial: {
|
|
1436
|
+
glyph: string;
|
|
1437
|
+
token: "stateDrift";
|
|
1438
|
+
};
|
|
1439
|
+
warn: {
|
|
1440
|
+
glyph: string;
|
|
1441
|
+
token: "stateWarn";
|
|
1442
|
+
};
|
|
1443
|
+
idempotent: {
|
|
1444
|
+
glyph: string;
|
|
1445
|
+
token: "stateInfo";
|
|
1446
|
+
};
|
|
1447
|
+
pending: {
|
|
1448
|
+
glyph: string;
|
|
1449
|
+
token: "statePending";
|
|
1450
|
+
};
|
|
1451
|
+
info: {
|
|
1452
|
+
glyph: string;
|
|
1453
|
+
token: "stateInfo";
|
|
1454
|
+
};
|
|
1455
|
+
};
|
|
1456
|
+
/** A semantic state name the framework knows how to draw. */
|
|
1457
|
+
type StateName = keyof typeof stateIntents;
|
|
1458
|
+
/** Resolve a state intent name to its glyph + colour token, or `null` if unknown. */
|
|
1459
|
+
declare function stateGlyph(name: string): StateIntent | null;
|
|
1460
|
+
/** Selection intents → checkbox glyph + colour. `locked` is selected + non-toggle. */
|
|
1461
|
+
declare const selectionIntents: {
|
|
1462
|
+
selected: {
|
|
1463
|
+
glyph: string;
|
|
1464
|
+
token: "accent";
|
|
1465
|
+
};
|
|
1466
|
+
unselected: {
|
|
1467
|
+
glyph: string;
|
|
1468
|
+
token: "fgDim";
|
|
1469
|
+
};
|
|
1470
|
+
locked: {
|
|
1471
|
+
glyph: string;
|
|
1472
|
+
token: "fgMuted";
|
|
1473
|
+
};
|
|
1474
|
+
};
|
|
1475
|
+
/** A selection intent name. */
|
|
1476
|
+
type SelectionName = keyof typeof selectionIntents;
|
|
1477
|
+
/** A complete set of box-drawing characters for one border style. */
|
|
1478
|
+
interface BoxChars {
|
|
1479
|
+
tl: string;
|
|
1480
|
+
tr: string;
|
|
1481
|
+
bl: string;
|
|
1482
|
+
br: string;
|
|
1483
|
+
h: string;
|
|
1484
|
+
v: string;
|
|
1485
|
+
}
|
|
1486
|
+
/**
|
|
1487
|
+
* blink's border styles. The house style is **single-line, rounded** corners;
|
|
1488
|
+
* `single` (square) is a legacy opt-out. There is **no double-line border** — it
|
|
1489
|
+
* reads dated, and focus / modals are signalled by colour, not a heavier line.
|
|
1490
|
+
* `ascii` is the fontless fallback (`+ - |`).
|
|
1491
|
+
*/
|
|
1492
|
+
declare const boxStyles: {
|
|
1493
|
+
single: {
|
|
1494
|
+
tl: string;
|
|
1495
|
+
tr: string;
|
|
1496
|
+
bl: string;
|
|
1497
|
+
br: string;
|
|
1498
|
+
h: string;
|
|
1499
|
+
v: string;
|
|
1500
|
+
};
|
|
1501
|
+
rounded: {
|
|
1502
|
+
tl: string;
|
|
1503
|
+
tr: string;
|
|
1504
|
+
bl: string;
|
|
1505
|
+
br: string;
|
|
1506
|
+
h: string;
|
|
1507
|
+
v: string;
|
|
1508
|
+
};
|
|
1509
|
+
ascii: {
|
|
1510
|
+
tl: string;
|
|
1511
|
+
tr: string;
|
|
1512
|
+
bl: string;
|
|
1513
|
+
br: string;
|
|
1514
|
+
h: string;
|
|
1515
|
+
v: string;
|
|
1516
|
+
};
|
|
1517
|
+
};
|
|
1518
|
+
type BoxStyleName = 'single' | 'rounded';
|
|
1519
|
+
/**
|
|
1520
|
+
* Pick the right box characters for a style + icon set. In `ascii` mode every
|
|
1521
|
+
* style collapses to the ASCII set (`+ - |`); rounded corners aren't ASCII-safe.
|
|
1522
|
+
*/
|
|
1523
|
+
declare function boxChars(style: BoxStyleName, set: IconSet): BoxChars;
|
|
1524
|
+
/** Spinner frames per icon set. Braille for nerd/unicode, classic for ascii. */
|
|
1525
|
+
declare const spinnerFrames: {
|
|
1526
|
+
readonly braille: readonly ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
1527
|
+
readonly ascii: readonly ["|", "/", "-", "\\"];
|
|
1528
|
+
};
|
|
1529
|
+
/** Frames for the given icon set. */
|
|
1530
|
+
declare function spinnerFor(set: IconSet): readonly string[];
|
|
1531
|
+
/** Block-shade ramp — the only "gradient" the contract allows. */
|
|
1532
|
+
declare const blocks: {
|
|
1533
|
+
readonly full: "█";
|
|
1534
|
+
readonly dark: "▓";
|
|
1535
|
+
readonly medium: "▒";
|
|
1536
|
+
readonly light: "░";
|
|
1537
|
+
readonly cursor: "▎";
|
|
1538
|
+
};
|
|
1539
|
+
/**
|
|
1540
|
+
* Horizontal eighth-block ramp — the sub-cell material for a determinate
|
|
1541
|
+
* {@link ProgressBar}. Indexed by eighths filled (`0` = empty, `8` = a full
|
|
1542
|
+
* cell `█`). Left-to-right partials, the same family as {@link blocks}.
|
|
1543
|
+
*/
|
|
1544
|
+
declare const blocksH: readonly [" ", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█"];
|
|
1545
|
+
|
|
1546
|
+
/**
|
|
1547
|
+
* Returns a resolver bound to the icon set in context, so components don't have
|
|
1548
|
+
* to thread `iconSet` everywhere:
|
|
1549
|
+
*
|
|
1550
|
+
* ```tsx
|
|
1551
|
+
* const g = useGlyph();
|
|
1552
|
+
* <Text color={tokens.stateOk}>{g('check')}</Text>
|
|
1553
|
+
* ```
|
|
1554
|
+
*/
|
|
1555
|
+
declare function useGlyph(): (name: string) => string;
|
|
1556
|
+
|
|
1557
|
+
interface DetectOptions {
|
|
1558
|
+
/**
|
|
1559
|
+
* Path to a JSON file holding `{ "iconSet": "nerd" | "unicode" | "ascii" }`.
|
|
1560
|
+
* Defaults to `~/.config/blink/preferences.json`. This is the per-user
|
|
1561
|
+
* answer saved by a first-run probe (apps own the probe UI).
|
|
1562
|
+
*/
|
|
1563
|
+
configPath?: string;
|
|
1564
|
+
/**
|
|
1565
|
+
* Absolute paths to "a Nerd Font is installed" marker files. If any exists,
|
|
1566
|
+
* detection resolves to `nerd`. Apps pass their own markers (e.g. a font
|
|
1567
|
+
* bundle's install receipt) — blink ships none, staying agnostic.
|
|
1568
|
+
*/
|
|
1569
|
+
markerFiles?: string[];
|
|
1570
|
+
/** Override `process.env` (testing). */
|
|
1571
|
+
env?: NodeJS.ProcessEnv;
|
|
1572
|
+
}
|
|
1573
|
+
/**
|
|
1574
|
+
* Resolve which {@link IconSet} to render with, once, at app startup.
|
|
1575
|
+
*
|
|
1576
|
+
* The cascade (first match wins):
|
|
1577
|
+
* 1. hard env override — `BLINK_ICON_SET`, or `BLINK_NERD_FONT=1|0`, `BLINK_ASCII=1`
|
|
1578
|
+
* 2. saved per-user preference — `configPath`
|
|
1579
|
+
* 3. app-state marker — a font bundle's install receipt (`markerFiles`)
|
|
1580
|
+
* 4. terminal hints — `WT_SESSION`, `ITERM_PROFILE`, `TERM_PROGRAM`, …
|
|
1581
|
+
* 5. CI / dumb terminals → `ascii`
|
|
1582
|
+
* 6. safe default → `unicode`
|
|
1583
|
+
*
|
|
1584
|
+
* Never throws and never blocks: a blink app always gets a usable icon set.
|
|
1585
|
+
*/
|
|
1586
|
+
declare function detectIconSet(opts?: DetectOptions): Promise<IconSet>;
|
|
1587
|
+
|
|
1588
|
+
/**
|
|
1589
|
+
* Terminal cell width of a string, used to pin {@link List} columns and to drop
|
|
1590
|
+
* {@link Footer} chips that don't fit — so the character grid stays aligned (the
|
|
1591
|
+
* contract's "strict character-cell grid").
|
|
1592
|
+
*
|
|
1593
|
+
* Delegates to the same `string-width` Ink uses for its own layout, so the
|
|
1594
|
+
* widths computed here always agree with how Ink sizes its boxes — including
|
|
1595
|
+
* the wide glyphs in the palette (`⚠`, `☑` measure two cells) and the
|
|
1596
|
+
* multi-char `ascii` fallbacks (`[x]` measures three). Hand-rolling this drifts
|
|
1597
|
+
* from Ink and makes fixed-width cells truncate or misalign.
|
|
1598
|
+
*
|
|
1599
|
+
* Caveat: a terminal configured with "ambiguous = wide" (common in CJK locales)
|
|
1600
|
+
* may render some glyphs wider than string-width reports; that's a terminal
|
|
1601
|
+
* setting no layout-time measurement can see.
|
|
1602
|
+
*/
|
|
1603
|
+
declare function cellWidth(str: string): number;
|
|
1604
|
+
|
|
1605
|
+
interface Dimensions {
|
|
1606
|
+
columns: number;
|
|
1607
|
+
rows: number;
|
|
1608
|
+
}
|
|
1609
|
+
/**
|
|
1610
|
+
* The live terminal size in character cells, updating on `SIGWINCH` (resize).
|
|
1611
|
+
*
|
|
1612
|
+
* blink's design target is 100×30; the mobile-mosh fallback is 60×20. Layouts
|
|
1613
|
+
* read this to switch between the full multi-pane view and a stacked one.
|
|
1614
|
+
* Falls back to 80×24 when the stream isn't a TTY (pipes, CI).
|
|
1615
|
+
*
|
|
1616
|
+
* Tip: size your root box to `rows - 1`, not `rows`. An output exactly as tall
|
|
1617
|
+
* as the terminal makes Ink redraw with a full-screen clear every render
|
|
1618
|
+
* (visible flicker on each keypress); one spare line keeps it on the smooth
|
|
1619
|
+
* incremental path.
|
|
1620
|
+
*/
|
|
1621
|
+
declare function useStdoutDimensions(): Dimensions;
|
|
1622
|
+
|
|
1623
|
+
/**
|
|
1624
|
+
* The cursor blink — blink's one sanctioned text animation.
|
|
1625
|
+
*
|
|
1626
|
+
* Returns a boolean that toggles at `hz` (default 1 Hz) with `step-end` timing:
|
|
1627
|
+
* fully on for half the period, fully off for the other half. No fade, by
|
|
1628
|
+
* contract. Pass `active=false` to hold it visible (e.g. an unfocused input).
|
|
1629
|
+
*
|
|
1630
|
+
* ```tsx
|
|
1631
|
+
* const on = useBlink();
|
|
1632
|
+
* <Text>{value}{on ? '▎' : ' '}</Text>
|
|
1633
|
+
* ```
|
|
1634
|
+
*/
|
|
1635
|
+
declare function useBlink(active?: boolean, hz?: number): boolean;
|
|
1636
|
+
|
|
1637
|
+
interface SpinnerOptions {
|
|
1638
|
+
/** Advance only while true. Defaults to true. */
|
|
1639
|
+
active?: boolean;
|
|
1640
|
+
/** Frame interval in ms. Defaults to 80 (the contract's spinner cadence). */
|
|
1641
|
+
intervalMs?: number;
|
|
1642
|
+
}
|
|
1643
|
+
/**
|
|
1644
|
+
* A monotonically-increasing frame counter for spinners, advancing every
|
|
1645
|
+
* `intervalMs` (default 80 ms) while `active`. The caller picks the glyph:
|
|
1646
|
+
*
|
|
1647
|
+
* ```tsx
|
|
1648
|
+
* const frame = useSpinnerFrame({ active: syncing });
|
|
1649
|
+
* const frames = spinnerFor(iconSet);
|
|
1650
|
+
* <Text color={tokens.stateInfo}>{frames[frame % frames.length]}</Text>
|
|
1651
|
+
* ```
|
|
1652
|
+
*
|
|
1653
|
+
* Returns a counter rather than a glyph so it's icon-set agnostic and trivially
|
|
1654
|
+
* testable. Resets to 0 whenever it goes inactive.
|
|
1655
|
+
*/
|
|
1656
|
+
declare function useSpinnerFrame(opts?: SpinnerOptions): number;
|
|
1657
|
+
|
|
1658
|
+
interface UseListWindowOptions {
|
|
1659
|
+
/** Total number of rows. */
|
|
1660
|
+
rowCount: number;
|
|
1661
|
+
/** Index of the focused row; `null` / `< 0` = no focus-follow (window holds). */
|
|
1662
|
+
focusedIndex: number | null;
|
|
1663
|
+
/** Max rows the viewport renders, INCLUDING any marker rows. */
|
|
1664
|
+
height: number;
|
|
1665
|
+
/**
|
|
1666
|
+
* Context rows kept between the caret and the edge before the window scrolls.
|
|
1667
|
+
* Default `0` → the caret may reach the last content row, and moving past it
|
|
1668
|
+
* scrolls the window by one (vim `scrolloff=0`).
|
|
1669
|
+
*/
|
|
1670
|
+
scrolloff?: number;
|
|
1671
|
+
/**
|
|
1672
|
+
* Reserve a chrome row for the `▴`/`▾` "N more" marker on an overflowing side.
|
|
1673
|
+
* Default `true`. When `false`, the window still scrolls but clips silently.
|
|
1674
|
+
*/
|
|
1675
|
+
overflowMarkers?: boolean;
|
|
1676
|
+
}
|
|
1677
|
+
interface ListWindow {
|
|
1678
|
+
/** First visible CONTENT row index (inclusive). Slice with `rows.slice(start, end)`. */
|
|
1679
|
+
start: number;
|
|
1680
|
+
/** One past the last visible content row. */
|
|
1681
|
+
end: number;
|
|
1682
|
+
/** Rows hidden above / below the window (0 = none). Drive the markers off these. */
|
|
1683
|
+
aboveCount: number;
|
|
1684
|
+
belowCount: number;
|
|
1685
|
+
}
|
|
1686
|
+
/**
|
|
1687
|
+
* Pure windowing math: given the previous window start, where does the window
|
|
1688
|
+
* sit now so it (a) fits `height`, (b) contains `focusedIndex`, and (c) reserves
|
|
1689
|
+
* a chrome row for each overflow marker that shows? Exported for unit tests and
|
|
1690
|
+
* for callers windowing something other than {@link useListWindow}.
|
|
1691
|
+
*
|
|
1692
|
+
* Markers are **separate chrome rows**, not overlays: `content + markers` always
|
|
1693
|
+
* equals `height`, so the window never exceeds its container, and `aboveCount` /
|
|
1694
|
+
* `belowCount` are the exact hidden-row counts (no off-by-one against a marker).
|
|
1695
|
+
* The caret is always inside the content band, so the focused row is never
|
|
1696
|
+
* hidden behind a "N more".
|
|
1697
|
+
*
|
|
1698
|
+
* Marker reservation and the window position are mutually dependent (a marker
|
|
1699
|
+
* costs a content row, which can move the window, which can flip a marker), so
|
|
1700
|
+
* the start is settled over a few passes, then the markers are finalised from
|
|
1701
|
+
* the settled start so the returned values are always self-consistent.
|
|
1702
|
+
*
|
|
1703
|
+
* One consequence: the *single* step where a marker first appears shifts the
|
|
1704
|
+
* window by two rows (the new marker claims a content row, so the band shrinks
|
|
1705
|
+
* by one as focus moves by one). Every other step is a smooth 1/row scroll —
|
|
1706
|
+
* the steady-state behaviour. The alternative (markers overlaying edge rows)
|
|
1707
|
+
* keeps 1/row but muddies the hidden-row counts; clean counts win here.
|
|
1708
|
+
*/
|
|
1709
|
+
declare function computeWindow(prevStart: number, rowCount: number, focusedIndex: number | null, height: number, scrolloff?: number, overflowMarkers?: boolean): ListWindow;
|
|
1710
|
+
/**
|
|
1711
|
+
* Headless windowing for a fixed-height viewport over a flat row array — the
|
|
1712
|
+
* engine behind a windowed {@link List}, and reusable on its own for any
|
|
1713
|
+
* keyboard-paged list.
|
|
1714
|
+
*
|
|
1715
|
+
* It owns the window **offset** across renders (view-state, like a scroll
|
|
1716
|
+
* position — *not* the focus/selection state the contract reserves for the app)
|
|
1717
|
+
* and re-derives the window each render to keep `focusedIndex` visible. The hook
|
|
1718
|
+
* never sees a keystroke: the app moves `focusedIndex`, the window follows. With
|
|
1719
|
+
* `scrolloff = 0` the caret travels to the edge of the viewport and only then
|
|
1720
|
+
* does the window scroll.
|
|
1721
|
+
*
|
|
1722
|
+
* ```tsx
|
|
1723
|
+
* const win = useListWindow({ rowCount: rows.length, focusedIndex, height: 20 });
|
|
1724
|
+
* const visible = rows.slice(win.start, win.end);
|
|
1725
|
+
* ```
|
|
1726
|
+
*/
|
|
1727
|
+
declare function useListWindow(opts: UseListWindowOptions): ListWindow;
|
|
1728
|
+
|
|
1729
|
+
interface UseListNavigationOptions {
|
|
1730
|
+
/** Ordered row ids. */
|
|
1731
|
+
ids: string[];
|
|
1732
|
+
/** Controlled focus. Omit to let the hook own it (seeded to the first id). */
|
|
1733
|
+
focusedId?: string | null;
|
|
1734
|
+
onFocusChange?: (id: string) => void;
|
|
1735
|
+
/** Wrap first↔last instead of clamping at the ends. Default false. */
|
|
1736
|
+
wrap?: boolean;
|
|
1737
|
+
}
|
|
1738
|
+
interface ListNavigation {
|
|
1739
|
+
focusedId: string | null;
|
|
1740
|
+
/** Index of `focusedId` in `ids` (`-1` when none) — feed to {@link useListWindow}. */
|
|
1741
|
+
focusedIndex: number;
|
|
1742
|
+
focusNext: () => void;
|
|
1743
|
+
focusPrev: () => void;
|
|
1744
|
+
focusFirst: () => void;
|
|
1745
|
+
focusLast: () => void;
|
|
1746
|
+
focusTo: (id: string) => void;
|
|
1747
|
+
}
|
|
1748
|
+
/**
|
|
1749
|
+
* Headless focus movement for a list — clamp/wrap at the ends, next/prev/first/
|
|
1750
|
+
* last/seek. Exposes **intent methods**, never a baked-in key handler: the app
|
|
1751
|
+
* owns `useInput` and maps its own keys (`j/k`, arrows, …) to these, so blink
|
|
1752
|
+
* never embeds one app's keymap. Separate from {@link useListSelection} because
|
|
1753
|
+
* focus movement is generic to any list, including read-only menus that never
|
|
1754
|
+
* select.
|
|
1755
|
+
*
|
|
1756
|
+
* ```tsx
|
|
1757
|
+
* const nav = useListNavigation({ ids });
|
|
1758
|
+
* useInput((_, key) => {
|
|
1759
|
+
* if (key.downArrow) nav.focusNext();
|
|
1760
|
+
* if (key.upArrow) nav.focusPrev();
|
|
1761
|
+
* });
|
|
1762
|
+
* <List rows={rows} focusedId={nav.focusedId} height={20} />
|
|
1763
|
+
* ```
|
|
1764
|
+
*/
|
|
1765
|
+
declare function useListNavigation(opts: UseListNavigationOptions): ListNavigation;
|
|
1766
|
+
|
|
1767
|
+
type SelectionMode = 'single' | 'multi';
|
|
1768
|
+
interface UseListSelectionOptions {
|
|
1769
|
+
/** The selectable row ids (used for bounds only; selection is by id). */
|
|
1770
|
+
ids: string[];
|
|
1771
|
+
/** `'single'` keeps exactly one selected; `'multi'` toggles a subset. */
|
|
1772
|
+
mode: SelectionMode;
|
|
1773
|
+
/** multi/single: minimum that must stay selected — deselecting below is blocked. */
|
|
1774
|
+
min?: number;
|
|
1775
|
+
/** multi: maximum selectable — selecting above is blocked. */
|
|
1776
|
+
max?: number;
|
|
1777
|
+
/** Initially selected ids. */
|
|
1778
|
+
initial?: Iterable<string>;
|
|
1779
|
+
onChange?: (selected: Set<string>) => void;
|
|
1780
|
+
}
|
|
1781
|
+
interface ListSelection {
|
|
1782
|
+
/** Feed straight to `<List selectedIds={...} />`. */
|
|
1783
|
+
selectedIds: Set<string>;
|
|
1784
|
+
isSelected: (id: string) => boolean;
|
|
1785
|
+
/** Toggle `id`. Returns `false` when blocked by `min`/`max` (and sets `blocked`). */
|
|
1786
|
+
toggle: (id: string) => boolean;
|
|
1787
|
+
/** Single-select: make `id` the only selected row. */
|
|
1788
|
+
selectOnly: (id: string) => void;
|
|
1789
|
+
/** Clear all (blocked when `min` would be violated). */
|
|
1790
|
+
clear: () => void;
|
|
1791
|
+
/** True when the last operation was blocked by `min`/`max` — render as `Input.error`. */
|
|
1792
|
+
blocked: boolean;
|
|
1793
|
+
}
|
|
1794
|
+
/**
|
|
1795
|
+
* Headless selection logic for a list — the toggle, the single-select
|
|
1796
|
+
* invariant, and the min/max guards that every consumer otherwise hand-rolls.
|
|
1797
|
+
* It owns the selected `Set`; the **app still owns the keys** and renders the
|
|
1798
|
+
* existing presentational {@link List}. Pair with {@link useListNavigation} for
|
|
1799
|
+
* the cursor; this hook is only about *what is selected*.
|
|
1800
|
+
*
|
|
1801
|
+
* ```tsx
|
|
1802
|
+
* const sel = useListSelection({ ids, mode: 'multi', min: 1 });
|
|
1803
|
+
* useInput((input) => { if (input === ' ' && focusedId) sel.toggle(focusedId); });
|
|
1804
|
+
* <List rows={rows} selectedIds={sel.selectedIds} focusedId={focusedId} />
|
|
1805
|
+
* ```
|
|
1806
|
+
*/
|
|
1807
|
+
declare function useListSelection(opts: UseListSelectionOptions): ListSelection;
|
|
1808
|
+
|
|
1809
|
+
/**
|
|
1810
|
+
* Pane emphasis, by PURPOSE — the consumer declares what a pane *means*, blink
|
|
1811
|
+
* draws it. There is exactly one border shape in the house style (single-line,
|
|
1812
|
+
* rounded), so tone never changes the geometry, only the colour:
|
|
1813
|
+
*
|
|
1814
|
+
* - `resting` — a pane at rest (muted border).
|
|
1815
|
+
* - `focus` — the focused pane (border + title recoloured lavender).
|
|
1816
|
+
* - `error` — an error / destructive pane (border + title red).
|
|
1817
|
+
*/
|
|
1818
|
+
type PaneTone = 'resting' | 'focus' | 'error';
|
|
1819
|
+
/**
|
|
1820
|
+
* Legacy border treatments. Kept only so older call sites keep working while
|
|
1821
|
+
* they migrate to {@link PaneTone}. `'double'` is gone from the house style and
|
|
1822
|
+
* now renders as the rounded default; `'square'` is the one legacy shape opt-out.
|
|
1823
|
+
* @deprecated pass `tone` instead.
|
|
1824
|
+
*/
|
|
1825
|
+
type PaneVariant = 'default' | 'rounded' | 'square' | 'double' | 'error';
|
|
1826
|
+
interface PaneProps {
|
|
1827
|
+
/** Title shown inside the top border: `╭─ title ──────╮`. Keep ≤ 18 chars. */
|
|
1828
|
+
title?: string;
|
|
1829
|
+
/**
|
|
1830
|
+
* Semantic emphasis. The framework owns the border colour, the title colour,
|
|
1831
|
+
* and the (always rounded) shape. Default `'resting'`.
|
|
1832
|
+
*/
|
|
1833
|
+
tone?: PaneTone;
|
|
1834
|
+
/**
|
|
1835
|
+
* @deprecated use `tone="focus"`. A focused pane gets the lavender border.
|
|
1836
|
+
* Kept as a back-compat alias; `tone` wins when both are set.
|
|
1837
|
+
*/
|
|
1838
|
+
focused?: boolean;
|
|
1839
|
+
/**
|
|
1840
|
+
* @deprecated use `tone`. `'error'` ⇒ `tone="error"`, `'square'` ⇒ the legacy
|
|
1841
|
+
* square shape; every other value falls through to the rounded house style.
|
|
1842
|
+
*/
|
|
1843
|
+
variant?: PaneVariant;
|
|
1844
|
+
/** Flex grow factor (default 1 — panes fill available space). */
|
|
1845
|
+
flexGrow?: number;
|
|
1846
|
+
/** Flex basis (e.g. `'56%'`). */
|
|
1847
|
+
flexBasis?: BoxProps['flexBasis'];
|
|
1848
|
+
/** Fixed width in cells. */
|
|
1849
|
+
width?: BoxProps['width'];
|
|
1850
|
+
/** Fixed height in cells. */
|
|
1851
|
+
height?: BoxProps['height'];
|
|
1852
|
+
/** Minimum height in cells. */
|
|
1853
|
+
minHeight?: BoxProps['minHeight'];
|
|
1854
|
+
children?: React.ReactNode;
|
|
1855
|
+
}
|
|
1856
|
+
/**
|
|
1857
|
+
* A box-drawn rectangle with an optional title in the top border — the
|
|
1858
|
+
* analogue of a "card" in blink (panes don't lift, shadow, or change shape).
|
|
1859
|
+
*
|
|
1860
|
+
* Borders are real box-drawing glyphs. Ink's native border renders the
|
|
1861
|
+
* full-height left/right sides and the bottom edge; the top edge is drawn by
|
|
1862
|
+
* hand so the title can sit *inside* it. The two halves share a width, so they
|
|
1863
|
+
* join into one continuous frame. Focus and elevation are signalled by **border
|
|
1864
|
+
* colour** alone — the shape is identical across tones, so the layout never
|
|
1865
|
+
* shifts when a pane gains or loses focus.
|
|
1866
|
+
*/
|
|
1867
|
+
declare function Pane({ title, tone, focused, variant, flexGrow, flexBasis, width, height, minHeight, children, }: PaneProps): React.ReactElement;
|
|
1868
|
+
|
|
1869
|
+
/**
|
|
1870
|
+
* One row's worth of data — declared as **intent**, never style. A row says what
|
|
1871
|
+
* it MEANS (`state="installed"`, `selected`, `domain="postgresql"`) and the
|
|
1872
|
+
* framework resolves the glyph and its colour from the house tokens. The
|
|
1873
|
+
* consumer never passes a raw glyph or a raw colour. (The focus caret is the one
|
|
1874
|
+
* exception — it is chrome, not data.)
|
|
1875
|
+
*/
|
|
1876
|
+
interface ListRowData {
|
|
1877
|
+
/** Stable identity, used for focus/selection lookups and React keys. */
|
|
1878
|
+
id: string;
|
|
1879
|
+
/** The row's primary text, in `fg` (or `fgDim` when `muted`). */
|
|
1880
|
+
label: string;
|
|
1881
|
+
/**
|
|
1882
|
+
* Semantic status name → the framework draws its glyph + colour:
|
|
1883
|
+
* `installed | ok | done | missing | error | drift | partial | idempotent |
|
|
1884
|
+
* pending | warn | info`. See `stateGlyph()`.
|
|
1885
|
+
*/
|
|
1886
|
+
state?: string;
|
|
1887
|
+
/** Selection intent → `☑ / ☐`. Presence of the field (even `false`) opts the row into the checkbox column. */
|
|
1888
|
+
selected?: boolean;
|
|
1889
|
+
/** Required, non-toggle (implies selected) → `▣`. */
|
|
1890
|
+
locked?: boolean;
|
|
1891
|
+
/** A **registered** domain glyph name → the glyph + its colour, owned at registration. */
|
|
1892
|
+
domain?: string;
|
|
1893
|
+
/** Right-aligned consequence / aside text (content), in `fgDim`. */
|
|
1894
|
+
meta?: string;
|
|
1895
|
+
/** De-emphasise the whole row (e.g. a disabled / required label). */
|
|
1896
|
+
muted?: boolean;
|
|
1897
|
+
}
|
|
1898
|
+
interface ListRowProps {
|
|
1899
|
+
row: ListRowData;
|
|
1900
|
+
/** Carries the `►` caret + `bgFocused` fill. Wins over `selected`. */
|
|
1901
|
+
focused?: boolean;
|
|
1902
|
+
/** Draws the `bgSelected` fill (only when not also focused). */
|
|
1903
|
+
selected?: boolean;
|
|
1904
|
+
/**
|
|
1905
|
+
* Which intent columns this row reserves, and their fixed cell widths, so the
|
|
1906
|
+
* glyph columns line up down the whole list. `List` computes these from the
|
|
1907
|
+
* widest cell in each column; a standalone `ListRow` derives them from itself.
|
|
1908
|
+
*/
|
|
1909
|
+
showCheckbox?: boolean;
|
|
1910
|
+
showState?: boolean;
|
|
1911
|
+
showDomain?: boolean;
|
|
1912
|
+
caretWidth?: number;
|
|
1913
|
+
checkboxWidth?: number;
|
|
1914
|
+
stateWidth?: number;
|
|
1915
|
+
domainWidth?: number;
|
|
1916
|
+
}
|
|
1917
|
+
interface ListProps {
|
|
1918
|
+
rows: ListRowData[];
|
|
1919
|
+
/** Id of the row that holds the nav caret. */
|
|
1920
|
+
focusedId?: string | null;
|
|
1921
|
+
/** Ids drawn with the selection fill. */
|
|
1922
|
+
selectedIds?: Set<string>;
|
|
1923
|
+
/**
|
|
1924
|
+
* Max rows to render, including any overflow-marker rows. Omit to render every
|
|
1925
|
+
* row. When set and `rows` exceeds it, List shows a window that always contains
|
|
1926
|
+
* `focusedId` and scrolls as focus nears an edge — keyboard-paged, never
|
|
1927
|
+
* mouse-scrolled (see the contract).
|
|
1928
|
+
*/
|
|
1929
|
+
height?: number;
|
|
1930
|
+
/** Context rows kept before the window scrolls. Default 0 (scroll at the edge). */
|
|
1931
|
+
scrolloff?: number;
|
|
1932
|
+
/** Draw `▴ N more` / `▾ N more` on overflowing sides. Default true. */
|
|
1933
|
+
overflowMarkers?: boolean;
|
|
1934
|
+
}
|
|
1935
|
+
/**
|
|
1936
|
+
* A single list row — a caret column, then the optional intent columns
|
|
1937
|
+
* (checkbox · state · domain), the label, then the meta pushed right. The
|
|
1938
|
+
* focused row fills with `bgFocused`; a selected-but-unfocused row fills with
|
|
1939
|
+
* `bgSelected`. No hover, by contract.
|
|
1940
|
+
*
|
|
1941
|
+
* Each intent column is a **fixed-width cell** (truncating, never wrapping), so
|
|
1942
|
+
* labels stay column-aligned no matter how wide a row's glyph (or its multi-char
|
|
1943
|
+
* `ascii`/`unicode` fallback) renders. The glyph and its colour are resolved
|
|
1944
|
+
* here from the row's intent — the consumer hands over meaning, not pixels.
|
|
1945
|
+
*/
|
|
1946
|
+
declare function ListRow({ row, focused, selected, showCheckbox, showState, showDomain, caretWidth, checkboxWidth, stateWidth, domainWidth, }: ListRowProps): React.ReactElement;
|
|
1947
|
+
/**
|
|
1948
|
+
* A vertical stack of {@link ListRow}s — blink's plain list. Exactly one row may
|
|
1949
|
+
* be focused (`focusedId`); any number may be selection-filled (`selectedIds`).
|
|
1950
|
+
* Rows are pure **intent** ({@link ListRowData}); the list resolves the glyphs.
|
|
1951
|
+
*
|
|
1952
|
+
* List sizes the caret / checkbox / state / domain columns to the widest cell
|
|
1953
|
+
* across all rows and passes those widths down, so every row shares one grid —
|
|
1954
|
+
* the fix for ragged columns when glyphs fall back to variable-width text.
|
|
1955
|
+
*/
|
|
1956
|
+
declare function List({ rows, focusedId, selectedIds, height, scrolloff, overflowMarkers, }: ListProps): React.ReactElement;
|
|
1957
|
+
|
|
1958
|
+
interface HeaderProps {
|
|
1959
|
+
/**
|
|
1960
|
+
* Leading accent mark. Defaults to the blink cursor block `▎`. Pass a node
|
|
1961
|
+
* (e.g. a blinking `<Cursor />`) for the live mark, or a string for a static
|
|
1962
|
+
* one; `null` drops the mark entirely.
|
|
1963
|
+
*/
|
|
1964
|
+
mark?: React.ReactNode;
|
|
1965
|
+
/** Screen title, in `fg`. */
|
|
1966
|
+
title: string;
|
|
1967
|
+
/** Optional aside, rendered `· subtitle` in `fgMuted`. */
|
|
1968
|
+
subtitle?: string;
|
|
1969
|
+
/** Optional node flush-right — status, counts, a delta summary. */
|
|
1970
|
+
right?: React.ReactNode;
|
|
1971
|
+
}
|
|
1972
|
+
/**
|
|
1973
|
+
* The one-row status bar that tops a blink screen: a lavender mark, a title
|
|
1974
|
+
* (with an optional `· subtitle`), and a right-aligned status slot. It recurs on
|
|
1975
|
+
* every wizard / app screen — the brand chrome — so it is a primitive, not
|
|
1976
|
+
* per-app code. One line, never wraps; the subtitle truncates first.
|
|
1977
|
+
*/
|
|
1978
|
+
declare function Header({ mark, title, subtitle, right, }: HeaderProps): React.ReactElement;
|
|
1979
|
+
|
|
1980
|
+
/** One term/value row. Carries intent (`state`, `muted`) — never a raw glyph or colour. */
|
|
1981
|
+
interface DescriptionItem {
|
|
1982
|
+
/** The label in the gutter (`fgDim`). Omit for a full-width value line. */
|
|
1983
|
+
term?: string;
|
|
1984
|
+
/** The value text. */
|
|
1985
|
+
value: string;
|
|
1986
|
+
/**
|
|
1987
|
+
* A semantic status name → the framework draws its glyph + colour before the
|
|
1988
|
+
* value (`installed`, `missing`, `drift`, `pending`, …). See `stateGlyph()`.
|
|
1989
|
+
*/
|
|
1990
|
+
state?: string;
|
|
1991
|
+
/** De-emphasise the value (e.g. a description line) → rendered in `fgMuted`. */
|
|
1992
|
+
muted?: boolean;
|
|
1993
|
+
}
|
|
1994
|
+
interface DescriptionListProps {
|
|
1995
|
+
/** The rows. */
|
|
1996
|
+
items?: DescriptionItem[];
|
|
1997
|
+
/** Term-column width in cells. Default 10. */
|
|
1998
|
+
gutter?: number;
|
|
1999
|
+
}
|
|
2000
|
+
/**
|
|
2001
|
+
* A key/value block aligned to a character gutter — the generic shape behind any
|
|
2002
|
+
* detail pane or summary screen. The complement to {@link List}: List is a
|
|
2003
|
+
* vertical menu of peers, DescriptionList is the attributes of one thing.
|
|
2004
|
+
*
|
|
2005
|
+
* INTENT, NOT STYLE: a row may carry a semantic `state` (→ framework glyph +
|
|
2006
|
+
* colour) and a `muted` flag (→ de-emphasised value); it never takes a raw glyph
|
|
2007
|
+
* or a raw colour. Term-less rows render as a full-width value line.
|
|
2008
|
+
*/
|
|
2009
|
+
declare function DescriptionList({ items, gutter, }: DescriptionListProps): React.ReactElement;
|
|
2010
|
+
|
|
2011
|
+
interface LogViewProps {
|
|
2012
|
+
/** The full line buffer. LogView renders only the tail that fits `height`. */
|
|
2013
|
+
lines: string[];
|
|
2014
|
+
/** Viewport height in rows, including an overflow-marker row when one shows. */
|
|
2015
|
+
height: number;
|
|
2016
|
+
/** Follow the newest line (default). `false` freezes the window as lines append. */
|
|
2017
|
+
follow?: boolean;
|
|
2018
|
+
/** Wrap long lines to the viewport width (default) vs truncate them. */
|
|
2019
|
+
wrap?: boolean;
|
|
2020
|
+
/** Viewport width in cells. Omit to measure the rendered box (Ink `measureElement`). */
|
|
2021
|
+
width?: number;
|
|
2022
|
+
}
|
|
2023
|
+
/**
|
|
2024
|
+
* A bottom-anchored, height-bounded viewport over an ever-growing line stream —
|
|
2025
|
+
* subprocess output, a build log, a chat transcript. The newest lines stay
|
|
2026
|
+
* pinned to the bottom; older lines fall off the top with a dim `▴ N more`
|
|
2027
|
+
* marker. The mirror of a windowed {@link List}: that one follows a *focused*
|
|
2028
|
+
* row, this one follows the *tail*.
|
|
2029
|
+
*
|
|
2030
|
+
* Unlike Ink's `<Static>` (append-only scrollback that grows past the screen),
|
|
2031
|
+
* LogView is a fixed region. The app owns the subprocess and feeds `lines`;
|
|
2032
|
+
* LogView only renders the tail — the window advancing on new data is content
|
|
2033
|
+
* re-render, not motion, under the one-animation contract.
|
|
2034
|
+
*
|
|
2035
|
+
* When `wrap` is on, a logical line spans `ceil(width / cellWidth)` visual rows,
|
|
2036
|
+
* so the tail is counted in **visual rows**, not array entries — which needs a
|
|
2037
|
+
* width. Pass `width` (it's known inside a sized `Pane`); otherwise LogView
|
|
2038
|
+
* measures its own box, approximating for the first frame.
|
|
2039
|
+
*/
|
|
2040
|
+
declare function LogView({ lines, height, follow, wrap, width, }: LogViewProps): React.ReactElement;
|
|
2041
|
+
|
|
2042
|
+
/** One hotkey: a key chip plus its terse description. */
|
|
2043
|
+
interface HotkeyDef {
|
|
2044
|
+
/** The key, lowercased: `'tab'`, `'enter'`, `'q'`, `'/'`, `'?'`. */
|
|
2045
|
+
k: string;
|
|
2046
|
+
/** Terse lowercase label for what the key does (`'switch pane'`). */
|
|
2047
|
+
desc: string;
|
|
2048
|
+
}
|
|
2049
|
+
interface FooterProps {
|
|
2050
|
+
/** Hotkeys, laid left-to-right with a 3-cell gap between them. */
|
|
2051
|
+
keys?: HotkeyDef[];
|
|
2052
|
+
/** Optional status node, flush-right in faint text (e.g. `'6 of 8'`). */
|
|
2053
|
+
right?: React.ReactNode;
|
|
2054
|
+
/**
|
|
2055
|
+
* Cells of breathing room above the bar. House default is `1` so the footer
|
|
2056
|
+
* never butts up against the content above it; pass `0` to pin it flush.
|
|
2057
|
+
*/
|
|
2058
|
+
marginTop?: number;
|
|
2059
|
+
}
|
|
2060
|
+
/**
|
|
2061
|
+
* The always-visible hotkey bar pinned to the bottom row. A single sunken-fill
|
|
2062
|
+
* line: hotkeys flush-left (3-cell gaps), an optional status node flush-right
|
|
2063
|
+
* in faint text. Never wraps — the bar is one cell tall by contract.
|
|
2064
|
+
*
|
|
2065
|
+
* When the chips + status don't fit the terminal width, whole chips are dropped
|
|
2066
|
+
* from the right rather than clipped mid-word (a chip reading `he` or a `q` with
|
|
2067
|
+
* no label is worse than one fewer key). Apps should order `keys` by importance.
|
|
2068
|
+
*
|
|
2069
|
+
* Ink's `<Box>` has no fill (only `<Text>` takes `backgroundColor`), and a
|
|
2070
|
+
* terminal cell can't layer — every cell is one glyph with one fg + one bg. So
|
|
2071
|
+
* the solid sunken bar of the design system is built by making the *gaps and
|
|
2072
|
+
* padding themselves* background-carrying spaces inside a single `<Text>`, with
|
|
2073
|
+
* the inverse key chips nested as `<Text>` that override the bg. That paints the
|
|
2074
|
+
* whole row edge-to-edge rather than only the cells under the glyphs.
|
|
2075
|
+
*
|
|
2076
|
+
* A measurable (string) `right` is flush-right with an exact space fill. An
|
|
2077
|
+
* unmeasurable React-node `right` falls back to the flex layout below (its
|
|
2078
|
+
* middle gap stays unfilled), since the fill width can't be computed.
|
|
2079
|
+
*/
|
|
2080
|
+
declare function Footer({ keys, right, marginTop }: FooterProps): React.ReactElement;
|
|
2081
|
+
|
|
2082
|
+
interface CursorProps {
|
|
2083
|
+
/** Blink while true (default), hold solid while false. */
|
|
2084
|
+
active?: boolean;
|
|
2085
|
+
/** Block colour. Defaults to `tokens.fg`. */
|
|
2086
|
+
color?: string;
|
|
2087
|
+
}
|
|
2088
|
+
/**
|
|
2089
|
+
* The blink caret — a `▎` block that toggles at 1 Hz with step-end timing,
|
|
2090
|
+
* blink's one sanctioned text animation. The "off" frame is a single space, so
|
|
2091
|
+
* the caret occupies a stable cell and never nudges the line around it.
|
|
2092
|
+
*/
|
|
2093
|
+
declare function Cursor({ active, color }: CursorProps): React.ReactElement;
|
|
2094
|
+
interface InputProps {
|
|
2095
|
+
/** Title shown inside the top border. Keep ≤ 18 chars. */
|
|
2096
|
+
title?: string;
|
|
2097
|
+
/** Current field value. The app owns key handling; this is presentational. */
|
|
2098
|
+
value?: string;
|
|
2099
|
+
/** Hint shown in `fgDisabled` while the value is empty. */
|
|
2100
|
+
placeholder?: string;
|
|
2101
|
+
/** Focused fields round their border and trail a live cursor. */
|
|
2102
|
+
focused?: boolean;
|
|
2103
|
+
/** Error message — promotes the border to red and shows a line below. */
|
|
2104
|
+
error?: string;
|
|
2105
|
+
}
|
|
2106
|
+
/**
|
|
2107
|
+
* A single-line text field: a {@link Pane} wrapping one row of value (or
|
|
2108
|
+
* placeholder), with a {@link Cursor} trailing while focused. Value-driven and
|
|
2109
|
+
* presentational — the app feeds `value` and owns the keys.
|
|
2110
|
+
*
|
|
2111
|
+
* INTENT, NOT STYLE: the consumer never picks a border treatment; the field
|
|
2112
|
+
* derives its pane `tone` from its own state — `error` → red, `focused` →
|
|
2113
|
+
* lavender, otherwise resting. An error also prints a `✗ message` line below.
|
|
2114
|
+
*/
|
|
2115
|
+
declare function Input({ title, value, placeholder, focused, error, }: InputProps): React.ReactElement;
|
|
2116
|
+
|
|
2117
|
+
/** One footer action: a key chip plus its label. */
|
|
2118
|
+
interface DialogAction {
|
|
2119
|
+
/** The hotkey shown in the chip, e.g. `'y'`. */
|
|
2120
|
+
key: string;
|
|
2121
|
+
/** What the key does, e.g. `'delete'`. */
|
|
2122
|
+
label: string;
|
|
2123
|
+
/** The default/confirming action — renders its chip in inverse-accent. */
|
|
2124
|
+
primary?: boolean;
|
|
2125
|
+
}
|
|
2126
|
+
/** Dialog emphasis, by purpose. `'default'` is a focused (lavender) modal. */
|
|
2127
|
+
type DialogTone = 'default' | 'error';
|
|
2128
|
+
interface DialogProps {
|
|
2129
|
+
/** Shown inside the top border. Keep ≤ 18 chars (it nests in the frame). */
|
|
2130
|
+
title: string;
|
|
2131
|
+
/**
|
|
2132
|
+
* Semantic emphasis: `'default'` is a focused (lavender) modal, `'error'`
|
|
2133
|
+
* recolours it red. The consumer never picks the shape — it is always the
|
|
2134
|
+
* rounded house pane.
|
|
2135
|
+
*/
|
|
2136
|
+
tone?: DialogTone;
|
|
2137
|
+
/** Body rows, one `<Text>` line each — the convenience for plain-text bodies. */
|
|
2138
|
+
lines?: string[];
|
|
2139
|
+
/** Rich body (a `List`, glyph rows, a small form). Wins over `lines` when both given. */
|
|
2140
|
+
children?: React.ReactNode;
|
|
2141
|
+
/** Footer actions, laid out left-to-right with a 2-cell gap. */
|
|
2142
|
+
actions?: DialogAction[];
|
|
2143
|
+
/** Fixed dialog width in cells. */
|
|
2144
|
+
width?: number;
|
|
2145
|
+
}
|
|
2146
|
+
/**
|
|
2147
|
+
* A centred modal — the analogue of a "confirm" overlay in blink. There is no
|
|
2148
|
+
* backdrop, blur, or fade; Ink has no absolute positioning or z-index, so the
|
|
2149
|
+
* app renders `<Dialog/>` as a *full-screen replacement layer* instead of a
|
|
2150
|
+
* floating panel — it simply replaces focus.
|
|
2151
|
+
*
|
|
2152
|
+
* The frame is a fixed-width rounded {@link Pane}: lavender (focus tone) by
|
|
2153
|
+
* default, red for `tone="error"` — elevation is colour, never a heavier line.
|
|
2154
|
+
* The body is a blank line, the supplied `lines`, another blank line, then the
|
|
2155
|
+
* actions row. The primary action's key chip renders in inverse-accent video;
|
|
2156
|
+
* the rest sit muted.
|
|
2157
|
+
*/
|
|
2158
|
+
declare function Dialog({ title, tone, lines, children, actions, width, }: DialogProps): React.ReactElement;
|
|
2159
|
+
|
|
2160
|
+
/** Notice severity — the only thing the consumer expresses. */
|
|
2161
|
+
type BannerTone = 'info' | 'success' | 'warn';
|
|
2162
|
+
interface BannerProps {
|
|
2163
|
+
/** Rich body. Wins over `text` when both are given. */
|
|
2164
|
+
children?: React.ReactNode;
|
|
2165
|
+
/** Plain-text body, the common case. */
|
|
2166
|
+
text?: string;
|
|
2167
|
+
/** Intent — the framework picks the leading glyph + its colour. Default `'info'`. */
|
|
2168
|
+
tone?: BannerTone;
|
|
2169
|
+
}
|
|
2170
|
+
/**
|
|
2171
|
+
* A one-line, non-blocking notice in the content flow — "auto-selected X",
|
|
2172
|
+
* "saved", "3 items skipped". The middle ground between the blocking
|
|
2173
|
+
* {@link Dialog} and the persistent `Footer`: it acknowledges a side effect
|
|
2174
|
+
* without stealing focus.
|
|
2175
|
+
*
|
|
2176
|
+
* INTENT, NOT STYLE: the consumer picks a `tone`; the framework owns the leading
|
|
2177
|
+
* glyph and the colour. Per the contract, semantic colour lives on the glyph —
|
|
2178
|
+
* the message text stays calm (`fgMuted`). Purely presentational: no timer, no
|
|
2179
|
+
* auto-dismiss (the app mounts/unmounts it, keeping the one-animation contract
|
|
2180
|
+
* clean).
|
|
2181
|
+
*/
|
|
2182
|
+
declare function Banner({ children, text, tone }: BannerProps): React.ReactElement;
|
|
2183
|
+
|
|
2184
|
+
interface SpinnerProps {
|
|
2185
|
+
/** Advance the spinner. When false, the first frame is shown statically. Defaults to true. */
|
|
2186
|
+
active?: boolean;
|
|
2187
|
+
/** Override the glyph colour. Defaults to the info state token. */
|
|
2188
|
+
color?: string;
|
|
2189
|
+
/** Frame interval in ms. Defaults to 80 (the contract's spinner cadence). */
|
|
2190
|
+
intervalMs?: number;
|
|
2191
|
+
}
|
|
2192
|
+
/**
|
|
2193
|
+
* A one-cell animated spinner — braille for nerd/unicode, classic `| / - \` for
|
|
2194
|
+
* ascii. Frames advance every `intervalMs` while `active`; when inactive it
|
|
2195
|
+
* rests on frame 0 with no timer running.
|
|
2196
|
+
*
|
|
2197
|
+
* The frame counter comes from {@link useSpinnerFrame} and the glyph table from
|
|
2198
|
+
* {@link spinnerFor}, so the component stays icon-set agnostic — context decides
|
|
2199
|
+
* which alphabet renders.
|
|
2200
|
+
*/
|
|
2201
|
+
declare function Spinner({ active, color, intervalMs }: SpinnerProps): React.ReactElement;
|
|
2202
|
+
|
|
2203
|
+
interface ProgressBarProps {
|
|
2204
|
+
/** Progress in `0..1` (clamped). */
|
|
2205
|
+
value: number;
|
|
2206
|
+
/** Bar length in cells. */
|
|
2207
|
+
width: number;
|
|
2208
|
+
/** Filled colour. Defaults to `tokens.accent`. */
|
|
2209
|
+
color?: string;
|
|
2210
|
+
/** Track (empty rail) colour. Defaults to `tokens.border` (surface1), matching the design system's `bar-rest`. */
|
|
2211
|
+
trackColor?: string;
|
|
2212
|
+
/** Append a ` NN%` readout in `fgDim` after the bar. Defaults to `true`, matching the design system. */
|
|
2213
|
+
showPercent?: boolean;
|
|
2214
|
+
}
|
|
2215
|
+
/**
|
|
2216
|
+
* A determinate progress bar built from the horizontal eighth-block ramp
|
|
2217
|
+
* ({@link blocksH}) — the complement to the indeterminate {@link Spinner}. The
|
|
2218
|
+
* fractional cell renders to one of eight partials for sub-cell precision.
|
|
2219
|
+
*
|
|
2220
|
+
* The empty remainder is a visible `░` rail in the track colour (the design
|
|
2221
|
+
* system's `[████░░░░]` treatment), not blank space — so the bar always reads
|
|
2222
|
+
* as a bar, full or not.
|
|
2223
|
+
*
|
|
2224
|
+
* The `ascii` icon set has no partials, so it degrades to whole `#` cells (the
|
|
2225
|
+
* fractional eighth is dropped) over a blank track, like every other glyph that
|
|
2226
|
+
* falls back.
|
|
2227
|
+
*/
|
|
2228
|
+
declare function ProgressBar({ value, width, color, trackColor, showPercent }: ProgressBarProps): React.ReactElement;
|
|
2229
|
+
|
|
2230
|
+
/**
|
|
2231
|
+
* The states a {@link ProgressList} line can be in — the execution vocabulary of
|
|
2232
|
+
* any apply / migrate / sync / build. Each maps to **intent**, never style: the
|
|
2233
|
+
* framework owns the glyph (or the live spinner) and its semantic colour.
|
|
2234
|
+
*
|
|
2235
|
+
* - `pending` — queued, not started (`◯`, dim).
|
|
2236
|
+
* - `running` — executing now → the one sanctioned animation, a {@link Spinner}.
|
|
2237
|
+
* - `ok` / `done` — finished cleanly (`✓`, green).
|
|
2238
|
+
* - `failed` / `error` — finished badly (`✗`, red).
|
|
2239
|
+
* - `waiting` — blocked on a *manual* action and may stay so indefinitely
|
|
2240
|
+
* (`◐`, warn). It reads apart from `running` so a step paused on, say, a device
|
|
2241
|
+
* pairing is legible. (The pause prompt itself is an app `Dialog`, not a
|
|
2242
|
+
* primitive.)
|
|
2243
|
+
* - `skipped` — deliberately not run (`◌`, disabled).
|
|
2244
|
+
*/
|
|
2245
|
+
type ProgressState = 'pending' | 'running' | 'ok' | 'done' | 'failed' | 'error' | 'waiting' | 'skipped';
|
|
2246
|
+
/** One step / task in a {@link ProgressList}. Intent only — no glyph, no colour. */
|
|
2247
|
+
interface ProgressItem {
|
|
2248
|
+
/** Stable identity, used for the active-line lookup and React keys. */
|
|
2249
|
+
id: string;
|
|
2250
|
+
/** The step's primary text. */
|
|
2251
|
+
label: string;
|
|
2252
|
+
/** A **registered** domain glyph name → its glyph + owned colour (optional column). */
|
|
2253
|
+
domain?: string;
|
|
2254
|
+
/** Execution status → the framework draws the glyph / spinner + colour. */
|
|
2255
|
+
state: ProgressState;
|
|
2256
|
+
/** Right-aligned aside (elapsed time, hint, count …), in `fgDim`. */
|
|
2257
|
+
meta?: string;
|
|
2258
|
+
}
|
|
2259
|
+
interface ProgressListProps {
|
|
2260
|
+
/** The steps, in execution order. */
|
|
2261
|
+
items: ProgressItem[];
|
|
2262
|
+
/** Id of the active (executing) line — fills with `bgFocused`, kept in view. */
|
|
2263
|
+
activeId?: string | null;
|
|
2264
|
+
/**
|
|
2265
|
+
* Max lines to render, including any overflow-marker rows. Omit to render every
|
|
2266
|
+
* line. When set and `items` exceeds it, the window always contains `activeId`
|
|
2267
|
+
* and follows it as it advances — keyboard-paged, like {@link List}.
|
|
2268
|
+
*/
|
|
2269
|
+
height?: number;
|
|
2270
|
+
/** Draw `▴ N more` / `▾ N more` on overflowing sides. Default true. */
|
|
2271
|
+
overflowMarkers?: boolean;
|
|
2272
|
+
/**
|
|
2273
|
+
* Advance the running line's spinner. Defaults to true; pass `false` for a
|
|
2274
|
+
* static frame (snapshots / non-interactive renders), matching every other
|
|
2275
|
+
* blink motion.
|
|
2276
|
+
*/
|
|
2277
|
+
animate?: boolean;
|
|
2278
|
+
}
|
|
2279
|
+
/**
|
|
2280
|
+
* A list of steps that transition through {@link ProgressState}s, with a live
|
|
2281
|
+
* {@link Spinner} on the running line — the universal apply / migrate / sync /
|
|
2282
|
+
* build view. The complement to {@link ProgressBar}: the bar is the aggregate
|
|
2283
|
+
* (compose one above this), this is the per-line detail.
|
|
2284
|
+
*
|
|
2285
|
+
* INTENT, NOT STYLE: a line carries a `state` (and an optional `domain` name);
|
|
2286
|
+
* the framework owns the glyph (or the spinner), its colour, the label tier, the
|
|
2287
|
+
* active-line fill, and the `▴/▾` overflow chrome. The consumer never passes a
|
|
2288
|
+
* glyph or a colour. The list windows to keep the active line in view, exactly
|
|
2289
|
+
* like {@link List}, since a queue is usually taller than its pane.
|
|
2290
|
+
*/
|
|
2291
|
+
declare function ProgressList({ items, activeId, height, overflowMarkers, animate, }: ProgressListProps): React.ReactElement;
|
|
2292
|
+
|
|
2293
|
+
/**
|
|
2294
|
+
* Form — a vertical set of LABELLED fields, each a control of a known `kind`,
|
|
2295
|
+
* navigated by keyboard and validated by declared constraints. The universal
|
|
2296
|
+
* "options / config screen" primitive (git config, tokens, versions, flags).
|
|
2297
|
+
* Without it every app re-derives checkbox / radio / toggle from the raw
|
|
2298
|
+
* SELECTION glyphs — exactly the divergence blink exists to avoid.
|
|
2299
|
+
*
|
|
2300
|
+
* INTENT, NOT STYLE: a field declares its `kind`; blink owns the glyph, the
|
|
2301
|
+
* colour, the focus fill, the required marker, and the error line. The consumer
|
|
2302
|
+
* never passes a glyph or a colour. `text` / `secret` reuse {@link Input} (the
|
|
2303
|
+
* `▎` cursor, placeholder, error, focus border); Form is the layer above it.
|
|
2304
|
+
*/
|
|
2305
|
+
/** The control a field renders — the ONLY look prop. */
|
|
2306
|
+
type FieldKind = 'text' | 'secret' | 'toggle' | 'select' | 'multiselect';
|
|
2307
|
+
/** A choice for a `select` / `multiselect` — a bare id, or an id with a label. */
|
|
2308
|
+
type ChoiceInput = string | {
|
|
2309
|
+
id: string;
|
|
2310
|
+
label?: string;
|
|
2311
|
+
};
|
|
2312
|
+
/** A resolved choice (id + display label). */
|
|
2313
|
+
interface ResolvedChoice {
|
|
2314
|
+
id: string;
|
|
2315
|
+
label: string;
|
|
2316
|
+
}
|
|
2317
|
+
/** One field in a {@link Form}. */
|
|
2318
|
+
interface FieldSpec {
|
|
2319
|
+
/** Stable key into the values object. */
|
|
2320
|
+
name: string;
|
|
2321
|
+
/** Which control to draw — the only look prop. */
|
|
2322
|
+
kind: FieldKind;
|
|
2323
|
+
/** The label above the control. */
|
|
2324
|
+
label: string;
|
|
2325
|
+
/** Required → a warn `*` marker and an empty-value error. */
|
|
2326
|
+
required?: boolean;
|
|
2327
|
+
/** Placeholder for `text` / `secret`, shown while empty. */
|
|
2328
|
+
placeholder?: string;
|
|
2329
|
+
/** Choices for `select` / `multiselect`. */
|
|
2330
|
+
choices?: ChoiceInput[];
|
|
2331
|
+
/** Derive choices from another field's current value (its selected ids). */
|
|
2332
|
+
optionsFrom?: string;
|
|
2333
|
+
/** `multiselect` lower bound — Form refuses to deselect below it. */
|
|
2334
|
+
min?: number;
|
|
2335
|
+
/** `multiselect` upper bound — Form refuses to select above it. */
|
|
2336
|
+
max?: number;
|
|
2337
|
+
}
|
|
2338
|
+
/** A single field's value: a string (text/secret/select), a set of ids (multiselect), or a flag (toggle). */
|
|
2339
|
+
type FieldValue = string | string[] | boolean | undefined;
|
|
2340
|
+
/** The form's value map, keyed by field name. */
|
|
2341
|
+
type FormValues = Record<string, FieldValue>;
|
|
2342
|
+
/** Validation result — `ok` plus a per-field message map. */
|
|
2343
|
+
interface FormValidation {
|
|
2344
|
+
ok: boolean;
|
|
2345
|
+
errors: Record<string, string>;
|
|
2346
|
+
}
|
|
2347
|
+
/** A keyboard focus stop. text/secret/toggle = one per field; select/multiselect = one per choice. */
|
|
2348
|
+
interface FocusStop {
|
|
2349
|
+
/** `name` for single-stop fields, `name::choiceId` for choice fields. */
|
|
2350
|
+
id: string;
|
|
2351
|
+
name: string;
|
|
2352
|
+
kind: FieldKind;
|
|
2353
|
+
choiceId: string | null;
|
|
2354
|
+
}
|
|
2355
|
+
/**
|
|
2356
|
+
* `optionsFrom` resolves the choices of a select/multiselect from another
|
|
2357
|
+
* field's current value (its selected ids); otherwise normalises the field's
|
|
2358
|
+
* own `choices` to `{ id, label }`.
|
|
2359
|
+
*/
|
|
2360
|
+
declare function resolveChoices(field: FieldSpec, values?: FormValues): ResolvedChoice[];
|
|
2361
|
+
/**
|
|
2362
|
+
* Ordered focus stops. text/secret/toggle yield one stop (`id = name`);
|
|
2363
|
+
* select/multiselect yield one stop PER choice (`id = name::choiceId`), so nav
|
|
2364
|
+
* moves linearly choice → choice → next field.
|
|
2365
|
+
*/
|
|
2366
|
+
declare function buildStops(fields: FieldSpec[], values?: FormValues): FocusStop[];
|
|
2367
|
+
/**
|
|
2368
|
+
* Validate `required` (any kind) and `multiselect` `min`. Pure — used by
|
|
2369
|
+
* {@link useFormNavigation}'s `commit()` and renderable directly into a {@link Form}.
|
|
2370
|
+
*/
|
|
2371
|
+
declare function validateForm(fields: FieldSpec[], values?: FormValues): FormValidation;
|
|
2372
|
+
/** What {@link useFormNavigation} returns — the intents the app wires keys to. */
|
|
2373
|
+
interface FormNavigation {
|
|
2374
|
+
/** Current focus-stop id (`name` or `name::choiceId`), or null when empty. */
|
|
2375
|
+
focusId: string | null;
|
|
2376
|
+
/** The current focus stop. */
|
|
2377
|
+
focusStop: FocusStop | null;
|
|
2378
|
+
/** All stops, in order. */
|
|
2379
|
+
stops: FocusStop[];
|
|
2380
|
+
/** Move to the next / previous control (linear). */
|
|
2381
|
+
next: () => void;
|
|
2382
|
+
prev: () => void;
|
|
2383
|
+
/** Jump focus to a field by name. */
|
|
2384
|
+
focusField: (name: string) => void;
|
|
2385
|
+
/** `␣` on the focused control — toggles a flag / selects a choice (honouring min/max). */
|
|
2386
|
+
toggle: () => void;
|
|
2387
|
+
/** Edit a `text` / `secret` field's value. */
|
|
2388
|
+
setText: (name: string, str: string) => void;
|
|
2389
|
+
/** Validate the current values. */
|
|
2390
|
+
commit: () => FormValidation;
|
|
2391
|
+
}
|
|
2392
|
+
/**
|
|
2393
|
+
* Headless navigation for a {@link Form} — the app owns the keys and calls these
|
|
2394
|
+
* intents; NO Form component reads a key (the rest of blink works the same way).
|
|
2395
|
+
* `onChange(nextValues)` receives every value edit.
|
|
2396
|
+
*
|
|
2397
|
+
* ```tsx
|
|
2398
|
+
* const nav = useFormNavigation({ fields, values, onChange: setValues });
|
|
2399
|
+
* useInput((input, key) => {
|
|
2400
|
+
* if (key.downArrow) nav.next();
|
|
2401
|
+
* else if (key.upArrow) nav.prev();
|
|
2402
|
+
* else if (input === ' ') nav.toggle();
|
|
2403
|
+
* });
|
|
2404
|
+
* return <Form fields={fields} values={values} focusId={nav.focusId} errors={errors} />;
|
|
2405
|
+
* ```
|
|
2406
|
+
*/
|
|
2407
|
+
declare function useFormNavigation({ fields, values, onChange, }: {
|
|
2408
|
+
fields: FieldSpec[];
|
|
2409
|
+
values?: FormValues;
|
|
2410
|
+
onChange?: (next: FormValues) => void;
|
|
2411
|
+
}): FormNavigation;
|
|
2412
|
+
interface FormProps {
|
|
2413
|
+
/** The fields, top to bottom. */
|
|
2414
|
+
fields: FieldSpec[];
|
|
2415
|
+
/** Current values, keyed by field name. */
|
|
2416
|
+
values?: FormValues;
|
|
2417
|
+
/** The focused stop id (from {@link useFormNavigation}'s `focusId`). */
|
|
2418
|
+
focusId?: string | null;
|
|
2419
|
+
/** Per-field error messages (e.g. from {@link validateForm}). */
|
|
2420
|
+
errors?: Record<string, string>;
|
|
2421
|
+
}
|
|
2422
|
+
/**
|
|
2423
|
+
* Render-only: `fields` + `values` + `focusId` (+ `errors`) in; glyphs, colours,
|
|
2424
|
+
* the focus fill, required markers, and error lines out. Reads no keys — pair it
|
|
2425
|
+
* with {@link useFormNavigation} and the app's `useInput`.
|
|
2426
|
+
*/
|
|
2427
|
+
declare function Form({ fields, values, focusId, errors }: FormProps): React.ReactElement;
|
|
2428
|
+
|
|
2429
|
+
export { ACTIONS, Banner, type BannerProps, type BannerTone, type BlinkContextValue, type BoxChars, type BoxStyleName, type BuiltinGlyphName, CLOUD, COMMON_DOMAINS, COMPANIES, type ChoiceInput, type CommonDomainName, Cursor, type CursorProps, DATABASES, DEFAULT_GLYPH_COLOR, DEFAULT_UNICODE, DEVINFRA, type DescriptionItem, DescriptionList, type DescriptionListProps, type DetectOptions, Dialog, type DialogAction, type DialogProps, type DialogTone, type Dimensions, EDITORS, FILES, FRAMEWORKS, type FieldKind, type FieldSpec, type FieldValue, type FocusStop, Footer, type FooterProps, Form, type FormNavigation, type FormProps, type FormValidation, type FormValues, GLYPH_PACKS, type GlyphColor, type GlyphInput, type GlyphVariants, Header, type HeaderProps, type HotkeyDef, type IconSet, Input, type InputProps, LANGUAGES, List, type ListNavigation, type ListProps, ListRow, type ListRowData, type ListRowProps, type ListSelection, type ListWindow, LogView, type LogViewProps, NERD_INDEX, NERD_INDEX_SOURCES, OS, PACKAGES, PALETTE_SLOTS, type Palette, type PaletteColor, Pane, type PaneProps, type PaneTone, type PaneVariant, ProgressBar, type ProgressBarProps, type ProgressItem, ProgressList, type ProgressListProps, type ProgressState, type ResolvedChoice, SOCIAL, SYSTEM, type SelectionMode, type SelectionName, type SemanticTokens, Spinner, type SpinnerOptions, type SpinnerProps, type StateIntent, type StateName, type Theme, type ThemeControls, type ThemeDefinition, type ThemeMeta, type ThemeMode, ThemeProvider, type ThemeProviderProps, type UseListNavigationOptions, type UseListSelectionOptions, type UseListWindowOptions, allThemes, blocks, blocksH, boxChars, boxStyles, buildStops, buildTokens, catppuccinMocha, cellWidth, computeWindow, defaultTheme, deriveAscii, detectIconSet, getTheme, glyph, glyphColor, gruvbox, hasGlyph, hasTheme, latte, listThemes, mocha, mochaTokens, navGlyphs, neutral, nf, nfChar, nfHas, nord, palettes, registerGlyph, registerGlyphs, registerNerdIndex, registerTheme, registeredNames, resolveChoices, selectionIntents, spinnerFor, spinnerFrames, stateGlyph, stateGlyphs, stateIntents, tokyonight, useBlink, useBlink$1 as useBlinkContext, useFormNavigation, useGlyph, useIconSet, useListNavigation, useListSelection, useListWindow, useSpinnerFrame, useStdoutDimensions, useTheme, useThemeControls, useTokens, validateForm };
|