design-canvas-plugin-style-kit 0.1.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/dist/index.js ADDED
@@ -0,0 +1,3191 @@
1
+ // src/token-registry.ts
2
+ var SPACING_H_SCALE = [
3
+ ["None", "0"],
4
+ ["XXS", "2px"],
5
+ ["XS", "4px"],
6
+ ["SNudge", "6px"],
7
+ ["S", "8px"],
8
+ ["MNudge", "10px"],
9
+ ["M", "12px"],
10
+ ["L", "16px"],
11
+ ["XL", "20px"],
12
+ ["XXL", "24px"],
13
+ ["XXXL", "32px"]
14
+ ];
15
+ var SPACING_V_SCALE = SPACING_H_SCALE;
16
+ var FONT_SIZE_SCALE = [
17
+ ["Base100", "10px"],
18
+ ["Base200", "12px"],
19
+ ["Base300", "14px"],
20
+ ["Base400", "16px"],
21
+ ["Base500", "20px"],
22
+ ["Base600", "24px"],
23
+ ["Hero700", "28px"],
24
+ ["Hero800", "32px"],
25
+ ["Hero900", "40px"],
26
+ ["Hero1000", "68px"]
27
+ ];
28
+ var LINE_HEIGHT_SCALE = [
29
+ ["Base100", "14px"],
30
+ ["Base200", "16px"],
31
+ ["Base300", "20px"],
32
+ ["Base400", "22px"],
33
+ ["Base500", "28px"],
34
+ ["Base600", "32px"],
35
+ ["Hero700", "36px"],
36
+ ["Hero800", "40px"],
37
+ ["Hero900", "52px"],
38
+ ["Hero1000", "92px"]
39
+ ];
40
+ var FONT_WEIGHT_SCALE = [
41
+ ["Regular", "400"],
42
+ ["Medium", "500"],
43
+ ["Semibold", "600"],
44
+ ["Bold", "700"]
45
+ ];
46
+ var BORDER_RADIUS_SCALE = [
47
+ ["None", "0"],
48
+ ["Small", "2px"],
49
+ ["Medium", "4px"],
50
+ ["Large", "6px"],
51
+ ["XLarge", "8px"],
52
+ ["2XLarge", "12px"],
53
+ ["3XLarge", "16px"],
54
+ ["4XLarge", "24px"],
55
+ ["5XLarge", "32px"],
56
+ ["6XLarge", "40px"],
57
+ ["Circular", "10000px"]
58
+ ];
59
+ var STROKE_WIDTH_SCALE = [
60
+ ["Thin", "1px"],
61
+ ["Thick", "2px"],
62
+ ["Thicker", "3px"],
63
+ ["Thickest", "4px"]
64
+ ];
65
+ var SHADOW_SCALE = [
66
+ "shadow2",
67
+ "shadow4",
68
+ "shadow8",
69
+ "shadow16",
70
+ "shadow28",
71
+ "shadow64",
72
+ "shadow2Brand",
73
+ "shadow4Brand",
74
+ "shadow8Brand",
75
+ "shadow16Brand",
76
+ "shadow28Brand",
77
+ "shadow64Brand"
78
+ ];
79
+ var DURATION_SCALE = [
80
+ ["UltraFast", "50ms"],
81
+ ["Faster", "100ms"],
82
+ ["Fast", "150ms"],
83
+ ["Normal", "200ms"],
84
+ ["Gentle", "250ms"],
85
+ ["Slow", "300ms"],
86
+ ["Slower", "400ms"],
87
+ ["UltraSlow", "500ms"]
88
+ ];
89
+ var CURVE_SCALE = [
90
+ ["AccelerateMax", "cubic-bezier(1,0,1,1)"],
91
+ ["AccelerateMid", "cubic-bezier(0.7,0,1,0.5)"],
92
+ ["AccelerateMin", "cubic-bezier(0.8,0,1,1)"],
93
+ ["DecelerateMax", "cubic-bezier(0,0,0,1)"],
94
+ ["DecelerateMid", "cubic-bezier(0.1,0.9,0.2,1)"],
95
+ ["DecelerateMin", "cubic-bezier(0.33,0,0.1,1)"],
96
+ ["EasyEaseMax", "cubic-bezier(0.8,0,0.1,1)"],
97
+ ["EasyEase", "cubic-bezier(0.33,0,0.67,1)"],
98
+ ["Linear", "cubic-bezier(0,0,1,1)"]
99
+ ];
100
+ var FONT_FAMILY_NAMES = [
101
+ "fontFamilyBase",
102
+ "fontFamilyMonospace",
103
+ "fontFamilyNumeric"
104
+ ];
105
+ var NEUTRAL_BG = [
106
+ "colorNeutralBackground1",
107
+ "colorNeutralBackground2",
108
+ "colorNeutralBackground3",
109
+ "colorNeutralBackground4",
110
+ "colorNeutralBackground5",
111
+ "colorNeutralBackground6",
112
+ "colorNeutralBackgroundInverted",
113
+ "colorNeutralBackgroundStatic",
114
+ "colorSubtleBackground",
115
+ "colorTransparentBackground"
116
+ ];
117
+ var NEUTRAL_FG = [
118
+ "colorNeutralForeground1",
119
+ "colorNeutralForeground2",
120
+ "colorNeutralForeground3",
121
+ "colorNeutralForeground4",
122
+ "colorNeutralForegroundDisabled"
123
+ ];
124
+ var BRAND_BG = [
125
+ "colorBrandBackground",
126
+ "colorBrandBackground2",
127
+ "colorBrandBackgroundInverted",
128
+ "colorBrandBackgroundStatic"
129
+ ];
130
+ var BRAND_FG = [
131
+ "colorBrandForegroundLink",
132
+ "colorBrandForeground1",
133
+ "colorBrandForeground2",
134
+ "colorBrandForegroundInverted"
135
+ ];
136
+ var NEUTRAL_STROKE = [
137
+ "colorNeutralStroke1",
138
+ "colorNeutralStroke2",
139
+ "colorNeutralStroke3",
140
+ "colorNeutralStrokeSubtle",
141
+ "colorNeutralStrokeDisabled",
142
+ "colorNeutralStrokeAccessible"
143
+ ];
144
+ var BRAND_STROKE = [
145
+ "colorBrandStroke1",
146
+ "colorBrandStroke2",
147
+ "colorCompoundBrandStroke"
148
+ ];
149
+ function resolveVar(cssVar) {
150
+ const provider = document.querySelector(".fui-FluentProvider") ?? document.documentElement;
151
+ return getComputedStyle(provider).getPropertyValue(cssVar).trim();
152
+ }
153
+ function buildScale(category, label, prefix, entries) {
154
+ return {
155
+ category,
156
+ label,
157
+ entries: entries.map(([suffix, fallback]) => {
158
+ const name = `${prefix}${suffix}`;
159
+ const cssVar = `--${name}`;
160
+ const value = resolveVar(cssVar) || fallback;
161
+ return { name, cssVar, value, label: suffix };
162
+ })
163
+ };
164
+ }
165
+ function buildColorScale(category, label, names) {
166
+ return {
167
+ category,
168
+ label,
169
+ entries: names.map((name) => {
170
+ const cssVar = `--${name}`;
171
+ const value = resolveVar(cssVar) || "#000";
172
+ const short = name.replace("colorNeutral", "").replace("colorBrand", "").replace("colorCompound", "").replace("colorSubtle", "Subtle").replace("colorTransparent", "Transparent");
173
+ return { name, cssVar, value, label: short };
174
+ })
175
+ };
176
+ }
177
+ function buildShadowScale() {
178
+ return {
179
+ category: "shadow",
180
+ label: "Shadow",
181
+ entries: SHADOW_SCALE.map((name) => {
182
+ const cssVar = `--${name}`;
183
+ const value = resolveVar(cssVar) || "0 0 0 rgba(0,0,0,0)";
184
+ const short = name.replace("shadow", "").replace("Brand", " Brand") || name;
185
+ return { name, cssVar, value, label: short };
186
+ })
187
+ };
188
+ }
189
+ function buildFontFamilyScale() {
190
+ return {
191
+ category: "fontFamily",
192
+ label: "Font Family",
193
+ entries: FONT_FAMILY_NAMES.map((name) => {
194
+ const cssVar = `--${name}`;
195
+ const value = resolveVar(cssVar) || "";
196
+ const short = name.replace("fontFamily", "");
197
+ return { name, cssVar, value, label: short };
198
+ })
199
+ };
200
+ }
201
+ function buildRegistry() {
202
+ return [
203
+ buildScale("spacingH", "Horizontal Spacing", "spacingHorizontal", SPACING_H_SCALE),
204
+ buildScale("spacingV", "Vertical Spacing", "spacingVertical", SPACING_V_SCALE),
205
+ buildScale("fontSize", "Font Size", "fontSize", FONT_SIZE_SCALE),
206
+ buildScale("lineHeight", "Line Height", "lineHeight", LINE_HEIGHT_SCALE),
207
+ buildScale("fontWeight", "Font Weight", "fontWeight", FONT_WEIGHT_SCALE),
208
+ buildScale("borderRadius", "Border Radius", "borderRadius", BORDER_RADIUS_SCALE),
209
+ buildScale("strokeWidth", "Stroke Width", "strokeWidth", STROKE_WIDTH_SCALE),
210
+ buildShadowScale(),
211
+ buildFontFamilyScale(),
212
+ buildScale("duration", "Duration", "duration", DURATION_SCALE),
213
+ buildScale("curve", "Timing Curve", "curve", CURVE_SCALE),
214
+ buildColorScale("colorNeutralBg", "Neutral Background", NEUTRAL_BG),
215
+ buildColorScale("colorNeutralFg", "Neutral Foreground", NEUTRAL_FG),
216
+ buildColorScale("colorBrandBg", "Brand Background", BRAND_BG),
217
+ buildColorScale("colorBrandFg", "Brand Foreground", BRAND_FG),
218
+ buildColorScale("colorNeutralStroke", "Neutral Stroke", NEUTRAL_STROKE),
219
+ buildColorScale("colorBrandStroke", "Brand Stroke", BRAND_STROKE)
220
+ ];
221
+ }
222
+ function findToken(registry2, tokenName) {
223
+ for (const scale of registry2) {
224
+ const idx = scale.entries.findIndex((e) => e.name === tokenName);
225
+ if (idx !== -1) return { scale, index: idx };
226
+ }
227
+ return null;
228
+ }
229
+ function getResolvedValue(cssVar) {
230
+ return resolveVar(cssVar);
231
+ }
232
+ function serializeForPrompt(registry2) {
233
+ return registry2.map(
234
+ (s) => `${s.label}: ${s.entries.map((e) => `${e.label}=${e.value}`).join(", ")}`
235
+ ).join("\n");
236
+ }
237
+
238
+ // src/override-engine.ts
239
+ var STYLE_ID = "dc-style-kit-overrides";
240
+ var activeOverrides = /* @__PURE__ */ new Map();
241
+ var activeStaticOverrides = /* @__PURE__ */ new Map();
242
+ var listeners = [];
243
+ function getOrCreateStyleEl() {
244
+ let el2 = document.getElementById(STYLE_ID);
245
+ if (!el2) {
246
+ el2 = document.createElement("style");
247
+ el2.id = STYLE_ID;
248
+ el2.setAttribute("data-dc-plugin", "style-kit");
249
+ document.head.appendChild(el2);
250
+ }
251
+ return el2;
252
+ }
253
+ function overrideKey(o) {
254
+ const scopeKey = o.scope.type === "global" ? "_global_" : o.scope.selector;
255
+ return `${scopeKey}::${o.tokenName}`;
256
+ }
257
+ function staticKey(o) {
258
+ const scopeKey = o.scope.type === "global" ? "_global_" : o.scope.selector;
259
+ return `${scopeKey}::static::${o.property}`;
260
+ }
261
+ var TOKEN_TO_CSS_PROPERTY = {
262
+ fontSize: "font-size",
263
+ fontWeight: "font-weight",
264
+ lineHeight: "line-height",
265
+ fontFamily: "font-family",
266
+ spacingHorizontal: "padding-inline",
267
+ spacingVertical: "padding-block",
268
+ borderRadius: "border-radius",
269
+ strokeWidth: "border-width",
270
+ colorNeutralBackground: "background-color",
271
+ colorBrandBackground: "background-color",
272
+ colorSubtleBackground: "background-color",
273
+ colorTransparentBackground: "background-color",
274
+ colorNeutralForeground: "color",
275
+ colorBrandForeground: "color",
276
+ colorNeutralStroke: "border-color",
277
+ colorBrandStroke: "border-color",
278
+ colorPaletteRedBackground: "background-color",
279
+ colorPaletteRedForeground: "color",
280
+ colorPaletteRedBorder: "border-color",
281
+ colorPaletteGreenBackground: "background-color",
282
+ colorPaletteGreenForeground: "color",
283
+ colorPaletteGreenBorder: "border-color",
284
+ colorPaletteYellowBackground: "background-color",
285
+ colorPaletteYellowForeground: "color",
286
+ colorPaletteBlueForeground: "color",
287
+ colorPaletteBlueBackground: "background-color",
288
+ colorPaletteBlueBorder: "border-color",
289
+ shadow: "box-shadow"
290
+ };
291
+ function cssPropertyForToken(tokenName) {
292
+ for (const [prefix, prop] of Object.entries(TOKEN_TO_CSS_PROPERTY)) {
293
+ if (tokenName.startsWith(prefix)) return prop;
294
+ }
295
+ return null;
296
+ }
297
+ function renderCSS() {
298
+ const el2 = getOrCreateStyleEl();
299
+ const byScope = /* @__PURE__ */ new Map();
300
+ for (const o of activeOverrides.values()) {
301
+ const key = o.scope.type === "global" ? "_global_" : o.scope.selector;
302
+ if (!byScope.has(key)) byScope.set(key, { tokens: [], statics: [] });
303
+ byScope.get(key).tokens.push(o);
304
+ }
305
+ for (const o of activeStaticOverrides.values()) {
306
+ const key = o.scope.type === "global" ? "_global_" : o.scope.selector;
307
+ if (!byScope.has(key)) byScope.set(key, { tokens: [], statics: [] });
308
+ byScope.get(key).statics.push(o);
309
+ }
310
+ let css = "/* Style Kit overrides \u2014 auto-generated */\n";
311
+ for (const [scopeKey, { tokens, statics }] of byScope) {
312
+ const isGlobal = scopeKey === "_global_";
313
+ const selector = isGlobal ? ".fui-FluentProvider" : scopeKey;
314
+ const tokenProps = tokens.map((o) => ` ${o.cssVar}: ${o.newValue};`).join("\n");
315
+ const concreteProps = isGlobal ? "" : tokens.map((o) => {
316
+ const prop = cssPropertyForToken(o.tokenName);
317
+ return prop ? ` ${prop}: ${o.newValue} !important;` : "";
318
+ }).filter(Boolean).join("\n");
319
+ const staticProps = statics.map((o) => ` ${o.property}: ${o.newValue} !important;`).join("\n");
320
+ const allProps = [tokenProps, concreteProps, staticProps].filter(Boolean).join("\n");
321
+ css += `${selector} {
322
+ ${allProps}
323
+ }
324
+ `;
325
+ }
326
+ el2.textContent = css;
327
+ notify();
328
+ }
329
+ function notify() {
330
+ const list = Array.from(activeOverrides.values());
331
+ for (const fn of listeners) fn(list);
332
+ }
333
+ function applyOverride(override) {
334
+ const key = overrideKey(override);
335
+ activeOverrides.set(key, override);
336
+ renderCSS();
337
+ }
338
+ function removeOverride(tokenName, scope) {
339
+ const key = overrideKey({ tokenName, scope });
340
+ activeOverrides.delete(key);
341
+ renderCSS();
342
+ }
343
+ function getOverrides() {
344
+ return Array.from(activeOverrides.values());
345
+ }
346
+ function getOverrideForToken(tokenName, scope) {
347
+ return activeOverrides.get(overrideKey({ tokenName, scope }));
348
+ }
349
+ function clearAll() {
350
+ activeOverrides.clear();
351
+ activeStaticOverrides.clear();
352
+ renderCSS();
353
+ }
354
+ function bulkReplace(overrides) {
355
+ activeOverrides.clear();
356
+ for (const o of overrides) {
357
+ activeOverrides.set(overrideKey(o), o);
358
+ }
359
+ renderCSS();
360
+ }
361
+ function onOverridesChanged(fn) {
362
+ listeners.push(fn);
363
+ return () => {
364
+ listeners = listeners.filter((l) => l !== fn);
365
+ };
366
+ }
367
+ function applyStaticOverride(override) {
368
+ const key = staticKey(override);
369
+ activeStaticOverrides.set(key, override);
370
+ renderCSS();
371
+ }
372
+ function removeStaticOverride(property, scope) {
373
+ const key = staticKey({ property, scope });
374
+ activeStaticOverrides.delete(key);
375
+ renderCSS();
376
+ }
377
+ function getStaticOverrideForProperty(property, scope) {
378
+ return activeStaticOverrides.get(staticKey({ property, scope }));
379
+ }
380
+ function getStaticOverridesList() {
381
+ return Array.from(activeStaticOverrides.values());
382
+ }
383
+ function serializeOverrides() {
384
+ const list = getOverrides();
385
+ const staticList = getStaticOverridesList();
386
+ const registry2 = buildRegistry();
387
+ const tokenEntries = list.map((o) => {
388
+ const entry = {
389
+ token: o.tokenName,
390
+ from: o.originalValue,
391
+ to: o.newValue,
392
+ scope: o.scope.type === "global" ? "Global" : o.scope.label
393
+ };
394
+ const match = findToken(registry2, o.tokenName);
395
+ if (match) {
396
+ const swapTarget = match.scale.entries.find(
397
+ (e) => e.value === o.newValue && e.name !== o.tokenName
398
+ );
399
+ if (swapTarget) {
400
+ entry.swapTo = swapTarget.name;
401
+ }
402
+ }
403
+ return entry;
404
+ });
405
+ const staticEntries = staticList.map((o) => ({
406
+ token: `[static] ${o.property}`,
407
+ from: o.originalValue,
408
+ to: o.newValue,
409
+ scope: o.scope.type === "global" ? "Global" : o.scope.label
410
+ }));
411
+ const overrides = [...tokenEntries, ...staticEntries];
412
+ const css = getOrCreateStyleEl().textContent ?? "";
413
+ const globalOverrides = list.filter((o) => o.scope.type === "global");
414
+ const themeLines = globalOverrides.map(
415
+ (o) => ` ${o.tokenName}: '${o.newValue}',`
416
+ );
417
+ const themePartial = `const themeOverrides = {
418
+ ${themeLines.join("\n")}
419
+ };`;
420
+ return { overrides, css, themePartial };
421
+ }
422
+ function destroy() {
423
+ activeOverrides.clear();
424
+ activeStaticOverrides.clear();
425
+ listeners = [];
426
+ const el2 = document.getElementById(STYLE_ID);
427
+ el2?.remove();
428
+ }
429
+
430
+ // src/variations.ts
431
+ var STORE_KEY = "style-kit:variations";
432
+ var store = { active: null, variations: [] };
433
+ var storage = null;
434
+ var changeListeners = [];
435
+ function persist() {
436
+ storage?.set(STORE_KEY, store);
437
+ for (const fn of changeListeners) fn(store);
438
+ }
439
+ function genId() {
440
+ return `v_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
441
+ }
442
+ function initVariations(ctx2) {
443
+ storage = ctx2.storage;
444
+ const saved = storage.get(STORE_KEY);
445
+ if (saved && Array.isArray(saved.variations)) {
446
+ store = saved;
447
+ }
448
+ }
449
+ function getStore() {
450
+ return store;
451
+ }
452
+ function saveVariation(name) {
453
+ const overrides = getOverrides();
454
+ const v = {
455
+ id: genId(),
456
+ name,
457
+ created: (/* @__PURE__ */ new Date()).toISOString(),
458
+ overrides: overrides.map((o) => ({
459
+ tokenName: o.tokenName,
460
+ cssVar: o.cssVar,
461
+ originalValue: o.originalValue,
462
+ newValue: o.newValue,
463
+ scope: o.scope
464
+ }))
465
+ };
466
+ store.variations.push(v);
467
+ store.active = v.id;
468
+ persist();
469
+ return v;
470
+ }
471
+ function loadVariation(id) {
472
+ const v = store.variations.find((x) => x.id === id);
473
+ if (!v) return false;
474
+ const overrides = v.overrides.map((o) => ({
475
+ tokenName: o.tokenName,
476
+ cssVar: o.cssVar,
477
+ originalValue: o.originalValue,
478
+ newValue: o.newValue,
479
+ scope: o.scope
480
+ }));
481
+ bulkReplace(overrides);
482
+ store.active = id;
483
+ persist();
484
+ return true;
485
+ }
486
+ function loadBase() {
487
+ clearAll();
488
+ store.active = null;
489
+ persist();
490
+ }
491
+ function deleteVariation(id) {
492
+ store.variations = store.variations.filter((v) => v.id !== id);
493
+ if (store.active === id) {
494
+ clearAll();
495
+ store.active = null;
496
+ }
497
+ persist();
498
+ }
499
+ function updateActiveVariation() {
500
+ if (!store.active) return;
501
+ const v = store.variations.find((x) => x.id === store.active);
502
+ if (!v) return;
503
+ const overrides = getOverrides();
504
+ v.overrides = overrides.map((o) => ({
505
+ tokenName: o.tokenName,
506
+ cssVar: o.cssVar,
507
+ originalValue: o.originalValue,
508
+ newValue: o.newValue,
509
+ scope: o.scope
510
+ }));
511
+ persist();
512
+ }
513
+ function onVariationsChanged(fn) {
514
+ changeListeners.push(fn);
515
+ return () => {
516
+ changeListeners = changeListeners.filter((l) => l !== fn);
517
+ };
518
+ }
519
+ function destroyVariations() {
520
+ changeListeners = [];
521
+ }
522
+
523
+ // src/token-detector.ts
524
+ var PROBES = [
525
+ ["border-top-left-radius", "Border Radius", ["borderRadius"]],
526
+ ["padding-left", "Padding Left", ["spacingH"]],
527
+ ["padding-right", "Padding Right", ["spacingH"]],
528
+ ["padding-top", "Padding Top", ["spacingV"]],
529
+ ["padding-bottom", "Padding Bottom", ["spacingV"]],
530
+ ["margin-left", "Margin Left", ["spacingH"]],
531
+ ["margin-right", "Margin Right", ["spacingH"]],
532
+ ["margin-top", "Margin Top", ["spacingV"]],
533
+ ["margin-bottom", "Margin Bottom", ["spacingV"]],
534
+ ["column-gap", "Column Gap", ["spacingH"]],
535
+ ["row-gap", "Row Gap", ["spacingV"]],
536
+ ["gap", "Gap", ["spacingH"]],
537
+ ["font-size", "Font Size", ["fontSize"]],
538
+ ["font-weight", "Font Weight", ["fontWeight"]],
539
+ ["line-height", "Line Height", ["lineHeight"]],
540
+ ["border-top-width", "Border Width", ["strokeWidth"]],
541
+ ["color", "Text Color", ["colorNeutralFg", "colorBrandFg"]],
542
+ ["background-color", "Background", ["colorNeutralBg", "colorBrandBg"]],
543
+ ["border-top-color", "Border Color", ["colorNeutralStroke", "colorBrandStroke"]],
544
+ ["font-family", "Font Family", ["fontFamily"]],
545
+ ["box-shadow", "Box Shadow", ["shadow"]],
546
+ ["transition-duration", "Duration", ["duration"]],
547
+ ["transition-timing-function", "Easing", ["curve"]]
548
+ ];
549
+ var EXTRA_STATICS = [
550
+ ["width", "Width"],
551
+ ["height", "Height"],
552
+ ["max-width", "Max Width"],
553
+ ["min-height", "Min Height"],
554
+ ["opacity", "Opacity"],
555
+ ["text-transform", "Text Transform"],
556
+ ["letter-spacing", "Letter Spacing"],
557
+ ["text-decoration-line", "Text Decoration"],
558
+ ["display", "Display"],
559
+ ["overflow-x", "Overflow X"],
560
+ ["overflow-y", "Overflow Y"],
561
+ ["position", "Position"],
562
+ ["z-index", "Z-Index"],
563
+ ["cursor", "Cursor"],
564
+ ["white-space", "White Space"],
565
+ ["text-overflow", "Text Overflow"],
566
+ ["text-align", "Text Align"],
567
+ ["flex-direction", "Flex Direction"],
568
+ ["align-items", "Align Items"],
569
+ ["justify-content", "Justify Content"]
570
+ ];
571
+ var NOISE_VALUES = /* @__PURE__ */ new Set([
572
+ "",
573
+ "auto",
574
+ "none",
575
+ "normal",
576
+ "visible",
577
+ "0",
578
+ "0px",
579
+ "transparent",
580
+ "rgba(0, 0, 0, 0)",
581
+ "start",
582
+ "stretch",
583
+ "baseline",
584
+ "static",
585
+ "clip",
586
+ "ltr",
587
+ "row"
588
+ ]);
589
+ function isNoiseValue(prop, val) {
590
+ const v = val.toLowerCase().trim();
591
+ if (!v || NOISE_VALUES.has(v)) return true;
592
+ if (prop === "opacity" && v === "1") return true;
593
+ if (prop === "cursor" && (v === "default" || v === "auto")) return true;
594
+ if (prop === "display" && v === "block") return true;
595
+ if (prop === "text-align" && v === "start") return true;
596
+ return false;
597
+ }
598
+ function isColorVal(val) {
599
+ return /^(rgb|hsl|#)/i.test(val);
600
+ }
601
+ function normalize(val) {
602
+ return val.trim().toLowerCase().replace(/\s+/g, " ");
603
+ }
604
+ function valuesMatch(a, b) {
605
+ return normalize(a) === normalize(b);
606
+ }
607
+ function detectAll(element, registry2) {
608
+ const cs = getComputedStyle(element);
609
+ const rawTokens = [];
610
+ const rawStatics = [];
611
+ const seen = /* @__PURE__ */ new Set();
612
+ for (const [prop, label, cats] of PROBES) {
613
+ const val = cs.getPropertyValue(prop).trim();
614
+ if (!val || val === "none" || val === "normal" || val === "auto" || val === "0px" || val === "0") continue;
615
+ seen.add(prop);
616
+ let matched = false;
617
+ for (const cat of cats) {
618
+ const scale = registry2.find((s) => s.category === cat);
619
+ if (!scale) continue;
620
+ const idx = scale.entries.findIndex((e) => valuesMatch(e.value, val));
621
+ if (idx !== -1) {
622
+ rawTokens.push({ property: prop, label, current: scale.entries[idx], scale, index: idx });
623
+ matched = true;
624
+ break;
625
+ }
626
+ }
627
+ if (!matched) {
628
+ rawStatics.push({ property: prop, label, value: val, isColor: isColorVal(val), candidateCategories: [...cats] });
629
+ }
630
+ }
631
+ for (const [prop, label] of EXTRA_STATICS) {
632
+ if (seen.has(prop)) continue;
633
+ const val = cs.getPropertyValue(prop).trim();
634
+ if (isNoiseValue(prop, val)) continue;
635
+ rawStatics.push({ property: prop, label, value: val, isColor: isColorVal(val), candidateCategories: [] });
636
+ }
637
+ return { tokens: dedup(rawTokens), statics: dedupStatics(rawStatics) };
638
+ }
639
+ function dedup(items) {
640
+ const groups = /* @__PURE__ */ new Map();
641
+ for (const d of items) {
642
+ const key = `${d.scale.category}::${d.current.name}`;
643
+ const arr = groups.get(key) ?? [];
644
+ arr.push(d);
645
+ groups.set(key, arr);
646
+ }
647
+ const result = [];
648
+ for (const [, group] of groups) {
649
+ const first = group[0];
650
+ if (group.length > 1) {
651
+ first.label = mergeLabel(group);
652
+ }
653
+ result.push(first);
654
+ }
655
+ return result;
656
+ }
657
+ function mergeLabel(group) {
658
+ const props = group.map((g) => g.property);
659
+ const base = props[0].replace(/-left|-right|-top|-bottom|-top-left-radius|-top-right-radius|-bottom-left-radius|-bottom-right-radius/, "");
660
+ if (base.includes("radius")) return "Border Radius";
661
+ if (base.includes("padding")) return allSides(props) ? "Padding" : hasSide(props, "horiz") ? "Padding (H)" : "Padding (V)";
662
+ if (base.includes("margin")) return allSides(props) ? "Margin" : hasSide(props, "horiz") ? "Margin (H)" : "Margin (V)";
663
+ return group[0].label;
664
+ }
665
+ function allSides(props) {
666
+ return props.length >= 4 || hasSide(props, "horiz") && hasSide(props, "vert");
667
+ }
668
+ function hasSide(props, dir) {
669
+ if (dir === "horiz") return props.some((p) => p.includes("left") || p.includes("right"));
670
+ return props.some((p) => p.includes("top") || p.includes("bottom"));
671
+ }
672
+ function dedupStatics(items) {
673
+ const groups = /* @__PURE__ */ new Map();
674
+ for (const sv of items) {
675
+ const base = sv.property.replace(/-left$|-right$|-top$|-bottom$/, "").replace(/-top-left-radius$|-top-right-radius$|-bottom-left-radius$|-bottom-right-radius$/, "-radius").replace(/-x$|-y$/, "");
676
+ const key = `${base}::${sv.value}`;
677
+ const arr = groups.get(key) ?? [];
678
+ arr.push(sv);
679
+ groups.set(key, arr);
680
+ }
681
+ const result = [];
682
+ for (const [, group] of groups) {
683
+ const first = { ...group[0] };
684
+ if (group.length > 1) {
685
+ first.label = mergeStaticLabel(group);
686
+ const cats = /* @__PURE__ */ new Set();
687
+ for (const sv of group) sv.candidateCategories.forEach((c) => cats.add(c));
688
+ first.candidateCategories = [...cats];
689
+ }
690
+ result.push(first);
691
+ }
692
+ return result;
693
+ }
694
+ function mergeStaticLabel(group) {
695
+ const props = group.map((g) => g.property);
696
+ if (props.some((p) => p.includes("overflow"))) return "Overflow";
697
+ if (props.some((p) => p.includes("padding"))) {
698
+ if (props.length >= 4) return "Padding";
699
+ if (props.some((p) => p.includes("left") || p.includes("right"))) return "Padding (H)";
700
+ return "Padding (V)";
701
+ }
702
+ if (props.some((p) => p.includes("margin"))) {
703
+ if (props.length >= 4) return "Margin";
704
+ if (props.some((p) => p.includes("left") || p.includes("right"))) return "Margin (H)";
705
+ return "Margin (V)";
706
+ }
707
+ if (props.some((p) => p.includes("radius"))) return "Border Radius";
708
+ if (props.some((p) => p.includes("border") && p.includes("color"))) return "Border Color";
709
+ if (props.some((p) => p.includes("border") && p.includes("width"))) return "Border Width";
710
+ return group[0].label;
711
+ }
712
+ function findNearestInScale(value, categories, registry2) {
713
+ const numMatch = value.match(/^([\d.]+)(px|rem|em)?$/);
714
+ if (!numMatch) return null;
715
+ const numVal = parseFloat(numMatch[1]);
716
+ let best = null;
717
+ for (const cat of categories) {
718
+ const scale = registry2.find((s) => s.category === cat);
719
+ if (!scale) continue;
720
+ for (const entry of scale.entries) {
721
+ const entryMatch = entry.value.match(/^([\d.]+)(px|rem|em)?$/);
722
+ if (!entryMatch) continue;
723
+ const entryVal = parseFloat(entryMatch[1]);
724
+ const dist = Math.abs(numVal - entryVal);
725
+ if (!best || dist < best.distance) {
726
+ best = { entry, scale, distance: dist };
727
+ }
728
+ }
729
+ }
730
+ return best;
731
+ }
732
+
733
+ // src/panel-styles.ts
734
+ var PANEL_STYLES = (
735
+ /* css */
736
+ `
737
+ /* \u2500\u2500\u2500 Reset & Root \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
738
+ .sk {
739
+ --sk-bg: #ffffff;
740
+ --sk-bg-subtle: #f5f5f5;
741
+ --sk-bg-hover: #ebebeb;
742
+ --sk-bg-active: #e0e0e0;
743
+ --sk-bg-accent: #0078d4;
744
+ --sk-bg-accent-subtle: #e8f4fd;
745
+ --sk-fg: #242424;
746
+ --sk-fg-secondary: #616161;
747
+ --sk-fg-tertiary: #8a8a8a;
748
+ --sk-fg-on-accent: #ffffff;
749
+ --sk-border: #e0e0e0;
750
+ --sk-border-subtle: #ebebeb;
751
+ --sk-radius-s: 4px;
752
+ --sk-radius-m: 6px;
753
+ --sk-radius-l: 8px;
754
+ --sk-shadow-sm: 0 1px 2px rgba(0,0,0,0.06);
755
+ --sk-shadow-md: 0 2px 8px rgba(0,0,0,0.1);
756
+ --sk-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
757
+ --sk-mono: 'SF Mono', 'Cascadia Code', 'Consolas', monospace;
758
+ --sk-transition: 150ms ease;
759
+
760
+ font-family: var(--sk-font);
761
+ font-size: 12px;
762
+ color: var(--sk-fg);
763
+ line-height: 1.4;
764
+ display: flex;
765
+ flex-direction: column;
766
+ height: 100%;
767
+ overflow: hidden;
768
+ -webkit-font-smoothing: antialiased;
769
+ }
770
+
771
+ .sk.sk--dark {
772
+ --sk-bg: #1e1e1e;
773
+ --sk-bg-subtle: #2a2a2a;
774
+ --sk-bg-hover: #333333;
775
+ --sk-bg-active: #3d3d3d;
776
+ --sk-bg-accent: #4dabf5;
777
+ --sk-bg-accent-subtle: #1a3a52;
778
+ --sk-fg: #e0e0e0;
779
+ --sk-fg-secondary: #999999;
780
+ --sk-fg-tertiary: #666666;
781
+ --sk-fg-on-accent: #000000;
782
+ --sk-border: #3d3d3d;
783
+ --sk-border-subtle: #333333;
784
+ }
785
+
786
+ /* \u2500\u2500\u2500 Sections \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
787
+ .sk-header {
788
+ padding: 12px 14px 8px;
789
+ border-bottom: 1px solid var(--sk-border-subtle);
790
+ flex-shrink: 0;
791
+ }
792
+
793
+ .sk-header__title {
794
+ display: flex;
795
+ align-items: center;
796
+ gap: 6px;
797
+ font-size: 13px;
798
+ font-weight: 600;
799
+ margin: 0;
800
+ }
801
+
802
+ .sk-header__badge {
803
+ font-size: 10px;
804
+ font-weight: 500;
805
+ padding: 1px 6px;
806
+ border-radius: 10px;
807
+ background: var(--sk-bg-accent);
808
+ color: var(--sk-fg-on-accent);
809
+ }
810
+
811
+ .sk-body {
812
+ flex: 1;
813
+ overflow-y: auto;
814
+ overflow-x: hidden;
815
+ scrollbar-width: thin;
816
+ scrollbar-color: var(--sk-border) transparent;
817
+ }
818
+
819
+ .sk-footer {
820
+ padding: 10px 14px;
821
+ border-top: 1px solid var(--sk-border-subtle);
822
+ flex-shrink: 0;
823
+ }
824
+
825
+ /* \u2500\u2500\u2500 Scope Bar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
826
+ .sk-scope {
827
+ display: flex;
828
+ align-items: center;
829
+ gap: 6px;
830
+ padding: 8px 14px;
831
+ background: var(--sk-bg-subtle);
832
+ border-bottom: 1px solid var(--sk-border-subtle);
833
+ font-size: 11px;
834
+ flex-shrink: 0;
835
+ }
836
+
837
+ .sk-scope__label {
838
+ color: var(--sk-fg-tertiary);
839
+ text-transform: uppercase;
840
+ letter-spacing: 0.5px;
841
+ font-weight: 500;
842
+ flex-shrink: 0;
843
+ }
844
+
845
+ .sk-scope__value {
846
+ display: flex;
847
+ align-items: center;
848
+ gap: 4px;
849
+ padding: 2px 8px;
850
+ background: var(--sk-bg);
851
+ border: 1px solid var(--sk-border);
852
+ border-radius: var(--sk-radius-s);
853
+ cursor: pointer;
854
+ transition: border-color var(--sk-transition);
855
+ min-width: 0;
856
+ flex: 1;
857
+ }
858
+
859
+ .sk-scope__value:hover {
860
+ border-color: var(--sk-bg-accent);
861
+ }
862
+
863
+ .sk-scope__value-text {
864
+ overflow: hidden;
865
+ text-overflow: ellipsis;
866
+ white-space: nowrap;
867
+ flex: 1;
868
+ direction: rtl;
869
+ text-align: left;
870
+ }
871
+
872
+ .sk-scope__pick {
873
+ flex-shrink: 0;
874
+ display: inline-flex;
875
+ align-items: center;
876
+ gap: 4px;
877
+ padding: 3px 10px;
878
+ border-radius: var(--sk-radius-s);
879
+ border: 1px solid var(--sk-bg-accent);
880
+ background: var(--sk-bg-accent-subtle);
881
+ cursor: pointer;
882
+ color: var(--sk-bg-accent);
883
+ font-size: 11px;
884
+ font-weight: 600;
885
+ font-family: var(--sk-font);
886
+ transition: all var(--sk-transition);
887
+ }
888
+
889
+ .sk-scope__pick svg {
890
+ width: 12px;
891
+ height: 12px;
892
+ }
893
+
894
+ .sk-scope__pick:hover {
895
+ background: var(--sk-bg-accent);
896
+ color: var(--sk-fg-on-accent);
897
+ border-color: var(--sk-bg-accent);
898
+ }
899
+
900
+ .sk-scope__pick--icon-only {
901
+ padding: 3px;
902
+ width: 22px;
903
+ height: 22px;
904
+ justify-content: center;
905
+ }
906
+
907
+ .sk-scope__pick.sk-scope__pick--active {
908
+ background: var(--sk-bg-accent);
909
+ color: var(--sk-fg-on-accent);
910
+ border-color: var(--sk-bg-accent);
911
+ animation: sk-pulse 1.5s ease infinite;
912
+ }
913
+
914
+ @keyframes sk-pulse {
915
+ 0%, 100% { box-shadow: 0 0 0 0 rgba(0,120,212,0.3); }
916
+ 50% { box-shadow: 0 0 0 4px rgba(0,120,212,0); }
917
+ }
918
+
919
+ /* \u2500\u2500\u2500 Variations Bar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
920
+ .sk-variations {
921
+ display: flex;
922
+ align-items: center;
923
+ gap: 4px;
924
+ padding: 6px 14px;
925
+ border-bottom: 1px solid var(--sk-border-subtle);
926
+ overflow-x: auto;
927
+ scrollbar-width: none;
928
+ flex-shrink: 0;
929
+ }
930
+
931
+ .sk-variations::-webkit-scrollbar { display: none; }
932
+
933
+ .sk-var-chip {
934
+ display: inline-flex;
935
+ align-items: center;
936
+ gap: 4px;
937
+ padding: 3px 10px;
938
+ border-radius: 100px;
939
+ border: 1px solid var(--sk-border);
940
+ background: var(--sk-bg);
941
+ font-size: 11px;
942
+ cursor: pointer;
943
+ white-space: nowrap;
944
+ transition: all var(--sk-transition);
945
+ user-select: none;
946
+ }
947
+
948
+ .sk-var-chip:hover {
949
+ border-color: var(--sk-fg-secondary);
950
+ }
951
+
952
+ .sk-var-chip--active {
953
+ background: var(--sk-bg-accent);
954
+ color: var(--sk-fg-on-accent);
955
+ border-color: var(--sk-bg-accent);
956
+ }
957
+
958
+ .sk-var-chip--base {
959
+ font-weight: 500;
960
+ }
961
+
962
+ .sk-var-chip__x {
963
+ width: 12px;
964
+ height: 12px;
965
+ display: flex;
966
+ align-items: center;
967
+ justify-content: center;
968
+ border-radius: 50%;
969
+ opacity: 0.6;
970
+ transition: opacity var(--sk-transition);
971
+ font-size: 10px;
972
+ }
973
+
974
+ .sk-var-chip__x:hover { opacity: 1; }
975
+
976
+ .sk-var-add {
977
+ width: 24px;
978
+ height: 24px;
979
+ display: flex;
980
+ align-items: center;
981
+ justify-content: center;
982
+ border-radius: 50%;
983
+ border: 1px dashed var(--sk-border);
984
+ background: transparent;
985
+ cursor: pointer;
986
+ color: var(--sk-fg-tertiary);
987
+ font-size: 14px;
988
+ flex-shrink: 0;
989
+ transition: all var(--sk-transition);
990
+ }
991
+
992
+ .sk-var-add:hover {
993
+ border-color: var(--sk-bg-accent);
994
+ color: var(--sk-bg-accent);
995
+ background: var(--sk-bg-accent-subtle);
996
+ }
997
+
998
+ /* \u2500\u2500\u2500 Inline Input (replaces window.prompt) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
999
+ .sk-inline-input {
1000
+ display: inline-flex;
1001
+ align-items: center;
1002
+ gap: 3px;
1003
+ flex-shrink: 0;
1004
+ }
1005
+
1006
+ .sk-inline-input__field {
1007
+ width: 120px;
1008
+ padding: 2px 6px;
1009
+ border: 1px solid var(--sk-bg-accent);
1010
+ border-radius: 100px;
1011
+ background: var(--sk-bg);
1012
+ color: var(--sk-fg);
1013
+ font-size: 11px;
1014
+ outline: none;
1015
+ font-family: inherit;
1016
+ }
1017
+
1018
+ .sk-inline-input__field:focus {
1019
+ border-color: var(--sk-bg-accent);
1020
+ box-shadow: 0 0 0 1px var(--sk-bg-accent);
1021
+ }
1022
+
1023
+ .sk-inline-input__ok,
1024
+ .sk-inline-input__cancel {
1025
+ width: 20px;
1026
+ height: 20px;
1027
+ display: flex;
1028
+ align-items: center;
1029
+ justify-content: center;
1030
+ border-radius: 50%;
1031
+ border: none;
1032
+ background: transparent;
1033
+ cursor: pointer;
1034
+ font-size: 11px;
1035
+ color: var(--sk-fg-secondary);
1036
+ padding: 0;
1037
+ }
1038
+
1039
+ .sk-inline-input__ok:hover { color: var(--sk-bg-accent); }
1040
+ .sk-inline-input__cancel:hover { color: var(--sk-fg); }
1041
+
1042
+ /* \u2500\u2500\u2500 Token Category (collapsible) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
1043
+ .sk-cat {
1044
+ border-bottom: 1px solid var(--sk-border-subtle);
1045
+ }
1046
+
1047
+ .sk-cat__header {
1048
+ display: flex;
1049
+ align-items: center;
1050
+ gap: 6px;
1051
+ padding: 8px 14px;
1052
+ cursor: pointer;
1053
+ user-select: none;
1054
+ transition: background var(--sk-transition);
1055
+ }
1056
+
1057
+ .sk-cat__header:hover {
1058
+ background: var(--sk-bg-subtle);
1059
+ }
1060
+
1061
+ .sk-cat__chevron {
1062
+ width: 12px;
1063
+ height: 12px;
1064
+ color: var(--sk-fg-tertiary);
1065
+ transition: transform var(--sk-transition);
1066
+ flex-shrink: 0;
1067
+ }
1068
+
1069
+ .sk-cat--open > .sk-cat__header > .sk-cat__chevron {
1070
+ transform: rotate(90deg);
1071
+ }
1072
+
1073
+ .sk-cat__label {
1074
+ font-size: 11px;
1075
+ font-weight: 600;
1076
+ text-transform: uppercase;
1077
+ letter-spacing: 0.4px;
1078
+ color: var(--sk-fg-secondary);
1079
+ flex: 1;
1080
+ }
1081
+
1082
+ .sk-cat__count {
1083
+ font-size: 10px;
1084
+ padding: 0 5px;
1085
+ border-radius: 8px;
1086
+ background: var(--sk-bg-accent);
1087
+ color: var(--sk-fg-on-accent);
1088
+ font-weight: 500;
1089
+ line-height: 16px;
1090
+ }
1091
+
1092
+ .sk-cat__body {
1093
+ display: none;
1094
+ padding: 0 14px 8px;
1095
+ }
1096
+
1097
+ .sk-cat--open > .sk-cat__body {
1098
+ display: block;
1099
+ }
1100
+
1101
+ /* \u2500\u2500\u2500 Token Stepper (discrete scale control) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
1102
+ .sk-stepper {
1103
+ display: flex;
1104
+ align-items: center;
1105
+ gap: 6px;
1106
+ padding: 4px 0;
1107
+ }
1108
+
1109
+ .sk-stepper__label {
1110
+ width: 90px;
1111
+ font-size: 11px;
1112
+ color: var(--sk-fg-secondary);
1113
+ overflow: hidden;
1114
+ text-overflow: ellipsis;
1115
+ white-space: nowrap;
1116
+ flex-shrink: 0;
1117
+ }
1118
+
1119
+ .sk-stepper__track {
1120
+ flex: 1;
1121
+ display: flex;
1122
+ align-items: center;
1123
+ gap: 0;
1124
+ height: 24px;
1125
+ position: relative;
1126
+ }
1127
+
1128
+ .sk-stepper__step {
1129
+ flex: 1;
1130
+ height: 6px;
1131
+ background: var(--sk-bg-hover);
1132
+ cursor: pointer;
1133
+ position: relative;
1134
+ transition: background var(--sk-transition);
1135
+ }
1136
+
1137
+ .sk-stepper__step:first-child {
1138
+ border-radius: 3px 0 0 3px;
1139
+ }
1140
+
1141
+ .sk-stepper__step:last-child {
1142
+ border-radius: 0 3px 3px 0;
1143
+ }
1144
+
1145
+ .sk-stepper__step--active {
1146
+ background: var(--sk-bg-accent);
1147
+ }
1148
+
1149
+ .sk-stepper__step--modified {
1150
+ background: var(--sk-bg-accent);
1151
+ opacity: 0.7;
1152
+ }
1153
+
1154
+ .sk-stepper__step:hover {
1155
+ background: var(--sk-bg-accent);
1156
+ opacity: 0.85;
1157
+ }
1158
+
1159
+ .sk-stepper__step:hover::after {
1160
+ content: attr(data-tooltip);
1161
+ position: absolute;
1162
+ bottom: calc(100% + 6px);
1163
+ left: 50%;
1164
+ transform: translateX(-50%);
1165
+ padding: 3px 8px;
1166
+ background: var(--sk-fg);
1167
+ color: var(--sk-bg);
1168
+ font-size: 10px;
1169
+ border-radius: var(--sk-radius-s);
1170
+ white-space: nowrap;
1171
+ pointer-events: none;
1172
+ z-index: 10;
1173
+ box-shadow: var(--sk-shadow-md);
1174
+ }
1175
+
1176
+ .sk-stepper__value {
1177
+ width: 48px;
1178
+ font-size: 10px;
1179
+ font-family: var(--sk-mono);
1180
+ color: var(--sk-fg-tertiary);
1181
+ text-align: right;
1182
+ flex-shrink: 0;
1183
+ }
1184
+
1185
+ .sk-stepper__value--modified {
1186
+ color: var(--sk-bg-accent);
1187
+ font-weight: 600;
1188
+ }
1189
+
1190
+ /* \u2500\u2500\u2500 Color Swatch (for color tokens) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
1191
+ .sk-swatch-row {
1192
+ display: flex;
1193
+ align-items: center;
1194
+ gap: 6px;
1195
+ padding: 3px 0;
1196
+ }
1197
+
1198
+ .sk-swatch-row__label {
1199
+ width: 90px;
1200
+ font-size: 11px;
1201
+ color: var(--sk-fg-secondary);
1202
+ overflow: hidden;
1203
+ text-overflow: ellipsis;
1204
+ white-space: nowrap;
1205
+ flex-shrink: 0;
1206
+ }
1207
+
1208
+ .sk-swatch {
1209
+ width: 20px;
1210
+ height: 20px;
1211
+ border-radius: var(--sk-radius-s);
1212
+ border: 1px solid var(--sk-border);
1213
+ cursor: pointer;
1214
+ transition: transform var(--sk-transition), box-shadow var(--sk-transition);
1215
+ flex-shrink: 0;
1216
+ }
1217
+
1218
+ .sk-swatch:hover {
1219
+ transform: scale(1.15);
1220
+ box-shadow: var(--sk-shadow-sm);
1221
+ }
1222
+
1223
+ .sk-swatch--active {
1224
+ outline: 2px solid var(--sk-bg-accent);
1225
+ outline-offset: 1px;
1226
+ }
1227
+
1228
+ .sk-swatch__hex {
1229
+ font-size: 10px;
1230
+ font-family: var(--sk-mono);
1231
+ color: var(--sk-fg-tertiary);
1232
+ flex: 1;
1233
+ }
1234
+
1235
+ .sk-swatch__hex--modified {
1236
+ color: var(--sk-bg-accent);
1237
+ font-weight: 600;
1238
+ }
1239
+
1240
+ .sk-color-input {
1241
+ width: 0;
1242
+ height: 0;
1243
+ padding: 0;
1244
+ border: 0;
1245
+ opacity: 0;
1246
+ position: absolute;
1247
+ }
1248
+
1249
+ /* \u2500\u2500\u2500 Prompt Bar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
1250
+ .sk-prompt {
1251
+ padding: 10px 14px;
1252
+ border-bottom: 1px solid var(--sk-border-subtle);
1253
+ flex-shrink: 0;
1254
+ }
1255
+
1256
+ .sk-prompt__wrap {
1257
+ display: flex;
1258
+ align-items: center;
1259
+ gap: 6px;
1260
+ border: 1px solid var(--sk-border);
1261
+ border-radius: var(--sk-radius-m);
1262
+ padding: 0 4px 0 10px;
1263
+ background: var(--sk-bg);
1264
+ transition: border-color var(--sk-transition), box-shadow var(--sk-transition);
1265
+ }
1266
+
1267
+ .sk-prompt__wrap:focus-within {
1268
+ border-color: var(--sk-bg-accent);
1269
+ box-shadow: 0 0 0 1px var(--sk-bg-accent);
1270
+ }
1271
+
1272
+ .sk-prompt__icon {
1273
+ flex-shrink: 0;
1274
+ color: var(--sk-fg-tertiary);
1275
+ width: 14px;
1276
+ height: 14px;
1277
+ }
1278
+
1279
+ .sk-prompt__input {
1280
+ flex: 1;
1281
+ border: none;
1282
+ outline: none;
1283
+ background: transparent;
1284
+ font-family: var(--sk-font);
1285
+ font-size: 12px;
1286
+ color: var(--sk-fg);
1287
+ padding: 7px 0;
1288
+ min-width: 0;
1289
+ }
1290
+
1291
+ .sk-prompt__input::placeholder {
1292
+ color: var(--sk-fg-tertiary);
1293
+ }
1294
+
1295
+ .sk-prompt__send {
1296
+ flex-shrink: 0;
1297
+ width: 26px;
1298
+ height: 26px;
1299
+ display: flex;
1300
+ align-items: center;
1301
+ justify-content: center;
1302
+ border-radius: var(--sk-radius-s);
1303
+ border: none;
1304
+ background: var(--sk-bg-accent);
1305
+ color: var(--sk-fg-on-accent);
1306
+ cursor: pointer;
1307
+ transition: opacity var(--sk-transition);
1308
+ opacity: 0.4;
1309
+ }
1310
+
1311
+ .sk-prompt__send--ready {
1312
+ opacity: 1;
1313
+ }
1314
+
1315
+ .sk-prompt__send:hover {
1316
+ opacity: 0.9;
1317
+ }
1318
+
1319
+ .sk-prompt__send--loading {
1320
+ opacity: 0.5;
1321
+ cursor: wait;
1322
+ }
1323
+
1324
+ .sk-prompt__result {
1325
+ margin-top: 6px;
1326
+ padding: 6px 8px;
1327
+ background: var(--sk-bg-accent-subtle);
1328
+ border-radius: var(--sk-radius-s);
1329
+ font-size: 11px;
1330
+ color: var(--sk-fg-secondary);
1331
+ display: flex;
1332
+ align-items: center;
1333
+ gap: 6px;
1334
+ }
1335
+
1336
+ .sk-prompt__result-icon {
1337
+ flex-shrink: 0;
1338
+ }
1339
+
1340
+ /* \u2500\u2500\u2500 Action Buttons \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
1341
+ .sk-actions {
1342
+ display: flex;
1343
+ gap: 6px;
1344
+ flex-wrap: wrap;
1345
+ }
1346
+
1347
+ .sk-btn {
1348
+ display: inline-flex;
1349
+ align-items: center;
1350
+ gap: 5px;
1351
+ padding: 5px 10px;
1352
+ border-radius: var(--sk-radius-s);
1353
+ border: 1px solid var(--sk-border);
1354
+ background: var(--sk-bg);
1355
+ font-family: var(--sk-font);
1356
+ font-size: 11px;
1357
+ color: var(--sk-fg);
1358
+ cursor: pointer;
1359
+ transition: all var(--sk-transition);
1360
+ user-select: none;
1361
+ white-space: nowrap;
1362
+ }
1363
+
1364
+ .sk-btn:hover {
1365
+ background: var(--sk-bg-hover);
1366
+ border-color: var(--sk-fg-secondary);
1367
+ }
1368
+
1369
+ .sk-btn--primary {
1370
+ background: var(--sk-bg-accent);
1371
+ color: var(--sk-fg-on-accent);
1372
+ border-color: var(--sk-bg-accent);
1373
+ }
1374
+
1375
+ .sk-btn--primary:hover {
1376
+ opacity: 0.9;
1377
+ }
1378
+
1379
+ .sk-btn--ghost {
1380
+ border-color: transparent;
1381
+ background: transparent;
1382
+ }
1383
+
1384
+ .sk-btn--ghost:hover {
1385
+ background: var(--sk-bg-subtle);
1386
+ }
1387
+
1388
+ .sk-btn--sm {
1389
+ padding: 3px 6px;
1390
+ font-size: 10px;
1391
+ }
1392
+
1393
+ .sk-btn__icon {
1394
+ width: 12px;
1395
+ height: 12px;
1396
+ }
1397
+
1398
+ /* \u2500\u2500\u2500 Override Summary Pill \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
1399
+ .sk-summary {
1400
+ display: flex;
1401
+ align-items: center;
1402
+ justify-content: space-between;
1403
+ padding: 6px 14px;
1404
+ background: var(--sk-bg-accent-subtle);
1405
+ border-bottom: 1px solid var(--sk-border-subtle);
1406
+ font-size: 11px;
1407
+ flex-shrink: 0;
1408
+ }
1409
+
1410
+ .sk-summary__text {
1411
+ color: var(--sk-bg-accent);
1412
+ font-weight: 500;
1413
+ }
1414
+
1415
+ .sk-summary__actions {
1416
+ display: flex;
1417
+ gap: 4px;
1418
+ }
1419
+
1420
+ /* \u2500\u2500\u2500 Empty State \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
1421
+ .sk-empty {
1422
+ display: flex;
1423
+ flex-direction: column;
1424
+ align-items: center;
1425
+ justify-content: center;
1426
+ padding: 32px 20px;
1427
+ text-align: center;
1428
+ gap: 12px;
1429
+ color: var(--sk-fg-tertiary);
1430
+ }
1431
+
1432
+ .sk-empty__icon {
1433
+ font-size: 28px;
1434
+ opacity: 0.5;
1435
+ }
1436
+
1437
+ .sk-empty__text {
1438
+ font-size: 13px;
1439
+ font-weight: 500;
1440
+ max-width: 220px;
1441
+ color: var(--sk-fg-secondary);
1442
+ }
1443
+
1444
+ .sk-empty__hint {
1445
+ font-size: 11px;
1446
+ opacity: 0.7;
1447
+ max-width: 220px;
1448
+ }
1449
+
1450
+ .sk-empty__cta {
1451
+ display: inline-flex;
1452
+ align-items: center;
1453
+ gap: 6px;
1454
+ margin-top: 4px;
1455
+ padding: 8px 16px;
1456
+ border-radius: var(--sk-radius-m);
1457
+ border: none;
1458
+ background: var(--sk-bg-accent);
1459
+ color: var(--sk-fg-on-accent);
1460
+ font-size: 12px;
1461
+ font-weight: 600;
1462
+ font-family: var(--sk-font);
1463
+ cursor: pointer;
1464
+ transition: all var(--sk-transition);
1465
+ }
1466
+
1467
+ .sk-empty__cta svg {
1468
+ width: 14px;
1469
+ height: 14px;
1470
+ }
1471
+
1472
+ .sk-empty__cta:hover {
1473
+ opacity: 0.9;
1474
+ box-shadow: var(--sk-shadow-sm);
1475
+ }
1476
+
1477
+ /* \u2500\u2500\u2500 Undo Toast (overlaid) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
1478
+ .sk-toast {
1479
+ position: absolute;
1480
+ bottom: 56px;
1481
+ left: 14px;
1482
+ right: 14px;
1483
+ display: flex;
1484
+ align-items: center;
1485
+ gap: 8px;
1486
+ padding: 8px 12px;
1487
+ background: var(--sk-fg);
1488
+ color: var(--sk-bg);
1489
+ border-radius: var(--sk-radius-m);
1490
+ box-shadow: var(--sk-shadow-md);
1491
+ font-size: 11px;
1492
+ animation: sk-slideUp 200ms ease;
1493
+ z-index: 100;
1494
+ }
1495
+
1496
+ .sk-toast__text { flex: 1; }
1497
+
1498
+ .sk-toast__action {
1499
+ font-weight: 600;
1500
+ cursor: pointer;
1501
+ text-decoration: underline;
1502
+ white-space: nowrap;
1503
+ }
1504
+
1505
+ @keyframes sk-slideUp {
1506
+ from { opacity: 0; transform: translateY(8px); }
1507
+ to { opacity: 1; transform: translateY(0); }
1508
+ }
1509
+
1510
+ /* \u2500\u2500\u2500 Scrollbar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
1511
+ .sk-body::-webkit-scrollbar { width: 4px; }
1512
+ .sk-body::-webkit-scrollbar-track { background: transparent; }
1513
+ .sk-body::-webkit-scrollbar-thumb {
1514
+ background: var(--sk-border);
1515
+ border-radius: 2px;
1516
+ }
1517
+
1518
+ /* \u2500\u2500\u2500 Detected Tokens (element-centric view) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
1519
+ .sk-detect {
1520
+ padding: 8px 14px;
1521
+ display: flex;
1522
+ flex-direction: column;
1523
+ gap: 0;
1524
+ }
1525
+
1526
+ .sk-detect__empty {
1527
+ display: flex;
1528
+ flex-direction: column;
1529
+ align-items: center;
1530
+ gap: 10px;
1531
+ padding: 20px 10px;
1532
+ text-align: center;
1533
+ }
1534
+
1535
+ .sk-detect__empty-text {
1536
+ font-size: 11px;
1537
+ color: var(--sk-fg-tertiary);
1538
+ }
1539
+
1540
+ /* \u2500\u2500\u2500 Row (shared between token and static rows) \u2500\u2500\u2500\u2500\u2500 */
1541
+ .sk-row {
1542
+ position: relative;
1543
+ display: flex;
1544
+ align-items: center;
1545
+ gap: 6px;
1546
+ padding: 5px 0;
1547
+ border-bottom: 1px solid var(--sk-border-subtle);
1548
+ }
1549
+
1550
+ .sk-row:last-child { border-bottom: none; }
1551
+
1552
+ .sk-row--changed {
1553
+ background: var(--sk-bg-accent-subtle);
1554
+ border-radius: var(--sk-radius-s);
1555
+ margin: 0 -6px;
1556
+ padding: 5px 6px;
1557
+ border-left: 2px solid var(--sk-bg-accent);
1558
+ }
1559
+
1560
+ .sk-row__label {
1561
+ width: 80px;
1562
+ font-size: 11px;
1563
+ font-weight: 500;
1564
+ color: var(--sk-fg-secondary);
1565
+ overflow: hidden;
1566
+ text-overflow: ellipsis;
1567
+ white-space: nowrap;
1568
+ flex-shrink: 0;
1569
+ }
1570
+
1571
+ .sk-row__trigger {
1572
+ display: flex;
1573
+ align-items: center;
1574
+ gap: 5px;
1575
+ flex: 1;
1576
+ min-width: 0;
1577
+ padding: 4px 6px;
1578
+ border: 1px solid var(--sk-border);
1579
+ border-radius: var(--sk-radius-s);
1580
+ background: var(--sk-bg);
1581
+ cursor: pointer;
1582
+ font-family: var(--sk-font);
1583
+ font-size: 11px;
1584
+ color: var(--sk-fg);
1585
+ transition: border-color var(--sk-transition), box-shadow var(--sk-transition);
1586
+ text-align: left;
1587
+ }
1588
+
1589
+ .sk-row__trigger:hover {
1590
+ border-color: var(--sk-fg-secondary);
1591
+ }
1592
+
1593
+ .sk-row__trigger--open {
1594
+ border-color: var(--sk-bg-accent);
1595
+ box-shadow: 0 0 0 1px var(--sk-bg-accent);
1596
+ }
1597
+
1598
+ .sk-row__trigger--swapped {
1599
+ border-color: var(--sk-bg-accent);
1600
+ background: var(--sk-bg-accent-subtle);
1601
+ }
1602
+
1603
+ .sk-row__trigger--static {
1604
+ border-style: dashed;
1605
+ }
1606
+
1607
+ .sk-row__trigger--modified {
1608
+ border-style: solid;
1609
+ border-color: var(--sk-bg-accent);
1610
+ background: var(--sk-bg-accent-subtle);
1611
+ }
1612
+
1613
+ .sk-row__trigger--readonly {
1614
+ cursor: default;
1615
+ opacity: 0.8;
1616
+ border-style: none;
1617
+ background: transparent;
1618
+ padding-left: 0;
1619
+ }
1620
+
1621
+ .sk-row__trigger--readonly:hover {
1622
+ border-color: var(--sk-border);
1623
+ }
1624
+
1625
+ .sk-row__swatch {
1626
+ width: 14px;
1627
+ height: 14px;
1628
+ border-radius: 3px;
1629
+ border: 1px solid var(--sk-border);
1630
+ flex-shrink: 0;
1631
+ }
1632
+
1633
+ .sk-row__name {
1634
+ font-weight: 500;
1635
+ overflow: hidden;
1636
+ text-overflow: ellipsis;
1637
+ white-space: nowrap;
1638
+ }
1639
+
1640
+ .sk-row__val {
1641
+ font-size: 10px;
1642
+ font-family: var(--sk-mono);
1643
+ color: var(--sk-fg-tertiary);
1644
+ margin-left: auto;
1645
+ white-space: nowrap;
1646
+ flex-shrink: 0;
1647
+ }
1648
+
1649
+ .sk-row__val--full {
1650
+ font-weight: 400;
1651
+ flex: 1;
1652
+ margin-left: 0;
1653
+ overflow: hidden;
1654
+ text-overflow: ellipsis;
1655
+ }
1656
+
1657
+ .sk-row__chev {
1658
+ width: 12px;
1659
+ height: 12px;
1660
+ flex-shrink: 0;
1661
+ color: var(--sk-fg-tertiary);
1662
+ transition: transform var(--sk-transition);
1663
+ display: flex;
1664
+ align-items: center;
1665
+ }
1666
+
1667
+ .sk-row__trigger--open .sk-row__chev {
1668
+ transform: rotate(180deg);
1669
+ }
1670
+
1671
+ .sk-row__unlink {
1672
+ flex-shrink: 0;
1673
+ width: 22px;
1674
+ height: 22px;
1675
+ display: flex;
1676
+ align-items: center;
1677
+ justify-content: center;
1678
+ border: 1px solid var(--sk-border);
1679
+ border-radius: var(--sk-radius-s);
1680
+ background: var(--sk-bg);
1681
+ color: var(--sk-fg-tertiary);
1682
+ cursor: pointer;
1683
+ padding: 0;
1684
+ transition: all var(--sk-transition);
1685
+ }
1686
+
1687
+ .sk-row__unlink:hover {
1688
+ border-color: var(--sk-bg-accent);
1689
+ color: var(--sk-bg-accent);
1690
+ background: var(--sk-bg-accent-subtle);
1691
+ }
1692
+
1693
+ .sk-row__unlink--loading {
1694
+ opacity: 0.5;
1695
+ cursor: wait;
1696
+ animation: sk-pulse 1s ease infinite;
1697
+ }
1698
+
1699
+ /* \u2500\u2500\u2500 Dropdown \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
1700
+ .sk-dropdown {
1701
+ position: absolute;
1702
+ top: 100%;
1703
+ left: 80px;
1704
+ right: 0;
1705
+ z-index: 50;
1706
+ margin-top: 2px;
1707
+ padding: 4px 0;
1708
+ background: var(--sk-bg);
1709
+ border: 1px solid var(--sk-border);
1710
+ border-radius: var(--sk-radius-m);
1711
+ box-shadow: var(--sk-shadow-md);
1712
+ max-height: 200px;
1713
+ overflow-y: auto;
1714
+ overflow-x: hidden;
1715
+ scrollbar-width: thin;
1716
+ scrollbar-color: var(--sk-border) transparent;
1717
+ animation: sk-fadeIn 100ms ease;
1718
+ }
1719
+
1720
+ .sk-dropdown::-webkit-scrollbar { width: 4px; }
1721
+ .sk-dropdown::-webkit-scrollbar-track { background: transparent; }
1722
+ .sk-dropdown::-webkit-scrollbar-thumb {
1723
+ background: var(--sk-border);
1724
+ border-radius: 2px;
1725
+ }
1726
+
1727
+ @keyframes sk-fadeIn {
1728
+ from { opacity: 0; transform: translateY(-4px); }
1729
+ to { opacity: 1; transform: translateY(0); }
1730
+ }
1731
+
1732
+ .sk-dropdown__opt {
1733
+ display: flex;
1734
+ align-items: center;
1735
+ gap: 6px;
1736
+ width: 100%;
1737
+ padding: 5px 10px;
1738
+ border: none;
1739
+ background: transparent;
1740
+ cursor: pointer;
1741
+ font-family: var(--sk-font);
1742
+ font-size: 11px;
1743
+ color: var(--sk-fg);
1744
+ text-align: left;
1745
+ transition: background var(--sk-transition);
1746
+ }
1747
+
1748
+ .sk-dropdown__opt:hover {
1749
+ background: var(--sk-bg-hover);
1750
+ }
1751
+
1752
+ .sk-dropdown__opt--active {
1753
+ background: var(--sk-bg-accent-subtle);
1754
+ font-weight: 600;
1755
+ }
1756
+
1757
+ .sk-dropdown__opt--active:hover {
1758
+ background: var(--sk-bg-accent-subtle);
1759
+ }
1760
+
1761
+ .sk-dropdown__opt--original {
1762
+ border-left: 2px solid var(--sk-fg-tertiary);
1763
+ }
1764
+
1765
+ .sk-dropdown__opt--nearest {
1766
+ background: var(--sk-bg-accent-subtle);
1767
+ }
1768
+
1769
+ .sk-dropdown__swatch {
1770
+ width: 16px;
1771
+ height: 16px;
1772
+ border-radius: 3px;
1773
+ border: 1px solid var(--sk-border);
1774
+ flex-shrink: 0;
1775
+ }
1776
+
1777
+ .sk-dropdown__name {
1778
+ flex: 1;
1779
+ overflow: hidden;
1780
+ text-overflow: ellipsis;
1781
+ white-space: nowrap;
1782
+ }
1783
+
1784
+ .sk-dropdown__val {
1785
+ font-size: 10px;
1786
+ font-family: var(--sk-mono);
1787
+ color: var(--sk-fg-tertiary);
1788
+ white-space: nowrap;
1789
+ flex-shrink: 0;
1790
+ }
1791
+
1792
+ .sk-dropdown__badge {
1793
+ font-size: 9px;
1794
+ padding: 1px 5px;
1795
+ border-radius: 8px;
1796
+ background: var(--sk-bg-accent);
1797
+ color: var(--sk-fg-on-accent);
1798
+ white-space: nowrap;
1799
+ flex-shrink: 0;
1800
+ }
1801
+
1802
+ .sk-dropdown__input-row {
1803
+ display: flex;
1804
+ align-items: center;
1805
+ gap: 4px;
1806
+ padding: 6px 8px;
1807
+ border-bottom: 1px solid var(--sk-border-subtle);
1808
+ }
1809
+
1810
+ .sk-dropdown__input {
1811
+ flex: 1;
1812
+ min-width: 0;
1813
+ padding: 4px 6px;
1814
+ border: 1px solid var(--sk-border);
1815
+ border-radius: var(--sk-radius-s);
1816
+ background: var(--sk-bg);
1817
+ font-family: var(--sk-mono);
1818
+ font-size: 11px;
1819
+ color: var(--sk-fg);
1820
+ outline: none;
1821
+ transition: border-color var(--sk-transition);
1822
+ }
1823
+
1824
+ .sk-dropdown__input:focus {
1825
+ border-color: var(--sk-bg-accent);
1826
+ box-shadow: 0 0 0 1px var(--sk-bg-accent);
1827
+ }
1828
+
1829
+ .sk-dropdown__apply {
1830
+ padding: 4px 10px;
1831
+ border: none;
1832
+ border-radius: var(--sk-radius-s);
1833
+ background: var(--sk-bg-accent);
1834
+ color: var(--sk-fg-on-accent);
1835
+ font-size: 11px;
1836
+ font-weight: 500;
1837
+ cursor: pointer;
1838
+ white-space: nowrap;
1839
+ transition: opacity var(--sk-transition);
1840
+ }
1841
+
1842
+ .sk-dropdown__apply:hover {
1843
+ opacity: 0.85;
1844
+ }
1845
+
1846
+ .sk-dropdown__color-pick {
1847
+ width: 22px;
1848
+ height: 22px;
1849
+ padding: 0;
1850
+ border: 1px solid var(--sk-border);
1851
+ border-radius: var(--sk-radius-s);
1852
+ cursor: pointer;
1853
+ flex-shrink: 0;
1854
+ background: none;
1855
+ }
1856
+
1857
+ .sk-dropdown__sep {
1858
+ padding: 4px 8px;
1859
+ font-size: 10px;
1860
+ color: var(--sk-fg-tertiary);
1861
+ text-transform: uppercase;
1862
+ letter-spacing: 0.5px;
1863
+ border-bottom: 1px solid var(--sk-border-subtle);
1864
+ }
1865
+
1866
+ /* \u2500\u2500\u2500 Property Groups \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
1867
+ .sk-group {
1868
+ padding: 0 14px;
1869
+ }
1870
+
1871
+ .sk-group + .sk-group {
1872
+ border-top: 1px solid var(--sk-border-subtle);
1873
+ }
1874
+
1875
+ .sk-group__label {
1876
+ font-size: 10px;
1877
+ font-weight: 600;
1878
+ text-transform: uppercase;
1879
+ letter-spacing: 0.4px;
1880
+ color: var(--sk-fg-tertiary);
1881
+ padding: 8px 0 2px;
1882
+ }
1883
+
1884
+ /* \u2500\u2500\u2500 Browse All Toggle \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
1885
+ .sk-browse-toggle {
1886
+ display: block;
1887
+ width: 100%;
1888
+ padding: 10px 14px;
1889
+ font-size: 11px;
1890
+ color: var(--sk-bg-accent);
1891
+ background: transparent;
1892
+ border: none;
1893
+ border-top: 1px solid var(--sk-border-subtle);
1894
+ cursor: pointer;
1895
+ text-align: left;
1896
+ font-family: var(--sk-font);
1897
+ transition: background var(--sk-transition);
1898
+ }
1899
+
1900
+ .sk-browse-toggle:hover {
1901
+ background: var(--sk-bg-accent-subtle);
1902
+ }
1903
+ `
1904
+ );
1905
+
1906
+ // src/panel.ts
1907
+ var ICONS = {
1908
+ chevron: '<svg viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M4 2l4 4-4 4"/></svg>',
1909
+ pick: '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.3"><circle cx="8" cy="8" r="5"/><path d="M8 3v10M3 8h10"/></svg>',
1910
+ sparkle: '<svg viewBox="0 0 16 16" fill="currentColor"><path d="M8 0l1.5 5.5L15 6l-5.5 1.5L8 13l-1.5-5.5L1 6l5.5-1.5z"/></svg>',
1911
+ send: '<svg viewBox="0 0 16 16" fill="currentColor"><path d="M14.5 8L1 1.5l3.25 6.5L1 14.5z"/></svg>',
1912
+ copy: '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.3"><rect x="5" y="5" width="8" height="8" rx="1.5"/><path d="M3 11V3.5A1.5 1.5 0 014.5 2H11"/></svg>',
1913
+ github: '<svg viewBox="0 0 16 16" fill="currentColor"><path d="M8 .2A8 8 0 005.47 15.8c.4.07.55-.17.55-.39V14c-2.24.49-2.71-1.07-2.71-1.07a2.13 2.13 0 00-.9-1.18c-.73-.5.06-.49.06-.49.8.06 1.23.83 1.23.83.72 1.23 1.88.87 2.34.67a1.71 1.71 0 01.51-1.07c-1.78-.2-3.64-.89-3.64-3.95a3.1 3.1 0 01.82-2.15 2.88 2.88 0 01.08-2.12s.67-.22 2.2.82a7.58 7.58 0 014 0c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12a3.1 3.1 0 01.82 2.15c0 3.07-1.87 3.75-3.65 3.95a1.92 1.92 0 01.55 1.49v2.21c0 .26.14.47.55.39A8 8 0 008 .2z"/></svg>',
1914
+ undo: '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.3"><path d="M3 7l3-3M3 7l3 3M3.5 7H10a3 3 0 010 6H8"/></svg>',
1915
+ save: '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.3"><path d="M12.5 14.5h-9a1 1 0 01-1-1v-11a1 1 0 011-1h7l3 3v9a1 1 0 01-1 1z"/><path d="M5.5 1.5v3h4v-3M5.5 9.5h5"/></svg>',
1916
+ x: '<svg viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M3 3l6 6M9 3l-6 6"/></svg>',
1917
+ plus: '<svg viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M6 2v8M2 6h8"/></svg>',
1918
+ code: '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.3"><path d="M5 4L1.5 8 5 12M11 4l3.5 4L11 12"/></svg>',
1919
+ unlink: '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M7 9l-1.5 1.5a2.12 2.12 0 01-3-3L5 5"/><path d="M9 7l1.5-1.5a2.12 2.12 0 013 3L11 11"/></svg>',
1920
+ chevronDown: '<svg viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M3 4.5l3 3 3-3"/></svg>'
1921
+ };
1922
+ function icon(name, cls = "") {
1923
+ return `<span class="${cls}" style="display:inline-flex;width:1em;height:1em">${ICONS[name]}</span>`;
1924
+ }
1925
+ function el(tag, cls) {
1926
+ const e = document.createElement(tag);
1927
+ if (cls) e.className = cls;
1928
+ return e;
1929
+ }
1930
+ function html(parent, content) {
1931
+ parent.innerHTML = content;
1932
+ }
1933
+ var registry = [];
1934
+ var currentScope = { type: "global" };
1935
+ var pickedElement = null;
1936
+ var detectedTokensCache = [];
1937
+ var staticValuesCache = [];
1938
+ var promptMessages = [];
1939
+ var collapsedCategories = /* @__PURE__ */ new Set();
1940
+ var showAllTokens = false;
1941
+ var openDropdownId = null;
1942
+ var promotedStatics = /* @__PURE__ */ new Map();
1943
+ var panelRoot = null;
1944
+ var ctx = null;
1945
+ var CATEGORY_ORDER = [
1946
+ "spacingH",
1947
+ "spacingV",
1948
+ "fontSize",
1949
+ "fontWeight",
1950
+ "borderRadius",
1951
+ "shadow",
1952
+ "strokeWidth",
1953
+ "colorNeutralBg",
1954
+ "colorNeutralFg",
1955
+ "colorBrandBg",
1956
+ "colorBrandFg",
1957
+ "colorNeutralStroke",
1958
+ "colorBrandStroke",
1959
+ "lineHeight",
1960
+ "duration",
1961
+ "curve"
1962
+ ];
1963
+ function truncatePath(path, maxSegments = 2) {
1964
+ const sep = " > ";
1965
+ const parts = path.split(sep);
1966
+ if (parts.length <= maxSegments) return path;
1967
+ return "\u2026 > " + parts.slice(-maxSegments).join(sep);
1968
+ }
1969
+ function showInlineInput(anchor, placeholder, onSubmit) {
1970
+ anchor.closest(".sk-variations, .sk-summary")?.querySelector(".sk-inline-input")?.remove();
1971
+ const wrapper = el("div", "sk-inline-input");
1972
+ const input = document.createElement("input");
1973
+ input.type = "text";
1974
+ input.className = "sk-inline-input__field";
1975
+ input.placeholder = placeholder;
1976
+ input.setAttribute("autocomplete", "off");
1977
+ const confirmBtn = el("button", "sk-inline-input__ok");
1978
+ confirmBtn.textContent = "\u2713";
1979
+ confirmBtn.title = "Confirm";
1980
+ const cancelBtn = el("button", "sk-inline-input__cancel");
1981
+ cancelBtn.textContent = "\u2715";
1982
+ cancelBtn.title = "Cancel";
1983
+ function submit() {
1984
+ const val = input.value.trim();
1985
+ wrapper.remove();
1986
+ if (val) onSubmit(val);
1987
+ }
1988
+ function cancel() {
1989
+ wrapper.remove();
1990
+ }
1991
+ input.addEventListener("keydown", (e) => {
1992
+ if (e.key === "Enter") {
1993
+ e.preventDefault();
1994
+ submit();
1995
+ }
1996
+ if (e.key === "Escape") {
1997
+ e.preventDefault();
1998
+ cancel();
1999
+ }
2000
+ });
2001
+ confirmBtn.addEventListener("click", submit);
2002
+ cancelBtn.addEventListener("click", cancel);
2003
+ wrapper.appendChild(input);
2004
+ wrapper.appendChild(confirmBtn);
2005
+ wrapper.appendChild(cancelBtn);
2006
+ anchor.parentElement?.insertBefore(wrapper, anchor.nextSibling);
2007
+ input.focus();
2008
+ }
2009
+ async function triggerPick(activateEl) {
2010
+ if (!ctx) return;
2011
+ activateEl?.classList.add("sk-scope__pick--active");
2012
+ try {
2013
+ const result = await ctx.services.elementPicker.pick({
2014
+ source: "style-kit",
2015
+ prompt: "Click an element to see its design tokens"
2016
+ });
2017
+ if (result) {
2018
+ pickedElement = { element: result.element, selector: result.selector, displayPath: result.displayPath };
2019
+ currentScope = { type: "selector", selector: result.selector, label: result.displayPath };
2020
+ const detection = detectAll(result.element, registry);
2021
+ detectedTokensCache = detection.tokens;
2022
+ staticValuesCache = detection.statics;
2023
+ showAllTokens = false;
2024
+ refresh();
2025
+ }
2026
+ } finally {
2027
+ activateEl?.classList.remove("sk-scope__pick--active");
2028
+ }
2029
+ }
2030
+ function renderScopeBar() {
2031
+ const bar = el("div", "sk-scope");
2032
+ const label = el("span", "sk-scope__label");
2033
+ label.textContent = "SCOPE";
2034
+ bar.appendChild(label);
2035
+ const valueBtn = el("button", "sk-scope__value");
2036
+ const valueTxt = el("span", "sk-scope__value-text");
2037
+ valueTxt.textContent = currentScope.type === "global" ? "Global (all elements)" : truncatePath(currentScope.label);
2038
+ valueTxt.title = currentScope.type === "global" ? "" : currentScope.label;
2039
+ valueBtn.appendChild(valueTxt);
2040
+ valueBtn.addEventListener("click", () => {
2041
+ if (pickedElement) {
2042
+ if (currentScope.type === "global") {
2043
+ currentScope = { type: "selector", selector: pickedElement.selector, label: pickedElement.displayPath };
2044
+ } else {
2045
+ currentScope = { type: "global" };
2046
+ }
2047
+ refresh();
2048
+ }
2049
+ });
2050
+ bar.appendChild(valueBtn);
2051
+ const pickBtn = el("button", "sk-scope__pick");
2052
+ html(pickBtn, `${icon("pick")} Pick Element`);
2053
+ pickBtn.title = "Pick an element to inspect its design tokens";
2054
+ pickBtn.addEventListener("click", () => triggerPick(pickBtn));
2055
+ bar.appendChild(pickBtn);
2056
+ if (pickedElement) {
2057
+ const clearBtn = el("button", "sk-scope__pick sk-scope__pick--icon-only");
2058
+ html(clearBtn, icon("x"));
2059
+ clearBtn.title = "Clear selection";
2060
+ clearBtn.addEventListener("click", () => {
2061
+ pickedElement = null;
2062
+ currentScope = { type: "global" };
2063
+ detectedTokensCache = [];
2064
+ staticValuesCache = [];
2065
+ promotedStatics.clear();
2066
+ showAllTokens = false;
2067
+ refresh();
2068
+ });
2069
+ bar.appendChild(clearBtn);
2070
+ }
2071
+ return bar;
2072
+ }
2073
+ function renderVariationsBar() {
2074
+ const bar = el("div", "sk-variations");
2075
+ const store2 = getStore();
2076
+ const baseChip = el("button", `sk-var-chip sk-var-chip--base ${store2.active === null ? "sk-var-chip--active" : ""}`);
2077
+ baseChip.textContent = "Base";
2078
+ baseChip.addEventListener("click", () => {
2079
+ loadBase();
2080
+ refresh();
2081
+ });
2082
+ bar.appendChild(baseChip);
2083
+ for (const v of store2.variations) {
2084
+ const chip = el("button", `sk-var-chip ${store2.active === v.id ? "sk-var-chip--active" : ""}`);
2085
+ const nameSpan = el("span");
2086
+ nameSpan.textContent = v.name;
2087
+ chip.appendChild(nameSpan);
2088
+ if (store2.active === v.id) {
2089
+ const xBtn = el("span", "sk-var-chip__x");
2090
+ html(xBtn, icon("x"));
2091
+ xBtn.addEventListener("click", (e) => {
2092
+ e.stopPropagation();
2093
+ deleteVariation(v.id);
2094
+ refresh();
2095
+ });
2096
+ chip.appendChild(xBtn);
2097
+ }
2098
+ chip.addEventListener("click", () => {
2099
+ loadVariation(v.id);
2100
+ refresh();
2101
+ });
2102
+ bar.appendChild(chip);
2103
+ }
2104
+ const addBtn = el("button", "sk-var-add");
2105
+ html(addBtn, icon("plus"));
2106
+ addBtn.title = "Save current overrides as a variation";
2107
+ addBtn.addEventListener("click", () => {
2108
+ showInlineInput(addBtn, "Variation name\u2026", (name) => {
2109
+ saveVariation(name);
2110
+ refresh();
2111
+ });
2112
+ });
2113
+ bar.appendChild(addBtn);
2114
+ return bar;
2115
+ }
2116
+ function renderOverrideSummary() {
2117
+ const overrides = getOverrides();
2118
+ const staticOverrides = getStaticOverridesList();
2119
+ const total = overrides.length + staticOverrides.length;
2120
+ if (total === 0) return null;
2121
+ const bar = el("div", "sk-summary");
2122
+ const text = el("span", "sk-summary__text");
2123
+ text.textContent = `${total} override${total > 1 ? "s" : ""} active`;
2124
+ bar.appendChild(text);
2125
+ const actions = el("div", "sk-summary__actions");
2126
+ const undoBtn = el("button", "sk-btn sk-btn--sm sk-btn--ghost");
2127
+ html(undoBtn, `${icon("undo", "sk-btn__icon")} Reset all`);
2128
+ undoBtn.addEventListener("click", () => {
2129
+ clearAll();
2130
+ refresh();
2131
+ });
2132
+ actions.appendChild(undoBtn);
2133
+ const saveBtn = el("button", "sk-btn sk-btn--sm sk-btn--ghost");
2134
+ html(saveBtn, `${icon("save", "sk-btn__icon")} Save`);
2135
+ saveBtn.addEventListener("click", () => {
2136
+ const store2 = getStore();
2137
+ if (store2.active) {
2138
+ updateActiveVariation();
2139
+ ctx?.services.notify.toast("Variation updated");
2140
+ } else {
2141
+ showInlineInput(saveBtn, "Variation name\u2026", (name) => {
2142
+ saveVariation(name);
2143
+ refresh();
2144
+ });
2145
+ }
2146
+ });
2147
+ actions.appendChild(saveBtn);
2148
+ bar.appendChild(actions);
2149
+ return bar;
2150
+ }
2151
+ function isColorCategory(cat) {
2152
+ return cat.startsWith("color");
2153
+ }
2154
+ function renderTokenStepper(scale, entry) {
2155
+ const override = getOverrideForToken(entry.name, currentScope);
2156
+ const currentValue = override?.newValue ?? entry.value;
2157
+ const isModified = !!override;
2158
+ const activeIdx = scale.entries.findIndex(
2159
+ (e) => override ? e.value === override.newValue : e.name === entry.name
2160
+ );
2161
+ const row = el("div", "sk-stepper");
2162
+ const label = el("span", "sk-stepper__label");
2163
+ label.textContent = entry.label;
2164
+ label.title = entry.name;
2165
+ row.appendChild(label);
2166
+ const track = el("div", "sk-stepper__track");
2167
+ scale.entries.forEach((step, i) => {
2168
+ const seg = el("div", "sk-stepper__step");
2169
+ if (i <= activeIdx) {
2170
+ seg.classList.add(i === activeIdx ? "sk-stepper__step--active" : "sk-stepper__step--modified");
2171
+ }
2172
+ seg.setAttribute("data-tooltip", `${step.label}: ${step.value}`);
2173
+ seg.addEventListener("click", () => {
2174
+ if (step.name === entry.name && !override) return;
2175
+ applyOverride({
2176
+ tokenName: entry.name,
2177
+ cssVar: entry.cssVar,
2178
+ originalValue: entry.value,
2179
+ newValue: step.value,
2180
+ scope: currentScope
2181
+ });
2182
+ refresh();
2183
+ });
2184
+ track.appendChild(seg);
2185
+ });
2186
+ row.appendChild(track);
2187
+ const val = el("span", `sk-stepper__value ${isModified ? "sk-stepper__value--modified" : ""}`);
2188
+ val.textContent = currentValue;
2189
+ row.appendChild(val);
2190
+ return row;
2191
+ }
2192
+ function renderColorSwatch(scale, entry) {
2193
+ const override = getOverrideForToken(entry.name, currentScope);
2194
+ const currentValue = override?.newValue ?? entry.value;
2195
+ const isModified = !!override;
2196
+ const row = el("div", "sk-swatch-row");
2197
+ const label = el("span", "sk-swatch-row__label");
2198
+ label.textContent = entry.label;
2199
+ label.title = entry.name;
2200
+ row.appendChild(label);
2201
+ const swatch = el("div", `sk-swatch ${isModified ? "sk-swatch--active" : ""}`);
2202
+ swatch.style.backgroundColor = currentValue;
2203
+ row.appendChild(swatch);
2204
+ const colorInput = el("input", "sk-color-input");
2205
+ colorInput.type = "color";
2206
+ colorInput.value = currentValue.startsWith("#") ? currentValue : rgbToHex(currentValue);
2207
+ colorInput.addEventListener("input", () => {
2208
+ applyOverride({
2209
+ tokenName: entry.name,
2210
+ cssVar: entry.cssVar,
2211
+ originalValue: entry.value,
2212
+ newValue: colorInput.value,
2213
+ scope: currentScope
2214
+ });
2215
+ refresh();
2216
+ });
2217
+ row.appendChild(colorInput);
2218
+ swatch.addEventListener("click", () => colorInput.click());
2219
+ const hex = el("span", `sk-swatch__hex ${isModified ? "sk-swatch__hex--modified" : ""}`);
2220
+ hex.textContent = currentValue;
2221
+ row.appendChild(hex);
2222
+ if (isModified) {
2223
+ const undo = el("button", "sk-btn sk-btn--sm sk-btn--ghost");
2224
+ html(undo, icon("undo", "sk-btn__icon"));
2225
+ undo.title = "Reset to original";
2226
+ undo.addEventListener("click", () => {
2227
+ removeOverride(entry.name, currentScope);
2228
+ refresh();
2229
+ });
2230
+ row.appendChild(undo);
2231
+ }
2232
+ return row;
2233
+ }
2234
+ function renderCategory(scale) {
2235
+ const cat = el("div", "sk-cat");
2236
+ const isOpen = !collapsedCategories.has(scale.category);
2237
+ if (isOpen) cat.classList.add("sk-cat--open");
2238
+ const overridesInCat = scale.entries.filter(
2239
+ (e) => getOverrideForToken(e.name, currentScope)
2240
+ ).length;
2241
+ const header = el("div", "sk-cat__header");
2242
+ const chevron = el("span", "sk-cat__chevron");
2243
+ html(chevron, ICONS.chevron);
2244
+ header.appendChild(chevron);
2245
+ const label = el("span", "sk-cat__label");
2246
+ label.textContent = scale.label;
2247
+ header.appendChild(label);
2248
+ if (overridesInCat > 0) {
2249
+ const count = el("span", "sk-cat__count");
2250
+ count.textContent = String(overridesInCat);
2251
+ header.appendChild(count);
2252
+ }
2253
+ header.addEventListener("click", () => {
2254
+ if (collapsedCategories.has(scale.category)) {
2255
+ collapsedCategories.delete(scale.category);
2256
+ } else {
2257
+ collapsedCategories.add(scale.category);
2258
+ }
2259
+ refresh();
2260
+ });
2261
+ cat.appendChild(header);
2262
+ const body = el("div", "sk-cat__body");
2263
+ const isColor = isColorCategory(scale.category);
2264
+ for (const entry of scale.entries) {
2265
+ if (isColor) {
2266
+ body.appendChild(renderColorSwatch(scale, entry));
2267
+ } else {
2268
+ body.appendChild(renderTokenStepper(scale, entry));
2269
+ }
2270
+ }
2271
+ cat.appendChild(body);
2272
+ return cat;
2273
+ }
2274
+ function renderPromptBar() {
2275
+ const bar = el("div", "sk-prompt");
2276
+ const wrap = el("div", "sk-prompt__wrap");
2277
+ const sparkleIcon = el("span", "sk-prompt__icon");
2278
+ html(sparkleIcon, ICONS.sparkle);
2279
+ wrap.appendChild(sparkleIcon);
2280
+ const input = el("input", "sk-prompt__input");
2281
+ input.type = "text";
2282
+ input.placeholder = getScopePlaceholder();
2283
+ wrap.appendChild(input);
2284
+ const sendBtn = el("button", "sk-prompt__send");
2285
+ html(sendBtn, ICONS.send);
2286
+ wrap.appendChild(sendBtn);
2287
+ input.addEventListener("input", () => {
2288
+ sendBtn.classList.toggle("sk-prompt__send--ready", input.value.trim().length > 0);
2289
+ });
2290
+ const submit = async () => {
2291
+ const text = input.value.trim();
2292
+ if (!text || !ctx?.services.copilot.available) return;
2293
+ sendBtn.classList.add("sk-prompt__send--loading");
2294
+ input.disabled = true;
2295
+ try {
2296
+ const systemPrompt = buildSystemPrompt();
2297
+ promptMessages.push({ role: "user", content: text });
2298
+ const response = await ctx.services.copilot.ask([
2299
+ { role: "system", content: systemPrompt },
2300
+ ...promptMessages
2301
+ ]);
2302
+ promptMessages.push({ role: "assistant", content: response });
2303
+ const parsed = parseOverrideResponse(response);
2304
+ if (parsed.length > 0) {
2305
+ for (const o of parsed) {
2306
+ applyOverride({
2307
+ tokenName: o.token,
2308
+ cssVar: `--${o.token}`,
2309
+ originalValue: getResolvedValue(`--${o.token}`),
2310
+ newValue: o.value,
2311
+ scope: currentScope
2312
+ });
2313
+ }
2314
+ input.value = "";
2315
+ sendBtn.classList.remove("sk-prompt__send--ready");
2316
+ ctx.services.notify.toast(`Applied ${parsed.length} token override${parsed.length > 1 ? "s" : ""}`);
2317
+ refresh();
2318
+ } else {
2319
+ ctx.services.notify.toast("No token changes suggested \u2014 try being more specific");
2320
+ }
2321
+ } catch (err) {
2322
+ ctx.services.notify.toast("Copilot request failed");
2323
+ } finally {
2324
+ sendBtn.classList.remove("sk-prompt__send--loading");
2325
+ input.disabled = false;
2326
+ input.focus();
2327
+ }
2328
+ };
2329
+ input.addEventListener("keydown", (e) => {
2330
+ if (e.key === "Enter") submit();
2331
+ });
2332
+ sendBtn.addEventListener("click", submit);
2333
+ bar.appendChild(wrap);
2334
+ return bar;
2335
+ }
2336
+ function renderFooterActions() {
2337
+ const footer = el("div", "sk-footer");
2338
+ const actions = el("div", "sk-actions");
2339
+ const copyBtn = el("button", "sk-btn");
2340
+ html(copyBtn, `${icon("copy", "sk-btn__icon")} Copy CSS`);
2341
+ copyBtn.addEventListener("click", async () => {
2342
+ const { css } = serializeOverrides();
2343
+ await ctx?.services.clipboard.copy(css);
2344
+ ctx?.services.notify.toast("CSS copied to clipboard");
2345
+ });
2346
+ actions.appendChild(copyBtn);
2347
+ const themeBtn = el("button", "sk-btn");
2348
+ html(themeBtn, `${icon("code", "sk-btn__icon")} Theme`);
2349
+ themeBtn.addEventListener("click", async () => {
2350
+ const { themePartial } = serializeOverrides();
2351
+ await ctx?.services.clipboard.copy(themePartial);
2352
+ ctx?.services.notify.toast("Theme partial copied");
2353
+ });
2354
+ actions.appendChild(themeBtn);
2355
+ if (ctx?.services.github.available) {
2356
+ const issueBtn = el("button", "sk-btn");
2357
+ html(issueBtn, `${icon("github", "sk-btn__icon")} Create Issue`);
2358
+ issueBtn.addEventListener("click", async () => {
2359
+ await applyToCode();
2360
+ });
2361
+ actions.appendChild(issueBtn);
2362
+ }
2363
+ const fixPromptBtn = el("button", "sk-btn sk-btn--primary");
2364
+ html(fixPromptBtn, `${icon("copy", "sk-btn__icon")} Copy Fix Prompt`);
2365
+ fixPromptBtn.addEventListener("click", async () => {
2366
+ await copyFixPrompt();
2367
+ });
2368
+ actions.appendChild(fixPromptBtn);
2369
+ footer.appendChild(actions);
2370
+ return footer;
2371
+ }
2372
+ function renderEmptyState() {
2373
+ const empty = el("div", "sk-empty");
2374
+ html(empty, `
2375
+ <div class="sk-empty__icon">${ICONS.pick}</div>
2376
+ <div class="sk-empty__text">Pick an element to explore its design tokens</div>
2377
+ <div class="sk-empty__hint">Select any element on the page to see which tokens it uses, swap values, and ship overrides.</div>
2378
+ `);
2379
+ const ctaBtn = el("button", "sk-empty__cta");
2380
+ html(ctaBtn, `${icon("pick")} Pick Element`);
2381
+ ctaBtn.addEventListener("click", () => triggerPick(ctaBtn));
2382
+ empty.appendChild(ctaBtn);
2383
+ return empty;
2384
+ }
2385
+ var GROUP_ORDER = ["Typography", "Appearance", "Layout"];
2386
+ function getPropertyGroup(prop) {
2387
+ if (/^font-|^line-height$|^color$|^letter-spacing$/.test(prop)) return "Typography";
2388
+ if (/^text-|^white-space$/.test(prop)) return "Typography";
2389
+ if (/background|border|shadow|opacity|radius|stroke/.test(prop)) return "Appearance";
2390
+ return "Layout";
2391
+ }
2392
+ function renderGroupedProperties() {
2393
+ const wrap = el("div", "sk-detect");
2394
+ const total = detectedTokensCache.length + staticValuesCache.length;
2395
+ if (total === 0) {
2396
+ const empty = el("div", "sk-detect__empty");
2397
+ html(empty, `
2398
+ <span class="sk-detect__empty-text">No Fluent tokens detected on this element.</span>
2399
+ <button class="sk-btn sk-btn--sm">Browse all tokens</button>
2400
+ `);
2401
+ empty.querySelector("button")?.addEventListener("click", () => {
2402
+ showAllTokens = true;
2403
+ refresh();
2404
+ });
2405
+ wrap.appendChild(empty);
2406
+ return wrap;
2407
+ }
2408
+ const groups = /* @__PURE__ */ new Map();
2409
+ for (const g of GROUP_ORDER) groups.set(g, []);
2410
+ for (const dt of detectedTokensCache) {
2411
+ const g = getPropertyGroup(dt.property);
2412
+ groups.get(g)?.push({ kind: "token", token: dt });
2413
+ }
2414
+ for (const sv of staticValuesCache) {
2415
+ const g = getPropertyGroup(sv.property);
2416
+ groups.get(g)?.push({ kind: "static", sv });
2417
+ }
2418
+ for (const groupName of GROUP_ORDER) {
2419
+ const items = groups.get(groupName);
2420
+ if (!items || items.length === 0) continue;
2421
+ const groupEl = el("div", "sk-group");
2422
+ const label = el("div", "sk-group__label");
2423
+ label.textContent = groupName;
2424
+ groupEl.appendChild(label);
2425
+ for (const item of items) {
2426
+ if (item.kind === "token") {
2427
+ groupEl.appendChild(renderScalePicker(item.token));
2428
+ } else {
2429
+ const promotion = promotedStatics.get(item.sv.property);
2430
+ if (promotion) {
2431
+ groupEl.appendChild(renderPromotedRow(item.sv, promotion));
2432
+ } else {
2433
+ groupEl.appendChild(renderStaticRow(item.sv));
2434
+ }
2435
+ }
2436
+ }
2437
+ wrap.appendChild(groupEl);
2438
+ }
2439
+ return wrap;
2440
+ }
2441
+ function renderScalePicker(dt) {
2442
+ const rowId = `tok-${dt.property}-${dt.scale.category}`;
2443
+ const override = getOverrideForToken(dt.current.name, currentScope);
2444
+ const isSwapped = !!override;
2445
+ const item = el("div", `sk-row${isSwapped ? " sk-row--changed" : ""}`);
2446
+ const isColor = dt.scale.category.startsWith("color");
2447
+ const isOpen = openDropdownId === rowId;
2448
+ let activeIdx = dt.index;
2449
+ let activeEntry = dt.current;
2450
+ if (override) {
2451
+ const swappedIdx = dt.scale.entries.findIndex((e) => cssValuesMatch(e.value, override.newValue));
2452
+ if (swappedIdx !== -1) {
2453
+ activeIdx = swappedIdx;
2454
+ activeEntry = dt.scale.entries[swappedIdx];
2455
+ }
2456
+ }
2457
+ const label = el("span", "sk-row__label");
2458
+ label.textContent = dt.label;
2459
+ item.appendChild(label);
2460
+ const trigger = el("button", `sk-row__trigger ${isSwapped ? "sk-row__trigger--swapped" : ""} ${isOpen ? "sk-row__trigger--open" : ""}`);
2461
+ if (isColor) {
2462
+ const swatch = el("span", "sk-row__swatch");
2463
+ swatch.style.backgroundColor = activeEntry.value;
2464
+ trigger.appendChild(swatch);
2465
+ }
2466
+ const name = el("span", "sk-row__name");
2467
+ name.textContent = activeEntry.label;
2468
+ trigger.appendChild(name);
2469
+ const val = el("span", "sk-row__val");
2470
+ val.textContent = activeEntry.value;
2471
+ trigger.appendChild(val);
2472
+ const chev = el("span", "sk-row__chev");
2473
+ html(chev, ICONS.chevronDown);
2474
+ trigger.appendChild(chev);
2475
+ trigger.addEventListener("click", () => {
2476
+ openDropdownId = isOpen ? null : rowId;
2477
+ refresh();
2478
+ });
2479
+ item.appendChild(trigger);
2480
+ const unlinkBtn = el("button", "sk-row__unlink");
2481
+ html(unlinkBtn, icon("unlink"));
2482
+ unlinkBtn.title = `Unlink token \u2192 static: ${dt.property}: ${activeEntry.value}`;
2483
+ unlinkBtn.addEventListener("click", () => {
2484
+ const css = `${dt.property}: ${activeEntry.value};`;
2485
+ ctx?.services.clipboard.copy(css);
2486
+ ctx?.services.notify.toast(`Unlinked: ${css}`);
2487
+ });
2488
+ item.appendChild(unlinkBtn);
2489
+ if (isOpen) {
2490
+ const dropdown = el("div", "sk-dropdown");
2491
+ for (let i = 0; i < dt.scale.entries.length; i++) {
2492
+ const entry = dt.scale.entries[i];
2493
+ const isActive = i === activeIdx;
2494
+ const isOriginal = i === dt.index && isSwapped;
2495
+ const opt = el("button", `sk-dropdown__opt ${isActive ? "sk-dropdown__opt--active" : ""} ${isOriginal ? "sk-dropdown__opt--original" : ""}`);
2496
+ if (isColor) {
2497
+ const sw = el("span", "sk-dropdown__swatch");
2498
+ sw.style.backgroundColor = entry.value;
2499
+ opt.appendChild(sw);
2500
+ }
2501
+ const optName = el("span", "sk-dropdown__name");
2502
+ optName.textContent = entry.label;
2503
+ opt.appendChild(optName);
2504
+ const optVal = el("span", "sk-dropdown__val");
2505
+ optVal.textContent = entry.value;
2506
+ opt.appendChild(optVal);
2507
+ if (isOriginal) {
2508
+ const badge = el("span", "sk-dropdown__badge");
2509
+ badge.textContent = "original";
2510
+ opt.appendChild(badge);
2511
+ }
2512
+ opt.addEventListener("click", () => {
2513
+ handleTokenSwap(dt, entry, i);
2514
+ openDropdownId = null;
2515
+ });
2516
+ dropdown.appendChild(opt);
2517
+ }
2518
+ item.appendChild(dropdown);
2519
+ }
2520
+ return item;
2521
+ }
2522
+ function handleTokenSwap(dt, target, targetIdx) {
2523
+ if (targetIdx === dt.index) {
2524
+ removeOverride(dt.current.name, currentScope);
2525
+ } else {
2526
+ applyOverride({
2527
+ tokenName: dt.current.name,
2528
+ cssVar: dt.current.cssVar,
2529
+ originalValue: dt.current.value,
2530
+ newValue: target.value,
2531
+ scope: currentScope
2532
+ });
2533
+ }
2534
+ if (pickedElement) {
2535
+ const d = detectAll(pickedElement.element, registry);
2536
+ detectedTokensCache = d.tokens;
2537
+ staticValuesCache = d.statics;
2538
+ }
2539
+ refresh();
2540
+ }
2541
+ function cssValuesMatch(a, b) {
2542
+ return a.trim().toLowerCase() === b.trim().toLowerCase();
2543
+ }
2544
+ function renderPromotedRow(sv, promotion) {
2545
+ const rowId = `promoted-${sv.property}`;
2546
+ const isOpen = openDropdownId === rowId;
2547
+ const isColor = sv.isColor;
2548
+ const item = el("div", "sk-row sk-row--changed");
2549
+ const label = el("span", "sk-row__label");
2550
+ label.textContent = sv.label;
2551
+ label.title = sv.property;
2552
+ item.appendChild(label);
2553
+ const trigger = el("button", `sk-row__trigger sk-row__trigger--swapped ${isOpen ? "sk-row__trigger--open" : ""}`);
2554
+ if (isColor) {
2555
+ const swatch = el("span", "sk-row__swatch");
2556
+ swatch.style.backgroundColor = promotion.entry.value;
2557
+ trigger.appendChild(swatch);
2558
+ }
2559
+ const name = el("span", "sk-row__name");
2560
+ name.textContent = promotion.entry.label;
2561
+ trigger.appendChild(name);
2562
+ const val = el("span", "sk-row__val");
2563
+ val.textContent = promotion.entry.value;
2564
+ trigger.appendChild(val);
2565
+ const chev = el("span", "sk-row__chev");
2566
+ html(chev, ICONS.chevronDown);
2567
+ trigger.appendChild(chev);
2568
+ trigger.addEventListener("click", () => {
2569
+ openDropdownId = isOpen ? null : rowId;
2570
+ refresh();
2571
+ });
2572
+ item.appendChild(trigger);
2573
+ const unlinkBtn = el("button", "sk-row__unlink");
2574
+ html(unlinkBtn, icon("unlink"));
2575
+ unlinkBtn.title = `Unlink: revert to ${sv.property}: ${sv.value}`;
2576
+ unlinkBtn.addEventListener("click", () => {
2577
+ removeOverride(promotion.entry.name, currentScope);
2578
+ promotedStatics.delete(sv.property);
2579
+ refresh();
2580
+ });
2581
+ item.appendChild(unlinkBtn);
2582
+ if (isOpen) {
2583
+ const dropdown = el("div", "sk-dropdown");
2584
+ for (const entry of promotion.scale.entries) {
2585
+ const isActive = entry.name === promotion.entry.name;
2586
+ const opt = el("button", `sk-dropdown__opt ${isActive ? "sk-dropdown__opt--active" : ""}`);
2587
+ if (isColor) {
2588
+ const sw = el("span", "sk-dropdown__swatch");
2589
+ sw.style.backgroundColor = entry.value;
2590
+ opt.appendChild(sw);
2591
+ }
2592
+ const optName = el("span", "sk-dropdown__name");
2593
+ optName.textContent = entry.label;
2594
+ opt.appendChild(optName);
2595
+ const optVal = el("span", "sk-dropdown__val");
2596
+ optVal.textContent = entry.value;
2597
+ opt.appendChild(optVal);
2598
+ opt.addEventListener("click", () => {
2599
+ promotedStatics.set(sv.property, { entry, scale: promotion.scale });
2600
+ applyOverride({
2601
+ tokenName: entry.name,
2602
+ cssVar: entry.cssVar,
2603
+ originalValue: sv.value,
2604
+ newValue: entry.value,
2605
+ scope: currentScope
2606
+ });
2607
+ openDropdownId = null;
2608
+ refresh();
2609
+ });
2610
+ dropdown.appendChild(opt);
2611
+ }
2612
+ item.appendChild(dropdown);
2613
+ }
2614
+ return item;
2615
+ }
2616
+ function renderStaticRow(sv) {
2617
+ const rowId = `static-${sv.property}`;
2618
+ const hasCandidates = sv.candidateCategories.length > 0;
2619
+ const isOpen = openDropdownId === rowId;
2620
+ const row = el("div", "sk-row");
2621
+ const label = el("span", "sk-row__label");
2622
+ label.textContent = sv.label;
2623
+ label.title = sv.property;
2624
+ row.appendChild(label);
2625
+ const staticOverride = getStaticOverrideForProperty(sv.property, currentScope);
2626
+ const displayValue = staticOverride ? staticOverride.newValue : sv.value;
2627
+ const isModified = !!staticOverride;
2628
+ const trigger = el("button", `sk-row__trigger sk-row__trigger--static ${isOpen ? "sk-row__trigger--open" : ""} ${isModified ? "sk-row__trigger--modified" : ""}`);
2629
+ if (sv.isColor) {
2630
+ const swatch = el("span", "sk-row__swatch");
2631
+ swatch.style.backgroundColor = displayValue;
2632
+ trigger.appendChild(swatch);
2633
+ }
2634
+ const valText = el("span", "sk-row__val sk-row__val--full");
2635
+ valText.textContent = displayValue;
2636
+ trigger.appendChild(valText);
2637
+ const chev = el("span", "sk-row__chev");
2638
+ html(chev, ICONS.chevronDown);
2639
+ trigger.appendChild(chev);
2640
+ trigger.addEventListener("click", () => {
2641
+ openDropdownId = isOpen ? null : rowId;
2642
+ refresh();
2643
+ });
2644
+ row.appendChild(trigger);
2645
+ if (isModified) {
2646
+ const undoBtn = el("button", "sk-row__unlink");
2647
+ html(undoBtn, icon("undo"));
2648
+ undoBtn.title = `Reset to ${sv.value}`;
2649
+ undoBtn.addEventListener("click", () => {
2650
+ removeStaticOverride(sv.property, currentScope);
2651
+ refresh();
2652
+ });
2653
+ row.appendChild(undoBtn);
2654
+ } else if (hasCandidates && ctx?.services.copilot.available) {
2655
+ const findBtn = el("button", "sk-row__unlink");
2656
+ html(findBtn, icon("sparkle"));
2657
+ findBtn.title = "Ask Copilot to find a matching token";
2658
+ findBtn.addEventListener("click", () => handleFindToken(sv, findBtn));
2659
+ row.appendChild(findBtn);
2660
+ }
2661
+ if (isOpen) {
2662
+ const dropdown = el("div", "sk-dropdown");
2663
+ const inputRow = el("div", "sk-dropdown__input-row");
2664
+ if (sv.isColor) {
2665
+ const colorInput = el("input", "sk-dropdown__color-pick");
2666
+ colorInput.type = "color";
2667
+ colorInput.value = displayValue.startsWith("#") ? displayValue : rgbToHex(displayValue);
2668
+ colorInput.addEventListener("input", () => {
2669
+ const txtInput = inputRow.querySelector(".sk-dropdown__input");
2670
+ if (txtInput) txtInput.value = colorInput.value;
2671
+ });
2672
+ inputRow.appendChild(colorInput);
2673
+ }
2674
+ const input = el("input", "sk-dropdown__input");
2675
+ input.type = "text";
2676
+ input.value = displayValue;
2677
+ input.placeholder = "Type a value\u2026";
2678
+ input.setAttribute("autocomplete", "off");
2679
+ const applyBtn = el("button", "sk-dropdown__apply");
2680
+ applyBtn.textContent = "Apply";
2681
+ const submitValue = () => {
2682
+ const val = input.value.trim();
2683
+ if (!val) {
2684
+ openDropdownId = null;
2685
+ refresh();
2686
+ return;
2687
+ }
2688
+ if (val === sv.value && isModified) {
2689
+ removeStaticOverride(sv.property, currentScope);
2690
+ } else if (val !== sv.value) {
2691
+ applyStaticOverride({ property: sv.property, originalValue: sv.value, newValue: val, scope: currentScope });
2692
+ }
2693
+ openDropdownId = null;
2694
+ refresh();
2695
+ };
2696
+ input.addEventListener("keydown", (e) => {
2697
+ if (e.key === "Enter") {
2698
+ e.preventDefault();
2699
+ submitValue();
2700
+ }
2701
+ if (e.key === "Escape") {
2702
+ e.preventDefault();
2703
+ openDropdownId = null;
2704
+ refresh();
2705
+ }
2706
+ e.stopPropagation();
2707
+ });
2708
+ applyBtn.addEventListener("click", submitValue);
2709
+ inputRow.appendChild(input);
2710
+ inputRow.appendChild(applyBtn);
2711
+ dropdown.appendChild(inputRow);
2712
+ requestAnimationFrame(() => {
2713
+ input.focus();
2714
+ input.select();
2715
+ });
2716
+ if (hasCandidates) {
2717
+ const sep = el("div", "sk-dropdown__sep");
2718
+ sep.textContent = "or pick a token";
2719
+ dropdown.appendChild(sep);
2720
+ const nearest = findNearestInScale(sv.value, sv.candidateCategories, registry);
2721
+ const relevantScales = registry.filter((s) => sv.candidateCategories.includes(s.category));
2722
+ for (const scale of relevantScales) {
2723
+ const isColor = scale.category.startsWith("color");
2724
+ for (const entry of scale.entries) {
2725
+ const isNearest = nearest && entry.name === nearest.entry.name;
2726
+ const opt = el("button", `sk-dropdown__opt ${isNearest ? "sk-dropdown__opt--nearest" : ""}`);
2727
+ if (isColor) {
2728
+ const sw = el("span", "sk-dropdown__swatch");
2729
+ sw.style.backgroundColor = entry.value;
2730
+ opt.appendChild(sw);
2731
+ }
2732
+ const optName = el("span", "sk-dropdown__name");
2733
+ optName.textContent = entry.label;
2734
+ opt.appendChild(optName);
2735
+ const optVal = el("span", "sk-dropdown__val");
2736
+ optVal.textContent = entry.value;
2737
+ opt.appendChild(optVal);
2738
+ if (isNearest) {
2739
+ const badge = el("span", "sk-dropdown__badge");
2740
+ badge.textContent = "nearest";
2741
+ opt.appendChild(badge);
2742
+ }
2743
+ opt.addEventListener("click", () => {
2744
+ promotedStatics.set(sv.property, { entry, scale });
2745
+ applyOverride({
2746
+ tokenName: entry.name,
2747
+ cssVar: entry.cssVar,
2748
+ originalValue: sv.value,
2749
+ newValue: entry.value,
2750
+ scope: currentScope
2751
+ });
2752
+ openDropdownId = null;
2753
+ refresh();
2754
+ });
2755
+ dropdown.appendChild(opt);
2756
+ }
2757
+ }
2758
+ }
2759
+ row.appendChild(dropdown);
2760
+ }
2761
+ return row;
2762
+ }
2763
+ async function handleFindToken(sv, btn) {
2764
+ if (!ctx?.services.copilot.available) return;
2765
+ btn.classList.add("sk-row__unlink--loading");
2766
+ const relevantScales = registry.filter((s) => sv.candidateCategories.includes(s.category));
2767
+ const scaleInfo = relevantScales.map(
2768
+ (s) => `${s.label} (${s.category}): ${s.entries.map((e) => `${e.label}=${e.value} [var:${e.cssVar}]`).join(", ")}`
2769
+ ).join("\n");
2770
+ try {
2771
+ const response = await ctx.services.copilot.ask([
2772
+ { role: "system", content: "You are a design token matching assistant. Return ONLY valid JSON, no markdown." },
2773
+ { role: "user", content: `Find the closest Fluent UI design token for:
2774
+ Property: ${sv.property}
2775
+ Current value: ${sv.value}
2776
+
2777
+ Available token scales:
2778
+ ${scaleInfo}
2779
+
2780
+ Return JSON: { "match": { "tokenName": "name", "cssVar": "--var", "value": "resolved" }, "reason": "why" }
2781
+ Or if no close match: { "match": null, "reason": "explanation" }` }
2782
+ ]);
2783
+ const jsonStr = response.match(/\{[\s\S]*\}/)?.[0];
2784
+ const json = jsonStr ? JSON.parse(jsonStr) : null;
2785
+ if (json?.match) {
2786
+ const tokenInfo = findToken(registry, json.match.tokenName);
2787
+ if (tokenInfo) {
2788
+ promotedStatics.set(sv.property, {
2789
+ entry: tokenInfo.scale.entries[tokenInfo.index],
2790
+ scale: tokenInfo.scale
2791
+ });
2792
+ }
2793
+ applyOverride({
2794
+ tokenName: json.match.tokenName,
2795
+ cssVar: json.match.cssVar,
2796
+ originalValue: sv.value,
2797
+ newValue: json.match.value,
2798
+ scope: currentScope
2799
+ });
2800
+ ctx.services.notify.toast(`Matched: ${json.match.tokenName}`);
2801
+ refresh();
2802
+ } else {
2803
+ ctx.services.notify.toast(json?.reason ?? "No close token match found");
2804
+ }
2805
+ } catch {
2806
+ ctx.services.notify.toast("Token matching failed");
2807
+ } finally {
2808
+ btn.classList.remove("sk-row__unlink--loading");
2809
+ }
2810
+ }
2811
+ function buildSystemPrompt() {
2812
+ const overrides = getOverrides();
2813
+ const overrideState = overrides.length > 0 ? `Current overrides:
2814
+ ${overrides.map((o) => ` ${o.tokenName}: ${o.originalValue} \u2192 ${o.newValue}`).join("\n")}` : "No overrides active.";
2815
+ const scopeDesc = currentScope.type === "global" ? "Changes apply globally." : `Changes scoped to: ${currentScope.label}`;
2816
+ const elementTokens = detectedTokensCache.length > 0 ? `Currently selected element uses these tokens:
2817
+ ${detectedTokensCache.map((dt) => {
2818
+ const override = getOverrideForToken(dt.current.name, currentScope);
2819
+ const active = override ? dt.scale.entries.find((e) => cssValuesMatch(e.value, override.newValue)) ?? dt.current : dt.current;
2820
+ return ` ${dt.label} (${dt.property}): ${active.label} = ${active.value} [scale: ${dt.scale.entries.map((e) => e.label).join(", ")}]`;
2821
+ }).join("\n")}` : "No element selected.";
2822
+ return `You are a design token advisor for a Fluent UI application.
2823
+ The user is inspecting an element and wants to change its design tokens via dropdowns in the Style Kit panel.
2824
+ Return token overrides that swap the current token to a different entry in the SAME scale.
2825
+
2826
+ ${elementTokens}
2827
+
2828
+ Available token scales:
2829
+ ${serializeForPrompt(registry)}
2830
+
2831
+ ${overrideState}
2832
+
2833
+ ${scopeDesc}
2834
+
2835
+ IMPORTANT: Respond with ONLY valid JSON in this exact format:
2836
+ { "overrides": [ { "token": "currentTokenName", "value": "newCSSValue" } ] }
2837
+
2838
+ Rules:
2839
+ - "token" must be the token name currently in use on the element (from the list above).
2840
+ - "value" must be the exact CSS value of the target entry from the same scale.
2841
+ - For example, if the element uses fontSizeBase300=14px and the user says "make font-size 800", respond with: { "overrides": [{ "token": "fontSizeBase300", "value": "28px" }] } (using fontSizeBase800's value).
2842
+ - Do not include explanations, markdown, or anything outside the JSON object.
2843
+ - Token names must match exactly from the detected element tokens above.
2844
+ - Values must be valid CSS values from the scales (use the exact scale values).`;
2845
+ }
2846
+ function parseOverrideResponse(response) {
2847
+ try {
2848
+ const jsonMatch = response.match(/\{[\s\S]*"overrides"[\s\S]*\}/);
2849
+ if (!jsonMatch) return [];
2850
+ const parsed = JSON.parse(jsonMatch[0]);
2851
+ if (!Array.isArray(parsed.overrides)) return [];
2852
+ return parsed.overrides.filter(
2853
+ (o) => typeof o.token === "string" && typeof o.value === "string"
2854
+ );
2855
+ } catch {
2856
+ return [];
2857
+ }
2858
+ }
2859
+ function getScopePlaceholder() {
2860
+ if (currentScope.type === "global") {
2861
+ return 'Try "make it more compact" or "darker background"';
2862
+ }
2863
+ return `Adjust ${truncatePath(currentScope.label)}...`;
2864
+ }
2865
+ async function buildOverrideContext() {
2866
+ const overrides = getOverrides();
2867
+ if (overrides.length === 0) {
2868
+ ctx?.services.notify.toast("No overrides to apply");
2869
+ return null;
2870
+ }
2871
+ const { overrides: serialized, css, themePartial } = serializeOverrides();
2872
+ const depPattern = /___[a-z0-9]+_\d+|\.fui-/;
2873
+ const isDep = (o) => o.scope !== "Global" && depPattern.test(o.scope);
2874
+ const store2 = getStore();
2875
+ const variationName = store2.active ? store2.variations.find((v) => v.id === store2.active)?.name ?? "Unnamed" : "Unsaved";
2876
+ let screenshotUrl = "";
2877
+ try {
2878
+ screenshotUrl = await ctx.services.screenshot.capture();
2879
+ } catch {
2880
+ }
2881
+ const sections = [];
2882
+ sections.push(`## Style Override \u2014 "${variationName}"`);
2883
+ sections.push("");
2884
+ if (screenshotUrl) {
2885
+ sections.push(`### Visual Reference`);
2886
+ sections.push(`![screenshot](${screenshotUrl})`);
2887
+ sections.push("");
2888
+ }
2889
+ sections.push("### Overrides");
2890
+ sections.push("");
2891
+ sections.push("| Token | Original | Target | Swap To | Scope | Location |");
2892
+ sections.push("|-------|----------|--------|---------|-------|----------|");
2893
+ for (const o of serialized) {
2894
+ const loc = isDep(o) ? "\u26A0\uFE0F dependency" : "local";
2895
+ sections.push(`| \`${o.token}\` | \`${o.from}\` | \`${o.to}\` | ${o.swapTo ? `\`${o.swapTo}\`` : "\u2014"} | ${o.scope} | ${loc} |`);
2896
+ }
2897
+ sections.push("");
2898
+ const locals = serialized.filter((o) => !isDep(o));
2899
+ if (locals.length > 0) {
2900
+ const localSwaps = locals.filter((o) => o.swapTo);
2901
+ const localRaw = locals.filter((o) => !o.swapTo);
2902
+ sections.push("### Local changes (in project source)");
2903
+ sections.push("");
2904
+ if (localSwaps.length > 0) {
2905
+ sections.push("**Token swaps** \u2014 replace the token reference in `makeStyles()`:");
2906
+ sections.push("");
2907
+ for (const s of localSwaps) {
2908
+ sections.push(`- \`tokens.${s.token}\` \u2192 \`tokens.${s.swapTo}\``);
2909
+ }
2910
+ sections.push("");
2911
+ sections.push("```bash");
2912
+ sections.push("# Deterministic apply:");
2913
+ const payload = JSON.stringify(localSwaps.map((s) => ({ from: s.token, to: s.swapTo })));
2914
+ sections.push("echo '" + payload + "' | node .dc-plugins/style-kit/scripts/apply-token-swaps.cjs --apply");
2915
+ sections.push("```");
2916
+ sections.push("");
2917
+ }
2918
+ if (localRaw.length > 0) {
2919
+ sections.push("**Value overrides** \u2014 update the token value in a theme partial or `makeStyles()`:");
2920
+ sections.push("");
2921
+ sections.push("```typescript");
2922
+ sections.push(themePartial);
2923
+ sections.push("```");
2924
+ sections.push("");
2925
+ }
2926
+ }
2927
+ const deps = serialized.filter(isDep);
2928
+ if (deps.length > 0) {
2929
+ sections.push("### Dependency component overrides");
2930
+ sections.push("");
2931
+ sections.push("These tokens are styled **inside a library component** (detected from Fluent UI / Griffel generated classnames in the scope selector). The `makeStyles()` call is in the dependency package, not project source.");
2932
+ sections.push("");
2933
+ for (const o of deps) {
2934
+ sections.push(`#### \`${o.token}\`: \`${o.from}\` \u2192 \`${o.to}\`${o.swapTo ? ` (swap to \`${o.swapTo}\`)` : ""}`);
2935
+ sections.push("");
2936
+ sections.push(`**Scope selector:** \`${o.scope}\``);
2937
+ sections.push("");
2938
+ sections.push("**Resolution options (pick one):**");
2939
+ sections.push("");
2940
+ sections.push(`1. **CSS custom property override** \u2014 redefine \`--${o.token}\` on a wrapper \`<div>\` around the dependency component using \`makeStyles()\`. The dependency reads \`var(--${o.token})\` internally, so a closer ancestor wins via CSS inheritance.`);
2941
+ sections.push(" ```tsx");
2942
+ sections.push(" const useStyles = makeStyles({");
2943
+ if (o.swapTo) {
2944
+ sections.push(` root: { '--${o.token}': tokens.${o.swapTo} },`);
2945
+ } else {
2946
+ sections.push(` root: { '--${o.token}': '${o.to}' },`);
2947
+ }
2948
+ sections.push(" });");
2949
+ sections.push(" // wrap: <div className={styles.root}><DependencyComponent ... /></div>");
2950
+ sections.push(" ```");
2951
+ sections.push("");
2952
+ sections.push(`2. **Scoped FluentProvider** \u2014 wrap the component in a \`<FluentProvider>\` with a partial theme that redefines \`${o.token}\`. Lighter touch but affects ALL children using that token.`);
2953
+ sections.push(" ```tsx");
2954
+ sections.push(` <FluentProvider theme={{ ...webLightTheme, ${o.token}: '${o.to}' }}>`);
2955
+ sections.push(" <DependencyComponent ... />");
2956
+ sections.push(" </FluentProvider>");
2957
+ sections.push(" ```");
2958
+ sections.push("");
2959
+ sections.push("3. **Fix upstream** \u2014 open a PR on the component package to expose a prop or change the token.");
2960
+ sections.push("");
2961
+ }
2962
+ }
2963
+ if (css.trim()) {
2964
+ sections.push("<details><summary>Raw CSS overrides (reference only)</summary>");
2965
+ sections.push("");
2966
+ sections.push("```css");
2967
+ sections.push(css);
2968
+ sections.push("```");
2969
+ sections.push("</details>");
2970
+ sections.push("");
2971
+ }
2972
+ sections.push(`<!-- dc:style-kit:overrides:${btoa(JSON.stringify(serialized))} -->`);
2973
+ const body = sections.join("\n");
2974
+ const labels = ["design-canvas", "style-kit"];
2975
+ if (deps.length > 0) labels.push("dependency-override");
2976
+ if (serialized.some((o) => o.swapTo)) labels.push("token-swap");
2977
+ const title = deps.length > 0 ? `style: ${serialized.length} override${serialized.length > 1 ? "s" : ""} (${deps.length} in dependency) \u2014 ${variationName}` : `style: ${serialized.length} token override${serialized.length > 1 ? "s" : ""} \u2014 ${variationName}`;
2978
+ return { title, body, labels };
2979
+ }
2980
+ async function copyFixPrompt() {
2981
+ const context = await buildOverrideContext();
2982
+ if (!context) return;
2983
+ const prompt = `Apply the following style overrides to the codebase.
2984
+
2985
+ ${context.body}`;
2986
+ await ctx?.services.clipboard.copy(prompt);
2987
+ ctx?.services.notify.toast("Fix prompt copied \u2014 paste it into Copilot Chat or your preferred LLM to apply.", "info");
2988
+ }
2989
+ async function applyToCode() {
2990
+ const context = await buildOverrideContext();
2991
+ if (!context) return;
2992
+ if (!ctx?.services.github.available) {
2993
+ ctx?.services.notify.toast("GitHub not configured \u2014 enable it in Design Canvas settings", "warning");
2994
+ return;
2995
+ }
2996
+ try {
2997
+ ctx.services.notify.toast("Creating issue...");
2998
+ const result = await ctx.services.github.createIssue({
2999
+ title: context.title,
3000
+ body: context.body,
3001
+ labels: context.labels
3002
+ });
3003
+ try {
3004
+ await ctx.services.github.assignCopilot(result.number);
3005
+ } catch {
3006
+ }
3007
+ showIssueLinkToast(result.number, result.url);
3008
+ } catch {
3009
+ ctx.services.notify.toast("Failed to create issue");
3010
+ }
3011
+ }
3012
+ function showIssueLinkToast(issueNumber, url) {
3013
+ if (!panelRoot) return;
3014
+ panelRoot.querySelector(".sk-issue-banner")?.remove();
3015
+ const banner = el("div", "sk-issue-banner");
3016
+ banner.style.cssText = 'display:flex;flex-direction:column;gap:8px;padding:10px 12px;margin:0 12px 8px;background:var(--colorStatusSuccessBackground1,#dff6dd);border:1px solid var(--colorStatusSuccessBorder1,#9fd89f);border-radius:6px;font-family:var(--fontFamilyBase,"Segoe UI",sans-serif);font-size:13px;color:var(--colorNeutralForeground1,#242424);animation:sk-slide-up .2s ease-out';
3017
+ const topRow = el("div", "");
3018
+ topRow.style.cssText = "display:flex;align-items:center;gap:8px";
3019
+ const ghIcon = el("span", "");
3020
+ ghIcon.innerHTML = ICONS.github;
3021
+ ghIcon.style.cssText = "display:inline-flex;width:16px;height:16px;flex-shrink:0";
3022
+ topRow.appendChild(ghIcon);
3023
+ const text = el("span", "");
3024
+ text.style.cssText = "flex:1;font-weight:600";
3025
+ text.textContent = `Issue #${issueNumber} created \xB7 Copilot assigned`;
3026
+ topRow.appendChild(text);
3027
+ const close = el("button", "");
3028
+ close.innerHTML = ICONS.x;
3029
+ close.style.cssText = "display:inline-flex;width:14px;height:14px;background:none;border:none;cursor:pointer;color:var(--colorNeutralForeground3,#707070);padding:0;flex-shrink:0";
3030
+ close.addEventListener("click", () => banner.remove());
3031
+ topRow.appendChild(close);
3032
+ banner.appendChild(topRow);
3033
+ const bottomRow = el("div", "");
3034
+ bottomRow.style.cssText = "display:flex;align-items:center;gap:10px";
3035
+ const link = el("a", "");
3036
+ link.href = url;
3037
+ link.target = "_blank";
3038
+ link.rel = "noopener noreferrer";
3039
+ link.textContent = "View on GitHub \u2192";
3040
+ link.style.cssText = "color:var(--colorBrandForegroundLink,#0078d4);text-decoration:none;font-weight:600;font-size:12px;white-space:nowrap";
3041
+ link.addEventListener("mouseenter", () => {
3042
+ link.style.textDecoration = "underline";
3043
+ });
3044
+ link.addEventListener("mouseleave", () => {
3045
+ link.style.textDecoration = "none";
3046
+ });
3047
+ bottomRow.appendChild(link);
3048
+ banner.appendChild(bottomRow);
3049
+ const actions = panelRoot.querySelector(".sk-actions");
3050
+ if (actions) {
3051
+ actions.parentElement?.insertBefore(banner, actions);
3052
+ } else {
3053
+ panelRoot.appendChild(banner);
3054
+ }
3055
+ }
3056
+ function rgbToHex(rgb) {
3057
+ const match = rgb.match(/\d+/g);
3058
+ if (!match || match.length < 3) return "#000000";
3059
+ const [r, g, b] = match.map(Number);
3060
+ return "#" + [r, g, b].map((c) => c.toString(16).padStart(2, "0")).join("");
3061
+ }
3062
+ function refresh() {
3063
+ if (!panelRoot) return;
3064
+ const body = panelRoot.querySelector(".sk-body");
3065
+ const scrollTop = body?.scrollTop ?? 0;
3066
+ render(panelRoot);
3067
+ const newBody = panelRoot.querySelector(".sk-body");
3068
+ if (newBody) newBody.scrollTop = scrollTop;
3069
+ }
3070
+ function render(root) {
3071
+ root.innerHTML = "";
3072
+ panelRoot = root;
3073
+ const styleTag = document.createElement("style");
3074
+ styleTag.textContent = PANEL_STYLES;
3075
+ root.appendChild(styleTag);
3076
+ const container = el("div", `sk ${ctx?.isDark ? "sk--dark" : ""}`);
3077
+ container.appendChild(renderPromptBar());
3078
+ container.appendChild(renderScopeBar());
3079
+ container.appendChild(renderVariationsBar());
3080
+ const summary = renderOverrideSummary();
3081
+ if (summary) container.appendChild(summary);
3082
+ const body = el("div", "sk-body");
3083
+ if (registry.length === 0) {
3084
+ body.appendChild(renderEmptyState());
3085
+ } else if (pickedElement && !showAllTokens) {
3086
+ body.appendChild(renderGroupedProperties());
3087
+ const browseToggle = el("button", "sk-browse-toggle");
3088
+ browseToggle.textContent = "Browse all token scales \u203A";
3089
+ browseToggle.addEventListener("click", () => {
3090
+ showAllTokens = true;
3091
+ refresh();
3092
+ });
3093
+ body.appendChild(browseToggle);
3094
+ } else if (showAllTokens) {
3095
+ if (pickedElement) {
3096
+ const backLink = el("button", "sk-browse-toggle");
3097
+ backLink.textContent = "\u2039 Back to detected tokens";
3098
+ backLink.addEventListener("click", () => {
3099
+ showAllTokens = false;
3100
+ refresh();
3101
+ });
3102
+ body.appendChild(backLink);
3103
+ }
3104
+ const sorted = [...registry].sort((a, b) => {
3105
+ const aOverrides = a.entries.some((e) => getOverrideForToken(e.name, currentScope));
3106
+ const bOverrides = b.entries.some((e) => getOverrideForToken(e.name, currentScope));
3107
+ if (aOverrides !== bOverrides) return aOverrides ? -1 : 1;
3108
+ return CATEGORY_ORDER.indexOf(a.category) - CATEGORY_ORDER.indexOf(b.category);
3109
+ });
3110
+ for (const scale of sorted) {
3111
+ body.appendChild(renderCategory(scale));
3112
+ }
3113
+ } else {
3114
+ body.appendChild(renderEmptyState());
3115
+ const browseToggle = el("button", "sk-browse-toggle");
3116
+ browseToggle.textContent = "Browse all token scales \u203A";
3117
+ browseToggle.addEventListener("click", () => {
3118
+ showAllTokens = true;
3119
+ refresh();
3120
+ });
3121
+ body.appendChild(browseToggle);
3122
+ }
3123
+ container.appendChild(body);
3124
+ if (getOverrides().length > 0) {
3125
+ container.appendChild(renderFooterActions());
3126
+ }
3127
+ root.appendChild(container);
3128
+ }
3129
+ function mountPanel(mountPoint, context) {
3130
+ ctx = context;
3131
+ registry = buildRegistry();
3132
+ for (const scale of registry) {
3133
+ collapsedCategories.add(scale.category);
3134
+ }
3135
+ render(mountPoint);
3136
+ const unsubOverrides = onOverridesChanged(() => refresh());
3137
+ const unsubVariations = onVariationsChanged(() => refresh());
3138
+ return () => {
3139
+ unsubOverrides();
3140
+ unsubVariations();
3141
+ panelRoot = null;
3142
+ ctx = null;
3143
+ };
3144
+ }
3145
+
3146
+ // src/index.ts
3147
+ var cleanup = null;
3148
+ var plugin = {
3149
+ meta: {
3150
+ id: "style-kit",
3151
+ name: "Style Kit",
3152
+ icon: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none"><path d="M7.5 2C5.01 2 3 4.01 3 6.5c0 1.47.7 2.77 1.79 3.6L10 17l5.21-6.9A4.48 4.48 0 0 0 17 6.5C17 4.01 14.99 2 12.5 2c-1.25 0-2.37.51-3.18 1.33L10 2.66l-.68-.33A4.47 4.47 0 0 0 7.5 2Zm-2 4.5a2 2 0 1 1 4 0 2 2 0 0 1-4 0Zm5.5 0a1 1 0 1 1 2 0 1 1 0 0 1-2 0Zm-2 3a1 1 0 1 1 2 0 1 1 0 0 1-2 0Z" stroke="currentColor" stroke-width="1.2" fill="none"/></svg>',
3153
+ description: "Token-aware design tuning \u2014 explore scales, override live, save variations, ship changes",
3154
+ version: "1.0.0",
3155
+ panel: {
3156
+ defaultWidth: 340,
3157
+ defaultHeight: 600,
3158
+ minWidth: 300,
3159
+ minHeight: 400
3160
+ }
3161
+ },
3162
+ activate(ctx2) {
3163
+ initVariations(ctx2);
3164
+ cleanup = mountPanel(ctx2.mountPoint, {
3165
+ mountPoint: ctx2.mountPoint,
3166
+ isDark: ctx2.isDark,
3167
+ emit: (type, payload) => ctx2.emit(type, payload),
3168
+ services: ctx2.services,
3169
+ backgroundColor: ctx2.isDark ? "#333333" : "#FFFFFF"
3170
+ // Set background color based on theme
3171
+ });
3172
+ ctx2.services.commands?.register(
3173
+ "style-kit.toggle",
3174
+ "Ctrl+Shift+K",
3175
+ () => ctx2.panel.isOpen ? ctx2.panel.close() : ctx2.panel.open()
3176
+ );
3177
+ },
3178
+ deactivate() {
3179
+ cleanup?.();
3180
+ cleanup = null;
3181
+ destroy();
3182
+ destroyVariations();
3183
+ },
3184
+ getHandoffContext() {
3185
+ return serializeOverrides();
3186
+ }
3187
+ };
3188
+ var index_default = plugin;
3189
+ export {
3190
+ index_default as default
3191
+ };