@wz927/codedesign 0.3.6 → 0.3.7

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.
@@ -8,11 +8,53 @@
8
8
  * Incoming messages from CLI (via UI): { id, op, params }
9
9
  * Outgoing responses to CLI (via UI): { id, ok, data?, error? }
10
10
  */
11
- const PLUGIN_VERSION = '0.1.0';
11
+ const PLUGIN_VERSION = '0.2.0';
12
12
  figma.showUI(__html__, { width: 360, height: 360, themeColors: true });
13
13
  function send(msg) {
14
14
  figma.ui.postMessage({ __kind: 'to_cli', msg });
15
15
  }
16
+ // ─── Async API compatibility ─────────────────────────────────────────────
17
+ // Figma made many sync APIs deprecated in 2024 (dynamic-page mode throws on
18
+ // them). Prefer async versions; fall back to sync for older Figma builds.
19
+ async function getNode(id) {
20
+ const anyFigma = figma;
21
+ if (typeof anyFigma.getNodeByIdAsync === 'function') {
22
+ return await anyFigma.getNodeByIdAsync(id);
23
+ }
24
+ return figma.getNodeById(id);
25
+ }
26
+ async function getLocalPaint() {
27
+ const anyFigma = figma;
28
+ if (typeof anyFigma.getLocalPaintStylesAsync === 'function') {
29
+ return await anyFigma.getLocalPaintStylesAsync();
30
+ }
31
+ return figma.getLocalPaintStyles();
32
+ }
33
+ async function getLocalText() {
34
+ const anyFigma = figma;
35
+ if (typeof anyFigma.getLocalTextStylesAsync === 'function') {
36
+ return await anyFigma.getLocalTextStylesAsync();
37
+ }
38
+ return figma.getLocalTextStyles();
39
+ }
40
+ async function getLocalEffect() {
41
+ const anyFigma = figma;
42
+ if (typeof anyFigma.getLocalEffectStylesAsync === 'function') {
43
+ return await anyFigma.getLocalEffectStylesAsync();
44
+ }
45
+ return figma.getLocalEffectStyles();
46
+ }
47
+ async function findAllComponents(types) {
48
+ const anyFigma = figma;
49
+ // dynamic-page mode requires loading all pages before findAll-style queries
50
+ if (typeof anyFigma.loadAllPagesAsync === 'function') {
51
+ try {
52
+ await anyFigma.loadAllPagesAsync();
53
+ }
54
+ catch { }
55
+ }
56
+ return figma.root.findAllWithCriteria({ types: types });
57
+ }
16
58
  async function sendHello() {
17
59
  var _a;
18
60
  const page = figma.currentPage;
@@ -75,10 +117,10 @@ function toPaint(p) {
75
117
  }
76
118
  throw new Error('Unsupported paint type ' + p.type);
77
119
  }
78
- function getParent(id) {
120
+ async function getParent(id) {
79
121
  if (!id)
80
122
  return figma.currentPage;
81
- const n = figma.getNodeById(id);
123
+ const n = await getNode(id);
82
124
  if (!n)
83
125
  throw new Error('Parent not found: ' + id);
84
126
  if (!('appendChild' in n))
@@ -152,7 +194,7 @@ async function handle(req) {
152
194
  case 'getSelection':
153
195
  return figma.currentPage.selection.map(nodeSummary);
154
196
  case 'getNode': {
155
- const n = figma.getNodeById(p.id);
197
+ const n = await getNode(p.id);
156
198
  if (!n)
157
199
  throw new Error('Not found: ' + p.id);
158
200
  const base = nodeSummary(n);
@@ -166,14 +208,14 @@ async function handle(req) {
166
208
  }
167
209
  case 'createFrame': {
168
210
  const frame = figma.createFrame();
169
- getParent(p.parentId).appendChild(frame);
211
+ (await getParent(p.parentId)).appendChild(frame);
170
212
  applyFrameProps(frame, p);
171
213
  return nodeSummary(frame);
172
214
  }
173
215
  case 'createText': {
174
216
  const font = await loadFontFor(p);
175
217
  const t = figma.createText();
176
- getParent(p.parentId).appendChild(t);
218
+ (await getParent(p.parentId)).appendChild(t);
177
219
  t.fontName = { family: font.family, style: font.style };
178
220
  t.characters = (_b = p.characters) !== null && _b !== void 0 ? _b : '';
179
221
  if (typeof p.x === 'number')
@@ -200,7 +242,7 @@ async function handle(req) {
200
242
  }
201
243
  case 'createRectangle': {
202
244
  const r = figma.createRectangle();
203
- getParent(p.parentId).appendChild(r);
245
+ (await getParent(p.parentId)).appendChild(r);
204
246
  if (typeof p.x === 'number')
205
247
  r.x = p.x;
206
248
  if (typeof p.y === 'number')
@@ -220,7 +262,7 @@ async function handle(req) {
220
262
  }
221
263
  case 'createEllipse': {
222
264
  const e = figma.createEllipse();
223
- getParent(p.parentId).appendChild(e);
265
+ (await getParent(p.parentId)).appendChild(e);
224
266
  if (typeof p.x === 'number')
225
267
  e.x = p.x;
226
268
  if (typeof p.y === 'number')
@@ -237,7 +279,7 @@ async function handle(req) {
237
279
  return nodeSummary(e);
238
280
  }
239
281
  case 'setProps': {
240
- const n = figma.getNodeById(p.id);
282
+ const n = (await getNode(p.id));
241
283
  if (!n)
242
284
  throw new Error('Not found: ' + p.id);
243
285
  const patch = (_c = p.patch) !== null && _c !== void 0 ? _c : {};
@@ -254,7 +296,6 @@ async function handle(req) {
254
296
  if (patch[k] !== undefined)
255
297
  n[k] = patch[k];
256
298
  }
257
- // cornerRadius: number → uniform; [tl,tr,br,bl] → per-corner
258
299
  if (patch.cornerRadius !== undefined) {
259
300
  const cr = patch.cornerRadius;
260
301
  if (typeof cr === 'number') {
@@ -267,7 +308,6 @@ async function handle(req) {
267
308
  n.bottomLeftRadius = cr[3];
268
309
  }
269
310
  }
270
- // Also support explicit per-corner keys
271
311
  if (patch.topLeftRadius !== undefined)
272
312
  n.topLeftRadius = patch.topLeftRadius;
273
313
  if (patch.topRightRadius !== undefined)
@@ -283,7 +323,7 @@ async function handle(req) {
283
323
  return nodeSummary(n);
284
324
  }
285
325
  case 'setAutoLayout': {
286
- const n = figma.getNodeById(p.id);
326
+ const n = (await getNode(p.id));
287
327
  if (!n)
288
328
  throw new Error('Not found: ' + p.id);
289
329
  n.layoutMode = p.layoutMode;
@@ -302,27 +342,27 @@ async function handle(req) {
302
342
  return nodeSummary(n);
303
343
  }
304
344
  case 'appendChild': {
305
- const par = getParent(p.parentId);
306
- const child = figma.getNodeById(p.childId);
345
+ const par = await getParent(p.parentId);
346
+ const child = (await getNode(p.childId));
307
347
  if (!child)
308
348
  throw new Error('Child not found');
309
349
  par.appendChild(child);
310
350
  return nodeSummary(child);
311
351
  }
312
352
  case 'deleteNode': {
313
- const n = figma.getNodeById(p.id);
353
+ const n = await getNode(p.id);
314
354
  if (!n)
315
355
  throw new Error('Not found');
316
356
  n.remove();
317
357
  return { deleted: p.id };
318
358
  }
319
359
  case 'cloneNode': {
320
- const n = figma.getNodeById(p.id);
360
+ const n = (await getNode(p.id));
321
361
  if (!n || !('clone' in n))
322
362
  throw new Error('Cannot clone');
323
363
  const c = n.clone();
324
364
  if (p.parentId)
325
- getParent(p.parentId).appendChild(c);
365
+ (await getParent(p.parentId)).appendChild(c);
326
366
  else
327
367
  (_f = n.parent) === null || _f === void 0 ? void 0 : _f.appendChild(c);
328
368
  if (typeof p.dx === 'number')
@@ -331,20 +371,21 @@ async function handle(req) {
331
371
  c.y += p.dy;
332
372
  return nodeSummary(c);
333
373
  }
334
- case 'listStyles':
374
+ case 'listStyles': {
375
+ const [paint, text, effect] = await Promise.all([getLocalPaint(), getLocalText(), getLocalEffect()]);
335
376
  return {
336
- paint: figma.getLocalPaintStyles().map((s) => ({ id: s.id, key: s.key, name: s.name })),
337
- text: figma.getLocalTextStyles().map((s) => ({ id: s.id, key: s.key, name: s.name })),
338
- effect: figma.getLocalEffectStyles().map((s) => ({ id: s.id, key: s.key, name: s.name })),
377
+ paint: paint.map((s) => ({ id: s.id, key: s.key, name: s.name })),
378
+ text: text.map((s) => ({ id: s.id, key: s.key, name: s.name })),
379
+ effect: effect.map((s) => ({ id: s.id, key: s.key, name: s.name })),
339
380
  };
340
- case 'listComponents':
341
- return figma.root.findAllWithCriteria({ types: ['COMPONENT', 'COMPONENT_SET'] }).map((c) => ({
342
- id: c.id, key: c.key, name: c.name, type: c.type,
343
- }));
381
+ }
382
+ case 'listComponents': {
383
+ const comps = await findAllComponents(['COMPONENT', 'COMPONENT_SET']);
384
+ return comps.map((c) => ({ id: c.id, key: c.key, name: c.name, type: c.type }));
385
+ }
344
386
  case 'createInstance': {
345
- const all = figma.root.findAllWithCriteria({ types: ['COMPONENT'] });
387
+ const all = (await findAllComponents(['COMPONENT']));
346
388
  let comp = all.find((c) => c.key === p.componentKey || c.id === p.componentKey);
347
- // If not found locally, try to import from a team library by component key.
348
389
  if (!comp && p.componentKey) {
349
390
  try {
350
391
  comp = await figma.importComponentByKeyAsync(p.componentKey);
@@ -354,7 +395,7 @@ async function handle(req) {
354
395
  if (!comp)
355
396
  throw new Error('Component not found: ' + p.componentKey);
356
397
  const inst = comp.createInstance();
357
- getParent(p.parentId).appendChild(inst);
398
+ (await getParent(p.parentId)).appendChild(inst);
358
399
  if (typeof p.x === 'number')
359
400
  inst.x = p.x;
360
401
  if (typeof p.y === 'number')
@@ -364,10 +405,8 @@ async function handle(req) {
364
405
  return nodeSummary(inst);
365
406
  }
366
407
  case 'createVector': {
367
- // Draw an icon/shape using SVG path data.
368
- // params: { parentId?, vectorPaths: [{windingRule, data}], fills, strokes, strokeWeight, x, y, width, height, name }
369
408
  const vec = figma.createVector();
370
- getParent(p.parentId).appendChild(vec);
409
+ (await getParent(p.parentId)).appendChild(vec);
371
410
  if (p.vectorPaths)
372
411
  vec.vectorPaths = p.vectorPaths;
373
412
  if (p.fills)
@@ -387,10 +426,8 @@ async function handle(req) {
387
426
  return nodeSummary(vec);
388
427
  }
389
428
  case 'createLine': {
390
- // A simple line / divider.
391
- // params: { parentId?, x, y, length, vertical?, strokeWeight, color:{r,g,b}, name }
392
429
  const line = figma.createLine();
393
- getParent(p.parentId).appendChild(line);
430
+ (await getParent(p.parentId)).appendChild(line);
394
431
  if (typeof p.x === 'number')
395
432
  line.x = p.x;
396
433
  if (typeof p.y === 'number')
@@ -406,23 +443,28 @@ async function handle(req) {
406
443
  return nodeSummary(line);
407
444
  }
408
445
  case 'applyStyle': {
409
- // Apply a local Figma style (paint/text/effect) to a node by style id.
410
- // params: { id, fillStyleId?, strokeStyleId?, textStyleId?, effectStyleId? }
411
- const n = figma.getNodeById(p.id);
446
+ const n = (await getNode(p.id));
412
447
  if (!n)
413
448
  throw new Error('Not found: ' + p.id);
449
+ // In dynamic-page mode, style setters are async (setFillStyleIdAsync etc.)
450
+ async function setStyle(prop, async, val) {
451
+ if (typeof n[async] === 'function')
452
+ await n[async](val);
453
+ else
454
+ n[prop] = val;
455
+ }
414
456
  if (p.fillStyleId !== undefined)
415
- n.fillStyleId = p.fillStyleId;
457
+ await setStyle('fillStyleId', 'setFillStyleIdAsync', p.fillStyleId);
416
458
  if (p.strokeStyleId !== undefined)
417
- n.strokeStyleId = p.strokeStyleId;
459
+ await setStyle('strokeStyleId', 'setStrokeStyleIdAsync', p.strokeStyleId);
418
460
  if (p.textStyleId !== undefined)
419
- n.textStyleId = p.textStyleId;
461
+ await setStyle('textStyleId', 'setTextStyleIdAsync', p.textStyleId);
420
462
  if (p.effectStyleId !== undefined)
421
- n.effectStyleId = p.effectStyleId;
463
+ await setStyle('effectStyleId', 'setEffectStyleIdAsync', p.effectStyleId);
422
464
  return nodeSummary(n);
423
465
  }
424
466
  case 'viewport': {
425
- const n = figma.getNodeById(p.id);
467
+ const n = (await getNode(p.id));
426
468
  if (!n)
427
469
  throw new Error('Not found');
428
470
  figma.viewport.scrollAndZoomIntoView([n]);
@@ -431,7 +473,7 @@ async function handle(req) {
431
473
  return { ok: true };
432
474
  }
433
475
  case 'exportNode': {
434
- const n = figma.getNodeById(p.id);
476
+ const n = (await getNode(p.id));
435
477
  if (!n || !('exportAsync' in n))
436
478
  throw new Error('Node not exportable');
437
479
  const format = ((_j = p.format) !== null && _j !== void 0 ? _j : 'PNG').toUpperCase();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wz927/codedesign",
3
- "version": "0.3.6",
3
+ "version": "0.3.7",
4
4
  "description": "Terminal AI agent for designers — drive Figma through natural-language conversation.",
5
5
  "type": "module",
6
6
  "bin": {