@treeui/mcp 0.7.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/{chunk-2Y7LP7ZZ.js → chunk-DGIMKZPZ.js} +9536 -5623
- package/dist/cli.cjs +9536 -5623
- package/dist/cli.js +1 -1
- package/dist/index.cjs +7000 -2740
- package/dist/index.d.cts +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +426 -79
- package/package.json +22 -3
package/dist/index.d.cts
CHANGED
|
@@ -106,6 +106,13 @@ interface TreeuiTokenEntry {
|
|
|
106
106
|
description?: string;
|
|
107
107
|
/** Per-theme values for tokens that change with `[data-tree-theme]`. */
|
|
108
108
|
themeValues?: Record<string, string>;
|
|
109
|
+
/**
|
|
110
|
+
* True for interaction states the library computes from the semantic layer
|
|
111
|
+
* (hover, press, selected, disabled). They are real, emitted variables an
|
|
112
|
+
* agent may read, but a product must not theme against them — and they rank
|
|
113
|
+
* below the semantic tokens in search so they cannot bury the answer.
|
|
114
|
+
*/
|
|
115
|
+
derived?: boolean;
|
|
109
116
|
}
|
|
110
117
|
interface TreeuiCatalog {
|
|
111
118
|
generatedAt: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -106,6 +106,13 @@ interface TreeuiTokenEntry {
|
|
|
106
106
|
description?: string;
|
|
107
107
|
/** Per-theme values for tokens that change with `[data-tree-theme]`. */
|
|
108
108
|
themeValues?: Record<string, string>;
|
|
109
|
+
/**
|
|
110
|
+
* True for interaction states the library computes from the semantic layer
|
|
111
|
+
* (hover, press, selected, disabled). They are real, emitted variables an
|
|
112
|
+
* agent may read, but a product must not theme against them — and they rank
|
|
113
|
+
* below the semantic tokens in search so they cannot bury the answer.
|
|
114
|
+
*/
|
|
115
|
+
derived?: boolean;
|
|
109
116
|
}
|
|
110
117
|
interface TreeuiCatalog {
|
|
111
118
|
generatedAt: string;
|
package/dist/index.js
CHANGED
|
@@ -11,14 +11,175 @@ import {
|
|
|
11
11
|
searchComponents,
|
|
12
12
|
searchRecipes,
|
|
13
13
|
searchTokens
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-DGIMKZPZ.js";
|
|
15
15
|
|
|
16
16
|
// src/catalog-source.ts
|
|
17
17
|
import fs from "fs";
|
|
18
18
|
import path from "path";
|
|
19
19
|
import { parseAllDocuments } from "yaml";
|
|
20
20
|
|
|
21
|
+
// ../tokens/src/primitives.ts
|
|
22
|
+
var treePrimitives = {
|
|
23
|
+
white: "#ffffff",
|
|
24
|
+
black: "#000000",
|
|
25
|
+
/**
|
|
26
|
+
* Neutral ramp. Carries both themes: 0-300 are light-mode surfaces and
|
|
27
|
+
* borders, 700-950 are dark-mode surfaces, and the 400-600 middle is where
|
|
28
|
+
* borders and muted text land in whichever theme needs that luminance.
|
|
29
|
+
*/
|
|
30
|
+
gray: {
|
|
31
|
+
0: "#ffffff",
|
|
32
|
+
50: "#f6f8fa",
|
|
33
|
+
100: "#eff2f5",
|
|
34
|
+
200: "#d0d7de",
|
|
35
|
+
300: "#afb8c1",
|
|
36
|
+
/** Clears 3:1 on every light surface, including the subtle one — control edges. */
|
|
37
|
+
400: "#858c93",
|
|
38
|
+
/** Its dark-mode counterpart, 3:1 on every dark surface. */
|
|
39
|
+
500: "#737c88",
|
|
40
|
+
600: "#59636e",
|
|
41
|
+
650: "#576270",
|
|
42
|
+
700: "#444c56",
|
|
43
|
+
800: "#2d333b",
|
|
44
|
+
850: "#22272e",
|
|
45
|
+
900: "#1c2128",
|
|
46
|
+
950: "#0d1117",
|
|
47
|
+
/** Ink. Not gray-1000: it carries a blue cast, which is why it is named. */
|
|
48
|
+
ink: "#1f2328",
|
|
49
|
+
/** Muted ink for dark surfaces. */
|
|
50
|
+
inkDark: "#adbac7",
|
|
51
|
+
inkDarkMuted: "#95a3b2"
|
|
52
|
+
},
|
|
53
|
+
blue: {
|
|
54
|
+
50: "#ddf4ff",
|
|
55
|
+
300: "#6cb6ff",
|
|
56
|
+
400: "#559cf5",
|
|
57
|
+
450: "#6cabf7",
|
|
58
|
+
500: "#0969da",
|
|
59
|
+
600: "#0550ae",
|
|
60
|
+
800: "#1a3453",
|
|
61
|
+
900: "#0a1a2f"
|
|
62
|
+
},
|
|
63
|
+
/**
|
|
64
|
+
* Secondary accent ramp. Seeded from `#8957e5` and stepped until the mid tone
|
|
65
|
+
* reads as text on its own tint in both themes — see `DECISIONS.md`.
|
|
66
|
+
*/
|
|
67
|
+
purple: {
|
|
68
|
+
50: "#efeaf9",
|
|
69
|
+
300: "#ba9df0",
|
|
70
|
+
400: "#ad8bed",
|
|
71
|
+
500: "#794dcb",
|
|
72
|
+
600: "#633fa6",
|
|
73
|
+
800: "#332e4a"
|
|
74
|
+
},
|
|
75
|
+
/**
|
|
76
|
+
* The status hues carry the narrowest margin in the palette, because the band
|
|
77
|
+
* they have to clear is `bg.subtle` — the secondary surface a status line
|
|
78
|
+
* usually lands on (inside a card, under a field), not the primary one they
|
|
79
|
+
* are easy to calibrate against. Each of these three was a step away from the
|
|
80
|
+
* floor and is now a step above it; `status.test.ts` holds the headroom, and
|
|
81
|
+
* the contract validator holds the floor.
|
|
82
|
+
*/
|
|
83
|
+
green: {
|
|
84
|
+
400: "#5bae5e",
|
|
85
|
+
500: "#197d36"
|
|
86
|
+
},
|
|
87
|
+
amber: {
|
|
88
|
+
400: "#c99227",
|
|
89
|
+
500: "#946300"
|
|
90
|
+
},
|
|
91
|
+
red: {
|
|
92
|
+
400: "#f5766e",
|
|
93
|
+
500: "#d1242f"
|
|
94
|
+
},
|
|
95
|
+
/**
|
|
96
|
+
* Categorical data-viz hues. Deliberately not a ramp — these are eight
|
|
97
|
+
* distinct hues chosen for separation under the three common CVD types, each
|
|
98
|
+
* held at >= 3:1 against its own theme's surface so a mark is visible without
|
|
99
|
+
* relying on the legend. Assign in order.
|
|
100
|
+
*/
|
|
101
|
+
dataviz: {
|
|
102
|
+
light: {
|
|
103
|
+
1: "#2a78d6",
|
|
104
|
+
// 2, 3 and 7 were #1baf7a / #eda100 / #e87ba4, measuring 2.82, 2.17 and
|
|
105
|
+
// 2.69 against the light surface — a mark you cannot see is not a
|
|
106
|
+
// category. Darkened along the same hue until each clears 3:1.
|
|
107
|
+
2: "#12855c",
|
|
108
|
+
3: "#8a6100",
|
|
109
|
+
4: "#008300",
|
|
110
|
+
5: "#4a3aa7",
|
|
111
|
+
6: "#e34948",
|
|
112
|
+
7: "#c4587f",
|
|
113
|
+
8: "#eb6834"
|
|
114
|
+
},
|
|
115
|
+
dark: {
|
|
116
|
+
1: "#3987e5",
|
|
117
|
+
2: "#199e70",
|
|
118
|
+
3: "#c98500",
|
|
119
|
+
4: "#008300",
|
|
120
|
+
5: "#9085e9",
|
|
121
|
+
6: "#e66767",
|
|
122
|
+
7: "#d55181",
|
|
123
|
+
8: "#d95926"
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
var treeAlpha = {
|
|
128
|
+
/** Modal and drawer scrim. */
|
|
129
|
+
overlay: { light: 0.5, dark: 0.6 },
|
|
130
|
+
/** Focus ring, over the brand colour. */
|
|
131
|
+
focusRing: 0.32
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
// ../tokens/src/color.ts
|
|
135
|
+
var clampChannel = (value) => Math.min(255, Math.max(0, Math.round(value)));
|
|
136
|
+
var expandShortHex = (hex) => hex.length === 4 ? `#${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}` : hex;
|
|
137
|
+
var parseHex = (value) => {
|
|
138
|
+
const hex = expandShortHex(value.trim());
|
|
139
|
+
const match = /^#([0-9a-fA-F]{6})$/.exec(hex);
|
|
140
|
+
if (!match) {
|
|
141
|
+
throw new Error(`Invalid hex color: "${value}"`);
|
|
142
|
+
}
|
|
143
|
+
const int = Number.parseInt(match[1], 16);
|
|
144
|
+
return {
|
|
145
|
+
r: int >> 16 & 255,
|
|
146
|
+
g: int >> 8 & 255,
|
|
147
|
+
b: int & 255
|
|
148
|
+
};
|
|
149
|
+
};
|
|
150
|
+
var formatHex = ({ r, g, b }) => {
|
|
151
|
+
const toPart = (channel) => clampChannel(channel).toString(16).padStart(2, "0");
|
|
152
|
+
return `#${toPart(r)}${toPart(g)}${toPart(b)}`;
|
|
153
|
+
};
|
|
154
|
+
var mixColors = (a, b, weight) => {
|
|
155
|
+
const t = Math.min(1, Math.max(0, weight));
|
|
156
|
+
return {
|
|
157
|
+
r: a.r + (b.r - a.r) * t,
|
|
158
|
+
g: a.g + (b.g - a.g) * t,
|
|
159
|
+
b: a.b + (b.b - a.b) * t
|
|
160
|
+
};
|
|
161
|
+
};
|
|
162
|
+
var BLACK = { r: 0, g: 0, b: 0 };
|
|
163
|
+
var WHITE = { r: 255, g: 255, b: 255 };
|
|
164
|
+
var darken = (color, amount) => mixColors(color, BLACK, amount);
|
|
165
|
+
var lighten = (color, amount) => mixColors(color, WHITE, amount);
|
|
166
|
+
var channelLuminance = (value) => {
|
|
167
|
+
const channel = value / 255;
|
|
168
|
+
return channel <= 0.03928 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4;
|
|
169
|
+
};
|
|
170
|
+
var relativeLuminance = ({ r, g, b }) => 0.2126 * channelLuminance(r) + 0.7152 * channelLuminance(g) + 0.0722 * channelLuminance(b);
|
|
171
|
+
var contrastRatio = (a, b) => {
|
|
172
|
+
const luminanceA = relativeLuminance(a);
|
|
173
|
+
const luminanceB = relativeLuminance(b);
|
|
174
|
+
const lighter = Math.max(luminanceA, luminanceB);
|
|
175
|
+
const darker = Math.min(luminanceA, luminanceB);
|
|
176
|
+
return (lighter + 0.05) / (darker + 0.05);
|
|
177
|
+
};
|
|
178
|
+
var bestContrast = (color, candidates) => contrastRatio(color, candidates[0]) >= contrastRatio(color, candidates[1]) ? candidates[0] : candidates[1];
|
|
179
|
+
var withAlpha = ({ r, g, b }, alpha2) => `rgba(${clampChannel(r)}, ${clampChannel(g)}, ${clampChannel(b)}, ${alpha2})`;
|
|
180
|
+
|
|
21
181
|
// ../tokens/src/tokens.ts
|
|
182
|
+
var alpha = (color, amount) => withAlpha(parseHex(color), amount);
|
|
22
183
|
var treeTokens = {
|
|
23
184
|
font: {
|
|
24
185
|
family: {
|
|
@@ -32,7 +193,11 @@ var treeTokens = {
|
|
|
32
193
|
base: "0.9375rem",
|
|
33
194
|
lg: "1.125rem",
|
|
34
195
|
xl: "1.375rem",
|
|
35
|
-
|
|
196
|
+
// `2xl` used to repeat `xl` verbatim, which made every
|
|
197
|
+
// `clamp(xl … 2xl)` a constant and left a hole between 1.375 and 1.75rem.
|
|
198
|
+
// It now fills that hole; `xl` is unchanged, since it is the more used of
|
|
199
|
+
// the two. `tokens.test.ts` keeps the scale strictly increasing.
|
|
200
|
+
"2xl": "1.5rem",
|
|
36
201
|
"3xl": "1.75rem",
|
|
37
202
|
"4xl": "2.25rem",
|
|
38
203
|
"5xl": "3rem"
|
|
@@ -52,6 +217,11 @@ var treeTokens = {
|
|
|
52
217
|
base: "1.5",
|
|
53
218
|
body: "1.6",
|
|
54
219
|
relaxed: "1.65"
|
|
220
|
+
},
|
|
221
|
+
tracking: {
|
|
222
|
+
tight: "-0.02em",
|
|
223
|
+
normal: "0em",
|
|
224
|
+
wide: "0.08em"
|
|
55
225
|
}
|
|
56
226
|
},
|
|
57
227
|
space: {
|
|
@@ -72,10 +242,23 @@ var treeTokens = {
|
|
|
72
242
|
lg: "0.875rem",
|
|
73
243
|
pill: "999px"
|
|
74
244
|
},
|
|
245
|
+
// Elevation. The geometry is theme-independent and lives here; the *colour*
|
|
246
|
+
// comes from `--tree-color-shadow-rgb`, which the theme sets — a slate shadow
|
|
247
|
+
// was being emitted on `:root` and reused verbatim on the dark surface, where
|
|
248
|
+
// a near-black umbra is what actually reads.
|
|
75
249
|
shadow: {
|
|
76
|
-
xs: "0 1px 2px rgba(
|
|
77
|
-
sm: "0 6px 18px rgba(
|
|
78
|
-
md: "0 14px 34px rgba(
|
|
250
|
+
xs: "0 1px 2px rgba(var(--tree-color-shadow-rgb), 0.06)",
|
|
251
|
+
sm: "0 6px 18px rgba(var(--tree-color-shadow-rgb), 0.08)",
|
|
252
|
+
md: "0 14px 34px rgba(var(--tree-color-shadow-rgb), 0.12)",
|
|
253
|
+
// Marketing-scale elevation: a large, soft lift for a hero card or a banded
|
|
254
|
+
// section, where `md` reads flat. Same neutral shadow colour as the smaller
|
|
255
|
+
// steps, so the whole scale stays one family.
|
|
256
|
+
lg: "0 24px 48px -24px rgba(var(--tree-color-shadow-rgb), 0.22)",
|
|
257
|
+
xl: "0 40px 80px -32px rgba(var(--tree-color-shadow-rgb), 0.28)",
|
|
258
|
+
// Elevation tinted by whatever accent is in scope (see `--tree-color-accent-*`
|
|
259
|
+
// and the `accent` prop on TSection/THero/TPageSurface), so a surface's glow
|
|
260
|
+
// follows its accent instead of being written by hand per landing page.
|
|
261
|
+
accent: "0 24px 48px -32px color-mix(in srgb, var(--tree-color-accent-primary) 60%, transparent)"
|
|
79
262
|
},
|
|
80
263
|
// Brand gradient for high-emphasis surfaces (e.g. the primary CTA).
|
|
81
264
|
// References the theme brand vars so it follows light/dark automatically;
|
|
@@ -89,6 +272,15 @@ var treeTokens = {
|
|
|
89
272
|
strong: "1.5px"
|
|
90
273
|
}
|
|
91
274
|
},
|
|
275
|
+
/**
|
|
276
|
+
* Opacity for the parts of a disabled control that carry no text — an icon,
|
|
277
|
+
* a thumbnail, a toolbar. Anywhere a label sits on a surface, disabled is a
|
|
278
|
+
* colour instead (`--tree-color-state-disabled-*`), because opacity cannot be
|
|
279
|
+
* measured for contrast. This replaces eight different hand-picked values.
|
|
280
|
+
*/
|
|
281
|
+
opacity: {
|
|
282
|
+
disabled: "0.5"
|
|
283
|
+
},
|
|
92
284
|
motion: {
|
|
93
285
|
duration: {
|
|
94
286
|
fast: "100ms",
|
|
@@ -153,95 +345,233 @@ var treeThemes = {
|
|
|
153
345
|
light: {
|
|
154
346
|
color: {
|
|
155
347
|
bg: {
|
|
156
|
-
primary:
|
|
157
|
-
surface:
|
|
158
|
-
subtle:
|
|
348
|
+
primary: treePrimitives.gray[50],
|
|
349
|
+
surface: treePrimitives.gray[0],
|
|
350
|
+
subtle: treePrimitives.gray[100]
|
|
159
351
|
},
|
|
160
352
|
border: {
|
|
161
|
-
default:
|
|
162
|
-
strong:
|
|
353
|
+
default: treePrimitives.gray[200],
|
|
354
|
+
strong: treePrimitives.gray[300]
|
|
355
|
+
// `border.interactive` is deliberately absent: WCAG 1.4.11 asks for 3:1
|
|
356
|
+
// on the boundary that *identifies* a control, and `default` measures
|
|
357
|
+
// 1.45:1 here — fine for a card edge, not fine for the only thing
|
|
358
|
+
// telling a user where an input is. The library derives a passing value
|
|
359
|
+
// from `strong` (see states.ts); a product may set one, and it is
|
|
360
|
+
// validated the same way.
|
|
163
361
|
},
|
|
164
362
|
text: {
|
|
165
|
-
primary:
|
|
166
|
-
muted:
|
|
167
|
-
inverse:
|
|
363
|
+
primary: treePrimitives.gray.ink,
|
|
364
|
+
muted: treePrimitives.gray[600],
|
|
365
|
+
inverse: treePrimitives.gray[0]
|
|
168
366
|
},
|
|
367
|
+
/** Umbra as an `r, g, b` triple so the elevation scale can vary alpha. */
|
|
368
|
+
"shadow-rgb": "15, 23, 42",
|
|
169
369
|
brand: {
|
|
170
|
-
primary:
|
|
171
|
-
hover:
|
|
172
|
-
soft:
|
|
173
|
-
contrast:
|
|
370
|
+
primary: treePrimitives.blue[500],
|
|
371
|
+
hover: treePrimitives.blue[600],
|
|
372
|
+
soft: treePrimitives.blue[50],
|
|
373
|
+
contrast: treePrimitives.gray[0]
|
|
374
|
+
},
|
|
375
|
+
// Secondary brand accent — a closed axis, never a free colour. It is what
|
|
376
|
+
// `TTag tone="accent"` paints with, and the default value of the surface
|
|
377
|
+
// `accent` axis (TSection/THero/TPageSurface), which remaps these four
|
|
378
|
+
// variables when a product wants a status hue instead. Derived with
|
|
379
|
+
// `deriveBrandRamp` from #8957e5 so the accent stays legible as text on
|
|
380
|
+
// its own soft tint (measured 4.77:1) and on every background.
|
|
381
|
+
accent: {
|
|
382
|
+
primary: treePrimitives.purple[500],
|
|
383
|
+
hover: treePrimitives.purple[600],
|
|
384
|
+
soft: treePrimitives.purple[50],
|
|
385
|
+
contrast: treePrimitives.gray[0]
|
|
174
386
|
},
|
|
175
387
|
status: {
|
|
176
|
-
success:
|
|
177
|
-
warning:
|
|
178
|
-
error:
|
|
179
|
-
info:
|
|
388
|
+
success: treePrimitives.green[500],
|
|
389
|
+
warning: treePrimitives.amber[500],
|
|
390
|
+
error: treePrimitives.red[500],
|
|
391
|
+
info: treePrimitives.blue[500]
|
|
180
392
|
},
|
|
181
393
|
// Categorical data-viz palette. Fixed hue order (blue → orange), validated
|
|
182
394
|
// for CVD separation and contrast against the light surface. Assign in order;
|
|
183
395
|
// the components cycle after 8 as a fallback, but prefer folding a 9th+
|
|
184
396
|
// category into "Other".
|
|
185
|
-
chart:
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
3: "#eda100",
|
|
189
|
-
4: "#008300",
|
|
190
|
-
5: "#4a3aa7",
|
|
191
|
-
6: "#e34948",
|
|
192
|
-
7: "#e87ba4",
|
|
193
|
-
8: "#eb6834"
|
|
194
|
-
},
|
|
195
|
-
overlay: "rgba(31, 35, 40, 0.5)",
|
|
196
|
-
"focus-ring": "rgba(9, 105, 218, 0.32)"
|
|
397
|
+
chart: treePrimitives.dataviz.light,
|
|
398
|
+
overlay: alpha(treePrimitives.gray.ink, treeAlpha.overlay.light),
|
|
399
|
+
"focus-ring": alpha(treePrimitives.blue[500], treeAlpha.focusRing)
|
|
197
400
|
}
|
|
198
401
|
},
|
|
199
402
|
dark: {
|
|
200
403
|
color: {
|
|
201
404
|
bg: {
|
|
202
|
-
primary:
|
|
203
|
-
surface:
|
|
204
|
-
subtle:
|
|
405
|
+
primary: treePrimitives.gray[900],
|
|
406
|
+
surface: treePrimitives.gray[850],
|
|
407
|
+
subtle: treePrimitives.gray[800]
|
|
205
408
|
},
|
|
206
409
|
border: {
|
|
207
|
-
default:
|
|
208
|
-
strong:
|
|
410
|
+
default: treePrimitives.gray[700],
|
|
411
|
+
strong: treePrimitives.gray[650]
|
|
209
412
|
},
|
|
210
413
|
text: {
|
|
211
|
-
primary:
|
|
212
|
-
muted:
|
|
213
|
-
inverse:
|
|
414
|
+
primary: treePrimitives.gray.inkDark,
|
|
415
|
+
muted: treePrimitives.gray.inkDarkMuted,
|
|
416
|
+
inverse: treePrimitives.gray[900]
|
|
214
417
|
},
|
|
418
|
+
/** Near-black umbra: a slate shadow is invisible on a dark surface. */
|
|
419
|
+
"shadow-rgb": "0, 0, 0",
|
|
215
420
|
brand: {
|
|
216
|
-
primary:
|
|
217
|
-
hover:
|
|
218
|
-
soft:
|
|
219
|
-
contrast:
|
|
421
|
+
primary: treePrimitives.blue[400],
|
|
422
|
+
hover: treePrimitives.blue[450],
|
|
423
|
+
soft: treePrimitives.blue[800],
|
|
424
|
+
contrast: treePrimitives.blue[900]
|
|
425
|
+
},
|
|
426
|
+
// Dark pair of the secondary accent (same #8957e5 seed, stepped for the
|
|
427
|
+
// dark surface). Measured 5.93:1 on bg-primary, 4.67:1 on bg-subtle and
|
|
428
|
+
// 4.72:1 on its own soft tint — AA on every background, and above what
|
|
429
|
+
// the dark brand pair itself clears. `accent.test.ts` holds that bar.
|
|
430
|
+
accent: {
|
|
431
|
+
primary: treePrimitives.purple[400],
|
|
432
|
+
hover: treePrimitives.purple[300],
|
|
433
|
+
soft: treePrimitives.purple[800],
|
|
434
|
+
contrast: treePrimitives.gray[900]
|
|
220
435
|
},
|
|
221
436
|
status: {
|
|
222
|
-
success:
|
|
223
|
-
warning:
|
|
224
|
-
error:
|
|
225
|
-
info:
|
|
437
|
+
success: treePrimitives.green[400],
|
|
438
|
+
warning: treePrimitives.amber[400],
|
|
439
|
+
error: treePrimitives.red[400],
|
|
440
|
+
info: treePrimitives.blue[300]
|
|
226
441
|
},
|
|
227
442
|
// Same hue order stepped for the dark surface (not a separate palette) —
|
|
228
443
|
// each step re-validated for the dark band and >= 3:1 on the dark surface.
|
|
229
|
-
chart:
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
3: "#c98500",
|
|
233
|
-
4: "#008300",
|
|
234
|
-
5: "#9085e9",
|
|
235
|
-
6: "#e66767",
|
|
236
|
-
7: "#d55181",
|
|
237
|
-
8: "#d95926"
|
|
238
|
-
},
|
|
239
|
-
overlay: "rgba(13, 17, 23, 0.6)",
|
|
240
|
-
"focus-ring": "rgba(83, 155, 245, 0.32)"
|
|
444
|
+
chart: treePrimitives.dataviz.dark,
|
|
445
|
+
overlay: alpha(treePrimitives.gray[950], treeAlpha.overlay.dark),
|
|
446
|
+
"focus-ring": alpha(treePrimitives.blue[400], treeAlpha.focusRing)
|
|
241
447
|
}
|
|
242
448
|
}
|
|
243
449
|
};
|
|
244
450
|
|
|
451
|
+
// ../tokens/src/states.ts
|
|
452
|
+
var AA_UI = 3;
|
|
453
|
+
var AA_TEXT = 4.5;
|
|
454
|
+
var WHITE2 = { r: 255, g: 255, b: 255 };
|
|
455
|
+
var BLACK2 = { r: 0, g: 0, b: 0 };
|
|
456
|
+
var STEP = {
|
|
457
|
+
hover: 0.18,
|
|
458
|
+
press: 0.32,
|
|
459
|
+
/** Tint states move toward the solid colour instead of toward black/white. */
|
|
460
|
+
softHover: 0.14,
|
|
461
|
+
softPress: 0.26,
|
|
462
|
+
/** Neutral row/menu surfaces, as a fraction of the ink mixed into the surface. */
|
|
463
|
+
neutralHover: 0.06,
|
|
464
|
+
neutralPress: 0.12
|
|
465
|
+
};
|
|
466
|
+
var DEGENERATE = 1.12;
|
|
467
|
+
var step = (color, amount, mode) => {
|
|
468
|
+
const preferred = mode === "light" ? darken(color, amount) : lighten(color, amount);
|
|
469
|
+
if (contrastRatio(preferred, color) >= DEGENERATE) return preferred;
|
|
470
|
+
const flipped = mode === "light" ? lighten(color, amount) : darken(color, amount);
|
|
471
|
+
return contrastRatio(flipped, color) > contrastRatio(preferred, color) ? flipped : preferred;
|
|
472
|
+
};
|
|
473
|
+
var fadeToward = (from, to, against, minRatio) => {
|
|
474
|
+
let best = from;
|
|
475
|
+
for (let amount = 0.05; amount <= 0.9; amount += 0.05) {
|
|
476
|
+
const candidate = mixColors(from, to, amount);
|
|
477
|
+
if (contrastRatio(candidate, against) < minRatio) break;
|
|
478
|
+
best = candidate;
|
|
479
|
+
}
|
|
480
|
+
return best;
|
|
481
|
+
};
|
|
482
|
+
var quantize = (color) => parseHex(formatHex(color));
|
|
483
|
+
var untilLegible = (color, against, minRatio) => {
|
|
484
|
+
if (contrastRatio(color, against) >= minRatio) return color;
|
|
485
|
+
const goLighter = relativeLuminance(color) >= relativeLuminance(against);
|
|
486
|
+
let candidate = color;
|
|
487
|
+
for (let i = 0; i < 24; i += 1) {
|
|
488
|
+
candidate = goLighter ? lighten(candidate, 0.05) : darken(candidate, 0.05);
|
|
489
|
+
if (contrastRatio(candidate, against) >= minRatio) return candidate;
|
|
490
|
+
}
|
|
491
|
+
return bestContrast(against, [WHITE2, BLACK2]);
|
|
492
|
+
};
|
|
493
|
+
var STATUS_KEYS = ["success", "warning", "error", "info"];
|
|
494
|
+
var deriveStateColors = (color, mode) => {
|
|
495
|
+
const surface = parseHex(color.bg.surface);
|
|
496
|
+
const subtle = parseHex(color.bg.subtle);
|
|
497
|
+
const ink = parseHex(color.text.primary);
|
|
498
|
+
const muted = parseHex(color.text.muted);
|
|
499
|
+
const inverse = parseHex(color.text.inverse);
|
|
500
|
+
const familyStates = (primary, soft) => {
|
|
501
|
+
const base = parseHex(primary);
|
|
502
|
+
const tint = parseHex(soft);
|
|
503
|
+
const softHover = quantize(mixColors(tint, base, STEP.softHover));
|
|
504
|
+
const softPress = quantize(mixColors(tint, base, STEP.softPress));
|
|
505
|
+
return {
|
|
506
|
+
press: formatHex(step(base, STEP.press, mode)),
|
|
507
|
+
"soft-hover": formatHex(softHover),
|
|
508
|
+
"soft-press": formatHex(softPress),
|
|
509
|
+
"on-soft": formatHex(untilLegible(base, tint, AA_TEXT)),
|
|
510
|
+
"on-soft-hover": formatHex(untilLegible(base, softHover, AA_TEXT)),
|
|
511
|
+
"on-soft-press": formatHex(untilLegible(base, softPress, AA_TEXT))
|
|
512
|
+
};
|
|
513
|
+
};
|
|
514
|
+
const brand = familyStates(color.brand.primary, color.brand.soft);
|
|
515
|
+
const accent = familyStates(color.accent.primary, color.accent.soft);
|
|
516
|
+
const status = Object.fromEntries(
|
|
517
|
+
STATUS_KEYS.flatMap((key) => {
|
|
518
|
+
const base = parseHex(color.status[key]);
|
|
519
|
+
const tint = quantize(mixColors(surface, base, 0.14));
|
|
520
|
+
const softHover = quantize(mixColors(tint, base, STEP.softHover));
|
|
521
|
+
return [
|
|
522
|
+
[`${key}-hover`, formatHex(step(base, STEP.hover, mode))],
|
|
523
|
+
[`${key}-press`, formatHex(step(base, STEP.press, mode))],
|
|
524
|
+
[`${key}-soft`, formatHex(tint)],
|
|
525
|
+
[`${key}-soft-hover`, formatHex(softHover)],
|
|
526
|
+
/**
|
|
527
|
+
* The alert's edge. Mixing the neutral border toward the status by a
|
|
528
|
+
* fixed 36% — the recipe the stylesheet used inline — produced a
|
|
529
|
+
* 1.8:1 edge on the tint, i.e. an edge nobody could see. Derived from
|
|
530
|
+
* the status colour instead, softened as far as 3:1 allows.
|
|
531
|
+
*/
|
|
532
|
+
[`${key}-border`, formatHex(quantize(fadeToward(base, tint, tint, AA_UI)))],
|
|
533
|
+
[`${key}-contrast`, formatHex(bestContrast(base, [WHITE2, inverse]))],
|
|
534
|
+
// The status colour as ink on its own tint — soft alerts and badges.
|
|
535
|
+
[`${key}-on-soft`, formatHex(untilLegible(base, tint, AA_TEXT))],
|
|
536
|
+
[`${key}-on-soft-hover`, formatHex(untilLegible(base, softHover, AA_TEXT))]
|
|
537
|
+
];
|
|
538
|
+
})
|
|
539
|
+
);
|
|
540
|
+
const disabledBg = subtle;
|
|
541
|
+
const disabledFg = quantize(fadeToward(muted, disabledBg, disabledBg, AA_UI));
|
|
542
|
+
const disabledBorder = mixColors(parseHex(color.border.default), disabledBg, 0.45);
|
|
543
|
+
return {
|
|
544
|
+
border: {
|
|
545
|
+
/**
|
|
546
|
+
* Control boundaries. Explicit in the semantic layer when a product sets
|
|
547
|
+
* one; derived here from `border.strong` otherwise, pushed until it
|
|
548
|
+
* clears 3:1 on the *subtle* surface — the worst of the three, and the
|
|
549
|
+
* one inputs actually sit on inside a card.
|
|
550
|
+
*/
|
|
551
|
+
interactive: color.border.interactive ?? formatHex(untilLegible(parseHex(color.border.strong), subtle, AA_UI))
|
|
552
|
+
},
|
|
553
|
+
brand,
|
|
554
|
+
accent,
|
|
555
|
+
status,
|
|
556
|
+
state: {
|
|
557
|
+
/** Neutral interactive surfaces: menu items, table rows, ghost buttons. */
|
|
558
|
+
"hover-bg": formatHex(quantize(mixColors(surface, ink, STEP.neutralHover))),
|
|
559
|
+
"press-bg": formatHex(quantize(mixColors(surface, ink, STEP.neutralPress))),
|
|
560
|
+
/**
|
|
561
|
+
* Selection reads as the brand tint. The ink is the brand's derived
|
|
562
|
+
* on-tint ink, not `brand.primary` — on the shipped dark tint those
|
|
563
|
+
* differ, and the raw brand measured 4.44:1 there.
|
|
564
|
+
*/
|
|
565
|
+
"selected-bg": color.brand.soft,
|
|
566
|
+
"selected-fg": brand["on-soft"],
|
|
567
|
+
"selected-border": color.brand.primary,
|
|
568
|
+
"disabled-bg": formatHex(disabledBg),
|
|
569
|
+
"disabled-fg": formatHex(disabledFg),
|
|
570
|
+
"disabled-border": formatHex(disabledBorder)
|
|
571
|
+
}
|
|
572
|
+
};
|
|
573
|
+
};
|
|
574
|
+
|
|
245
575
|
// src/token-source.ts
|
|
246
576
|
var toCssVariable = (path2) => `--tree-${path2.join("-")}`;
|
|
247
577
|
var flattenTokens = (record, path2 = []) => Object.entries(record).flatMap(([key, value]) => {
|
|
@@ -278,21 +608,29 @@ var buildTokenEntries = () => {
|
|
|
278
608
|
);
|
|
279
609
|
const themed = /* @__PURE__ */ new Map();
|
|
280
610
|
for (const [themeName, theme] of Object.entries(treeThemes)) {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
611
|
+
const mode = themeName === "dark" ? "dark" : "light";
|
|
612
|
+
const derived = { color: deriveStateColors(theme.color, mode) };
|
|
613
|
+
for (const [source, entries] of [
|
|
614
|
+
["semantic", flattenTokens(theme)],
|
|
615
|
+
["derived", flattenTokens(derived)]
|
|
616
|
+
]) {
|
|
617
|
+
for (const [path2, value] of entries) {
|
|
618
|
+
const cssVar = toCssVariable(path2);
|
|
619
|
+
const existing = themed.get(cssVar);
|
|
620
|
+
if (existing) {
|
|
621
|
+
existing.themeValues = { ...existing.themeValues, [themeName]: String(value) };
|
|
622
|
+
continue;
|
|
623
|
+
}
|
|
624
|
+
themed.set(cssVar, {
|
|
625
|
+
cssVar,
|
|
626
|
+
path: path2.join("."),
|
|
627
|
+
value: String(value),
|
|
628
|
+
category: path2[0],
|
|
629
|
+
derived: source === "derived",
|
|
630
|
+
description: source === "derived" ? `${describeToken(path2[0])} Derived by the library from the semantic layer \u2014 read it, do not override it.` : describeToken(path2[0]),
|
|
631
|
+
themeValues: { [themeName]: String(value) }
|
|
632
|
+
});
|
|
287
633
|
}
|
|
288
|
-
themed.set(cssVar, {
|
|
289
|
-
cssVar,
|
|
290
|
-
path: path2.join("."),
|
|
291
|
-
value: String(value),
|
|
292
|
-
category: path2[0],
|
|
293
|
-
description: describeToken(path2[0]),
|
|
294
|
-
themeValues: { [themeName]: String(value) }
|
|
295
|
-
});
|
|
296
634
|
}
|
|
297
635
|
}
|
|
298
636
|
for (const entry of themed.values()) {
|
|
@@ -542,15 +880,24 @@ var buildStoryIndex = (repoRoot) => {
|
|
|
542
880
|
continue;
|
|
543
881
|
}
|
|
544
882
|
const componentName = componentMatch[1];
|
|
545
|
-
const
|
|
546
|
-
|
|
883
|
+
const storyTitle = [...source.matchAll(/title:\s*'([^']+)'/g)].map((match) => match[1]).find((value) => /^(Components|Foundation|Patterns|Showcase)\/.+/.test(value));
|
|
884
|
+
if (!storyTitle) {
|
|
885
|
+
console.warn(
|
|
886
|
+
`[catalog] ${fileName} declares component: ${componentName} but no Storybook meta title \u2014 it will have no category or docs link.`
|
|
887
|
+
);
|
|
888
|
+
}
|
|
547
889
|
const titleParts = storyTitle?.split("/") ?? [];
|
|
548
890
|
const category = titleParts.length > 2 ? titleParts.slice(1, -1).join(" / ") : titleParts[1];
|
|
549
|
-
|
|
891
|
+
const entry = {
|
|
550
892
|
storyFile: path.relative(repoRoot, filePath),
|
|
551
893
|
storyTitle,
|
|
552
894
|
category
|
|
553
|
-
}
|
|
895
|
+
};
|
|
896
|
+
index.set(componentName, entry);
|
|
897
|
+
const fileComponent = `T${fileName.replace(/\.stories\.ts$/, "")}`;
|
|
898
|
+
if (!index.has(fileComponent)) {
|
|
899
|
+
index.set(fileComponent, entry);
|
|
900
|
+
}
|
|
554
901
|
}
|
|
555
902
|
return index;
|
|
556
903
|
};
|
package/package.json
CHANGED
|
@@ -1,8 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@treeui/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "Model Context Protocol server and AI catalog for TreeUI.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "TreeUI",
|
|
7
|
+
"homepage": "https://viserion77.github.io/treeui/",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/Viserion77/treeui.git",
|
|
11
|
+
"directory": "packages/mcp"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/Viserion77/treeui/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"treeui",
|
|
18
|
+
"mcp",
|
|
19
|
+
"model-context-protocol",
|
|
20
|
+
"ai",
|
|
21
|
+
"design-system"
|
|
22
|
+
],
|
|
5
23
|
"type": "module",
|
|
24
|
+
"sideEffects": false,
|
|
6
25
|
"main": "./dist/index.cjs",
|
|
7
26
|
"module": "./dist/index.js",
|
|
8
27
|
"types": "./dist/index.d.ts",
|
|
@@ -20,8 +39,8 @@
|
|
|
20
39
|
"dist"
|
|
21
40
|
],
|
|
22
41
|
"dependencies": {
|
|
23
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
24
|
-
"yaml": "^2.9.
|
|
42
|
+
"@modelcontextprotocol/sdk": "^1.30.1",
|
|
43
|
+
"yaml": "^2.9.1"
|
|
25
44
|
},
|
|
26
45
|
"scripts": {
|
|
27
46
|
"generate:catalog": "node --import tsx scripts/generate-catalog.ts",
|