@dannote/figma-use 0.5.8 → 0.6.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 CHANGED
@@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.6.0] - 2026-01-18
11
+
12
+ ### Added
13
+
14
+ - **`node bounds`** — get node position, size, center point, edges
15
+ - **`path` commands** — vector path manipulation:
16
+ - `path get <id>` — read SVG path data
17
+ - `path set <id> "M..."` — replace path data
18
+ - `path move <id> --dx --dy` — translate all points
19
+ - `path scale <id> --factor` — scale from center
20
+ - `path flip <id> --axis x|y` — mirror horizontally/vertically
21
+ - Uses [svgpath](https://github.com/fontello/svgpath) library for path transformations
22
+ - Path command tests
23
+
24
+ ## [0.5.9] - 2026-01-18
25
+
26
+ ### Changed
27
+
28
+ - Version now read from package.json instead of hardcoded
29
+
10
30
  ## [0.5.8] - 2026-01-18
11
31
 
12
32
  ### Added
package/README.md CHANGED
@@ -230,11 +230,23 @@ figma-use set effect <id> --type DROP_SHADOW --radius 10 --color "#00000040"
230
230
  figma-use node get <id> # Get node properties
231
231
  figma-use node tree # Page structure as readable tree
232
232
  figma-use node children <id> # List children
233
+ figma-use node bounds <id> # Position, size, center point
233
234
  figma-use find --name "Button" # Find by name
234
235
  figma-use find --type FRAME # Find by type
235
236
  figma-use selection get # Current selection
236
237
  ```
237
238
 
239
+ ### Vector Paths
240
+
241
+ ```bash
242
+ figma-use create vector --x 0 --y 0 --path "M 0 0 L 100 50 L 0 100 Z" --fill "#F00"
243
+ figma-use path get <id> # Read path data
244
+ figma-use path set <id> "M 0 0 ..." # Replace path
245
+ figma-use path move <id> --dx 10 --dy -5 # Translate points
246
+ figma-use path scale <id> --factor 1.5 # Scale from center
247
+ figma-use path flip <id> --axis x # Mirror horizontally
248
+ ```
249
+
238
250
  ### Export
239
251
 
240
252
  ```bash
package/SKILL.md CHANGED
@@ -315,3 +315,76 @@ figma-use set text "I123:456;789:10" "New text" # Modify text inside instance
315
315
  figma-use find --type FRAME 2>&1 | grep "stroke: #EF4444" # Red border
316
316
  figma-use find --type TEXT 2>&1 | grep "Bold" # Bold text
317
317
  ```
318
+
319
+ ---
320
+
321
+ ## Vector Drawing
322
+
323
+ Work iteratively: bounds → draw → screenshot → adjust → repeat.
324
+
325
+ ### Path Commands
326
+
327
+ ```bash
328
+ figma-use node bounds <id> # Position, size, center
329
+ figma-use create vector --path "M 0 0 L 100 50 Z" --fill "#F00"
330
+ figma-use path get <id> # Read path data
331
+ figma-use path set <id> "M 0 0 L 100 100 Z" # Replace path
332
+ figma-use path move <id> --dx 10 --dy -5 # Translate points
333
+ figma-use path scale <id> --factor 1.5 # Scale from center
334
+ figma-use path flip <id> --axis x # Mirror horizontally
335
+ figma-use path flip <id> --axis y # Mirror vertically
336
+ ```
337
+
338
+ ### SVG Path Syntax
339
+
340
+ ```
341
+ M x y Move to L x y Line to
342
+ H x Horizontal to V y Vertical to
343
+ C x1 y1 x2 y2 x y Cubic Bezier Q x1 y1 x y Quadratic Bezier
344
+ Z Close path
345
+ ```
346
+
347
+ ### Iterative Workflow
348
+
349
+ ```bash
350
+ # 1. Create canvas, get bounds
351
+ figma-use create frame --width 300 --height 300 --fill "#F0F4FF" --name "Drawing"
352
+ figma-use node bounds 123:456
353
+
354
+ # 2. Draw shape with Bezier curves
355
+ figma-use create vector --path "M 80 180 C 50 180 50 140 80 140 C 180 130 200 150 180 195 Z" --fill "#FFD700" --parent 123:456
356
+
357
+ # 3. Screenshot to verify
358
+ figma-use export node 123:456 --output /tmp/check.png
359
+
360
+ # 4. Adjust if needed
361
+ figma-use path scale 123:457 --factor 0.9
362
+ figma-use path move 123:457 --dx 20 --dy 0
363
+
364
+ # 5. Get bounds of first shape, position next relative to it
365
+ figma-use node bounds 123:457
366
+ ```
367
+
368
+ ### Common Shapes
369
+
370
+ ```bash
371
+ # Triangle
372
+ figma-use create vector --path "M 50 0 L 100 100 L 0 100 Z" --fill "#F00"
373
+
374
+ # Star
375
+ figma-use create vector --path "M 50 0 L 61 35 L 98 35 L 68 57 L 79 91 L 50 70 L 21 91 L 32 57 L 2 35 L 39 35 Z" --fill "#FFD700"
376
+
377
+ # Heart
378
+ figma-use create vector --path "M 50 90 C 20 60 0 30 25 10 C 40 0 50 10 50 25 C 50 10 60 0 75 10 C 100 30 80 60 50 90 Z" --fill "#E11D48"
379
+
380
+ # Arrow right
381
+ figma-use create vector --path "M 0 20 L 60 20 L 60 10 L 80 25 L 60 40 L 60 30 L 0 30 Z" --fill "#000"
382
+ ```
383
+
384
+ ### Complex Illustrations
385
+
386
+ For intricate artwork, import SVG instead:
387
+
388
+ ```bash
389
+ figma-use import --svg "$(cat icon.svg)"
390
+ ```
package/dist/cli/index.js CHANGED
@@ -1106,6 +1106,15 @@ function formatResult(result, context) {
1106
1106
  if ("deleted" in obj && obj.deleted !== undefined) {
1107
1107
  return formatDeleted({ deleted: Boolean(obj.deleted) });
1108
1108
  }
1109
+ if (context === "path" && "paths" in obj) {
1110
+ const paths = obj.paths;
1111
+ return ok("Path updated") + `
1112
+ ` + paths.map((p) => dim(p.data)).join(`
1113
+ `);
1114
+ }
1115
+ if ("updated" in obj && obj.updated === true) {
1116
+ return ok("Updated");
1117
+ }
1109
1118
  if (context === "export" || "data" in obj) {
1110
1119
  return formatExport({ data: String(obj.data ?? ""), filename: obj.filename });
1111
1120
  }
@@ -22779,19 +22788,20 @@ __export(exports_commands, {
22779
22788
  variable: () => variable_default,
22780
22789
  style: () => style_default,
22781
22790
  status: () => status_default,
22782
- set: () => set_default,
22791
+ set: () => set_default2,
22783
22792
  selection: () => selection_default,
22784
22793
  render: () => render_default,
22785
22794
  proxy: () => proxy_default,
22786
22795
  profile: () => profile_default,
22787
22796
  plugin: () => plugin_default,
22797
+ path: () => path_default,
22788
22798
  page: () => page_default2,
22789
22799
  node: () => node_default,
22790
22800
  me: () => me_default,
22791
22801
  mcp: () => mcp_default,
22792
22802
  import: () => import_default,
22793
22803
  group: () => group_default,
22794
- get: () => get_default2,
22804
+ get: () => get_default3,
22795
22805
  font: () => font_default2,
22796
22806
  find: () => find_default,
22797
22807
  file: () => file_default,
@@ -29200,6 +29210,31 @@ var to_component_default = defineCommand({
29200
29210
  }
29201
29211
  });
29202
29212
 
29213
+ // packages/cli/src/commands/node/bounds.ts
29214
+ var bounds_default = defineCommand({
29215
+ meta: { description: "Get node bounding box" },
29216
+ args: {
29217
+ id: { type: "positional", description: "Node ID", required: true },
29218
+ json: { type: "boolean", description: "Output as JSON" }
29219
+ },
29220
+ async run({ args }) {
29221
+ try {
29222
+ const result = await sendCommand("node-bounds", { id: args.id });
29223
+ if (args.json) {
29224
+ printResult(result, true);
29225
+ } else {
29226
+ const b3 = result;
29227
+ console.log(`x: ${b3.x}, y: ${b3.y}`);
29228
+ console.log(`width: ${b3.width}, height: ${b3.height}`);
29229
+ console.log(`center: (${b3.centerX}, ${b3.centerY})`);
29230
+ console.log(`right: ${b3.right}, bottom: ${b3.bottom}`);
29231
+ }
29232
+ } catch (e6) {
29233
+ handleError(e6);
29234
+ }
29235
+ }
29236
+ });
29237
+
29203
29238
  // packages/cli/src/commands/node/index.ts
29204
29239
  var node_default = defineCommand({
29205
29240
  meta: { description: "Node operations" },
@@ -29213,7 +29248,138 @@ var node_default = defineCommand({
29213
29248
  move: move_default,
29214
29249
  resize: resize_default,
29215
29250
  "set-parent": set_parent_default,
29216
- "to-component": to_component_default
29251
+ "to-component": to_component_default,
29252
+ bounds: bounds_default
29253
+ }
29254
+ });
29255
+ // packages/cli/src/commands/path/get.ts
29256
+ var get_default2 = defineCommand({
29257
+ meta: { description: "Get vector path data" },
29258
+ args: {
29259
+ id: { type: "positional", description: "Vector node ID", required: true },
29260
+ json: { type: "boolean", description: "Output as JSON" }
29261
+ },
29262
+ async run({ args }) {
29263
+ try {
29264
+ const result = await sendCommand("path-get", { id: args.id });
29265
+ if (args.json) {
29266
+ printResult(result, true);
29267
+ } else {
29268
+ for (const p4 of result.paths) {
29269
+ console.log(p4.data);
29270
+ }
29271
+ }
29272
+ } catch (e6) {
29273
+ handleError(e6);
29274
+ }
29275
+ }
29276
+ });
29277
+
29278
+ // packages/cli/src/commands/path/set.ts
29279
+ init_output();
29280
+ var set_default = defineCommand({
29281
+ meta: { description: "Set vector path data" },
29282
+ args: {
29283
+ id: { type: "positional", description: "Vector node ID", required: true },
29284
+ path: { type: "positional", description: "SVG path data", required: true },
29285
+ windingRule: { type: "string", description: "Winding rule (NONZERO or EVENODD)", default: "NONZERO" },
29286
+ json: { type: "boolean", description: "Output as JSON" }
29287
+ },
29288
+ async run({ args }) {
29289
+ try {
29290
+ const result = await sendCommand("path-set", {
29291
+ id: args.id,
29292
+ path: args.path,
29293
+ windingRule: args.windingRule
29294
+ });
29295
+ printResult(result, args.json, "update");
29296
+ } catch (e6) {
29297
+ handleError(e6);
29298
+ }
29299
+ }
29300
+ });
29301
+
29302
+ // packages/cli/src/commands/path/move.ts
29303
+ init_output();
29304
+ var move_default2 = defineCommand({
29305
+ meta: { description: "Move all path points by offset" },
29306
+ args: {
29307
+ id: { type: "positional", description: "Vector node ID", required: true },
29308
+ dx: { type: "string", description: "X offset", default: "0" },
29309
+ dy: { type: "string", description: "Y offset", default: "0" },
29310
+ json: { type: "boolean", description: "Output as JSON" }
29311
+ },
29312
+ async run({ args }) {
29313
+ try {
29314
+ const result = await sendCommand("path-move", {
29315
+ id: args.id,
29316
+ dx: Number(args.dx),
29317
+ dy: Number(args.dy)
29318
+ });
29319
+ printResult(result, args.json, "path");
29320
+ } catch (e6) {
29321
+ handleError(e6);
29322
+ }
29323
+ }
29324
+ });
29325
+
29326
+ // packages/cli/src/commands/path/scale.ts
29327
+ init_output();
29328
+ var scale_default = defineCommand({
29329
+ meta: { description: "Scale path from center" },
29330
+ args: {
29331
+ id: { type: "positional", description: "Vector node ID", required: true },
29332
+ factor: { type: "string", description: "Scale factor (e.g., 1.5 for 150%)", required: true },
29333
+ json: { type: "boolean", description: "Output as JSON" }
29334
+ },
29335
+ async run({ args }) {
29336
+ try {
29337
+ const result = await sendCommand("path-scale", {
29338
+ id: args.id,
29339
+ factor: Number(args.factor)
29340
+ });
29341
+ printResult(result, args.json, "path");
29342
+ } catch (e6) {
29343
+ handleError(e6);
29344
+ }
29345
+ }
29346
+ });
29347
+
29348
+ // packages/cli/src/commands/path/flip.ts
29349
+ init_output();
29350
+ var flip_default = defineCommand({
29351
+ meta: { description: "Flip path horizontally or vertically" },
29352
+ args: {
29353
+ id: { type: "positional", description: "Vector node ID", required: true },
29354
+ axis: { type: "string", description: "Axis to flip (x or y)", required: true },
29355
+ json: { type: "boolean", description: "Output as JSON" }
29356
+ },
29357
+ async run({ args }) {
29358
+ try {
29359
+ if (args.axis !== "x" && args.axis !== "y") {
29360
+ console.error('Axis must be "x" or "y"');
29361
+ process.exit(1);
29362
+ }
29363
+ const result = await sendCommand("path-flip", {
29364
+ id: args.id,
29365
+ axis: args.axis
29366
+ });
29367
+ printResult(result, args.json, "path");
29368
+ } catch (e6) {
29369
+ handleError(e6);
29370
+ }
29371
+ }
29372
+ });
29373
+
29374
+ // packages/cli/src/commands/path/index.ts
29375
+ var path_default = defineCommand({
29376
+ meta: { description: "Vector path operations" },
29377
+ subCommands: {
29378
+ get: get_default2,
29379
+ set: set_default,
29380
+ move: move_default2,
29381
+ scale: scale_default,
29382
+ flip: flip_default
29217
29383
  }
29218
29384
  });
29219
29385
  // packages/cli/src/commands/create/rect.ts
@@ -30115,7 +30281,7 @@ var minmax_default = defineCommand({
30115
30281
  });
30116
30282
 
30117
30283
  // packages/cli/src/commands/set/index.ts
30118
- var set_default = defineCommand({
30284
+ var set_default2 = defineCommand({
30119
30285
  meta: { description: "Set node properties" },
30120
30286
  subCommands: {
30121
30287
  fill: fill_default,
@@ -30194,7 +30360,7 @@ var styles_default = defineCommand({
30194
30360
  });
30195
30361
 
30196
30362
  // packages/cli/src/commands/get/index.ts
30197
- var get_default2 = defineCommand({
30363
+ var get_default3 = defineCommand({
30198
30364
  meta: { description: "Get document info" },
30199
30365
  subCommands: {
30200
30366
  pages: pages_default,
@@ -30203,7 +30369,7 @@ var get_default2 = defineCommand({
30203
30369
  }
30204
30370
  });
30205
30371
  // packages/cli/src/commands/selection/get.ts
30206
- var get_default3 = defineCommand({
30372
+ var get_default4 = defineCommand({
30207
30373
  meta: { description: "Get selected nodes" },
30208
30374
  args: {
30209
30375
  json: { type: "boolean", description: "Output as JSON" }
@@ -30219,7 +30385,7 @@ var get_default3 = defineCommand({
30219
30385
  });
30220
30386
 
30221
30387
  // packages/cli/src/commands/selection/set.ts
30222
- var set_default2 = defineCommand({
30388
+ var set_default3 = defineCommand({
30223
30389
  meta: { description: "Set selection" },
30224
30390
  args: {
30225
30391
  ids: { type: "positional", description: "Node IDs (space separated)", required: true },
@@ -30240,8 +30406,8 @@ var set_default2 = defineCommand({
30240
30406
  var selection_default = defineCommand({
30241
30407
  meta: { description: "Selection operations" },
30242
30408
  subCommands: {
30243
- get: get_default3,
30244
- set: set_default2
30409
+ get: get_default4,
30410
+ set: set_default3
30245
30411
  }
30246
30412
  });
30247
30413
  // packages/cli/src/export-guard.ts
@@ -30439,7 +30605,7 @@ var list_default2 = defineCommand({
30439
30605
  });
30440
30606
 
30441
30607
  // packages/cli/src/commands/page/set.ts
30442
- var set_default3 = defineCommand({
30608
+ var set_default4 = defineCommand({
30443
30609
  meta: { description: "Switch to page by ID or name" },
30444
30610
  args: {
30445
30611
  page: { type: "positional", description: "Page ID or name", required: true },
@@ -30460,11 +30626,11 @@ var page_default2 = defineCommand({
30460
30626
  meta: { description: "Page operations" },
30461
30627
  subCommands: {
30462
30628
  list: list_default2,
30463
- set: set_default3
30629
+ set: set_default4
30464
30630
  }
30465
30631
  });
30466
30632
  // packages/cli/src/commands/viewport/get.ts
30467
- var get_default4 = defineCommand({
30633
+ var get_default5 = defineCommand({
30468
30634
  meta: { description: "Get viewport position and zoom" },
30469
30635
  args: {
30470
30636
  json: { type: "boolean", description: "Output as JSON" }
@@ -30480,7 +30646,7 @@ var get_default4 = defineCommand({
30480
30646
  });
30481
30647
 
30482
30648
  // packages/cli/src/commands/viewport/set.ts
30483
- var set_default4 = defineCommand({
30649
+ var set_default5 = defineCommand({
30484
30650
  meta: { description: "Set viewport position and zoom" },
30485
30651
  args: {
30486
30652
  x: { type: "string", description: "X position" },
@@ -30524,8 +30690,8 @@ var zoom_to_fit_default = defineCommand({
30524
30690
  var viewport_default = defineCommand({
30525
30691
  meta: { description: "Viewport operations" },
30526
30692
  subCommands: {
30527
- get: get_default4,
30528
- set: set_default4,
30693
+ get: get_default5,
30694
+ set: set_default5,
30529
30695
  "zoom-to-fit": zoom_to_fit_default
30530
30696
  }
30531
30697
  });
@@ -30547,7 +30713,7 @@ var list_default3 = defineCommand({
30547
30713
  });
30548
30714
 
30549
30715
  // packages/cli/src/commands/variable/get.ts
30550
- var get_default5 = defineCommand({
30716
+ var get_default6 = defineCommand({
30551
30717
  meta: { description: "Get variable by ID" },
30552
30718
  args: {
30553
30719
  id: { type: "positional", description: "Variable ID", required: true },
@@ -30589,7 +30755,7 @@ var create_default2 = defineCommand({
30589
30755
  });
30590
30756
 
30591
30757
  // packages/cli/src/commands/variable/set.ts
30592
- var set_default5 = defineCommand({
30758
+ var set_default6 = defineCommand({
30593
30759
  meta: { description: "Set variable value for mode" },
30594
30760
  args: {
30595
30761
  id: { type: "positional", description: "Variable ID", required: true },
@@ -30656,9 +30822,9 @@ var variable_default = defineCommand({
30656
30822
  meta: { description: "Variable operations" },
30657
30823
  subCommands: {
30658
30824
  list: list_default3,
30659
- get: get_default5,
30825
+ get: get_default6,
30660
30826
  create: create_default2,
30661
- set: set_default5,
30827
+ set: set_default6,
30662
30828
  delete: delete_default2,
30663
30829
  bind: bind_default
30664
30830
  }
@@ -30680,7 +30846,7 @@ var list_default4 = defineCommand({
30680
30846
  });
30681
30847
 
30682
30848
  // packages/cli/src/commands/collection/get.ts
30683
- var get_default6 = defineCommand({
30849
+ var get_default7 = defineCommand({
30684
30850
  meta: { description: "Get variable collection by ID" },
30685
30851
  args: {
30686
30852
  id: { type: "positional", description: "Collection ID", required: true },
@@ -30735,7 +30901,7 @@ var collection_default = defineCommand({
30735
30901
  meta: { description: "Variable collection operations" },
30736
30902
  subCommands: {
30737
30903
  list: list_default4,
30738
- get: get_default6,
30904
+ get: get_default7,
30739
30905
  create: create_default3,
30740
30906
  delete: delete_default3
30741
30907
  }
@@ -31282,12 +31448,15 @@ var font_default2 = defineCommand({
31282
31448
  list: list_default8
31283
31449
  }
31284
31450
  });
31451
+ // package.json
31452
+ var version = "0.6.0";
31453
+
31285
31454
  // packages/cli/src/index.ts
31286
31455
  var main = defineCommand({
31287
31456
  meta: {
31288
31457
  name: "figma-use",
31289
31458
  description: "Control Figma from the command line. Supports JSX rendering with components and variants \u2014 see `figma-use render --examples`",
31290
- version: "0.5.0"
31459
+ version
31291
31460
  },
31292
31461
  subCommands: exports_commands
31293
31462
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dannote/figma-use",
3
- "version": "0.5.8",
3
+ "version": "0.6.0",
4
4
  "description": "Control Figma from the command line. Full read/write access for AI agents.",
5
5
  "type": "module",
6
6
  "bin": {