import {
  generateCSSShorthand,
  generateVarId,
  isVisible,
  pixelRound
} from "./chunk-Q7GF65O3.js";

// src/utils/identity.ts
import { isTruthy } from "remeda";
function hasValue(key, obj, typeGuard) {
  const isObject = typeof obj === "object" && obj !== null;
  if (!isObject || !(key in obj)) return false;
  const val = obj[key];
  return typeGuard ? typeGuard(val) : val !== void 0;
}
function isFrame(val) {
  return typeof val === "object" && !!val && "clipsContent" in val && typeof val.clipsContent === "boolean";
}
function isLayout(val) {
  return typeof val === "object" && !!val && "absoluteBoundingBox" in val && typeof val.absoluteBoundingBox === "object" && !!val.absoluteBoundingBox && "x" in val.absoluteBoundingBox && "y" in val.absoluteBoundingBox && "width" in val.absoluteBoundingBox && "height" in val.absoluteBoundingBox;
}
function isInAutoLayoutFlow(node, parent) {
  const autoLayoutModes = ["HORIZONTAL", "VERTICAL"];
  return isFrame(parent) && autoLayoutModes.includes(parent.layoutMode ?? "NONE") && isLayout(node) && node.layoutPositioning !== "ABSOLUTE";
}
function isStrokeWeights(val) {
  return typeof val === "object" && val !== null && "top" in val && "right" in val && "bottom" in val && "left" in val;
}
function isRectangle(key, obj) {
  const recordObj = obj;
  return typeof obj === "object" && !!obj && key in recordObj && typeof recordObj[key] === "object" && !!recordObj[key] && "x" in recordObj[key] && "y" in recordObj[key] && "width" in recordObj[key] && "height" in recordObj[key];
}
function isRectangleCornerRadii(val) {
  return Array.isArray(val) && val.length === 4 && val.every((v) => typeof v === "number");
}

// src/extractors/node-walker.ts
function extractFromDesign(fileUrl, nodes, extractors, options = {}, globalVars = { styles: {} }) {
  const context = {
    globalVars,
    currentDepth: 0
  };
  const processedNodes = nodes.filter((node) => shouldProcessNode(node, options)).map((node) => processNodeWithExtractors(fileUrl, node, extractors, context, options)).filter((node) => node !== null);
  return {
    nodes: processedNodes,
    globalVars: context.globalVars
  };
}
function processNodeWithExtractors(fileUrl, node, extractors, context, options) {
  if (!shouldProcessNode(node, options)) {
    return null;
  }
  const result = {
    id: node.id,
    name: node.name,
    type: node.type === "VECTOR" ? "IMAGE-SVG" : node.type
  };
  for (const extractor of extractors) {
    extractor(fileUrl, node, result, context);
  }
  if (shouldTraverseChildren(node, context, options)) {
    const childContext = {
      ...context,
      currentDepth: context.currentDepth + 1,
      parent: node
    };
    if (hasValue("children", node) && node.children.length > 0) {
      const children = node.children.filter((child) => shouldProcessNode(child, options)).map((child) => processNodeWithExtractors(fileUrl, child, extractors, childContext, options)).filter((child) => child !== null);
      if (children.length > 0) {
        const childrenToInclude = options.afterChildren ? options.afterChildren(node, result, children) : children;
        if (childrenToInclude.length > 0) {
          result.children = childrenToInclude;
        } else {
          result.children = children;
        }
      }
    }
  }
  return result;
}
function shouldProcessNode(node, options) {
  if (!isVisible(node)) {
    return false;
  }
  if (options.nodeFilter && !options.nodeFilter(node)) {
    return false;
  }
  return true;
}
function shouldTraverseChildren(node, context, options) {
  if (options.maxDepth !== void 0 && context.currentDepth >= options.maxDepth) {
    return false;
  }
  return true;
}

// src/transformers/component.ts
function simplifyComponents(aggregatedComponents) {
  return Object.fromEntries(
    Object.entries(aggregatedComponents).map(([id, comp]) => [
      id,
      {
        id,
        key: comp.key,
        name: comp.name,
        componentSetId: comp.componentSetId
      }
    ])
  );
}
function simplifyComponentSets(aggregatedComponentSets) {
  return Object.fromEntries(
    Object.entries(aggregatedComponentSets).map(([id, set]) => [
      id,
      {
        id,
        key: set.key,
        name: set.name,
        description: set.description
      }
    ])
  );
}

// src/extractors/design-extractor.ts
function simplifyRawFigmaObject(fileUrl, apiResponse, nodeExtractors, options = {}) {
  const { metadata, rawNodes, components, componentSets, extraStyles } = parseAPIResponse(apiResponse);
  const globalVars = { styles: {}, extraStyles };
  const { nodes: extractedNodes, globalVars: finalGlobalVars } = extractFromDesign(
    fileUrl,
    rawNodes,
    nodeExtractors,
    options,
    globalVars
  );
  return {
    ...metadata,
    nodes: extractedNodes,
    components: simplifyComponents(components),
    componentSets: simplifyComponentSets(componentSets),
    globalVars: { styles: finalGlobalVars.styles }
  };
}
function parseAPIResponse(data) {
  const aggregatedComponents = {};
  const aggregatedComponentSets = {};
  let extraStyles = {};
  let nodesToParse;
  if ("nodes" in data) {
    const nodeResponses = Object.values(data.nodes);
    nodeResponses.forEach((nodeResponse) => {
      if (nodeResponse.components) {
        Object.assign(aggregatedComponents, nodeResponse.components);
      }
      if (nodeResponse.componentSets) {
        Object.assign(aggregatedComponentSets, nodeResponse.componentSets);
      }
      if (nodeResponse.styles) {
        Object.assign(extraStyles, nodeResponse.styles);
      }
    });
    nodesToParse = nodeResponses.map((n) => n.document).filter(isVisible);
  } else {
    Object.assign(aggregatedComponents, data.components);
    Object.assign(aggregatedComponentSets, data.componentSets);
    if (data.styles) {
      extraStyles = data.styles;
    }
    nodesToParse = data.document.children.filter(isVisible);
  }
  const { name } = data;
  return {
    metadata: {
      name
    },
    rawNodes: nodesToParse,
    extraStyles,
    components: aggregatedComponents,
    componentSets: aggregatedComponentSets
  };
}

// src/transformers/layout.ts
function buildSimplifiedLayout(n, parent) {
  const frameValues = buildSimplifiedFrameValues(n);
  const layoutValues = buildSimplifiedLayoutValues(n, parent, frameValues.mode) || {};
  return { ...frameValues, ...layoutValues };
}
function convertAlign(axisAlign, stretch) {
  if (stretch && stretch.mode !== "none") {
    const { children, mode, axis } = stretch;
    const direction = getDirection(axis, mode);
    const shouldStretch = children.length > 0 && children.reduce((shouldStretch2, c) => {
      if (!shouldStretch2) return false;
      if ("layoutPositioning" in c && c.layoutPositioning === "ABSOLUTE") return true;
      if (direction === "horizontal") {
        return "layoutSizingHorizontal" in c && c.layoutSizingHorizontal === "FILL";
      } else if (direction === "vertical") {
        return "layoutSizingVertical" in c && c.layoutSizingVertical === "FILL";
      }
      return false;
    }, true);
    if (shouldStretch) return "stretch";
  }
  switch (axisAlign) {
    case "MIN":
      return void 0;
    case "MAX":
      return "flex-end";
    case "CENTER":
      return "center";
    case "SPACE_BETWEEN":
      return "space-between";
    case "BASELINE":
      return "baseline";
    default:
      return void 0;
  }
}
function convertSelfAlign(align) {
  switch (align) {
    case "MIN":
      return void 0;
    case "MAX":
      return "flex-end";
    case "CENTER":
      return "center";
    case "STRETCH":
      return "stretch";
    default:
      return void 0;
  }
}
function convertSizing(s) {
  if (s === "FIXED") return "fixed";
  if (s === "FILL") return "fill";
  if (s === "HUG") return "hug";
  return void 0;
}
function getDirection(axis, mode) {
  switch (axis) {
    case "primary":
      switch (mode) {
        case "row":
          return "horizontal";
        case "column":
          return "vertical";
      }
    case "counter":
      switch (mode) {
        case "row":
          return "horizontal";
        case "column":
          return "vertical";
      }
  }
}
function buildSimplifiedFrameValues(n) {
  if (!isFrame(n)) {
    return { mode: "none" };
  }
  const frameValues = {
    mode: !n.layoutMode || n.layoutMode === "NONE" ? "none" : n.layoutMode === "HORIZONTAL" ? "row" : "column"
  };
  const overflowScroll = [];
  if (n.overflowDirection?.includes("HORIZONTAL")) overflowScroll.push("x");
  if (n.overflowDirection?.includes("VERTICAL")) overflowScroll.push("y");
  if (overflowScroll.length > 0) frameValues.overflowScroll = overflowScroll;
  frameValues.layoutGrow = n.layoutGrow;
  frameValues.layoutAlign = n.layoutAlign;
  if (n.constraints) {
    if (n.constraints.vertical == "BOTTOM" && n.constraints.horizontal == "LEFT") {
      frameValues.locationAbsoluteToParent = {
        left: 0,
        bottom: 0
      };
      frameValues.locationRelativeToParent = void 0;
    }
    if (n.constraints.vertical == "BOTTOM" && n.constraints.horizontal == "RIGHT") {
      frameValues.locationAbsoluteToParent = {
        right: 0,
        bottom: 0
      };
      frameValues.locationRelativeToParent = void 0;
    }
  }
  if (frameValues.mode === "none") {
    return frameValues;
  }
  frameValues.justifyContent = convertAlign(n.primaryAxisAlignItems ?? "MIN", {
    children: n.children,
    axis: "primary",
    mode: frameValues.mode
  });
  frameValues.alignItems = convertAlign(n.counterAxisAlignItems ?? "MIN", {
    children: n.children,
    axis: "counter",
    mode: frameValues.mode
  });
  frameValues.alignSelf = convertSelfAlign(n.layoutAlign);
  frameValues.wrap = n.layoutWrap === "WRAP" ? true : void 0;
  frameValues.gap = n.itemSpacing ? `${n.itemSpacing ?? 0}px` : void 0;
  if (n.paddingTop || n.paddingBottom || n.paddingLeft || n.paddingRight) {
    frameValues.padding = generateCSSShorthand({
      top: n.paddingTop ?? 0,
      right: n.paddingRight ?? 0,
      bottom: n.paddingBottom ?? 0,
      left: n.paddingLeft ?? 0
    });
  }
  return frameValues;
}
function buildSimplifiedLayoutValues(n, parent, mode) {
  if (!isLayout(n)) return void 0;
  const layoutValues = { mode };
  layoutValues.sizing = {
    horizontal: convertSizing(n.layoutSizingHorizontal),
    vertical: convertSizing(n.layoutSizingVertical)
  };
  if (
    // If parent is a frame but not an AutoLayout, or if the node is absolute, include positioning-related properties
    isFrame(parent) && !isInAutoLayoutFlow(n, parent)
  ) {
    if (n.layoutPositioning === "ABSOLUTE") {
      layoutValues.position = "absolute";
    }
    if (n.absoluteBoundingBox && parent.absoluteBoundingBox) {
      layoutValues.locationRelativeToParent = {
        x: pixelRound(n.absoluteBoundingBox.x - parent.absoluteBoundingBox.x),
        y: pixelRound(n.absoluteBoundingBox.y - parent.absoluteBoundingBox.y)
      };
    }
  }
  if (isRectangle("absoluteBoundingBox", n)) {
    const dimensions = {};
    if (mode === "row") {
      if (!n.layoutGrow && n.layoutSizingHorizontal == "FIXED")
        dimensions.width = n.absoluteBoundingBox.width;
      if (n.layoutAlign !== "STRETCH" && n.layoutSizingVertical == "FIXED")
        dimensions.height = n.absoluteBoundingBox.height;
    } else if (mode === "column") {
      if (n.layoutAlign !== "STRETCH" && n.layoutSizingHorizontal == "FIXED")
        dimensions.width = n.absoluteBoundingBox.width;
      if (!n.layoutGrow && n.layoutSizingVertical == "FIXED")
        dimensions.height = n.absoluteBoundingBox.height;
      if (n.preserveRatio) {
        dimensions.aspectRatio = n.absoluteBoundingBox?.width / n.absoluteBoundingBox?.height;
      }
    } else {
      if (!n.layoutSizingHorizontal || n.layoutSizingHorizontal === "FIXED") {
        dimensions.width = n.absoluteBoundingBox.width;
      }
      if (!n.layoutSizingVertical || n.layoutSizingVertical === "FIXED") {
        dimensions.height = n.absoluteBoundingBox.height;
      }
    }
    if (Object.keys(dimensions).length > 0) {
      if (dimensions.width) {
        dimensions.width = pixelRound(dimensions.width);
      }
      if (dimensions.height) {
        dimensions.height = pixelRound(dimensions.height);
      }
      layoutValues.dimensions = dimensions;
    }
    if (!layoutValues.locationRelativeToParent && !parent) {
      layoutValues.marginLeft = pixelRound(n.absoluteBoundingBox.x);
      layoutValues.marginTop = pixelRound(n.absoluteBoundingBox.y);
    }
  }
  return layoutValues;
}

// src/transformers/style.ts
function translateScaleMode(scaleMode, hasChildren, scalingFactor) {
  const isBackground = hasChildren;
  switch (scaleMode) {
    case "FILL":
      return {
        css: isBackground ? { backgroundSize: "cover", backgroundRepeat: "no-repeat", isBackground: true } : { objectFit: "cover", isBackground: false },
        processing: { needsCropping: false, requiresImageDimensions: false }
      };
    case "FIT":
      return {
        css: isBackground ? { backgroundSize: "contain", backgroundRepeat: "no-repeat", isBackground: true } : { objectFit: "contain", isBackground: false },
        processing: { needsCropping: false, requiresImageDimensions: false }
      };
    case "TILE":
      return {
        css: {
          backgroundRepeat: "repeat",
          backgroundSize: scalingFactor ? `calc(var(--original-width) * ${scalingFactor}) calc(var(--original-height) * ${scalingFactor})` : "auto",
          isBackground: true
        },
        processing: { needsCropping: false, requiresImageDimensions: true }
      };
    case "STRETCH":
      return {
        css: isBackground ? { backgroundSize: "100% 100%", backgroundRepeat: "no-repeat", isBackground: true } : { objectFit: "fill", isBackground: false },
        processing: { needsCropping: false, requiresImageDimensions: false }
      };
    default:
      return {
        css: {},
        processing: { needsCropping: false, requiresImageDimensions: false }
      };
  }
}
function generateTransformHash(transform) {
  const values = transform.flat();
  const hash = values.reduce((acc, val) => {
    const str = val.toString();
    for (let i = 0; i < str.length; i++) {
      acc = (acc << 5) - acc + str.charCodeAt(i) & 4294967295;
    }
    return acc;
  }, 0);
  return Math.abs(hash).toString(16).substring(0, 6);
}
function handleImageTransform(imageTransform) {
  const transformHash = generateTransformHash(imageTransform);
  return {
    needsCropping: true,
    requiresImageDimensions: false,
    cropTransform: imageTransform,
    filenameSuffix: `${transformHash}`
  };
}
function buildSimplifiedStrokes(n, hasChildren = false) {
  let strokes = { colors: [] };
  if (hasValue("strokes", n) && Array.isArray(n.strokes) && n.strokes.length) {
    strokes.colors = n.strokes.filter(isVisible).map((stroke) => parsePaint("", stroke, hasChildren));
  }
  if (hasValue("strokeWeight", n) && typeof n.strokeWeight === "number" && n.strokeWeight > 0) {
    strokes.strokeWeight = `${n.strokeWeight}px`;
  }
  if (hasValue("strokeDashes", n) && Array.isArray(n.strokeDashes) && n.strokeDashes.length) {
    strokes.strokeDashes = n.strokeDashes;
  }
  if (hasValue("individualStrokeWeights", n, isStrokeWeights)) {
    strokes.strokeWeight = generateCSSShorthand(n.individualStrokeWeights);
  }
  return strokes;
}
function parsePaint(fileUrl, raw, hasChildren = false) {
  if (raw.type === "IMAGE") {
    const baseImageFill = {
      type: "IMAGE",
      src: fileUrl ? fileUrl + raw.imageRef : raw.imageRef,
      scaleMode: raw.scaleMode,
      scalingFactor: raw.scalingFactor
    };
    const isBackground = hasChildren || baseImageFill.scaleMode === "TILE";
    const { css, processing } = translateScaleMode(
      baseImageFill.scaleMode,
      isBackground,
      raw.scalingFactor
    );
    let finalProcessing = processing;
    if (raw.imageTransform) {
      const transformProcessing = handleImageTransform(raw.imageTransform);
      finalProcessing = {
        ...processing,
        ...transformProcessing,
        // Keep requiresImageDimensions from scale mode (needed for TILE)
        requiresImageDimensions: processing.requiresImageDimensions || transformProcessing.requiresImageDimensions
      };
    }
    return {
      ...baseImageFill,
      ...css,
      imageDownloadArguments: finalProcessing
    };
  } else if (raw.type === "SOLID") {
    const { hex, opacity } = convertColor(raw.color, raw.opacity);
    if (opacity === 1) {
      return hex;
    } else {
      return formatRGBAColor(raw.color, opacity);
    }
  } else if (raw.type === "PATTERN") {
    return parsePatternPaint(raw);
  } else if (["GRADIENT_LINEAR", "GRADIENT_RADIAL", "GRADIENT_ANGULAR", "GRADIENT_DIAMOND"].includes(
    raw.type
  )) {
    return {
      type: raw.type,
      gradient: convertGradientToCss(raw)
    };
  } else {
    throw new Error(`Unknown paint type: ${raw.type}`);
  }
}
function parsePatternPaint(raw) {
  let backgroundRepeat = "repeat";
  let horizontal = "left";
  switch (raw.horizontalAlignment) {
    case "START":
      horizontal = "left";
      break;
    case "CENTER":
      horizontal = "center";
      break;
    case "END":
      horizontal = "right";
      break;
  }
  let vertical = "top";
  switch (raw.verticalAlignment) {
    case "START":
      vertical = "top";
      break;
    case "CENTER":
      vertical = "center";
      break;
    case "END":
      vertical = "bottom";
      break;
  }
  return {
    type: raw.type,
    patternSource: {
      type: "IMAGE-PNG",
      nodeId: raw.sourceNodeId
    },
    backgroundRepeat,
    backgroundSize: `${Math.round(raw.scalingFactor * 100)}%`,
    backgroundPosition: `${horizontal} ${vertical}`
  };
}
function convertColor(color, opacity = 1) {
  const r = Math.round(color.r * 255);
  const g = Math.round(color.g * 255);
  const b = Math.round(color.b * 255);
  const a = Math.round(opacity * color.a * 100) / 100;
  const hex = "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase();
  return { hex, opacity: a };
}
function formatRGBAColor(color, opacity = 1) {
  const r = Math.round(color.r * 255);
  const g = Math.round(color.g * 255);
  const b = Math.round(color.b * 255);
  const a = Math.round(opacity * color.a * 100) / 100;
  return `rgba(${r}, ${g}, ${b}, ${a})`;
}
function mapGradientStops(gradient, elementBounds = { width: 1, height: 1 }) {
  const handles = gradient.gradientHandlePositions;
  if (!handles || handles.length < 2) {
    const stops = gradient.gradientStops.map(({ position, color }) => {
      const cssColor = formatRGBAColor(color, 1);
      return `${cssColor} ${Math.round(position * 100)}%`;
    }).join(", ");
    return { stops, cssGeometry: "0deg" };
  }
  const [handle1, handle2, handle3] = handles;
  switch (gradient.type) {
    case "GRADIENT_LINEAR": {
      return mapLinearGradient(gradient.gradientStops, handle1, handle2, elementBounds);
    }
    case "GRADIENT_RADIAL": {
      return mapRadialGradient(gradient.gradientStops, handle1, handle2, handle3, elementBounds);
    }
    case "GRADIENT_ANGULAR": {
      return mapAngularGradient(gradient.gradientStops, handle1, handle2, handle3, elementBounds);
    }
    case "GRADIENT_DIAMOND": {
      return mapDiamondGradient(gradient.gradientStops, handle1, handle2, handle3, elementBounds);
    }
    default: {
      const stops = gradient.gradientStops.map(({ position, color }) => {
        const cssColor = formatRGBAColor(color, 1);
        return `${cssColor} ${Math.round(position * 100)}%`;
      }).join(", ");
      return { stops, cssGeometry: "0deg" };
    }
  }
}
function mapLinearGradient(gradientStops, start, end, elementBounds) {
  const dx = end.x - start.x;
  const dy = end.y - start.y;
  const gradientLength = Math.sqrt(dx * dx + dy * dy);
  if (gradientLength === 0) {
    const stops = gradientStops.map(({ position, color }) => {
      const cssColor = formatRGBAColor(color, 1);
      return `${cssColor} ${Math.round(position * 100)}%`;
    }).join(", ");
    return { stops, cssGeometry: "0deg" };
  }
  const angle = Math.atan2(dy, dx) * (180 / Math.PI) + 90;
  const extendedIntersections = findExtendedLineIntersections(start, end);
  if (extendedIntersections.length >= 2) {
    const fullLineStart = Math.min(extendedIntersections[0], extendedIntersections[1]);
    const fullLineEnd = Math.max(extendedIntersections[0], extendedIntersections[1]);
    const fullLineLength = fullLineEnd - fullLineStart;
    const mappedStops2 = gradientStops.map(({ position, color }) => {
      const cssColor = formatRGBAColor(color, 1);
      const figmaLinePosition = position;
      const tOnExtendedLine = figmaLinePosition * (1 - 0) + 0;
      const extendedPosition = (tOnExtendedLine - fullLineStart) / (fullLineEnd - fullLineStart);
      const clampedPosition = Math.max(0, Math.min(1, extendedPosition));
      return `${cssColor} ${Math.round(clampedPosition * 100)}%`;
    });
    return {
      stops: mappedStops2.join(", "),
      cssGeometry: `${Math.round(angle)}deg`
    };
  }
  const mappedStops = gradientStops.map(({ position, color }) => {
    const cssColor = formatRGBAColor(color, 1);
    return `${cssColor} ${Math.round(position * 100)}%`;
  });
  return {
    stops: mappedStops.join(", "),
    cssGeometry: `${Math.round(angle)}deg`
  };
}
function findExtendedLineIntersections(start, end) {
  const dx = end.x - start.x;
  const dy = end.y - start.y;
  if (Math.abs(dx) < 1e-10 && Math.abs(dy) < 1e-10) {
    return [];
  }
  const intersections = [];
  if (Math.abs(dy) > 1e-10) {
    const t = -start.y / dy;
    const x = start.x + t * dx;
    if (x >= 0 && x <= 1) {
      intersections.push(t);
    }
  }
  if (Math.abs(dy) > 1e-10) {
    const t = (1 - start.y) / dy;
    const x = start.x + t * dx;
    if (x >= 0 && x <= 1) {
      intersections.push(t);
    }
  }
  if (Math.abs(dx) > 1e-10) {
    const t = -start.x / dx;
    const y = start.y + t * dy;
    if (y >= 0 && y <= 1) {
      intersections.push(t);
    }
  }
  if (Math.abs(dx) > 1e-10) {
    const t = (1 - start.x) / dx;
    const y = start.y + t * dy;
    if (y >= 0 && y <= 1) {
      intersections.push(t);
    }
  }
  const uniqueIntersections = [
    ...new Set(intersections.map((t) => Math.round(t * 1e6) / 1e6))
  ];
  return uniqueIntersections.sort((a, b) => a - b);
}
function mapRadialGradient(gradientStops, center, edge, widthHandle, elementBounds) {
  const centerX = Math.round(center.x * 100);
  const centerY = Math.round(center.y * 100);
  const stops = gradientStops.map(({ position, color }) => {
    const cssColor = formatRGBAColor(color, 1);
    return `${cssColor} ${Math.round(position * 100)}%`;
  }).join(", ");
  return {
    stops,
    cssGeometry: `circle at ${centerX}% ${centerY}%`
  };
}
function mapAngularGradient(gradientStops, center, angleHandle, widthHandle, elementBounds) {
  const centerX = Math.round(center.x * 100);
  const centerY = Math.round(center.y * 100);
  const angle = Math.atan2(angleHandle.y - center.y, angleHandle.x - center.x) * (180 / Math.PI) + 90;
  const stops = gradientStops.map(({ position, color }) => {
    const cssColor = formatRGBAColor(color, 1);
    return `${cssColor} ${Math.round(position * 100)}%`;
  }).join(", ");
  return {
    stops,
    cssGeometry: `from ${Math.round(angle)}deg at ${centerX}% ${centerY}%`
  };
}
function mapDiamondGradient(gradientStops, center, edge, widthHandle, elementBounds) {
  const centerX = Math.round(center.x * 100);
  const centerY = Math.round(center.y * 100);
  const stops = gradientStops.map(({ position, color }) => {
    const cssColor = formatRGBAColor(color, 1);
    return `${cssColor} ${Math.round(position * 100)}%`;
  }).join(", ");
  return {
    stops,
    cssGeometry: `ellipse at ${centerX}% ${centerY}%`
  };
}
function convertGradientToCss(gradient) {
  const sortedGradient = {
    ...gradient,
    gradientStops: [...gradient.gradientStops].sort((a, b) => a.position - b.position)
  };
  const { stops, cssGeometry } = mapGradientStops(sortedGradient);
  switch (gradient.type) {
    case "GRADIENT_LINEAR": {
      return `linear-gradient(${cssGeometry}, ${stops})`;
    }
    case "GRADIENT_RADIAL": {
      return `radial-gradient(${cssGeometry}, ${stops})`;
    }
    case "GRADIENT_ANGULAR": {
      return `conic-gradient(${cssGeometry}, ${stops})`;
    }
    case "GRADIENT_DIAMOND": {
      return `radial-gradient(${cssGeometry}, ${stops})`;
    }
    default:
      return `linear-gradient(0deg, ${stops})`;
  }
}

// src/transformers/effects.ts
function buildSimplifiedEffects(n) {
  if (!hasValue("effects", n)) return {};
  const effects = n.effects.filter((e) => e.visible);
  const dropShadows = effects.filter((e) => e.type === "DROP_SHADOW").map(simplifyDropShadow);
  const innerShadows = effects.filter((e) => e.type === "INNER_SHADOW").map(simplifyInnerShadow);
  const boxShadow = [...dropShadows, ...innerShadows].join(", ");
  const filterBlurValues = effects.filter((e) => e.type === "LAYER_BLUR").map(simplifyBlur).join(" ");
  const backdropFilterValues = effects.filter((e) => e.type === "BACKGROUND_BLUR").map(simplifyBlur).join(" ");
  const result = {};
  if (boxShadow) {
    if (n.type === "TEXT") {
      result.textShadow = boxShadow;
    } else {
      result.boxShadow = boxShadow;
    }
  }
  if (filterBlurValues) result.filter = filterBlurValues;
  if (backdropFilterValues) result.backdropFilter = backdropFilterValues;
  return result;
}
function simplifyDropShadow(effect) {
  return `${effect.offset.x}px ${effect.offset.y}px ${effect.radius}px ${effect.spread ?? 0}px ${formatRGBAColor(effect.color)}`;
}
function simplifyInnerShadow(effect) {
  return `inset ${effect.offset.x}px ${effect.offset.y}px ${effect.radius}px ${effect.spread ?? 0}px ${formatRGBAColor(effect.color)}`;
}
function simplifyBlur(effect) {
  return `blur(${effect.radius}px)`;
}

// src/transformers/text.ts
function isTextNode(n) {
  return n.type === "TEXT";
}
function hasTextStyle(n) {
  return hasValue("style", n) && Object.keys(n.style).length > 0;
}
function extractNodeText(n) {
  if (hasValue("characters", n, isTruthy)) {
    return n.characters;
  }
}
function extractTextStyle(n) {
  if (hasTextStyle(n)) {
    const style = n.style;
    const textStyle = {
      fontFamily: style.fontFamily,
      fontWeight: style.fontWeight,
      fontSize: style.fontSize,
      textLineHeight: "lineHeightPx" in style && style.lineHeightPx ? style.lineHeightPx : void 0,
      lineHeight: "lineHeightPx" in style && style.lineHeightPx && style.fontSize ? `${style.lineHeightPx / style.fontSize}em` : void 0,
      letterSpacing: style.letterSpacing && style.letterSpacing !== 0 && style.fontSize ? `${style.letterSpacing / style.fontSize * 100}%` : void 0,
      textCase: style.textCase,
      textAlignHorizontal: style.textAlignHorizontal,
      textAlignVertical: style.textAlignVertical
    };
    return textStyle;
  }
}

// src/extractors/built-in.ts
function findOrCreateVar(globalVars, value, prefix) {
  const [existingVarId] = Object.entries(globalVars.styles).find(
    ([_, existingValue]) => JSON.stringify(existingValue) === JSON.stringify(value)
  ) ?? [];
  if (existingVarId) {
    return existingVarId;
  }
  const varId = generateVarId(prefix);
  globalVars.styles[varId] = value;
  return varId;
}
var layoutExtractor = (fileUrl, node, result, context) => {
  const layout = buildSimplifiedLayout(node, context.parent);
  if (Object.keys(layout).length > 1) {
    result.layout = findOrCreateVar(context.globalVars, layout, "layout");
  }
};
var textExtractor = (fileUrl, node, result, context) => {
  if (isTextNode(node)) {
    result.text = extractNodeText(node);
  }
  if (hasTextStyle(node)) {
    const textStyle = extractTextStyle(node);
    if (textStyle) {
      const styleName = getStyleName(node, context, ["text", "typography"]);
      if (styleName) {
        context.globalVars.styles[styleName] = textStyle;
        result.textStyle = styleName;
      } else {
        result.textStyle = findOrCreateVar(context.globalVars, textStyle, "style");
      }
    }
  }
};
var visualsExtractor = (fileUrl, node, result, context) => {
  const hasChildren = hasValue("children", node) && Array.isArray(node.children) && node.children.length > 0;
  if (hasValue("fills", node) && Array.isArray(node.fills) && node.fills.length) {
    const visibleFills = node.fills.filter((fill) => fill.visible !== false);
    const fills = visibleFills.map((fill) => parsePaint(fileUrl, fill, hasChildren)).reverse();
    const styleName = getStyleName(node, context, ["fill", "fills"]);
    if (styleName) {
      context.globalVars.styles[styleName] = fills;
      result.fills = styleName;
    } else {
      result.fills = findOrCreateVar(context.globalVars, fills, "fill");
    }
  }
  const strokes = buildSimplifiedStrokes(node, hasChildren);
  if (strokes.colors.length) {
    const styleName = getStyleName(node, context, ["stroke", "strokes"]);
    if (styleName) {
      context.globalVars.styles[styleName] = strokes.colors;
      result.strokes = styleName;
      if (strokes.strokeWeight) result.strokeWeight = strokes.strokeWeight;
      if (strokes.strokeDashes) result.strokeDashes = strokes.strokeDashes;
      if (strokes.strokeWeights) result.strokeWeights = strokes.strokeWeights;
    } else {
      result.strokes = findOrCreateVar(context.globalVars, strokes, "stroke");
    }
  }
  const effects = buildSimplifiedEffects(node);
  if (Object.keys(effects).length) {
    const styleName = getStyleName(node, context, ["effect", "effects"]);
    if (styleName) {
      context.globalVars.styles[styleName] = effects;
      result.effects = styleName;
    } else {
      result.effects = findOrCreateVar(context.globalVars, effects, "effect");
    }
  }
  if (hasValue("opacity", node) && typeof node.opacity === "number" && node.opacity !== 1) {
    result.opacity = node.opacity;
  }
  if (hasValue("cornerRadius", node) && typeof node.cornerRadius === "number") {
    result.borderRadius = `${node.cornerRadius}px`;
  }
  if (hasValue("rectangleCornerRadii", node, isRectangleCornerRadii)) {
    result.borderLeftTopRadius = node.rectangleCornerRadii[0];
    result.borderRightTopRadius = node.rectangleCornerRadii[1];
    result.borderLeftBottomRadius = node.rectangleCornerRadii[2];
    result.borderRightBottomRadius = node.rectangleCornerRadii[3];
  }
};
var componentExtractor = (fileUrl, node, result, _context) => {
  if (node.type === "INSTANCE") {
    if (hasValue("componentId", node)) {
      result.componentId = node.componentId;
    }
    if (hasValue("componentProperties", node)) {
      result.componentProperties = Object.entries(node.componentProperties ?? {}).map(
        ([name, { value, type }]) => ({
          name,
          value: value.toString(),
          type
        })
      );
    }
  }
};
function getStyleName(node, context, keys) {
  if (!hasValue("styles", node)) return void 0;
  const styleMap = node.styles;
  for (const key of keys) {
    const styleId = styleMap[key];
    if (styleId) {
      const meta = context.globalVars.extraStyles?.[styleId];
      if (meta?.name) return meta.name;
    }
  }
  return void 0;
}
var allExtractors = [layoutExtractor, textExtractor, visualsExtractor, componentExtractor];
var layoutAndText = [layoutExtractor, textExtractor];
var contentOnly = [textExtractor];
var visualsOnly = [visualsExtractor];
var layoutOnly = [layoutExtractor];
var SVG_ELIGIBLE_TYPES = /* @__PURE__ */ new Set([
  "IMAGE-SVG",
  // VECTOR nodes are converted to IMAGE-SVG, or containers that were collapsed
  "STAR",
  "LINE",
  "ELLIPSE",
  "REGULAR_POLYGON",
  "RECTANGLE"
]);
function collapseSvgContainers(node, result, children) {
  const allChildrenAreSvgEligible = children.every(
    (child) => SVG_ELIGIBLE_TYPES.has(child.type)
  );
  if ((node.type === "FRAME" || node.type === "GROUP" || node.type === "INSTANCE") && allChildrenAreSvgEligible) {
    result.type = "IMAGE-SVG";
    return [];
  }
  return children;
}

export {
  extractFromDesign,
  simplifyRawFigmaObject,
  parseAPIResponse,
  layoutExtractor,
  textExtractor,
  visualsExtractor,
  componentExtractor,
  allExtractors,
  layoutAndText,
  contentOnly,
  visualsOnly,
  layoutOnly,
  collapseSvgContainers
};
