@vadenai/mcp-server 0.3.1 → 0.3.2
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
CHANGED
|
@@ -20,7 +20,7 @@ import { handleUpdateComponentStyle } from "./tools/write-components.js";
|
|
|
20
20
|
import { handleUpdateDesignTokens } from "./tools/write-tokens.js";
|
|
21
21
|
import { validateArrayAddArgs, validateArrayRemoveArgs, validateFieldUpdateArgs, validatePushWireframesArgs, } from "./validation.js";
|
|
22
22
|
// --- コマンドライン引数パース ---
|
|
23
|
-
const VERSION = "0.3.
|
|
23
|
+
const VERSION = "0.3.2";
|
|
24
24
|
function parseArgs() {
|
|
25
25
|
const args = process.argv.slice(2);
|
|
26
26
|
if (args.includes("--help") || args.includes("-h")) {
|
package/dist/tools/components.js
CHANGED
|
@@ -385,6 +385,16 @@ function formatStyleSection(data) {
|
|
|
385
385
|
}
|
|
386
386
|
lines.push("");
|
|
387
387
|
}
|
|
388
|
+
// --- Resolved Styles (JSON) ---
|
|
389
|
+
const resolvedStylesJson = buildResolvedStylesJson(resolvedStyleEntries, resolvedVariantEntries);
|
|
390
|
+
lines.push("");
|
|
391
|
+
lines.push("## Resolved Styles (JSON)");
|
|
392
|
+
lines.push("");
|
|
393
|
+
lines.push("Machine-readable resolved styles for programmatic consumption (Figma Plugin API, code generation, etc.).");
|
|
394
|
+
lines.push("");
|
|
395
|
+
lines.push("```json");
|
|
396
|
+
lines.push(JSON.stringify(resolvedStylesJson, null, 2));
|
|
397
|
+
lines.push("```");
|
|
388
398
|
}
|
|
389
399
|
return lines.join("\n");
|
|
390
400
|
}
|
|
@@ -458,6 +468,125 @@ function formatResolvedProps(props) {
|
|
|
458
468
|
}
|
|
459
469
|
return result;
|
|
460
470
|
}
|
|
471
|
+
/**
|
|
472
|
+
* ResolvedVisualProperties からテキスト出力用のプロパティ(boxShadow等の文字列形式)を除き、
|
|
473
|
+
* 構造化された JSON オブジェクトを構築する。
|
|
474
|
+
*/
|
|
475
|
+
function buildResolvedStylesJson(baseEntries, variantEntries) {
|
|
476
|
+
const json = {};
|
|
477
|
+
for (const { label, props } of baseEntries) {
|
|
478
|
+
json[label] = propsToJson(props);
|
|
479
|
+
}
|
|
480
|
+
if (variantEntries.length > 0) {
|
|
481
|
+
const variants = {};
|
|
482
|
+
for (const { group, name, props } of variantEntries) {
|
|
483
|
+
const cleaned = propsToJson(props);
|
|
484
|
+
if (Object.keys(cleaned).length === 0)
|
|
485
|
+
continue;
|
|
486
|
+
if (!variants[group])
|
|
487
|
+
variants[group] = {};
|
|
488
|
+
variants[group][name] = cleaned;
|
|
489
|
+
}
|
|
490
|
+
if (Object.keys(variants).length > 0) {
|
|
491
|
+
json.variants = variants;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
return json;
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* ResolvedVisualProperties を JSON 出力用に整形する。
|
|
498
|
+
* - undefined フィールドを除外
|
|
499
|
+
* - boxShadow 文字列の代わりに boxShadowLayers 配列を使用
|
|
500
|
+
* - padding を統合フィールドに正規化
|
|
501
|
+
*/
|
|
502
|
+
function propsToJson(props) {
|
|
503
|
+
const result = {};
|
|
504
|
+
if (props.display)
|
|
505
|
+
result.display = props.display;
|
|
506
|
+
if (props.width)
|
|
507
|
+
result.width = props.width;
|
|
508
|
+
if (props.height)
|
|
509
|
+
result.height = props.height;
|
|
510
|
+
if (props.aspectRatio)
|
|
511
|
+
result.aspectRatio = props.aspectRatio;
|
|
512
|
+
if (props.overflow)
|
|
513
|
+
result.overflow = props.overflow;
|
|
514
|
+
if (props.borderRadius)
|
|
515
|
+
result.borderRadius = props.borderRadius;
|
|
516
|
+
if (props.border) {
|
|
517
|
+
result.border = {
|
|
518
|
+
width: props.border.width,
|
|
519
|
+
style: props.borderStyle ?? (props.border.color ? "solid" : undefined),
|
|
520
|
+
color: props.border.color || undefined,
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
else if (props.borderStyle) {
|
|
524
|
+
result.borderStyle = props.borderStyle;
|
|
525
|
+
}
|
|
526
|
+
if (props.backgroundColor)
|
|
527
|
+
result.backgroundColor = props.backgroundColor;
|
|
528
|
+
if (props.color)
|
|
529
|
+
result.color = props.color;
|
|
530
|
+
if (props.fontWeight)
|
|
531
|
+
result.fontWeight = props.fontWeight;
|
|
532
|
+
if (props.fontSize)
|
|
533
|
+
result.fontSize = props.fontSize;
|
|
534
|
+
if (props.fontFamily)
|
|
535
|
+
result.fontFamily = props.fontFamily;
|
|
536
|
+
if (props.letterSpacing)
|
|
537
|
+
result.letterSpacing = props.letterSpacing;
|
|
538
|
+
if (props.objectFit)
|
|
539
|
+
result.objectFit = props.objectFit;
|
|
540
|
+
if (props.alignItems)
|
|
541
|
+
result.alignItems = props.alignItems;
|
|
542
|
+
if (props.justifyContent)
|
|
543
|
+
result.justifyContent = props.justifyContent;
|
|
544
|
+
if (props.opacity)
|
|
545
|
+
result.opacity = props.opacity;
|
|
546
|
+
if (props.flexShrink)
|
|
547
|
+
result.flexShrink = props.flexShrink;
|
|
548
|
+
if (props.gap)
|
|
549
|
+
result.gap = props.gap;
|
|
550
|
+
// boxShadow: 構造化配列を boxShadowLayers キーで出力
|
|
551
|
+
if (props.boxShadowLayers && props.boxShadowLayers.length > 0) {
|
|
552
|
+
result.boxShadowLayers = props.boxShadowLayers;
|
|
553
|
+
}
|
|
554
|
+
else if (props.boxShadow) {
|
|
555
|
+
result.boxShadow = props.boxShadow;
|
|
556
|
+
}
|
|
557
|
+
// padding: 常にオブジェクト形式 {top, right, bottom, left} に統一
|
|
558
|
+
{
|
|
559
|
+
const p = {};
|
|
560
|
+
if (props.padding) {
|
|
561
|
+
p.top = props.padding;
|
|
562
|
+
p.right = props.padding;
|
|
563
|
+
p.bottom = props.padding;
|
|
564
|
+
p.left = props.padding;
|
|
565
|
+
}
|
|
566
|
+
if (props.paddingX) {
|
|
567
|
+
p.left = props.paddingX;
|
|
568
|
+
p.right = props.paddingX;
|
|
569
|
+
}
|
|
570
|
+
if (props.paddingY) {
|
|
571
|
+
p.top = props.paddingY;
|
|
572
|
+
p.bottom = props.paddingY;
|
|
573
|
+
}
|
|
574
|
+
if (props.paddingTop)
|
|
575
|
+
p.top = props.paddingTop;
|
|
576
|
+
if (props.paddingBottom)
|
|
577
|
+
p.bottom = props.paddingBottom;
|
|
578
|
+
if (props.paddingLeft)
|
|
579
|
+
p.left = props.paddingLeft;
|
|
580
|
+
if (props.paddingRight)
|
|
581
|
+
p.right = props.paddingRight;
|
|
582
|
+
if (Object.keys(p).length > 0)
|
|
583
|
+
result.padding = p;
|
|
584
|
+
}
|
|
585
|
+
// gradient
|
|
586
|
+
if (props.gradient)
|
|
587
|
+
result.background = props.gradient;
|
|
588
|
+
return result;
|
|
589
|
+
}
|
|
461
590
|
/** テスト用にキャッシュをクリアする */
|
|
462
591
|
export function clearRegistryCache() {
|
|
463
592
|
cache = new WeakMap();
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
export interface BoxShadowLayer {
|
|
2
|
+
type: "drop" | "inset";
|
|
3
|
+
x: number;
|
|
4
|
+
y: number;
|
|
5
|
+
blur: number;
|
|
6
|
+
spread: number;
|
|
7
|
+
color: string;
|
|
8
|
+
}
|
|
1
9
|
export interface ResolvedVisualProperties {
|
|
2
10
|
width?: string;
|
|
3
11
|
height?: string;
|
|
@@ -18,6 +26,7 @@ export interface ResolvedVisualProperties {
|
|
|
18
26
|
justifyContent?: string;
|
|
19
27
|
opacity?: string;
|
|
20
28
|
boxShadow?: string;
|
|
29
|
+
boxShadowLayers?: BoxShadowLayer[];
|
|
21
30
|
padding?: string;
|
|
22
31
|
paddingX?: string;
|
|
23
32
|
paddingY?: string;
|
|
@@ -33,3 +42,8 @@ export interface ResolvedVisualProperties {
|
|
|
33
42
|
gradient?: string;
|
|
34
43
|
}
|
|
35
44
|
export declare function resolveVisualProperties(classes: string, resolvedColors: Record<string, unknown>): ResolvedVisualProperties;
|
|
45
|
+
/**
|
|
46
|
+
* CSS box-shadow 文字列を構造化された BoxShadowLayer 配列にパースする。
|
|
47
|
+
* 複数レイヤー(カンマ区切り)、inset キーワード、rgba/rgb カラーに対応。
|
|
48
|
+
*/
|
|
49
|
+
export declare function parseBoxShadow(shadow: string): BoxShadowLayer[];
|
|
@@ -628,5 +628,94 @@ export function resolveVisualProperties(classes, resolvedColors) {
|
|
|
628
628
|
if (result.gradient && !result.gradient.endsWith(")")) {
|
|
629
629
|
result.gradient = `${result.gradient})`;
|
|
630
630
|
}
|
|
631
|
+
// boxShadow 文字列を構造化配列にパース
|
|
632
|
+
if (result.boxShadow && result.boxShadow !== "none") {
|
|
633
|
+
result.boxShadowLayers = parseBoxShadow(result.boxShadow);
|
|
634
|
+
}
|
|
631
635
|
return result;
|
|
632
636
|
}
|
|
637
|
+
/**
|
|
638
|
+
* CSS box-shadow 文字列を構造化された BoxShadowLayer 配列にパースする。
|
|
639
|
+
* 複数レイヤー(カンマ区切り)、inset キーワード、rgba/rgb カラーに対応。
|
|
640
|
+
*/
|
|
641
|
+
export function parseBoxShadow(shadow) {
|
|
642
|
+
if (!shadow || shadow === "none")
|
|
643
|
+
return [];
|
|
644
|
+
const layers = [];
|
|
645
|
+
// カンマで分割するが、rgba()/rgb() 内のカンマは無視する
|
|
646
|
+
const rawLayers = splitShadowLayers(shadow);
|
|
647
|
+
for (const raw of rawLayers) {
|
|
648
|
+
const trimmed = raw.trim();
|
|
649
|
+
if (!trimmed)
|
|
650
|
+
continue;
|
|
651
|
+
const isInset = trimmed.startsWith("inset ");
|
|
652
|
+
const withoutInset = isInset ? trimmed.slice(6).trim() : trimmed;
|
|
653
|
+
// 色部分を抽出(rgba(...), rgb(...), #hex, named color)
|
|
654
|
+
let color = "";
|
|
655
|
+
let numbersStr = withoutInset;
|
|
656
|
+
// rgba/rgb 関数を先に抽出
|
|
657
|
+
const rgbaMatch = withoutInset.match(/(rgba?\([^)]+\))/);
|
|
658
|
+
if (rgbaMatch) {
|
|
659
|
+
color = rgbaMatch[1];
|
|
660
|
+
numbersStr = withoutInset.replace(color, "").trim();
|
|
661
|
+
}
|
|
662
|
+
else {
|
|
663
|
+
// #hex or named color — 末尾にあると仮定
|
|
664
|
+
const hexMatch = withoutInset.match(/(#[0-9a-fA-F]{3,8})\s*$/);
|
|
665
|
+
if (hexMatch) {
|
|
666
|
+
color = hexMatch[1];
|
|
667
|
+
numbersStr = withoutInset.slice(0, hexMatch.index).trim();
|
|
668
|
+
}
|
|
669
|
+
else {
|
|
670
|
+
// css variable like var(--foreground)
|
|
671
|
+
const varMatch = withoutInset.match(/(var\([^)]+\))\s*$/);
|
|
672
|
+
if (varMatch) {
|
|
673
|
+
color = varMatch[1];
|
|
674
|
+
numbersStr = withoutInset.slice(0, varMatch.index).trim();
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
// 数値部分をパース(x, y, blur, spread)
|
|
679
|
+
const numTokens = numbersStr
|
|
680
|
+
.split(/\s+/)
|
|
681
|
+
.filter((t) => /^-?\d/.test(t))
|
|
682
|
+
.map((t) => Number.parseFloat(t));
|
|
683
|
+
const x = numTokens[0] ?? 0;
|
|
684
|
+
const y = numTokens[1] ?? 0;
|
|
685
|
+
const blur = numTokens[2] ?? 0;
|
|
686
|
+
const spread = numTokens[3] ?? 0;
|
|
687
|
+
layers.push({
|
|
688
|
+
type: isInset ? "inset" : "drop",
|
|
689
|
+
x,
|
|
690
|
+
y,
|
|
691
|
+
blur,
|
|
692
|
+
spread,
|
|
693
|
+
color: color || "rgba(0,0,0,0)",
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
return layers;
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* box-shadow のカンマ区切りを括弧内のカンマを考慮して分割する。
|
|
700
|
+
*/
|
|
701
|
+
function splitShadowLayers(shadow) {
|
|
702
|
+
const layers = [];
|
|
703
|
+
let depth = 0;
|
|
704
|
+
let current = "";
|
|
705
|
+
for (const ch of shadow) {
|
|
706
|
+
if (ch === "(")
|
|
707
|
+
depth++;
|
|
708
|
+
else if (ch === ")")
|
|
709
|
+
depth--;
|
|
710
|
+
if (ch === "," && depth === 0) {
|
|
711
|
+
layers.push(current);
|
|
712
|
+
current = "";
|
|
713
|
+
}
|
|
714
|
+
else {
|
|
715
|
+
current += ch;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
if (current.trim())
|
|
719
|
+
layers.push(current);
|
|
720
|
+
return layers;
|
|
721
|
+
}
|