/** * Shared swatch shape helpers. * * This module lives OUTSIDE src/frontend/extension/ so it can be imported by the * free frontend bundle (src/frontend/main.tsx -> components/SwatchGroup.tsx) * without importing extension-only code. The shape-class and clip-path logic is * kept local to preserve base/extension bundle separation. * * IMPORTANT (Tailwind purge): the shape utility class string literals below are * the ONLY base-build references to these classes. Tailwind scans source files * for class strings, so keeping them here ensures * bwvs:rounded-md / rounded-xs / rounded-[22%] / rounded-full and the clip-path values * survive the base CSS build even when extension renderers are unavailable. */ export type SwatchShape = | "square" | "soft" | "rounded" | "squircle" | "circle" | "diamond" | "hexagon" // Layout-driven alias used by the pill layout; treated like circle. | "pill" | string; const normalizeShape = (shape: SwatchShape): string => String(shape || "") .trim() .toLowerCase(); /** * Returns the CSS clip-path polygon for clip-path shapes (diamond/hexagon), or * null for radius-based shapes. */ export const getShapeClipPath = (shape: SwatchShape): string | null => { const normalized = normalizeShape(shape); if (normalized === "hexagon") { return "polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%)"; } if (normalized === "diamond") { return "polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)"; } return null; }; /** * Resolve the Tailwind border-radius (or clip-path) utility class for a swatch * shape. Mirrors the extension getSwatchShapeClass. */ export const getShapeClass = (shape: SwatchShape): string => { const normalized = normalizeShape(shape); const clipPath = getShapeClipPath(normalized); if (normalized === "circle" || normalized === "pill") { return "bwvs:rounded-full"; } if (clipPath) { return `bwvs:rounded-none bwvs:[clip-path:${clipPath.replace(/ /g, "_")}]`; } if (normalized === "squircle") return "bwvs:rounded-[22%]"; if (normalized === "rounded") return "bwvs:rounded-md"; if (normalized === "soft") return "bwvs:rounded-xs"; return "bwvs:rounded-none"; }; /** * Resolve the container shape class (radius-only; clip-path shapes fall back to * a square container so the inner clip-path is not double-clipped). Mirrors the * extension getContainerShapeClass. */ export const getContainerShapeClass = (shape: SwatchShape): string => { const normalized = normalizeShape(shape); if (normalized === "circle" || normalized === "pill") { return "bwvs:rounded-full"; } if (normalized === "squircle") return "bwvs:rounded-[22%]"; if (normalized === "rounded") return "bwvs:rounded-md"; if (normalized === "soft") return "bwvs:rounded-xs"; return "bwvs:rounded-none"; };