@zakmandhro/bunti 0.1.4 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +65 -0
- package/README.md +145 -34
- package/dist/cli.d.ts +44 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +147 -0
- package/dist/cli.js.map +1 -0
- package/dist/colors.d.ts +144 -0
- package/dist/colors.d.ts.map +1 -0
- package/dist/colors.js +426 -0
- package/dist/colors.js.map +1 -0
- package/dist/components/Box.d.ts +11 -0
- package/dist/components/Box.d.ts.map +1 -0
- package/dist/components/Box.js +9 -0
- package/dist/components/Box.js.map +1 -0
- package/dist/components/Button.d.ts +24 -0
- package/dist/components/Button.d.ts.map +1 -0
- package/dist/components/Button.js +102 -0
- package/dist/components/Button.js.map +1 -0
- package/dist/components/Card.d.ts +33 -0
- package/dist/components/Card.d.ts.map +1 -0
- package/dist/components/Card.js +79 -0
- package/dist/components/Card.js.map +1 -0
- package/dist/components/Header.d.ts +23 -0
- package/dist/components/Header.d.ts.map +1 -0
- package/dist/components/Header.js +58 -0
- package/dist/components/Header.js.map +1 -0
- package/dist/components/Input.d.ts +25 -0
- package/dist/components/Input.d.ts.map +1 -0
- package/dist/components/Input.js +232 -0
- package/dist/components/Input.js.map +1 -0
- package/dist/components/Link.d.ts +21 -0
- package/dist/components/Link.d.ts.map +1 -0
- package/dist/components/Link.js +29 -0
- package/dist/components/Link.js.map +1 -0
- package/dist/components/Modal.d.ts +38 -0
- package/dist/components/Modal.d.ts.map +1 -0
- package/dist/components/Modal.js +87 -0
- package/dist/components/Modal.js.map +1 -0
- package/dist/components/Progress.d.ts +27 -0
- package/dist/components/Progress.d.ts.map +1 -0
- package/dist/components/Progress.js +39 -0
- package/dist/components/Progress.js.map +1 -0
- package/dist/components/Spinner.d.ts +19 -0
- package/dist/components/Spinner.d.ts.map +1 -0
- package/dist/components/Spinner.js +27 -0
- package/dist/components/Spinner.js.map +1 -0
- package/dist/components/index.d.ts +10 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +10 -0
- package/dist/components/index.js.map +1 -0
- package/dist/data/nf-glyphs.d.ts +11 -0
- package/dist/data/nf-glyphs.d.ts.map +1 -0
- package/dist/data/nf-glyphs.js +535 -0
- package/dist/data/nf-glyphs.js.map +1 -0
- package/dist/data/nf-names.d.ts +8 -0
- package/dist/data/nf-names.d.ts.map +1 -0
- package/dist/data/nf-names.js +2 -0
- package/dist/data/nf-names.js.map +1 -0
- package/dist/demo-registry.d.ts +27 -0
- package/dist/demo-registry.d.ts.map +1 -0
- package/dist/demo-registry.js +57 -0
- package/dist/demo-registry.js.map +1 -0
- package/dist/demos/2048.ts +743 -0
- package/dist/demos/animation.ts +246 -0
- package/dist/demos/demo-layout.ts +124 -0
- package/dist/demos/engine.ts +374 -0
- package/dist/demos/interaction.ts +91 -0
- package/dist/demos/login.ts +203 -0
- package/dist/demos/mission-control.ts +558 -0
- package/dist/demos/showcase.ts +224 -0
- package/dist/detect.d.ts +104 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/detect.js +227 -0
- package/dist/detect.js.map +1 -0
- package/dist/diagnostics.d.ts +73 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics.js +225 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/dsl/context.d.ts +11 -0
- package/dist/dsl/context.d.ts.map +1 -0
- package/dist/dsl/context.js +563 -0
- package/dist/dsl/context.js.map +1 -0
- package/dist/dsl/hooks.d.ts +12 -0
- package/dist/dsl/hooks.d.ts.map +1 -0
- package/dist/dsl/hooks.js +148 -0
- package/dist/dsl/hooks.js.map +1 -0
- package/dist/dsl/interaction.d.ts +16 -0
- package/dist/dsl/interaction.d.ts.map +1 -0
- package/dist/dsl/interaction.js +101 -0
- package/dist/dsl/interaction.js.map +1 -0
- package/dist/dsl/layers.d.ts +14 -0
- package/dist/dsl/layers.d.ts.map +1 -0
- package/dist/dsl/layers.js +87 -0
- package/dist/dsl/layers.js.map +1 -0
- package/dist/dsl/motion.d.ts +10 -0
- package/dist/dsl/motion.d.ts.map +1 -0
- package/dist/dsl/motion.js +150 -0
- package/dist/dsl/motion.js.map +1 -0
- package/dist/dsl/render.d.ts +22 -0
- package/dist/dsl/render.d.ts.map +1 -0
- package/dist/dsl/render.js +69 -0
- package/dist/dsl/render.js.map +1 -0
- package/dist/dsl/types.d.ts +510 -0
- package/dist/dsl/types.d.ts.map +1 -0
- package/dist/dsl/types.js +33 -0
- package/dist/dsl/types.js.map +1 -0
- package/dist/dsl.d.ts +18 -0
- package/dist/dsl.d.ts.map +1 -0
- package/dist/dsl.js +17 -0
- package/dist/dsl.js.map +1 -0
- package/dist/easing.d.ts +45 -0
- package/dist/easing.d.ts.map +1 -0
- package/dist/easing.js +121 -0
- package/dist/easing.js.map +1 -0
- package/dist/geometry.d.ts +77 -0
- package/dist/geometry.d.ts.map +1 -0
- package/dist/geometry.js +130 -0
- package/dist/geometry.js.map +1 -0
- package/dist/icons-full.d.ts +5 -0
- package/dist/icons-full.d.ts.map +1 -0
- package/dist/icons-full.js +31 -0
- package/dist/icons-full.js.map +1 -0
- package/dist/icons.d.ts +442 -0
- package/dist/icons.d.ts.map +1 -0
- package/dist/icons.js +314 -0
- package/dist/icons.js.map +1 -0
- package/dist/index.d.ts +228 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +46 -0
- package/dist/index.js.map +1 -0
- package/dist/input.d.ts +177 -0
- package/dist/input.d.ts.map +1 -0
- package/dist/input.js +533 -0
- package/dist/input.js.map +1 -0
- package/dist/layout.d.ts +194 -0
- package/dist/layout.d.ts.map +1 -0
- package/dist/layout.js +673 -0
- package/dist/layout.js.map +1 -0
- package/dist/render.d.ts +49 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +650 -0
- package/dist/render.js.map +1 -0
- package/dist/state.d.ts +226 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +130 -0
- package/dist/state.js.map +1 -0
- package/dist/theme.d.ts +129 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +167 -0
- package/dist/theme.js.map +1 -0
- package/dist/themes/catppuccin-mocha.d.ts +19 -0
- package/dist/themes/catppuccin-mocha.d.ts.map +1 -0
- package/dist/themes/catppuccin-mocha.js +36 -0
- package/dist/themes/catppuccin-mocha.js.map +1 -0
- package/dist/themes/dracula.d.ts +21 -0
- package/dist/themes/dracula.d.ts.map +1 -0
- package/dist/themes/dracula.js +37 -0
- package/dist/themes/dracula.js.map +1 -0
- package/dist/themes/github-light.d.ts +21 -0
- package/dist/themes/github-light.d.ts.map +1 -0
- package/dist/themes/github-light.js +36 -0
- package/dist/themes/github-light.js.map +1 -0
- package/dist/themes/index.d.ts +19 -0
- package/dist/themes/index.d.ts.map +1 -0
- package/dist/themes/index.js +17 -0
- package/dist/themes/index.js.map +1 -0
- package/dist/themes/nord.d.ts +23 -0
- package/dist/themes/nord.d.ts.map +1 -0
- package/dist/themes/nord.js +38 -0
- package/dist/themes/nord.js.map +1 -0
- package/dist/themes/one-dark-pro.d.ts +21 -0
- package/dist/themes/one-dark-pro.d.ts.map +1 -0
- package/dist/themes/one-dark-pro.js +36 -0
- package/dist/themes/one-dark-pro.js.map +1 -0
- package/dist/themes/tokyo-night.d.ts +20 -0
- package/dist/themes/tokyo-night.d.ts.map +1 -0
- package/dist/themes/tokyo-night.js +37 -0
- package/dist/themes/tokyo-night.js.map +1 -0
- package/dist/utils.d.ts +28 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +158 -0
- package/dist/utils.js.map +1 -0
- package/dist/vendor/colors.d.ts +45 -0
- package/dist/vendor/colors.d.ts.map +1 -0
- package/dist/vendor/colors.js +72 -0
- package/dist/vendor/colors.js.map +1 -0
- package/llms.txt +79 -0
- package/package.json +52 -11
- package/src/colors.ts +0 -255
- package/src/components/Button.ts +0 -104
- package/src/components/Card.ts +0 -53
- package/src/components/Header.ts +0 -65
- package/src/components/Input.ts +0 -124
- package/src/components/index.ts +0 -4
- package/src/data/glyphs.ts +0 -30
- package/src/detect.ts +0 -60
- package/src/dsl.ts +0 -661
- package/src/icons.ts +0 -186
- package/src/index.ts +0 -72
- package/src/layout.ts +0 -640
- package/src/render.ts +0 -302
- package/src/state.ts +0 -148
- package/src/utils.ts +0 -165
package/dist/state.d.ts
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bunti Functional Screen State & Initialization
|
|
3
|
+
*/
|
|
4
|
+
import { type ColorTier, type TerminalProfile } from './detect';
|
|
5
|
+
import type { HeldKeyTracker, InputTokenizer, KeyEvent, TerminalResponse } from './input';
|
|
6
|
+
import type { Theme, ThemeColor } from './theme';
|
|
7
|
+
/** 24-bit color triplet; each channel is 0-255. */
|
|
8
|
+
export interface RGB {
|
|
9
|
+
r: number;
|
|
10
|
+
g: number;
|
|
11
|
+
b: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* One terminal cell in the screen buffers. Drawing calls (blit/rect/setCell)
|
|
15
|
+
* take Partial<Cell> as their style argument.
|
|
16
|
+
*/
|
|
17
|
+
export interface Cell {
|
|
18
|
+
/** The glyph in this cell ('' marks a wide-glyph tail). */
|
|
19
|
+
char: string;
|
|
20
|
+
/** Foreground color: palette name, hex, ANSI-256 code, RGB, or theme token. */
|
|
21
|
+
fg?: string | number | RGB | ThemeColor;
|
|
22
|
+
/** Background color: palette name, hex, ANSI-256 code, RGB, or theme token. */
|
|
23
|
+
bg?: string | number | RGB | ThemeColor;
|
|
24
|
+
/** SGR bold. */
|
|
25
|
+
bold?: boolean;
|
|
26
|
+
/** Resolved fg ANSI code (set by setCell; do not set directly). */
|
|
27
|
+
fgCode?: string | number;
|
|
28
|
+
/** Resolved bg ANSI code (set by setCell; do not set directly). */
|
|
29
|
+
bgCode?: string | number;
|
|
30
|
+
/** Cell is a wide-glyph tail or transparent; the differ skips it. */
|
|
31
|
+
skip?: boolean;
|
|
32
|
+
/** Bypasses automatic emoji-to-Nerd-Font replacement. */
|
|
33
|
+
raw?: boolean;
|
|
34
|
+
/** SGR italic. */
|
|
35
|
+
italic?: boolean;
|
|
36
|
+
/** SGR underline. */
|
|
37
|
+
underline?: boolean;
|
|
38
|
+
/** SGR dim (faint). */
|
|
39
|
+
dim?: boolean;
|
|
40
|
+
/** SGR strikethrough. */
|
|
41
|
+
strike?: boolean;
|
|
42
|
+
}
|
|
43
|
+
/** A named clickable region registered via ctx.hitbox() (absolute cells). */
|
|
44
|
+
export interface Hitbox {
|
|
45
|
+
id: string;
|
|
46
|
+
x: number;
|
|
47
|
+
y: number;
|
|
48
|
+
width: number;
|
|
49
|
+
height: number;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The mutable per-screen state behind a render() session: double buffers,
|
|
53
|
+
* input queues, focus/hover bookkeeping, and component state. Exposed as
|
|
54
|
+
* `ctx.state` for advanced use; most apps only need the BuntiContext API.
|
|
55
|
+
*/
|
|
56
|
+
export interface ScreenState {
|
|
57
|
+
/** Terminal width in cells. */
|
|
58
|
+
width: number;
|
|
59
|
+
/** Terminal height in rows. */
|
|
60
|
+
height: number;
|
|
61
|
+
/** What is currently on the terminal (updated by flush). */
|
|
62
|
+
frontBuffer: Cell[];
|
|
63
|
+
/** The frame being drawn; diffed against frontBuffer on flush. */
|
|
64
|
+
backBuffer: Cell[];
|
|
65
|
+
/** Mouse column (0-based). */
|
|
66
|
+
mouseX: number;
|
|
67
|
+
/** Mouse row (0-based). */
|
|
68
|
+
mouseY: number;
|
|
69
|
+
/** Raw SGR button code of the most recent mouse event. */
|
|
70
|
+
mouseButton: number;
|
|
71
|
+
/** True while the left mouse button is held down. */
|
|
72
|
+
isMouseDown: boolean;
|
|
73
|
+
/** Terminal focus (drives the 5fps unfocused throttle). */
|
|
74
|
+
hasFocus: boolean;
|
|
75
|
+
/** First non-modified key name of the current frame (back-compat). */
|
|
76
|
+
lastKey?: string;
|
|
77
|
+
/** KeyEvents accumulated between frames; drained each tick into keys. */
|
|
78
|
+
keyQueue: KeyEvent[];
|
|
79
|
+
/** This frame's KeyEvents (drained from keyQueue by the render loop). */
|
|
80
|
+
keys: KeyEvent[];
|
|
81
|
+
/** Terminal probe replies (cursor position, DA1/DA2, DECRQM, DCS).
|
|
82
|
+
* Consumers drain this array; it never feeds the key queue. */
|
|
83
|
+
terminalResponses: TerminalResponse[];
|
|
84
|
+
/** Press-origin of the click emitted this frame (undefined otherwise). */
|
|
85
|
+
clickX?: number;
|
|
86
|
+
clickY?: number;
|
|
87
|
+
/** Press-origin of an in-flight left-button press (click emits here). */
|
|
88
|
+
mouseDownX?: number;
|
|
89
|
+
mouseDownY?: number;
|
|
90
|
+
/** Hover state per hitbox id as of its last evaluation. */
|
|
91
|
+
hoverStates: Map<string, boolean>;
|
|
92
|
+
/** Hitbox ids whose hover turned on this frame. */
|
|
93
|
+
hoverEntered: Set<string>;
|
|
94
|
+
/** Hitbox ids whose hover turned off this frame. */
|
|
95
|
+
hoverLeft: Set<string>;
|
|
96
|
+
/** Lazily created by applyInputToState. */
|
|
97
|
+
inputTokenizer?: InputTokenizer;
|
|
98
|
+
/** Lazily created by the input dispatch; injectable for tests. */
|
|
99
|
+
heldKeys?: HeldKeyTracker;
|
|
100
|
+
/** Id of the currently focused focusable, if any. */
|
|
101
|
+
focusedId?: string;
|
|
102
|
+
/** Tab-cycle registration order (rebuilt each frame). */
|
|
103
|
+
focusableIds: string[];
|
|
104
|
+
/** Clickable regions registered this frame via ctx.hitbox(). */
|
|
105
|
+
hitboxes: Map<string, Hitbox>;
|
|
106
|
+
/** Backing store for useState/useAsync and motion/animation clocks. */
|
|
107
|
+
componentState: Map<string, any>;
|
|
108
|
+
/** Positional counter for keyless hooks (reset each frame). */
|
|
109
|
+
hookCounter?: number;
|
|
110
|
+
/** Epoch ms when render() started (ctx.elapsedTime = now - startTime). */
|
|
111
|
+
startTime: number;
|
|
112
|
+
/** Timestamp of the previous render tick (loop() owns this). */
|
|
113
|
+
lastFrameAt?: number;
|
|
114
|
+
/** Render tick counter (loop() owns this; exposed as ctx.frame). */
|
|
115
|
+
frameCount?: number;
|
|
116
|
+
/** Ms since the previous tick, clamped to 100 (exposed as ctx.dt). */
|
|
117
|
+
dt?: number;
|
|
118
|
+
/** SGR run-tracking across flushes (differ internals). */
|
|
119
|
+
lastFg?: any;
|
|
120
|
+
lastBg?: any;
|
|
121
|
+
lastBold?: boolean;
|
|
122
|
+
lastItalic?: boolean;
|
|
123
|
+
lastUnderline?: boolean;
|
|
124
|
+
lastDim?: boolean;
|
|
125
|
+
lastStrike?: boolean;
|
|
126
|
+
/** Last OSC 22 mouse-cursor shape emitted ('pointer' over hitboxes). */
|
|
127
|
+
pointerShape?: 'default' | 'pointer';
|
|
128
|
+
/** Forces the next flush to clear and repaint everything. */
|
|
129
|
+
needsFullRedraw?: boolean;
|
|
130
|
+
/** Optional screen identifier. */
|
|
131
|
+
id?: string;
|
|
132
|
+
/** The options render() was called with. */
|
|
133
|
+
options: ScreenOptions;
|
|
134
|
+
/** Stops the active render loop (what ctx.requestStop() calls). */
|
|
135
|
+
requestStop?: () => void;
|
|
136
|
+
/** True once the loop has been stopped. */
|
|
137
|
+
isStopped?: boolean;
|
|
138
|
+
/** True once the terminal has been restored (teardown ran). */
|
|
139
|
+
isRestored?: boolean;
|
|
140
|
+
/** True while a resize is settling (frames are skipped). */
|
|
141
|
+
isResizing?: boolean;
|
|
142
|
+
/** Epoch ms when the current resize debounce window ends. */
|
|
143
|
+
resizeSettlesAt?: number;
|
|
144
|
+
/** Active semantic theme (swapped live via ctx.setTheme). */
|
|
145
|
+
theme?: Theme;
|
|
146
|
+
/** Env-detected terminal profile (detected once per createScreenState). */
|
|
147
|
+
terminal?: TerminalProfile;
|
|
148
|
+
/**
|
|
149
|
+
* Effective synchronized-output flag for this screen. False only when the
|
|
150
|
+
* terminal was positively identified as lacking mode 2026 support;
|
|
151
|
+
* unknown terminals keep the sync wrap (harmlessly ignored when
|
|
152
|
+
* unsupported) to preserve pre-detection behavior.
|
|
153
|
+
*/
|
|
154
|
+
syncOutput?: boolean;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Options for render(). Interactive apps typically want
|
|
158
|
+
* `{ keyboard: true, mouse: true, alternateBuffer: true, hideCursor: true }`.
|
|
159
|
+
*/
|
|
160
|
+
export interface ScreenOptions {
|
|
161
|
+
/** Target frames per second (default 60; throttles to 5 when unfocused). */
|
|
162
|
+
fps?: number;
|
|
163
|
+
/** Enables SGR mouse tracking (hover/click/wheel, ctx.hitbox). */
|
|
164
|
+
mouse?: boolean;
|
|
165
|
+
/** Enables terminal focus-in/out tracking (drives the fps throttle). */
|
|
166
|
+
focus?: boolean;
|
|
167
|
+
/**
|
|
168
|
+
* Enables keyboard input (raw-mode stdin -> ctx.lastKey / ctx.keys).
|
|
169
|
+
* Requires a real TTY: piped stdin is ignored.
|
|
170
|
+
*/
|
|
171
|
+
keyboard?: boolean;
|
|
172
|
+
/** Renders on the alternate screen buffer and restores the shell on exit. */
|
|
173
|
+
alternateBuffer?: boolean;
|
|
174
|
+
/** Hides the terminal cursor while rendering (restored on exit). */
|
|
175
|
+
hideCursor?: boolean;
|
|
176
|
+
/** Forces the icon tier: true = Nerd Font glyphs, false = ASCII fallbacks. */
|
|
177
|
+
nerdFont?: boolean;
|
|
178
|
+
/** How long a resize must settle before repainting (default 1ms). */
|
|
179
|
+
resizeDebounceMs?: number;
|
|
180
|
+
/** Bare-ESC disambiguation window for the input tokenizer (default 30ms). */
|
|
181
|
+
escTimeoutMs?: number;
|
|
182
|
+
/** Held-key expiry window for ctx.isKeyHeld (default 150ms). */
|
|
183
|
+
holdWindowMs?: number;
|
|
184
|
+
/** Default text color for cells without an explicit fg. */
|
|
185
|
+
defaultFg?: string | number | RGB | ThemeColor;
|
|
186
|
+
/** Semantic theme exposed as ctx.theme (defaults to darkTheme). */
|
|
187
|
+
theme?: Theme;
|
|
188
|
+
/** Forces the color capability tier (otherwise detected from env). */
|
|
189
|
+
colorTier?: ColorTier;
|
|
190
|
+
/**
|
|
191
|
+
* Called when the render callback throws during a frame.
|
|
192
|
+
* Return `'continue'` to keep the loop alive (app-level error boundary);
|
|
193
|
+
* any other return tears the screen down, restores the terminal, and
|
|
194
|
+
* rejects the promise returned by render()/loop() with the error.
|
|
195
|
+
*/
|
|
196
|
+
onError?: (err: unknown) => void | 'continue';
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Creates a fresh ScreenState based on the terminal dimensions and options.
|
|
200
|
+
*/
|
|
201
|
+
export declare function createScreenState(options?: ScreenOptions): ScreenState;
|
|
202
|
+
/**
|
|
203
|
+
* Resizes the front/back buffers to match the new terminal size.
|
|
204
|
+
*/
|
|
205
|
+
export declare function resizeScreen(state: ScreenState): void;
|
|
206
|
+
/**
|
|
207
|
+
* Clears the back buffer to the base wallpaper or a given cell style.
|
|
208
|
+
*/
|
|
209
|
+
export declare function clearBackBuffer(state: ScreenState): void;
|
|
210
|
+
/** Raw ANSI escape sequences used by the renderer (advanced use). */
|
|
211
|
+
export declare const ANSI: {
|
|
212
|
+
reset: string;
|
|
213
|
+
clear: string;
|
|
214
|
+
home: string;
|
|
215
|
+
alternateBuffer: string;
|
|
216
|
+
mainBuffer: string;
|
|
217
|
+
hideCursor: string;
|
|
218
|
+
showCursor: string;
|
|
219
|
+
mouseEnable: string;
|
|
220
|
+
mouseDisable: string;
|
|
221
|
+
focusEnable: string;
|
|
222
|
+
focusDisable: string;
|
|
223
|
+
syncStart: string;
|
|
224
|
+
syncEnd: string;
|
|
225
|
+
};
|
|
226
|
+
//# sourceMappingURL=state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"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"}
|
package/dist/state.js
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bunti Functional Screen State & Initialization
|
|
3
|
+
*/
|
|
4
|
+
import { identifyTerminal, setColorTier, } from './detect';
|
|
5
|
+
/**
|
|
6
|
+
* Creates a fresh ScreenState based on the terminal dimensions and options.
|
|
7
|
+
*/
|
|
8
|
+
export function createScreenState(options = {}) {
|
|
9
|
+
if (options.colorTier !== undefined)
|
|
10
|
+
setColorTier(options.colorTier);
|
|
11
|
+
const terminal = identifyTerminal();
|
|
12
|
+
const width = process.stdout.columns || 80;
|
|
13
|
+
const height = process.stdout.rows || 24;
|
|
14
|
+
const size = width * height;
|
|
15
|
+
const state = {
|
|
16
|
+
width,
|
|
17
|
+
height,
|
|
18
|
+
frontBuffer: Array.from({ length: size }, () => ({
|
|
19
|
+
char: '',
|
|
20
|
+
fg: undefined,
|
|
21
|
+
bg: undefined,
|
|
22
|
+
fgCode: undefined,
|
|
23
|
+
bgCode: undefined,
|
|
24
|
+
skip: false,
|
|
25
|
+
})),
|
|
26
|
+
backBuffer: Array.from({ length: size }, () => ({
|
|
27
|
+
char: ' ',
|
|
28
|
+
fg: undefined,
|
|
29
|
+
bg: undefined,
|
|
30
|
+
fgCode: undefined,
|
|
31
|
+
bgCode: undefined,
|
|
32
|
+
skip: false,
|
|
33
|
+
})),
|
|
34
|
+
mouseX: 0,
|
|
35
|
+
mouseY: 0,
|
|
36
|
+
mouseButton: 0,
|
|
37
|
+
isMouseDown: false,
|
|
38
|
+
hasFocus: true,
|
|
39
|
+
keyQueue: [],
|
|
40
|
+
keys: [],
|
|
41
|
+
terminalResponses: [],
|
|
42
|
+
hoverStates: new Map(),
|
|
43
|
+
hoverEntered: new Set(),
|
|
44
|
+
hoverLeft: new Set(),
|
|
45
|
+
focusableIds: [],
|
|
46
|
+
hitboxes: new Map(),
|
|
47
|
+
componentState: new Map(),
|
|
48
|
+
startTime: Date.now(),
|
|
49
|
+
options,
|
|
50
|
+
theme: options.theme,
|
|
51
|
+
terminal,
|
|
52
|
+
// Judgment call: unknown terminals keep today's always-sync behavior
|
|
53
|
+
// (mode 2026 is ignored when unsupported); only positively identified
|
|
54
|
+
// non-supporting terminals skip the wrap.
|
|
55
|
+
syncOutput: terminal.app === 'unknown' ? true : terminal.syncOutput,
|
|
56
|
+
};
|
|
57
|
+
return state;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Resizes the front/back buffers to match the new terminal size.
|
|
61
|
+
*/
|
|
62
|
+
export function resizeScreen(state) {
|
|
63
|
+
const width = process.stdout.columns || 80;
|
|
64
|
+
const height = process.stdout.rows || 24;
|
|
65
|
+
const size = width * height;
|
|
66
|
+
state.width = width;
|
|
67
|
+
state.height = height;
|
|
68
|
+
state.frontBuffer = Array.from({ length: size }, () => ({
|
|
69
|
+
char: '',
|
|
70
|
+
fg: undefined,
|
|
71
|
+
bg: undefined,
|
|
72
|
+
fgCode: undefined,
|
|
73
|
+
bgCode: undefined,
|
|
74
|
+
skip: false,
|
|
75
|
+
}));
|
|
76
|
+
state.backBuffer = Array.from({ length: size }, () => ({
|
|
77
|
+
char: ' ',
|
|
78
|
+
fg: undefined,
|
|
79
|
+
bg: undefined,
|
|
80
|
+
fgCode: undefined,
|
|
81
|
+
bgCode: undefined,
|
|
82
|
+
skip: false,
|
|
83
|
+
}));
|
|
84
|
+
state.lastFg = undefined;
|
|
85
|
+
state.lastBg = undefined;
|
|
86
|
+
state.lastBold = false;
|
|
87
|
+
state.lastItalic = false;
|
|
88
|
+
state.lastUnderline = false;
|
|
89
|
+
state.lastDim = false;
|
|
90
|
+
state.lastStrike = false;
|
|
91
|
+
state.needsFullRedraw = true;
|
|
92
|
+
state.isResizing = true;
|
|
93
|
+
state.resizeSettlesAt = Date.now() + (state.options.resizeDebounceMs ?? 1);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Clears the back buffer to the base wallpaper or a given cell style.
|
|
97
|
+
*/
|
|
98
|
+
export function clearBackBuffer(state) {
|
|
99
|
+
for (let i = 0; i < state.backBuffer.length; i++) {
|
|
100
|
+
const cell = state.backBuffer[i];
|
|
101
|
+
cell.char = ' ';
|
|
102
|
+
cell.fg = undefined;
|
|
103
|
+
cell.bg = undefined;
|
|
104
|
+
cell.fgCode = undefined;
|
|
105
|
+
cell.bgCode = undefined;
|
|
106
|
+
cell.bold = false;
|
|
107
|
+
cell.italic = false;
|
|
108
|
+
cell.underline = false;
|
|
109
|
+
cell.dim = false;
|
|
110
|
+
cell.strike = false;
|
|
111
|
+
cell.skip = false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/** Raw ANSI escape sequences used by the renderer (advanced use). */
|
|
115
|
+
export const ANSI = {
|
|
116
|
+
reset: '\x1b[0m',
|
|
117
|
+
clear: '\x1b[2J',
|
|
118
|
+
home: '\x1b[H',
|
|
119
|
+
alternateBuffer: '\x1b[?1049h',
|
|
120
|
+
mainBuffer: '\x1b[?1049l',
|
|
121
|
+
hideCursor: '\x1b[?25l',
|
|
122
|
+
showCursor: '\x1b[?25h',
|
|
123
|
+
mouseEnable: '\x1b[?1003h\x1b[?1006h',
|
|
124
|
+
mouseDisable: '\x1b[?1003l\x1b[?1006l',
|
|
125
|
+
focusEnable: '\x1b[?1004h',
|
|
126
|
+
focusDisable: '\x1b[?1004l',
|
|
127
|
+
syncStart: '\x1b[?2026h',
|
|
128
|
+
syncEnd: '\x1b[?2026l',
|
|
129
|
+
};
|
|
130
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAEL,gBAAgB,EAChB,YAAY,GAEb,MAAM,UAAU,CAAC;AA+MlB;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,UAAyB,EAAE;IAC3D,IAAI,OAAO,CAAC,SAAS,KAAK,SAAS;QAAE,YAAY,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAErE,MAAM,QAAQ,GAAG,gBAAgB,EAAE,CAAC;IACpC,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC;IAC3C,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;IACzC,MAAM,IAAI,GAAG,KAAK,GAAG,MAAM,CAAC;IAE5B,MAAM,KAAK,GAAgB;QACzB,KAAK;QACL,MAAM;QACN,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;YAC/C,IAAI,EAAE,EAAE;YACR,EAAE,EAAE,SAAS;YACb,EAAE,EAAE,SAAS;YACb,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE,SAAS;YACjB,IAAI,EAAE,KAAK;SACZ,CAAC,CAAC;QACH,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;YAC9C,IAAI,EAAE,GAAG;YACT,EAAE,EAAE,SAAS;YACb,EAAE,EAAE,SAAS;YACb,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE,SAAS;YACjB,IAAI,EAAE,KAAK;SACZ,CAAC,CAAC;QACH,MAAM,EAAE,CAAC;QACT,MAAM,EAAE,CAAC;QACT,WAAW,EAAE,CAAC;QACd,WAAW,EAAE,KAAK;QAClB,QAAQ,EAAE,IAAI;QACd,QAAQ,EAAE,EAAE;QACZ,IAAI,EAAE,EAAE;QACR,iBAAiB,EAAE,EAAE;QACrB,WAAW,EAAE,IAAI,GAAG,EAAE;QACtB,YAAY,EAAE,IAAI,GAAG,EAAE;QACvB,SAAS,EAAE,IAAI,GAAG,EAAE;QACpB,YAAY,EAAE,EAAE;QAChB,QAAQ,EAAE,IAAI,GAAG,EAAE;QACnB,cAAc,EAAE,IAAI,GAAG,EAAE;QACzB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;QACrB,OAAO;QACP,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,QAAQ;QACR,qEAAqE;QACrE,sEAAsE;QACtE,0CAA0C;QAC1C,UAAU,EAAE,QAAQ,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU;KACpE,CAAC;IAEF,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,KAAkB;IAC7C,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC;IAC3C,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;IACzC,MAAM,IAAI,GAAG,KAAK,GAAG,MAAM,CAAC;IAE5B,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC;IACpB,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC;IACtB,KAAK,CAAC,WAAW,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;QACtD,IAAI,EAAE,EAAE;QACR,EAAE,EAAE,SAAS;QACb,EAAE,EAAE,SAAS;QACb,MAAM,EAAE,SAAS;QACjB,MAAM,EAAE,SAAS;QACjB,IAAI,EAAE,KAAK;KACZ,CAAC,CAAC,CAAC;IACJ,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;QACrD,IAAI,EAAE,GAAG;QACT,EAAE,EAAE,SAAS;QACb,EAAE,EAAE,SAAS;QACb,MAAM,EAAE,SAAS;QACjB,MAAM,EAAE,SAAS;QACjB,IAAI,EAAE,KAAK;KACZ,CAAC,CAAC,CAAC;IACJ,KAAK,CAAC,MAAM,GAAG,SAAS,CAAC;IACzB,KAAK,CAAC,MAAM,GAAG,SAAS,CAAC;IACzB,KAAK,CAAC,QAAQ,GAAG,KAAK,CAAC;IACvB,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC;IACzB,KAAK,CAAC,aAAa,GAAG,KAAK,CAAC;IAC5B,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC;IACtB,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC;IACzB,KAAK,CAAC,eAAe,GAAG,IAAI,CAAC;IAC7B,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC;IACxB,KAAK,CAAC,eAAe,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,gBAAgB,IAAI,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,KAAkB;IAChD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACjD,MAAM,IAAI,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC,CAAE,CAAC;QAClC,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC;QAChB,IAAI,CAAC,EAAE,GAAG,SAAS,CAAC;QACpB,IAAI,CAAC,EAAE,GAAG,SAAS,CAAC;QACpB,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;QACxB,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC;QAClB,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;QACvB,IAAI,CAAC,GAAG,GAAG,KAAK,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC;IACpB,CAAC;AACH,CAAC;AAED,qEAAqE;AACrE,MAAM,CAAC,MAAM,IAAI,GAAG;IAClB,KAAK,EAAE,SAAS;IAChB,KAAK,EAAE,SAAS;IAChB,IAAI,EAAE,QAAQ;IACd,eAAe,EAAE,aAAa;IAC9B,UAAU,EAAE,aAAa;IACzB,UAAU,EAAE,WAAW;IACvB,UAAU,EAAE,WAAW;IACvB,WAAW,EAAE,wBAAwB;IACrC,YAAY,EAAE,wBAAwB;IACtC,WAAW,EAAE,aAAa;IAC1B,YAAY,EAAE,aAAa;IAC3B,SAAS,EAAE,aAAa;IACxB,OAAO,EAAE,aAAa;CACvB,CAAC"}
|
package/dist/theme.d.ts
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bunti Semantic Theme System
|
|
3
|
+
*
|
|
4
|
+
* Themes are sets of semantic color tokens (background, primary, danger, ...)
|
|
5
|
+
* rather than hardcoded colors. Every token is a ThemeColor: callable as an
|
|
6
|
+
* fg-styler (`theme.primary('text')` returns an ANSI-colored string) AND
|
|
7
|
+
* usable as plain color data anywhere a color is accepted (bgColor,
|
|
8
|
+
* borderColor, wallpaper, gradient stops) via its `.rgb` / `.hex` payload.
|
|
9
|
+
*
|
|
10
|
+
* `createTheme(partial)` fills missing tokens by derivation so sparse inputs
|
|
11
|
+
* (e.g. a converted VS Code theme with only a handful of colors) still
|
|
12
|
+
* produce a coherent theme.
|
|
13
|
+
*/
|
|
14
|
+
import { type ColorValue } from './colors';
|
|
15
|
+
import type { RGB } from './state';
|
|
16
|
+
export type ThemeMode = 'dark' | 'light';
|
|
17
|
+
/** Any value accepted as a theme token: name, hex, ANSI code, RGB, ThemeColor. */
|
|
18
|
+
export type ThemeColorInput = ColorValue;
|
|
19
|
+
/**
|
|
20
|
+
* A semantic theme color. Callable as a foreground styler —
|
|
21
|
+
* `theme.primary('Mission Control')` returns an fg-styled ANSI string — and
|
|
22
|
+
* carries `.rgb` / `.hex` so it is accepted anywhere a color value goes.
|
|
23
|
+
*/
|
|
24
|
+
export type ThemeColor = ((text: string | number) => string) & {
|
|
25
|
+
/** Resolved 24-bit color value. */
|
|
26
|
+
readonly rgb: RGB;
|
|
27
|
+
/** Resolved #rrggbb hex string. */
|
|
28
|
+
readonly hex: string;
|
|
29
|
+
};
|
|
30
|
+
/** The semantic token names every Theme carries. */
|
|
31
|
+
export declare const THEME_TOKENS: readonly ["background", "surface", "surfaceRaised", "foreground", "muted", "primary", "onPrimary", "accent", "border", "focus", "selection", "success", "warning", "danger", "info"];
|
|
32
|
+
export type ThemeToken = (typeof THEME_TOKENS)[number];
|
|
33
|
+
/**
|
|
34
|
+
* A complete semantic theme. Build one with `createTheme()`, or import a
|
|
35
|
+
* preset from '@zakmandhro/bunti/themes' (dracula, tokyoNight, nord, ...).
|
|
36
|
+
*/
|
|
37
|
+
export interface Theme {
|
|
38
|
+
/** Display name (e.g. 'dracula'). */
|
|
39
|
+
name: string;
|
|
40
|
+
/** 'dark' or 'light' — components use it for mode-aware hover shifts. */
|
|
41
|
+
mode: ThemeMode;
|
|
42
|
+
/** App/screen background (wallpaper). */
|
|
43
|
+
background: ThemeColor;
|
|
44
|
+
/** Panel/card background, one step above `background`. */
|
|
45
|
+
surface: ThemeColor;
|
|
46
|
+
/** Elevated surface (modals, popovers), one step above `surface`. */
|
|
47
|
+
surfaceRaised: ThemeColor;
|
|
48
|
+
/** Default body text color. */
|
|
49
|
+
foreground: ThemeColor;
|
|
50
|
+
/** De-emphasized text (labels, hints, disabled). */
|
|
51
|
+
muted: ThemeColor;
|
|
52
|
+
/** Brand/action color (primary buttons, highlights). */
|
|
53
|
+
primary: ThemeColor;
|
|
54
|
+
/** Text rendered on top of `primary` fills. */
|
|
55
|
+
onPrimary: ThemeColor;
|
|
56
|
+
/** Secondary flourish color (links, icons, titles). */
|
|
57
|
+
accent: ThemeColor;
|
|
58
|
+
/** Default box/card border color. */
|
|
59
|
+
border: ThemeColor;
|
|
60
|
+
/** Focus ring / active-element color. */
|
|
61
|
+
focus: ThemeColor;
|
|
62
|
+
/** Selected-row/selection background wash. */
|
|
63
|
+
selection: ThemeColor;
|
|
64
|
+
/** Positive status. */
|
|
65
|
+
success: ThemeColor;
|
|
66
|
+
/** Cautionary status. */
|
|
67
|
+
warning: ThemeColor;
|
|
68
|
+
/** Destructive/error status. */
|
|
69
|
+
danger: ThemeColor;
|
|
70
|
+
/** Neutral-informational status. */
|
|
71
|
+
info: ThemeColor;
|
|
72
|
+
/** Optional named gradient stop lists (e.g. for wallpapers). */
|
|
73
|
+
gradients?: Record<string, (string | RGB)[]>;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Sparse theme spec: any subset of tokens; the rest are derived by
|
|
77
|
+
* `createTheme()`.
|
|
78
|
+
*/
|
|
79
|
+
export type ThemeInput = Partial<Record<ThemeToken, ThemeColorInput>> & {
|
|
80
|
+
name?: string;
|
|
81
|
+
mode?: ThemeMode;
|
|
82
|
+
gradients?: Record<string, (string | RGB)[]>;
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Creates a ThemeColor from any color value: a function that fg-styles text,
|
|
86
|
+
* branded and carrying `.rgb` + `.hex`. ThemeColor inputs pass through
|
|
87
|
+
* unchanged.
|
|
88
|
+
*/
|
|
89
|
+
export declare function themeColor(input: ThemeColorInput): ThemeColor;
|
|
90
|
+
/**
|
|
91
|
+
* Returns true when the value is a complete Theme (all tokens are branded
|
|
92
|
+
* ThemeColors), as opposed to a sparse ThemeInput.
|
|
93
|
+
*/
|
|
94
|
+
export declare function isTheme(value: unknown): value is Theme;
|
|
95
|
+
/**
|
|
96
|
+
* Builds a complete Theme from a sparse spec. Missing tokens are derived:
|
|
97
|
+
*
|
|
98
|
+
* - mode: from background luminance when a background is given, else 'dark'.
|
|
99
|
+
* - background: #0f0f23 (dark) / #f4f5f9 (light).
|
|
100
|
+
* - surface / surfaceRaised: background shifted 8% / 16% toward white in
|
|
101
|
+
* dark mode, toward black in light mode.
|
|
102
|
+
* - foreground: WCAG auto-contrast vs background, softened 8% toward it.
|
|
103
|
+
* - muted: foreground mixed 45% toward background.
|
|
104
|
+
* - primary: bunti-blue #3bbce1 (dark) / #0d7ea6 (light).
|
|
105
|
+
* - onPrimary: WCAG auto-contrast (pure black/white) vs primary.
|
|
106
|
+
* - accent: primary shifted 25% toward white (dark) / black (light).
|
|
107
|
+
* - border: foreground mixed 70% toward background.
|
|
108
|
+
* - focus: primary.
|
|
109
|
+
* - selection: primary mixed 70% toward background.
|
|
110
|
+
* - success/warning/danger/info: PALETTE-derived defaults per mode.
|
|
111
|
+
*/
|
|
112
|
+
export declare function createTheme(input?: ThemeInput): Theme;
|
|
113
|
+
/**
|
|
114
|
+
* Normalizes a Theme | ThemeInput. Full Themes pass through. Sparse inputs
|
|
115
|
+
* overlay `base` when given (used by `ctx.themed`), otherwise they are
|
|
116
|
+
* completed by `createTheme` derivation (used by `ctx.setTheme`).
|
|
117
|
+
*/
|
|
118
|
+
export declare function resolveTheme(input: Theme | ThemeInput, base?: Theme): Theme;
|
|
119
|
+
/**
|
|
120
|
+
* Built-in dark theme, derived from Bunti's PALETTE aesthetic
|
|
121
|
+
* (midnight / silver / bunti-blue / plasma).
|
|
122
|
+
*/
|
|
123
|
+
export declare const darkTheme: Theme;
|
|
124
|
+
/**
|
|
125
|
+
* Built-in light theme: the PALETTE aesthetic re-balanced for light
|
|
126
|
+
* backgrounds (deepened primary/semantic colors for WCAG contrast).
|
|
127
|
+
*/
|
|
128
|
+
export declare const lightTheme: Theme;
|
|
129
|
+
//# sourceMappingURL=theme.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../src/theme.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EACL,KAAK,UAAU,EAShB,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,SAAS,CAAC;AAEnC,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;AAEzC,kFAAkF;AAClF,MAAM,MAAM,eAAe,GAAG,UAAU,CAAC;AAEzC;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,KAAK,MAAM,CAAC,GAAG;IAC7D,mCAAmC;IACnC,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC;IAClB,mCAAmC;IACnC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,oDAAoD;AACpD,eAAO,MAAM,YAAY,sLAgBf,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvD;;;GAGG;AACH,MAAM,WAAW,KAAK;IACpB,qCAAqC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,IAAI,EAAE,SAAS,CAAC;IAChB,yCAAyC;IACzC,UAAU,EAAE,UAAU,CAAC;IACvB,0DAA0D;IAC1D,OAAO,EAAE,UAAU,CAAC;IACpB,qEAAqE;IACrE,aAAa,EAAE,UAAU,CAAC;IAC1B,+BAA+B;IAC/B,UAAU,EAAE,UAAU,CAAC;IACvB,oDAAoD;IACpD,KAAK,EAAE,UAAU,CAAC;IAClB,wDAAwD;IACxD,OAAO,EAAE,UAAU,CAAC;IACpB,+CAA+C;IAC/C,SAAS,EAAE,UAAU,CAAC;IACtB,uDAAuD;IACvD,MAAM,EAAE,UAAU,CAAC;IACnB,qCAAqC;IACrC,MAAM,EAAE,UAAU,CAAC;IACnB,yCAAyC;IACzC,KAAK,EAAE,UAAU,CAAC;IAClB,8CAA8C;IAC9C,SAAS,EAAE,UAAU,CAAC;IACtB,uBAAuB;IACvB,OAAO,EAAE,UAAU,CAAC;IACpB,yBAAyB;IACzB,OAAO,EAAE,UAAU,CAAC;IACpB,gCAAgC;IAChC,MAAM,EAAE,UAAU,CAAC;IACnB,oCAAoC;IACpC,IAAI,EAAE,UAAU,CAAC;IACjB,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC;CAC9C;AAED;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC,GAAG;IACtE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC;CAC9C,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,UAAU,CAW7D;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,KAAK,CAOtD;AAKD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,WAAW,CAAC,KAAK,GAAE,UAAe,GAAG,KAAK,CAkEzD;AASD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,UAAU,EAAE,IAAI,CAAC,EAAE,KAAK,GAAG,KAAK,CAK3E;AAED;;;GAGG;AACH,eAAO,MAAM,SAAS,EAAE,KAOtB,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,UAAU,EAAE,KAOvB,CAAC"}
|
package/dist/theme.js
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bunti Semantic Theme System
|
|
3
|
+
*
|
|
4
|
+
* Themes are sets of semantic color tokens (background, primary, danger, ...)
|
|
5
|
+
* rather than hardcoded colors. Every token is a ThemeColor: callable as an
|
|
6
|
+
* fg-styler (`theme.primary('text')` returns an ANSI-colored string) AND
|
|
7
|
+
* usable as plain color data anywhere a color is accepted (bgColor,
|
|
8
|
+
* borderColor, wallpaper, gradient stops) via its `.rgb` / `.hex` payload.
|
|
9
|
+
*
|
|
10
|
+
* `createTheme(partial)` fills missing tokens by derivation so sparse inputs
|
|
11
|
+
* (e.g. a converted VS Code theme with only a handful of colors) still
|
|
12
|
+
* produce a coherent theme.
|
|
13
|
+
*/
|
|
14
|
+
import { contrastText, fade, fg, isThemeColor, relativeLuminance, resolveColorToRGB, rgbToHex, THEME_COLOR, } from './colors';
|
|
15
|
+
/** The semantic token names every Theme carries. */
|
|
16
|
+
export const THEME_TOKENS = [
|
|
17
|
+
'background',
|
|
18
|
+
'surface',
|
|
19
|
+
'surfaceRaised',
|
|
20
|
+
'foreground',
|
|
21
|
+
'muted',
|
|
22
|
+
'primary',
|
|
23
|
+
'onPrimary',
|
|
24
|
+
'accent',
|
|
25
|
+
'border',
|
|
26
|
+
'focus',
|
|
27
|
+
'selection',
|
|
28
|
+
'success',
|
|
29
|
+
'warning',
|
|
30
|
+
'danger',
|
|
31
|
+
'info',
|
|
32
|
+
];
|
|
33
|
+
/**
|
|
34
|
+
* Creates a ThemeColor from any color value: a function that fg-styles text,
|
|
35
|
+
* branded and carrying `.rgb` + `.hex`. ThemeColor inputs pass through
|
|
36
|
+
* unchanged.
|
|
37
|
+
*/
|
|
38
|
+
export function themeColor(input) {
|
|
39
|
+
if (isThemeColor(input))
|
|
40
|
+
return input;
|
|
41
|
+
const rgb = Object.freeze(resolveColorToRGB(input));
|
|
42
|
+
const color = ((text) => fg(rgb, String(text)));
|
|
43
|
+
Object.defineProperties(color, {
|
|
44
|
+
rgb: { value: rgb, enumerable: true },
|
|
45
|
+
hex: { value: rgbToHex(rgb), enumerable: true },
|
|
46
|
+
[THEME_COLOR]: { value: true },
|
|
47
|
+
});
|
|
48
|
+
return color;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Returns true when the value is a complete Theme (all tokens are branded
|
|
52
|
+
* ThemeColors), as opposed to a sparse ThemeInput.
|
|
53
|
+
*/
|
|
54
|
+
export function isTheme(value) {
|
|
55
|
+
if (typeof value !== 'object' || value === null)
|
|
56
|
+
return false;
|
|
57
|
+
const candidate = value;
|
|
58
|
+
return (typeof candidate.name === 'string' &&
|
|
59
|
+
THEME_TOKENS.every((token) => isThemeColor(candidate[token])));
|
|
60
|
+
}
|
|
61
|
+
const WHITE = { r: 255, g: 255, b: 255 };
|
|
62
|
+
const BLACK = { r: 0, g: 0, b: 0 };
|
|
63
|
+
/**
|
|
64
|
+
* Builds a complete Theme from a sparse spec. Missing tokens are derived:
|
|
65
|
+
*
|
|
66
|
+
* - mode: from background luminance when a background is given, else 'dark'.
|
|
67
|
+
* - background: #0f0f23 (dark) / #f4f5f9 (light).
|
|
68
|
+
* - surface / surfaceRaised: background shifted 8% / 16% toward white in
|
|
69
|
+
* dark mode, toward black in light mode.
|
|
70
|
+
* - foreground: WCAG auto-contrast vs background, softened 8% toward it.
|
|
71
|
+
* - muted: foreground mixed 45% toward background.
|
|
72
|
+
* - primary: bunti-blue #3bbce1 (dark) / #0d7ea6 (light).
|
|
73
|
+
* - onPrimary: WCAG auto-contrast (pure black/white) vs primary.
|
|
74
|
+
* - accent: primary shifted 25% toward white (dark) / black (light).
|
|
75
|
+
* - border: foreground mixed 70% toward background.
|
|
76
|
+
* - focus: primary.
|
|
77
|
+
* - selection: primary mixed 70% toward background.
|
|
78
|
+
* - success/warning/danger/info: PALETTE-derived defaults per mode.
|
|
79
|
+
*/
|
|
80
|
+
export function createTheme(input = {}) {
|
|
81
|
+
const mode = input.mode ??
|
|
82
|
+
(input.background !== undefined
|
|
83
|
+
? relativeLuminance(resolveColorToRGB(input.background)) > 0.5
|
|
84
|
+
? 'light'
|
|
85
|
+
: 'dark'
|
|
86
|
+
: 'dark');
|
|
87
|
+
const dark = mode === 'dark';
|
|
88
|
+
const lift = dark ? WHITE : BLACK;
|
|
89
|
+
const shift = (base, amount) => fade(base, lift, amount);
|
|
90
|
+
const background = themeColor(input.background ??
|
|
91
|
+
(dark ? { r: 15, g: 15, b: 35 } : { r: 244, g: 245, b: 249 }));
|
|
92
|
+
const foreground = themeColor(input.foreground ??
|
|
93
|
+
fade(contrastText(background.rgb), background.rgb, 0.08));
|
|
94
|
+
const primary = themeColor(input.primary ??
|
|
95
|
+
(dark ? { r: 59, g: 188, b: 225 } : { r: 13, g: 126, b: 166 }));
|
|
96
|
+
const theme = {
|
|
97
|
+
name: input.name ?? `custom-${mode}`,
|
|
98
|
+
mode,
|
|
99
|
+
background,
|
|
100
|
+
surface: themeColor(input.surface ?? shift(background.rgb, 0.08)),
|
|
101
|
+
surfaceRaised: themeColor(input.surfaceRaised ?? shift(background.rgb, 0.16)),
|
|
102
|
+
foreground,
|
|
103
|
+
muted: themeColor(input.muted ?? fade(foreground.rgb, background.rgb, 0.45)),
|
|
104
|
+
primary,
|
|
105
|
+
onPrimary: themeColor(input.onPrimary ?? contrastText(primary.rgb)),
|
|
106
|
+
accent: themeColor(input.accent ?? shift(primary.rgb, 0.25)),
|
|
107
|
+
border: themeColor(input.border ?? fade(foreground.rgb, background.rgb, 0.7)),
|
|
108
|
+
focus: themeColor(input.focus ?? primary.rgb),
|
|
109
|
+
selection: themeColor(input.selection ?? fade(primary.rgb, background.rgb, 0.7)),
|
|
110
|
+
success: themeColor(input.success ??
|
|
111
|
+
(dark ? { r: 0, g: 215, b: 0 } : { r: 15, g: 157, b: 60 })),
|
|
112
|
+
warning: themeColor(input.warning ??
|
|
113
|
+
(dark ? { r: 255, g: 175, b: 0 } : { r: 178, g: 107, b: 0 })),
|
|
114
|
+
danger: themeColor(input.danger ??
|
|
115
|
+
(dark ? { r: 215, g: 0, b: 0 } : { r: 198, g: 40, b: 40 })),
|
|
116
|
+
info: themeColor(input.info ??
|
|
117
|
+
(dark ? { r: 0, g: 175, b: 255 } : { r: 11, g: 120, b: 194 })),
|
|
118
|
+
};
|
|
119
|
+
if (input.gradients)
|
|
120
|
+
theme.gradients = input.gradients;
|
|
121
|
+
return theme;
|
|
122
|
+
}
|
|
123
|
+
function themeToInput(theme) {
|
|
124
|
+
const input = { name: theme.name, mode: theme.mode };
|
|
125
|
+
for (const token of THEME_TOKENS)
|
|
126
|
+
input[token] = theme[token];
|
|
127
|
+
if (theme.gradients)
|
|
128
|
+
input.gradients = theme.gradients;
|
|
129
|
+
return input;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Normalizes a Theme | ThemeInput. Full Themes pass through. Sparse inputs
|
|
133
|
+
* overlay `base` when given (used by `ctx.themed`), otherwise they are
|
|
134
|
+
* completed by `createTheme` derivation (used by `ctx.setTheme`).
|
|
135
|
+
*/
|
|
136
|
+
export function resolveTheme(input, base) {
|
|
137
|
+
if (isTheme(input))
|
|
138
|
+
return input;
|
|
139
|
+
return base
|
|
140
|
+
? createTheme({ ...themeToInput(base), ...input })
|
|
141
|
+
: createTheme(input);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Built-in dark theme, derived from Bunti's PALETTE aesthetic
|
|
145
|
+
* (midnight / silver / bunti-blue / plasma).
|
|
146
|
+
*/
|
|
147
|
+
export const darkTheme = createTheme({
|
|
148
|
+
name: 'bunti-dark',
|
|
149
|
+
mode: 'dark',
|
|
150
|
+
background: 'midnight',
|
|
151
|
+
foreground: 'silver',
|
|
152
|
+
primary: 'bunti-blue',
|
|
153
|
+
accent: 'plasma',
|
|
154
|
+
});
|
|
155
|
+
/**
|
|
156
|
+
* Built-in light theme: the PALETTE aesthetic re-balanced for light
|
|
157
|
+
* backgrounds (deepened primary/semantic colors for WCAG contrast).
|
|
158
|
+
*/
|
|
159
|
+
export const lightTheme = createTheme({
|
|
160
|
+
name: 'bunti-light',
|
|
161
|
+
mode: 'light',
|
|
162
|
+
background: '#f4f5f9',
|
|
163
|
+
foreground: '#23283b',
|
|
164
|
+
primary: '#0d7ea6',
|
|
165
|
+
accent: '#8a2bd8',
|
|
166
|
+
});
|
|
167
|
+
//# sourceMappingURL=theme.js.map
|