fractalstyler2 0.4.0 → 0.7.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/README.md +114 -95
- package/dist/cli.js +105 -25
- package/dist/components/Accordion.svelte +44 -0
- package/dist/components/Accordion.svelte.d.ts +23 -0
- package/dist/components/AccordionItem.svelte +44 -0
- package/dist/components/AccordionItem.svelte.d.ts +21 -0
- package/dist/components/AppShell.svelte +86 -0
- package/dist/components/AppShell.svelte.d.ts +39 -0
- package/dist/components/ColorPicker.svelte +38 -0
- package/dist/components/ColorPicker.svelte.d.ts +6 -0
- package/dist/components/Hero.svelte +14 -0
- package/dist/components/Hero.svelte.d.ts +9 -0
- package/dist/components/LayoutPicker.svelte +34 -0
- package/dist/components/LayoutPicker.svelte.d.ts +6 -0
- package/dist/components/MotionPicker.svelte +34 -0
- package/dist/components/MotionPicker.svelte.d.ts +6 -0
- package/dist/components/PageShell.svelte +14 -0
- package/dist/components/PageShell.svelte.d.ts +9 -0
- package/dist/components/PageSplit.svelte +26 -0
- package/dist/components/PageSplit.svelte.d.ts +18 -0
- package/dist/components/ShapePicker.svelte +37 -0
- package/dist/components/ShapePicker.svelte.d.ts +6 -0
- package/dist/css/fractalstyler.css +10643 -0
- package/dist/css/fractalstyler.min.css +2 -0
- package/dist/index.d.ts +18 -6
- package/dist/index.js +30 -12
- package/dist/mcp/schemas/compile_fractals.json +3 -3
- package/dist/mcp/schemas/css_to_fractals.json +1 -1
- package/dist/mcp/schemas/generate_component.json +2 -2
- package/dist/mcp/schemas/instructions.md +41 -14
- package/dist/mcp/schemas/list_fractals.json +9 -7
- package/dist/mcp/schemas/validate_recipe.json +2 -2
- package/dist/mcp/server.d.ts +1 -1
- package/dist/mcp/server.js +334 -237
- package/dist/presets.core.d.ts +42 -0
- package/dist/presets.core.js +190 -0
- package/dist/presets.svelte.d.ts +6 -0
- package/dist/presets.svelte.js +21 -0
- package/dist/styles/_00_presets.sass +111 -0
- package/dist/styles/_00_themes.sass +1061 -0
- package/dist/styles/_00_tokens.sass +57 -17
- package/dist/styles/_01_config.sass +77 -70
- package/dist/styles/_02_dimensions.sass +174 -0
- package/dist/styles/_03_containers.sass +105 -0
- package/dist/styles/_04_layouts.sass +100 -0
- package/dist/styles/_05_shells.sass +374 -0
- package/dist/styles/_06_visuals.sass +236 -0
- package/dist/styles/_07_interactions.sass +112 -0
- package/dist/styles/_08_own.sass +4 -0
- package/dist/styles/canonical-markups.md +160 -0
- package/dist/styles/index.sass +11 -18
- package/dist/themes.d.ts +7 -0
- package/dist/themes.js +45 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +2 -0
- package/package.json +26 -15
- package/plugin.json +11 -10
- package/registry.json +2002 -0
- package/skills/fractal-styler/SKILL.md +137 -50
- package/skills/fractal-styler/references/fractals.md +416 -28
- package/skills/fractal-styler/references/tokens.md +110 -36
- package/dist/styles/_02_fonts.sass +0 -13
- package/dist/styles/_03_responsive.sass +0 -31
- package/dist/styles/_04_atoms.sass +0 -155
- package/dist/styles/_05_molecules.sass +0 -97
- package/dist/styles/_06_recipes.sass +0 -189
- package/dist/styles/_07_base.sass +0 -44
- package/dist/styles/_08_blocks.sass +0 -433
- package/dist/styles/_09_utilities.sass +0 -208
- package/dist/styles/_10_layouts.sass +0 -373
- package/dist/styles/_11_own.sass +0 -21
- package/dist/styles/_fractals.sass +0 -14
- package/skills/style-migration/SKILL.md +0 -45
- package/templates/_00_tokens.sass +0 -136
- package/templates/_01_config.sass +0 -66
- package/templates/_02_fonts.sass +0 -13
- package/templates/_03_responsive.sass +0 -31
- package/templates/_04_atoms.sass +0 -155
- package/templates/_05_molecules.sass +0 -97
- package/templates/_06_recipes.sass +0 -189
- package/templates/_07_base.sass +0 -44
- package/templates/_08_blocks.sass +0 -433
- package/templates/_09_utilities.sass +0 -207
- package/templates/_10_layouts.sass +0 -373
- package/templates/_11_own.sass +0 -21
- package/templates/_fractals.sass +0 -14
- package/templates/index.sass +0 -18
package/dist/mcp/server.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
3
|
* fractalstyler2 MCP Server
|
|
4
|
-
* Model Context Protocol server exposing design tokens,
|
|
4
|
+
* Model Context Protocol server exposing design tokens, the class registry,
|
|
5
5
|
* token snapping, component generation, and linting for OpenDesign, Claude Desktop, Cursor, etc.
|
|
6
6
|
*/
|
|
7
7
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
@@ -11,16 +11,27 @@ import * as sass from 'sass';
|
|
|
11
11
|
import { existsSync, readFileSync } from 'node:fs';
|
|
12
12
|
import { dirname, join } from 'node:path';
|
|
13
13
|
import { fileURLToPath } from 'node:url';
|
|
14
|
-
const VERSION =
|
|
14
|
+
const VERSION = (() => {
|
|
15
|
+
// Read from package.json so this can never drift from the published version.
|
|
16
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
for (const c of [join(HERE, '..', '..', 'package.json'), join(HERE, '..', 'package.json')]) {
|
|
18
|
+
try {
|
|
19
|
+
if (existsSync(c))
|
|
20
|
+
return JSON.parse(readFileSync(c, 'utf8')).version;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
/* fall through */
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return '0.0.0';
|
|
27
|
+
})();
|
|
15
28
|
// Resolve styles directory for SASS compiler loadPaths
|
|
16
29
|
function getStylesDir() {
|
|
17
30
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
18
31
|
const candidates = [
|
|
19
|
-
join(HERE, '..', 'styles'),
|
|
20
|
-
join(HERE, '..', '..', 'templates'),
|
|
32
|
+
join(HERE, '..', 'styles'), // dist/mcp -> dist/styles
|
|
21
33
|
join(HERE, 'styles'),
|
|
22
|
-
join(process.cwd(), 'src', 'lib', 'styles')
|
|
23
|
-
join(process.cwd(), 'templates')
|
|
34
|
+
join(process.cwd(), 'src', 'lib', 'styles')
|
|
24
35
|
];
|
|
25
36
|
for (const candidate of candidates) {
|
|
26
37
|
if (existsSync(candidate) && existsSync(join(candidate, '_00_tokens.sass'))) {
|
|
@@ -40,29 +51,28 @@ const DESIGN_TOKENS = {
|
|
|
40
51
|
xl: '1240px'
|
|
41
52
|
},
|
|
42
53
|
space: {
|
|
43
|
-
scale: ['3xs', '2xs', 'xs', '
|
|
54
|
+
scale: ['3xs', '2xs', 'xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'],
|
|
44
55
|
clamp: {
|
|
45
56
|
'3xs': 'clamp(0.3125rem, 0.3125rem + 0vw, 0.3125rem)', // ~5px
|
|
46
57
|
'2xs': 'clamp(0.5625rem, 0.5369rem + 0.1136vw, 0.625rem)', // ~9-10px
|
|
47
58
|
xs: 'clamp(0.875rem, 0.8494rem + 0.1136vw, 0.9375rem)', // ~14-15px
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
59
|
+
sm: 'clamp(1.125rem, 1.0739rem + 0.2273vw, 1.25rem)', // ~18-20px
|
|
60
|
+
md: 'clamp(1.6875rem, 1.6108rem + 0.3409vw, 1.875rem)', // ~27-30px
|
|
61
|
+
lg: 'clamp(2.25rem, 2.1477rem + 0.4545vw, 2.5rem)', // ~36-40px
|
|
51
62
|
xl: 'clamp(3.375rem, 3.2216rem + 0.6818vw, 3.75rem)', // ~54-60px
|
|
52
63
|
'2xl': 'clamp(4.5rem, 4.2955rem + 0.9091vw, 5rem)', // ~72-80px
|
|
53
|
-
'3xl': 'clamp(6.75rem, 6.4432rem + 1.3636vw, 7.5rem)'
|
|
54
|
-
's-l': 'clamp(1.125rem, 0.5625rem + 2.5vw, 2.5rem)' // fluid s to l
|
|
64
|
+
'3xl': 'clamp(6.75rem, 6.4432rem + 1.3636vw, 7.5rem)' // ~108-120px
|
|
55
65
|
},
|
|
56
66
|
approxPx: {
|
|
57
67
|
'3xs': 5,
|
|
58
68
|
'2xs': 9,
|
|
59
69
|
xs: 14,
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
xl:
|
|
64
|
-
'2xl':
|
|
65
|
-
'3xl':
|
|
70
|
+
sm: 18,
|
|
71
|
+
md: 28,
|
|
72
|
+
lg: 38,
|
|
73
|
+
xl: 56,
|
|
74
|
+
'2xl': 76,
|
|
75
|
+
'3xl': 114
|
|
66
76
|
}
|
|
67
77
|
},
|
|
68
78
|
typography: {
|
|
@@ -132,9 +142,20 @@ const DESIGN_TOKENS = {
|
|
|
132
142
|
themeColor: 'var(--theme-color)',
|
|
133
143
|
themeColorAlt: 'var(--theme-color-alt)'
|
|
134
144
|
},
|
|
145
|
+
status: {
|
|
146
|
+
success: 'var(--success)',
|
|
147
|
+
successHover: 'var(--success-hover)',
|
|
148
|
+
warning: 'var(--warning)',
|
|
149
|
+
warningHover: 'var(--warning-hover)',
|
|
150
|
+
danger: 'var(--danger)',
|
|
151
|
+
dangerHover: 'var(--danger-hover)',
|
|
152
|
+
info: 'var(--info)',
|
|
153
|
+
infoHover: 'var(--info-hover)',
|
|
154
|
+
feedbackError: 'var(--feedback-error)'
|
|
155
|
+
},
|
|
135
156
|
brand: {
|
|
136
|
-
theme: 'var(--theme)',
|
|
137
|
-
themeHover: 'var(--theme-
|
|
157
|
+
theme: 'var(--theme-color)',
|
|
158
|
+
themeHover: 'var(--theme-color-alt)',
|
|
138
159
|
themeActive: 'var(--theme-active)',
|
|
139
160
|
ring: 'var(--ring)'
|
|
140
161
|
},
|
|
@@ -147,76 +168,57 @@ const DESIGN_TOKENS = {
|
|
|
147
168
|
}
|
|
148
169
|
};
|
|
149
170
|
// Catalog of Fractals (Mixins)
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
{ name: 'stack', signature: '+stack($gap: xs, $x: null)', description: 'Vertical rhythm: flex column + gap.' },
|
|
186
|
-
{ name: 'cluster', signature: '+cluster($gap: xs, $x: start, $y: center)', description: 'Wrapping row for tags/buttons/chips.' },
|
|
187
|
-
{ name: 'center-column', signature: '+center-column($max: var(--measure, 60ch), $pad: s)', description: 'Bounded reading column with max-width measure.' },
|
|
188
|
-
{ name: 'cover', signature: '+cover($min: 100vh, $pad: s)', description: 'Full-height container with vertically centered focal child.' },
|
|
189
|
-
{ name: 'frame', signature: '+frame($ratio: 16 / 9)', description: 'Aspect-ratio container for media (images, video, iframe).' },
|
|
190
|
-
{ name: 'reel', signature: '+reel($gap: xs)', description: 'Horizontal scroll-snap rail.' },
|
|
191
|
-
{ name: 'with-sidebar', signature: '+with-sidebar($rail: 240px, $gap: s, $min: 60%)', description: 'Intrinsic sidebar and fluid main content.' },
|
|
192
|
-
{ name: 'surface', signature: '+surface($bg: surface, $pad: null, $radius: 6, $elevation: none)', description: 'All-in-one material fractal: skin, radius, pad, and elevation.' },
|
|
193
|
-
{ name: 'cols', signature: '+cols($map, $gap: s)', description: 'Responsive column grid mapped across breakpoints, e.g. (base: 1, sm: 2, lg: 3).' }
|
|
194
|
-
],
|
|
195
|
-
recipes: [
|
|
196
|
-
{ name: 'card', signature: '+card($bg: surface, $pad: null, $radius: 6, $elevation: none)', description: 'Vertical card container recipe with optional pad and elevation.' },
|
|
197
|
-
{ name: 'control', signature: '+control($size: md, $radius: 4)', description: 'Universal interactive control recipe (buttons, triggers, inputs).' },
|
|
198
|
-
{ name: 'select', signature: '+select($size: md, $radius: 4)', description: 'Select input recipe with embedded SVG chevron.' },
|
|
199
|
-
{ name: 'badge', signature: '+badge($radius: 4)', description: 'Compact status badge recipe.' }
|
|
200
|
-
],
|
|
201
|
-
layouts: [
|
|
202
|
-
{ name: 'grid-3', class: '.grid-3', description: 'Responsive 1 → 2 → 3 column reflow.' },
|
|
203
|
-
{ name: 'card-grid', class: '.card-grid', description: 'Intrinsic auto-fit grid for cards.' },
|
|
204
|
-
{ name: 'hero', class: '.hero', description: 'Full viewport cover with centered hero message.' },
|
|
205
|
-
{ name: 'holy-grail', class: '.holy-grail', description: 'Responsive header / (nav · main · aside) / footer.' },
|
|
206
|
-
{ name: 'docs', class: '.docs', description: 'Docs template with sidebar nav, center reading column, and right TOC.' },
|
|
207
|
-
{ name: 'app-shell', class: '.app-shell', description: 'Sticky header, fluid body, and footer application frame.' }
|
|
208
|
-
]
|
|
171
|
+
/**
|
|
172
|
+
* The class registry — the public API — read from registry.json.
|
|
173
|
+
*
|
|
174
|
+
* This was previously a hardcoded catalog of authoring mixins (+box, +stack,
|
|
175
|
+
* +surface...). Those never shipped in v2; the registry replaced them. It is
|
|
176
|
+
* loaded from the generated file now so this surface cannot describe an API
|
|
177
|
+
* the stylesheet does not have.
|
|
178
|
+
*/
|
|
179
|
+
function getRegistryPath() {
|
|
180
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
181
|
+
const candidates = [
|
|
182
|
+
join(HERE, '..', '..', 'registry.json'), // dist/mcp -> package root
|
|
183
|
+
join(HERE, '..', 'registry.json'),
|
|
184
|
+
join(process.cwd(), 'registry.json')
|
|
185
|
+
];
|
|
186
|
+
return candidates.find((c) => existsSync(c)) ?? null;
|
|
187
|
+
}
|
|
188
|
+
const REGISTRY = (() => {
|
|
189
|
+
const p = getRegistryPath();
|
|
190
|
+
if (!p)
|
|
191
|
+
return [];
|
|
192
|
+
try {
|
|
193
|
+
return JSON.parse(readFileSync(p, 'utf8'));
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
return [];
|
|
197
|
+
}
|
|
198
|
+
})();
|
|
199
|
+
const LAYER_NAMES = {
|
|
200
|
+
L0: 'Tokens',
|
|
201
|
+
L1: 'Dimensions',
|
|
202
|
+
L2: 'Containers',
|
|
203
|
+
L3: 'Layouts',
|
|
204
|
+
L4: 'Shells',
|
|
205
|
+
L5: 'Visuals & Interactions'
|
|
209
206
|
};
|
|
207
|
+
/** Registry grouped by layer, which is what list_fractals serves. */
|
|
208
|
+
const FRACTAL_CATALOG = REGISTRY.reduce((acc, entry) => {
|
|
209
|
+
(acc[entry.layer] ??= []).push(entry);
|
|
210
|
+
return acc;
|
|
211
|
+
}, {});
|
|
210
212
|
// Guidelines & Golden Rules
|
|
211
213
|
const GUIDELINES = `
|
|
212
214
|
# fractalstyler2 Design System Rules
|
|
213
215
|
|
|
214
|
-
1. Never hardcode a value that a token covers
|
|
215
|
-
2.
|
|
216
|
-
3.
|
|
217
|
-
4.
|
|
218
|
-
5. Mobile-first: define base
|
|
219
|
-
6.
|
|
216
|
+
1. Never hardcode a value that a token covers: reach for .gap-sm, .radius-md, .surface. Literals like .gap-16 are the sanctioned escape hatch when an exact pixel is load-bearing.
|
|
217
|
+
2. Classes are the ONLY public API (.gap-sm, .pad-x-sm, .box.xcenter, .button.primary). The system defines no authoring mixins and no SASS functions; +stack, +surface, space() and the like do not exist.
|
|
218
|
+
3. Visual toggles ride classes (.open, .active, .elevated); semantic state stays on native attributes ([disabled], [aria-expanded='true'], :focus-visible).
|
|
219
|
+
4. Do NOT invent class names. A composed string like class="row ycenter xbetween gap-sm pad-md surface border" is the finished state, not something to tidy into a semantic class. _08_own.sass is for third-party widget overrides only; check docs/13-cookbook.md before writing any custom declaration.
|
|
220
|
+
5. Mobile-first: define base classes first, then bind to a side of the 768px seam with the -mob / -desk suffixes (.pad-xs-mob .pad-lg-desk). Grids step on their own; .grid-6 goes 6 -> 3 -> 2 -> 1 and never strands a row.
|
|
221
|
+
6. Literal utilities come from a discrete ladder, not any integer: 0, 1, 2, 4, 6, 8, 12, then every multiple of 8 — to 64px for gap/pad/marg and radius, to 512px for w/h/square (.gap-16, .radius-4, .w-240). Negative margins use the -- infix (.marg--16).
|
|
220
222
|
`.trim();
|
|
221
223
|
// Snapping helper: find closest token
|
|
222
224
|
function findNearestToken(val, scaleMap) {
|
|
@@ -250,13 +252,13 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
250
252
|
tools: [
|
|
251
253
|
{
|
|
252
254
|
name: 'compile_fractals',
|
|
253
|
-
description: 'Compiles indented SASS
|
|
255
|
+
description: 'Compiles indented SASS into CSS to verify output. The system exposes no authoring mixins — compose in markup and use this for the rare custom declaration.',
|
|
254
256
|
inputSchema: {
|
|
255
257
|
type: 'object',
|
|
256
258
|
properties: {
|
|
257
259
|
sassCode: {
|
|
258
260
|
type: 'string',
|
|
259
|
-
description: 'Indented SASS code
|
|
261
|
+
description: 'Indented SASS code (plain declarations; the styles directory is on the load path).'
|
|
260
262
|
},
|
|
261
263
|
className: {
|
|
262
264
|
type: 'string',
|
|
@@ -295,7 +297,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
295
297
|
},
|
|
296
298
|
{
|
|
297
299
|
name: 'css_to_fractals',
|
|
298
|
-
description: 'Converts raw CSS declarations (from Figma/OpenDesign inspection) into
|
|
300
|
+
description: 'Converts raw CSS declarations (from Figma/OpenDesign inspection) into fractalstyler2 registry classes, composed in markup.',
|
|
299
301
|
inputSchema: {
|
|
300
302
|
type: 'object',
|
|
301
303
|
properties: {
|
|
@@ -309,7 +311,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
309
311
|
},
|
|
310
312
|
{
|
|
311
313
|
name: 'generate_component',
|
|
312
|
-
description: 'Generates a production-ready Svelte 5 component with runes and
|
|
314
|
+
description: 'Generates a production-ready Svelte 5 component with runes, composed entirely from registry classes and carrying no style block.',
|
|
313
315
|
inputSchema: {
|
|
314
316
|
type: 'object',
|
|
315
317
|
properties: {
|
|
@@ -331,25 +333,25 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
331
333
|
},
|
|
332
334
|
{
|
|
333
335
|
name: 'validate_recipe',
|
|
334
|
-
description: 'Lints
|
|
336
|
+
description: 'Lints markup against the golden rules: flags class names absent from the registry, component <style> blocks, and hardcoded pixel values.',
|
|
335
337
|
inputSchema: {
|
|
336
338
|
type: 'object',
|
|
337
339
|
properties: {
|
|
338
|
-
code: { type: 'string', description: 'The
|
|
340
|
+
code: { type: 'string', description: 'The Svelte or HTML markup to validate.' }
|
|
339
341
|
},
|
|
340
342
|
required: ['code']
|
|
341
343
|
}
|
|
342
344
|
},
|
|
343
345
|
{
|
|
344
346
|
name: 'list_fractals',
|
|
345
|
-
description: 'Returns the
|
|
347
|
+
description: 'Returns the class registry — the public API. Every composable class with the CSS it applies and its source file, grouped by layer. Query this before naming any class.',
|
|
346
348
|
inputSchema: {
|
|
347
349
|
type: 'object',
|
|
348
350
|
properties: {
|
|
349
|
-
|
|
351
|
+
layer: {
|
|
350
352
|
type: 'string',
|
|
351
|
-
enum: ['all', '
|
|
352
|
-
description: 'Filter by
|
|
353
|
+
enum: ['all', 'L0', 'L1', 'L2', 'L3', 'L4', 'L5'],
|
|
354
|
+
description: 'Filter by layer: L0 tokens, L1 dimensions, L2 containers, L3 layouts, L4 shells, L5 visuals & interactions.'
|
|
353
355
|
}
|
|
354
356
|
}
|
|
355
357
|
}
|
|
@@ -372,12 +374,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
372
374
|
.split('\n')
|
|
373
375
|
.map((line) => (line.trim() ? `\t${line}` : ''))
|
|
374
376
|
.join('\n');
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
.${className}
|
|
377
|
+
// No @use is injected: the system exposes no mixin module to import.
|
|
378
|
+
// loadPaths is set to the styles directory, so a caller that wants
|
|
379
|
+
// the token layer can `@use '00_tokens'` explicitly.
|
|
380
|
+
const fullSass = `.${className}
|
|
381
381
|
${indented}
|
|
382
382
|
`;
|
|
383
383
|
try {
|
|
@@ -426,7 +426,6 @@ ${indented}
|
|
|
426
426
|
inputPx: args.gap,
|
|
427
427
|
nearestToken: match.token,
|
|
428
428
|
cssVar: `var(--space-${match.token})`,
|
|
429
|
-
suggestedMixin: `+gap(${match.token})`,
|
|
430
429
|
utilityClass: `.gap-${match.token}`
|
|
431
430
|
};
|
|
432
431
|
}
|
|
@@ -436,7 +435,6 @@ ${indented}
|
|
|
436
435
|
inputPx: args.padding,
|
|
437
436
|
nearestToken: match.token,
|
|
438
437
|
cssVar: `var(--space-${match.token})`,
|
|
439
|
-
suggestedMixin: `+pad(${match.token})`,
|
|
440
438
|
utilityClass: `.pad-${match.token}`
|
|
441
439
|
};
|
|
442
440
|
}
|
|
@@ -447,7 +445,6 @@ ${indented}
|
|
|
447
445
|
inputPx: args.radius,
|
|
448
446
|
nearestToken: match.token,
|
|
449
447
|
cssVar: `var(--radius-${match.token})`,
|
|
450
|
-
suggestedMixin: `+radius(${match.token})`,
|
|
451
448
|
utilityClass: `.radius-${match.token}`
|
|
452
449
|
};
|
|
453
450
|
}
|
|
@@ -457,7 +454,6 @@ ${indented}
|
|
|
457
454
|
inputPx: args.fontSize,
|
|
458
455
|
nearestToken: match.token,
|
|
459
456
|
cssVar: `var(--text-${match.token})`,
|
|
460
|
-
suggestedMixin: `+type(${match.token})`,
|
|
461
457
|
utilityClass: `.text-${match.token}`
|
|
462
458
|
};
|
|
463
459
|
}
|
|
@@ -466,81 +462,197 @@ ${indented}
|
|
|
466
462
|
};
|
|
467
463
|
}
|
|
468
464
|
case 'css_to_fractals': {
|
|
465
|
+
// Emits registry CLASSES, not mixins. The system has no authoring
|
|
466
|
+
// mixins; earlier versions of this tool suggested +surface/+stack,
|
|
467
|
+
// which do not compile.
|
|
469
468
|
const rawCss = args.css || '';
|
|
470
|
-
const
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
let
|
|
477
|
-
let
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
const
|
|
481
|
-
|
|
469
|
+
const decls = rawCss
|
|
470
|
+
.split(/[;\n]/)
|
|
471
|
+
.map((l) => l.trim())
|
|
472
|
+
.filter(Boolean);
|
|
473
|
+
const classes = [];
|
|
474
|
+
const unmapped = [];
|
|
475
|
+
let display = null;
|
|
476
|
+
let direction = null;
|
|
477
|
+
const spaceStep = (n) => findNearestToken(n, DESIGN_TOKENS.space.approxPx).token;
|
|
478
|
+
const px = (v) => {
|
|
479
|
+
const n = parseInt(v, 10);
|
|
480
|
+
return isNaN(n) ? null : n;
|
|
481
|
+
};
|
|
482
|
+
for (const decl of decls) {
|
|
483
|
+
const idx = decl.indexOf(':');
|
|
484
|
+
if (idx < 0)
|
|
482
485
|
continue;
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
486
|
+
const prop = decl.slice(0, idx).trim();
|
|
487
|
+
const val = decl.slice(idx + 1).trim();
|
|
488
|
+
const n = px(val);
|
|
489
|
+
switch (prop) {
|
|
490
|
+
case 'display':
|
|
491
|
+
display = val;
|
|
492
|
+
break;
|
|
493
|
+
case 'flex-direction':
|
|
494
|
+
direction = val;
|
|
495
|
+
break;
|
|
496
|
+
case 'gap':
|
|
497
|
+
if (n !== null)
|
|
498
|
+
classes.push(`gap-${spaceStep(n)}`);
|
|
499
|
+
break;
|
|
500
|
+
case 'row-gap':
|
|
501
|
+
if (n !== null)
|
|
502
|
+
classes.push(`rgap-${spaceStep(n)}`);
|
|
503
|
+
break;
|
|
504
|
+
case 'column-gap':
|
|
505
|
+
if (n !== null)
|
|
506
|
+
classes.push(`cgap-${spaceStep(n)}`);
|
|
507
|
+
break;
|
|
508
|
+
case 'padding':
|
|
509
|
+
if (n !== null)
|
|
510
|
+
classes.push(`pad-${spaceStep(n)}`);
|
|
511
|
+
break;
|
|
512
|
+
case 'padding-inline':
|
|
513
|
+
if (n !== null)
|
|
514
|
+
classes.push(`pad-x-${spaceStep(n)}`);
|
|
515
|
+
break;
|
|
516
|
+
case 'padding-block':
|
|
517
|
+
if (n !== null)
|
|
518
|
+
classes.push(`pad-y-${spaceStep(n)}`);
|
|
519
|
+
break;
|
|
520
|
+
case 'margin':
|
|
521
|
+
if (n !== null)
|
|
522
|
+
classes.push(`marg-${spaceStep(n)}`);
|
|
523
|
+
break;
|
|
524
|
+
case 'border-radius':
|
|
525
|
+
if (n !== null)
|
|
526
|
+
classes.push(`radius-${n}`);
|
|
527
|
+
break;
|
|
528
|
+
case 'width':
|
|
529
|
+
if (val === '100%')
|
|
530
|
+
classes.push('wfull');
|
|
531
|
+
else if (n !== null)
|
|
532
|
+
classes.push(`w-${n}`);
|
|
533
|
+
break;
|
|
534
|
+
case 'height':
|
|
535
|
+
if (val === '100%')
|
|
536
|
+
classes.push('hfull');
|
|
537
|
+
else if (n !== null)
|
|
538
|
+
classes.push(`h-${n}`);
|
|
539
|
+
break;
|
|
540
|
+
case 'background':
|
|
541
|
+
case 'background-color':
|
|
542
|
+
if (val.includes('raised'))
|
|
543
|
+
classes.push('raised');
|
|
544
|
+
else if (val.includes('panel'))
|
|
545
|
+
classes.push('panel');
|
|
546
|
+
else if (val.includes('surface') || /^#(fff|ffffff)$/i.test(val))
|
|
547
|
+
classes.push('surface');
|
|
548
|
+
else
|
|
549
|
+
unmapped.push(`${prop}: ${val}`);
|
|
550
|
+
break;
|
|
551
|
+
case 'color':
|
|
552
|
+
if (val.includes('muted'))
|
|
553
|
+
classes.push('text-muted');
|
|
554
|
+
else if (val.includes('secondary'))
|
|
555
|
+
classes.push('text-secondary');
|
|
556
|
+
else if (val.includes('primary'))
|
|
557
|
+
classes.push('text-primary');
|
|
558
|
+
else
|
|
559
|
+
unmapped.push(`${prop}: ${val}`);
|
|
560
|
+
break;
|
|
561
|
+
case 'border':
|
|
562
|
+
classes.push('border');
|
|
563
|
+
break;
|
|
564
|
+
case 'border-bottom':
|
|
565
|
+
classes.push('border-bottom');
|
|
566
|
+
break;
|
|
567
|
+
case 'border-top':
|
|
568
|
+
classes.push('border-top');
|
|
569
|
+
break;
|
|
570
|
+
case 'box-shadow': {
|
|
571
|
+
// Depth is a three-step scale; pick by the blur radius.
|
|
572
|
+
const blur = parseInt(val.split(/\s+/)[3] ?? '0', 10);
|
|
573
|
+
classes.push(blur >= 24 ? 'shadow-lg' : blur >= 8 ? 'shadow-md' : 'shadow-sm');
|
|
574
|
+
break;
|
|
502
575
|
}
|
|
576
|
+
case 'flex-wrap':
|
|
577
|
+
if (val === 'wrap')
|
|
578
|
+
classes.push('wrap');
|
|
579
|
+
break;
|
|
580
|
+
case 'flex-grow':
|
|
581
|
+
if (val !== '0')
|
|
582
|
+
classes.push('grow');
|
|
583
|
+
break;
|
|
584
|
+
case 'flex-shrink':
|
|
585
|
+
if (val === '0')
|
|
586
|
+
classes.push('shrink-0');
|
|
587
|
+
break;
|
|
588
|
+
case 'min-width':
|
|
589
|
+
case 'min-height':
|
|
590
|
+
if (val === '0')
|
|
591
|
+
classes.push('min0');
|
|
592
|
+
break;
|
|
593
|
+
case 'position':
|
|
594
|
+
if (['relative', 'absolute', 'fixed', 'sticky'].includes(val))
|
|
595
|
+
classes.push(val);
|
|
596
|
+
break;
|
|
597
|
+
case 'text-overflow':
|
|
598
|
+
if (val === 'ellipsis')
|
|
599
|
+
classes.push('truncate');
|
|
600
|
+
break;
|
|
601
|
+
case 'font-weight':
|
|
602
|
+
if (['400', '500', '600', '700'].includes(val))
|
|
603
|
+
classes.push(`weight-${val}`);
|
|
604
|
+
else
|
|
605
|
+
unmapped.push(`${prop}: ${val}`);
|
|
606
|
+
break;
|
|
607
|
+
case 'justify-content':
|
|
608
|
+
case 'align-items':
|
|
609
|
+
// resolved below, once the container is known
|
|
610
|
+
unmapped.push(`${prop}: ${val}`);
|
|
611
|
+
break;
|
|
612
|
+
default:
|
|
613
|
+
unmapped.push(`${prop}: ${val}`);
|
|
503
614
|
}
|
|
504
|
-
else if (prop === 'border-radius') {
|
|
505
|
-
const num = parseInt(val, 10);
|
|
506
|
-
if (!isNaN(num)) {
|
|
507
|
-
radiusVal = String(num);
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
else if (prop === 'background-color' || prop === 'background') {
|
|
511
|
-
if (val.includes('raised'))
|
|
512
|
-
bgVal = 'raised';
|
|
513
|
-
else if (val.includes('surface') || val === '#ffffff' || val === '#fff')
|
|
514
|
-
bgVal = 'surface';
|
|
515
|
-
else
|
|
516
|
-
bgVal = 'bg';
|
|
517
|
-
}
|
|
518
|
-
else if (prop === 'box-shadow') {
|
|
519
|
-
elevationVal = 'md';
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
// If surface combination
|
|
523
|
-
if (bgVal || padVal || radiusVal) {
|
|
524
|
-
mixins.push(`+surface(${bgVal || 'surface'}, ${padVal || 's'}, ${radiusVal || '12'}${elevationVal ? `, ${elevationVal}` : ''})`);
|
|
525
|
-
}
|
|
526
|
-
if (hasFlexCol) {
|
|
527
|
-
mixins.push(`+stack(${gapVal || 's'})`);
|
|
528
615
|
}
|
|
529
|
-
|
|
530
|
-
|
|
616
|
+
// Container first, so alignment can be named on the physical axis.
|
|
617
|
+
const container = display === 'grid' ? 'grid' : display === 'flex' ? (direction === 'row' ? 'row' : 'box') : null;
|
|
618
|
+
if (container)
|
|
619
|
+
classes.unshift(container);
|
|
620
|
+
const ALIGN = {
|
|
621
|
+
'flex-start': 'left',
|
|
622
|
+
start: 'left',
|
|
623
|
+
center: 'center',
|
|
624
|
+
'flex-end': 'right',
|
|
625
|
+
end: 'right',
|
|
626
|
+
'space-between': 'between',
|
|
627
|
+
'space-evenly': 'evenly',
|
|
628
|
+
'space-around': 'around'
|
|
629
|
+
};
|
|
630
|
+
const resolved = [];
|
|
631
|
+
for (const u of unmapped.slice()) {
|
|
632
|
+
const m = /^(justify-content|align-items):\s*(.+)$/.exec(u);
|
|
633
|
+
if (!m || !container)
|
|
634
|
+
continue;
|
|
635
|
+
const word = ALIGN[m[2]];
|
|
636
|
+
if (!word)
|
|
637
|
+
continue;
|
|
638
|
+
// In a row, justify-content is the X axis; in a column it is Y.
|
|
639
|
+
const isX = container === 'row' ? m[1] === 'justify-content' : m[1] === 'align-items';
|
|
640
|
+
resolved.push(`${isX ? 'x' : 'y'}${word === 'left' && !isX ? 'top' : word === 'right' && !isX ? 'bot' : word}`);
|
|
641
|
+
unmapped.splice(unmapped.indexOf(u), 1);
|
|
531
642
|
}
|
|
532
|
-
|
|
533
|
-
|
|
643
|
+
classes.push(...resolved);
|
|
644
|
+
const attr = classes.length ? `<div class="${classes.join(' ')}">` : '';
|
|
645
|
+
let text = classes.length
|
|
646
|
+
? `Compose in markup:\n\n${attr}`
|
|
647
|
+
: 'No registry class matched. Check list_fractals for the full API.';
|
|
648
|
+
if (unmapped.length) {
|
|
649
|
+
text +=
|
|
650
|
+
`\n\nNot covered by the registry:\n` +
|
|
651
|
+
unmapped.map((u) => ` ${u}`).join('\n') +
|
|
652
|
+
`\n\nThese are candidates for a component <style> block or _08_own.sass — ` +
|
|
653
|
+
`but check docs/13-cookbook.md first; most patterns compose.`;
|
|
534
654
|
}
|
|
535
|
-
|
|
536
|
-
return {
|
|
537
|
-
content: [
|
|
538
|
-
{
|
|
539
|
-
type: 'text',
|
|
540
|
-
text: `Suggested fractalstyler2 recipe:\n\n${output}`
|
|
541
|
-
}
|
|
542
|
-
]
|
|
543
|
-
};
|
|
655
|
+
return { content: [{ type: 'text', text }] };
|
|
544
656
|
}
|
|
545
657
|
case 'generate_component': {
|
|
546
658
|
const compName = args.name || 'CustomCard';
|
|
@@ -555,89 +667,69 @@ ${indented}
|
|
|
555
667
|
let { title = 'Card Title', description = '${desc || 'Card summary text'}', children } = $props();
|
|
556
668
|
</script>
|
|
557
669
|
|
|
558
|
-
<article class="card"${elevation !== 'none' ? ' data-elevated' : ''}>
|
|
559
|
-
<div class="row ycenter xbetween">
|
|
560
|
-
<h3 class="text-lg">{title}</h3>
|
|
670
|
+
<article class="card box gap-sm pad-md surface border"${elevation !== 'none' ? ' data-elevated' : ''}>
|
|
671
|
+
<div class="row ycenter xbetween gap-sm">
|
|
672
|
+
<h3 class="text-lg weight-600 m-0">{title}</h3>
|
|
561
673
|
<span class="badge">Active</span>
|
|
562
674
|
</div>
|
|
563
|
-
<p class="
|
|
675
|
+
<p class="text-sm text-secondary">{description}</p>
|
|
564
676
|
{#if children}
|
|
565
677
|
{@render children()}
|
|
566
678
|
{/if}
|
|
567
679
|
</article>
|
|
568
|
-
|
|
569
|
-
<style lang="sass">
|
|
570
|
-
@use '$lib/styles/fractals' as *
|
|
571
|
-
</style>`;
|
|
680
|
+
`;
|
|
572
681
|
break;
|
|
573
682
|
case 'panel':
|
|
574
683
|
template = `<script lang="ts">
|
|
575
684
|
let { heading = 'Panel Heading', children } = $props();
|
|
576
685
|
</script>
|
|
577
686
|
|
|
578
|
-
<section class="panel">
|
|
579
|
-
<header class="row ycenter xbetween">
|
|
580
|
-
<h2 class="text-xl">{heading}</h2>
|
|
687
|
+
<section class="panel box gap-md pad-md">
|
|
688
|
+
<header class="row ycenter xbetween gap-sm">
|
|
689
|
+
<h2 class="text-xl weight-600 m-0">{heading}</h2>
|
|
581
690
|
</header>
|
|
582
|
-
<div class="box gap-
|
|
691
|
+
<div class="box gap-sm">
|
|
583
692
|
{#if children}
|
|
584
693
|
{@render children()}
|
|
585
694
|
{/if}
|
|
586
695
|
</div>
|
|
587
696
|
</section>
|
|
588
|
-
|
|
589
|
-
<style lang="sass">
|
|
590
|
-
@use '$lib/styles/fractals' as *
|
|
591
|
-
</style>`;
|
|
697
|
+
`;
|
|
592
698
|
break;
|
|
593
699
|
case 'button':
|
|
594
700
|
template = `<script lang="ts">
|
|
595
701
|
let { variant = 'primary', disabled = false, onclick, children } = $props();
|
|
596
702
|
</script>
|
|
597
703
|
|
|
598
|
-
<button class="button
|
|
704
|
+
<button class="button {variant}" {disabled} {onclick}>
|
|
599
705
|
{#if children}
|
|
600
706
|
{@render children()}
|
|
601
707
|
{:else}
|
|
602
708
|
Action
|
|
603
709
|
{/if}
|
|
604
710
|
</button>
|
|
605
|
-
|
|
606
|
-
<style lang="sass">
|
|
607
|
-
@use '$lib/styles/fractals' as *
|
|
608
|
-
</style>`;
|
|
711
|
+
`;
|
|
609
712
|
break;
|
|
610
713
|
case 'badge':
|
|
611
714
|
template = `<script lang="ts">
|
|
612
715
|
let { label = 'Badge', variant = 'default' } = $props();
|
|
613
716
|
</script>
|
|
614
717
|
|
|
615
|
-
<span class="badge" data-
|
|
718
|
+
<span class="badge" data-status={variant}>
|
|
616
719
|
{label}
|
|
617
720
|
</span>
|
|
618
|
-
|
|
619
|
-
<style lang="sass">
|
|
620
|
-
@use '$lib/styles/fractals' as *
|
|
621
|
-
</style>`;
|
|
721
|
+
`;
|
|
622
722
|
break;
|
|
623
723
|
default:
|
|
624
724
|
template = `<script lang="ts">
|
|
625
725
|
let { children } = $props();
|
|
626
726
|
</script>
|
|
627
727
|
|
|
628
|
-
<div class="
|
|
728
|
+
<div class="box gap-sm pad-md surface border radius-md">
|
|
629
729
|
{#if children}
|
|
630
730
|
{@render children()}
|
|
631
731
|
{/if}
|
|
632
|
-
</div
|
|
633
|
-
|
|
634
|
-
<style lang="sass">
|
|
635
|
-
@use '$lib/styles/fractals' as *
|
|
636
|
-
|
|
637
|
-
.custom-container
|
|
638
|
-
+surface(surface, m, 16)
|
|
639
|
-
+stack(s)
|
|
640
|
-
</style>`;
|
|
732
|
+
</div>`;
|
|
641
733
|
break;
|
|
642
734
|
}
|
|
643
735
|
return {
|
|
@@ -652,28 +744,34 @@ ${indented}
|
|
|
652
744
|
case 'validate_recipe': {
|
|
653
745
|
const code = args.code || '';
|
|
654
746
|
const diagnostics = [];
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
}
|
|
747
|
+
// The failure mode worth catching is an invented class, not a legacy one.
|
|
748
|
+
const INVENTED = /class="([^"]*)"/g;
|
|
749
|
+
let m;
|
|
750
|
+
while ((m = INVENTED.exec(code)) !== null) {
|
|
751
|
+
for (const cls of m[1].split(/\s+/).filter(Boolean)) {
|
|
752
|
+
if (cls.includes('{') || cls.includes('$'))
|
|
753
|
+
continue;
|
|
754
|
+
const known = REGISTRY.some((e) => e.class.split('.').filter(Boolean).includes(cls)) ||
|
|
755
|
+
/^(gap|rgap|cgap|pad|marg|radius|w|h|square|text|weight|clamp|grid|frame)-/.test(cls) ||
|
|
756
|
+
['open', 'active', 'checked', 'primary', 'ghost', 'is-icon'].includes(cls);
|
|
757
|
+
if (!known) {
|
|
758
|
+
diagnostics.push({
|
|
759
|
+
severity: 'error',
|
|
760
|
+
message: `Unknown class ".${cls}". It is not in the registry. Compose from existing classes — call list_fractals — or check docs/13-cookbook.md for the pattern you are building.`
|
|
761
|
+
});
|
|
762
|
+
}
|
|
671
763
|
}
|
|
672
764
|
}
|
|
765
|
+
if (/<style/.test(code)) {
|
|
766
|
+
diagnostics.push({
|
|
767
|
+
severity: 'error',
|
|
768
|
+
message: 'Component <style> block found. Compose in markup instead; the registry covers nearly every pattern (docs/13-cookbook.md).'
|
|
769
|
+
});
|
|
770
|
+
}
|
|
673
771
|
if (/padding:\s*\d+px/i.test(code) || /gap:\s*\d+px/i.test(code)) {
|
|
674
772
|
diagnostics.push({
|
|
675
773
|
severity: 'warning',
|
|
676
|
-
message: 'Hardcoded px values
|
|
774
|
+
message: 'Hardcoded px values in CSS. Compose .gap-sm / .pad-md in the markup instead — or a literal .gap-18 if the exact value is load-bearing.'
|
|
677
775
|
});
|
|
678
776
|
}
|
|
679
777
|
if (code.includes('.is-active') || code.includes('--active')) {
|
|
@@ -693,13 +791,12 @@ ${indented}
|
|
|
693
791
|
};
|
|
694
792
|
}
|
|
695
793
|
case 'list_fractals': {
|
|
696
|
-
const
|
|
697
|
-
if (
|
|
698
|
-
|
|
794
|
+
const layer = args.layer || 'all';
|
|
795
|
+
if (REGISTRY.length === 0) {
|
|
796
|
+
throw new Error('Class registry not found. registry.json ships with the package; run `npm run registry` if working from source.');
|
|
699
797
|
}
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
};
|
|
798
|
+
const payload = layer === 'all' ? FRACTAL_CATALOG : { [layer]: FRACTAL_CATALOG[layer] ?? [] };
|
|
799
|
+
return { content: [{ type: 'text', text: JSON.stringify(payload, null, 2) }] };
|
|
703
800
|
}
|
|
704
801
|
default:
|
|
705
802
|
throw new Error(`Unknown tool: ${name}`);
|
|
@@ -719,8 +816,8 @@ server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
|
719
816
|
},
|
|
720
817
|
{
|
|
721
818
|
uri: 'fractalstyler2://fractals',
|
|
722
|
-
name: '
|
|
723
|
-
description: '
|
|
819
|
+
name: 'Class Registry',
|
|
820
|
+
description: 'The public API: every composable class, grouped by layer L0-L5, with the CSS it applies and its source file.',
|
|
724
821
|
mimeType: 'application/json'
|
|
725
822
|
},
|
|
726
823
|
{
|
|
@@ -780,7 +877,7 @@ server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
|
780
877
|
prompts: [
|
|
781
878
|
{
|
|
782
879
|
name: 'design_system_review',
|
|
783
|
-
description: 'Audit and refactor a component or screen to
|
|
880
|
+
description: 'Audit and refactor a component or screen to compose from the fractalstyler2 class registry.'
|
|
784
881
|
},
|
|
785
882
|
{
|
|
786
883
|
name: 'generate_ui',
|
|
@@ -802,7 +899,7 @@ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
|
802
899
|
role: 'user',
|
|
803
900
|
content: {
|
|
804
901
|
type: 'text',
|
|
805
|
-
text: `
|
|
902
|
+
text: `Review the following code and refactor it to compose from the fractalstyler2 class registry in the markup. Remove stylesheet declarations the registry already covers; do not introduce new class names. Call list_fractals if unsure of a class:\n\n${GUIDELINES}`
|
|
806
903
|
}
|
|
807
904
|
}
|
|
808
905
|
]
|
|
@@ -816,7 +913,7 @@ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
|
816
913
|
role: 'user',
|
|
817
914
|
content: {
|
|
818
915
|
type: 'text',
|
|
819
|
-
text: `Generate a responsive UI component using Svelte 5 runes ($props, $state) and
|
|
916
|
+
text: `Generate a responsive UI component using Svelte 5 runes ($props, $state), composed entirely from fractalstyler2 registry classes in the markup. Do not write a style block and do not invent class names — call list_fractals if unsure. Adhere to the golden rules:\n\n${GUIDELINES}`
|
|
820
917
|
}
|
|
821
918
|
}
|
|
822
919
|
]
|