figmatk 0.3.8 → 0.3.9

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.
@@ -13,7 +13,7 @@
13
13
  {
14
14
  "name": "figmatk",
15
15
  "description": "Swiss Army Knife for Figma Files (.deck)",
16
- "version": "0.3.8",
16
+ "version": "0.3.9",
17
17
  "author": {
18
18
  "name": "FigmaTK Contributors"
19
19
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "figmatk",
3
- "version": "0.3.3",
3
+ "version": "0.3.9",
4
4
  "description": "Create and edit Figma Slides .deck files programmatically — no Figma API required",
5
5
  "author": {
6
6
  "name": "FigmaTK Contributors"
@@ -500,6 +500,100 @@ function renderLine(deck, node) {
500
500
  return `<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" stroke="${stroke}" stroke-width="${sw}"/>`;
501
501
  }
502
502
 
503
+ /**
504
+ * VECTOR — decode fillGeometry/strokeGeometry commandsBlob binary to SVG paths.
505
+ *
506
+ * Blob format: [cmdByte][float32LE params...]
507
+ * 0x01 = moveTo (x, y)
508
+ * 0x02 = lineTo (x, y)
509
+ * 0x04 = cubicTo (c1x, c1y, c2x, c2y, x, y)
510
+ * 0x00 = close
511
+ *
512
+ * Coordinates are in node-size space. The full affine transform matrix is used
513
+ * to position, scale, and rotate the vector in the slide.
514
+ */
515
+ function renderVector(deck, node) {
516
+ const t = node.transform ?? {};
517
+ const m00 = t.m00 ?? 1, m01 = t.m01 ?? 0, m02 = t.m02 ?? 0;
518
+ const m10 = t.m10 ?? 0, m11 = t.m11 ?? 1, m12 = t.m12 ?? 0;
519
+ const blobs = deck.message?.blobs;
520
+ const parts = [];
521
+
522
+ // Fill paths
523
+ const fillColor = resolveFill(getFillPaints(node));
524
+ if (fillColor && node.fillGeometry?.length && blobs) {
525
+ for (const geo of node.fillGeometry) {
526
+ const d = decodeCmdBlob(blobs, geo.commandsBlob);
527
+ if (!d) continue;
528
+ const rule = geo.windingRule === 'EVENODD' ? ' fill-rule="evenodd"' : '';
529
+ parts.push(`<path d="${d}" fill="${fillColor}"${rule}/>`);
530
+ }
531
+ }
532
+
533
+ // Stroke paths
534
+ const strokeColor = resolveFill(node.strokePaints);
535
+ const sw = node.strokeWeight ?? 0;
536
+ if (strokeColor && sw > 0 && node.strokeGeometry?.length && blobs) {
537
+ for (const geo of node.strokeGeometry) {
538
+ const d = decodeCmdBlob(blobs, geo.commandsBlob);
539
+ if (!d) continue;
540
+ // strokeGeometry encodes the stroke outline as a fill shape
541
+ const rule = geo.windingRule === 'EVENODD' ? ' fill-rule="evenodd"' : '';
542
+ parts.push(`<path d="${d}" fill="${strokeColor}"${rule}/>`);
543
+ }
544
+ }
545
+
546
+ if (!parts.length) return renderPlaceholder(deck, node);
547
+ return `<g transform="matrix(${m00},${m10},${m01},${m11},${m02},${m12})">\n${parts.join('\n')}\n</g>`;
548
+ }
549
+
550
+ /** Decode a commandsBlob index into an SVG path d-string. */
551
+ function decodeCmdBlob(blobs, blobIdx) {
552
+ if (blobIdx == null || !blobs?.[blobIdx]) return null;
553
+ const raw = blobs[blobIdx].bytes ?? blobs[blobIdx];
554
+ if (!raw) return null;
555
+
556
+ // Convert indexed object to Buffer if needed
557
+ let buf;
558
+ if (Buffer.isBuffer(raw) || raw instanceof Uint8Array) {
559
+ buf = Buffer.from(raw);
560
+ } else {
561
+ const len = Object.keys(raw).length;
562
+ buf = Buffer.alloc(len);
563
+ for (let i = 0; i < len; i++) buf[i] = raw[i];
564
+ }
565
+
566
+ const cmds = [];
567
+ let off = 0;
568
+ while (off < buf.length) {
569
+ const cmd = buf[off++];
570
+ if (cmd === 0x01) { // moveTo
571
+ const x = buf.readFloatLE(off); off += 4;
572
+ const y = buf.readFloatLE(off); off += 4;
573
+ cmds.push(`M${f(x)},${f(y)}`);
574
+ } else if (cmd === 0x02) { // lineTo
575
+ const x = buf.readFloatLE(off); off += 4;
576
+ const y = buf.readFloatLE(off); off += 4;
577
+ cmds.push(`L${f(x)},${f(y)}`);
578
+ } else if (cmd === 0x04) { // cubicTo
579
+ const c1x = buf.readFloatLE(off); off += 4;
580
+ const c1y = buf.readFloatLE(off); off += 4;
581
+ const c2x = buf.readFloatLE(off); off += 4;
582
+ const c2y = buf.readFloatLE(off); off += 4;
583
+ const x = buf.readFloatLE(off); off += 4;
584
+ const y = buf.readFloatLE(off); off += 4;
585
+ cmds.push(`C${f(c1x)},${f(c1y)} ${f(c2x)},${f(c2y)} ${f(x)},${f(y)}`);
586
+ } else if (cmd === 0x00) { // close
587
+ cmds.push('Z');
588
+ } else {
589
+ break; // unknown command — stop
590
+ }
591
+ }
592
+ return cmds.length ? cmds.join('') : null;
593
+ }
594
+
595
+ function f(v) { return +v.toFixed(2); }
596
+
503
597
  function renderPlaceholder(deck, node) {
504
598
  const { x, y } = pos(node);
505
599
  const { w, h } = size(node);
@@ -580,7 +674,7 @@ const RENDERERS = {
580
674
  GROUP: renderGroup,
581
675
  SECTION: renderGroup,
582
676
  BOOLEAN_OPERATION: renderGroup,
583
- VECTOR: renderPlaceholder,
677
+ VECTOR: renderVector,
584
678
  LINE: renderLine,
585
679
  STAR: renderPlaceholder,
586
680
  POLYGON: renderPlaceholder,
package/manifest.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": "0.2",
3
3
  "name": "figmatk",
4
- "version": "0.3.3",
4
+ "version": "0.3.9",
5
5
  "description": "Create and edit Figma Slides .deck files programmatically - no Figma API required",
6
6
  "author": {
7
7
  "name": "FigmaTK Contributors"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "figmatk",
3
- "version": "0.3.8",
3
+ "version": "0.3.9",
4
4
  "description": "Figma Toolkit — Swiss-army knife CLI for Figma .deck and .fig files",
5
5
  "type": "module",
6
6
  "bin": {
@@ -6,7 +6,7 @@ description: >
6
6
  clone or remove slides, or produce a .deck file for Figma Slides.
7
7
  Powered by FigmaTK under the hood.
8
8
  metadata:
9
- version: "0.3.8"
9
+ version: "0.3.9"
10
10
  ---
11
11
 
12
12
  # FigmaTK Skill
@@ -6,7 +6,7 @@ description: >
6
6
  template from an existing deck, define reusable layouts, mark editable
7
7
  text/image slots, or prepare a draft template for later instantiation.
8
8
  metadata:
9
- version: "0.3.8"
9
+ version: "0.3.9"
10
10
  ---
11
11
 
12
12
  # Figma Template Builder