archymedes-cli 2.0.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.
@@ -0,0 +1,249 @@
1
+ // packages/archymedes-cli/src/theme.ts
2
+ var BLUEPRINT = `
3
+ /* Blueprint navy, chalk-white ink and an amber pencil — the original drafting table. */
4
+ @theme blueprint {
5
+ --primary: #7cc5ff;
6
+ --secondary: #9fb4e8;
7
+ --accent: #ffcf6b;
8
+ --bg: #0a1524;
9
+ --surface: #122135;
10
+ --text: #e8eefb;
11
+ --text-muted: #6f89b0;
12
+ --success: #79dfa6;
13
+ --warning: #ffcf6b;
14
+ --error: #ff8f7a;
15
+ --border: round;
16
+ --border-color: #24406a;
17
+ --border-focus: #7cc5ff;
18
+ }
19
+ `;
20
+ var PARCHMENT = `
21
+ /* The same drawing inked on paper, for a light terminal. */
22
+ @theme parchment {
23
+ --primary: #2f5fbf;
24
+ --secondary: #5a4a86;
25
+ --accent: #9a6a00;
26
+ --bg: #faf6ec;
27
+ --surface: #f1eadd;
28
+ --text: #1b1a16;
29
+ --text-muted: #6a6252;
30
+ --success: #196a44;
31
+ --warning: #8a5300;
32
+ --error: #b3243b;
33
+ --border: round;
34
+ --border-color: #d8cfb8;
35
+ --border-focus: #2f5fbf;
36
+ }
37
+ `;
38
+ var CHALKBOARD = `
39
+ /* A slate: near-black green, chalk white, chalk yellow — for terminals with contrast to spare. */
40
+ @theme chalkboard {
41
+ --primary: #9fe8d8;
42
+ --secondary: #d8d0b0;
43
+ --accent: #ffe066;
44
+ --bg: #0d1a15;
45
+ --surface: #16271f;
46
+ --text: #f2efe4;
47
+ --text-muted: #86987f;
48
+ --success: #7ef2b0;
49
+ --warning: #ffd166;
50
+ --error: #ff7a7a;
51
+ --border: single;
52
+ --border-color: #2c443a;
53
+ --border-focus: #ffe066;
54
+ }
55
+ `;
56
+ var HIGH_CONTRAST = `
57
+ /* Maximum separation, named colours only — for low vision and for terminals with a fixed palette. */
58
+ @theme high-contrast {
59
+ --primary: brightCyan;
60
+ --secondary: brightMagenta;
61
+ --accent: brightYellow;
62
+ --bg: black;
63
+ --surface: black;
64
+ --text: brightWhite;
65
+ --text-muted: white;
66
+ --success: brightGreen;
67
+ --warning: brightYellow;
68
+ --error: brightRed;
69
+ --border: single;
70
+ --border-color: white;
71
+ --border-focus: brightCyan;
72
+ }
73
+ `;
74
+ var ARCHYMEDES = `
75
+ /* Bronze instruments, limestone ink and olive accents on charcoal — Archymedes's own. */
76
+ @theme archymedes {
77
+ --primary: #e7bb78;
78
+ --secondary: #b9c59a;
79
+ --accent: #e5a58c;
80
+ --bg: #191815;
81
+ --surface: #25231e;
82
+ --text: #f2ecdf;
83
+ --text-muted: #aaa18f;
84
+ --success: #b5cc96;
85
+ --warning: #efc778;
86
+ --error: #f29686;
87
+ --border: single;
88
+ --border-color: #736956;
89
+ --border-focus: #e7bb78;
90
+ }
91
+ `;
92
+ var BUILTIN_THEME_SOURCES = {
93
+ archymedes: ARCHYMEDES,
94
+ blueprint: BLUEPRINT,
95
+ parchment: PARCHMENT,
96
+ chalkboard: CHALKBOARD,
97
+ "high-contrast": HIGH_CONTRAST
98
+ };
99
+ var DEFAULT_THEME_NAME = "archymedes";
100
+ var NAMED_COLORS = {
101
+ black: 0,
102
+ red: 1,
103
+ green: 2,
104
+ yellow: 3,
105
+ blue: 4,
106
+ magenta: 5,
107
+ cyan: 6,
108
+ white: 7,
109
+ brightblack: 8,
110
+ brightred: 9,
111
+ brightgreen: 10,
112
+ brightyellow: 11,
113
+ brightblue: 12,
114
+ brightmagenta: 13,
115
+ brightcyan: 14,
116
+ brightwhite: 15,
117
+ gray: 8,
118
+ grey: 8
119
+ };
120
+ function parseColor(value) {
121
+ const text = value.trim();
122
+ const named = NAMED_COLORS[text.toLowerCase().replace(/[\s_-]/g, "")];
123
+ if (named !== undefined)
124
+ return named;
125
+ const hex = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(text);
126
+ if (!hex)
127
+ return;
128
+ const digits = hex[1];
129
+ const full = digits.length === 3 ? digits.split("").map((digit) => digit + digit).join("") : digits;
130
+ return {
131
+ r: Number.parseInt(full.slice(0, 2), 16),
132
+ g: Number.parseInt(full.slice(2, 4), 16),
133
+ b: Number.parseInt(full.slice(4, 6), 16)
134
+ };
135
+ }
136
+ function rgbTo256({ r, g, b }) {
137
+ const level = (value) => {
138
+ if (value < 48)
139
+ return 0;
140
+ if (value < 115)
141
+ return 1;
142
+ return Math.min(5, Math.round((value - 35) / 40));
143
+ };
144
+ const cubeIndex = 16 + 36 * level(r) + 6 * level(g) + level(b);
145
+ const cubeValue = (step) => step === 0 ? 0 : 55 + step * 40;
146
+ const cube = { r: cubeValue(level(r)), g: cubeValue(level(g)), b: cubeValue(level(b)) };
147
+ const greyAverage = (r + g + b) / 3;
148
+ const greyStep = Math.max(0, Math.min(23, Math.round((greyAverage - 8) / 10)));
149
+ const greyValue = 8 + greyStep * 10;
150
+ const grey = { r: greyValue, g: greyValue, b: greyValue };
151
+ return distance({ r, g, b }, grey) < distance({ r, g, b }, cube) ? 232 + greyStep : cubeIndex;
152
+ }
153
+ function distance(a, b) {
154
+ return (a.r - b.r) ** 2 + (a.g - b.g) ** 2 + (a.b - b.b) ** 2;
155
+ }
156
+ function colorCode(value, depth) {
157
+ if (depth === "none")
158
+ return "";
159
+ const parsed = parseColor(value);
160
+ if (parsed === undefined)
161
+ return "";
162
+ if (typeof parsed === "number")
163
+ return parsed < 8 ? `\x1B[${30 + parsed}m` : `\x1B[${90 + parsed - 8}m`;
164
+ return depth === "truecolor" ? `\x1B[38;2;${parsed.r};${parsed.g};${parsed.b}m` : `\x1B[38;5;${rgbTo256(parsed)}m`;
165
+ }
166
+ var DEFAULT_TOKENS = {
167
+ primary: "cyan",
168
+ secondary: "blue",
169
+ accent: "yellow",
170
+ bg: "black",
171
+ surface: "black",
172
+ text: "white",
173
+ textMuted: "brightBlack",
174
+ success: "green",
175
+ warning: "yellow",
176
+ error: "red",
177
+ border: "round",
178
+ borderColor: "brightBlack",
179
+ borderFocus: "cyan"
180
+ };
181
+ var TOKEN_NAMES = {
182
+ primary: "primary",
183
+ secondary: "secondary",
184
+ accent: "accent",
185
+ bg: "bg",
186
+ background: "bg",
187
+ surface: "surface",
188
+ text: "text",
189
+ fg: "text",
190
+ "text-muted": "textMuted",
191
+ muted: "textMuted",
192
+ success: "success",
193
+ warning: "warning",
194
+ error: "error",
195
+ border: "border",
196
+ "border-color": "borderColor",
197
+ "border-focus": "borderFocus",
198
+ highlight: "secondary"
199
+ };
200
+ function parseThemeSource(source) {
201
+ const themes = [];
202
+ const withoutLineComments = source.replace(/(^|\s)\/\/[^\n]*/g, "$1");
203
+ const blockPattern = /(?:\/\*([\s\S]*?)\*\/\s*)?@theme\s+([A-Za-z0-9_-]+)\s*\{([^}]*)\}/g;
204
+ for (const match of withoutLineComments.matchAll(blockPattern)) {
205
+ const description = (match[1] ?? "").replace(/\s+/g, " ").trim();
206
+ const tokens = { ...DEFAULT_TOKENS };
207
+ for (const declaration of match[3].matchAll(/(?:--|\$)([a-z0-9-]+)\s*:\s*([^;}]+)[;]?/gi)) {
208
+ const key = TOKEN_NAMES[declaration[1].toLowerCase()];
209
+ if (key)
210
+ tokens[key] = declaration[2].trim();
211
+ }
212
+ themes.push({ name: match[2], description, tokens });
213
+ }
214
+ return themes;
215
+ }
216
+ function builtinThemes() {
217
+ return Object.values(BUILTIN_THEME_SOURCES).flatMap((source) => parseThemeSource(source));
218
+ }
219
+ function findBuiltinTheme(name) {
220
+ return builtinThemes().find((theme) => theme.name.toLowerCase() === name.trim().toLowerCase());
221
+ }
222
+ function borderStyleOf(value) {
223
+ const text = value.trim().toLowerCase();
224
+ return text === "single" || text === "double" || text === "none" ? text : "round";
225
+ }
226
+ function buildPalette(theme, depth) {
227
+ const code = (value) => colorCode(value, depth);
228
+ return {
229
+ theme: theme.name,
230
+ depth,
231
+ primary: code(theme.tokens.primary),
232
+ secondary: code(theme.tokens.secondary),
233
+ accent: code(theme.tokens.accent),
234
+ text: code(theme.tokens.text),
235
+ muted: code(theme.tokens.textMuted),
236
+ success: code(theme.tokens.success),
237
+ warning: code(theme.tokens.warning),
238
+ error: code(theme.tokens.error),
239
+ border: code(theme.tokens.borderColor),
240
+ borderFocus: code(theme.tokens.borderFocus),
241
+ borderStyle: borderStyleOf(theme.tokens.border),
242
+ bg: theme.tokens.bg,
243
+ surface: theme.tokens.surface,
244
+ tokens: theme.tokens
245
+ };
246
+ }
247
+ var NO_COLOR_PALETTE = buildPalette(findBuiltinTheme(DEFAULT_THEME_NAME) ?? { name: DEFAULT_THEME_NAME, description: "", tokens: DEFAULT_TOKENS }, "none");
248
+
249
+ export { BUILTIN_THEME_SOURCES, rgbTo256, parseThemeSource, NO_COLOR_PALETTE };