@dannote/figma-use 0.3.0 → 0.5.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/CHANGELOG.md +100 -0
- package/README.md +217 -334
- package/SKILL.md +78 -1
- package/bin/figma-use.js +4 -1
- package/dist/cli/index.js +563 -83
- package/dist/proxy/index.js +180 -2
- package/package.json +1 -1
- package/packages/plugin/dist/main.js +93 -1
package/dist/cli/index.js
CHANGED
|
@@ -18920,6 +18920,92 @@ var require_react_reconciler = __commonJS((exports, module) => {
|
|
|
18920
18920
|
}
|
|
18921
18921
|
});
|
|
18922
18922
|
|
|
18923
|
+
// packages/cli/src/render/vars.ts
|
|
18924
|
+
var exports_vars = {};
|
|
18925
|
+
__export(exports_vars, {
|
|
18926
|
+
resolveVariable: () => resolveVariable,
|
|
18927
|
+
loadVariablesIntoRegistry: () => loadVariablesIntoRegistry,
|
|
18928
|
+
isVariable: () => isVariable,
|
|
18929
|
+
isRegistryLoaded: () => isRegistryLoaded,
|
|
18930
|
+
getRegistrySize: () => getRegistrySize,
|
|
18931
|
+
figmaVar: () => figmaVar,
|
|
18932
|
+
defineVars: () => defineVars
|
|
18933
|
+
});
|
|
18934
|
+
function isVariable(value) {
|
|
18935
|
+
return typeof value === "object" && value !== null && VAR_SYMBOL in value;
|
|
18936
|
+
}
|
|
18937
|
+
function loadVariablesIntoRegistry(variables) {
|
|
18938
|
+
variableRegistry.clear();
|
|
18939
|
+
for (const v3 of variables) {
|
|
18940
|
+
const match = v3.id.match(/VariableID:(\d+):(\d+)/);
|
|
18941
|
+
if (match) {
|
|
18942
|
+
variableRegistry.set(v3.name, {
|
|
18943
|
+
id: v3.id,
|
|
18944
|
+
sessionID: parseInt(match[1], 10),
|
|
18945
|
+
localID: parseInt(match[2], 10)
|
|
18946
|
+
});
|
|
18947
|
+
}
|
|
18948
|
+
}
|
|
18949
|
+
}
|
|
18950
|
+
function resolveVariable(variable) {
|
|
18951
|
+
if (variable._resolved) {
|
|
18952
|
+
return variable._resolved;
|
|
18953
|
+
}
|
|
18954
|
+
const idMatch = variable.name.match(/^(?:VariableID:)?(\d+):(\d+)$/);
|
|
18955
|
+
if (idMatch) {
|
|
18956
|
+
const resolved2 = {
|
|
18957
|
+
id: `VariableID:${idMatch[1]}:${idMatch[2]}`,
|
|
18958
|
+
sessionID: parseInt(idMatch[1], 10),
|
|
18959
|
+
localID: parseInt(idMatch[2], 10)
|
|
18960
|
+
};
|
|
18961
|
+
variable._resolved = resolved2;
|
|
18962
|
+
return resolved2;
|
|
18963
|
+
}
|
|
18964
|
+
const resolved = variableRegistry.get(variable.name);
|
|
18965
|
+
if (!resolved) {
|
|
18966
|
+
const available = Array.from(variableRegistry.keys()).slice(0, 5).join(", ");
|
|
18967
|
+
throw new Error(`Variable "${variable.name}" not found. ` + `Available: ${available}${variableRegistry.size > 5 ? "..." : ""}. ` + `Make sure variables are loaded before render.`);
|
|
18968
|
+
}
|
|
18969
|
+
variable._resolved = resolved;
|
|
18970
|
+
return resolved;
|
|
18971
|
+
}
|
|
18972
|
+
function isRegistryLoaded() {
|
|
18973
|
+
return variableRegistry.size > 0;
|
|
18974
|
+
}
|
|
18975
|
+
function getRegistrySize() {
|
|
18976
|
+
return variableRegistry.size;
|
|
18977
|
+
}
|
|
18978
|
+
function defineVars(vars) {
|
|
18979
|
+
const result = {};
|
|
18980
|
+
for (const [key, def] of Object.entries(vars)) {
|
|
18981
|
+
if (typeof def === "string") {
|
|
18982
|
+
result[key] = {
|
|
18983
|
+
[VAR_SYMBOL]: true,
|
|
18984
|
+
name: def
|
|
18985
|
+
};
|
|
18986
|
+
} else {
|
|
18987
|
+
result[key] = {
|
|
18988
|
+
[VAR_SYMBOL]: true,
|
|
18989
|
+
name: def.name,
|
|
18990
|
+
value: def.value
|
|
18991
|
+
};
|
|
18992
|
+
}
|
|
18993
|
+
}
|
|
18994
|
+
return result;
|
|
18995
|
+
}
|
|
18996
|
+
function figmaVar(name, value) {
|
|
18997
|
+
return {
|
|
18998
|
+
[VAR_SYMBOL]: true,
|
|
18999
|
+
name,
|
|
19000
|
+
value
|
|
19001
|
+
};
|
|
19002
|
+
}
|
|
19003
|
+
var VAR_SYMBOL, variableRegistry;
|
|
19004
|
+
var init_vars = __esm(() => {
|
|
19005
|
+
VAR_SYMBOL = Symbol.for("figma.variable");
|
|
19006
|
+
variableRegistry = new Map;
|
|
19007
|
+
});
|
|
19008
|
+
|
|
18923
19009
|
// node_modules/esbuild/lib/main.js
|
|
18924
19010
|
var require_main = __commonJS((exports, module) => {
|
|
18925
19011
|
var __dirname = "/Users/dannote/Development/figma-use/node_modules/esbuild/lib", __filename = "/Users/dannote/Development/figma-use/node_modules/esbuild/lib/main.js";
|
|
@@ -24126,7 +24212,7 @@ var profile_default = defineCommand({
|
|
|
24126
24212
|
});
|
|
24127
24213
|
// packages/cli/src/commands/render.ts
|
|
24128
24214
|
init_format();
|
|
24129
|
-
var
|
|
24215
|
+
var React3 = __toESM(require_react(), 1);
|
|
24130
24216
|
import { resolve as resolve3 } from "path";
|
|
24131
24217
|
import { existsSync as existsSync2, writeFileSync as writeFileSync2, unlinkSync } from "fs";
|
|
24132
24218
|
import { tmpdir } from "os";
|
|
@@ -27547,6 +27633,89 @@ function parseColor(color) {
|
|
|
27547
27633
|
}
|
|
27548
27634
|
|
|
27549
27635
|
// packages/cli/src/render/reconciler.ts
|
|
27636
|
+
init_vars();
|
|
27637
|
+
|
|
27638
|
+
// packages/cli/src/render/components.tsx
|
|
27639
|
+
init_vars();
|
|
27640
|
+
var React = __toESM(require_react(), 1);
|
|
27641
|
+
var componentRegistry = new Map;
|
|
27642
|
+
function getComponentRegistry() {
|
|
27643
|
+
return componentRegistry;
|
|
27644
|
+
}
|
|
27645
|
+
var c6 = (type) => (props) => React.createElement(type, props);
|
|
27646
|
+
var Frame = c6("frame");
|
|
27647
|
+
var Rectangle = c6("rectangle");
|
|
27648
|
+
var Ellipse = c6("ellipse");
|
|
27649
|
+
var Text = c6("text");
|
|
27650
|
+
var Line = c6("line");
|
|
27651
|
+
var Star = c6("star");
|
|
27652
|
+
var Polygon = c6("polygon");
|
|
27653
|
+
var Vector = c6("vector");
|
|
27654
|
+
var Component = c6("component");
|
|
27655
|
+
var Instance = c6("instance");
|
|
27656
|
+
var Group = c6("group");
|
|
27657
|
+
var Page = c6("page");
|
|
27658
|
+
var INTRINSIC_ELEMENTS = [
|
|
27659
|
+
"Frame",
|
|
27660
|
+
"Rectangle",
|
|
27661
|
+
"Ellipse",
|
|
27662
|
+
"Text",
|
|
27663
|
+
"Line",
|
|
27664
|
+
"Star",
|
|
27665
|
+
"Polygon",
|
|
27666
|
+
"Vector",
|
|
27667
|
+
"Component",
|
|
27668
|
+
"Instance",
|
|
27669
|
+
"Group",
|
|
27670
|
+
"Page",
|
|
27671
|
+
"View"
|
|
27672
|
+
];
|
|
27673
|
+
|
|
27674
|
+
// packages/cli/src/render/component-set.tsx
|
|
27675
|
+
var React2 = __toESM(require_react(), 1);
|
|
27676
|
+
var componentSetRegistry = new Map;
|
|
27677
|
+
function getComponentSetRegistry() {
|
|
27678
|
+
return componentSetRegistry;
|
|
27679
|
+
}
|
|
27680
|
+
function generateVariantCombinations(variants) {
|
|
27681
|
+
const keys = Object.keys(variants);
|
|
27682
|
+
if (keys.length === 0)
|
|
27683
|
+
return [{}];
|
|
27684
|
+
const result = [];
|
|
27685
|
+
function combine(index, current) {
|
|
27686
|
+
if (index === keys.length) {
|
|
27687
|
+
result.push(current);
|
|
27688
|
+
return;
|
|
27689
|
+
}
|
|
27690
|
+
const key = keys[index];
|
|
27691
|
+
for (const value of variants[key]) {
|
|
27692
|
+
combine(index + 1, { ...current, [key]: value });
|
|
27693
|
+
}
|
|
27694
|
+
}
|
|
27695
|
+
combine(0, {});
|
|
27696
|
+
return result;
|
|
27697
|
+
}
|
|
27698
|
+
function buildVariantName(props) {
|
|
27699
|
+
return Object.entries(props).map(([k6, v3]) => `${k6}=${v3}`).join(", ");
|
|
27700
|
+
}
|
|
27701
|
+
function buildStateGroupPropertyValueOrders(variants) {
|
|
27702
|
+
return Object.entries(variants).map(([property, values]) => ({
|
|
27703
|
+
property,
|
|
27704
|
+
values: [...values]
|
|
27705
|
+
}));
|
|
27706
|
+
}
|
|
27707
|
+
|
|
27708
|
+
// packages/cli/src/render/reconciler.ts
|
|
27709
|
+
var renderedComponents = new Map;
|
|
27710
|
+
var renderedComponentSets = new Map;
|
|
27711
|
+
var renderedComponentSetVariants = new Map;
|
|
27712
|
+
var pendingComponentSetInstances = [];
|
|
27713
|
+
function getPendingComponentSetInstances() {
|
|
27714
|
+
return [...pendingComponentSetInstances];
|
|
27715
|
+
}
|
|
27716
|
+
function clearPendingComponentSetInstances() {
|
|
27717
|
+
pendingComponentSetInstances.length = 0;
|
|
27718
|
+
}
|
|
27550
27719
|
function styleToNodeChange(type, props, localID, sessionID, parentGUID, position, textContent) {
|
|
27551
27720
|
const style = props.style || {};
|
|
27552
27721
|
const name = props.name || type;
|
|
@@ -27559,10 +27728,16 @@ function styleToNodeChange(type, props, localID, sessionID, parentGUID, position
|
|
|
27559
27728
|
visible: true,
|
|
27560
27729
|
opacity: typeof style.opacity === "number" ? style.opacity : 1
|
|
27561
27730
|
};
|
|
27562
|
-
const width = style.width ?? props.width
|
|
27563
|
-
const height = style.height ?? props.height
|
|
27731
|
+
const width = style.width ?? props.width;
|
|
27732
|
+
const height = style.height ?? props.height;
|
|
27564
27733
|
if (width !== undefined && height !== undefined) {
|
|
27565
27734
|
nodeChange.size = { x: Number(width), y: Number(height) };
|
|
27735
|
+
} else if (width !== undefined) {
|
|
27736
|
+
nodeChange.size = { x: Number(width), y: 1 };
|
|
27737
|
+
} else if (height !== undefined) {
|
|
27738
|
+
nodeChange.size = { x: 1, y: Number(height) };
|
|
27739
|
+
} else if (type !== "TEXT") {
|
|
27740
|
+
nodeChange.size = { x: 1, y: 1 };
|
|
27566
27741
|
}
|
|
27567
27742
|
const x3 = Number(style.x ?? props.x ?? 0);
|
|
27568
27743
|
const y5 = Number(style.y ?? props.y ?? 0);
|
|
@@ -27575,22 +27750,52 @@ function styleToNodeChange(type, props, localID, sessionID, parentGUID, position
|
|
|
27575
27750
|
m12: y5
|
|
27576
27751
|
};
|
|
27577
27752
|
if (style.backgroundColor) {
|
|
27578
|
-
const
|
|
27579
|
-
|
|
27580
|
-
|
|
27581
|
-
|
|
27582
|
-
|
|
27583
|
-
|
|
27584
|
-
|
|
27753
|
+
const bgColor = style.backgroundColor;
|
|
27754
|
+
if (isVariable(bgColor)) {
|
|
27755
|
+
const resolved = resolveVariable(bgColor);
|
|
27756
|
+
const fallback = bgColor.value ? parseColor(bgColor.value) : { r: 0, g: 0, b: 0, a: 1 };
|
|
27757
|
+
nodeChange.fillPaints = [{
|
|
27758
|
+
type: "SOLID",
|
|
27759
|
+
color: { r: fallback.r, g: fallback.g, b: fallback.b, a: fallback.a },
|
|
27760
|
+
opacity: 1,
|
|
27761
|
+
visible: true,
|
|
27762
|
+
colorVariableBinding: {
|
|
27763
|
+
variableID: { sessionID: resolved.sessionID, localID: resolved.localID }
|
|
27764
|
+
}
|
|
27765
|
+
}];
|
|
27766
|
+
} else {
|
|
27767
|
+
const color = parseColor(bgColor);
|
|
27768
|
+
nodeChange.fillPaints = [{
|
|
27769
|
+
type: "SOLID",
|
|
27770
|
+
color: { r: color.r, g: color.g, b: color.b, a: color.a },
|
|
27771
|
+
opacity: color.a,
|
|
27772
|
+
visible: true
|
|
27773
|
+
}];
|
|
27774
|
+
}
|
|
27585
27775
|
}
|
|
27586
27776
|
if (style.borderColor) {
|
|
27587
|
-
const
|
|
27588
|
-
|
|
27589
|
-
|
|
27590
|
-
|
|
27591
|
-
|
|
27592
|
-
|
|
27593
|
-
|
|
27777
|
+
const borderColor = style.borderColor;
|
|
27778
|
+
if (isVariable(borderColor)) {
|
|
27779
|
+
const resolved = resolveVariable(borderColor);
|
|
27780
|
+
const fallback = borderColor.value ? parseColor(borderColor.value) : { r: 0, g: 0, b: 0, a: 1 };
|
|
27781
|
+
nodeChange.strokePaints = [{
|
|
27782
|
+
type: "SOLID",
|
|
27783
|
+
color: { r: fallback.r, g: fallback.g, b: fallback.b, a: fallback.a },
|
|
27784
|
+
opacity: 1,
|
|
27785
|
+
visible: true,
|
|
27786
|
+
colorVariableBinding: {
|
|
27787
|
+
variableID: { sessionID: resolved.sessionID, localID: resolved.localID }
|
|
27788
|
+
}
|
|
27789
|
+
}];
|
|
27790
|
+
} else {
|
|
27791
|
+
const color = parseColor(borderColor);
|
|
27792
|
+
nodeChange.strokePaints = [{
|
|
27793
|
+
type: "SOLID",
|
|
27794
|
+
color: { r: color.r, g: color.g, b: color.b, a: color.a },
|
|
27795
|
+
opacity: color.a,
|
|
27796
|
+
visible: true
|
|
27797
|
+
}];
|
|
27798
|
+
}
|
|
27594
27799
|
nodeChange.strokeWeight = Number(style.borderWidth ?? 1);
|
|
27595
27800
|
}
|
|
27596
27801
|
if (style.borderRadius !== undefined) {
|
|
@@ -27614,6 +27819,11 @@ function styleToNodeChange(type, props, localID, sessionID, parentGUID, position
|
|
|
27614
27819
|
}
|
|
27615
27820
|
if (style.flexDirection) {
|
|
27616
27821
|
nodeChange.stackMode = style.flexDirection === "row" ? "HORIZONTAL" : "VERTICAL";
|
|
27822
|
+
const isRow = style.flexDirection === "row";
|
|
27823
|
+
const primarySize = isRow ? width : height;
|
|
27824
|
+
const counterSize = isRow ? height : width;
|
|
27825
|
+
nodeChange.stackPrimarySizing = primarySize !== undefined ? "FIXED" : "RESIZE_TO_FIT";
|
|
27826
|
+
nodeChange.stackCounterSizing = counterSize !== undefined ? "FIXED" : "RESIZE_TO_FIT";
|
|
27617
27827
|
}
|
|
27618
27828
|
if (style.gap !== undefined) {
|
|
27619
27829
|
nodeChange.stackSpacing = Number(style.gap);
|
|
@@ -27631,49 +27841,89 @@ function styleToNodeChange(type, props, localID, sessionID, parentGUID, position
|
|
|
27631
27841
|
if (pb !== undefined)
|
|
27632
27842
|
nodeChange.stackPaddingBottom = Number(pb);
|
|
27633
27843
|
if (style.justifyContent) {
|
|
27634
|
-
const
|
|
27844
|
+
const validValues = {
|
|
27635
27845
|
"flex-start": "MIN",
|
|
27636
27846
|
center: "CENTER",
|
|
27637
27847
|
"flex-end": "MAX",
|
|
27638
|
-
"space-
|
|
27848
|
+
"space-evenly": "SPACE_EVENLY"
|
|
27639
27849
|
};
|
|
27640
|
-
|
|
27850
|
+
const mapped = validValues[style.justifyContent];
|
|
27851
|
+
if (mapped) {
|
|
27852
|
+
nodeChange.stackPrimaryAlignItems = mapped;
|
|
27853
|
+
} else {
|
|
27854
|
+
consola2.warn(`justifyContent: "${style.justifyContent}" not supported, using "flex-start"`);
|
|
27855
|
+
nodeChange.stackPrimaryAlignItems = "MIN";
|
|
27856
|
+
}
|
|
27641
27857
|
}
|
|
27642
27858
|
if (style.alignItems) {
|
|
27643
|
-
const
|
|
27859
|
+
const validValues = {
|
|
27644
27860
|
"flex-start": "MIN",
|
|
27645
27861
|
center: "CENTER",
|
|
27646
27862
|
"flex-end": "MAX",
|
|
27647
27863
|
stretch: "STRETCH"
|
|
27648
27864
|
};
|
|
27649
|
-
|
|
27865
|
+
const mapped = validValues[style.alignItems];
|
|
27866
|
+
if (mapped) {
|
|
27867
|
+
nodeChange.stackCounterAlignItems = mapped;
|
|
27868
|
+
} else {
|
|
27869
|
+
consola2.warn(`alignItems: "${style.alignItems}" not supported, using "flex-start"`);
|
|
27870
|
+
nodeChange.stackCounterAlignItems = "MIN";
|
|
27871
|
+
}
|
|
27650
27872
|
}
|
|
27651
27873
|
if (type.toLowerCase() === "text" && textContent) {
|
|
27652
27874
|
const nc = nodeChange;
|
|
27653
27875
|
nc.textData = { characters: textContent };
|
|
27876
|
+
nc.textAutoResize = "WIDTH_AND_HEIGHT";
|
|
27877
|
+
nc.textAlignVertical = "TOP";
|
|
27654
27878
|
if (style.fontSize)
|
|
27655
27879
|
nc.fontSize = Number(style.fontSize);
|
|
27656
|
-
|
|
27657
|
-
|
|
27658
|
-
|
|
27659
|
-
|
|
27660
|
-
|
|
27661
|
-
|
|
27662
|
-
|
|
27663
|
-
|
|
27664
|
-
}
|
|
27880
|
+
nc.lineHeight = { value: 100, units: "PERCENT" };
|
|
27881
|
+
const family = style.fontFamily || "Inter";
|
|
27882
|
+
const fontStyle = mapFontWeight(style.fontWeight);
|
|
27883
|
+
nc.fontName = {
|
|
27884
|
+
family,
|
|
27885
|
+
style: fontStyle,
|
|
27886
|
+
postscript: `${family}-${fontStyle}`.replace(/\s+/g, "")
|
|
27887
|
+
};
|
|
27665
27888
|
if (style.textAlign) {
|
|
27666
27889
|
const map = { left: "LEFT", center: "CENTER", right: "RIGHT" };
|
|
27667
27890
|
nc.textAlignHorizontal = map[style.textAlign] || "LEFT";
|
|
27668
27891
|
}
|
|
27669
27892
|
if (style.color) {
|
|
27670
|
-
const
|
|
27671
|
-
|
|
27672
|
-
|
|
27673
|
-
|
|
27674
|
-
|
|
27675
|
-
|
|
27676
|
-
|
|
27893
|
+
const textColor = style.color;
|
|
27894
|
+
if (isVariable(textColor)) {
|
|
27895
|
+
const resolved = resolveVariable(textColor);
|
|
27896
|
+
const fallback = textColor.value ? parseColor(textColor.value) : { r: 0, g: 0, b: 0, a: 1 };
|
|
27897
|
+
nodeChange.fillPaints = [{
|
|
27898
|
+
type: "SOLID",
|
|
27899
|
+
color: { r: fallback.r, g: fallback.g, b: fallback.b, a: fallback.a },
|
|
27900
|
+
opacity: 1,
|
|
27901
|
+
visible: true,
|
|
27902
|
+
colorVariableBinding: {
|
|
27903
|
+
variableID: { sessionID: resolved.sessionID, localID: resolved.localID }
|
|
27904
|
+
}
|
|
27905
|
+
}];
|
|
27906
|
+
} else {
|
|
27907
|
+
const color = parseColor(textColor);
|
|
27908
|
+
nodeChange.fillPaints = [{
|
|
27909
|
+
type: "SOLID",
|
|
27910
|
+
color: { r: color.r, g: color.g, b: color.b, a: color.a },
|
|
27911
|
+
opacity: color.a,
|
|
27912
|
+
visible: true
|
|
27913
|
+
}];
|
|
27914
|
+
}
|
|
27915
|
+
}
|
|
27916
|
+
}
|
|
27917
|
+
if (type.toLowerCase() === "instance" && props.componentId) {
|
|
27918
|
+
const match = String(props.componentId).match(/(\d+):(\d+)/);
|
|
27919
|
+
if (match) {
|
|
27920
|
+
const nc = nodeChange;
|
|
27921
|
+
nc.symbolData = {
|
|
27922
|
+
symbolID: {
|
|
27923
|
+
sessionID: parseInt(match[1], 10),
|
|
27924
|
+
localID: parseInt(match[2], 10)
|
|
27925
|
+
}
|
|
27926
|
+
};
|
|
27677
27927
|
}
|
|
27678
27928
|
}
|
|
27679
27929
|
return nodeChange;
|
|
@@ -27688,7 +27938,7 @@ function mapType(type) {
|
|
|
27688
27938
|
star: "STAR",
|
|
27689
27939
|
polygon: "REGULAR_POLYGON",
|
|
27690
27940
|
vector: "VECTOR",
|
|
27691
|
-
component: "
|
|
27941
|
+
component: "SYMBOL",
|
|
27692
27942
|
instance: "INSTANCE",
|
|
27693
27943
|
group: "GROUP",
|
|
27694
27944
|
page: "CANVAS"
|
|
@@ -27713,13 +27963,173 @@ function mapFontWeight(weight) {
|
|
|
27713
27963
|
};
|
|
27714
27964
|
return map[weight] || "Regular";
|
|
27715
27965
|
}
|
|
27716
|
-
function collectNodeChanges(instance, sessionID, parentGUID, position, result) {
|
|
27966
|
+
function collectNodeChanges(instance, sessionID, parentGUID, position, result, container) {
|
|
27967
|
+
if (instance.type === "__component_instance__") {
|
|
27968
|
+
const sym = instance.props.__componentSymbol;
|
|
27969
|
+
const name = instance.props.__componentName;
|
|
27970
|
+
const registry = getComponentRegistry();
|
|
27971
|
+
const def = registry.get(sym);
|
|
27972
|
+
if (!def) {
|
|
27973
|
+
consola2.error(`Component "${name}" not found in registry`);
|
|
27974
|
+
return;
|
|
27975
|
+
}
|
|
27976
|
+
let componentGUID = renderedComponents.get(sym);
|
|
27977
|
+
if (!componentGUID) {
|
|
27978
|
+
const componentLocalID = container.localIDCounter++;
|
|
27979
|
+
componentGUID = { sessionID, localID: componentLocalID };
|
|
27980
|
+
renderedComponents.set(sym, componentGUID);
|
|
27981
|
+
const componentResult = renderToNodeChanges(def.element, {
|
|
27982
|
+
sessionID,
|
|
27983
|
+
parentGUID,
|
|
27984
|
+
startLocalID: container.localIDCounter
|
|
27985
|
+
});
|
|
27986
|
+
container.localIDCounter = componentResult.nextLocalID;
|
|
27987
|
+
if (componentResult.nodeChanges.length > 0) {
|
|
27988
|
+
const rootChange = componentResult.nodeChanges[0];
|
|
27989
|
+
const originalRootGUID = { ...rootChange.guid };
|
|
27990
|
+
rootChange.guid = componentGUID;
|
|
27991
|
+
rootChange.type = "SYMBOL";
|
|
27992
|
+
rootChange.name = name;
|
|
27993
|
+
rootChange.parentIndex = { guid: parentGUID, position };
|
|
27994
|
+
for (let i3 = 1;i3 < componentResult.nodeChanges.length; i3++) {
|
|
27995
|
+
const child = componentResult.nodeChanges[i3];
|
|
27996
|
+
if (child.parentIndex?.guid.localID === originalRootGUID.localID && child.parentIndex?.guid.sessionID === originalRootGUID.sessionID) {
|
|
27997
|
+
child.parentIndex.guid = componentGUID;
|
|
27998
|
+
}
|
|
27999
|
+
}
|
|
28000
|
+
const style = instance.props.style || {};
|
|
28001
|
+
if (style.x !== undefined || style.y !== undefined) {
|
|
28002
|
+
const x3 = Number(style.x ?? 0);
|
|
28003
|
+
const y5 = Number(style.y ?? 0);
|
|
28004
|
+
rootChange.transform = { m00: 1, m01: 0, m02: x3, m10: 0, m11: 1, m12: y5 };
|
|
28005
|
+
}
|
|
28006
|
+
result.push(...componentResult.nodeChanges);
|
|
28007
|
+
}
|
|
28008
|
+
} else {
|
|
28009
|
+
const instanceLocalID = container.localIDCounter++;
|
|
28010
|
+
const style = instance.props.style || {};
|
|
28011
|
+
const x3 = Number(style.x ?? 0);
|
|
28012
|
+
const y5 = Number(style.y ?? 0);
|
|
28013
|
+
const instanceChange = {
|
|
28014
|
+
guid: { sessionID, localID: instanceLocalID },
|
|
28015
|
+
phase: "CREATED",
|
|
28016
|
+
parentIndex: { guid: parentGUID, position },
|
|
28017
|
+
type: "INSTANCE",
|
|
28018
|
+
name,
|
|
28019
|
+
visible: true,
|
|
28020
|
+
opacity: 1,
|
|
28021
|
+
transform: { m00: 1, m01: 0, m02: x3, m10: 0, m11: 1, m12: y5 }
|
|
28022
|
+
};
|
|
28023
|
+
const nc = instanceChange;
|
|
28024
|
+
nc.symbolData = { symbolID: componentGUID };
|
|
28025
|
+
result.push(instanceChange);
|
|
28026
|
+
}
|
|
28027
|
+
return;
|
|
28028
|
+
}
|
|
28029
|
+
if (instance.type === "__component_set_instance__") {
|
|
28030
|
+
const sym = instance.props.__componentSetSymbol;
|
|
28031
|
+
const name = instance.props.__componentSetName;
|
|
28032
|
+
const variantProps = instance.props.__variantProps || {};
|
|
28033
|
+
const csRegistry = getComponentSetRegistry();
|
|
28034
|
+
const csDef = csRegistry.get(sym);
|
|
28035
|
+
if (!csDef) {
|
|
28036
|
+
consola2.error(`ComponentSet "${name}" not found in registry`);
|
|
28037
|
+
return;
|
|
28038
|
+
}
|
|
28039
|
+
let componentSetGUID = renderedComponentSets.get(sym);
|
|
28040
|
+
if (!componentSetGUID) {
|
|
28041
|
+
const componentSetLocalID = container.localIDCounter++;
|
|
28042
|
+
componentSetGUID = { sessionID, localID: componentSetLocalID };
|
|
28043
|
+
renderedComponentSets.set(sym, componentSetGUID);
|
|
28044
|
+
const variants = csDef.variants;
|
|
28045
|
+
const combinations = generateVariantCombinations(variants);
|
|
28046
|
+
const variantComponentIds = new Map;
|
|
28047
|
+
const setChange = {
|
|
28048
|
+
guid: componentSetGUID,
|
|
28049
|
+
phase: "CREATED",
|
|
28050
|
+
parentIndex: { guid: parentGUID, position },
|
|
28051
|
+
type: "FRAME",
|
|
28052
|
+
name,
|
|
28053
|
+
visible: true,
|
|
28054
|
+
opacity: 1,
|
|
28055
|
+
size: { x: 1, y: 1 }
|
|
28056
|
+
};
|
|
28057
|
+
const setNc = setChange;
|
|
28058
|
+
setNc.isStateGroup = true;
|
|
28059
|
+
setNc.stateGroupPropertyValueOrders = buildStateGroupPropertyValueOrders(variants);
|
|
28060
|
+
setNc.stackMode = "HORIZONTAL";
|
|
28061
|
+
setNc.stackSpacing = 20;
|
|
28062
|
+
setNc.stackPrimarySizing = "RESIZE_TO_FIT";
|
|
28063
|
+
setNc.stackCounterSizing = "RESIZE_TO_FIT";
|
|
28064
|
+
result.push(setChange);
|
|
28065
|
+
combinations.forEach((combo, i3) => {
|
|
28066
|
+
const variantName = buildVariantName(combo);
|
|
28067
|
+
const variantLocalID = container.localIDCounter++;
|
|
28068
|
+
const variantGUID = { sessionID, localID: variantLocalID };
|
|
28069
|
+
variantComponentIds.set(variantName, variantGUID);
|
|
28070
|
+
const variantElement = csDef.render(combo);
|
|
28071
|
+
const variantResult = renderToNodeChanges(variantElement, {
|
|
28072
|
+
sessionID,
|
|
28073
|
+
parentGUID: componentSetGUID,
|
|
28074
|
+
startLocalID: container.localIDCounter
|
|
28075
|
+
});
|
|
28076
|
+
container.localIDCounter = variantResult.nextLocalID;
|
|
28077
|
+
if (variantResult.nodeChanges.length > 0) {
|
|
28078
|
+
const rootChange = variantResult.nodeChanges[0];
|
|
28079
|
+
const originalRootGUID = { ...rootChange.guid };
|
|
28080
|
+
rootChange.guid = variantGUID;
|
|
28081
|
+
rootChange.type = "SYMBOL";
|
|
28082
|
+
rootChange.name = variantName;
|
|
28083
|
+
rootChange.parentIndex = {
|
|
28084
|
+
guid: componentSetGUID,
|
|
28085
|
+
position: String.fromCharCode(33 + i3)
|
|
28086
|
+
};
|
|
28087
|
+
for (let j = 1;j < variantResult.nodeChanges.length; j++) {
|
|
28088
|
+
const child = variantResult.nodeChanges[j];
|
|
28089
|
+
if (child.parentIndex?.guid.localID === originalRootGUID.localID && child.parentIndex?.guid.sessionID === originalRootGUID.sessionID) {
|
|
28090
|
+
child.parentIndex.guid = variantGUID;
|
|
28091
|
+
}
|
|
28092
|
+
}
|
|
28093
|
+
result.push(...variantResult.nodeChanges);
|
|
28094
|
+
}
|
|
28095
|
+
});
|
|
28096
|
+
renderedComponentSetVariants.set(sym, variantComponentIds);
|
|
28097
|
+
const requestedVariantName = buildVariantName({
|
|
28098
|
+
...getDefaultVariants(variants),
|
|
28099
|
+
...variantProps
|
|
28100
|
+
});
|
|
28101
|
+
const style = instance.props.style || {};
|
|
28102
|
+
pendingComponentSetInstances.push({
|
|
28103
|
+
componentSetName: name,
|
|
28104
|
+
variantName: requestedVariantName,
|
|
28105
|
+
parentGUID,
|
|
28106
|
+
position: String.fromCharCode(34 + combinations.length),
|
|
28107
|
+
x: Number(style.x ?? 0),
|
|
28108
|
+
y: Number(style.y ?? 0)
|
|
28109
|
+
});
|
|
28110
|
+
} else {
|
|
28111
|
+
const requestedVariantName = buildVariantName({
|
|
28112
|
+
...getDefaultVariants(csDef.variants),
|
|
28113
|
+
...variantProps
|
|
28114
|
+
});
|
|
28115
|
+
const style = instance.props.style || {};
|
|
28116
|
+
pendingComponentSetInstances.push({
|
|
28117
|
+
componentSetName: name,
|
|
28118
|
+
variantName: requestedVariantName,
|
|
28119
|
+
parentGUID,
|
|
28120
|
+
position,
|
|
28121
|
+
x: Number(style.x ?? 0),
|
|
28122
|
+
y: Number(style.y ?? 0)
|
|
28123
|
+
});
|
|
28124
|
+
}
|
|
28125
|
+
return;
|
|
28126
|
+
}
|
|
27717
28127
|
const nodeChange = styleToNodeChange(instance.type, instance.props, instance.localID, sessionID, parentGUID, position, instance.textContent);
|
|
27718
28128
|
result.push(nodeChange);
|
|
27719
28129
|
const thisGUID = { sessionID, localID: instance.localID };
|
|
27720
28130
|
instance.children.forEach((child, i3) => {
|
|
27721
28131
|
const childPosition = String.fromCharCode(33 + i3 % 90);
|
|
27722
|
-
collectNodeChanges(child, sessionID, thisGUID, childPosition, result);
|
|
28132
|
+
collectNodeChanges(child, sessionID, thisGUID, childPosition, result, container);
|
|
27723
28133
|
});
|
|
27724
28134
|
}
|
|
27725
28135
|
var hostConfig = {
|
|
@@ -27858,43 +28268,28 @@ function renderToNodeChanges(element, options) {
|
|
|
27858
28268
|
const nodeChanges = [];
|
|
27859
28269
|
container.children.forEach((child, i3) => {
|
|
27860
28270
|
const position = String.fromCharCode(33 + i3 % 90);
|
|
27861
|
-
collectNodeChanges(child, options.sessionID, options.parentGUID, position, nodeChanges);
|
|
28271
|
+
collectNodeChanges(child, options.sessionID, options.parentGUID, position, nodeChanges, container);
|
|
27862
28272
|
});
|
|
27863
28273
|
return {
|
|
27864
28274
|
nodeChanges,
|
|
27865
28275
|
nextLocalID: container.localIDCounter
|
|
27866
28276
|
};
|
|
27867
28277
|
}
|
|
27868
|
-
|
|
27869
|
-
|
|
27870
|
-
|
|
27871
|
-
|
|
27872
|
-
|
|
27873
|
-
|
|
27874
|
-
|
|
27875
|
-
|
|
27876
|
-
|
|
27877
|
-
|
|
27878
|
-
|
|
27879
|
-
|
|
27880
|
-
|
|
27881
|
-
|
|
27882
|
-
|
|
27883
|
-
var INTRINSIC_ELEMENTS = [
|
|
27884
|
-
"Frame",
|
|
27885
|
-
"Rectangle",
|
|
27886
|
-
"Ellipse",
|
|
27887
|
-
"Text",
|
|
27888
|
-
"Line",
|
|
27889
|
-
"Star",
|
|
27890
|
-
"Polygon",
|
|
27891
|
-
"Vector",
|
|
27892
|
-
"Component",
|
|
27893
|
-
"Instance",
|
|
27894
|
-
"Group",
|
|
27895
|
-
"Page",
|
|
27896
|
-
"View"
|
|
27897
|
-
];
|
|
28278
|
+
function getDefaultVariants(variants) {
|
|
28279
|
+
const defaults = {};
|
|
28280
|
+
for (const [key, values] of Object.entries(variants)) {
|
|
28281
|
+
if (values.length > 0) {
|
|
28282
|
+
defaults[key] = values[0];
|
|
28283
|
+
}
|
|
28284
|
+
}
|
|
28285
|
+
return defaults;
|
|
28286
|
+
}
|
|
28287
|
+
function resetRenderedComponents() {
|
|
28288
|
+
renderedComponents.clear();
|
|
28289
|
+
renderedComponentSets.clear();
|
|
28290
|
+
renderedComponentSetVariants.clear();
|
|
28291
|
+
pendingComponentSetInstances.length = 0;
|
|
28292
|
+
}
|
|
27898
28293
|
// packages/cli/src/commands/render.ts
|
|
27899
28294
|
var import_esbuild = __toESM(require_main(), 1);
|
|
27900
28295
|
var PROXY_URL2 = process.env.FIGMA_PROXY_URL || "http://localhost:38451";
|
|
@@ -27921,8 +28316,27 @@ function findNodeModulesDir() {
|
|
|
27921
28316
|
var JSX_DEFINE = Object.fromEntries(INTRINSIC_ELEMENTS.map((name) => [name, JSON.stringify(name.toLowerCase())]));
|
|
27922
28317
|
function transformJsxSnippet(code) {
|
|
27923
28318
|
const snippet = code.trim();
|
|
27924
|
-
|
|
27925
|
-
|
|
28319
|
+
if (snippet.includes("import ") || snippet.includes("export ")) {
|
|
28320
|
+
return import_esbuild.transformSync(snippet, {
|
|
28321
|
+
loader: "tsx",
|
|
28322
|
+
jsx: "transform",
|
|
28323
|
+
jsxFactory: "React.createElement",
|
|
28324
|
+
jsxFragment: "React.Fragment",
|
|
28325
|
+
define: JSX_DEFINE
|
|
28326
|
+
}).code;
|
|
28327
|
+
}
|
|
28328
|
+
const jsxStart = snippet.search(/<[A-Z]/);
|
|
28329
|
+
const hasSetupCode = jsxStart > 0;
|
|
28330
|
+
const usesDefineVars = snippet.includes("defineVars");
|
|
28331
|
+
let fullCode;
|
|
28332
|
+
if (hasSetupCode) {
|
|
28333
|
+
const setupPart = snippet.slice(0, jsxStart).trim();
|
|
28334
|
+
const jsxPart = snippet.slice(jsxStart);
|
|
28335
|
+
const params = usesDefineVars ? "(React, { defineVars })" : "(React)";
|
|
28336
|
+
fullCode = `export default ${params} => { ${setupPart}; return () => (${jsxPart}); };`;
|
|
28337
|
+
} else {
|
|
28338
|
+
fullCode = `export default (React) => () => (${snippet});`;
|
|
28339
|
+
}
|
|
27926
28340
|
const result = import_esbuild.transformSync(fullCode, {
|
|
27927
28341
|
loader: "tsx",
|
|
27928
28342
|
jsx: "transform",
|
|
@@ -27932,11 +28346,63 @@ function transformJsxSnippet(code) {
|
|
|
27932
28346
|
});
|
|
27933
28347
|
return result.code;
|
|
27934
28348
|
}
|
|
28349
|
+
var HELP = `
|
|
28350
|
+
Render JSX to Figma (~100x faster than plugin API).
|
|
28351
|
+
|
|
28352
|
+
EXAMPLES
|
|
28353
|
+
|
|
28354
|
+
# From stdin
|
|
28355
|
+
echo '<Frame style={{padding: 24, backgroundColor: "#3B82F6"}}>
|
|
28356
|
+
<Text style={{fontSize: 18, color: "#FFF"}}>Hello</Text>
|
|
28357
|
+
</Frame>' | figma-use render --stdin
|
|
28358
|
+
|
|
28359
|
+
# From file
|
|
28360
|
+
figma-use render ./Card.figma.tsx
|
|
28361
|
+
figma-use render ./Card.figma.tsx --props '{"title": "Hello"}'
|
|
28362
|
+
|
|
28363
|
+
COMPONENTS
|
|
28364
|
+
|
|
28365
|
+
# defineComponent \u2014 first usage creates Component, rest create Instances
|
|
28366
|
+
const Card = defineComponent('Card', <Frame>...</Frame>)
|
|
28367
|
+
<Card /><Card /><Card />
|
|
28368
|
+
|
|
28369
|
+
# defineComponentSet \u2014 creates Figma ComponentSet with all variants
|
|
28370
|
+
const Button = defineComponentSet('Button', {
|
|
28371
|
+
variant: ['Primary', 'Secondary'] as const,
|
|
28372
|
+
size: ['Small', 'Large'] as const,
|
|
28373
|
+
}, ({ variant, size }) => <Frame>...</Frame>)
|
|
28374
|
+
<Button variant="Primary" size="Large" />
|
|
28375
|
+
|
|
28376
|
+
VARIABLE BINDINGS
|
|
28377
|
+
|
|
28378
|
+
const colors = defineVars({
|
|
28379
|
+
primary: { name: 'Colors/Blue/500', value: '#3B82F6' },
|
|
28380
|
+
})
|
|
28381
|
+
<Frame style={{ backgroundColor: colors.primary }} />
|
|
28382
|
+
|
|
28383
|
+
ELEMENTS
|
|
28384
|
+
|
|
28385
|
+
Frame, Rectangle, Ellipse, Text, Line, Star, Polygon, Vector, Group
|
|
28386
|
+
|
|
28387
|
+
STYLE PROPS
|
|
28388
|
+
|
|
28389
|
+
Layout: flexDirection, justifyContent, alignItems, gap, padding
|
|
28390
|
+
Size: width, height, x, y
|
|
28391
|
+
Appearance: backgroundColor, borderColor, borderWidth, borderRadius, opacity
|
|
28392
|
+
Text: fontSize, fontFamily, fontWeight, color, textAlign
|
|
28393
|
+
|
|
28394
|
+
SETUP
|
|
28395
|
+
|
|
28396
|
+
1. Start Figma: figma --remote-debugging-port=9222
|
|
28397
|
+
2. Start proxy: figma-use proxy
|
|
28398
|
+
3. Open plugin: Plugins \u2192 Development \u2192 Figma Use
|
|
28399
|
+
`;
|
|
27935
28400
|
var render_default = defineCommand({
|
|
27936
28401
|
meta: {
|
|
27937
|
-
description: "Render
|
|
28402
|
+
description: "Render JSX to Figma. Use --examples for API reference."
|
|
27938
28403
|
},
|
|
27939
28404
|
args: {
|
|
28405
|
+
examples: { type: "boolean", description: "Show examples and API reference" },
|
|
27940
28406
|
file: { type: "positional", description: "TSX/JSX file path", required: false },
|
|
27941
28407
|
stdin: { type: "boolean", description: "Read TSX from stdin" },
|
|
27942
28408
|
props: { type: "string", description: "JSON props to pass to component" },
|
|
@@ -27946,6 +28412,10 @@ var render_default = defineCommand({
|
|
|
27946
28412
|
dryRun: { type: "boolean", description: "Output NodeChanges without sending to Figma" }
|
|
27947
28413
|
},
|
|
27948
28414
|
async run({ args }) {
|
|
28415
|
+
if (args.examples) {
|
|
28416
|
+
console.log(HELP);
|
|
28417
|
+
return;
|
|
28418
|
+
}
|
|
27949
28419
|
let filePath;
|
|
27950
28420
|
let tempFile = null;
|
|
27951
28421
|
if (args.stdin) {
|
|
@@ -27973,8 +28443,9 @@ var render_default = defineCommand({
|
|
|
27973
28443
|
const module = await import(filePath);
|
|
27974
28444
|
const exportName = args.export || "default";
|
|
27975
28445
|
let Component2 = module[exportName];
|
|
27976
|
-
if (typeof Component2 === "function" && Component2.length === 1 && args.stdin) {
|
|
27977
|
-
|
|
28446
|
+
if (typeof Component2 === "function" && (Component2.length === 1 || Component2.length === 2) && args.stdin) {
|
|
28447
|
+
const { defineVars: defineVars2 } = await Promise.resolve().then(() => (init_vars(), exports_vars));
|
|
28448
|
+
Component2 = Component2(React3, { defineVars: defineVars2 });
|
|
27978
28449
|
}
|
|
27979
28450
|
if (!Component2) {
|
|
27980
28451
|
console.error(fail(`Export "${exportName}" not found`));
|
|
@@ -27992,19 +28463,26 @@ var render_default = defineCommand({
|
|
|
27992
28463
|
}
|
|
27993
28464
|
const parentGUID = args.parent ? parseGUID(args.parent) : await getParentGUID();
|
|
27994
28465
|
const sessionID = parentGUID.sessionID || Date.now() % 1e6;
|
|
27995
|
-
const sendNodeChanges = async (changes) => {
|
|
28466
|
+
const sendNodeChanges = async (changes, pendingInstances2) => {
|
|
27996
28467
|
const response = await fetch(`${PROXY_URL2}/render`, {
|
|
27997
28468
|
method: "POST",
|
|
27998
28469
|
headers: { "Content-Type": "application/json" },
|
|
27999
|
-
body: JSON.stringify({ fileKey, nodeChanges: changes })
|
|
28470
|
+
body: JSON.stringify({ fileKey, nodeChanges: changes, pendingComponentSetInstances: pendingInstances2 })
|
|
28000
28471
|
});
|
|
28001
28472
|
const data = await response.json();
|
|
28002
28473
|
if (data.error) {
|
|
28003
28474
|
throw new Error(data.error);
|
|
28004
28475
|
}
|
|
28005
28476
|
};
|
|
28477
|
+
if (!isRegistryLoaded()) {
|
|
28478
|
+
try {
|
|
28479
|
+
const variables = await sendCommand("get-variables", { simple: true });
|
|
28480
|
+
loadVariablesIntoRegistry(variables);
|
|
28481
|
+
} catch {}
|
|
28482
|
+
}
|
|
28006
28483
|
const props = args.props ? JSON.parse(args.props) : {};
|
|
28007
|
-
const element =
|
|
28484
|
+
const element = React3.createElement(Component2, props);
|
|
28485
|
+
resetRenderedComponents();
|
|
28008
28486
|
const result = renderToNodeChanges(element, {
|
|
28009
28487
|
sessionID,
|
|
28010
28488
|
parentGUID,
|
|
@@ -28017,7 +28495,9 @@ var render_default = defineCommand({
|
|
|
28017
28495
|
if (!args.json) {
|
|
28018
28496
|
console.log(`Rendering ${result.nodeChanges.length} nodes...`);
|
|
28019
28497
|
}
|
|
28020
|
-
|
|
28498
|
+
const pendingInstances = getPendingComponentSetInstances();
|
|
28499
|
+
clearPendingComponentSetInstances();
|
|
28500
|
+
await sendNodeChanges(result.nodeChanges, pendingInstances);
|
|
28021
28501
|
if (args.json) {
|
|
28022
28502
|
const ids = result.nodeChanges.map((nc) => ({
|
|
28023
28503
|
id: `${nc.guid.sessionID}:${nc.guid.localID}`,
|
|
@@ -30209,8 +30689,8 @@ var component_default2 = defineCommand({
|
|
|
30209
30689
|
var main = defineCommand({
|
|
30210
30690
|
meta: {
|
|
30211
30691
|
name: "figma-use",
|
|
30212
|
-
description: "Control Figma from the command line",
|
|
30213
|
-
version: "0.
|
|
30692
|
+
description: "Control Figma from the command line. Supports JSX rendering with components and variants \u2014 see `figma-use render --examples`",
|
|
30693
|
+
version: "0.5.0"
|
|
30214
30694
|
},
|
|
30215
30695
|
subCommands: exports_commands
|
|
30216
30696
|
});
|