figrelay 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/bin/darwin-amd64/figma-mcp +0 -0
- package/bin/darwin-arm64/figma-mcp +0 -0
- package/bin/linux-amd64/figma-mcp +0 -0
- package/bin/linux-arm64/figma-mcp +0 -0
- package/bin/plugin/code.js +65 -59
- package/bin/plugin/ui.html +49 -42
- package/bin/windows-amd64/figma-mcp.exe +0 -0
- package/bin/windows-arm64/figma-mcp.exe +0 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ Needs [Node.js](https://nodejs.org) 18+ and the **Figma Desktop** app (the web a
|
|
|
18
18
|
```json
|
|
19
19
|
{
|
|
20
20
|
"mcpServers": {
|
|
21
|
-
"figma-
|
|
21
|
+
"figma-relay": {
|
|
22
22
|
"command": "npx",
|
|
23
23
|
"args": ["-y", "figrelay@latest"]
|
|
24
24
|
}
|
|
@@ -28,9 +28,9 @@ Needs [Node.js](https://nodejs.org) 18+ and the **Figma Desktop** app (the web a
|
|
|
28
28
|
|
|
29
29
|
| Client | Where the config lives |
|
|
30
30
|
|---|---|
|
|
31
|
-
| Claude CLI | `claude mcp add figma-
|
|
31
|
+
| Claude CLI | `claude mcp add figma-relay -- npx -y figrelay@latest`, or `.mcp.json` |
|
|
32
32
|
| Claude Desktop | Settings → Developer → Edit Config (`claude_desktop_config.json`) |
|
|
33
|
-
| Codex CLI | `codex mcp add figma-
|
|
33
|
+
| Codex CLI | `codex mcp add figma-relay -- npx -y figrelay@latest`, or `~/.codex/config.toml` (TOML) |
|
|
34
34
|
| VS Code (Copilot) | `.vscode/mcp.json` — **different shape**, see the doc |
|
|
35
35
|
| Cursor | `.cursor/mcp.json` |
|
|
36
36
|
| Antigravity | MCP Servers → Manage → View raw config (`mcp_config.json`) |
|
|
@@ -64,7 +64,7 @@ The tools tell the AI *what it can call*; the skills tell it *how to do the job
|
|
|
64
64
|
Every listed tool costs context, even in sessions that never touch Figma. `FIGMA_MCP_DISABLE` (or the `-disable` flag) hides whole groups: `components`, `styles`, `variables`, `prototype`. Hiding all four cuts the tool list by about a third.
|
|
65
65
|
|
|
66
66
|
```json
|
|
67
|
-
"figma-
|
|
67
|
+
"figma-relay": {
|
|
68
68
|
"command": "npx",
|
|
69
69
|
"args": ["-y", "figrelay@latest"],
|
|
70
70
|
"env": { "FIGMA_MCP_DISABLE": "variables,prototype" }
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/bin/plugin/code.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Receives {id, command, params} from the Go bridge (relayed through ui.html),
|
|
3
3
|
// executes it against the Figma Plugin API, and replies {id, result|error}.
|
|
4
4
|
|
|
5
|
-
figma.showUI(__html__, { width: 280, height: 104 });
|
|
5
|
+
figma.showUI(__html__, { width: 280, height: 104, themeColors: true });
|
|
6
6
|
|
|
7
7
|
// The UI can collapse to a dot; selection changes must not resize it back.
|
|
8
8
|
let collapsed = false;
|
|
@@ -197,10 +197,18 @@ function serialize(n, depth, compact) {
|
|
|
197
197
|
return s;
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
+
function has(x) {
|
|
201
|
+
return x !== undefined && x !== null;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function need(n, prop, what, id = n.id) {
|
|
205
|
+
if (!(prop in n)) throw new Error("node " + id + " " + what);
|
|
206
|
+
}
|
|
207
|
+
|
|
200
208
|
async function parentOf(params) {
|
|
201
209
|
if (params.parent_id) {
|
|
202
210
|
const p = await node(params.parent_id);
|
|
203
|
-
|
|
211
|
+
need(p, "appendChild", "cannot have children", params.parent_id);
|
|
204
212
|
return p;
|
|
205
213
|
}
|
|
206
214
|
return figma.currentPage;
|
|
@@ -213,7 +221,7 @@ function buildGradient(g) {
|
|
|
213
221
|
const c = hexToRGB(s.color);
|
|
214
222
|
return {
|
|
215
223
|
position: s.position,
|
|
216
|
-
color: { r: c.r, g: c.g, b: c.b, a: s.opacity
|
|
224
|
+
color: { r: c.r, g: c.g, b: c.b, a: has(s.opacity) ? s.opacity : 1 },
|
|
217
225
|
};
|
|
218
226
|
});
|
|
219
227
|
if (stops.length < 2) throw new Error("gradient needs at least 2 stops");
|
|
@@ -249,7 +257,7 @@ function buildEffects(list) {
|
|
|
249
257
|
throw new Error("unsupported effect type: " + type);
|
|
250
258
|
}
|
|
251
259
|
const color = hexToRGB(e.color || "#000000");
|
|
252
|
-
color.a = e.color_opacity
|
|
260
|
+
color.a = has(e.color_opacity) ? e.color_opacity : 0.25;
|
|
253
261
|
return {
|
|
254
262
|
type,
|
|
255
263
|
color,
|
|
@@ -272,6 +280,12 @@ function applyShapeParams(n, params) {
|
|
|
272
280
|
}
|
|
273
281
|
}
|
|
274
282
|
|
|
283
|
+
async function makeShape(n, params) {
|
|
284
|
+
applyShapeParams(n, params);
|
|
285
|
+
(await parentOf(params)).appendChild(n);
|
|
286
|
+
return summarize(n);
|
|
287
|
+
}
|
|
288
|
+
|
|
275
289
|
// Tracking the nodes a script creates needs a wrapper around figma, but the
|
|
276
290
|
// host object's properties are non-configurable and non-writable, so a Proxy
|
|
277
291
|
// get trap that returns anything else (even a bound copy) breaks the Proxy
|
|
@@ -388,7 +402,7 @@ function buildTransition(item) {
|
|
|
388
402
|
const t = {
|
|
389
403
|
type,
|
|
390
404
|
easing: { type: (item.easing || "EASE_OUT").toUpperCase() },
|
|
391
|
-
duration: item.duration
|
|
405
|
+
duration: has(item.duration) ? item.duration : 0.3,
|
|
392
406
|
};
|
|
393
407
|
if (directional) {
|
|
394
408
|
t.direction = (item.direction || "LEFT").toUpperCase();
|
|
@@ -485,23 +499,15 @@ const handlers = {
|
|
|
485
499
|
// Figma gives new frames an opaque white fill, which silently covers
|
|
486
500
|
// whatever sits behind a nested frame. Transparent unless asked.
|
|
487
501
|
if (!params.fill_color || params.fill_color === "none") f.fills = [];
|
|
488
|
-
|
|
489
|
-
(await parentOf(params)).appendChild(f);
|
|
490
|
-
return summarize(f);
|
|
502
|
+
return makeShape(f, params);
|
|
491
503
|
},
|
|
492
504
|
|
|
493
505
|
async create_rectangle(params) {
|
|
494
|
-
|
|
495
|
-
applyShapeParams(r, params);
|
|
496
|
-
(await parentOf(params)).appendChild(r);
|
|
497
|
-
return summarize(r);
|
|
506
|
+
return makeShape(figma.createRectangle(), params);
|
|
498
507
|
},
|
|
499
508
|
|
|
500
509
|
async create_ellipse(params) {
|
|
501
|
-
|
|
502
|
-
applyShapeParams(e, params);
|
|
503
|
-
(await parentOf(params)).appendChild(e);
|
|
504
|
-
return summarize(e);
|
|
510
|
+
return makeShape(figma.createEllipse(), params);
|
|
505
511
|
},
|
|
506
512
|
|
|
507
513
|
async create_line(params) {
|
|
@@ -511,7 +517,7 @@ const handlers = {
|
|
|
511
517
|
l.resize(params.length > 0 ? params.length : 100, 0);
|
|
512
518
|
l.strokes = [{ type: "SOLID", color: hexToRGB(params.stroke_color || "#000000") }];
|
|
513
519
|
if (params.stroke_weight) l.strokeWeight = params.stroke_weight;
|
|
514
|
-
if (params.rotation
|
|
520
|
+
if (has(params.rotation)) l.rotation = params.rotation;
|
|
515
521
|
if (params.name) l.name = params.name;
|
|
516
522
|
(await parentOf(params)).appendChild(l);
|
|
517
523
|
return summarize(l);
|
|
@@ -523,7 +529,7 @@ const handlers = {
|
|
|
523
529
|
let n;
|
|
524
530
|
if (params.node_id) {
|
|
525
531
|
n = await node(params.node_id);
|
|
526
|
-
|
|
532
|
+
need(n, "fills", "has no fills", params.node_id);
|
|
527
533
|
n.fills = [paint];
|
|
528
534
|
} else {
|
|
529
535
|
const size = await image.getSizeAsync();
|
|
@@ -549,7 +555,7 @@ const handlers = {
|
|
|
549
555
|
if (params.font_size) t.fontSize = params.font_size;
|
|
550
556
|
if (params.fill_color) t.fills = [{ type: "SOLID", color: hexToRGB(params.fill_color) }];
|
|
551
557
|
if (params.line_height) t.lineHeight = { value: params.line_height, unit: "PIXELS" };
|
|
552
|
-
if (params.letter_spacing
|
|
558
|
+
if (has(params.letter_spacing)) {
|
|
553
559
|
t.letterSpacing = { value: params.letter_spacing, unit: "PIXELS" };
|
|
554
560
|
}
|
|
555
561
|
if (params.text_align) t.textAlignHorizontal = params.text_align;
|
|
@@ -589,10 +595,10 @@ const handlers = {
|
|
|
589
595
|
} else {
|
|
590
596
|
throw new Error('set_fills needs color, gradient, or color: "none" to clear');
|
|
591
597
|
}
|
|
592
|
-
if (params.opacity
|
|
598
|
+
if (has(params.opacity)) paint.opacity = params.opacity;
|
|
593
599
|
}
|
|
594
600
|
for (const n of nodes) {
|
|
595
|
-
|
|
601
|
+
need(n, "fills", "has no fills");
|
|
596
602
|
n.fills = clear ? [] : [paint];
|
|
597
603
|
}
|
|
598
604
|
return done(params, nodes);
|
|
@@ -602,7 +608,7 @@ const handlers = {
|
|
|
602
608
|
const out = [];
|
|
603
609
|
for (const item of params.items) {
|
|
604
610
|
const n = await node(item.node_id);
|
|
605
|
-
|
|
611
|
+
need(n, "x", "cannot be moved", item.node_id);
|
|
606
612
|
n.x = item.x;
|
|
607
613
|
n.y = item.y;
|
|
608
614
|
out.push(n);
|
|
@@ -614,7 +620,7 @@ const handlers = {
|
|
|
614
620
|
const out = [];
|
|
615
621
|
for (const item of params.items) {
|
|
616
622
|
const n = await node(item.node_id);
|
|
617
|
-
|
|
623
|
+
need(n, "resize", "cannot be resized", item.node_id);
|
|
618
624
|
n.resize(item.width, item.height);
|
|
619
625
|
out.push(n);
|
|
620
626
|
}
|
|
@@ -633,17 +639,17 @@ const handlers = {
|
|
|
633
639
|
|
|
634
640
|
async set_auto_layout(params) {
|
|
635
641
|
const n = await node(params.node_id);
|
|
636
|
-
|
|
642
|
+
need(n, "layoutMode", "does not support auto-layout", params.node_id);
|
|
637
643
|
n.layoutMode = params.layout_mode;
|
|
638
644
|
if (params.layout_mode === "NONE") return done(params, n);
|
|
639
|
-
if (params.item_spacing
|
|
640
|
-
if (params.padding
|
|
645
|
+
if (has(params.item_spacing)) n.itemSpacing = params.item_spacing;
|
|
646
|
+
if (has(params.padding)) {
|
|
641
647
|
n.paddingTop = n.paddingRight = n.paddingBottom = n.paddingLeft = params.padding;
|
|
642
648
|
}
|
|
643
|
-
if (params.padding_top
|
|
644
|
-
if (params.padding_right
|
|
645
|
-
if (params.padding_bottom
|
|
646
|
-
if (params.padding_left
|
|
649
|
+
if (has(params.padding_top)) n.paddingTop = params.padding_top;
|
|
650
|
+
if (has(params.padding_right)) n.paddingRight = params.padding_right;
|
|
651
|
+
if (has(params.padding_bottom)) n.paddingBottom = params.padding_bottom;
|
|
652
|
+
if (has(params.padding_left)) n.paddingLeft = params.padding_left;
|
|
647
653
|
if (params.primary_axis_align) n.primaryAxisAlignItems = params.primary_axis_align;
|
|
648
654
|
if (params.counter_axis_align) n.counterAxisAlignItems = params.counter_axis_align;
|
|
649
655
|
if (params.primary_axis_sizing) n.primaryAxisSizingMode = params.primary_axis_sizing;
|
|
@@ -654,18 +660,18 @@ const handlers = {
|
|
|
654
660
|
async set_corner_radius(params) {
|
|
655
661
|
const nodes = await targets(params);
|
|
656
662
|
for (const n of nodes) {
|
|
657
|
-
if (params.radius
|
|
658
|
-
|
|
663
|
+
if (has(params.radius)) {
|
|
664
|
+
need(n, "cornerRadius", "has no corner radius");
|
|
659
665
|
n.cornerRadius = params.radius;
|
|
660
666
|
}
|
|
661
667
|
if (!("topLeftRadius" in n)) {
|
|
662
|
-
if (params.radius
|
|
668
|
+
if (!has(params.radius)) throw new Error("node " + n.id + " has no per-corner radius");
|
|
663
669
|
continue;
|
|
664
670
|
}
|
|
665
|
-
if (params.top_left
|
|
666
|
-
if (params.top_right
|
|
667
|
-
if (params.bottom_right
|
|
668
|
-
if (params.bottom_left
|
|
671
|
+
if (has(params.top_left)) n.topLeftRadius = params.top_left;
|
|
672
|
+
if (has(params.top_right)) n.topRightRadius = params.top_right;
|
|
673
|
+
if (has(params.bottom_right)) n.bottomRightRadius = params.bottom_right;
|
|
674
|
+
if (has(params.bottom_left)) n.bottomLeftRadius = params.bottom_left;
|
|
669
675
|
}
|
|
670
676
|
return done(params, nodes);
|
|
671
677
|
},
|
|
@@ -673,15 +679,15 @@ const handlers = {
|
|
|
673
679
|
async set_strokes(params) {
|
|
674
680
|
const nodes = await targets(params);
|
|
675
681
|
for (const n of nodes) {
|
|
676
|
-
|
|
682
|
+
need(n, "strokes", "has no strokes");
|
|
677
683
|
if (params.color && params.color !== "none") {
|
|
678
684
|
const paint = { type: "SOLID", color: hexToRGB(params.color) };
|
|
679
|
-
if (params.opacity
|
|
685
|
+
if (has(params.opacity)) paint.opacity = params.opacity;
|
|
680
686
|
n.strokes = [paint];
|
|
681
687
|
} else {
|
|
682
688
|
n.strokes = [];
|
|
683
689
|
}
|
|
684
|
-
if (params.weight
|
|
690
|
+
if (has(params.weight) && "strokeWeight" in n) n.strokeWeight = params.weight;
|
|
685
691
|
if (params.align && "strokeAlign" in n) n.strokeAlign = params.align;
|
|
686
692
|
}
|
|
687
693
|
return done(params, nodes);
|
|
@@ -691,7 +697,7 @@ const handlers = {
|
|
|
691
697
|
const nodes = await targets(params);
|
|
692
698
|
const effects = buildEffects(params.effects);
|
|
693
699
|
for (const n of nodes) {
|
|
694
|
-
|
|
700
|
+
need(n, "effects", "has no effects");
|
|
695
701
|
n.effects = effects;
|
|
696
702
|
}
|
|
697
703
|
return done(params, nodes);
|
|
@@ -729,10 +735,10 @@ const handlers = {
|
|
|
729
735
|
n.fontName = font;
|
|
730
736
|
}
|
|
731
737
|
if (params.font_size) n.fontSize = params.font_size;
|
|
732
|
-
if (params.line_height
|
|
738
|
+
if (has(params.line_height)) {
|
|
733
739
|
n.lineHeight = { value: params.line_height, unit: "PIXELS" };
|
|
734
740
|
}
|
|
735
|
-
if (params.letter_spacing
|
|
741
|
+
if (has(params.letter_spacing)) {
|
|
736
742
|
n.letterSpacing = { value: params.letter_spacing, unit: "PIXELS" };
|
|
737
743
|
}
|
|
738
744
|
if (params.text_align) n.textAlignHorizontal = params.text_align;
|
|
@@ -748,7 +754,7 @@ const handlers = {
|
|
|
748
754
|
for (const item of params.items) {
|
|
749
755
|
try {
|
|
750
756
|
const target = await node(item.node_id);
|
|
751
|
-
|
|
757
|
+
need(target, "exportAsync", "cannot be exported", item.node_id);
|
|
752
758
|
const format = (item.format || "PNG").toUpperCase();
|
|
753
759
|
let settings;
|
|
754
760
|
if (format === "SVG") {
|
|
@@ -787,7 +793,7 @@ const handlers = {
|
|
|
787
793
|
if (params.node_id) scope = await node(params.node_id);
|
|
788
794
|
else if (params.page) scope = await resolvePage(params.page);
|
|
789
795
|
else scope = figma.currentPage;
|
|
790
|
-
|
|
796
|
+
need(scope, "findAll", "has no children to search");
|
|
791
797
|
const name = params.name ? params.name.toLowerCase() : null;
|
|
792
798
|
const text = params.text ? params.text.toLowerCase() : null;
|
|
793
799
|
const types = params.types && params.types.length ? params.types : null;
|
|
@@ -804,7 +810,7 @@ const handlers = {
|
|
|
804
810
|
const hex = solidFillHex(n);
|
|
805
811
|
if (!hex || hex.toLowerCase() !== fill) return false;
|
|
806
812
|
}
|
|
807
|
-
if (fontSize
|
|
813
|
+
if (has(fontSize)) {
|
|
808
814
|
if (n.type !== "TEXT" || val(n.fontSize) !== fontSize) return false;
|
|
809
815
|
}
|
|
810
816
|
if (fontStyle) {
|
|
@@ -858,7 +864,7 @@ const handlers = {
|
|
|
858
864
|
|
|
859
865
|
async append_children(params) {
|
|
860
866
|
const parent = await node(params.parent_id);
|
|
861
|
-
|
|
867
|
+
need(parent, "appendChild", "cannot have children", params.parent_id);
|
|
862
868
|
const moved = [];
|
|
863
869
|
const warnings = [];
|
|
864
870
|
let index = params.index;
|
|
@@ -872,7 +878,7 @@ const handlers = {
|
|
|
872
878
|
"; Figma renames detached variants to <Set>/<Variant>",
|
|
873
879
|
);
|
|
874
880
|
}
|
|
875
|
-
if (index
|
|
881
|
+
if (!has(index)) {
|
|
876
882
|
parent.appendChild(n);
|
|
877
883
|
} else {
|
|
878
884
|
parent.insertChild(index++, n);
|
|
@@ -884,15 +890,15 @@ const handlers = {
|
|
|
884
890
|
|
|
885
891
|
async clone_node(params) {
|
|
886
892
|
const n = await node(params.node_id);
|
|
887
|
-
|
|
893
|
+
need(n, "clone", "cannot be cloned", params.node_id);
|
|
888
894
|
const copy = n.clone();
|
|
889
895
|
if (params.parent_id) {
|
|
890
896
|
const p = await node(params.parent_id);
|
|
891
|
-
|
|
897
|
+
need(p, "appendChild", "cannot have children", params.parent_id);
|
|
892
898
|
p.appendChild(copy);
|
|
893
899
|
}
|
|
894
|
-
if (params.x
|
|
895
|
-
if (params.y
|
|
900
|
+
if (has(params.x)) copy.x = params.x;
|
|
901
|
+
if (has(params.y)) copy.y = params.y;
|
|
896
902
|
if (params.name) copy.name = params.name;
|
|
897
903
|
return summarize(copy);
|
|
898
904
|
},
|
|
@@ -943,8 +949,8 @@ const handlers = {
|
|
|
943
949
|
const c = await node(params.component_id);
|
|
944
950
|
if (c.type !== "COMPONENT") throw new Error("node " + params.component_id + " is " + c.type + ", not a COMPONENT");
|
|
945
951
|
const inst = c.createInstance();
|
|
946
|
-
if (params.x
|
|
947
|
-
if (params.y
|
|
952
|
+
if (has(params.x)) inst.x = params.x;
|
|
953
|
+
if (has(params.y)) inst.y = params.y;
|
|
948
954
|
(await parentOf(params)).appendChild(inst);
|
|
949
955
|
return summarize(inst);
|
|
950
956
|
},
|
|
@@ -973,7 +979,7 @@ const handlers = {
|
|
|
973
979
|
// Figma requires a default of the matching kind; pick the empty one
|
|
974
980
|
// rather than failing a call that only omitted it.
|
|
975
981
|
let def = params.default_value;
|
|
976
|
-
if (def
|
|
982
|
+
if (!has(def)) def = type === "BOOLEAN" ? false : "";
|
|
977
983
|
const key = c.addComponentProperty(params.name, type, def);
|
|
978
984
|
if (params.bind_node_id) {
|
|
979
985
|
const t = await node(params.bind_node_id);
|
|
@@ -1036,7 +1042,7 @@ const handlers = {
|
|
|
1036
1042
|
const s = figma.createPaintStyle();
|
|
1037
1043
|
s.name = params.name;
|
|
1038
1044
|
const paint = { type: "SOLID", color: hexToRGB(params.color) };
|
|
1039
|
-
if (params.opacity
|
|
1045
|
+
if (has(params.opacity)) paint.opacity = params.opacity;
|
|
1040
1046
|
s.paints = [paint];
|
|
1041
1047
|
return { id: s.id, name: s.name };
|
|
1042
1048
|
},
|
|
@@ -1130,7 +1136,7 @@ const handlers = {
|
|
|
1130
1136
|
const c = await figma.variables.getVariableCollectionByIdAsync(params.collection_id);
|
|
1131
1137
|
if (!c) throw new Error("variable collection not found: " + params.collection_id);
|
|
1132
1138
|
const v = figma.variables.createVariable(params.name, c, params.type);
|
|
1133
|
-
if (params.value
|
|
1139
|
+
if (has(params.value)) {
|
|
1134
1140
|
v.setValueForMode(c.defaultModeId, variableValue(v, params.value));
|
|
1135
1141
|
}
|
|
1136
1142
|
return { id: v.id, name: v.name, resolvedType: v.resolvedType, collectionId: c.id };
|
|
@@ -1152,7 +1158,7 @@ const handlers = {
|
|
|
1152
1158
|
const field = params.field;
|
|
1153
1159
|
for (const n of nodes) {
|
|
1154
1160
|
if (field === "fills" || field === "strokes") {
|
|
1155
|
-
|
|
1161
|
+
need(n, field, "has no " + field);
|
|
1156
1162
|
const paints = n[field].length ? n[field] : [{ type: "SOLID", color: { r: 0, g: 0, b: 0 } }];
|
|
1157
1163
|
n[field] = [figma.variables.setBoundVariableForPaint(paints[0], "color", v)].concat(paints.slice(1));
|
|
1158
1164
|
} else {
|
|
@@ -1270,7 +1276,7 @@ const handlers = {
|
|
|
1270
1276
|
} else {
|
|
1271
1277
|
target = figma.currentPage;
|
|
1272
1278
|
}
|
|
1273
|
-
|
|
1279
|
+
need(target, "exportAsync", "cannot be exported");
|
|
1274
1280
|
// With dynamic-page access a node can be reachable before its page is
|
|
1275
1281
|
// loaded, and exporting then yields a blank image with no error.
|
|
1276
1282
|
let owner = target;
|
package/bin/plugin/ui.html
CHANGED
|
@@ -16,15 +16,19 @@
|
|
|
16
16
|
sans-serif;
|
|
17
17
|
margin: 0;
|
|
18
18
|
padding: 10px;
|
|
19
|
-
color: #2c2c34;
|
|
20
|
-
background: linear-gradient(
|
|
19
|
+
color: var(--figma-color-text, #2c2c34);
|
|
20
|
+
background: linear-gradient(
|
|
21
|
+
160deg,
|
|
22
|
+
var(--figma-color-bg-secondary, #f6f7fb) 0%,
|
|
23
|
+
var(--figma-color-bg-secondary, #eef0f7) 100%
|
|
24
|
+
);
|
|
21
25
|
display: flex;
|
|
22
26
|
flex-direction: column;
|
|
23
27
|
}
|
|
24
28
|
|
|
25
29
|
.card {
|
|
26
|
-
background: #fff;
|
|
27
|
-
border: 1px solid #e6e8f0;
|
|
30
|
+
background: var(--figma-color-bg, #fff);
|
|
31
|
+
border: 1px solid var(--figma-color-border, #e6e8f0);
|
|
28
32
|
border-radius: 9px;
|
|
29
33
|
padding: 7px 9px;
|
|
30
34
|
box-shadow: 0 1px 2px rgba(30, 34, 60, 0.04);
|
|
@@ -37,7 +41,7 @@
|
|
|
37
41
|
cursor: pointer;
|
|
38
42
|
font-weight: 600;
|
|
39
43
|
font-size: 13px;
|
|
40
|
-
color: #1f2330;
|
|
44
|
+
color: var(--figma-color-text, #1f2330);
|
|
41
45
|
}
|
|
42
46
|
|
|
43
47
|
#dot {
|
|
@@ -45,7 +49,7 @@
|
|
|
45
49
|
width: 9px;
|
|
46
50
|
height: 9px;
|
|
47
51
|
border-radius: 50%;
|
|
48
|
-
background: #e0555b;
|
|
52
|
+
background: var(--figma-color-icon-danger, #e0555b);
|
|
49
53
|
box-shadow: 0 0 0 3px rgba(224, 85, 91, 0.15);
|
|
50
54
|
transition:
|
|
51
55
|
background 0.2s,
|
|
@@ -53,7 +57,7 @@
|
|
|
53
57
|
}
|
|
54
58
|
|
|
55
59
|
#dot.on {
|
|
56
|
-
background: #22a06b;
|
|
60
|
+
background: var(--figma-color-icon-success, #22a06b);
|
|
57
61
|
box-shadow: 0 0 0 3px rgba(34, 160, 107, 0.15);
|
|
58
62
|
}
|
|
59
63
|
|
|
@@ -65,15 +69,15 @@
|
|
|
65
69
|
border: 0;
|
|
66
70
|
border-radius: 5px;
|
|
67
71
|
background: transparent;
|
|
68
|
-
color: #8288a0;
|
|
72
|
+
color: var(--figma-color-text-secondary, #8288a0);
|
|
69
73
|
font-size: 13px;
|
|
70
74
|
line-height: 1;
|
|
71
75
|
cursor: pointer;
|
|
72
76
|
}
|
|
73
77
|
|
|
74
78
|
#collapse:hover {
|
|
75
|
-
background: #eef0f7;
|
|
76
|
-
color: #2c2c34;
|
|
79
|
+
background: var(--figma-color-bg-hover, #eef0f7);
|
|
80
|
+
color: var(--figma-color-text, #2c2c34);
|
|
77
81
|
}
|
|
78
82
|
|
|
79
83
|
/* Collapsed: the dot only, at Figma's 70px minimum width. */
|
|
@@ -96,7 +100,7 @@
|
|
|
96
100
|
}
|
|
97
101
|
|
|
98
102
|
#hint {
|
|
99
|
-
color: #6b6f7d;
|
|
103
|
+
color: var(--figma-color-text-secondary, #6b6f7d);
|
|
100
104
|
margin-top: 5px;
|
|
101
105
|
font-size: 10px;
|
|
102
106
|
line-height: 1.35;
|
|
@@ -105,7 +109,7 @@
|
|
|
105
109
|
#result {
|
|
106
110
|
display: none;
|
|
107
111
|
margin-top: 10px;
|
|
108
|
-
border-top: 1px solid #eceef5;
|
|
112
|
+
border-top: 1px solid var(--figma-color-border, #eceef5);
|
|
109
113
|
padding-top: 9px;
|
|
110
114
|
}
|
|
111
115
|
|
|
@@ -123,7 +127,7 @@
|
|
|
123
127
|
.res-title {
|
|
124
128
|
font-weight: 600;
|
|
125
129
|
font-size: 11px;
|
|
126
|
-
color: #1f2330;
|
|
130
|
+
color: var(--figma-color-text, #1f2330);
|
|
127
131
|
}
|
|
128
132
|
|
|
129
133
|
.res-list {
|
|
@@ -139,9 +143,9 @@
|
|
|
139
143
|
align-items: center;
|
|
140
144
|
gap: 6px;
|
|
141
145
|
padding: 5px 7px;
|
|
142
|
-
border: 1px solid #e6e8f0;
|
|
146
|
+
border: 1px solid var(--figma-color-border, #e6e8f0);
|
|
143
147
|
border-radius: 7px;
|
|
144
|
-
background: #fbfcfe;
|
|
148
|
+
background: var(--figma-color-bg-secondary, #fbfcfe);
|
|
145
149
|
}
|
|
146
150
|
|
|
147
151
|
.res-name {
|
|
@@ -151,22 +155,22 @@
|
|
|
151
155
|
text-overflow: ellipsis;
|
|
152
156
|
white-space: nowrap;
|
|
153
157
|
font-size: 11px;
|
|
154
|
-
color: #2c2c34;
|
|
158
|
+
color: var(--figma-color-text, #2c2c34);
|
|
155
159
|
}
|
|
156
160
|
|
|
157
161
|
.res-type {
|
|
158
162
|
flex: none;
|
|
159
163
|
font-size: 9px;
|
|
160
|
-
color: #a0a4b0;
|
|
164
|
+
color: var(--figma-color-text-tertiary, #a0a4b0);
|
|
161
165
|
text-transform: uppercase;
|
|
162
166
|
letter-spacing: 0.03em;
|
|
163
167
|
}
|
|
164
168
|
|
|
165
169
|
.copy-btn {
|
|
166
170
|
flex: none;
|
|
167
|
-
border: 1px solid #d6d9e6;
|
|
168
|
-
background: #fff;
|
|
169
|
-
color: #4b5168;
|
|
171
|
+
border: 1px solid var(--figma-color-border-strong, #d6d9e6);
|
|
172
|
+
background: var(--figma-color-bg, #fff);
|
|
173
|
+
color: var(--figma-color-text-secondary, #4b5168);
|
|
170
174
|
border-radius: 6px;
|
|
171
175
|
padding: 2px 8px;
|
|
172
176
|
font-size: 10px;
|
|
@@ -174,13 +178,13 @@
|
|
|
174
178
|
}
|
|
175
179
|
|
|
176
180
|
.copy-btn:hover {
|
|
177
|
-
background: #f0f2f8;
|
|
181
|
+
background: var(--figma-color-bg-hover, #f0f2f8);
|
|
178
182
|
}
|
|
179
183
|
|
|
180
184
|
.copy-btn.done {
|
|
181
|
-
color: #22a06b;
|
|
182
|
-
border-color: #bfe6d3;
|
|
183
|
-
background: #f0faf5;
|
|
185
|
+
color: var(--figma-color-text-success, #22a06b);
|
|
186
|
+
border-color: var(--figma-color-border-success, #bfe6d3);
|
|
187
|
+
background: var(--figma-color-bg-success-tertiary, #f0faf5);
|
|
184
188
|
}
|
|
185
189
|
|
|
186
190
|
.copy-all {
|
|
@@ -196,9 +200,9 @@
|
|
|
196
200
|
|
|
197
201
|
.filter-btn {
|
|
198
202
|
flex: none;
|
|
199
|
-
border: 1px solid #d6d9e6;
|
|
200
|
-
background: #fff;
|
|
201
|
-
color: #6b6f7d;
|
|
203
|
+
border: 1px solid var(--figma-color-border-strong, #d6d9e6);
|
|
204
|
+
background: var(--figma-color-bg, #fff);
|
|
205
|
+
color: var(--figma-color-text-secondary, #6b6f7d);
|
|
202
206
|
border-radius: 6px;
|
|
203
207
|
padding: 3px 9px;
|
|
204
208
|
font-size: 10px;
|
|
@@ -206,32 +210,32 @@
|
|
|
206
210
|
}
|
|
207
211
|
|
|
208
212
|
.filter-btn:hover {
|
|
209
|
-
background: #f0f2f8;
|
|
213
|
+
background: var(--figma-color-bg-hover, #f0f2f8);
|
|
210
214
|
}
|
|
211
215
|
|
|
212
216
|
.filter-btn.on {
|
|
213
|
-
background: #2c2c34;
|
|
214
|
-
border-color: #2c2c34;
|
|
215
|
-
color: #fff;
|
|
217
|
+
background: var(--figma-color-bg-inverse, #2c2c34);
|
|
218
|
+
border-color: var(--figma-color-bg-inverse, #2c2c34);
|
|
219
|
+
color: var(--figma-color-text-oninverse, #fff);
|
|
216
220
|
}
|
|
217
221
|
|
|
218
222
|
.res-empty {
|
|
219
223
|
font-size: 11px;
|
|
220
|
-
color: #a0a4b0;
|
|
224
|
+
color: var(--figma-color-text-tertiary, #a0a4b0);
|
|
221
225
|
padding: 6px 2px;
|
|
222
226
|
}
|
|
223
227
|
|
|
224
228
|
.key-prompt {
|
|
225
229
|
margin-bottom: 8px;
|
|
226
230
|
padding: 7px 8px;
|
|
227
|
-
border: 1px solid #ffe0a6;
|
|
228
|
-
background: #fff9ec;
|
|
231
|
+
border: 1px solid var(--figma-color-border-warning, #ffe0a6);
|
|
232
|
+
background: var(--figma-color-bg-warning-tertiary, #fff9ec);
|
|
229
233
|
border-radius: 7px;
|
|
230
234
|
}
|
|
231
235
|
|
|
232
236
|
.key-note {
|
|
233
237
|
font-size: 10px;
|
|
234
|
-
color: #8a6d3b;
|
|
238
|
+
color: var(--figma-color-text-warning, #8a6d3b);
|
|
235
239
|
line-height: 1.4;
|
|
236
240
|
margin-bottom: 5px;
|
|
237
241
|
}
|
|
@@ -244,7 +248,9 @@
|
|
|
244
248
|
.key-input {
|
|
245
249
|
flex: 1;
|
|
246
250
|
min-width: 0;
|
|
247
|
-
border: 1px solid #d6d9e6;
|
|
251
|
+
border: 1px solid var(--figma-color-border-strong, #d6d9e6);
|
|
252
|
+
background: var(--figma-color-bg, #fff);
|
|
253
|
+
color: var(--figma-color-text, #000);
|
|
248
254
|
border-radius: 6px;
|
|
249
255
|
padding: 3px 6px;
|
|
250
256
|
font-size: 10px;
|
|
@@ -252,8 +258,9 @@
|
|
|
252
258
|
|
|
253
259
|
.key-save {
|
|
254
260
|
flex: none;
|
|
255
|
-
border: 1px solid #d6d9e6;
|
|
256
|
-
background: #fff;
|
|
261
|
+
border: 1px solid var(--figma-color-border-strong, #d6d9e6);
|
|
262
|
+
background: var(--figma-color-bg, #fff);
|
|
263
|
+
color: var(--figma-color-text, #000);
|
|
257
264
|
border-radius: 6px;
|
|
258
265
|
padding: 3px 10px;
|
|
259
266
|
font-size: 10px;
|
|
@@ -261,24 +268,24 @@
|
|
|
261
268
|
}
|
|
262
269
|
|
|
263
270
|
.key-save:hover {
|
|
264
|
-
background: #f0f2f8;
|
|
271
|
+
background: var(--figma-color-bg-hover, #f0f2f8);
|
|
265
272
|
}
|
|
266
273
|
|
|
267
274
|
#credit {
|
|
268
275
|
margin-top: auto;
|
|
269
276
|
padding-top: 7px;
|
|
270
277
|
text-align: center;
|
|
271
|
-
color: #a0a4b0;
|
|
278
|
+
color: var(--figma-color-text-tertiary, #a0a4b0);
|
|
272
279
|
font-size: 10px;
|
|
273
280
|
}
|
|
274
281
|
|
|
275
282
|
#version {
|
|
276
|
-
color: #8288a0;
|
|
283
|
+
color: var(--figma-color-text-secondary, #8288a0);
|
|
277
284
|
margin-right: 4px;
|
|
278
285
|
}
|
|
279
286
|
|
|
280
287
|
#credit a {
|
|
281
|
-
color: #8288a0;
|
|
288
|
+
color: var(--figma-color-text-secondary, #8288a0);
|
|
282
289
|
text-decoration: none;
|
|
283
290
|
}
|
|
284
291
|
|
|
Binary file
|
|
Binary file
|