@grida/refig 0.0.1 → 0.0.2

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # `@grida/refig`
2
2
 
3
- > **re**nder **fig**ma — headless Figma renderer (Node.js + browser) in the spirit of [`resvg-js`](https://github.com/nicolo-ribaudo/resvg-js)
3
+ > **re**nder **fig**ma — headless Figma renderer (Node.js + browser) in the spirit of [`resvg`](https://github.com/linebender/resvg)
4
4
 
5
5
  Render Figma documents to **PNG, JPEG, WebP, PDF, and SVG** in **Node.js (no browser required)** or directly in the **browser**.
6
6
 
package/dist/browser.mjs CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  collectExportsFromDocument,
5
5
  exportSettingToRenderOptions,
6
6
  resolveMimeType
7
- } from "./chunk-PK5L35ID.mjs";
7
+ } from "./chunk-YI7PIULZ.mjs";
8
8
  export {
9
9
  FigmaDocument,
10
10
  FigmaRenderer,
@@ -13749,11 +13749,19 @@ var iofigma;
13749
13749
  function hasGeometryTrait(node2) {
13750
13750
  return "fillGeometry" in node2 || "strokeGeometry" in node2;
13751
13751
  }
13752
+ function getParentBounds(node2) {
13753
+ const box = "absoluteBoundingBox" in node2 ? node2.absoluteBoundingBox : void 0;
13754
+ const sz = "size" in node2 ? node2.size : void 0;
13755
+ return {
13756
+ width: box?.width ?? sz?.x ?? 0,
13757
+ height: box?.height ?? sz?.y ?? 0
13758
+ };
13759
+ }
13752
13760
  function createVectorNodeFromPath(pathData, geometry, parentNode, childId, name, options) {
13753
13761
  if (!pathData) return null;
13754
13762
  try {
13755
13763
  const vectorNetwork = index_default2.fromSVGPathData(pathData);
13756
- const bbox = index_default2.getBBox(vectorNetwork);
13764
+ const { width, height } = getParentBounds(parentNode);
13757
13765
  const strokeAsFill = options.strokeAsFill === true;
13758
13766
  return {
13759
13767
  id: childId,
@@ -13767,10 +13775,10 @@ var iofigma;
13767
13775
  }),
13768
13776
  ...positioning_trait({
13769
13777
  relativeTransform: [
13770
- [1, 0, bbox.x],
13771
- [0, 1, bbox.y]
13778
+ [1, 0, 0],
13779
+ [0, 1, 0]
13772
13780
  ],
13773
- size: { x: bbox.width, y: bbox.height }
13781
+ size: { x: width, y: height }
13774
13782
  }),
13775
13783
  ...strokeAsFill ? {
13776
13784
  ...fills_trait(
@@ -13794,8 +13802,8 @@ var iofigma;
13794
13802
  ..."effects" in parentNode && parentNode.effects ? effects_trait(parentNode.effects) : effects_trait(void 0),
13795
13803
  type: "vector",
13796
13804
  vector_network: vectorNetwork,
13797
- layout_target_width: bbox.width,
13798
- layout_target_height: bbox.height,
13805
+ layout_target_width: width,
13806
+ layout_target_height: height,
13799
13807
  fill_rule: map.windingRuleMap[geometry.windingRule] ?? "nonzero"
13800
13808
  };
13801
13809
  } catch (e) {
@@ -13806,13 +13814,73 @@ var iofigma;
13806
13814
  return null;
13807
13815
  }
13808
13816
  }
13817
+ function createPathNodeFromPath(pathData, geometry, parentNode, childId, name, options) {
13818
+ if (!pathData) return null;
13819
+ try {
13820
+ const { width, height } = getParentBounds(parentNode);
13821
+ const strokeAsFill = options.strokeAsFill === true;
13822
+ return {
13823
+ id: childId,
13824
+ ...base_node_trait({
13825
+ name,
13826
+ visible: "visible" in parentNode ? parentNode.visible : true,
13827
+ locked: "locked" in parentNode ? parentNode.locked : false,
13828
+ rotation: 0,
13829
+ opacity: "opacity" in parentNode && parentNode.opacity !== void 0 ? parentNode.opacity : 1,
13830
+ blendMode: "blendMode" in parentNode && parentNode.blendMode ? parentNode.blendMode : "NORMAL"
13831
+ }),
13832
+ ...positioning_trait({
13833
+ relativeTransform: [
13834
+ [1, 0, 0],
13835
+ [0, 1, 0]
13836
+ ],
13837
+ size: { x: width, y: height }
13838
+ }),
13839
+ ...strokeAsFill ? {
13840
+ ...fills_trait(
13841
+ parentNode.strokes ?? [],
13842
+ context,
13843
+ imageRefsUsed
13844
+ ),
13845
+ ...stroke_trait(
13846
+ { strokes: [], strokeWeight: 0 },
13847
+ context,
13848
+ imageRefsUsed
13849
+ )
13850
+ } : {
13851
+ ...options.useFill ? fills_trait(parentNode.fills, context, imageRefsUsed) : {},
13852
+ ...options.useStroke ? stroke_trait(parentNode, context, imageRefsUsed) : stroke_trait(
13853
+ { strokes: [], strokeWeight: 0 },
13854
+ context,
13855
+ imageRefsUsed
13856
+ )
13857
+ },
13858
+ ..."effects" in parentNode && parentNode.effects ? effects_trait(parentNode.effects) : effects_trait(void 0),
13859
+ type: "path",
13860
+ data: pathData,
13861
+ layout_target_width: width,
13862
+ layout_target_height: height,
13863
+ fill_rule: map.windingRuleMap[geometry.windingRule] ?? "nonzero"
13864
+ };
13865
+ } catch (e) {
13866
+ console.warn(`Failed to create path node (${name}):`, e);
13867
+ return null;
13868
+ }
13869
+ }
13809
13870
  function processFillGeometries(node2, parentGridaId, nodeTypeName) {
13810
13871
  if (!node2.fillGeometry?.length) return [];
13811
13872
  const childIds = [];
13812
13873
  node2.fillGeometry.forEach((geometry, idx) => {
13813
13874
  const childId = `${parentGridaId}_fill_${idx}`;
13814
13875
  const name = `${node2.name || nodeTypeName} Fill ${idx + 1}`;
13815
- const childNode = createVectorNodeFromPath(
13876
+ const childNode = context.prefer_path_for_geometry ? createPathNodeFromPath(
13877
+ geometry.path ?? "",
13878
+ geometry,
13879
+ node2,
13880
+ childId,
13881
+ name,
13882
+ { useFill: true, useStroke: false }
13883
+ ) : createVectorNodeFromPath(
13816
13884
  geometry.path ?? "",
13817
13885
  geometry,
13818
13886
  node2,
@@ -13833,7 +13901,14 @@ var iofigma;
13833
13901
  node2.strokeGeometry.forEach((geometry, idx) => {
13834
13902
  const childId = `${parentGridaId}_stroke_${idx}`;
13835
13903
  const name = `${node2.name || nodeTypeName} Stroke ${idx + 1}`;
13836
- const childNode = createVectorNodeFromPath(
13904
+ const childNode = context.prefer_path_for_geometry ? createPathNodeFromPath(
13905
+ geometry.path ?? "",
13906
+ geometry,
13907
+ node2,
13908
+ childId,
13909
+ name,
13910
+ { useFill: false, useStroke: false, strokeAsFill: true }
13911
+ ) : createVectorNodeFromPath(
13837
13912
  geometry.path ?? "",
13838
13913
  geometry,
13839
13914
  node2,
@@ -14082,7 +14157,7 @@ var iofigma;
14082
14157
  case "REGULAR_POLYGON":
14083
14158
  case "STAR":
14084
14159
  case "VECTOR": {
14085
- const useRestVectorNetwork = context.disable_volatile_apis !== true && "vectorNetwork" in node && node.vectorNetwork != null;
14160
+ const useRestVectorNetwork = context.prefer_path_for_geometry !== true && context.disable_volatile_apis !== true && "vectorNetwork" in node && node.vectorNetwork != null;
14086
14161
  if (useRestVectorNetwork) {
14087
14162
  try {
14088
14163
  const ir = restful2.map.normalizeRestVectorNetworkToIR(
@@ -15866,7 +15941,8 @@ function exportSettingToRenderOptions(node, setting) {
15866
15941
  JPG: "jpeg",
15867
15942
  PNG: "png",
15868
15943
  SVG: "svg",
15869
- PDF: "pdf"
15944
+ PDF: "pdf",
15945
+ WEBP: "webp"
15870
15946
  };
15871
15947
  const format = formatMap[setting.format] ?? "png";
15872
15948
  const constraint = setting.constraint;
@@ -15951,6 +16027,7 @@ function restJsonToSceneJson(json, rootNodeId, images) {
15951
16027
  const resolveImageSrc = images && (Object.keys(images).length > 0 ? (ref) => ref in images ? `res://images/${ref}` : null : void 0);
15952
16028
  const buildContext = (overrides) => ({
15953
16029
  gradient_id_generator: baseGradientGen,
16030
+ prefer_path_for_geometry: true,
15954
16031
  ...resolveImageSrc && { resolve_image_src: resolveImageSrc },
15955
16032
  ...overrides
15956
16033
  });
@@ -16089,6 +16166,7 @@ function figBytesToSceneJson(figBytes, rootNodeId) {
16089
16166
  node_id_generator: () => `refig-${++counter}`,
16090
16167
  gradient_id_generator: () => `grad-${++counter}`,
16091
16168
  preserve_figma_ids: true,
16169
+ prefer_path_for_geometry: true,
16092
16170
  ...resolveImageSrc && { resolve_image_src: resolveImageSrc }
16093
16171
  };
16094
16172
  const { document: packed } = iofigma.kiwi.convertPageToScene(page, context);
package/dist/cli.mjs CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  exportSettingToRenderOptions,
7
7
  figFileToRestLikeDocument,
8
8
  iofigma
9
- } from "./chunk-PK5L35ID.mjs";
9
+ } from "./chunk-YI7PIULZ.mjs";
10
10
 
11
11
  // cli.ts
12
12
  import {
@@ -31,6 +31,7 @@ var EXT_BY_FORMAT = {
31
31
  png: "png",
32
32
  jpeg: "jpeg",
33
33
  jpg: "jpeg",
34
+ webp: "webp",
34
35
  svg: "svg",
35
36
  pdf: "pdf"
36
37
  };
package/dist/index.mjs CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  collectExportsFromDocument,
5
5
  exportSettingToRenderOptions,
6
6
  resolveMimeType
7
- } from "./chunk-PK5L35ID.mjs";
7
+ } from "./chunk-YI7PIULZ.mjs";
8
8
 
9
9
  // index.ts
10
10
  import { readFileSync } from "fs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grida/refig",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "private": false,
5
5
  "description": "Headless Figma renderer — render .fig and REST API JSON to PNG/JPEG/WebP/PDF/SVG",
6
6
  "keywords": [
@@ -55,7 +55,7 @@
55
55
  "access": "public"
56
56
  },
57
57
  "dependencies": {
58
- "@grida/canvas-wasm": "0.90.0-canary.7",
58
+ "@grida/canvas-wasm": "0.90.0-canary.8",
59
59
  "commander": "^12.1.0"
60
60
  },
61
61
  "devDependencies": {