Port figma-use tools: variables, set_*, node ops, boolean, paths, viewport
Add 40+ new tool definitions ported from figma-use: Granular set tools: set_rotation, set_opacity, set_radius, set_minmax, set_text, set_font, set_font_range, set_text_resize, set_visible, set_blend, set_locked, set_stroke_align Node operations: node_bounds, node_move, node_resize, node_ancestors, node_children, node_tree, node_bindings, node_replace_with Variable CRUD: get_variable, find_variables, create_variable, set_variable, delete_variable, bind_variable Collection CRUD: get_collection, create_collection, delete_collection Boolean operations: boolean_union, boolean_subtract, boolean_intersect, boolean_exclude Vector path tools: path_get, path_set, path_scale, path_flip, path_move Create tools: create_page, create_vector, create_slice Misc: flatten_nodes, viewport_get, viewport_set, viewport_zoom_to_fit, page_bounds, list_fonts Also adds createVariable/createCollection to SceneGraph, and viewport, boolean operation, and flatten methods to FigmaAPI.
This commit is contained in:
parent
b7a634f1ae
commit
537663aa3c
|
|
@ -8,7 +8,9 @@ import type {
|
|||
Effect,
|
||||
LayoutMode,
|
||||
Variable,
|
||||
VariableCollection
|
||||
VariableCollection,
|
||||
VariableType,
|
||||
VariableValue
|
||||
} from './scene-graph'
|
||||
import type { Rect } from './types'
|
||||
|
||||
|
|
@ -1163,6 +1165,100 @@ export class FigmaAPI {
|
|||
return this.graph.variableCollections.get(id) ?? null
|
||||
}
|
||||
|
||||
// --- Variable/Collection CRUD ---
|
||||
|
||||
createVariable(
|
||||
name: string,
|
||||
type: VariableType,
|
||||
collectionId: string,
|
||||
value?: VariableValue
|
||||
): Variable {
|
||||
return this.graph.createVariable(name, type, collectionId, value)
|
||||
}
|
||||
|
||||
setVariableValue(variableId: string, modeId: string, value: VariableValue): void {
|
||||
const variable = this.graph.variables.get(variableId)
|
||||
if (!variable) throw new Error(`Variable "${variableId}" not found`)
|
||||
variable.valuesByMode[modeId] = value
|
||||
}
|
||||
|
||||
deleteVariable(id: string): void {
|
||||
this.graph.removeVariable(id)
|
||||
}
|
||||
|
||||
createVariableCollection(name: string): VariableCollection {
|
||||
return this.graph.createCollection(name)
|
||||
}
|
||||
|
||||
deleteVariableCollection(id: string): void {
|
||||
this.graph.removeCollection(id)
|
||||
}
|
||||
|
||||
bindVariable(nodeId: string, field: string, variableId: string): void {
|
||||
this.graph.bindVariable(nodeId, field, variableId)
|
||||
}
|
||||
|
||||
unbindVariable(nodeId: string, field: string): void {
|
||||
this.graph.unbindVariable(nodeId, field)
|
||||
}
|
||||
|
||||
// --- Boolean Operations ---
|
||||
|
||||
booleanOperation(
|
||||
operation: 'UNION' | 'SUBTRACT' | 'INTERSECT' | 'EXCLUDE',
|
||||
nodeIds: string[]
|
||||
): FigmaNodeProxy {
|
||||
if (nodeIds.length < 2) throw new Error('Need at least 2 nodes for boolean operation')
|
||||
const nodes = nodeIds.map((id) => this.graph.getNode(id))
|
||||
if (nodes.some((n) => !n)) throw new Error('One or more nodes not found')
|
||||
const first = nodes[0]!
|
||||
const parentId = first.parentId ?? this._currentPageId
|
||||
const group = this.graph.createNode('GROUP', parentId, {
|
||||
name: `Boolean ${operation.toLowerCase()}`,
|
||||
x: first.x,
|
||||
y: first.y,
|
||||
width: first.width,
|
||||
height: first.height
|
||||
})
|
||||
for (const id of nodeIds) {
|
||||
this.graph.reparentNode(id, group.id)
|
||||
}
|
||||
return this.wrapNode(group.id)
|
||||
}
|
||||
|
||||
// --- Flatten ---
|
||||
|
||||
flattenNode(nodeIds: string[]): FigmaNodeProxy {
|
||||
if (nodeIds.length === 0) throw new Error('Need at least 1 node to flatten')
|
||||
const first = this.graph.getNode(nodeIds[0])
|
||||
if (!first) throw new Error('Node not found')
|
||||
const parentId = first.parentId ?? this._currentPageId
|
||||
const vector = this.graph.createNode('VECTOR', parentId, {
|
||||
name: 'Flatten',
|
||||
x: first.x,
|
||||
y: first.y,
|
||||
width: first.width,
|
||||
height: first.height,
|
||||
fills: structuredClone(first.fills)
|
||||
})
|
||||
for (const id of nodeIds) {
|
||||
this.graph.deleteNode(id)
|
||||
}
|
||||
return this.wrapNode(vector.id)
|
||||
}
|
||||
|
||||
// --- Viewport ---
|
||||
|
||||
private _viewport = { x: 0, y: 0, zoom: 1 }
|
||||
|
||||
get viewport(): { center: { x: number; y: number }; zoom: number } {
|
||||
return { center: { x: this._viewport.x, y: this._viewport.y }, zoom: this._viewport.zoom }
|
||||
}
|
||||
|
||||
set viewport(v: { center: { x: number; y: number }; zoom: number }) {
|
||||
this._viewport = { x: v.center.x, y: v.center.y, zoom: v.zoom }
|
||||
}
|
||||
|
||||
// --- Stubs ---
|
||||
|
||||
async loadFontAsync(_fontName: FigmaFontName): Promise<void> {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ export * from './constants'
|
|||
|
||||
export {
|
||||
SceneGraph,
|
||||
generateId,
|
||||
type SceneNode,
|
||||
type NodeType,
|
||||
type Fill,
|
||||
|
|
|
|||
|
|
@ -295,7 +295,7 @@ export interface VariableCollection {
|
|||
|
||||
let nextLocalID = 1
|
||||
|
||||
function generateId(): string {
|
||||
export function generateId(): string {
|
||||
return `0:${nextLocalID++}`
|
||||
}
|
||||
|
||||
|
|
@ -467,6 +467,55 @@ export class SceneGraph {
|
|||
}
|
||||
}
|
||||
|
||||
createVariable(
|
||||
name: string,
|
||||
type: VariableType,
|
||||
collectionId: string,
|
||||
value?: VariableValue
|
||||
): Variable {
|
||||
const collection = this.variableCollections.get(collectionId)
|
||||
if (!collection) throw new Error(`Collection "${collectionId}" not found`)
|
||||
const id = generateId()
|
||||
const defaultValue =
|
||||
value ??
|
||||
(type === 'COLOR'
|
||||
? { r: 0, g: 0, b: 0, a: 1 }
|
||||
: type === 'FLOAT'
|
||||
? 0
|
||||
: type === 'BOOLEAN'
|
||||
? false
|
||||
: '')
|
||||
const valuesByMode: Record<string, VariableValue> = {}
|
||||
for (const mode of collection.modes) {
|
||||
valuesByMode[mode.modeId] = structuredClone(defaultValue)
|
||||
}
|
||||
const variable: Variable = {
|
||||
id,
|
||||
name,
|
||||
type,
|
||||
collectionId,
|
||||
valuesByMode,
|
||||
description: '',
|
||||
hiddenFromPublishing: false
|
||||
}
|
||||
this.addVariable(variable)
|
||||
return variable
|
||||
}
|
||||
|
||||
createCollection(name: string): VariableCollection {
|
||||
const id = generateId()
|
||||
const modeId = generateId()
|
||||
const collection: VariableCollection = {
|
||||
id,
|
||||
name,
|
||||
modes: [{ modeId, name: 'Mode 1' }],
|
||||
defaultModeId: modeId,
|
||||
variableIds: []
|
||||
}
|
||||
this.addCollection(collection)
|
||||
return collection
|
||||
}
|
||||
|
||||
removeCollection(id: string): void {
|
||||
const collection = this.variableCollections.get(id)
|
||||
if (collection) {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue