Files
galaxy/resources/bundled/mcp_skills/figma/figma-generate-library/scripts/createSemanticTokens.js
T

109 lines
3.8 KiB
JavaScript

/**
* createSemanticTokens
*
* Creates a batch of Figma variables in the given collection, one per entry in
* `tokenMap`. Supports raw values, variable alias references, code syntax, and
* scopes. Returns a map of token name → Variable for use in subsequent steps.
*
* @param {VariableCollection} collection - The target variable collection.
* @param {Record<string, string>} modeIds - Map of {modeName: modeId} from createVariableCollection.
* @param {Array<{
* name: string,
* type: 'COLOR' | 'FLOAT' | 'STRING' | 'BOOLEAN',
* values: Record<string, string | number | boolean | {type: 'VARIABLE_ALIAS', id: string}>,
* scopes?: VariableScope[],
* codeSyntax?: {WEB?: string, ANDROID?: string, iOS?: string}
* }>} tokenMap - Ordered list of token definitions.
* - `name`: Variable name using slash hierarchy (e.g. "color/bg/primary").
* - `type`: Figma variable type.
* - `values`: Map of {modeName: value}. Values can be raw (hex string for COLOR,
* number for FLOAT) or alias objects {type: 'VARIABLE_ALIAS', id: variableId}.
* For COLOR, raw values are accepted as hex strings ("#rrggbb" or "#rrggbbaa")
* and converted to {r, g, b, a} automatically.
* - `scopes`: Array of VariableScope strings. Omit to use [] (hidden/primitive).
* - `codeSyntax`: Platform code syntax strings. Omit to skip.
* @param {string} [runId] - Optional dsb_run_id to tag every variable.
* @returns {Promise<{variables: Record<string, Variable>}>}
* `variables` maps each token name to its created Variable object.
*/
async function createSemanticTokens(collection, modeIds, tokenMap, runId) {
const variables = {}
for (const token of tokenMap) {
// Create the variable
const variable = figma.variables.createVariable(token.name, collection, token.type)
// Tag for cleanup
variable.setPluginData('dsb_key', `variable/${token.name}`)
if (runId) {
variable.setPluginData('dsb_run_id', runId)
}
// Set values for each mode
for (const [modeName, rawValue] of Object.entries(token.values)) {
const modeId = modeIds[modeName]
if (!modeId) {
throw new Error(
`createSemanticTokens: mode "${modeName}" not found in modeIds for token "${token.name}". ` +
`Available modes: ${Object.keys(modeIds).join(', ')}`,
)
}
let value = rawValue
// Convert hex strings to Figma RGBA for COLOR type
if (token.type === 'COLOR' && typeof rawValue === 'string' && rawValue.startsWith('#')) {
value = hexToFigmaColor(rawValue)
}
variable.setValueForMode(modeId, value)
}
// Set scopes (default: empty array = hidden from property pickers / primitives)
variable.scopes = token.scopes || []
// Set code syntax per platform
if (token.codeSyntax) {
if (token.codeSyntax.WEB) {
variable.setVariableCodeSyntax('WEB', token.codeSyntax.WEB)
}
if (token.codeSyntax.ANDROID) {
variable.setVariableCodeSyntax('ANDROID', token.codeSyntax.ANDROID)
}
if (token.codeSyntax.iOS) {
variable.setVariableCodeSyntax('iOS', token.codeSyntax.iOS)
}
}
variables[token.name] = variable
}
return { variables }
}
/**
* Converts a hex color string to a Figma RGBA object.
* Supports "#rgb", "#rrggbb", and "#rrggbbaa".
*
* @param {string} hex
* @returns {{ r: number, g: number, b: number, a: number }}
*/
function hexToFigmaColor(hex) {
let h = hex.replace('#', '')
// Expand shorthand #rgb → #rrggbb
if (h.length === 3) {
h = h
.split('')
.map((c) => c + c)
.join('')
}
const r = parseInt(h.substring(0, 2), 16) / 255
const g = parseInt(h.substring(2, 4), 16) / 255
const b = parseInt(h.substring(4, 6), 16) / 255
const a = h.length === 8 ? parseInt(h.substring(6, 8), 16) / 255 : 1
return { r, g, b, a }
}