@agent-native/core 0.119.0 → 0.119.1

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.
Files changed (64) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +8 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/ingestion/figma-node-to-html.ts +20 -99
  5. package/corpus/core/src/ingestion/figma-paint-math.ts +371 -0
  6. package/corpus/core/src/ingestion/index.ts +20 -0
  7. package/corpus/templates/assets/app/routes/library.tsx +17 -9
  8. package/corpus/templates/design/AGENTS.md +18 -5
  9. package/corpus/templates/design/FIGMA_INTEROPERABILITY.md +25 -12
  10. package/corpus/templates/design/actions/hydrate-figma-paste-images.ts +115 -0
  11. package/corpus/templates/design/actions/import-figma-clipboard.ts +83 -18
  12. package/corpus/templates/design/actions/import-figma-frame.ts +34 -21
  13. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +28 -1
  14. package/corpus/templates/design/app/components/design/DesignImportPanel.tsx +106 -11
  15. package/corpus/templates/design/app/components/design/FigmaHydrationDialog.tsx +273 -0
  16. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +7 -7
  17. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +57 -9
  18. package/corpus/templates/design/app/components/design/design-canvas/iframe-events.ts +10 -0
  19. package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +8 -1
  20. package/corpus/templates/design/app/components/design/multi-screen/types.ts +3 -0
  21. package/corpus/templates/design/app/i18n-data.ts +150 -2
  22. package/corpus/templates/design/app/lib/design-file-upload.ts +85 -0
  23. package/corpus/templates/design/app/lib/design-import.ts +19 -1
  24. package/corpus/templates/design/app/lib/figma-clipboard.ts +110 -10
  25. package/corpus/templates/design/changelog/2026-07-20-paste-figma-frames-directly-onto-the-canvas-no-token-needed.md +6 -0
  26. package/corpus/templates/design/changelog/2026-07-21-fig-upload-frame-url-and-rate-limit-errors.md +5 -0
  27. package/corpus/templates/design/changelog/2026-07-22-connecting-a-figma-token-now-actually-fills-in-a-pasted-desi.md +6 -0
  28. package/corpus/templates/design/changelog/2026-07-22-figma-line-arrow-vectors-and-stroked-icons-now-render-instea.md +6 -0
  29. package/corpus/templates/design/changelog/2026-07-22-fill-a-no-token-figma-paste-s-images-by-dropping-the-origina.md +6 -0
  30. package/corpus/templates/design/changelog/2026-07-22-imported-and-pasted-figma-frames-now-render-faithfully-fixed.md +6 -0
  31. package/corpus/templates/design/server/handlers/import-design-file.ts +50 -0
  32. package/corpus/templates/design/server/lib/fig-file-decoder.ts +90 -49
  33. package/corpus/templates/design/server/lib/fig-file-import.ts +19 -5
  34. package/corpus/templates/design/server/lib/fig-file-to-html.ts +583 -78
  35. package/corpus/templates/design/server/lib/figma-clipboard-local-decode.ts +238 -0
  36. package/corpus/templates/design/server/lib/figma-image-hydration.ts +389 -0
  37. package/corpus/templates/design/server/lib/figma-node-import.ts +136 -36
  38. package/corpus/templates/design/server/register-secrets.ts +14 -2
  39. package/dist/collab/awareness.d.ts +2 -2
  40. package/dist/collab/awareness.d.ts.map +1 -1
  41. package/dist/collab/struct-routes.d.ts +1 -1
  42. package/dist/ingestion/figma-node-to-html.d.ts.map +1 -1
  43. package/dist/ingestion/figma-node-to-html.js +14 -88
  44. package/dist/ingestion/figma-node-to-html.js.map +1 -1
  45. package/dist/ingestion/figma-paint-math.d.ts +157 -0
  46. package/dist/ingestion/figma-paint-math.d.ts.map +1 -0
  47. package/dist/ingestion/figma-paint-math.js +274 -0
  48. package/dist/ingestion/figma-paint-math.js.map +1 -0
  49. package/dist/ingestion/index.d.ts +1 -0
  50. package/dist/ingestion/index.d.ts.map +1 -1
  51. package/dist/ingestion/index.js +1 -0
  52. package/dist/ingestion/index.js.map +1 -1
  53. package/dist/observability/routes.d.ts +5 -5
  54. package/dist/progress/routes.d.ts +1 -1
  55. package/dist/provider-api/actions/custom-provider-registration.d.ts +12 -12
  56. package/dist/provider-api/actions/provider-api.d.ts +7 -7
  57. package/dist/resources/handlers.d.ts +1 -1
  58. package/dist/secrets/routes.d.ts +3 -3
  59. package/dist/server/realtime-token.d.ts +1 -1
  60. package/dist/server/transcribe-voice.d.ts +1 -1
  61. package/package.json +1 -1
  62. package/src/ingestion/figma-node-to-html.ts +20 -99
  63. package/src/ingestion/figma-paint-math.ts +371 -0
  64. package/src/ingestion/index.ts +20 -0
@@ -36,6 +36,8 @@ const MAX_FIGMA_IMAGE_BYTES = 15 * 1024 * 1024;
36
36
  const MAX_TOTAL_FIGMA_IMAGE_BYTES = 64 * 1024 * 1024;
37
37
  const MAX_FIGMA_IMAGE_REFERENCES = 256;
38
38
  const MAX_FIGMA_IMAGE_IDS_PER_REQUEST = 50;
39
+ // Figma's `/images` endpoint takes ids in the query string; cap the batch by
40
+ // both count and URL length so a complex frame's ids are fetched in full.
39
41
  const MAX_FIGMA_IMAGE_IDS_QUERY_CHARS = 1_800;
40
42
  const MAX_CONCURRENT_FIGMA_IMAGE_UPLOADS = 4;
41
43
  const FIGMA_IMAGE_FETCH_TIMEOUT_MS = 20_000;
@@ -415,6 +417,7 @@ type FigmaProviderEnvelope = {
415
417
  ok?: boolean;
416
418
  status?: number;
417
419
  statusText?: string;
420
+ headers?: Record<string, string>;
418
421
  json?: unknown;
419
422
  text?: string;
420
423
  truncated?: boolean;
@@ -422,6 +425,46 @@ type FigmaProviderEnvelope = {
422
425
  };
423
426
  };
424
427
 
428
+ export type FigmaRateLimitError = Error & {
429
+ statusCode: 429;
430
+ retryAfterSeconds?: number;
431
+ figmaPlanTier?: string;
432
+ figmaRateLimitType?: "low" | "high";
433
+ figmaUpgradeUrl?: string;
434
+ };
435
+
436
+ const FIGMA_PLAN_TIERS = new Set([
437
+ "enterprise",
438
+ "org",
439
+ "pro",
440
+ "starter",
441
+ "student",
442
+ ]);
443
+
444
+ export function isFigmaRateLimitError(
445
+ err: unknown,
446
+ ): err is FigmaRateLimitError {
447
+ return (
448
+ err instanceof Error && (err as { statusCode?: unknown }).statusCode === 429
449
+ );
450
+ }
451
+
452
+ function figmaUpgradeUrl(value: string | undefined): string | undefined {
453
+ if (!value) return undefined;
454
+ try {
455
+ const url = new URL(value);
456
+ if (
457
+ url.protocol === "https:" &&
458
+ (url.hostname === "figma.com" || url.hostname.endsWith(".figma.com"))
459
+ ) {
460
+ return value;
461
+ }
462
+ } catch {
463
+ return undefined;
464
+ }
465
+ return undefined;
466
+ }
467
+
425
468
  export function providerJson(envelope: unknown, label: string): unknown {
426
469
  const response = (envelope as FigmaProviderEnvelope | null)?.response;
427
470
  if (!response) throw new Error(`Figma ${label} response was empty.`);
@@ -431,11 +474,41 @@ export function providerJson(envelope: unknown, label: string): unknown {
431
474
  );
432
475
  }
433
476
  if (response.ok === false) {
477
+ const jsonBody = response.json as {
478
+ message?: string;
479
+ error?: string;
480
+ } | null;
434
481
  const detail =
435
482
  (typeof response.text === "string" && response.text.trim()) ||
483
+ (typeof jsonBody?.message === "string" && jsonBody.message) ||
436
484
  response.statusText ||
437
485
  `HTTP ${response.status ?? "error"}`;
438
- throw new Error(`Figma ${label} request failed: ${detail}`);
486
+ const err = new Error(`Figma ${label} request failed: ${detail}`);
487
+ if (response.status === 429) {
488
+ const rateLimitError = err as FigmaRateLimitError;
489
+ rateLimitError.statusCode = 429;
490
+
491
+ const retryAfterHeader = response.headers?.["retry-after"];
492
+ if (retryAfterHeader) {
493
+ rateLimitError.retryAfterSeconds = parseInt(retryAfterHeader, 10);
494
+ }
495
+
496
+ const planTier = response.headers?.["x-figma-plan-tier"];
497
+ if (planTier && FIGMA_PLAN_TIERS.has(planTier)) {
498
+ rateLimitError.figmaPlanTier = planTier;
499
+ }
500
+
501
+ const rateLimitType = response.headers?.["x-figma-rate-limit-type"];
502
+ if (rateLimitType === "low" || rateLimitType === "high") {
503
+ rateLimitError.figmaRateLimitType = rateLimitType;
504
+ }
505
+
506
+ const upgradeUrl = figmaUpgradeUrl(
507
+ response.headers?.["x-figma-upgrade-link"],
508
+ );
509
+ if (upgradeUrl) rateLimitError.figmaUpgradeUrl = upgradeUrl;
510
+ }
511
+ throw err;
439
512
  }
440
513
  return response.json;
441
514
  }
@@ -567,47 +640,39 @@ async function fetchFallbackImageUrls(
567
640
  nodeIds: string[],
568
641
  ): Promise<Record<string, string>> {
569
642
  if (nodeIds.length === 0) return {};
570
- const batches: string[][] = [];
643
+ const result: Record<string, string> = {};
644
+ const fetchBatch = async (ids: string[]) => {
645
+ if (ids.length === 0) return;
646
+ const envelope = await figmaGet(`/images/${fileKey}`, {
647
+ ids: ids.join(","),
648
+ format: "png",
649
+ scale: 2,
650
+ });
651
+ const json = providerJson(envelope, "images") as {
652
+ images?: Record<string, string | null | undefined>;
653
+ };
654
+ for (const [id, url] of Object.entries(json.images ?? {})) {
655
+ if (typeof url === "string" && url) result[id] = url;
656
+ }
657
+ };
658
+ // Batch over the full ID list by count and query length so complex frames
659
+ // don't silently drop layers beyond the first request.
571
660
  let batch: string[] = [];
572
661
  let queryChars = 0;
573
662
  for (const nodeId of nodeIds) {
574
- if (nodeId.length > MAX_FIGMA_IMAGE_IDS_QUERY_CHARS) {
575
- throw new Error("A Figma image fallback node id was unexpectedly long.");
576
- }
577
- const addedChars = nodeId.length + (batch.length > 0 ? 1 : 0);
663
+ const addedChars = nodeId.length + 1;
578
664
  if (
579
665
  batch.length >= MAX_FIGMA_IMAGE_IDS_PER_REQUEST ||
580
666
  queryChars + addedChars > MAX_FIGMA_IMAGE_IDS_QUERY_CHARS
581
667
  ) {
582
- batches.push(batch);
668
+ await fetchBatch(batch);
583
669
  batch = [];
584
670
  queryChars = 0;
585
671
  }
586
672
  batch.push(nodeId);
587
- queryChars += nodeId.length + (batch.length > 1 ? 1 : 0);
588
- }
589
- if (batch.length > 0) batches.push(batch);
590
-
591
- const responses = await mapWithConcurrency(
592
- batches,
593
- MAX_CONCURRENT_FIGMA_IMAGE_UPLOADS,
594
- async (ids) => {
595
- const envelope = await figmaGet(`/images/${fileKey}`, {
596
- ids: ids.join(","),
597
- format: "png",
598
- scale: 2,
599
- });
600
- return providerJson(envelope, "images") as {
601
- images?: Record<string, string | null | undefined>;
602
- };
603
- },
604
- );
605
- const result: Record<string, string> = {};
606
- for (const response of responses) {
607
- for (const [id, url] of Object.entries(response.images ?? {})) {
608
- if (typeof url === "string" && url) result[id] = url;
609
- }
673
+ queryChars += addedChars;
610
674
  }
675
+ await fetchBatch(batch);
611
676
  return result;
612
677
  }
613
678
 
@@ -628,6 +693,34 @@ async function fetchImageFillUrls(
628
693
  return result;
629
694
  }
630
695
 
696
+ /**
697
+ * Fetch CDN URLs for the given image-fill hex hashes via Figma's
698
+ * `/files/:key/images` endpoint, then mirror them to durable storage.
699
+ * Returns a Map from hex hash to durable URL. Hashes that Figma cannot
700
+ * resolve (deleted images, permission gaps) are omitted from the result.
701
+ *
702
+ * Used by `hydrate-figma-paste-images` to fill in `about:blank` placeholders
703
+ * that the local-kiwi clipboard decode path leaves behind.
704
+ */
705
+ export async function resolveImageFillRefs(
706
+ fileKey: string,
707
+ hexHashes: string[],
708
+ ): Promise<Map<string, string>> {
709
+ if (hexHashes.length === 0) return new Map();
710
+ const cdnUrls = await fetchImageFillUrls(fileKey, hexHashes);
711
+ const cdnUrlList = Object.values(cdnUrls).filter(
712
+ (u): u is string => typeof u === "string" && u.length > 0,
713
+ );
714
+ if (cdnUrlList.length === 0) return new Map();
715
+ const durableMap = await mirrorFigmaImageUrls(cdnUrlList);
716
+ const result = new Map<string, string>();
717
+ for (const [hash, cdnUrl] of Object.entries(cdnUrls)) {
718
+ const durableUrl = durableMap.get(cdnUrl);
719
+ if (durableUrl) result.set(hash, durableUrl);
720
+ }
721
+ return result;
722
+ }
723
+
631
724
  export function sanitizeTitle(
632
725
  name: string | undefined,
633
726
  fallback: string,
@@ -673,7 +766,11 @@ export async function buildScreenFilesFromFigmaNodes(
673
766
  source?: (nodeId: string, node: FigmaNode) => Record<string, unknown>;
674
767
  sourceLabel?: (nodeId: string, node: FigmaNode) => string;
675
768
  } = {},
676
- ): Promise<{ files: ImportedDesignFile[]; fidelityEntries: FidelityEntry[] }> {
769
+ ): Promise<{
770
+ files: ImportedDesignFile[];
771
+ fidelityEntries: FidelityEntry[];
772
+ missingImageFillCount: number;
773
+ }> {
677
774
  const entries = Object.entries(nodesById);
678
775
  const fallbackNodeIds = new Set<string>();
679
776
  const imageFillRefs = new Set<string>();
@@ -696,16 +793,16 @@ export async function buildScreenFilesFromFigmaNodes(
696
793
  (nodeId) => !fallbackImageUrls[nodeId],
697
794
  );
698
795
  if (missingFallbackNodeIds.length > 0) {
699
- throw new Error(
700
- `Figma could not render ${missingFallbackNodeIds.length} required fallback layer${missingFallbackNodeIds.length === 1 ? "" : "s"} (${missingFallbackNodeIds.slice(0, 5).join(", ")}${missingFallbackNodeIds.length > 5 ? ", …" : ""}). Nothing was imported so the design would not silently lose visible content. Try a smaller selection or retry the import.`,
796
+ console.warn(
797
+ `[figma-import] ${missingFallbackNodeIds.length} fallback layer(s) could not be rendered and will be omitted (${missingFallbackNodeIds.slice(0, 5).join(", ")}${missingFallbackNodeIds.length > 5 ? ", …" : ""}).`,
701
798
  );
702
799
  }
703
800
  const missingImageFillRefs = Array.from(imageFillRefs).filter(
704
801
  (imageRef) => !imageFillUrls[imageRef],
705
802
  );
706
803
  if (missingImageFillRefs.length > 0) {
707
- throw new Error(
708
- `Figma did not return ${missingImageFillRefs.length} required image fill${missingImageFillRefs.length === 1 ? "" : "s"}. Nothing was imported so the design would not silently omit imagery. Retry the import or export the affected frame from Figma.`,
804
+ console.warn(
805
+ `[figma-import] ${missingImageFillRefs.length} image fill ref(s) could not be resolved (likely from a component library file); those fills will be omitted.`,
709
806
  );
710
807
  }
711
808
  const durableUrls = await mirrorFigmaImageUrls([
@@ -763,5 +860,8 @@ export async function buildScreenFilesFromFigmaNodes(
763
860
  });
764
861
  }
765
862
 
766
- return { files, fidelityEntries };
863
+ const finalMissingCount = missingImageFillRefs.filter(
864
+ (r) => !imageFillUrls[r],
865
+ ).length;
866
+ return { files, fidelityEntries, missingImageFillCount: finalMissingCount };
767
867
  }
@@ -65,7 +65,18 @@ registerRequiredSecret({
65
65
  "User-Agent": "AgentNative/1.0",
66
66
  },
67
67
  });
68
- if (res.ok) return true;
68
+ console.log("[figma-validator] GET /v1/me status:", res.status);
69
+ if (res.ok) {
70
+ console.log("[figma-validator] token accepted");
71
+ return true;
72
+ }
73
+ let body = "";
74
+ try {
75
+ body = await res.text();
76
+ } catch {
77
+ /* ignore */
78
+ }
79
+ console.log("[figma-validator] Figma error body:", body);
69
80
  if (res.status === 401 || res.status === 403) {
70
81
  return {
71
82
  ok: false,
@@ -73,7 +84,8 @@ registerRequiredSecret({
73
84
  };
74
85
  }
75
86
  return { ok: false, error: `Figma returned ${res.status}.` };
76
- } catch {
87
+ } catch (err) {
88
+ console.log("[figma-validator] fetch threw:", err);
77
89
  return {
78
90
  ok: false,
79
91
  error: "Could not reach Figma. Check your network and try again.",
@@ -62,11 +62,11 @@ export declare const postAwareness: import("h3").EventHandlerWithFetch<import("h
62
62
  error: string;
63
63
  states?: undefined;
64
64
  } | {
65
- error?: undefined;
66
65
  states: {
67
66
  clientId: number;
68
67
  state: string;
69
68
  }[];
69
+ error?: undefined;
70
70
  }>>;
71
71
  /**
72
72
  * GET /_agent-native/collab/:docId/users
@@ -77,10 +77,10 @@ export declare const getActiveUsers: import("h3").EventHandlerWithFetch<import("
77
77
  error: string;
78
78
  users?: undefined;
79
79
  } | {
80
- error?: undefined;
81
80
  users: {
82
81
  clientId: number;
83
82
  lastSeen: number;
84
83
  }[];
84
+ error?: undefined;
85
85
  }>>;
86
86
  //# sourceMappingURL=awareness.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"awareness.d.ts","sourceRoot":"","sources":["../../src/collab/awareness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAgB3C,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAOD,eAAO,MAAM,sBAAsB,EAAG,kBAA2B,CAAC;AAElE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,WAAW,CAAC;IACpB,IAAI,EAAE,kBAAkB,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,MAAM,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnD,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAOD,wBAAgB,mBAAmB,IAAI,YAAY,CAElD;AAED,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,cAAc,GAAG,SAAS,GAChC,IAAI,CAON;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,EAClD,KAAK,CAAC,EAAE,cAAc,GACrB,IAAI,CAgBN;AAoBD,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,SAAS,GAAE,MAAmB,GAC7B,IAAI,CAON;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAE1E;AAiBD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAO1E;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,GAAG,IAAI,CAOnE;AAkCD;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa;;;;;;kBAwDa,MAAM;eAAS,MAAM;;GAoB1D,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,cAAc;;;;;;kBAYM,MAAM;kBAAY,MAAM;;GAMvD,CAAC"}
1
+ {"version":3,"file":"awareness.d.ts","sourceRoot":"","sources":["../../src/collab/awareness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAgB3C,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAOD,eAAO,MAAM,sBAAsB,EAAG,kBAA2B,CAAC;AAElE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,WAAW,CAAC;IACpB,IAAI,EAAE,kBAAkB,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,MAAM,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnD,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAOD,wBAAgB,mBAAmB,IAAI,YAAY,CAElD;AAED,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,cAAc,GAAG,SAAS,GAChC,IAAI,CAON;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,EAClD,KAAK,CAAC,EAAE,cAAc,GACrB,IAAI,CAgBN;AAoBD,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,SAAS,GAAE,MAAmB,GAC7B,IAAI,CAON;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAE1E;AAiBD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAO1E;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,GAAG,IAAI,CAOnE;AAkCD;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa;;;;;kBAwDa,MAAM;eAAS,MAAM;;;GAoB1D,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,cAAc;;;;;kBAYM,MAAM;kBAAY,MAAM;;;GAMvD,CAAC"}
@@ -13,8 +13,8 @@
13
13
  * Body: { json: any, fieldName?: string, type?: "map"|"array", requestSource?: string }
14
14
  */
15
15
  export declare const postCollabJson: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
16
- error: string;
17
16
  ok?: undefined;
17
+ error: string;
18
18
  } | {
19
19
  error?: undefined;
20
20
  ok: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"figma-node-to-html.d.ts","sourceRoot":"","sources":["../../src/ingestion/figma-node-to-html.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"figma-node-to-html.d.ts","sourceRoot":"","sources":["../../src/ingestion/figma-node-to-html.ts"],"names":[],"mappings":"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"}
@@ -61,6 +61,7 @@
61
61
  * which is a documented follow-up if a specific design surfaces a visible
62
62
  * mismatch.
63
63
  */
64
+ import { cssBlendMode, gradientAngleDegrees as gradientAngleDegreesMath, remapLinearStopPosition as remapLinearStopPositionMath, resolveGradientHandles, vectorLength, } from "./figma-paint-math.js";
64
65
  const UNSUPPORTED_STRUCTURAL_TYPES = new Set([
65
66
  "BOOLEAN_OPERATION",
66
67
  "VECTOR",
@@ -196,11 +197,11 @@ class FidelityTracker {
196
197
  return { entries, summary };
197
198
  }
198
199
  }
200
+ // ---------------------------------------------------------------------------
201
+ // Gradient angle / position derivation
202
+ // ---------------------------------------------------------------------------
199
203
  function resolveGradientGeometry(paint) {
200
- const handles = paint.gradientHandlePositions;
201
- if (!handles || handles.length < 3)
202
- return null;
203
- return { start: handles[0], end: handles[1], width: handles[2] };
204
+ return resolveGradientHandles(paint.gradientHandlePositions);
204
205
  }
205
206
  /**
206
207
  * Derive a CSS `linear-gradient()` angle (degrees) from Figma's normalized
@@ -218,14 +219,7 @@ function resolveGradientGeometry(paint) {
218
219
  * dx = 0, dy = 1*h -> atan2(dy, 0) = 90deg -> +90 = 180deg. Matches.
219
220
  */
220
221
  export function gradientAngleDegrees(paint, box) {
221
- const geometry = resolveGradientGeometry(paint);
222
- if (!geometry)
223
- return null;
224
- const dx = (geometry.end.x - geometry.start.x) * box.width;
225
- const dy = (geometry.end.y - geometry.start.y) * box.height;
226
- const angleRad = Math.atan2(dy, dx);
227
- const angleDeg = (angleRad * 180) / Math.PI + 90;
228
- return ((angleDeg % 360) + 360) % 360;
222
+ return gradientAngleDegreesMath(paint, box);
229
223
  }
230
224
  function gradientStopsCss(paint, remapPosition) {
231
225
  const stops = paint.gradientStops ?? [];
@@ -239,51 +233,8 @@ function gradientStopsCss(paint, remapPosition) {
239
233
  })
240
234
  .join(", ");
241
235
  }
242
- /**
243
- * CSS `linear-gradient(angle, ...)` always stretches its 0%/100% stops
244
- * across the box's FULL diagonal extent at that angle (the CSS spec's
245
- * "gradient line" always spans corner-to-corner) -- it has no way to say
246
- * "start partway in, end partway in" the way Figma's actual gradient handles
247
- * can (a designer can drag the start/end handles anywhere, including short
248
- * of the shape's edges, or past them). Figma's own stop positions are
249
- * fractions of the literal start-handle-to-end-handle distance, which only
250
- * happens to coincide with the CSS full-box span when the handles are
251
- * dragged exactly corner-to-corner -- a common case, but far from the only
252
- * one, and the divergence gets worse the more the box's aspect ratio departs
253
- * from square (rotated/skewed handles included, e.g. gradientTransform-authored
254
- * paints). This projects each Figma stop's real pixel position onto the same
255
- * angle CSS will use and re-expresses it as a percentage of the CSS line's
256
- * length, so a partial/offset gradient renders at the same actual pixel
257
- * positions Figma draws it at instead of silently stretching to fill the box.
258
- */
259
236
  function remapLinearStopPosition(geometry, box, angleDeg) {
260
- const angleRad = (angleDeg * Math.PI) / 180;
261
- const ux = Math.sin(angleRad);
262
- const uy = -Math.cos(angleRad);
263
- const lineLength = box.width * Math.abs(ux) + box.height * Math.abs(uy);
264
- if (lineLength < 1e-6)
265
- return (position) => position;
266
- const startPx = {
267
- x: geometry.start.x * box.width,
268
- y: geometry.start.y * box.height,
269
- };
270
- const endPx = {
271
- x: geometry.end.x * box.width,
272
- y: geometry.end.y * box.height,
273
- };
274
- const centerX = box.width / 2;
275
- const centerY = box.height / 2;
276
- return (position) => {
277
- const pointX = startPx.x + position * (endPx.x - startPx.x);
278
- const pointY = startPx.y + position * (endPx.y - startPx.y);
279
- const projected = (pointX - centerX) * ux + (pointY - centerY) * uy;
280
- return (projected + lineLength / 2) / lineLength;
281
- };
282
- }
283
- function vectorLength(from, to, box) {
284
- const dx = (to.x - from.x) * box.width;
285
- const dy = (to.y - from.y) * box.height;
286
- return Math.sqrt(dx * dx + dy * dy);
237
+ return remapLinearStopPositionMath(geometry, box, angleDeg);
287
238
  }
288
239
  /**
289
240
  * Convert one Figma paint layer to a CSS `background-image` value (or plain
@@ -581,42 +532,17 @@ function buildEffects(node, isTextNode, tracker) {
581
532
  // ---------------------------------------------------------------------------
582
533
  // Blend modes
583
534
  // ---------------------------------------------------------------------------
584
- const CSS_BLEND_MODES = new Set([
585
- "multiply",
586
- "screen",
587
- "overlay",
588
- "darken",
589
- "lighten",
590
- "color-dodge",
591
- "color-burn",
592
- "hard-light",
593
- "soft-light",
594
- "difference",
595
- "exclusion",
596
- "hue",
597
- "saturation",
598
- "color",
599
- "luminosity",
600
- ]);
601
- const FIGMA_ONLY_BLEND_MODE_FALLBACK = {
602
- LINEAR_BURN: "multiply",
603
- LINEAR_DODGE: "plus-lighter",
604
- LIGHTER: "plus-lighter",
605
- DARKER: "darken",
606
- };
607
535
  function buildBlendMode(node, tracker) {
608
536
  const mode = node.blendMode;
609
- if (!mode || mode === "PASS_THROUGH" || mode === "NORMAL")
537
+ if (!mode)
538
+ return undefined;
539
+ const result = cssBlendMode(mode);
540
+ if (!result)
610
541
  return undefined;
611
- const cssMode = mode.toLowerCase().replace(/_/g, "-");
612
- if (CSS_BLEND_MODES.has(cssMode))
613
- return cssMode;
614
- const fallback = FIGMA_ONLY_BLEND_MODE_FALLBACK[mode];
615
- if (fallback) {
616
- tracker.record(node, "approximated", `Figma blend mode "${mode}" has no CSS equivalent; approximated as mix-blend-mode: ${fallback}.`);
617
- return fallback;
542
+ if (result.verdict === "approximated") {
543
+ tracker.record(node, "approximated", `Figma blend mode "${mode}" has no CSS equivalent; approximated as mix-blend-mode: ${result.cssMode}.`);
618
544
  }
619
- return undefined;
545
+ return result.cssMode;
620
546
  }
621
547
  // ---------------------------------------------------------------------------
622
548
  // Text styling