@dryui/theme-wizard 4.0.0 → 5.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.
- package/package.json +3 -3
- package/dist/actions.d.ts +0 -4
- package/dist/actions.js +0 -9
- package/dist/components/AlphaSlider.svelte +0 -13
- package/dist/components/AlphaSlider.svelte.d.ts +0 -9
- package/dist/components/ContrastBadge.svelte +0 -22
- package/dist/components/ContrastBadge.svelte.d.ts +0 -8
- package/dist/components/HsbPicker.svelte +0 -304
- package/dist/components/HsbPicker.svelte.d.ts +0 -9
- package/dist/components/StepIndicator.svelte +0 -87
- package/dist/components/StepIndicator.svelte.d.ts +0 -7
- package/dist/components/TokenPreview.svelte +0 -55
- package/dist/components/TokenPreview.svelte.d.ts +0 -8
- package/dist/components/WizardShell.svelte +0 -140
- package/dist/components/WizardShell.svelte.d.ts +0 -15
- package/dist/engine/derivation.d.ts +0 -282
- package/dist/engine/derivation.js +0 -1445
- package/dist/engine/derivation.test.d.ts +0 -1
- package/dist/engine/derivation.test.js +0 -956
- package/dist/engine/export-css.d.ts +0 -32
- package/dist/engine/export-css.js +0 -90
- package/dist/engine/export-css.test.d.ts +0 -1
- package/dist/engine/export-css.test.js +0 -78
- package/dist/engine/index.d.ts +0 -10
- package/dist/engine/index.js +0 -6
- package/dist/engine/palette.d.ts +0 -16
- package/dist/engine/palette.js +0 -44
- package/dist/engine/presets.d.ts +0 -13
- package/dist/engine/presets.js +0 -124
- package/dist/engine/url-codec.d.ts +0 -53
- package/dist/engine/url-codec.js +0 -243
- package/dist/engine/url-codec.test.d.ts +0 -1
- package/dist/engine/url-codec.test.js +0 -137
- package/dist/index.d.ts +0 -15
- package/dist/index.js +0 -19
- package/dist/state.svelte.d.ts +0 -104
- package/dist/state.svelte.js +0 -574
- package/dist/steps/BrandColor.svelte +0 -216
- package/dist/steps/BrandColor.svelte.d.ts +0 -6
- package/dist/steps/Personality.svelte +0 -319
- package/dist/steps/Personality.svelte.d.ts +0 -3
- package/dist/steps/PreviewExport.svelte +0 -115
- package/dist/steps/PreviewExport.svelte.d.ts +0 -9
- package/dist/steps/Shape.svelte +0 -121
- package/dist/steps/Shape.svelte.d.ts +0 -18
- package/dist/steps/Typography.svelte +0 -115
- package/dist/steps/Typography.svelte.d.ts +0 -18
package/dist/engine/url-codec.js
DELETED
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Encode wizard input state into a compact URL-safe string.
|
|
3
|
-
*
|
|
4
|
-
* Format: `h{hue}s{sat}b{bri}` with optional `-n` neutral-mode flag and
|
|
5
|
-
* `-e{error}w{warning}s{success}i{info}` status suffix.
|
|
6
|
-
*
|
|
7
|
-
* Examples:
|
|
8
|
-
* Default brand only: `h230s65b85`
|
|
9
|
-
* Brand + status hue overrides: `h230s65b85-e350w45`
|
|
10
|
-
*/
|
|
11
|
-
export function encodeTheme(input) {
|
|
12
|
-
const { brand, options } = input;
|
|
13
|
-
const segments = [
|
|
14
|
-
`h${Math.round(brand.h)}s${Math.round(brand.s)}b${Math.round(brand.b)}`
|
|
15
|
-
];
|
|
16
|
-
if (options?.neutralMode === 'neutral') {
|
|
17
|
-
segments.push('n');
|
|
18
|
-
}
|
|
19
|
-
const sh = options?.statusHues;
|
|
20
|
-
if (sh && Object.keys(sh).length > 0) {
|
|
21
|
-
const parts = [];
|
|
22
|
-
if (sh.error !== undefined)
|
|
23
|
-
parts.push(`e${Math.round(sh.error)}`);
|
|
24
|
-
if (sh.warning !== undefined)
|
|
25
|
-
parts.push(`w${Math.round(sh.warning)}`);
|
|
26
|
-
if (sh.success !== undefined)
|
|
27
|
-
parts.push(`s${Math.round(sh.success)}`);
|
|
28
|
-
if (sh.info !== undefined)
|
|
29
|
-
parts.push(`i${Math.round(sh.info)}`);
|
|
30
|
-
if (parts.length > 0) {
|
|
31
|
-
segments.push(parts.join(''));
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
return segments.join('-');
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Decode a compact theme string back into brand input and options.
|
|
38
|
-
*
|
|
39
|
-
* Throws if the string is not a valid encoded theme.
|
|
40
|
-
*/
|
|
41
|
-
export function decodeTheme(encoded) {
|
|
42
|
-
if (!encoded) {
|
|
43
|
-
throw new Error('Empty encoded theme string');
|
|
44
|
-
}
|
|
45
|
-
// Parse brand: h{hue}s{sat}b{bri}
|
|
46
|
-
const [brandPart, ...segments] = encoded.split('-');
|
|
47
|
-
if (brandPart === undefined) {
|
|
48
|
-
throw new Error('Missing brand segment');
|
|
49
|
-
}
|
|
50
|
-
const brandMatch = brandPart.match(/^h(\d+(?:\.\d+)?)s(\d+(?:\.\d+)?)b(\d+(?:\.\d+)?)$/);
|
|
51
|
-
if (!brandMatch ||
|
|
52
|
-
brandMatch[1] === undefined ||
|
|
53
|
-
brandMatch[2] === undefined ||
|
|
54
|
-
brandMatch[3] === undefined) {
|
|
55
|
-
throw new Error(`Invalid brand segment: "${brandPart}"`);
|
|
56
|
-
}
|
|
57
|
-
const brand = {
|
|
58
|
-
h: parseFloat(brandMatch[1]),
|
|
59
|
-
s: parseFloat(brandMatch[2]),
|
|
60
|
-
b: parseFloat(brandMatch[3])
|
|
61
|
-
};
|
|
62
|
-
if (segments.length === 0) {
|
|
63
|
-
return { brand };
|
|
64
|
-
}
|
|
65
|
-
const options = {};
|
|
66
|
-
const statusHues = {};
|
|
67
|
-
let hasStatusHues = false;
|
|
68
|
-
for (const segment of segments) {
|
|
69
|
-
if (segment === 'n') {
|
|
70
|
-
options.neutralMode = 'neutral';
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
// Parse status hues: sequences of letter+digits, e.g. e350w45s145i210
|
|
74
|
-
const statusRegex = /([ewsi])(\d+(?:\.\d+)?)/g;
|
|
75
|
-
let match;
|
|
76
|
-
while ((match = statusRegex.exec(segment)) !== null) {
|
|
77
|
-
const key = match[1];
|
|
78
|
-
const rawHue = match[2];
|
|
79
|
-
if (key === undefined || rawHue === undefined)
|
|
80
|
-
continue;
|
|
81
|
-
const hue = parseFloat(rawHue);
|
|
82
|
-
if (key === 'e')
|
|
83
|
-
statusHues.error = hue;
|
|
84
|
-
else if (key === 'w')
|
|
85
|
-
statusHues.warning = hue;
|
|
86
|
-
else if (key === 's')
|
|
87
|
-
statusHues.success = hue;
|
|
88
|
-
else if (key === 'i')
|
|
89
|
-
statusHues.info = hue;
|
|
90
|
-
hasStatusHues = true;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
if (hasStatusHues) {
|
|
94
|
-
options.statusHues = statusHues;
|
|
95
|
-
}
|
|
96
|
-
if (Object.keys(options).length > 0) {
|
|
97
|
-
return { brand, options };
|
|
98
|
-
}
|
|
99
|
-
return { brand };
|
|
100
|
-
}
|
|
101
|
-
// ─── Full wizard recipe codec ────────────────────────────────────────────────
|
|
102
|
-
// Encodes ALL wizard inputs into a single compact hash.
|
|
103
|
-
// Format: h{h}s{s}b{b}[-n][-e{h}w{h}s{h}i{h}][-l{0-3}][-f{0-5}c{0-2}][-p{0-3}d{0-2}k{scale*100}][-x{0-3}y{int*100}z{0|1}]
|
|
104
|
-
// Example: h230s65b85-e0w40s145i210-l2-f0c1-p1d1k100-x2y100z1
|
|
105
|
-
const PERSONALITY_NAMES = ['minimal', 'clean', 'structured', 'rich'];
|
|
106
|
-
const PERSONALITY_INDEX = Object.fromEntries(PERSONALITY_NAMES.map((n, i) => [n, i]));
|
|
107
|
-
const FONT_NAMES = ['System', 'Humanist', 'Geometric', 'Classical', 'Serif', 'Mono'];
|
|
108
|
-
const FONT_INDEX = Object.fromEntries(FONT_NAMES.map((n, i) => [n, i]));
|
|
109
|
-
const SCALE_NAMES = ['compact', 'default', 'spacious'];
|
|
110
|
-
const SCALE_INDEX = Object.fromEntries(SCALE_NAMES.map((n, i) => [n, i]));
|
|
111
|
-
const RADIUS_NAMES = ['sharp', 'soft', 'rounded', 'pill'];
|
|
112
|
-
const RADIUS_INDEX = Object.fromEntries(RADIUS_NAMES.map((n, i) => [n, i]));
|
|
113
|
-
const SHADOW_NAMES = ['flat', 'subtle', 'elevated', 'deep'];
|
|
114
|
-
const SHADOW_INDEX = Object.fromEntries(SHADOW_NAMES.map((n, i) => [n, i]));
|
|
115
|
-
export function encodeRecipe(recipe) {
|
|
116
|
-
const { brand } = recipe;
|
|
117
|
-
const parts = [`h${Math.round(brand.h)}s${Math.round(brand.s)}b${Math.round(brand.b)}`];
|
|
118
|
-
if (recipe.neutralMode === 'neutral')
|
|
119
|
-
parts.push('n');
|
|
120
|
-
const sh = recipe.statusHues;
|
|
121
|
-
if (sh) {
|
|
122
|
-
const sp = [];
|
|
123
|
-
if (sh.error !== undefined)
|
|
124
|
-
sp.push(`e${Math.round(sh.error)}`);
|
|
125
|
-
if (sh.warning !== undefined)
|
|
126
|
-
sp.push(`w${Math.round(sh.warning)}`);
|
|
127
|
-
if (sh.success !== undefined)
|
|
128
|
-
sp.push(`s${Math.round(sh.success)}`);
|
|
129
|
-
if (sh.info !== undefined)
|
|
130
|
-
sp.push(`i${Math.round(sh.info)}`);
|
|
131
|
-
if (sp.length > 0)
|
|
132
|
-
parts.push(sp.join(''));
|
|
133
|
-
}
|
|
134
|
-
if (recipe.personality) {
|
|
135
|
-
const li = PERSONALITY_INDEX[recipe.personality] ?? 2;
|
|
136
|
-
parts.push(`l${li}`);
|
|
137
|
-
}
|
|
138
|
-
if (recipe.typography) {
|
|
139
|
-
const fi = FONT_INDEX[recipe.typography.fontPreset] ?? 0;
|
|
140
|
-
const si = SCALE_INDEX[recipe.typography.scale] ?? 1;
|
|
141
|
-
parts.push(`f${fi}c${si}`);
|
|
142
|
-
}
|
|
143
|
-
if (recipe.shape) {
|
|
144
|
-
const ri = RADIUS_INDEX[recipe.shape.radiusPreset] ?? 1;
|
|
145
|
-
const di = SCALE_INDEX[recipe.shape.density] ?? 1;
|
|
146
|
-
const ks = Math.round(recipe.shape.radiusScale * 100);
|
|
147
|
-
parts.push(`p${ri}d${di}k${ks}`);
|
|
148
|
-
}
|
|
149
|
-
if (recipe.shadows) {
|
|
150
|
-
const xi = SHADOW_INDEX[recipe.shadows.preset] ?? 2;
|
|
151
|
-
const yi = Math.round(recipe.shadows.intensity * 100);
|
|
152
|
-
const zi = recipe.shadows.tintBrand ? 1 : 0;
|
|
153
|
-
parts.push(`x${xi}y${yi}z${zi}`);
|
|
154
|
-
}
|
|
155
|
-
return parts.join('-');
|
|
156
|
-
}
|
|
157
|
-
export function decodeRecipe(encoded) {
|
|
158
|
-
if (!encoded)
|
|
159
|
-
throw new Error('Empty recipe string');
|
|
160
|
-
const [brandPart, ...segments] = encoded.split('-');
|
|
161
|
-
if (!brandPart)
|
|
162
|
-
throw new Error('Missing brand segment');
|
|
163
|
-
const brandMatch = brandPart.match(/^h(\d+(?:\.\d+)?)s(\d+(?:\.\d+)?)b(\d+(?:\.\d+)?)$/);
|
|
164
|
-
if (!brandMatch || !brandMatch[1] || !brandMatch[2] || !brandMatch[3]) {
|
|
165
|
-
throw new Error(`Invalid brand segment: "${brandPart}"`);
|
|
166
|
-
}
|
|
167
|
-
const recipe = {
|
|
168
|
-
brand: {
|
|
169
|
-
h: parseFloat(brandMatch[1]),
|
|
170
|
-
s: parseFloat(brandMatch[2]),
|
|
171
|
-
b: parseFloat(brandMatch[3])
|
|
172
|
-
}
|
|
173
|
-
};
|
|
174
|
-
const statusHues = {};
|
|
175
|
-
let hasStatus = false;
|
|
176
|
-
for (const seg of segments) {
|
|
177
|
-
if (seg === 'n') {
|
|
178
|
-
recipe.neutralMode = 'neutral';
|
|
179
|
-
continue;
|
|
180
|
-
}
|
|
181
|
-
// Personality: l{0-3}
|
|
182
|
-
const personalityMatch = seg.match(/^l([0-3])$/);
|
|
183
|
-
if (personalityMatch && personalityMatch[1] !== undefined) {
|
|
184
|
-
recipe.personality = (PERSONALITY_NAMES[parseInt(personalityMatch[1])] ??
|
|
185
|
-
'structured');
|
|
186
|
-
continue;
|
|
187
|
-
}
|
|
188
|
-
// Typography: f{idx}c{idx}
|
|
189
|
-
const typoMatch = seg.match(/^f(\d)c(\d)$/);
|
|
190
|
-
if (typoMatch && typoMatch[1] !== undefined && typoMatch[2] !== undefined) {
|
|
191
|
-
recipe.typography = {
|
|
192
|
-
fontPreset: FONT_NAMES[parseInt(typoMatch[1])] ?? 'System',
|
|
193
|
-
scale: (SCALE_NAMES[parseInt(typoMatch[2])] ?? 'default')
|
|
194
|
-
};
|
|
195
|
-
continue;
|
|
196
|
-
}
|
|
197
|
-
// Shape: p{idx}d{idx}k{scale*100}
|
|
198
|
-
const shapeMatch = seg.match(/^p(\d)d(\d)k(\d+)$/);
|
|
199
|
-
if (shapeMatch &&
|
|
200
|
-
shapeMatch[1] !== undefined &&
|
|
201
|
-
shapeMatch[2] !== undefined &&
|
|
202
|
-
shapeMatch[3] !== undefined) {
|
|
203
|
-
recipe.shape = {
|
|
204
|
-
radiusPreset: (RADIUS_NAMES[parseInt(shapeMatch[1])] ?? 'soft'),
|
|
205
|
-
density: (SCALE_NAMES[parseInt(shapeMatch[2])] ?? 'default'),
|
|
206
|
-
radiusScale: parseInt(shapeMatch[3]) / 100
|
|
207
|
-
};
|
|
208
|
-
continue;
|
|
209
|
-
}
|
|
210
|
-
// Shadows: x{idx}y{int*100}z{0|1}
|
|
211
|
-
const shadowMatch = seg.match(/^x(\d)y(\d+)z([01])$/);
|
|
212
|
-
if (shadowMatch &&
|
|
213
|
-
shadowMatch[1] !== undefined &&
|
|
214
|
-
shadowMatch[2] !== undefined &&
|
|
215
|
-
shadowMatch[3] !== undefined) {
|
|
216
|
-
recipe.shadows = {
|
|
217
|
-
preset: (SHADOW_NAMES[parseInt(shadowMatch[1])] ?? 'elevated'),
|
|
218
|
-
intensity: parseInt(shadowMatch[2]) / 100,
|
|
219
|
-
tintBrand: shadowMatch[3] === '1'
|
|
220
|
-
};
|
|
221
|
-
continue;
|
|
222
|
-
}
|
|
223
|
-
// Status hues: e{h}w{h}s{h}i{h}
|
|
224
|
-
const statusRegex = /([ewsi])(\d+(?:\.\d+)?)/g;
|
|
225
|
-
let match;
|
|
226
|
-
while ((match = statusRegex.exec(seg)) !== null) {
|
|
227
|
-
const key = match[1];
|
|
228
|
-
const hue = parseFloat(match[2]);
|
|
229
|
-
if (key === 'e')
|
|
230
|
-
statusHues.error = hue;
|
|
231
|
-
else if (key === 'w')
|
|
232
|
-
statusHues.warning = hue;
|
|
233
|
-
else if (key === 's')
|
|
234
|
-
statusHues.success = hue;
|
|
235
|
-
else if (key === 'i')
|
|
236
|
-
statusHues.info = hue;
|
|
237
|
-
hasStatus = true;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
if (hasStatus)
|
|
241
|
-
recipe.statusHues = statusHues;
|
|
242
|
-
return recipe;
|
|
243
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
// apps/docs/src/lib/theme-wizard/url-codec.test.ts
|
|
2
|
-
import { describe, test, expect } from 'bun:test';
|
|
3
|
-
import { encodeTheme, decodeTheme } from './url-codec.js';
|
|
4
|
-
// ─── Round-trip helpers ────────────────────────────────────────────────────────
|
|
5
|
-
function roundTrip(brand, options) {
|
|
6
|
-
const input = options !== undefined ? { brand, options } : { brand };
|
|
7
|
-
const encoded = encodeTheme(input);
|
|
8
|
-
return { encoded, decoded: decodeTheme(encoded) };
|
|
9
|
-
}
|
|
10
|
-
// ─── Encoding ─────────────────────────────────────────────────────────────────
|
|
11
|
-
describe('encodeTheme', () => {
|
|
12
|
-
test('encodes default brand to h230s65b85', () => {
|
|
13
|
-
const encoded = encodeTheme({ brand: { h: 230, s: 65, b: 85 } });
|
|
14
|
-
expect(encoded).toBe('h230s65b85');
|
|
15
|
-
});
|
|
16
|
-
test('encodes ocean brand to h200s80b70', () => {
|
|
17
|
-
const encoded = encodeTheme({ brand: { h: 200, s: 80, b: 70 } });
|
|
18
|
-
expect(encoded).toBe('h200s80b70');
|
|
19
|
-
});
|
|
20
|
-
test('encodes brand without options — no dash suffix', () => {
|
|
21
|
-
const encoded = encodeTheme({ brand: { h: 145, s: 60, b: 55 } });
|
|
22
|
-
expect(encoded).not.toContain('-');
|
|
23
|
-
});
|
|
24
|
-
test('encodes brand + error hue override with dash separator', () => {
|
|
25
|
-
const encoded = encodeTheme({
|
|
26
|
-
brand: { h: 230, s: 65, b: 85 },
|
|
27
|
-
options: { statusHues: { error: 350 } }
|
|
28
|
-
});
|
|
29
|
-
expect(encoded).toBe('h230s65b85-e350');
|
|
30
|
-
});
|
|
31
|
-
test('encodes brand + multiple status hue overrides', () => {
|
|
32
|
-
const encoded = encodeTheme({
|
|
33
|
-
brand: { h: 230, s: 65, b: 85 },
|
|
34
|
-
options: { statusHues: { error: 350, warning: 45 } }
|
|
35
|
-
});
|
|
36
|
-
expect(encoded).toBe('h230s65b85-e350w45');
|
|
37
|
-
});
|
|
38
|
-
test('encodes all four status hues', () => {
|
|
39
|
-
const encoded = encodeTheme({
|
|
40
|
-
brand: { h: 230, s: 65, b: 85 },
|
|
41
|
-
options: { statusHues: { error: 0, warning: 40, success: 145, info: 210 } }
|
|
42
|
-
});
|
|
43
|
-
expect(encoded).toBe('h230s65b85-e0w40s145i210');
|
|
44
|
-
});
|
|
45
|
-
test('empty statusHues object produces no suffix', () => {
|
|
46
|
-
const encoded = encodeTheme({ brand: { h: 230, s: 65, b: 85 }, options: { statusHues: {} } });
|
|
47
|
-
expect(encoded).toBe('h230s65b85');
|
|
48
|
-
});
|
|
49
|
-
test('rounds fractional hue values', () => {
|
|
50
|
-
const encoded = encodeTheme({ brand: { h: 230.7, s: 65.4, b: 85.2 } });
|
|
51
|
-
expect(encoded).toBe('h231s65b85');
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
// ─── Decoding ─────────────────────────────────────────────────────────────────
|
|
55
|
-
describe('decodeTheme', () => {
|
|
56
|
-
test('decodes h230s65b85 to default brand', () => {
|
|
57
|
-
const { brand } = decodeTheme('h230s65b85');
|
|
58
|
-
expect(brand).toEqual({ h: 230, s: 65, b: 85 });
|
|
59
|
-
});
|
|
60
|
-
test('decodes brand-only string — options is undefined', () => {
|
|
61
|
-
const result = decodeTheme('h200s80b70');
|
|
62
|
-
expect(result.options).toBeUndefined();
|
|
63
|
-
});
|
|
64
|
-
test('decodes string with single status hue override', () => {
|
|
65
|
-
const result = decodeTheme('h230s65b85-e350');
|
|
66
|
-
expect(result.brand).toEqual({ h: 230, s: 65, b: 85 });
|
|
67
|
-
expect(result.options?.statusHues?.error).toBe(350);
|
|
68
|
-
});
|
|
69
|
-
test('decodes string with multiple status hue overrides', () => {
|
|
70
|
-
const result = decodeTheme('h230s65b85-e350w45');
|
|
71
|
-
expect(result.options?.statusHues?.error).toBe(350);
|
|
72
|
-
expect(result.options?.statusHues?.warning).toBe(45);
|
|
73
|
-
});
|
|
74
|
-
test('decodes all four status hues', () => {
|
|
75
|
-
const result = decodeTheme('h230s65b85-e0w40s145i210');
|
|
76
|
-
expect(result.options?.statusHues).toEqual({
|
|
77
|
-
error: 0,
|
|
78
|
-
warning: 40,
|
|
79
|
-
success: 145,
|
|
80
|
-
info: 210
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
test('throws on empty string', () => {
|
|
84
|
-
expect(() => decodeTheme('')).toThrow();
|
|
85
|
-
});
|
|
86
|
-
test('throws on invalid brand segment', () => {
|
|
87
|
-
expect(() => decodeTheme('invalid')).toThrow();
|
|
88
|
-
});
|
|
89
|
-
});
|
|
90
|
-
// ─── Round-trip ───────────────────────────────────────────────────────────────
|
|
91
|
-
describe('round-trip', () => {
|
|
92
|
-
test('default brand round-trips', () => {
|
|
93
|
-
const brand = { h: 230, s: 65, b: 85 };
|
|
94
|
-
const { decoded } = roundTrip(brand);
|
|
95
|
-
expect(decoded.brand).toEqual(brand);
|
|
96
|
-
});
|
|
97
|
-
test('ocean brand round-trips', () => {
|
|
98
|
-
const brand = { h: 200, s: 80, b: 70 };
|
|
99
|
-
const { decoded } = roundTrip(brand);
|
|
100
|
-
expect(decoded.brand).toEqual(brand);
|
|
101
|
-
});
|
|
102
|
-
test('forest brand round-trips', () => {
|
|
103
|
-
const brand = { h: 145, s: 60, b: 55 };
|
|
104
|
-
const { decoded } = roundTrip(brand);
|
|
105
|
-
expect(decoded.brand).toEqual(brand);
|
|
106
|
-
});
|
|
107
|
-
test('sunset brand round-trips', () => {
|
|
108
|
-
const brand = { h: 25, s: 80, b: 90 };
|
|
109
|
-
const { decoded } = roundTrip(brand);
|
|
110
|
-
expect(decoded.brand).toEqual(brand);
|
|
111
|
-
});
|
|
112
|
-
test('brand + error hue override round-trips', () => {
|
|
113
|
-
const brand = { h: 230, s: 65, b: 85 };
|
|
114
|
-
const options = { statusHues: { error: 350 } };
|
|
115
|
-
const { decoded } = roundTrip(brand, options);
|
|
116
|
-
expect(decoded.brand).toEqual(brand);
|
|
117
|
-
expect(decoded.options?.statusHues?.error).toBe(350);
|
|
118
|
-
});
|
|
119
|
-
test('brand + all status hues round-trips', () => {
|
|
120
|
-
const brand = { h: 230, s: 65, b: 85 };
|
|
121
|
-
const options = {
|
|
122
|
-
statusHues: { error: 5, warning: 38, success: 150, info: 205 }
|
|
123
|
-
};
|
|
124
|
-
const { decoded } = roundTrip(brand, options);
|
|
125
|
-
expect(decoded.brand).toEqual(brand);
|
|
126
|
-
expect(decoded.options?.statusHues).toEqual({ error: 5, warning: 38, success: 150, info: 205 });
|
|
127
|
-
});
|
|
128
|
-
test('compact string format — no redundant characters', () => {
|
|
129
|
-
const { encoded } = roundTrip({ h: 230, s: 65, b: 85 });
|
|
130
|
-
// Should be short: h230s65b85 = 10 chars
|
|
131
|
-
expect(encoded.length).toBeLessThanOrEqual(15);
|
|
132
|
-
});
|
|
133
|
-
test('encoded string only uses URL-safe characters', () => {
|
|
134
|
-
const { encoded } = roundTrip({ h: 230, s: 65, b: 85 }, { statusHues: { error: 0, warning: 40, success: 145, info: 210 } });
|
|
135
|
-
expect(encoded).toMatch(/^[a-zA-Z0-9-]+$/);
|
|
136
|
-
});
|
|
137
|
-
});
|
package/dist/index.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export * from './engine/index.js';
|
|
2
|
-
export { bg } from './actions.js';
|
|
3
|
-
export { wizardState, getDerivedTheme, setBrandHsb, setStep, goNextStep, goPrevStep, activateFastTrack, applyPreset, getStyleString, setFontPreset, setTypeScale, resetToDefaults, applyRecipe, setRadiusPreset, setRadiusScale, setDensity, getShadowTokens, getShapeTokens, setPersonality, getPersonalityTokens, getAllTokens, getOverrideTokens, RADIUS_PRESETS, FONT_STACKS } from './state.svelte.js';
|
|
4
|
-
export type { PreviewMode, NeutralMode, RadiusPreset, Density, ShadowPreset, Personality, TypeScale, FontPreset } from './state.svelte.js';
|
|
5
|
-
export { default as PersonalityStep } from './steps/Personality.svelte';
|
|
6
|
-
export { default as BrandColor } from './steps/BrandColor.svelte';
|
|
7
|
-
export { default as Typography } from './steps/Typography.svelte';
|
|
8
|
-
export { default as Shape } from './steps/Shape.svelte';
|
|
9
|
-
export { default as PreviewExport } from './steps/PreviewExport.svelte';
|
|
10
|
-
export { default as WizardShell } from './components/WizardShell.svelte';
|
|
11
|
-
export { default as HsbPicker } from './components/HsbPicker.svelte';
|
|
12
|
-
export { default as ContrastBadge } from './components/ContrastBadge.svelte';
|
|
13
|
-
export { default as AlphaSlider } from './components/AlphaSlider.svelte';
|
|
14
|
-
export { default as StepIndicator } from './components/StepIndicator.svelte';
|
|
15
|
-
export { default as TokenPreview } from './components/TokenPreview.svelte';
|
package/dist/index.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
// Re-export all engine functions and types
|
|
2
|
-
export * from './engine/index.js';
|
|
3
|
-
// Re-export actions
|
|
4
|
-
export { bg } from './actions.js';
|
|
5
|
-
// Re-export state (Svelte 5 runes)
|
|
6
|
-
export { wizardState, getDerivedTheme, setBrandHsb, setStep, goNextStep, goPrevStep, activateFastTrack, applyPreset, getStyleString, setFontPreset, setTypeScale, resetToDefaults, applyRecipe, setRadiusPreset, setRadiusScale, setDensity, getShadowTokens, getShapeTokens, setPersonality, getPersonalityTokens, getAllTokens, getOverrideTokens, RADIUS_PRESETS, FONT_STACKS } from './state.svelte.js';
|
|
7
|
-
// Step components
|
|
8
|
-
export { default as PersonalityStep } from './steps/Personality.svelte';
|
|
9
|
-
export { default as BrandColor } from './steps/BrandColor.svelte';
|
|
10
|
-
export { default as Typography } from './steps/Typography.svelte';
|
|
11
|
-
export { default as Shape } from './steps/Shape.svelte';
|
|
12
|
-
export { default as PreviewExport } from './steps/PreviewExport.svelte';
|
|
13
|
-
// UI components
|
|
14
|
-
export { default as WizardShell } from './components/WizardShell.svelte';
|
|
15
|
-
export { default as HsbPicker } from './components/HsbPicker.svelte';
|
|
16
|
-
export { default as ContrastBadge } from './components/ContrastBadge.svelte';
|
|
17
|
-
export { default as AlphaSlider } from './components/AlphaSlider.svelte';
|
|
18
|
-
export { default as StepIndicator } from './components/StepIndicator.svelte';
|
|
19
|
-
export { default as TokenPreview } from './components/TokenPreview.svelte';
|
package/dist/state.svelte.d.ts
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import { type BrandInput, type ThemeTokens } from './engine/derivation.js';
|
|
2
|
-
import type { WizardRecipe } from './engine/url-codec.js';
|
|
3
|
-
export type PreviewMode = 'light' | 'dark' | 'side-by-side';
|
|
4
|
-
export type NeutralMode = 'monochromatic' | 'neutral';
|
|
5
|
-
export type RadiusPreset = 'sharp' | 'soft' | 'rounded' | 'pill';
|
|
6
|
-
export type Density = 'compact' | 'default' | 'spacious';
|
|
7
|
-
export type ShadowPreset = 'flat' | 'subtle' | 'elevated' | 'deep';
|
|
8
|
-
export type Personality = 'minimal' | 'clean' | 'structured' | 'rich';
|
|
9
|
-
export type FontPreset = 'System' | 'Humanist' | 'Geometric' | 'Classical' | 'Serif' | 'Mono';
|
|
10
|
-
export type TypeScale = 'compact' | 'default' | 'spacious';
|
|
11
|
-
export declare const wizardState: {
|
|
12
|
-
currentStep: number;
|
|
13
|
-
personality: Personality;
|
|
14
|
-
brandHsb: BrandInput;
|
|
15
|
-
neutralMode: NeutralMode;
|
|
16
|
-
statusHues: {
|
|
17
|
-
error: number;
|
|
18
|
-
warning: number;
|
|
19
|
-
success: number;
|
|
20
|
-
info: number;
|
|
21
|
-
};
|
|
22
|
-
darkBgOverrides: {
|
|
23
|
-
base?: string;
|
|
24
|
-
raised?: string;
|
|
25
|
-
overlay?: string;
|
|
26
|
-
};
|
|
27
|
-
fastTrack: boolean;
|
|
28
|
-
typography: {
|
|
29
|
-
fontPreset: FontPreset;
|
|
30
|
-
scale: TypeScale;
|
|
31
|
-
};
|
|
32
|
-
shape: {
|
|
33
|
-
radiusPreset: RadiusPreset;
|
|
34
|
-
radiusScale: number;
|
|
35
|
-
density: Density;
|
|
36
|
-
};
|
|
37
|
-
shadows: {
|
|
38
|
-
preset: ShadowPreset;
|
|
39
|
-
intensity: number;
|
|
40
|
-
tintBrand: boolean;
|
|
41
|
-
};
|
|
42
|
-
};
|
|
43
|
-
export declare function getDerivedTheme(): ThemeTokens;
|
|
44
|
-
/** Update the brand color (h: 0-360, s/b: 0-100). */
|
|
45
|
-
export declare function setBrandHsb(h: number, s: number, b: number): void;
|
|
46
|
-
/** Update a single status tone's hue. */
|
|
47
|
-
export declare function setStatusHue(tone: 'error' | 'warning' | 'success' | 'info', hue: number): void;
|
|
48
|
-
/** Update the neutral palette mode. */
|
|
49
|
-
export declare function setNeutralMode(mode: NeutralMode): void;
|
|
50
|
-
/** Set the personality (chrome level) and apply cross-step defaults. */
|
|
51
|
-
export declare function setPersonality(p: Personality): void;
|
|
52
|
-
/** Override a dark background level. */
|
|
53
|
-
export declare function setDarkBg(level: 'base' | 'raised' | 'overlay', value: string): void;
|
|
54
|
-
/** Navigate to a specific step (1–5). */
|
|
55
|
-
export declare function setStep(n: number): void;
|
|
56
|
-
/** Advance to the next step. */
|
|
57
|
-
export declare function goNextStep(): void;
|
|
58
|
-
/** Go back to the previous step. */
|
|
59
|
-
export declare function goPrevStep(): void;
|
|
60
|
-
/** Enable fast-track mode and jump to the final step. */
|
|
61
|
-
export declare function activateFastTrack(): void;
|
|
62
|
-
/** Apply a named preset from the PRESETS array. */
|
|
63
|
-
export declare function applyPreset(name: string): void;
|
|
64
|
-
/**
|
|
65
|
-
* Return a CSS custom property string for live preview injection.
|
|
66
|
-
*
|
|
67
|
-
* @param mode - 'light' | 'dark'
|
|
68
|
-
* @returns e.g. `--dry-color-brand: hsl(230, 75%, 60%); ...`
|
|
69
|
-
*/
|
|
70
|
-
export declare function getStyleString(mode: 'light' | 'dark'): string;
|
|
71
|
-
/** Update the font preset name. */
|
|
72
|
-
export declare function setFontPreset(preset: FontPreset): void;
|
|
73
|
-
/** Update the type scale. */
|
|
74
|
-
export declare function setTypeScale(scale: TypeScale): void;
|
|
75
|
-
/** Reset all wizard state to defaults. */
|
|
76
|
-
export declare function resetToDefaults(): void;
|
|
77
|
-
/** Apply a full wizard recipe, using personality defaults before explicit overrides. */
|
|
78
|
-
export declare function applyRecipe(recipe: WizardRecipe): void;
|
|
79
|
-
export declare function setRadiusPreset(preset: RadiusPreset): void;
|
|
80
|
-
export declare function setRadiusScale(scale: number): void;
|
|
81
|
-
export declare function setDensity(density: Density): void;
|
|
82
|
-
export declare const RADIUS_PRESETS: Record<RadiusPreset, {
|
|
83
|
-
sm: number;
|
|
84
|
-
md: number;
|
|
85
|
-
lg: number;
|
|
86
|
-
xl: number;
|
|
87
|
-
'2xl': number;
|
|
88
|
-
full: number;
|
|
89
|
-
}>;
|
|
90
|
-
export declare function setShadowPreset(preset: ShadowPreset): void;
|
|
91
|
-
export declare function setShadowIntensity(intensity: number): void;
|
|
92
|
-
export declare function setShadowTint(tint: boolean): void;
|
|
93
|
-
export declare function getShadowTokens(shadowPreset?: ShadowPreset, shadowIntensity?: number, tintBrand?: boolean, brandHue?: number): {
|
|
94
|
-
light: Record<string, string>;
|
|
95
|
-
dark: Record<string, string>;
|
|
96
|
-
};
|
|
97
|
-
export declare function getShapeTokens(radiusPreset?: RadiusPreset, radiusScale?: number, densityPreset?: Density): Record<string, string>;
|
|
98
|
-
export declare function getPersonalityTokens(): Record<string, string>;
|
|
99
|
-
export declare const FONT_STACKS: Record<FontPreset, string>;
|
|
100
|
-
export declare function getTypographyTokens(fontPreset?: FontPreset, scale?: TypeScale): Record<string, string>;
|
|
101
|
-
/** Return all tokens (color + shape + shadow + personality + typography) merged for a given mode. */
|
|
102
|
-
export declare function getAllTokens(mode?: 'light' | 'dark'): Record<string, string>;
|
|
103
|
-
/** Return only tokens that the user has changed from defaults. */
|
|
104
|
-
export declare function getOverrideTokens(mode?: 'light' | 'dark'): Record<string, string>;
|