From 7066b2745c87c620e298b779ecae3e6d94896f1c Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Wed, 1 May 2024 23:23:35 -0300 Subject: [PATCH 001/168] 2024 update --- .gitignore | 3 +- __tests__/altNodes/altConversions.test.ts | 2 +- apps/debug/package.json | 20 +- apps/plugin/package.json | 36 +- apps/plugin/ui-src/App.tsx | 5 +- assets/convert_tailwind_colors.js | 388 - package.json | 9 +- packages/backend/package.json | 16 +- packages/backend/src/common/numToAutoFixed.ts | 8 +- packages/backend/src/flutter/flutterMain.ts | 30 +- .../src/tailwind/builderImpl/tailwindColor.ts | 10 +- packages/eslint-config-custom/package.json | 6 +- packages/plugin-ui/package.json | 12 +- pnpm-lock.yaml | 6374 ++++++++++------- turbo.json | 30 +- 15 files changed, 3839 insertions(+), 3110 deletions(-) delete mode 100644 assets/convert_tailwind_colors.js diff --git a/.gitignore b/.gitignore index 3fbb8b82..360d5067 100644 --- a/.gitignore +++ b/.gitignore @@ -27,4 +27,5 @@ test/**/next-env.d.ts **/.idea **/.vscode -build \ No newline at end of file +build +.turbo \ No newline at end of file diff --git a/__tests__/altNodes/altConversions.test.ts b/__tests__/altNodes/altConversions.test.ts index 3b2306f8..aaebdb18 100644 --- a/__tests__/altNodes/altConversions.test.ts +++ b/__tests__/altNodes/altConversions.test.ts @@ -174,7 +174,7 @@ describe("AltConversions", () => { expect( tailwindMain(convertIntoAltNodes([node], new AltFrameNode())) ).toEqual( - `
` + `
` ); }); }); diff --git a/apps/debug/package.json b/apps/debug/package.json index b3a8ada4..0c7bc5a6 100644 --- a/apps/debug/package.json +++ b/apps/debug/package.json @@ -11,20 +11,20 @@ }, "dependencies": { "backend": "workspace:*", - "next": "^14.1.0", + "next": "^14.2.3", "plugin-ui": "workspace:*", - "react": "^18.2.0", - "react-dom": "^18.2.0" + "react": "^18.3.1", + "react-dom": "^18.3.1" }, "devDependencies": { - "@types/node": "^20.11.6", - "@types/react": "^18.2.48", - "@types/react-dom": "^18.2.18", - "autoprefixer": "^10.4.17", + "@types/node": "^20.12.8", + "@types/react": "^18.3.1", + "@types/react-dom": "^18.3.0", + "autoprefixer": "^10.4.19", "eslint-config-custom": "workspace:*", - "postcss": "^8.4.33", - "tailwindcss": "3.4.1", + "postcss": "^8.4.38", + "tailwindcss": "3.4.3", "tsconfig": "workspace:*", - "typescript": "^5.3.3" + "typescript": "^5.4.5" } } diff --git a/apps/plugin/package.json b/apps/plugin/package.json index 17df89e1..f772ac83 100644 --- a/apps/plugin/package.json +++ b/apps/plugin/package.json @@ -10,31 +10,31 @@ "dev": "pnpm build:watch" }, "dependencies": { - "@figma/plugin-typings": "^1.84.0", + "@figma/plugin-typings": "^1.92.0", "backend": "workspace:*", "plugin-ui": "workspace:*", - "react": "^18.2.0", - "react-dom": "^18.2.0" + "react": "^18.3.1", + "react-dom": "^18.3.1" }, "devDependencies": { - "@types/node": "^20.11.6", - "@types/react": "^18.2.48", - "@types/react-dom": "^18.2.18", - "@typescript-eslint/eslint-plugin": "^6.19.1", - "@typescript-eslint/parser": "^6.19.1", + "@types/node": "^20.12.8", + "@types/react": "^18.3.1", + "@types/react-dom": "^18.3.0", + "@typescript-eslint/eslint-plugin": "^7.8.0", + "@typescript-eslint/parser": "^7.8.0", "@vitejs/plugin-react": "^4.2.1", - "@vitejs/plugin-react-swc": "^3.5.0", - "autoprefixer": "^10.4.17", + "@vitejs/plugin-react-swc": "^3.6.0", + "autoprefixer": "^10.4.19", "concurrently": "^8.2.2", - "esbuild": "^0.19.12", + "esbuild": "^0.20.2", "eslint-config-custom": "workspace:*", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.5", - "postcss": "^8.4.33", - "tailwindcss": "3.4.1", + "eslint-plugin-react-hooks": "^4.6.2", + "eslint-plugin-react-refresh": "^0.4.6", + "postcss": "^8.4.38", + "tailwindcss": "3.4.3", "tsconfig": "workspace:*", - "typescript": "^5.3.3", - "vite": "^5.0.12", - "vite-plugin-singlefile": "^1.0.0" + "typescript": "^5.4.5", + "vite": "^5.2.10", + "vite-plugin-singlefile": "^2.0.1" } } diff --git a/apps/plugin/ui-src/App.tsx b/apps/plugin/ui-src/App.tsx index 71e6113f..f4c9516d 100644 --- a/apps/plugin/ui-src/App.tsx +++ b/apps/plugin/ui-src/App.tsx @@ -152,7 +152,10 @@ export default function App() { ); }} colors={state.colors} - gradients={state.gradients} + gradients={state.gradients.map((gradient) => ({ + ...gradient, + exportValue: gradient.exportedValue, + }))} /> ); diff --git a/assets/convert_tailwind_colors.js b/assets/convert_tailwind_colors.js deleted file mode 100644 index 8de413ae..00000000 --- a/assets/convert_tailwind_colors.js +++ /dev/null @@ -1,388 +0,0 @@ -// Step 1: Remove transparent, black and white from tailwind_colors. - -// transparent: "transparent", -// black: "#000", -// white: "#fff", -const tailwind_colors_all = { - rose: { - 50: "#fff1f2", - 100: "#ffe4e6", - 200: "#fecdd3", - 300: "#fda4af", - 400: "#fb7185", - 500: "#f43f5e", - 600: "#e11d48", - 700: "#be123c", - 800: "#9f1239", - 900: "#881337", - }, - pink: { - 50: "#fdf2f8", - 100: "#fce7f3", - 200: "#fbcfe8", - 300: "#f9a8d4", - 400: "#f472b6", - 500: "#ec4899", - 600: "#db2777", - 700: "#be185d", - 800: "#9d174d", - 900: "#831843", - }, - fuchsia: { - 50: "#fdf4ff", - 100: "#fae8ff", - 200: "#f5d0fe", - 300: "#f0abfc", - 400: "#e879f9", - 500: "#d946ef", - 600: "#c026d3", - 700: "#a21caf", - 800: "#86198f", - 900: "#701a75", - }, - purple: { - 50: "#faf5ff", - 100: "#f3e8ff", - 200: "#e9d5ff", - 300: "#d8b4fe", - 400: "#c084fc", - 500: "#a855f7", - 600: "#9333ea", - 700: "#7e22ce", - 800: "#6b21a8", - 900: "#581c87", - }, - violet: { - 50: "#f5f3ff", - 100: "#ede9fe", - 200: "#ddd6fe", - 300: "#c4b5fd", - 400: "#a78bfa", - 500: "#8b5cf6", - 600: "#7c3aed", - 700: "#6d28d9", - 800: "#5b21b6", - 900: "#4c1d95", - }, - indigo: { - 50: "#eef2ff", - 100: "#e0e7ff", - 200: "#c7d2fe", - 300: "#a5b4fc", - 400: "#818cf8", - 500: "#6366f1", - 600: "#4f46e5", - 700: "#4338ca", - 800: "#3730a3", - 900: "#312e81", - }, - blue: { - 50: "#eff6ff", - 100: "#dbeafe", - 200: "#bfdbfe", - 300: "#93c5fd", - 400: "#60a5fa", - 500: "#3b82f6", - 600: "#2563eb", - 700: "#1d4ed8", - 800: "#1e40af", - 900: "#1e3a8a", - }, - lightBlue: { - 50: "#f0f9ff", - 100: "#e0f2fe", - 200: "#bae6fd", - 300: "#7dd3fc", - 400: "#38bdf8", - 500: "#0ea5e9", - 600: "#0284c7", - 700: "#0369a1", - 800: "#075985", - 900: "#0c4a6e", - }, - cyan: { - 50: "#ecfeff", - 100: "#cffafe", - 200: "#a5f3fc", - 300: "#67e8f9", - 400: "#22d3ee", - 500: "#06b6d4", - 600: "#0891b2", - 700: "#0e7490", - 800: "#155e75", - 900: "#164e63", - }, - teal: { - 50: "#f0fdfa", - 100: "#ccfbf1", - 200: "#99f6e4", - 300: "#5eead4", - 400: "#2dd4bf", - 500: "#14b8a6", - 600: "#0d9488", - 700: "#0f766e", - 800: "#115e59", - 900: "#134e4a", - }, - emerald: { - 50: "#ecfdf5", - 100: "#d1fae5", - 200: "#a7f3d0", - 300: "#6ee7b7", - 400: "#34d399", - 500: "#10b981", - 600: "#059669", - 700: "#047857", - 800: "#065f46", - 900: "#064e3b", - }, - green: { - 50: "#f0fdf4", - 100: "#dcfce7", - 200: "#bbf7d0", - 300: "#86efac", - 400: "#4ade80", - 500: "#22c55e", - 600: "#16a34a", - 700: "#15803d", - 800: "#166534", - 900: "#14532d", - }, - lime: { - 50: "#f7fee7", - 100: "#ecfccb", - 200: "#d9f99d", - 300: "#bef264", - 400: "#a3e635", - 500: "#84cc16", - 600: "#65a30d", - 700: "#4d7c0f", - 800: "#3f6212", - 900: "#365314", - }, - yellow: { - 50: "#fefce8", - 100: "#fef9c3", - 200: "#fef08a", - 300: "#fde047", - 400: "#facc15", - 500: "#eab308", - 600: "#ca8a04", - 700: "#a16207", - 800: "#854d0e", - 900: "#713f12", - }, - amber: { - 50: "#fffbeb", - 100: "#fef3c7", - 200: "#fde68a", - 300: "#fcd34d", - 400: "#fbbf24", - 500: "#f59e0b", - 600: "#d97706", - 700: "#b45309", - 800: "#92400e", - 900: "#78350f", - }, - orange: { - 50: "#fff7ed", - 100: "#ffedd5", - 200: "#fed7aa", - 300: "#fdba74", - 400: "#fb923c", - 500: "#f97316", - 600: "#ea580c", - 700: "#c2410c", - 800: "#9a3412", - 900: "#7c2d12", - }, - red: { - 50: "#fef2f2", - 100: "#fee2e2", - 200: "#fecaca", - 300: "#fca5a5", - 400: "#f87171", - 500: "#ef4444", - 600: "#dc2626", - 700: "#b91c1c", - 800: "#991b1b", - 900: "#7f1d1d", - }, - warmGray: { - 50: "#fafaf9", - 100: "#f5f5f4", - 200: "#e7e5e4", - 300: "#d6d3d1", - 400: "#a8a29e", - 500: "#78716c", - 600: "#57534e", - 700: "#44403c", - 800: "#292524", - 900: "#1c1917", - }, - trueGray: { - 50: "#fafafa", - 100: "#f5f5f5", - 200: "#e5e5e5", - 300: "#d4d4d4", - 400: "#a3a3a3", - 500: "#737373", - 600: "#525252", - 700: "#404040", - 800: "#262626", - 900: "#171717", - }, - gray: { - 50: "#fafafa", - 100: "#f4f4f5", - 200: "#e4e4e7", - 300: "#d4d4d8", - 400: "#a1a1aa", - 500: "#71717a", - 600: "#52525b", - 700: "#3f3f46", - 800: "#27272a", - 900: "#18181b", - }, - coolGray: { - 50: "#f9fafb", - 100: "#f3f4f6", - 200: "#e5e7eb", - 300: "#d1d5db", - 400: "#9ca3af", - 500: "#6b7280", - 600: "#4b5563", - 700: "#374151", - 800: "#1f2937", - 900: "#111827", - }, - blueGray: { - 50: "#f8fafc", - 100: "#f1f5f9", - 200: "#e2e8f0", - 300: "#cbd5e1", - 400: "#94a3b8", - 500: "#64748b", - 600: "#475569", - 700: "#334155", - 800: "#1e293b", - 900: "#0f172a", - }, -}; - -// default colors available -const tailwind_colors = { - pink: { - 50: "#fdf2f8", - 100: "#fce7f3", - 200: "#fbcfe8", - 300: "#f9a8d4", - 400: "#f472b6", - 500: "#ec4899", - 600: "#db2777", - 700: "#be185d", - 800: "#9d174d", - 900: "#831843", - }, - purple: { - 50: "#f5f3ff", - 100: "#ede9fe", - 200: "#ddd6fe", - 300: "#c4b5fd", - 400: "#a78bfa", - 500: "#8b5cf6", - 600: "#7c3aed", - 700: "#6d28d9", - 800: "#5b21b6", - 900: "#4c1d95", - }, - indigo: { - 50: "#eef2ff", - 100: "#e0e7ff", - 200: "#c7d2fe", - 300: "#a5b4fc", - 400: "#818cf8", - 500: "#6366f1", - 600: "#4f46e5", - 700: "#4338ca", - 800: "#3730a3", - 900: "#312e81", - }, - blue: { - 50: "#eff6ff", - 100: "#dbeafe", - 200: "#bfdbfe", - 300: "#93c5fd", - 400: "#60a5fa", - 500: "#3b82f6", - 600: "#2563eb", - 700: "#1d4ed8", - 800: "#1e40af", - 900: "#1e3a8a", - }, - green: { - 50: "#ecfdf5", - 100: "#d1fae5", - 200: "#a7f3d0", - 300: "#6ee7b7", - 400: "#34d399", - 500: "#10b981", - 600: "#059669", - 700: "#047857", - 800: "#065f46", - 900: "#064e3b", - }, - yellow: { - 50: "#fffbeb", - 100: "#fef3c7", - 200: "#fde68a", - 300: "#fcd34d", - 400: "#fbbf24", - 500: "#f59e0b", - 600: "#d97706", - 700: "#b45309", - 800: "#92400e", - 900: "#78350f", - }, - red: { - 50: "#fef2f2", - 100: "#fee2e2", - 200: "#fecaca", - 300: "#fca5a5", - 400: "#f87171", - 500: "#ef4444", - 600: "#dc2626", - 700: "#b91c1c", - 800: "#991b1b", - 900: "#7f1d1d", - }, - gray: { - 50: "#f9fafb", - 100: "#f3f4f6", - 200: "#e5e7eb", - 300: "#d1d5db", - 400: "#9ca3af", - 500: "#6b7280", - 600: "#4b5563", - 700: "#374151", - 800: "#1f2937", - 900: "#111827", - }, -}; - -// Step #2: Transform that into an array (Array(2), Array(2), ...); -// Example: ["fuchsia", {…}] where {…} is {50: "#fdf4ff", ...} -const colorsArr = Object.entries(tailwind_colors); - -// Step #3: Transform into (Array(10), Array(10), ...) while reverting key-value; -// Example: {#fdf4ff: "fuchsia-50"} -var obj = Object.create({}); -const subArr = colorsArr.map((d) => { - return Object.entries(d[1]).flatMap((e) => { - obj[e[1]] = d[0] + "-" + e[0]; - return obj; - }); -}); - -// obj contains the result. -JSON.stringify(obj); diff --git a/package.json b/package.json index f780c8e1..f33ecef4 100644 --- a/package.json +++ b/package.json @@ -8,10 +8,9 @@ "format": "prettier --write \"**/*.{ts,tsx,md}\"" }, "devDependencies": { - "eslint": "^8.56.0", + "eslint": "^9.1.1", "eslint-config-custom": "workspace:*", - "prettier": "^3.2.4", - "turbo": "^1.11.3" - }, - "packageManager": "pnpm@8.7.5" + "prettier": "^3.2.5", + "turbo": "^1.13.3" + } } diff --git a/packages/backend/package.json b/packages/backend/package.json index 47d48bf4..471bdde0 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -14,17 +14,17 @@ "test": "jest" }, "dependencies": { - "@figma/plugin-typings": "^1.84.0", - "react": "18.2.0", - "react-dom": "18.2.0" + "@figma/plugin-typings": "^1.92.0", + "react": "18.3.1", + "react-dom": "18.3.1" }, "devDependencies": { - "@types/react": "^18.2.48", - "@types/react-dom": "^18.2.18", - "eslint": "^8.56.0", + "@types/react": "^18.3.1", + "@types/react-dom": "^18.3.0", + "eslint": "^9.1.1", "eslint-config-custom": "workspace:*", "tsconfig": "workspace:*", - "tsup": "^8.0.1", - "typescript": "^5.3.3" + "tsup": "^8.0.2", + "typescript": "^5.4.5" } } diff --git a/packages/backend/src/common/numToAutoFixed.ts b/packages/backend/src/common/numToAutoFixed.ts index cd305d49..d03bd529 100644 --- a/packages/backend/src/common/numToAutoFixed.ts +++ b/packages/backend/src/common/numToAutoFixed.ts @@ -43,8 +43,14 @@ export const generateWidgetCode = ( properties: Record, positionedValues?: string[] ): string => { + console.log("properties", properties); const propertiesArray = Object.entries(properties) - .filter(([, value]) => value !== "") + .filter(([, value]) => { + if (Array.isArray(value)) { + return value.length > 0; + } + return value !== ""; + }) .map(([key, value]) => { if (Array.isArray(value)) { return `${key}: [\n${indentStringFlutter(value.join(",\n"))},\n],`; diff --git a/packages/backend/src/flutter/flutterMain.ts b/packages/backend/src/flutter/flutterMain.ts index 0d471e63..3c9890d6 100644 --- a/packages/backend/src/flutter/flutterMain.ts +++ b/packages/backend/src/flutter/flutterMain.ts @@ -125,10 +125,11 @@ const flutterWidgetGenerator = ( }; const flutterGroup = (node: GroupNode): string => { + const widget = flutterWidgetGenerator(node.children); return flutterContainer( node, generateWidgetCode("Stack", { - children: [flutterWidgetGenerator(node.children)], + children: widget ? [widget] : [], }) ); }; @@ -181,10 +182,14 @@ const flutterFrame = ( return flutterContainer(node, rowColumn); } + if (node.isAsset) { + return flutterContainer(node, generateWidgetCode("FlutterLogo", {})); + } + return flutterContainer( node, generateWidgetCode("Stack", { - children: [children], + children: children !== "" ? [children] : [], }) ); } @@ -207,12 +212,21 @@ const makeRowColumn = ( return generateWidgetCode(rowOrColumn, widgetProps); }; -const addSpacingIfNeeded = (node: SceneNode, optimizeLayout: boolean): string => { - const nodeParentLayout = optimizeLayout && node.parent && "itemSpacing" in node.parent - ? node.parent.inferredAutoLayout - : null ?? node.parent; - - if (nodeParentLayout && node.parent?.type === "FRAME" && "itemSpacing" in nodeParentLayout && nodeParentLayout.layoutMode !== "NONE") { +const addSpacingIfNeeded = ( + node: SceneNode, + optimizeLayout: boolean +): string => { + const nodeParentLayout = + optimizeLayout && node.parent && "itemSpacing" in node.parent + ? node.parent.inferredAutoLayout + : null ?? node.parent; + + if ( + nodeParentLayout && + node.parent?.type === "FRAME" && + "itemSpacing" in nodeParentLayout && + nodeParentLayout.layoutMode !== "NONE" + ) { if (nodeParentLayout.itemSpacing > 0) { if (nodeParentLayout.layoutMode === "HORIZONTAL") { return generateWidgetCode("const SizedBox", { diff --git a/packages/backend/src/tailwind/builderImpl/tailwindColor.ts b/packages/backend/src/tailwind/builderImpl/tailwindColor.ts index f4a51106..c8d644f2 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindColor.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindColor.ts @@ -40,15 +40,13 @@ export const tailwindSolidColor = ( // example: text-opacity-50 // ignore the 100. If opacity was changed, let it be visible. const opacityProp = - opacity !== 1.0 - ? `${kind}-opacity-${nearestOpacity(opacity ?? 1.0)}` - : null; + opacity !== 1.0 ? `opacity-${nearestOpacity(opacity ?? 1.0)}` : null; // example: text-red-500 - const colorProp = `${kind}-${getTailwindFromFigmaRGB(color)}`; + const colorProp = `${kind}-${getTailwindFromFigmaRGB(color)}${opacityProp ? `/${opacityProp}` : ""}`; // if fill isn't visible, it shouldn't be painted. - return [colorProp, opacityProp].filter((d) => d).join(" "); + return colorProp; }; /** @@ -144,7 +142,6 @@ export const tailwindColors: Record = { "#1f2937": "gray-800", "#111827": "gray-900", "#030712": "gray-950", - "#fafafa": "neutral-50", "#f4f4f5": "zinc-100", "#e4e4e7": "zinc-200", "#d4d4d8": "zinc-300", @@ -155,6 +152,7 @@ export const tailwindColors: Record = { "#27272a": "zinc-800", "#18181b": "zinc-900", "#09090b": "zinc-950", + "#fafafa": "neutral-50", "#f5f5f5": "neutral-100", "#e5e5e5": "neutral-200", "#d4d4d4": "neutral-300", diff --git a/packages/eslint-config-custom/package.json b/packages/eslint-config-custom/package.json index b9db1890..678b9ce3 100644 --- a/packages/eslint-config-custom/package.json +++ b/packages/eslint-config-custom/package.json @@ -4,10 +4,10 @@ "main": "index.js", "license": "MIT", "dependencies": { - "eslint-config-next": "^14.1.0", + "eslint-config-next": "^14.2.3", "eslint-config-prettier": "^9.1.0", - "eslint-config-turbo": "^1.11.3", - "eslint-plugin-react": "7.33.2" + "eslint-config-turbo": "^1.13.3", + "eslint-plugin-react": "7.34.1" }, "publishConfig": { "access": "public" diff --git a/packages/plugin-ui/package.json b/packages/plugin-ui/package.json index 22b33b2e..cda1b68b 100644 --- a/packages/plugin-ui/package.json +++ b/packages/plugin-ui/package.json @@ -12,17 +12,17 @@ }, "dependencies": { "copy-to-clipboard": "^3.3.3", - "react": "^18.2.0", + "react": "^18.3.1", "react-syntax-highlighter": "^15.5.0", - "tailwindcss": "3.4.1" + "tailwindcss": "3.4.3" }, "devDependencies": { - "@types/react": "^18.2.48", - "@types/react-dom": "^18.2.18", + "@types/react": "^18.3.1", + "@types/react-dom": "^18.3.0", "@types/react-syntax-highlighter": "15.5.11", - "eslint": "^8.56.0", + "eslint": "^9.1.1", "eslint-config-custom": "workspace:*", "tsconfig": "workspace:*", - "typescript": "^5.3.3" + "typescript": "^5.4.5" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 89b8579e..b1b9c74c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: '6.0' +lockfileVersion: '9.0' settings: autoInstallPeers: true @@ -9,17 +9,17 @@ importers: .: devDependencies: eslint: - specifier: ^8.56.0 - version: 8.56.0 + specifier: ^9.1.1 + version: 9.1.1 eslint-config-custom: specifier: workspace:* version: link:packages/eslint-config-custom prettier: - specifier: ^3.2.4 - version: 3.2.4 + specifier: ^3.2.5 + version: 3.2.5 turbo: - specifier: ^1.11.3 - version: 1.11.3 + specifier: ^1.13.3 + version: 1.13.3 apps/debug: dependencies: @@ -27,51 +27,51 @@ importers: specifier: workspace:* version: link:../../packages/backend next: - specifier: ^14.1.0 - version: 14.1.0(react-dom@18.2.0)(react@18.2.0) + specifier: ^14.2.3 + version: 14.2.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) plugin-ui: specifier: workspace:* version: link:../../packages/plugin-ui react: - specifier: ^18.2.0 - version: 18.2.0 + specifier: ^18.3.1 + version: 18.3.1 react-dom: - specifier: ^18.2.0 - version: 18.2.0(react@18.2.0) + specifier: ^18.3.1 + version: 18.3.1(react@18.3.1) devDependencies: '@types/node': - specifier: ^20.11.6 - version: 20.11.6 + specifier: ^20.12.8 + version: 20.12.8 '@types/react': - specifier: ^18.2.48 - version: 18.2.48 + specifier: ^18.3.1 + version: 18.3.1 '@types/react-dom': - specifier: ^18.2.18 - version: 18.2.18 + specifier: ^18.3.0 + version: 18.3.0 autoprefixer: - specifier: ^10.4.17 - version: 10.4.17(postcss@8.4.33) + specifier: ^10.4.19 + version: 10.4.19(postcss@8.4.38) eslint-config-custom: specifier: workspace:* version: link:../../packages/eslint-config-custom postcss: - specifier: ^8.4.33 - version: 8.4.33 + specifier: ^8.4.38 + version: 8.4.38 tailwindcss: - specifier: 3.4.1 - version: 3.4.1 + specifier: 3.4.3 + version: 3.4.3 tsconfig: specifier: workspace:* version: link:../../packages/tsconfig typescript: - specifier: ^5.3.3 - version: 5.3.3 + specifier: ^5.4.5 + version: 5.4.5 apps/plugin: dependencies: '@figma/plugin-typings': - specifier: ^1.84.0 - version: 1.84.0 + specifier: ^1.92.0 + version: 1.92.0 backend: specifier: workspace:* version: link:../../packages/backend @@ -79,91 +79,91 @@ importers: specifier: workspace:* version: link:../../packages/plugin-ui react: - specifier: ^18.2.0 - version: 18.2.0 + specifier: ^18.3.1 + version: 18.3.1 react-dom: - specifier: ^18.2.0 - version: 18.2.0(react@18.2.0) + specifier: ^18.3.1 + version: 18.3.1(react@18.3.1) devDependencies: '@types/node': - specifier: ^20.11.6 - version: 20.11.6 + specifier: ^20.12.8 + version: 20.12.8 '@types/react': - specifier: ^18.2.48 - version: 18.2.48 + specifier: ^18.3.1 + version: 18.3.1 '@types/react-dom': - specifier: ^18.2.18 - version: 18.2.18 + specifier: ^18.3.0 + version: 18.3.0 '@typescript-eslint/eslint-plugin': - specifier: ^6.19.1 - version: 6.19.1(@typescript-eslint/parser@6.19.1)(eslint@8.56.0)(typescript@5.3.3) + specifier: ^7.8.0 + version: 7.8.0(@typescript-eslint/parser@7.8.0(eslint@9.1.1)(typescript@5.4.5))(eslint@9.1.1)(typescript@5.4.5) '@typescript-eslint/parser': - specifier: ^6.19.1 - version: 6.19.1(eslint@8.56.0)(typescript@5.3.3) + specifier: ^7.8.0 + version: 7.8.0(eslint@9.1.1)(typescript@5.4.5) '@vitejs/plugin-react': specifier: ^4.2.1 - version: 4.2.1(vite@5.0.12) + version: 4.2.1(vite@5.2.10(@types/node@20.12.8)) '@vitejs/plugin-react-swc': - specifier: ^3.5.0 - version: 3.5.0(vite@5.0.12) + specifier: ^3.6.0 + version: 3.6.0(@swc/helpers@0.5.11)(vite@5.2.10(@types/node@20.12.8)) autoprefixer: - specifier: ^10.4.17 - version: 10.4.17(postcss@8.4.33) + specifier: ^10.4.19 + version: 10.4.19(postcss@8.4.38) concurrently: specifier: ^8.2.2 version: 8.2.2 esbuild: - specifier: ^0.19.12 - version: 0.19.12 + specifier: ^0.20.2 + version: 0.20.2 eslint-config-custom: specifier: workspace:* version: link:../../packages/eslint-config-custom eslint-plugin-react-hooks: - specifier: ^4.6.0 - version: 4.6.0(eslint@8.56.0) + specifier: ^4.6.2 + version: 4.6.2(eslint@9.1.1) eslint-plugin-react-refresh: - specifier: ^0.4.5 - version: 0.4.5(eslint@8.56.0) + specifier: ^0.4.6 + version: 0.4.6(eslint@9.1.1) postcss: - specifier: ^8.4.33 - version: 8.4.33 + specifier: ^8.4.38 + version: 8.4.38 tailwindcss: - specifier: 3.4.1 - version: 3.4.1 + specifier: 3.4.3 + version: 3.4.3 tsconfig: specifier: workspace:* version: link:../../packages/tsconfig typescript: - specifier: ^5.3.3 - version: 5.3.3 + specifier: ^5.4.5 + version: 5.4.5 vite: - specifier: ^5.0.12 - version: 5.0.12(@types/node@20.11.6) + specifier: ^5.2.10 + version: 5.2.10(@types/node@20.12.8) vite-plugin-singlefile: - specifier: ^1.0.0 - version: 1.0.0(rollup@4.9.6)(vite@5.0.12) + specifier: ^2.0.1 + version: 2.0.1(rollup@4.17.2)(vite@5.2.10(@types/node@20.12.8)) packages/backend: dependencies: '@figma/plugin-typings': - specifier: ^1.84.0 - version: 1.84.0 + specifier: ^1.92.0 + version: 1.92.0 react: - specifier: 18.2.0 - version: 18.2.0 + specifier: 18.3.1 + version: 18.3.1 react-dom: - specifier: 18.2.0 - version: 18.2.0(react@18.2.0) + specifier: 18.3.1 + version: 18.3.1(react@18.3.1) devDependencies: '@types/react': - specifier: ^18.2.48 - version: 18.2.48 + specifier: ^18.3.1 + version: 18.3.1 '@types/react-dom': - specifier: ^18.2.18 - version: 18.2.18 + specifier: ^18.3.0 + version: 18.3.0 eslint: - specifier: ^8.56.0 - version: 8.56.0 + specifier: ^9.1.1 + version: 9.1.1 eslint-config-custom: specifier: workspace:* version: link:../eslint-config-custom @@ -171,26 +171,26 @@ importers: specifier: workspace:* version: link:../tsconfig tsup: - specifier: ^8.0.1 - version: 8.0.1(typescript@5.3.3) + specifier: ^8.0.2 + version: 8.0.2(@swc/core@1.4.17(@swc/helpers@0.5.11))(postcss@8.4.38)(typescript@5.4.5) typescript: - specifier: ^5.3.3 - version: 5.3.3 + specifier: ^5.4.5 + version: 5.4.5 packages/eslint-config-custom: dependencies: eslint-config-next: - specifier: ^14.1.0 - version: 14.1.0(eslint@8.56.0)(typescript@5.3.3) + specifier: ^14.2.3 + version: 14.2.3(eslint@9.1.1)(typescript@5.4.5) eslint-config-prettier: specifier: ^9.1.0 - version: 9.1.0(eslint@8.56.0) + version: 9.1.0(eslint@9.1.1) eslint-config-turbo: - specifier: ^1.11.3 - version: 1.11.3(eslint@8.56.0) + specifier: ^1.13.3 + version: 1.13.3(eslint@9.1.1) eslint-plugin-react: - specifier: 7.33.2 - version: 7.33.2(eslint@8.56.0) + specifier: 7.34.1 + version: 7.34.1(eslint@9.1.1) packages/plugin-ui: dependencies: @@ -198,27 +198,27 @@ importers: specifier: ^3.3.3 version: 3.3.3 react: - specifier: ^18.2.0 - version: 18.2.0 + specifier: ^18.3.1 + version: 18.3.1 react-syntax-highlighter: specifier: ^15.5.0 - version: 15.5.0(react@18.2.0) + version: 15.5.0(react@18.3.1) tailwindcss: - specifier: 3.4.1 - version: 3.4.1 + specifier: 3.4.3 + version: 3.4.3 devDependencies: '@types/react': - specifier: ^18.2.48 - version: 18.2.48 + specifier: ^18.3.1 + version: 18.3.1 '@types/react-dom': - specifier: ^18.2.18 - version: 18.2.18 + specifier: ^18.3.0 + version: 18.3.0 '@types/react-syntax-highlighter': specifier: 15.5.11 version: 15.5.11 eslint: - specifier: ^8.56.0 - version: 8.56.0 + specifier: ^9.1.1 + version: 9.1.1 eslint-config-custom: specifier: workspace:* version: link:../eslint-config-custom @@ -226,1438 +226,3436 @@ importers: specifier: workspace:* version: link:../tsconfig typescript: - specifier: ^5.3.3 - version: 5.3.3 + specifier: ^5.4.5 + version: 5.4.5 packages/tsconfig: {} packages: - /@aashutoshrathi/word-wrap@1.2.6: - resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} - engines: {node: '>=0.10.0'} - - /@alloc/quick-lru@5.2.0: + '@alloc/quick-lru@5.2.0': resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} - /@ampproject/remapping@2.2.1: - resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.22 - dev: true - /@babel/code-frame@7.23.5: - resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==} + '@babel/code-frame@7.24.2': + resolution: {integrity: sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/highlight': 7.23.4 - chalk: 2.4.2 - dev: true - /@babel/compat-data@7.23.5: - resolution: {integrity: sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==} + '@babel/compat-data@7.24.4': + resolution: {integrity: sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==} engines: {node: '>=6.9.0'} - dev: true - /@babel/core@7.23.9: - resolution: {integrity: sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==} + '@babel/core@7.24.5': + resolution: {integrity: sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA==} engines: {node: '>=6.9.0'} - dependencies: - '@ampproject/remapping': 2.2.1 - '@babel/code-frame': 7.23.5 - '@babel/generator': 7.23.6 - '@babel/helper-compilation-targets': 7.23.6 - '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.9) - '@babel/helpers': 7.23.9 - '@babel/parser': 7.23.9 - '@babel/template': 7.23.9 - '@babel/traverse': 7.23.9 - '@babel/types': 7.23.9 - convert-source-map: 2.0.0 - debug: 4.3.4 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - dev: true - /@babel/generator@7.23.6: - resolution: {integrity: sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==} + '@babel/generator@7.24.5': + resolution: {integrity: sha512-x32i4hEXvr+iI0NEoEfDKzlemF8AmtOP8CcrRaEcpzysWuoEb1KknpcvMsHKPONoKZiDuItklgWhB18xEhr9PA==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.23.9 - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.22 - jsesc: 2.5.2 - dev: true - /@babel/helper-compilation-targets@7.23.6: + '@babel/helper-compilation-targets@7.23.6': resolution: {integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/compat-data': 7.23.5 - '@babel/helper-validator-option': 7.23.5 - browserslist: 4.22.2 - lru-cache: 5.1.1 - semver: 6.3.1 - dev: true - /@babel/helper-environment-visitor@7.22.20: + '@babel/helper-environment-visitor@7.22.20': resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} engines: {node: '>=6.9.0'} - dev: true - /@babel/helper-function-name@7.23.0: + '@babel/helper-function-name@7.23.0': resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.23.9 - '@babel/types': 7.23.9 - dev: true - /@babel/helper-hoist-variables@7.22.5: + '@babel/helper-hoist-variables@7.22.5': resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.23.9 - dev: true - /@babel/helper-module-imports@7.22.15: - resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} + '@babel/helper-module-imports@7.24.3': + resolution: {integrity: sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.23.9 - dev: true - /@babel/helper-module-transforms@7.23.3(@babel/core@7.23.9): - resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} + '@babel/helper-module-transforms@7.24.5': + resolution: {integrity: sha512-9GxeY8c2d2mdQUP1Dye0ks3VDyIMS98kt/llQ2nUId8IsWqTF0l1LkSX0/uP7l7MCDrzXS009Hyhe2gzTiGW8A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.23.9 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-module-imports': 7.22.15 - '@babel/helper-simple-access': 7.22.5 - '@babel/helper-split-export-declaration': 7.22.6 - '@babel/helper-validator-identifier': 7.22.20 - dev: true - - /@babel/helper-plugin-utils@7.22.5: - resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==} + + '@babel/helper-plugin-utils@7.24.5': + resolution: {integrity: sha512-xjNLDopRzW2o6ba0gKbkZq5YWEBaK3PCyTOY1K2P/O07LGMhMqlMXPxwN4S5/RhWuCobT8z0jrlKGlYmeR1OhQ==} engines: {node: '>=6.9.0'} - dev: true - /@babel/helper-simple-access@7.22.5: - resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} + '@babel/helper-simple-access@7.24.5': + resolution: {integrity: sha512-uH3Hmf5q5n7n8mz7arjUlDOCbttY/DW4DYhE6FUsjKJ/oYC1kQQUvwEQWxRwUpX9qQKRXeqLwWxrqilMrf32sQ==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.23.9 - dev: true - /@babel/helper-split-export-declaration@7.22.6: - resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} + '@babel/helper-split-export-declaration@7.24.5': + resolution: {integrity: sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.23.9 - dev: true - /@babel/helper-string-parser@7.23.4: - resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==} + '@babel/helper-string-parser@7.24.1': + resolution: {integrity: sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==} engines: {node: '>=6.9.0'} - dev: true - /@babel/helper-validator-identifier@7.22.20: - resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} + '@babel/helper-validator-identifier@7.24.5': + resolution: {integrity: sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==} engines: {node: '>=6.9.0'} - dev: true - /@babel/helper-validator-option@7.23.5: + '@babel/helper-validator-option@7.23.5': resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==} engines: {node: '>=6.9.0'} - dev: true - /@babel/helpers@7.23.9: - resolution: {integrity: sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ==} + '@babel/helpers@7.24.5': + resolution: {integrity: sha512-CiQmBMMpMQHwM5m01YnrM6imUG1ebgYJ+fAIW4FZe6m4qHTPaRHti+R8cggAwkdz4oXhtO4/K9JWlh+8hIfR2Q==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.23.9 - '@babel/traverse': 7.23.9 - '@babel/types': 7.23.9 - transitivePeerDependencies: - - supports-color - dev: true - /@babel/highlight@7.23.4: - resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==} + '@babel/highlight@7.24.5': + resolution: {integrity: sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-validator-identifier': 7.22.20 - chalk: 2.4.2 - js-tokens: 4.0.0 - dev: true - /@babel/parser@7.23.9: - resolution: {integrity: sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==} + '@babel/parser@7.24.5': + resolution: {integrity: sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==} engines: {node: '>=6.0.0'} hasBin: true - dependencies: - '@babel/types': 7.23.9 - dev: true - /@babel/plugin-transform-react-jsx-self@7.23.3(@babel/core@7.23.9): - resolution: {integrity: sha512-qXRvbeKDSfwnlJnanVRp0SfuWE5DQhwQr5xtLBzp56Wabyo+4CMosF6Kfp+eOD/4FYpql64XVJ2W0pVLlJZxOQ==} + '@babel/plugin-transform-react-jsx-self@7.24.5': + resolution: {integrity: sha512-RtCJoUO2oYrYwFPtR1/jkoBEcFuI1ae9a9IMxeyAVa3a1Ap4AnxmyIKG2b2FaJKqkidw/0cxRbWN+HOs6ZWd1w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.9 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-transform-react-jsx-source@7.23.3(@babel/core@7.23.9): - resolution: {integrity: sha512-91RS0MDnAWDNvGC6Wio5XYkyWI39FMFO+JK9+4AlgaTH+yWwVTsw7/sn6LK0lH7c5F+TFkpv/3LfCJ1Ydwof/g==} + '@babel/plugin-transform-react-jsx-source@7.24.1': + resolution: {integrity: sha512-1v202n7aUq4uXAieRTKcwPzNyphlCuqHHDcdSNc+vdhoTEZcFMh+L5yZuCmGaIO7bs1nJUNfHB89TZyoL48xNA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.9 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/runtime@7.23.9: - resolution: {integrity: sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==} + '@babel/runtime@7.24.5': + resolution: {integrity: sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==} engines: {node: '>=6.9.0'} - dependencies: - regenerator-runtime: 0.14.1 - /@babel/template@7.23.9: - resolution: {integrity: sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA==} + '@babel/template@7.24.0': + resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.23.5 - '@babel/parser': 7.23.9 - '@babel/types': 7.23.9 - dev: true - /@babel/traverse@7.23.9: - resolution: {integrity: sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg==} + '@babel/traverse@7.24.5': + resolution: {integrity: sha512-7aaBLeDQ4zYcUFDUD41lJc1fG8+5IU9DaNSJAgal866FGvmD5EbWQgnEC6kO1gGLsX0esNkfnJSndbTXA3r7UA==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.23.5 - '@babel/generator': 7.23.6 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-function-name': 7.23.0 - '@babel/helper-hoist-variables': 7.22.5 - '@babel/helper-split-export-declaration': 7.22.6 - '@babel/parser': 7.23.9 - '@babel/types': 7.23.9 - debug: 4.3.4 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - dev: true - /@babel/types@7.23.9: - resolution: {integrity: sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==} + '@babel/types@7.24.5': + resolution: {integrity: sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-string-parser': 7.23.4 - '@babel/helper-validator-identifier': 7.22.20 - to-fast-properties: 2.0.0 - dev: true - /@esbuild/aix-ppc64@0.19.12: + '@esbuild/aix-ppc64@0.19.12': resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==} engines: {node: '>=12'} cpu: [ppc64] os: [aix] - requiresBuild: true - dev: true - optional: true - /@esbuild/android-arm64@0.19.12: + '@esbuild/aix-ppc64@0.20.2': + resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.19.12': resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==} engines: {node: '>=12'} cpu: [arm64] os: [android] - requiresBuild: true - dev: true - optional: true - /@esbuild/android-arm@0.19.12: + '@esbuild/android-arm64@0.20.2': + resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.19.12': resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==} engines: {node: '>=12'} cpu: [arm] os: [android] - requiresBuild: true - dev: true - optional: true - /@esbuild/android-x64@0.19.12: + '@esbuild/android-arm@0.20.2': + resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.19.12': resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==} engines: {node: '>=12'} cpu: [x64] os: [android] - requiresBuild: true - dev: true - optional: true - /@esbuild/darwin-arm64@0.19.12: + '@esbuild/android-x64@0.20.2': + resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.19.12': resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==} engines: {node: '>=12'} cpu: [arm64] os: [darwin] - requiresBuild: true - dev: true - optional: true - /@esbuild/darwin-x64@0.19.12: + '@esbuild/darwin-arm64@0.20.2': + resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.19.12': resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==} engines: {node: '>=12'} cpu: [x64] os: [darwin] - requiresBuild: true - dev: true - optional: true - /@esbuild/freebsd-arm64@0.19.12: + '@esbuild/darwin-x64@0.20.2': + resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.19.12': resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==} engines: {node: '>=12'} cpu: [arm64] os: [freebsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/freebsd-x64@0.19.12: + '@esbuild/freebsd-arm64@0.20.2': + resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.19.12': resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==} engines: {node: '>=12'} cpu: [x64] os: [freebsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-arm64@0.19.12: + '@esbuild/freebsd-x64@0.20.2': + resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.19.12': resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==} engines: {node: '>=12'} cpu: [arm64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-arm@0.19.12: + '@esbuild/linux-arm64@0.20.2': + resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.19.12': resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==} engines: {node: '>=12'} cpu: [arm] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-ia32@0.19.12: + '@esbuild/linux-arm@0.20.2': + resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.19.12': resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==} engines: {node: '>=12'} cpu: [ia32] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-loong64@0.19.12: + '@esbuild/linux-ia32@0.20.2': + resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.19.12': resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==} engines: {node: '>=12'} cpu: [loong64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-mips64el@0.19.12: + '@esbuild/linux-loong64@0.20.2': + resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.19.12': resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==} engines: {node: '>=12'} cpu: [mips64el] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-ppc64@0.19.12: + '@esbuild/linux-mips64el@0.20.2': + resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.19.12': resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==} engines: {node: '>=12'} cpu: [ppc64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-riscv64@0.19.12: + '@esbuild/linux-ppc64@0.20.2': + resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.19.12': resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==} engines: {node: '>=12'} cpu: [riscv64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-s390x@0.19.12: + '@esbuild/linux-riscv64@0.20.2': + resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.19.12': resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==} engines: {node: '>=12'} cpu: [s390x] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-x64@0.19.12: + '@esbuild/linux-s390x@0.20.2': + resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.19.12': resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==} engines: {node: '>=12'} cpu: [x64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/netbsd-x64@0.19.12: + '@esbuild/linux-x64@0.20.2': + resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.19.12': resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==} engines: {node: '>=12'} cpu: [x64] os: [netbsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/openbsd-x64@0.19.12: + '@esbuild/netbsd-x64@0.20.2': + resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-x64@0.19.12': resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==} engines: {node: '>=12'} cpu: [x64] os: [openbsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/sunos-x64@0.19.12: + '@esbuild/openbsd-x64@0.20.2': + resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.19.12': resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==} engines: {node: '>=12'} cpu: [x64] os: [sunos] - requiresBuild: true - dev: true - optional: true - /@esbuild/win32-arm64@0.19.12: + '@esbuild/sunos-x64@0.20.2': + resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.19.12': resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==} engines: {node: '>=12'} cpu: [arm64] os: [win32] - requiresBuild: true - dev: true - optional: true - /@esbuild/win32-ia32@0.19.12: + '@esbuild/win32-arm64@0.20.2': + resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.19.12': resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==} engines: {node: '>=12'} cpu: [ia32] os: [win32] - requiresBuild: true - dev: true - optional: true - /@esbuild/win32-x64@0.19.12: + '@esbuild/win32-ia32@0.20.2': + resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.19.12': resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==} engines: {node: '>=12'} cpu: [x64] os: [win32] - requiresBuild: true - dev: true - optional: true - /@eslint-community/eslint-utils@4.4.0(eslint@8.56.0): + '@esbuild/win32-x64@0.20.2': + resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.4.0': resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - dependencies: - eslint: 8.56.0 - eslint-visitor-keys: 3.4.3 - /@eslint-community/regexpp@4.10.0: + '@eslint-community/regexpp@4.10.0': resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - /@eslint/eslintrc@2.1.4: - resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - ajv: 6.12.6 - debug: 4.3.4 - espree: 9.6.1 - globals: 13.24.0 - ignore: 5.3.0 - import-fresh: 3.3.0 - js-yaml: 4.1.0 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color + '@eslint/eslintrc@3.0.2': + resolution: {integrity: sha512-wV19ZEGEMAC1eHgrS7UQPqsdEiCIbTKTasEfcXAigzoXICcqZSjBZEHlZwNVvKg6UBCjSlos84XiLqsRJnIcIg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - /@eslint/js@8.56.0: - resolution: {integrity: sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@eslint/js@9.1.1': + resolution: {integrity: sha512-5WoDz3Y19Bg2BnErkZTp0en+c/i9PvgFS7MBe1+m60HjFr0hrphlAGp4yzI7pxpt4xShln4ZyYp4neJm8hmOkQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - /@figma/plugin-typings@1.84.0: - resolution: {integrity: sha512-YisOp7S2y10BWMLBJY2DazCjRpg/0m03dDlS3nKhpw2alFQSXNuSLLd+y4pbnJpOXJMnInZdbikr/7JxudjsYg==} - dev: false + '@figma/plugin-typings@1.92.0': + resolution: {integrity: sha512-W02/uSt+n33Iro0/Goj+Dl1Fp+1i9+jjciw//BcBh0VOz2QzuiaurhftnboO8gKucOloBOROMbxe/Vylu4ItZw==} - /@humanwhocodes/config-array@0.11.14: - resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} + '@humanwhocodes/config-array@0.13.0': + resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} engines: {node: '>=10.10.0'} - dependencies: - '@humanwhocodes/object-schema': 2.0.2 - debug: 4.3.4 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - /@humanwhocodes/module-importer@1.0.1: + '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} - /@humanwhocodes/object-schema@2.0.2: - resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==} + '@humanwhocodes/object-schema@2.0.3': + resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} + + '@humanwhocodes/retry@0.2.3': + resolution: {integrity: sha512-X38nUbachlb01YMlvPFojKoiXq+LzZvuSce70KPMPdeM1Rj03k4dR7lDslhbqXn3Ang4EU3+EAmwEAsbrjHW3g==} + engines: {node: '>=18.18'} - /@isaacs/cliui@8.0.2: + '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} - dependencies: - string-width: 5.1.2 - string-width-cjs: /string-width@4.2.3 - strip-ansi: 7.1.0 - strip-ansi-cjs: /strip-ansi@6.0.1 - wrap-ansi: 8.1.0 - wrap-ansi-cjs: /wrap-ansi@7.0.0 - /@jridgewell/gen-mapping@0.3.3: - resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} + '@jridgewell/gen-mapping@0.3.5': + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/set-array': 1.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 - '@jridgewell/trace-mapping': 0.3.22 - /@jridgewell/resolve-uri@3.1.1: - resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - /@jridgewell/set-array@1.1.2: - resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} engines: {node: '>=6.0.0'} - /@jridgewell/sourcemap-codec@1.4.15: + '@jridgewell/sourcemap-codec@1.4.15': resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} - /@jridgewell/trace-mapping@0.3.22: - resolution: {integrity: sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==} - dependencies: - '@jridgewell/resolve-uri': 3.1.1 - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} - /@next/env@14.1.0: - resolution: {integrity: sha512-Py8zIo+02ht82brwwhTg36iogzFqGLPXlRGKQw5s+qP/kMNc4MAyDeEwBKDijk6zTIbegEgu8Qy7C1LboslQAw==} - dev: false + '@next/env@14.2.3': + resolution: {integrity: sha512-W7fd7IbkfmeeY2gXrzJYDx8D2lWKbVoTIj1o1ScPHNzvp30s1AuoEFSdr39bC5sjxJaxTtq3OTCZboNp0lNWHA==} - /@next/eslint-plugin-next@14.1.0: - resolution: {integrity: sha512-x4FavbNEeXx/baD/zC/SdrvkjSby8nBn8KcCREqk6UuwvwoAPZmaV8TFCAuo/cpovBRTIY67mHhe86MQQm/68Q==} - dependencies: - glob: 10.3.10 - dev: false + '@next/eslint-plugin-next@14.2.3': + resolution: {integrity: sha512-L3oDricIIjgj1AVnRdRor21gI7mShlSwU/1ZGHmqM3LzHhXXhdkrfeNY5zif25Bi5Dd7fiJHsbhoZCHfXYvlAw==} - /@next/swc-darwin-arm64@14.1.0: - resolution: {integrity: sha512-nUDn7TOGcIeyQni6lZHfzNoo9S0euXnu0jhsbMOmMJUBfgsnESdjN97kM7cBqQxZa8L/bM9om/S5/1dzCrW6wQ==} + '@next/swc-darwin-arm64@14.2.3': + resolution: {integrity: sha512-3pEYo/RaGqPP0YzwnlmPN2puaF2WMLM3apt5jLW2fFdXD9+pqcoTzRk+iZsf8ta7+quAe4Q6Ms0nR0SFGFdS1A==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - requiresBuild: true - dev: false - optional: true - /@next/swc-darwin-x64@14.1.0: - resolution: {integrity: sha512-1jgudN5haWxiAl3O1ljUS2GfupPmcftu2RYJqZiMJmmbBT5M1XDffjUtRUzP4W3cBHsrvkfOFdQ71hAreNQP6g==} + '@next/swc-darwin-x64@14.2.3': + resolution: {integrity: sha512-6adp7waE6P1TYFSXpY366xwsOnEXM+y1kgRpjSRVI2CBDOcbRjsJ67Z6EgKIqWIue52d2q/Mx8g9MszARj8IEA==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - requiresBuild: true - dev: false - optional: true - /@next/swc-linux-arm64-gnu@14.1.0: - resolution: {integrity: sha512-RHo7Tcj+jllXUbK7xk2NyIDod3YcCPDZxj1WLIYxd709BQ7WuRYl3OWUNG+WUfqeQBds6kvZYlc42NJJTNi4tQ==} + '@next/swc-linux-arm64-gnu@14.2.3': + resolution: {integrity: sha512-cuzCE/1G0ZSnTAHJPUT1rPgQx1w5tzSX7POXSLaS7w2nIUJUD+e25QoXD/hMfxbsT9rslEXugWypJMILBj/QsA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - requiresBuild: true - dev: false - optional: true - /@next/swc-linux-arm64-musl@14.1.0: - resolution: {integrity: sha512-v6kP8sHYxjO8RwHmWMJSq7VZP2nYCkRVQ0qolh2l6xroe9QjbgV8siTbduED4u0hlk0+tjS6/Tuy4n5XCp+l6g==} + '@next/swc-linux-arm64-musl@14.2.3': + resolution: {integrity: sha512-0D4/oMM2Y9Ta3nGuCcQN8jjJjmDPYpHX9OJzqk42NZGJocU2MqhBq5tWkJrUQOQY9N+In9xOdymzapM09GeiZw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - requiresBuild: true - dev: false - optional: true - /@next/swc-linux-x64-gnu@14.1.0: - resolution: {integrity: sha512-zJ2pnoFYB1F4vmEVlb/eSe+VH679zT1VdXlZKX+pE66grOgjmKJHKacf82g/sWE4MQ4Rk2FMBCRnX+l6/TVYzQ==} + '@next/swc-linux-x64-gnu@14.2.3': + resolution: {integrity: sha512-ENPiNnBNDInBLyUU5ii8PMQh+4XLr4pG51tOp6aJ9xqFQ2iRI6IH0Ds2yJkAzNV1CfyagcyzPfROMViS2wOZ9w==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - requiresBuild: true - dev: false - optional: true - /@next/swc-linux-x64-musl@14.1.0: - resolution: {integrity: sha512-rbaIYFt2X9YZBSbH/CwGAjbBG2/MrACCVu2X0+kSykHzHnYH5FjHxwXLkcoJ10cX0aWCEynpu+rP76x0914atg==} + '@next/swc-linux-x64-musl@14.2.3': + resolution: {integrity: sha512-BTAbq0LnCbF5MtoM7I/9UeUu/8ZBY0i8SFjUMCbPDOLv+un67e2JgyN4pmgfXBwy/I+RHu8q+k+MCkDN6P9ViQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - requiresBuild: true - dev: false - optional: true - /@next/swc-win32-arm64-msvc@14.1.0: - resolution: {integrity: sha512-o1N5TsYc8f/HpGt39OUQpQ9AKIGApd3QLueu7hXk//2xq5Z9OxmV6sQfNp8C7qYmiOlHYODOGqNNa0e9jvchGQ==} + '@next/swc-win32-arm64-msvc@14.2.3': + resolution: {integrity: sha512-AEHIw/dhAMLNFJFJIJIyOFDzrzI5bAjI9J26gbO5xhAKHYTZ9Or04BesFPXiAYXDNdrwTP2dQceYA4dL1geu8A==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - requiresBuild: true - dev: false - optional: true - /@next/swc-win32-ia32-msvc@14.1.0: - resolution: {integrity: sha512-XXIuB1DBRCFwNO6EEzCTMHT5pauwaSj4SWs7CYnME57eaReAKBXCnkUE80p/pAZcewm7hs+vGvNqDPacEXHVkw==} + '@next/swc-win32-ia32-msvc@14.2.3': + resolution: {integrity: sha512-vga40n1q6aYb0CLrM+eEmisfKCR45ixQYXuBXxOOmmoV8sYST9k7E3US32FsY+CkkF7NtzdcebiFT4CHuMSyZw==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] - requiresBuild: true - dev: false - optional: true - /@next/swc-win32-x64-msvc@14.1.0: - resolution: {integrity: sha512-9WEbVRRAqJ3YFVqEZIxUqkiO8l1nool1LmNxygr5HWF8AcSYsEpneUDhmjUVJEzO2A04+oPtZdombzzPPkTtgg==} + '@next/swc-win32-x64-msvc@14.2.3': + resolution: {integrity: sha512-Q1/zm43RWynxrO7lW4ehciQVj+5ePBhOK+/K2P7pLFX3JaJ/IZVC69SHidrmZSOkqz7ECIOhhy7XhAFG4JYyHA==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - requiresBuild: true - dev: false - optional: true - /@nodelib/fs.scandir@2.1.5: + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - /@nodelib/fs.stat@2.0.5: + '@nodelib/fs.stat@2.0.5': resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} engines: {node: '>= 8'} - /@nodelib/fs.walk@1.2.8: + '@nodelib/fs.walk@1.2.8': resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.16.0 - /@pkgjs/parseargs@0.11.0: + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - requiresBuild: true - optional: true - /@rollup/rollup-android-arm-eabi@4.9.6: - resolution: {integrity: sha512-MVNXSSYN6QXOulbHpLMKYi60ppyO13W9my1qogeiAqtjb2yR4LSmfU2+POvDkLzhjYLXz9Rf9+9a3zFHW1Lecg==} + '@rollup/rollup-android-arm-eabi@4.17.2': + resolution: {integrity: sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==} cpu: [arm] os: [android] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-android-arm64@4.9.6: - resolution: {integrity: sha512-T14aNLpqJ5wzKNf5jEDpv5zgyIqcpn1MlwCrUXLrwoADr2RkWA0vOWP4XxbO9aiO3dvMCQICZdKeDrFl7UMClw==} + '@rollup/rollup-android-arm64@4.17.2': + resolution: {integrity: sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==} cpu: [arm64] os: [android] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-darwin-arm64@4.9.6: - resolution: {integrity: sha512-CqNNAyhRkTbo8VVZ5R85X73H3R5NX9ONnKbXuHisGWC0qRbTTxnF1U4V9NafzJbgGM0sHZpdO83pLPzq8uOZFw==} + '@rollup/rollup-darwin-arm64@4.17.2': + resolution: {integrity: sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==} cpu: [arm64] os: [darwin] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-darwin-x64@4.9.6: - resolution: {integrity: sha512-zRDtdJuRvA1dc9Mp6BWYqAsU5oeLixdfUvkTHuiYOHwqYuQ4YgSmi6+/lPvSsqc/I0Omw3DdICx4Tfacdzmhog==} + '@rollup/rollup-darwin-x64@4.17.2': + resolution: {integrity: sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==} cpu: [x64] os: [darwin] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-arm-gnueabihf@4.9.6: - resolution: {integrity: sha512-oNk8YXDDnNyG4qlNb6is1ojTOGL/tRhbbKeE/YuccItzerEZT68Z9gHrY3ROh7axDc974+zYAPxK5SH0j/G+QQ==} + '@rollup/rollup-linux-arm-gnueabihf@4.17.2': + resolution: {integrity: sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.17.2': + resolution: {integrity: sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==} cpu: [arm] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-arm64-gnu@4.9.6: - resolution: {integrity: sha512-Z3O60yxPtuCYobrtzjo0wlmvDdx2qZfeAWTyfOjEDqd08kthDKexLpV97KfAeUXPosENKd8uyJMRDfFMxcYkDQ==} + '@rollup/rollup-linux-arm64-gnu@4.17.2': + resolution: {integrity: sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==} cpu: [arm64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-arm64-musl@4.9.6: - resolution: {integrity: sha512-gpiG0qQJNdYEVad+1iAsGAbgAnZ8j07FapmnIAQgODKcOTjLEWM9sRb+MbQyVsYCnA0Im6M6QIq6ax7liws6eQ==} + '@rollup/rollup-linux-arm64-musl@4.17.2': + resolution: {integrity: sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==} cpu: [arm64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-riscv64-gnu@4.9.6: - resolution: {integrity: sha512-+uCOcvVmFUYvVDr27aiyun9WgZk0tXe7ThuzoUTAukZJOwS5MrGbmSlNOhx1j80GdpqbOty05XqSl5w4dQvcOA==} + '@rollup/rollup-linux-powerpc64le-gnu@4.17.2': + resolution: {integrity: sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.17.2': + resolution: {integrity: sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==} cpu: [riscv64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-x64-gnu@4.9.6: - resolution: {integrity: sha512-HUNqM32dGzfBKuaDUBqFB7tP6VMN74eLZ33Q9Y1TBqRDn+qDonkAUyKWwF9BR9unV7QUzffLnz9GrnKvMqC/fw==} + '@rollup/rollup-linux-s390x-gnu@4.17.2': + resolution: {integrity: sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.17.2': + resolution: {integrity: sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==} cpu: [x64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-x64-musl@4.9.6: - resolution: {integrity: sha512-ch7M+9Tr5R4FK40FHQk8VnML0Szi2KRujUgHXd/HjuH9ifH72GUmw6lStZBo3c3GB82vHa0ZoUfjfcM7JiiMrQ==} + '@rollup/rollup-linux-x64-musl@4.17.2': + resolution: {integrity: sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==} cpu: [x64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-win32-arm64-msvc@4.9.6: - resolution: {integrity: sha512-VD6qnR99dhmTQ1mJhIzXsRcTBvTjbfbGGwKAHcu+52cVl15AC/kplkhxzW/uT0Xl62Y/meBKDZvoJSJN+vTeGA==} + '@rollup/rollup-win32-arm64-msvc@4.17.2': + resolution: {integrity: sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==} cpu: [arm64] os: [win32] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-win32-ia32-msvc@4.9.6: - resolution: {integrity: sha512-J9AFDq/xiRI58eR2NIDfyVmTYGyIZmRcvcAoJ48oDld/NTR8wyiPUu2X/v1navJ+N/FGg68LEbX3Ejd6l8B7MQ==} + '@rollup/rollup-win32-ia32-msvc@4.17.2': + resolution: {integrity: sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==} cpu: [ia32] os: [win32] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-win32-x64-msvc@4.9.6: - resolution: {integrity: sha512-jqzNLhNDvIZOrt69Ce4UjGRpXJBzhUBzawMwnaDAwyHriki3XollsewxWzOzz+4yOFDkuJHtTsZFwMxhYJWmLQ==} + '@rollup/rollup-win32-x64-msvc@4.17.2': + resolution: {integrity: sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==} cpu: [x64] os: [win32] - requiresBuild: true - dev: true - optional: true - /@rushstack/eslint-patch@1.7.1: - resolution: {integrity: sha512-irBNt5094vHloql4QzY8RdeI8Tns2kGsaiJ/m6jENWx9xCz/m/F4gKQ1dAailFmpL0Id9tgWLqZbTUO4SINM/Q==} - dev: false + '@rushstack/eslint-patch@1.10.2': + resolution: {integrity: sha512-hw437iINopmQuxWPSUEvqE56NCPsiU8N4AYtfHmJFckclktzK9YQJieD3XkDCDH4OjL+C7zgPUh73R/nrcHrqw==} - /@swc/core-darwin-arm64@1.3.106: - resolution: {integrity: sha512-XYcbViNyHnnm7RWOAO1YipMmthM7m2aXF32b0y+JMLYFBEyFpjVX9btLkzeL7wRx/5B3I35yJNhE+xyx0Q1Gkw==} + '@swc/core-darwin-arm64@1.4.17': + resolution: {integrity: sha512-HVl+W4LezoqHBAYg2JCqR+s9ife9yPfgWSj37iIawLWzOmuuJ7jVdIB7Ee2B75bEisSEKyxRlTl6Y1Oq3owBgw==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] - requiresBuild: true - dev: true - optional: true - /@swc/core-darwin-x64@1.3.106: - resolution: {integrity: sha512-YKDPhUdfuwhmOUS9+CaIwl/0Tp+f1b73BH2EIESuxSNsogZf18a8HQ8O0fQEwdiwmA5LEqw47cj+kfOWV/0+kw==} + '@swc/core-darwin-x64@1.4.17': + resolution: {integrity: sha512-WYRO9Fdzq4S/he8zjW5I95G1zcvyd9yyD3Tgi4/ic84P5XDlSMpBDpBLbr/dCPjmSg7aUXxNQqKqGkl6dQxYlA==} engines: {node: '>=10'} cpu: [x64] os: [darwin] - requiresBuild: true - dev: true - optional: true - /@swc/core-linux-arm-gnueabihf@1.3.106: - resolution: {integrity: sha512-bHxxJXogvFfocLL5inZxxtx/x/WgKozigp80Vbx0viac1fPDJrqKBw2X4MzpMiuTRAGVQ03jJI6pDwbSBf+yDw==} + '@swc/core-linux-arm-gnueabihf@1.4.17': + resolution: {integrity: sha512-cgbvpWOvtMH0XFjvwppUCR+Y+nf6QPaGu6AQ5hqCP+5Lv2zO5PG0RfasC4zBIjF53xgwEaaWmGP5/361P30X8Q==} engines: {node: '>=10'} cpu: [arm] os: [linux] - requiresBuild: true - dev: true - optional: true - /@swc/core-linux-arm64-gnu@1.3.106: - resolution: {integrity: sha512-c7jue++CHLgtpeaakEukoCLT9eNrImizbleE9Y7Is8CHqLq/7DG4s+7ma9DFKXIzW2MpTg9byIEQfpqSphVW6A==} + '@swc/core-linux-arm64-gnu@1.4.17': + resolution: {integrity: sha512-l7zHgaIY24cF9dyQ/FOWbmZDsEj2a9gRFbmgx2u19e3FzOPuOnaopFj0fRYXXKCmtdx+anD750iBIYnTR+pq/Q==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@swc/core-linux-arm64-musl@1.3.106: - resolution: {integrity: sha512-51EaC3Q8qAhLtWVnAVqoYX/gk3tK31cCBzUpwCcmhianhEBM2/WtKRAS4MqPhE8VVZuN3WjO2c2JaF2mX0yuoA==} + '@swc/core-linux-arm64-musl@1.4.17': + resolution: {integrity: sha512-qhH4gr9gAlVk8MBtzXbzTP3BJyqbAfUOATGkyUtohh85fPXQYuzVlbExix3FZXTwFHNidGHY8C+ocscI7uDaYw==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@swc/core-linux-x64-gnu@1.3.106: - resolution: {integrity: sha512-tOUi8BB6jAeCXgx7ESLNnX7nrbMVKQ/XajK77v7Ad4SXf9HYArnimBJpXUUyVFJTXLSv4e6c7s6XHHqXb5Lwcg==} + '@swc/core-linux-x64-gnu@1.4.17': + resolution: {integrity: sha512-vRDFATL1oN5oZMImkwbgSHEkp8xG1ofEASBypze01W1Tqto8t+yo6gsp69wzCZBlxldsvPpvFZW55Jq0Rn+UnA==} engines: {node: '>=10'} cpu: [x64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@swc/core-linux-x64-musl@1.3.106: - resolution: {integrity: sha512-binLw4Lbd83NPy4/m/teH2nbaifxveSD+sKDvpxywRbvYW2I0w/iCBpUBcbnl16TQF4TPOGpq5YwG9lVxPVw5g==} + '@swc/core-linux-x64-musl@1.4.17': + resolution: {integrity: sha512-zQNPXAXn3nmPqv54JVEN8k2JMEcMTQ6veVuU0p5O+A7KscJq+AGle/7ZQXzpXSfUCXlLMX4wvd+rwfGhh3J4cw==} engines: {node: '>=10'} cpu: [x64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@swc/core-win32-arm64-msvc@1.3.106: - resolution: {integrity: sha512-n4ttBWr8tM7DPzwcEOIBTyTMHZTzCmbic/HTtxEsPyMAf/Daen+yrTKzjPP6k2usfSrjkxA780RSJJxI1N8r2w==} + '@swc/core-win32-arm64-msvc@1.4.17': + resolution: {integrity: sha512-z86n7EhOwyzxwm+DLE5NoLkxCTme2lq7QZlDjbQyfCxOt6isWz8rkW5QowTX8w9Rdmk34ncrjSLvnHOeLY17+w==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - requiresBuild: true - dev: true - optional: true - /@swc/core-win32-ia32-msvc@1.3.106: - resolution: {integrity: sha512-GhDNIwxE5FhkujESI6h/4ysT3wxwmrzTUlZYaR8rRui6a6SdX9feIPUHPEE5o5hpyp+xqlmvRxKkRxOnwsq8iA==} + '@swc/core-win32-ia32-msvc@1.4.17': + resolution: {integrity: sha512-JBwuSTJIgiJJX6wtr4wmXbfvOswHFj223AumUrK544QV69k60FJ9q2adPW9Csk+a8wm1hLxq4HKa2K334UHJ/g==} engines: {node: '>=10'} cpu: [ia32] os: [win32] - requiresBuild: true - dev: true - optional: true - /@swc/core-win32-x64-msvc@1.3.106: - resolution: {integrity: sha512-2M6yWChuMS1+/MPo3Dor0SOMkvmiugonWlzsZBAu/oZboH2xKrHSRv7brsBujb2Oe47r+NsbV+vq9tnnP9Vl1Q==} + '@swc/core-win32-x64-msvc@1.4.17': + resolution: {integrity: sha512-jFkOnGQamtVDBm3MF5Kq1lgW8vx4Rm1UvJWRUfg+0gx7Uc3Jp3QMFeMNw/rDNQYRDYPG3yunCC+2463ycd5+dg==} engines: {node: '>=10'} cpu: [x64] os: [win32] - requiresBuild: true - dev: true - optional: true - /@swc/core@1.3.106: - resolution: {integrity: sha512-++QPSPkFq2qELYVScxNHJC42hKQChjiTWS2P0QQ5JWT4NHb9lmNSfrc1ylFIyImwRnxsW2MTBALLYLf95EFAsg==} + '@swc/core@1.4.17': + resolution: {integrity: sha512-tq+mdWvodMBNBBZbwFIMTVGYHe9N7zvEaycVVjfvAx20k1XozHbHhRv+9pEVFJjwRxLdXmtvFZd3QZHRAOpoNQ==} engines: {node: '>=10'} - requiresBuild: true peerDependencies: '@swc/helpers': ^0.5.0 peerDependenciesMeta: '@swc/helpers': optional: true - dependencies: - '@swc/counter': 0.1.2 - '@swc/types': 0.1.5 - optionalDependencies: - '@swc/core-darwin-arm64': 1.3.106 - '@swc/core-darwin-x64': 1.3.106 - '@swc/core-linux-arm-gnueabihf': 1.3.106 - '@swc/core-linux-arm64-gnu': 1.3.106 - '@swc/core-linux-arm64-musl': 1.3.106 - '@swc/core-linux-x64-gnu': 1.3.106 - '@swc/core-linux-x64-musl': 1.3.106 - '@swc/core-win32-arm64-msvc': 1.3.106 - '@swc/core-win32-ia32-msvc': 1.3.106 - '@swc/core-win32-x64-msvc': 1.3.106 - dev: true - - /@swc/counter@0.1.2: - resolution: {integrity: sha512-9F4ys4C74eSTEUNndnER3VJ15oru2NumfQxS8geE+f3eB5xvfxpWyqE5XlVnxb/R14uoXi6SLbBwwiDSkv+XEw==} - dev: true - - /@swc/helpers@0.5.2: - resolution: {integrity: sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==} - dependencies: - tslib: 2.6.2 - dev: false - /@swc/types@0.1.5: - resolution: {integrity: sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==} - dev: true + '@swc/counter@0.1.3': + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + + '@swc/helpers@0.5.11': + resolution: {integrity: sha512-YNlnKRWF2sVojTpIyzwou9XoTNbzbzONwRhOoniEioF1AtaitTvVZblaQRrAzChWQ1bLYyYSWzM18y4WwgzJ+A==} + + '@swc/helpers@0.5.5': + resolution: {integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==} + + '@swc/types@0.1.6': + resolution: {integrity: sha512-/JLo/l2JsT/LRd80C3HfbmVpxOAJ11FO2RCEslFrgzLltoP9j8XIbsyDcfCt2WWyX+CM96rBoNM+IToAkFOugg==} - /@types/babel__core@7.20.5: + '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} - dependencies: - '@babel/parser': 7.23.9 - '@babel/types': 7.23.9 - '@types/babel__generator': 7.6.8 - '@types/babel__template': 7.4.4 - '@types/babel__traverse': 7.20.5 - dev: true - /@types/babel__generator@7.6.8: + '@types/babel__generator@7.6.8': resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} - dependencies: - '@babel/types': 7.23.9 - dev: true - /@types/babel__template@7.4.4: + '@types/babel__template@7.4.4': resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} - dependencies: - '@babel/parser': 7.23.9 - '@babel/types': 7.23.9 - dev: true - /@types/babel__traverse@7.20.5: + '@types/babel__traverse@7.20.5': resolution: {integrity: sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==} - dependencies: - '@babel/types': 7.23.9 - dev: true - /@types/estree@1.0.5: + '@types/estree@1.0.5': resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} - dev: true - /@types/hast@2.3.9: - resolution: {integrity: sha512-pTHyNlaMD/oKJmS+ZZUyFUcsZeBZpC0lmGquw98CqRVNgAdJZJeD7GoeLiT6Xbx5rU9VCjSt0RwEvDgzh4obFw==} - dependencies: + '@types/hast@2.3.10': + resolution: {integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + + '@types/node@20.12.8': + resolution: {integrity: sha512-NU0rJLJnshZWdE/097cdCBbyW1h4hEg0xpovcoAQYHl8dnEyp/NAOiE45pvc+Bd1Dt+2r94v2eGFpQJ4R7g+2w==} + + '@types/prop-types@15.7.12': + resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} + + '@types/react-dom@18.3.0': + resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==} + + '@types/react-syntax-highlighter@15.5.11': + resolution: {integrity: sha512-ZqIJl+Pg8kD+47kxUjvrlElrraSUrYa4h0dauY/U/FTUuprSCqvUj+9PNQNQzVc6AJgIWUUxn87/gqsMHNbRjw==} + + '@types/react@18.3.1': + resolution: {integrity: sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==} + + '@types/semver@7.5.8': + resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} + + '@types/unist@2.0.10': + resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==} + + '@typescript-eslint/eslint-plugin@7.8.0': + resolution: {integrity: sha512-gFTT+ezJmkwutUPmB0skOj3GZJtlEGnlssems4AjkVweUPGj7jRwwqg0Hhg7++kPGJqKtTYx+R05Ftww372aIg==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + '@typescript-eslint/parser': ^7.0.0 + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/parser@7.2.0': + resolution: {integrity: sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/parser@7.8.0': + resolution: {integrity: sha512-KgKQly1pv0l4ltcftP59uQZCi4HUYswCLbTqVZEJu7uLX8CTLyswqMLqLN+2QFz4jCptqWVV4SB7vdxcH2+0kQ==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/scope-manager@7.2.0': + resolution: {integrity: sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==} + engines: {node: ^16.0.0 || >=18.0.0} + + '@typescript-eslint/scope-manager@7.8.0': + resolution: {integrity: sha512-viEmZ1LmwsGcnr85gIq+FCYI7nO90DVbE37/ll51hjv9aG+YZMb4WDE2fyWpUR4O/UrhGRpYXK/XajcGTk2B8g==} + engines: {node: ^18.18.0 || >=20.0.0} + + '@typescript-eslint/type-utils@7.8.0': + resolution: {integrity: sha512-H70R3AefQDQpz9mGv13Uhi121FNMh+WEaRqcXTX09YEDky21km4dV1ZXJIp8QjXc4ZaVkXVdohvWDzbnbHDS+A==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/types@7.2.0': + resolution: {integrity: sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==} + engines: {node: ^16.0.0 || >=18.0.0} + + '@typescript-eslint/types@7.8.0': + resolution: {integrity: sha512-wf0peJ+ZGlcH+2ZS23aJbOv+ztjeeP8uQ9GgwMJGVLx/Nj9CJt17GWgWWoSmoRVKAX2X+7fzEnAjxdvK2gqCLw==} + engines: {node: ^18.18.0 || >=20.0.0} + + '@typescript-eslint/typescript-estree@7.2.0': + resolution: {integrity: sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/typescript-estree@7.8.0': + resolution: {integrity: sha512-5pfUCOwK5yjPaJQNy44prjCwtr981dO8Qo9J9PwYXZ0MosgAbfEMB008dJ5sNo3+/BN6ytBPuSvXUg9SAqB0dg==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/utils@7.8.0': + resolution: {integrity: sha512-L0yFqOCflVqXxiZyXrDr80lnahQfSOfc9ELAAZ75sqicqp2i36kEZZGuUymHNFoYOqxRT05up760b4iGsl02nQ==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + + '@typescript-eslint/visitor-keys@7.2.0': + resolution: {integrity: sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==} + engines: {node: ^16.0.0 || >=18.0.0} + + '@typescript-eslint/visitor-keys@7.8.0': + resolution: {integrity: sha512-q4/gibTNBQNA0lGyYQCmWRS5D15n8rXh4QjK3KV+MBPlTYHpfBUT3D3PaPR/HeNiI9W6R7FvlkcGhNyAoP+caA==} + engines: {node: ^18.18.0 || >=20.0.0} + + '@vitejs/plugin-react-swc@3.6.0': + resolution: {integrity: sha512-XFRbsGgpGxGzEV5i5+vRiro1bwcIaZDIdBRP16qwm+jP68ue/S8FJTBEgOeojtVDYrbSua3XFp71kC8VJE6v+g==} + peerDependencies: + vite: ^4 || ^5 + + '@vitejs/plugin-react@4.2.1': + resolution: {integrity: sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.11.3: + resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + + ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + + array-buffer-byte-length@1.0.1: + resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} + engines: {node: '>= 0.4'} + + array-includes@3.1.8: + resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} + engines: {node: '>= 0.4'} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + array.prototype.findlast@1.2.5: + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlastindex@1.2.5: + resolution: {integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.2: + resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.2: + resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} + engines: {node: '>= 0.4'} + + array.prototype.toreversed@1.1.2: + resolution: {integrity: sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==} + + array.prototype.tosorted@1.1.3: + resolution: {integrity: sha512-/DdH4TiTmOKzyQbp/eadcCVexiCb36xJg7HshYOYJnNZFDj33GEv0P7GxsynpShhq4OLYJzbGcBDkLsDt7MnNg==} + + arraybuffer.prototype.slice@1.0.3: + resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} + engines: {node: '>= 0.4'} + + ast-types-flow@0.0.8: + resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} + + autoprefixer@10.4.19: + resolution: {integrity: sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + axe-core@4.7.0: + resolution: {integrity: sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==} + engines: {node: '>=4'} + + axobject-query@3.2.1: + resolution: {integrity: sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + + braces@3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + + browserslist@4.23.0: + resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + bundle-require@4.0.3: + resolution: {integrity: sha512-2iscZ3fcthP2vka4Y7j277YJevwmsby/FpFDwjgw34Nl7dtCpt7zz/4TexmHMzY6KZEih7En9ImlbbgUNNQGtA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + peerDependencies: + esbuild: '>=0.17' + + busboy@1.6.0: + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + call-bind@1.0.7: + resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + + caniuse-lite@1.0.30001614: + resolution: {integrity: sha512-jmZQ1VpmlRwHgdP1/uiKzgiAuGOfLEJsYFP4+GBou/QQ4U6IOJCB4NP1c+1p9RGLpwObcT94jA5/uO+F1vBbog==} + + chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + character-entities-legacy@1.1.4: + resolution: {integrity: sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==} + + character-entities@1.2.4: + resolution: {integrity: sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==} + + character-reference-invalid@1.1.4: + resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + comma-separated-tokens@1.0.8: + resolution: {integrity: sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==} + + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + concurrently@8.2.2: + resolution: {integrity: sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==} + engines: {node: ^14.13.0 || >=16.0.0} + hasBin: true + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + copy-to-clipboard@3.3.3: + resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} + + cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + damerau-levenshtein@1.0.8: + resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} + + data-view-buffer@1.0.1: + resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.1: + resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.0: + resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} + engines: {node: '>= 0.4'} + + date-fns@2.30.0: + resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} + engines: {node: '>=0.11'} + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + dotenv@16.0.3: + resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==} + engines: {node: '>=12'} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + electron-to-chromium@1.4.753: + resolution: {integrity: sha512-Wn1XKa0Lc5kMe5UIwQc4+i5lhhBggF0l82C1bE3oOMASt4JVqdOyRIVc8mh0kiuL5CCptqwQJBmFbaPglLrN0Q==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + enhanced-resolve@5.16.0: + resolution: {integrity: sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==} + engines: {node: '>=10.13.0'} + + es-abstract@1.23.3: + resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.0: + resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-iterator-helpers@1.0.19: + resolution: {integrity: sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.0.0: + resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.0.3: + resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.0.2: + resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} + + es-to-primitive@1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + + esbuild@0.19.12: + resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} + engines: {node: '>=12'} + hasBin: true + + esbuild@0.20.2: + resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==} + engines: {node: '>=12'} + hasBin: true + + escalade@3.1.2: + resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} + engines: {node: '>=6'} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-config-next@14.2.3: + resolution: {integrity: sha512-ZkNztm3Q7hjqvB1rRlOX8P9E/cXRL9ajRcs8jufEtwMfTVYRqnmtnaSu57QqHyBlovMuiB8LEzfLBkh5RYV6Fg==} + peerDependencies: + eslint: ^7.23.0 || ^8.0.0 + typescript: '>=3.3.1' + peerDependenciesMeta: + typescript: + optional: true + + eslint-config-prettier@9.1.0: + resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-config-turbo@1.13.3: + resolution: {integrity: sha512-if/QtwEiWZ5b7Bg8yZBPSvS0TeCG2Zvfa/+XBYANS7uSYucjmW+BBC8enJB0PqpB/YLGGOumeo3x7h1Nuba9iw==} + peerDependencies: + eslint: '>6.6.0' + + eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + + eslint-import-resolver-typescript@3.6.1: + resolution: {integrity: sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '*' + eslint-plugin-import: '*' + + eslint-module-utils@2.8.1: + resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + + eslint-plugin-import@2.29.1: + resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-plugin-jsx-a11y@6.8.0: + resolution: {integrity: sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA==} + engines: {node: '>=4.0'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + + eslint-plugin-react-hooks@4.6.2: + resolution: {integrity: sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + + eslint-plugin-react-refresh@0.4.6: + resolution: {integrity: sha512-NjGXdm7zgcKRkKMua34qVO9doI7VOxZ6ancSvBELJSSoX97jyndXcSoa8XBh69JoB31dNz3EEzlMcizZl7LaMA==} + peerDependencies: + eslint: '>=7' + + eslint-plugin-react@7.34.1: + resolution: {integrity: sha512-N97CxlouPT1AHt8Jn0mhhN2RrADlUAsk1/atcT2KyA/l9Q/E6ll7OIGwNumFmWfZ9skV3XXccYS19h80rHtgkw==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + + eslint-plugin-turbo@1.13.3: + resolution: {integrity: sha512-RjmlnqYsEqnJ+U3M3IS5jLJDjWv5NsvReCpsC61n5pJ4JMHTZ/lU0EIoL1ccuL1L5wP0APzdXdByBxERcPQ+Nw==} + peerDependencies: + eslint: '>6.6.0' + + eslint-scope@8.0.1: + resolution: {integrity: sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.0.0: + resolution: {integrity: sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.1.1: + resolution: {integrity: sha512-b4cRQ0BeZcSEzPpY2PjFY70VbO32K7BStTGtBsnIGdTSEEQzBi8hPBcGQmTG2zUvFr9uLe0TK42bw8YszuHEqg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + + espree@10.0.1: + resolution: {integrity: sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esquery@1.5.0: + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + + fault@1.0.4: + resolution: {integrity: sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==} + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + fill-range@7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.3.1: + resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} + + for-each@0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + + foreground-child@3.1.1: + resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} + engines: {node: '>=14'} + + format@0.2.2: + resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} + engines: {node: '>=0.4.x'} + + fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.1.6: + resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-intrinsic@1.2.4: + resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + engines: {node: '>= 0.4'} + + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + + get-symbol-description@1.0.2: + resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.7.3: + resolution: {integrity: sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob@10.3.10: + resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + + glob@10.3.12: + resolution: {integrity: sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + + globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + has-bigints@1.0.2: + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.0.3: + resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + engines: {node: '>= 0.4'} + + has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hast-util-parse-selector@2.2.5: + resolution: {integrity: sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==} + + hastscript@6.0.0: + resolution: {integrity: sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==} + + highlight.js@10.7.3: + resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} + + human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + + ignore@5.3.1: + resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} + engines: {node: '>= 4'} + + import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + internal-slot@1.0.7: + resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} + engines: {node: '>= 0.4'} + + is-alphabetical@1.0.4: + resolution: {integrity: sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==} + + is-alphanumerical@1.0.4: + resolution: {integrity: sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==} + + is-array-buffer@3.0.4: + resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} + engines: {node: '>= 0.4'} + + is-async-function@2.0.0: + resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} + engines: {node: '>= 0.4'} + + is-bigint@1.0.4: + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-boolean-object@1.1.2: + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.13.1: + resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} + + is-data-view@1.0.1: + resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} + engines: {node: '>= 0.4'} + + is-date-object@1.0.5: + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} + + is-decimal@1.0.4: + resolution: {integrity: sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.0.2: + resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-generator-function@1.0.10: + resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-hexadecimal@1.0.4: + resolution: {integrity: sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.0.7: + resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + + is-regex@1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.3: + resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} + engines: {node: '>= 0.4'} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-string@1.0.7: + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} + + is-symbol@1.0.4: + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.13: + resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} + engines: {node: '>= 0.4'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.0.2: + resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + + is-weakset@2.0.3: + resolution: {integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==} + engines: {node: '>= 0.4'} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + iterator.prototype@1.1.2: + resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} + + jackspeak@2.3.6: + resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} + engines: {node: '>=14'} + + jiti@1.21.0: + resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==} + hasBin: true + + joycon@3.1.1: + resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} + engines: {node: '>=10'} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + jsesc@2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + language-subtag-registry@0.3.22: + resolution: {integrity: sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==} + + language-tags@1.0.9: + resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} + engines: {node: '>=0.10'} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + + lilconfig@3.1.1: + resolution: {integrity: sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + load-tsconfig@0.2.5: + resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.sortby@4.7.0: + resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lowlight@1.20.0: + resolution: {integrity: sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==} + + lru-cache@10.2.2: + resolution: {integrity: sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==} + engines: {node: 14 || >=16.14} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@9.0.3: + resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} + engines: {node: '>=16 || 14 >=14.17'} + + minimatch@9.0.4: + resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass@7.0.4: + resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==} + engines: {node: '>=16 || 14 >=14.17'} + + ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + + nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + next@14.2.3: + resolution: {integrity: sha512-dowFkFTR8v79NPJO4QsBUtxv0g9BrS/phluVpMAt2ku7H+cbcBJlopXjkWlwxrk/xGqMemr7JkGPGemPrLLX7A==} + engines: {node: '>=18.17.0'} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.41.2 + react: ^18.2.0 + react-dom: ^18.2.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@playwright/test': + optional: true + sass: + optional: true + + node-releases@2.0.14: + resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + + object-inspect@1.13.1: + resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.5: + resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} + engines: {node: '>= 0.4'} + + object.entries@1.1.8: + resolution: {integrity: sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + + object.hasown@1.1.4: + resolution: {integrity: sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==} + engines: {node: '>= 0.4'} + + object.values@1.2.0: + resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} + engines: {node: '>= 0.4'} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-entities@2.0.0: + resolution: {integrity: sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@1.10.2: + resolution: {integrity: sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==} + engines: {node: '>=16 || 14 >=14.17'} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + + pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + engines: {node: '>= 6'} + + possible-typed-array-names@1.0.0: + resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} + engines: {node: '>= 0.4'} + + postcss-import@15.1.0: + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.0.0 + + postcss-js@4.0.1: + resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + + postcss-load-config@4.0.2: + resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} + engines: {node: '>= 14'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + + postcss-nested@6.0.1: + resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + + postcss-selector-parser@6.0.16: + resolution: {integrity: sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==} + engines: {node: '>=4'} + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.4.31: + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} + engines: {node: ^10 || ^12 || >=14} + + postcss@8.4.38: + resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier@3.2.5: + resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==} + engines: {node: '>=14'} + hasBin: true + + prismjs@1.27.0: + resolution: {integrity: sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==} + engines: {node: '>=6'} + + prismjs@1.29.0: + resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==} + engines: {node: '>=6'} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + property-information@5.6.0: + resolution: {integrity: sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + react-dom@18.3.1: + resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + peerDependencies: + react: ^18.3.1 + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-refresh@0.14.2: + resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} + engines: {node: '>=0.10.0'} + + react-syntax-highlighter@15.5.0: + resolution: {integrity: sha512-+zq2myprEnQmH5yw6Gqc8lD55QHnpKaU8TOcFeC/Lg/MQSs8UknEA0JC4nTZGFAXC2J2Hyj/ijJ7NlabyPi2gg==} + peerDependencies: + react: '>= 0.14.0' + + react@18.3.1: + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + engines: {node: '>=0.10.0'} + + read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + reflect.getprototypeof@1.0.6: + resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==} + engines: {node: '>= 0.4'} + + refractor@3.6.0: + resolution: {integrity: sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==} + + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + + regexp.prototype.flags@1.5.2: + resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} + engines: {node: '>= 0.4'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + + resolve@2.0.0-next.5: + resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} + hasBin: true + + reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rollup@4.17.2: + resolution: {integrity: sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + rxjs@7.8.1: + resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + + safe-array-concat@1.1.2: + resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} + engines: {node: '>=0.4'} + + safe-regex-test@1.0.3: + resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} + engines: {node: '>= 0.4'} + + scheduler@0.23.2: + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.6.0: + resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==} + engines: {node: '>=10'} + hasBin: true + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shell-quote@1.8.1: + resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} + + side-channel@1.0.6: + resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} + engines: {node: '>= 0.4'} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + engines: {node: '>=0.10.0'} + + source-map@0.8.0-beta.0: + resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} + engines: {node: '>= 8'} + + space-separated-tokens@1.1.5: + resolution: {integrity: sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==} + + spawn-command@0.0.2: + resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==} + + streamsearch@1.1.0: + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string.prototype.matchall@4.0.11: + resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==} + engines: {node: '>= 0.4'} + + string.prototype.trim@1.2.9: + resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.8: + resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + styled-jsx@5.1.1: + resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@babel/core': '*' + babel-plugin-macros: '*' + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0' + peerDependenciesMeta: + '@babel/core': + optional: true + babel-plugin-macros: + optional: true + + sucrase@3.35.0: + resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + tailwindcss@3.4.3: + resolution: {integrity: sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==} + engines: {node: '>=14.0.0'} + hasBin: true + + tapable@2.2.1: + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + engines: {node: '>=6'} + + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + + to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toggle-selection@1.0.6: + resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} + + tr46@1.0.1: + resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} + + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + + ts-api-utils@1.3.0: + resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} + engines: {node: '>=16'} + peerDependencies: + typescript: '>=4.2.0' + + ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + + tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + + tsup@8.0.2: + resolution: {integrity: sha512-NY8xtQXdH7hDUAZwcQdY/Vzlw9johQsaqf7iwZ6g1DOUlFYQ5/AtVAjTvihhEyeRlGo4dLRVHtrRaL35M1daqQ==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + '@microsoft/api-extractor': ^7.36.0 + '@swc/core': ^1 + postcss: ^8.4.12 + typescript: '>=4.5.0' + peerDependenciesMeta: + '@microsoft/api-extractor': + optional: true + '@swc/core': + optional: true + postcss: + optional: true + typescript: + optional: true + + turbo-darwin-64@1.13.3: + resolution: {integrity: sha512-glup8Qx1qEFB5jerAnXbS8WrL92OKyMmg5Hnd4PleLljAeYmx+cmmnsmLT7tpaVZIN58EAAwu8wHC6kIIqhbWA==} + cpu: [x64] + os: [darwin] + + turbo-darwin-arm64@1.13.3: + resolution: {integrity: sha512-/np2xD+f/+9qY8BVtuOQXRq5f9LehCFxamiQnwdqWm5iZmdjygC5T3uVSYuagVFsZKMvX3ycySwh8dylGTl6lg==} + cpu: [arm64] + os: [darwin] + + turbo-linux-64@1.13.3: + resolution: {integrity: sha512-G+HGrau54iAnbXLfl+N/PynqpDwi/uDzb6iM9hXEDG+yJnSJxaHMShhOkXYJPk9offm9prH33Khx2scXrYVW1g==} + cpu: [x64] + os: [linux] + + turbo-linux-arm64@1.13.3: + resolution: {integrity: sha512-qWwEl5VR02NqRyl68/3pwp3c/olZuSp+vwlwrunuoNTm6JXGLG5pTeme4zoHNnk0qn4cCX7DFrOboArlYxv0wQ==} + cpu: [arm64] + os: [linux] + + turbo-windows-64@1.13.3: + resolution: {integrity: sha512-Nudr4bRChfJzBPzEmpVV85VwUYRCGKecwkBFpbp2a4NtrJ3+UP1VZES653ckqCu2FRyRuS0n03v9euMbAvzH+Q==} + cpu: [x64] + os: [win32] + + turbo-windows-arm64@1.13.3: + resolution: {integrity: sha512-ouJCgsVLd3icjRLmRvHQDDZnmGzT64GBupM1Y+TjtYn2LVaEBoV6hicFy8x5DUpnqdLy+YpCzRMkWlwhmkX7sQ==} + cpu: [arm64] + os: [win32] + + turbo@1.13.3: + resolution: {integrity: sha512-n17HJv4F4CpsYTvKzUJhLbyewbXjq1oLCi90i5tW1TiWDz16ML1eDG7wi5dHaKxzh5efIM56SITnuVbMq5dk4g==} + hasBin: true + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + typed-array-buffer@1.0.2: + resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.1: + resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.2: + resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.6: + resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} + engines: {node: '>= 0.4'} + + typescript@5.4.5: + resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} + engines: {node: '>=14.17'} + hasBin: true + + unbox-primitive@1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + + undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + + update-browserslist-db@1.0.14: + resolution: {integrity: sha512-JixKH8GR2pWYshIPUg/NujK3JO7JiqEEUiNArE86NQyrgUuZeTlZQN3xuS/yiV5Kb48ev9K6RqNkaJjXsdg7Jw==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + vite-plugin-singlefile@2.0.1: + resolution: {integrity: sha512-J74tfN6TE4fz0Hp7E1+dmVTmCpyazv4yuIpR6jd22Kq76d2CQDSQx3wDiHX8LT02f922V+YrLhRq2VIk/UYrig==} + engines: {node: '>18.0.0'} + peerDependencies: + rollup: ^4.12.0 + vite: ^5.1.4 + + vite@5.2.10: + resolution: {integrity: sha512-PAzgUZbP7msvQvqdSD+ErD5qGnSFiGOoWmV5yAKUEI0kdhjbH6nMWVyZQC/hSc4aXwc0oJ9aEdIiF9Oje0JFCw==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + webidl-conversions@4.0.2: + resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} + + whatwg-url@7.1.0: + resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} + + which-boxed-primitive@1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + + which-builtin-type@1.1.3: + resolution: {integrity: sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.15: + resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} + engines: {node: '>= 0.4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yaml@2.4.2: + resolution: {integrity: sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA==} + engines: {node: '>= 14'} + hasBin: true + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + +snapshots: + + '@alloc/quick-lru@5.2.0': {} + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + + '@babel/code-frame@7.24.2': + dependencies: + '@babel/highlight': 7.24.5 + picocolors: 1.0.0 + + '@babel/compat-data@7.24.4': {} + + '@babel/core@7.24.5': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.24.2 + '@babel/generator': 7.24.5 + '@babel/helper-compilation-targets': 7.23.6 + '@babel/helper-module-transforms': 7.24.5(@babel/core@7.24.5) + '@babel/helpers': 7.24.5 + '@babel/parser': 7.24.5 + '@babel/template': 7.24.0 + '@babel/traverse': 7.24.5 + '@babel/types': 7.24.5 + convert-source-map: 2.0.0 + debug: 4.3.4 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.24.5': + dependencies: + '@babel/types': 7.24.5 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 2.5.2 + + '@babel/helper-compilation-targets@7.23.6': + dependencies: + '@babel/compat-data': 7.24.4 + '@babel/helper-validator-option': 7.23.5 + browserslist: 4.23.0 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-environment-visitor@7.22.20': {} + + '@babel/helper-function-name@7.23.0': + dependencies: + '@babel/template': 7.24.0 + '@babel/types': 7.24.5 + + '@babel/helper-hoist-variables@7.22.5': + dependencies: + '@babel/types': 7.24.5 + + '@babel/helper-module-imports@7.24.3': + dependencies: + '@babel/types': 7.24.5 + + '@babel/helper-module-transforms@7.24.5(@babel/core@7.24.5)': + dependencies: + '@babel/core': 7.24.5 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-module-imports': 7.24.3 + '@babel/helper-simple-access': 7.24.5 + '@babel/helper-split-export-declaration': 7.24.5 + '@babel/helper-validator-identifier': 7.24.5 + + '@babel/helper-plugin-utils@7.24.5': {} + + '@babel/helper-simple-access@7.24.5': + dependencies: + '@babel/types': 7.24.5 + + '@babel/helper-split-export-declaration@7.24.5': + dependencies: + '@babel/types': 7.24.5 + + '@babel/helper-string-parser@7.24.1': {} + + '@babel/helper-validator-identifier@7.24.5': {} + + '@babel/helper-validator-option@7.23.5': {} + + '@babel/helpers@7.24.5': + dependencies: + '@babel/template': 7.24.0 + '@babel/traverse': 7.24.5 + '@babel/types': 7.24.5 + transitivePeerDependencies: + - supports-color + + '@babel/highlight@7.24.5': + dependencies: + '@babel/helper-validator-identifier': 7.24.5 + chalk: 2.4.2 + js-tokens: 4.0.0 + picocolors: 1.0.0 + + '@babel/parser@7.24.5': + dependencies: + '@babel/types': 7.24.5 + + '@babel/plugin-transform-react-jsx-self@7.24.5(@babel/core@7.24.5)': + dependencies: + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 + + '@babel/plugin-transform-react-jsx-source@7.24.1(@babel/core@7.24.5)': + dependencies: + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 + + '@babel/runtime@7.24.5': + dependencies: + regenerator-runtime: 0.14.1 + + '@babel/template@7.24.0': + dependencies: + '@babel/code-frame': 7.24.2 + '@babel/parser': 7.24.5 + '@babel/types': 7.24.5 + + '@babel/traverse@7.24.5': + dependencies: + '@babel/code-frame': 7.24.2 + '@babel/generator': 7.24.5 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-hoist-variables': 7.22.5 + '@babel/helper-split-export-declaration': 7.24.5 + '@babel/parser': 7.24.5 + '@babel/types': 7.24.5 + debug: 4.3.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.24.5': + dependencies: + '@babel/helper-string-parser': 7.24.1 + '@babel/helper-validator-identifier': 7.24.5 + to-fast-properties: 2.0.0 + + '@esbuild/aix-ppc64@0.19.12': + optional: true + + '@esbuild/aix-ppc64@0.20.2': + optional: true + + '@esbuild/android-arm64@0.19.12': + optional: true + + '@esbuild/android-arm64@0.20.2': + optional: true + + '@esbuild/android-arm@0.19.12': + optional: true + + '@esbuild/android-arm@0.20.2': + optional: true + + '@esbuild/android-x64@0.19.12': + optional: true + + '@esbuild/android-x64@0.20.2': + optional: true + + '@esbuild/darwin-arm64@0.19.12': + optional: true + + '@esbuild/darwin-arm64@0.20.2': + optional: true + + '@esbuild/darwin-x64@0.19.12': + optional: true + + '@esbuild/darwin-x64@0.20.2': + optional: true + + '@esbuild/freebsd-arm64@0.19.12': + optional: true + + '@esbuild/freebsd-arm64@0.20.2': + optional: true + + '@esbuild/freebsd-x64@0.19.12': + optional: true + + '@esbuild/freebsd-x64@0.20.2': + optional: true + + '@esbuild/linux-arm64@0.19.12': + optional: true + + '@esbuild/linux-arm64@0.20.2': + optional: true + + '@esbuild/linux-arm@0.19.12': + optional: true + + '@esbuild/linux-arm@0.20.2': + optional: true + + '@esbuild/linux-ia32@0.19.12': + optional: true + + '@esbuild/linux-ia32@0.20.2': + optional: true + + '@esbuild/linux-loong64@0.19.12': + optional: true + + '@esbuild/linux-loong64@0.20.2': + optional: true + + '@esbuild/linux-mips64el@0.19.12': + optional: true + + '@esbuild/linux-mips64el@0.20.2': + optional: true + + '@esbuild/linux-ppc64@0.19.12': + optional: true + + '@esbuild/linux-ppc64@0.20.2': + optional: true + + '@esbuild/linux-riscv64@0.19.12': + optional: true + + '@esbuild/linux-riscv64@0.20.2': + optional: true + + '@esbuild/linux-s390x@0.19.12': + optional: true + + '@esbuild/linux-s390x@0.20.2': + optional: true + + '@esbuild/linux-x64@0.19.12': + optional: true + + '@esbuild/linux-x64@0.20.2': + optional: true + + '@esbuild/netbsd-x64@0.19.12': + optional: true + + '@esbuild/netbsd-x64@0.20.2': + optional: true + + '@esbuild/openbsd-x64@0.19.12': + optional: true + + '@esbuild/openbsd-x64@0.20.2': + optional: true + + '@esbuild/sunos-x64@0.19.12': + optional: true + + '@esbuild/sunos-x64@0.20.2': + optional: true + + '@esbuild/win32-arm64@0.19.12': + optional: true + + '@esbuild/win32-arm64@0.20.2': + optional: true + + '@esbuild/win32-ia32@0.19.12': + optional: true + + '@esbuild/win32-ia32@0.20.2': + optional: true + + '@esbuild/win32-x64@0.19.12': + optional: true + + '@esbuild/win32-x64@0.20.2': + optional: true + + '@eslint-community/eslint-utils@4.4.0(eslint@9.1.1)': + dependencies: + eslint: 9.1.1 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.10.0': {} + + '@eslint/eslintrc@3.0.2': + dependencies: + ajv: 6.12.6 + debug: 4.3.4 + espree: 10.0.1 + globals: 14.0.0 + ignore: 5.3.1 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.1.1': {} + + '@figma/plugin-typings@1.92.0': {} + + '@humanwhocodes/config-array@0.13.0': + dependencies: + '@humanwhocodes/object-schema': 2.0.3 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/object-schema@2.0.3': {} + + '@humanwhocodes/retry@0.2.3': {} + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@jridgewell/gen-mapping@0.3.5': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/sourcemap-codec@1.4.15': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + + '@next/env@14.2.3': {} + + '@next/eslint-plugin-next@14.2.3': + dependencies: + glob: 10.3.10 + + '@next/swc-darwin-arm64@14.2.3': + optional: true + + '@next/swc-darwin-x64@14.2.3': + optional: true + + '@next/swc-linux-arm64-gnu@14.2.3': + optional: true + + '@next/swc-linux-arm64-musl@14.2.3': + optional: true + + '@next/swc-linux-x64-gnu@14.2.3': + optional: true + + '@next/swc-linux-x64-musl@14.2.3': + optional: true + + '@next/swc-win32-arm64-msvc@14.2.3': + optional: true + + '@next/swc-win32-ia32-msvc@14.2.3': + optional: true + + '@next/swc-win32-x64-msvc@14.2.3': + optional: true + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.17.1 + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@rollup/rollup-android-arm-eabi@4.17.2': + optional: true + + '@rollup/rollup-android-arm64@4.17.2': + optional: true + + '@rollup/rollup-darwin-arm64@4.17.2': + optional: true + + '@rollup/rollup-darwin-x64@4.17.2': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.17.2': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.17.2': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.17.2': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.17.2': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.17.2': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.17.2': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.17.2': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.17.2': + optional: true + + '@rollup/rollup-linux-x64-musl@4.17.2': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.17.2': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.17.2': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.17.2': + optional: true + + '@rushstack/eslint-patch@1.10.2': {} + + '@swc/core-darwin-arm64@1.4.17': + optional: true + + '@swc/core-darwin-x64@1.4.17': + optional: true + + '@swc/core-linux-arm-gnueabihf@1.4.17': + optional: true + + '@swc/core-linux-arm64-gnu@1.4.17': + optional: true + + '@swc/core-linux-arm64-musl@1.4.17': + optional: true + + '@swc/core-linux-x64-gnu@1.4.17': + optional: true + + '@swc/core-linux-x64-musl@1.4.17': + optional: true + + '@swc/core-win32-arm64-msvc@1.4.17': + optional: true + + '@swc/core-win32-ia32-msvc@1.4.17': + optional: true + + '@swc/core-win32-x64-msvc@1.4.17': + optional: true + + '@swc/core@1.4.17(@swc/helpers@0.5.11)': + dependencies: + '@swc/counter': 0.1.3 + '@swc/types': 0.1.6 + optionalDependencies: + '@swc/core-darwin-arm64': 1.4.17 + '@swc/core-darwin-x64': 1.4.17 + '@swc/core-linux-arm-gnueabihf': 1.4.17 + '@swc/core-linux-arm64-gnu': 1.4.17 + '@swc/core-linux-arm64-musl': 1.4.17 + '@swc/core-linux-x64-gnu': 1.4.17 + '@swc/core-linux-x64-musl': 1.4.17 + '@swc/core-win32-arm64-msvc': 1.4.17 + '@swc/core-win32-ia32-msvc': 1.4.17 + '@swc/core-win32-x64-msvc': 1.4.17 + '@swc/helpers': 0.5.11 + + '@swc/counter@0.1.3': {} + + '@swc/helpers@0.5.11': + dependencies: + tslib: 2.6.2 + optional: true + + '@swc/helpers@0.5.5': + dependencies: + '@swc/counter': 0.1.3 + tslib: 2.6.2 + + '@swc/types@0.1.6': + dependencies: + '@swc/counter': 0.1.3 + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.24.5 + '@babel/types': 7.24.5 + '@types/babel__generator': 7.6.8 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.20.5 + + '@types/babel__generator@7.6.8': + dependencies: + '@babel/types': 7.24.5 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.24.5 + '@babel/types': 7.24.5 + + '@types/babel__traverse@7.20.5': + dependencies: + '@babel/types': 7.24.5 + + '@types/estree@1.0.5': {} + + '@types/hast@2.3.10': + dependencies: '@types/unist': 2.0.10 - dev: false - /@types/json-schema@7.0.15: - resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - dev: true + '@types/json-schema@7.0.15': {} - /@types/json5@0.0.29: - resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} - dev: false + '@types/json5@0.0.29': {} - /@types/node@20.11.6: - resolution: {integrity: sha512-+EOokTnksGVgip2PbYbr3xnR7kZigh4LbybAfBAw5BpnQ+FqBYUsvCEjYd70IXKlbohQ64mzEYmMtlWUY8q//Q==} + '@types/node@20.12.8': dependencies: undici-types: 5.26.5 - dev: true - /@types/prop-types@15.7.11: - resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==} - dev: true + '@types/prop-types@15.7.12': {} - /@types/react-dom@18.2.18: - resolution: {integrity: sha512-TJxDm6OfAX2KJWJdMEVTwWke5Sc/E/RlnPGvGfS0W7+6ocy2xhDVQVh/KvC2Uf7kACs+gDytdusDSdWfWkaNzw==} + '@types/react-dom@18.3.0': dependencies: - '@types/react': 18.2.48 - dev: true + '@types/react': 18.3.1 - /@types/react-syntax-highlighter@15.5.11: - resolution: {integrity: sha512-ZqIJl+Pg8kD+47kxUjvrlElrraSUrYa4h0dauY/U/FTUuprSCqvUj+9PNQNQzVc6AJgIWUUxn87/gqsMHNbRjw==} + '@types/react-syntax-highlighter@15.5.11': dependencies: - '@types/react': 18.2.48 - dev: true + '@types/react': 18.3.1 - /@types/react@18.2.48: - resolution: {integrity: sha512-qboRCl6Ie70DQQG9hhNREz81jqC1cs9EVNcjQ1AU+jH6NFfSAhVVbrrY/+nSF+Bsk4AOwm9Qa61InvMCyV+H3w==} + '@types/react@18.3.1': dependencies: - '@types/prop-types': 15.7.11 - '@types/scheduler': 0.16.8 + '@types/prop-types': 15.7.12 csstype: 3.1.3 - dev: true - /@types/scheduler@0.16.8: - resolution: {integrity: sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==} - dev: true + '@types/semver@7.5.8': {} - /@types/semver@7.5.6: - resolution: {integrity: sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==} - dev: true - - /@types/unist@2.0.10: - resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==} - dev: false + '@types/unist@2.0.10': {} - /@typescript-eslint/eslint-plugin@6.19.1(@typescript-eslint/parser@6.19.1)(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-roQScUGFruWod9CEyoV5KlCYrubC/fvG8/1zXuT0WTcxX87GnMMmnksMwSg99lo1xiKrBzw2icsJPMAw1OtKxg==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha - eslint: ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@typescript-eslint/eslint-plugin@7.8.0(@typescript-eslint/parser@7.8.0(eslint@9.1.1)(typescript@5.4.5))(eslint@9.1.1)(typescript@5.4.5)': dependencies: '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 6.19.1(eslint@8.56.0)(typescript@5.3.3) - '@typescript-eslint/scope-manager': 6.19.1 - '@typescript-eslint/type-utils': 6.19.1(eslint@8.56.0)(typescript@5.3.3) - '@typescript-eslint/utils': 6.19.1(eslint@8.56.0)(typescript@5.3.3) - '@typescript-eslint/visitor-keys': 6.19.1 + '@typescript-eslint/parser': 7.8.0(eslint@9.1.1)(typescript@5.4.5) + '@typescript-eslint/scope-manager': 7.8.0 + '@typescript-eslint/type-utils': 7.8.0(eslint@9.1.1)(typescript@5.4.5) + '@typescript-eslint/utils': 7.8.0(eslint@9.1.1)(typescript@5.4.5) + '@typescript-eslint/visitor-keys': 7.8.0 debug: 4.3.4 - eslint: 8.56.0 + eslint: 9.1.1 graphemer: 1.4.0 - ignore: 5.3.0 + ignore: 5.3.1 natural-compare: 1.4.0 - semver: 7.5.4 - ts-api-utils: 1.0.3(typescript@5.3.3) - typescript: 5.3.3 + semver: 7.6.0 + ts-api-utils: 1.3.0(typescript@5.4.5) + optionalDependencies: + typescript: 5.4.5 transitivePeerDependencies: - supports-color - dev: true - /@typescript-eslint/parser@6.19.1(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-WEfX22ziAh6pRE9jnbkkLGp/4RhTpffr2ZK5bJ18M8mIfA8A+k97U9ZyaXCEJRlmMHh7R9MJZWXp/r73DzINVQ==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@typescript-eslint/parser@7.2.0(eslint@9.1.1)(typescript@5.4.5)': dependencies: - '@typescript-eslint/scope-manager': 6.19.1 - '@typescript-eslint/types': 6.19.1 - '@typescript-eslint/typescript-estree': 6.19.1(typescript@5.3.3) - '@typescript-eslint/visitor-keys': 6.19.1 + '@typescript-eslint/scope-manager': 7.2.0 + '@typescript-eslint/types': 7.2.0 + '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.4.5) + '@typescript-eslint/visitor-keys': 7.2.0 debug: 4.3.4 - eslint: 8.56.0 - typescript: 5.3.3 + eslint: 9.1.1 + optionalDependencies: + typescript: 5.4.5 transitivePeerDependencies: - supports-color - /@typescript-eslint/scope-manager@6.19.1: - resolution: {integrity: sha512-4CdXYjKf6/6aKNMSly/BP4iCSOpvMmqtDzRtqFyyAae3z5kkqEjKndR5vDHL8rSuMIIWP8u4Mw4VxLyxZW6D5w==} - engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/parser@7.8.0(eslint@9.1.1)(typescript@5.4.5)': dependencies: - '@typescript-eslint/types': 6.19.1 - '@typescript-eslint/visitor-keys': 6.19.1 + '@typescript-eslint/scope-manager': 7.8.0 + '@typescript-eslint/types': 7.8.0 + '@typescript-eslint/typescript-estree': 7.8.0(typescript@5.4.5) + '@typescript-eslint/visitor-keys': 7.8.0 + debug: 4.3.4 + eslint: 9.1.1 + optionalDependencies: + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color - /@typescript-eslint/type-utils@6.19.1(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-0vdyld3ecfxJuddDjACUvlAeYNrHP/pDeQk2pWBR2ESeEzQhg52DF53AbI9QCBkYE23lgkhLCZNkHn2hEXXYIg==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@typescript-eslint/scope-manager@7.2.0': + dependencies: + '@typescript-eslint/types': 7.2.0 + '@typescript-eslint/visitor-keys': 7.2.0 + + '@typescript-eslint/scope-manager@7.8.0': + dependencies: + '@typescript-eslint/types': 7.8.0 + '@typescript-eslint/visitor-keys': 7.8.0 + + '@typescript-eslint/type-utils@7.8.0(eslint@9.1.1)(typescript@5.4.5)': dependencies: - '@typescript-eslint/typescript-estree': 6.19.1(typescript@5.3.3) - '@typescript-eslint/utils': 6.19.1(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/typescript-estree': 7.8.0(typescript@5.4.5) + '@typescript-eslint/utils': 7.8.0(eslint@9.1.1)(typescript@5.4.5) debug: 4.3.4 - eslint: 8.56.0 - ts-api-utils: 1.0.3(typescript@5.3.3) - typescript: 5.3.3 + eslint: 9.1.1 + ts-api-utils: 1.3.0(typescript@5.4.5) + optionalDependencies: + typescript: 5.4.5 transitivePeerDependencies: - supports-color - dev: true - /@typescript-eslint/types@6.19.1: - resolution: {integrity: sha512-6+bk6FEtBhvfYvpHsDgAL3uo4BfvnTnoge5LrrCj2eJN8g3IJdLTD4B/jK3Q6vo4Ql/Hoip9I8aB6fF+6RfDqg==} - engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/types@7.2.0': {} - /@typescript-eslint/typescript-estree@6.19.1(typescript@5.3.3): - resolution: {integrity: sha512-aFdAxuhzBFRWhy+H20nYu19+Km+gFfwNO4TEqyszkMcgBDYQjmPJ61erHxuT2ESJXhlhrO7I5EFIlZ+qGR8oVA==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@typescript-eslint/types@7.8.0': {} + + '@typescript-eslint/typescript-estree@7.2.0(typescript@5.4.5)': dependencies: - '@typescript-eslint/types': 6.19.1 - '@typescript-eslint/visitor-keys': 6.19.1 + '@typescript-eslint/types': 7.2.0 + '@typescript-eslint/visitor-keys': 7.2.0 debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.3 - semver: 7.5.4 - ts-api-utils: 1.0.3(typescript@5.3.3) - typescript: 5.3.3 + semver: 7.6.0 + ts-api-utils: 1.3.0(typescript@5.4.5) + optionalDependencies: + typescript: 5.4.5 transitivePeerDependencies: - supports-color - /@typescript-eslint/utils@6.19.1(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-JvjfEZuP5WoMqwh9SPAPDSHSg9FBHHGhjPugSRxu5jMfjvBpq5/sGTD+9M9aQ5sh6iJ8AY/Kk/oUYVEMAPwi7w==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 + '@typescript-eslint/typescript-estree@7.8.0(typescript@5.4.5)': + dependencies: + '@typescript-eslint/types': 7.8.0 + '@typescript-eslint/visitor-keys': 7.8.0 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.4 + semver: 7.6.0 + ts-api-utils: 1.3.0(typescript@5.4.5) + optionalDependencies: + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@7.8.0(eslint@9.1.1)(typescript@5.4.5)': dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.1.1) '@types/json-schema': 7.0.15 - '@types/semver': 7.5.6 - '@typescript-eslint/scope-manager': 6.19.1 - '@typescript-eslint/types': 6.19.1 - '@typescript-eslint/typescript-estree': 6.19.1(typescript@5.3.3) - eslint: 8.56.0 - semver: 7.5.4 + '@types/semver': 7.5.8 + '@typescript-eslint/scope-manager': 7.8.0 + '@typescript-eslint/types': 7.8.0 + '@typescript-eslint/typescript-estree': 7.8.0(typescript@5.4.5) + eslint: 9.1.1 + semver: 7.6.0 transitivePeerDependencies: - supports-color - typescript - dev: true - /@typescript-eslint/visitor-keys@6.19.1: - resolution: {integrity: sha512-gkdtIO+xSO/SmI0W68DBg4u1KElmIUo3vXzgHyGPs6cxgB0sa3TlptRAAE0hUY1hM6FcDKEv7aIwiTGm76cXfQ==} - engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/visitor-keys@7.2.0': dependencies: - '@typescript-eslint/types': 6.19.1 + '@typescript-eslint/types': 7.2.0 eslint-visitor-keys: 3.4.3 - /@ungap/structured-clone@1.2.0: - resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + '@typescript-eslint/visitor-keys@7.8.0': + dependencies: + '@typescript-eslint/types': 7.8.0 + eslint-visitor-keys: 3.4.3 - /@vitejs/plugin-react-swc@3.5.0(vite@5.0.12): - resolution: {integrity: sha512-1PrOvAaDpqlCV+Up8RkAh9qaiUjoDUcjtttyhXDKw53XA6Ve16SOp6cCOpRs8Dj8DqUQs6eTW5YkLcLJjrXAig==} - peerDependencies: - vite: ^4 || ^5 + '@vitejs/plugin-react-swc@3.6.0(@swc/helpers@0.5.11)(vite@5.2.10(@types/node@20.12.8))': dependencies: - '@swc/core': 1.3.106 - vite: 5.0.12(@types/node@20.11.6) + '@swc/core': 1.4.17(@swc/helpers@0.5.11) + vite: 5.2.10(@types/node@20.12.8) transitivePeerDependencies: - '@swc/helpers' - dev: true - /@vitejs/plugin-react@4.2.1(vite@5.0.12): - resolution: {integrity: sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - vite: ^4.2.0 || ^5.0.0 + '@vitejs/plugin-react@4.2.1(vite@5.2.10(@types/node@20.12.8))': dependencies: - '@babel/core': 7.23.9 - '@babel/plugin-transform-react-jsx-self': 7.23.3(@babel/core@7.23.9) - '@babel/plugin-transform-react-jsx-source': 7.23.3(@babel/core@7.23.9) + '@babel/core': 7.24.5 + '@babel/plugin-transform-react-jsx-self': 7.24.5(@babel/core@7.24.5) + '@babel/plugin-transform-react-jsx-source': 7.24.1(@babel/core@7.24.5) '@types/babel__core': 7.20.5 - react-refresh: 0.14.0 - vite: 5.0.12(@types/node@20.11.6) + react-refresh: 0.14.2 + vite: 5.2.10(@types/node@20.12.8) transitivePeerDependencies: - supports-color - dev: true - /acorn-jsx@5.3.2(acorn@8.11.3): - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + acorn-jsx@5.3.2(acorn@8.11.3): dependencies: acorn: 8.11.3 - /acorn@8.11.3: - resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} - engines: {node: '>=0.4.0'} - hasBin: true + acorn@8.11.3: {} - /ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 fast-json-stable-stringify: 2.1.0 json-schema-traverse: 0.4.1 uri-js: 4.4.1 - /ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} + ansi-regex@5.0.1: {} - /ansi-regex@6.0.1: - resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} - engines: {node: '>=12'} + ansi-regex@6.0.1: {} - /ansi-styles@3.2.1: - resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} - engines: {node: '>=4'} + ansi-styles@3.2.1: dependencies: color-convert: 1.9.3 - dev: true - /ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 - /ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} - engines: {node: '>=12'} - - /any-promise@1.3.0: - resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + ansi-styles@6.2.1: {} - /anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} + any-promise@1.3.0: {} + + anymatch@3.1.3: dependencies: normalize-path: 3.0.0 picomatch: 2.3.1 - /arg@5.0.2: - resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + arg@5.0.2: {} - /argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + argparse@2.0.1: {} - /aria-query@5.3.0: - resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + aria-query@5.3.0: dependencies: dequal: 2.0.3 - dev: false - /array-buffer-byte-length@1.0.0: - resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} + array-buffer-byte-length@1.0.1: dependencies: - call-bind: 1.0.5 - is-array-buffer: 3.0.2 - dev: false + call-bind: 1.0.7 + is-array-buffer: 3.0.4 - /array-includes@3.1.7: - resolution: {integrity: sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==} - engines: {node: '>= 0.4'} + array-includes@3.1.8: dependencies: - call-bind: 1.0.5 + call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.3 - get-intrinsic: 1.2.2 + es-abstract: 1.23.3 + es-object-atoms: 1.0.0 + get-intrinsic: 1.2.4 is-string: 1.0.7 - dev: false - /array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} + array-union@2.1.0: {} - /array.prototype.findlastindex@1.2.3: - resolution: {integrity: sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==} - engines: {node: '>= 0.4'} + array.prototype.findlast@1.2.5: dependencies: - call-bind: 1.0.5 + call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.3 + es-abstract: 1.23.3 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 es-shim-unscopables: 1.0.2 - get-intrinsic: 1.2.2 - dev: false - /array.prototype.flat@1.3.2: - resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} - engines: {node: '>= 0.4'} + array.prototype.findlastindex@1.2.5: dependencies: - call-bind: 1.0.5 + call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.3 + es-abstract: 1.23.3 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 es-shim-unscopables: 1.0.2 - dev: false - /array.prototype.flatmap@1.3.2: - resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} - engines: {node: '>= 0.4'} + array.prototype.flat@1.3.2: dependencies: - call-bind: 1.0.5 + call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.3 + es-abstract: 1.23.3 es-shim-unscopables: 1.0.2 - dev: false - /array.prototype.tosorted@1.1.2: - resolution: {integrity: sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg==} + array.prototype.flatmap@1.3.2: dependencies: - call-bind: 1.0.5 + call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.3 + es-abstract: 1.23.3 es-shim-unscopables: 1.0.2 - get-intrinsic: 1.2.2 - dev: false - /arraybuffer.prototype.slice@1.0.2: - resolution: {integrity: sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==} - engines: {node: '>= 0.4'} + array.prototype.toreversed@1.1.2: dependencies: - array-buffer-byte-length: 1.0.0 - call-bind: 1.0.5 + call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.3 - get-intrinsic: 1.2.2 - is-array-buffer: 3.0.2 - is-shared-array-buffer: 1.0.2 - dev: false + es-abstract: 1.23.3 + es-shim-unscopables: 1.0.2 - /ast-types-flow@0.0.8: - resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} - dev: false + array.prototype.tosorted@1.1.3: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-errors: 1.3.0 + es-shim-unscopables: 1.0.2 - /asynciterator.prototype@1.0.0: - resolution: {integrity: sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==} + arraybuffer.prototype.slice@1.0.3: dependencies: - has-symbols: 1.0.3 - dev: false + array-buffer-byte-length: 1.0.1 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + is-array-buffer: 3.0.4 + is-shared-array-buffer: 1.0.3 - /autoprefixer@10.4.17(postcss@8.4.33): - resolution: {integrity: sha512-/cpVNRLSfhOtcGflT13P2794gVSgmPgTR+erw5ifnMLZb0UnSlkK4tquLmkd3BhA+nLo5tX8Cu0upUsGKvKbmg==} - engines: {node: ^10 || ^12 || >=14} - hasBin: true - peerDependencies: - postcss: ^8.1.0 + ast-types-flow@0.0.8: {} + + autoprefixer@10.4.19(postcss@8.4.38): dependencies: - browserslist: 4.22.2 - caniuse-lite: 1.0.30001580 + browserslist: 4.23.0 + caniuse-lite: 1.0.30001614 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.0.0 - postcss: 8.4.33 + postcss: 8.4.38 postcss-value-parser: 4.2.0 - dev: true - /available-typed-arrays@1.0.5: - resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} - engines: {node: '>= 0.4'} - dev: false + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.0.0 - /axe-core@4.7.0: - resolution: {integrity: sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==} - engines: {node: '>=4'} - dev: false + axe-core@4.7.0: {} - /axobject-query@3.2.1: - resolution: {integrity: sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==} + axobject-query@3.2.1: dependencies: dequal: 2.0.3 - dev: false - /balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + balanced-match@1.0.2: {} - /binary-extensions@2.2.0: - resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} - engines: {node: '>=8'} + binary-extensions@2.3.0: {} - /brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - /brace-expansion@2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + brace-expansion@2.0.1: dependencies: balanced-match: 1.0.2 - /braces@3.0.2: - resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} - engines: {node: '>=8'} + braces@3.0.2: dependencies: fill-range: 7.0.1 - /browserslist@4.22.2: - resolution: {integrity: sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true + browserslist@4.23.0: dependencies: - caniuse-lite: 1.0.30001580 - electron-to-chromium: 1.4.645 + caniuse-lite: 1.0.30001614 + electron-to-chromium: 1.4.753 node-releases: 2.0.14 - update-browserslist-db: 1.0.13(browserslist@4.22.2) - dev: true + update-browserslist-db: 1.0.14(browserslist@4.23.0) - /bundle-require@4.0.2(esbuild@0.19.12): - resolution: {integrity: sha512-jwzPOChofl67PSTW2SGubV9HBQAhhR2i6nskiOThauo9dzwDUgOWQScFVaJkjEfYX+UXiD+LEx8EblQMc2wIag==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - peerDependencies: - esbuild: '>=0.17' + bundle-require@4.0.3(esbuild@0.19.12): dependencies: esbuild: 0.19.12 load-tsconfig: 0.2.5 - dev: true - /busboy@1.6.0: - resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} - engines: {node: '>=10.16.0'} + busboy@1.6.0: dependencies: streamsearch: 1.1.0 - dev: false - /cac@6.7.14: - resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} - engines: {node: '>=8'} - dev: true + cac@6.7.14: {} - /call-bind@1.0.5: - resolution: {integrity: sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==} + call-bind@1.0.7: dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 function-bind: 1.1.2 - get-intrinsic: 1.2.2 - set-function-length: 1.2.0 - dev: false + get-intrinsic: 1.2.4 + set-function-length: 1.2.2 - /callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} + callsites@3.1.0: {} - /camelcase-css@2.0.1: - resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} - engines: {node: '>= 6'} + camelcase-css@2.0.1: {} - /caniuse-lite@1.0.30001580: - resolution: {integrity: sha512-mtj5ur2FFPZcCEpXFy8ADXbDACuNFXg6mxVDqp7tqooX6l3zwm+d8EPoeOSIFRDvHs8qu7/SLFOGniULkcH2iA==} + caniuse-lite@1.0.30001614: {} - /chalk@2.4.2: - resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} - engines: {node: '>=4'} + chalk@2.4.2: dependencies: ansi-styles: 3.2.1 escape-string-regexp: 1.0.5 supports-color: 5.5.0 - dev: true - /chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - /character-entities-legacy@1.1.4: - resolution: {integrity: sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==} - dev: false + character-entities-legacy@1.1.4: {} - /character-entities@1.2.4: - resolution: {integrity: sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==} - dev: false + character-entities@1.2.4: {} - /character-reference-invalid@1.1.4: - resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==} - dev: false + character-reference-invalid@1.1.4: {} - /chokidar@3.5.3: - resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} - engines: {node: '>= 8.10.0'} + chokidar@3.6.0: dependencies: anymatch: 3.1.3 braces: 3.0.2 @@ -1669,53 +3667,33 @@ packages: optionalDependencies: fsevents: 2.3.3 - /client-only@0.0.1: - resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} - dev: false + client-only@0.0.1: {} - /cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} + cliui@8.0.1: dependencies: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - dev: true - /color-convert@1.9.3: - resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + color-convert@1.9.3: dependencies: color-name: 1.1.3 - dev: true - /color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} + color-convert@2.0.1: dependencies: color-name: 1.1.4 - /color-name@1.1.3: - resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} - dev: true + color-name@1.1.3: {} - /color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + color-name@1.1.4: {} - /comma-separated-tokens@1.0.8: - resolution: {integrity: sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==} - dev: false + comma-separated-tokens@1.0.8: {} - /commander@4.1.1: - resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} - engines: {node: '>= 6'} + commander@4.1.1: {} - /concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + concat-map@0.0.1: {} - /concurrently@8.2.2: - resolution: {integrity: sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==} - engines: {node: ^14.13.0 || >=16.0.0} - hasBin: true + concurrently@8.2.2: dependencies: chalk: 4.1.2 date-fns: 2.30.0 @@ -1726,238 +3704,191 @@ packages: supports-color: 8.1.1 tree-kill: 1.2.2 yargs: 17.7.2 - dev: true - /convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - dev: true + convert-source-map@2.0.0: {} - /copy-to-clipboard@3.3.3: - resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} + copy-to-clipboard@3.3.3: dependencies: toggle-selection: 1.0.6 - dev: false - /cross-spawn@7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} - engines: {node: '>= 8'} + cross-spawn@7.0.3: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 - /cssesc@3.0.0: - resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} - engines: {node: '>=4'} - hasBin: true + cssesc@3.0.0: {} - /csstype@3.1.3: - resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - dev: true + csstype@3.1.3: {} - /damerau-levenshtein@1.0.8: - resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} - dev: false + damerau-levenshtein@1.0.8: {} - /date-fns@2.30.0: - resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} - engines: {node: '>=0.11'} + data-view-buffer@1.0.1: dependencies: - '@babel/runtime': 7.23.9 - dev: true + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 - /debug@3.2.7: - resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true + data-view-byte-length@1.0.1: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 + + data-view-byte-offset@1.0.0: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 + + date-fns@2.30.0: + dependencies: + '@babel/runtime': 7.24.5 + + debug@3.2.7: dependencies: ms: 2.1.3 - dev: false - /debug@4.3.4: - resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true + debug@4.3.4: dependencies: ms: 2.1.2 - /deep-is@0.1.4: - resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + deep-is@0.1.4: {} - /define-data-property@1.1.1: - resolution: {integrity: sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==} - engines: {node: '>= 0.4'} + define-data-property@1.1.4: dependencies: - get-intrinsic: 1.2.2 + es-define-property: 1.0.0 + es-errors: 1.3.0 gopd: 1.0.1 - has-property-descriptors: 1.0.1 - dev: false - /define-properties@1.2.1: - resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} - engines: {node: '>= 0.4'} + define-properties@1.2.1: dependencies: - define-data-property: 1.1.1 - has-property-descriptors: 1.0.1 + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 object-keys: 1.1.1 - dev: false - /dequal@2.0.3: - resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} - engines: {node: '>=6'} - dev: false + dequal@2.0.3: {} - /didyoumean@1.2.2: - resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + didyoumean@1.2.2: {} - /dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} + dir-glob@3.0.1: dependencies: path-type: 4.0.0 - /dlv@1.1.3: - resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} - - /doctrine@2.1.0: - resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} - engines: {node: '>=0.10.0'} - dependencies: - esutils: 2.0.3 - dev: false + dlv@1.1.3: {} - /doctrine@3.0.0: - resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} - engines: {node: '>=6.0.0'} + doctrine@2.1.0: dependencies: esutils: 2.0.3 - /dotenv@16.0.3: - resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==} - engines: {node: '>=12'} - dev: false + dotenv@16.0.3: {} - /eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + eastasianwidth@0.2.0: {} - /electron-to-chromium@1.4.645: - resolution: {integrity: sha512-EeS1oQDCmnYsRDRy2zTeC336a/4LZ6WKqvSaM1jLocEk5ZuyszkQtCpsqvuvaIXGOUjwtvF6LTcS8WueibXvSw==} - dev: true + electron-to-chromium@1.4.753: {} - /emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + emoji-regex@8.0.0: {} - /emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + emoji-regex@9.2.2: {} - /enhanced-resolve@5.15.0: - resolution: {integrity: sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==} - engines: {node: '>=10.13.0'} + enhanced-resolve@5.16.0: dependencies: graceful-fs: 4.2.11 tapable: 2.2.1 - dev: false - /es-abstract@1.22.3: - resolution: {integrity: sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==} - engines: {node: '>= 0.4'} - dependencies: - array-buffer-byte-length: 1.0.0 - arraybuffer.prototype.slice: 1.0.2 - available-typed-arrays: 1.0.5 - call-bind: 1.0.5 - es-set-tostringtag: 2.0.2 + es-abstract@1.23.3: + dependencies: + array-buffer-byte-length: 1.0.1 + arraybuffer.prototype.slice: 1.0.3 + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + data-view-buffer: 1.0.1 + data-view-byte-length: 1.0.1 + data-view-byte-offset: 1.0.0 + es-define-property: 1.0.0 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + es-set-tostringtag: 2.0.3 es-to-primitive: 1.2.1 function.prototype.name: 1.1.6 - get-intrinsic: 1.2.2 - get-symbol-description: 1.0.0 - globalthis: 1.0.3 + get-intrinsic: 1.2.4 + get-symbol-description: 1.0.2 + globalthis: 1.0.4 gopd: 1.0.1 - has-property-descriptors: 1.0.1 - has-proto: 1.0.1 + has-property-descriptors: 1.0.2 + has-proto: 1.0.3 has-symbols: 1.0.3 - hasown: 2.0.0 - internal-slot: 1.0.6 - is-array-buffer: 3.0.2 + hasown: 2.0.2 + internal-slot: 1.0.7 + is-array-buffer: 3.0.4 is-callable: 1.2.7 - is-negative-zero: 2.0.2 + is-data-view: 1.0.1 + is-negative-zero: 2.0.3 is-regex: 1.1.4 - is-shared-array-buffer: 1.0.2 + is-shared-array-buffer: 1.0.3 is-string: 1.0.7 - is-typed-array: 1.1.12 + is-typed-array: 1.1.13 is-weakref: 1.0.2 object-inspect: 1.13.1 object-keys: 1.1.1 object.assign: 4.1.5 - regexp.prototype.flags: 1.5.1 - safe-array-concat: 1.1.0 - safe-regex-test: 1.0.2 - string.prototype.trim: 1.2.8 - string.prototype.trimend: 1.0.7 - string.prototype.trimstart: 1.0.7 - typed-array-buffer: 1.0.0 - typed-array-byte-length: 1.0.0 - typed-array-byte-offset: 1.0.0 - typed-array-length: 1.0.4 + regexp.prototype.flags: 1.5.2 + safe-array-concat: 1.1.2 + safe-regex-test: 1.0.3 + string.prototype.trim: 1.2.9 + string.prototype.trimend: 1.0.8 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.2 + typed-array-byte-length: 1.0.1 + typed-array-byte-offset: 1.0.2 + typed-array-length: 1.0.6 unbox-primitive: 1.0.2 - which-typed-array: 1.1.13 - dev: false + which-typed-array: 1.1.15 + + es-define-property@1.0.0: + dependencies: + get-intrinsic: 1.2.4 + + es-errors@1.3.0: {} - /es-iterator-helpers@1.0.15: - resolution: {integrity: sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==} + es-iterator-helpers@1.0.19: dependencies: - asynciterator.prototype: 1.0.0 - call-bind: 1.0.5 + call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.3 - es-set-tostringtag: 2.0.2 + es-abstract: 1.23.3 + es-errors: 1.3.0 + es-set-tostringtag: 2.0.3 function-bind: 1.1.2 - get-intrinsic: 1.2.2 - globalthis: 1.0.3 - has-property-descriptors: 1.0.1 - has-proto: 1.0.1 + get-intrinsic: 1.2.4 + globalthis: 1.0.4 + has-property-descriptors: 1.0.2 + has-proto: 1.0.3 has-symbols: 1.0.3 - internal-slot: 1.0.6 + internal-slot: 1.0.7 iterator.prototype: 1.1.2 - safe-array-concat: 1.1.0 - dev: false + safe-array-concat: 1.1.2 - /es-set-tostringtag@2.0.2: - resolution: {integrity: sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==} - engines: {node: '>= 0.4'} + es-object-atoms@1.0.0: dependencies: - get-intrinsic: 1.2.2 - has-tostringtag: 1.0.0 - hasown: 2.0.0 - dev: false + es-errors: 1.3.0 - /es-shim-unscopables@1.0.2: - resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} + es-set-tostringtag@2.0.3: dependencies: - hasown: 2.0.0 - dev: false + get-intrinsic: 1.2.4 + has-tostringtag: 1.0.2 + hasown: 2.0.2 - /es-to-primitive@1.2.1: - resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} - engines: {node: '>= 0.4'} + es-shim-unscopables@1.0.2: + dependencies: + hasown: 2.0.2 + + es-to-primitive@1.2.1: dependencies: is-callable: 1.2.7 is-date-object: 1.0.5 is-symbol: 1.0.4 - dev: false - /esbuild@0.19.12: - resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} - engines: {node: '>=12'} - hasBin: true - requiresBuild: true + esbuild@0.19.12: optionalDependencies: '@esbuild/aix-ppc64': 0.19.12 '@esbuild/android-arm': 0.19.12 @@ -1982,89 +3913,83 @@ packages: '@esbuild/win32-arm64': 0.19.12 '@esbuild/win32-ia32': 0.19.12 '@esbuild/win32-x64': 0.19.12 - dev: true - - /escalade@3.1.1: - resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} - engines: {node: '>=6'} - dev: true - - /escape-string-regexp@1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} - dev: true - - /escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - /eslint-config-next@14.1.0(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-SBX2ed7DoRFXC6CQSLc/SbLY9Ut6HxNB2wPTcoIWjUMd7aF7O/SIE7111L8FdZ9TXsNV4pulUDnfthpyPtbFUg==} - peerDependencies: - eslint: ^7.23.0 || ^8.0.0 - typescript: '>=3.3.1' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@next/eslint-plugin-next': 14.1.0 - '@rushstack/eslint-patch': 1.7.1 - '@typescript-eslint/parser': 6.19.1(eslint@8.56.0)(typescript@5.3.3) - eslint: 8.56.0 + esbuild@0.20.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.20.2 + '@esbuild/android-arm': 0.20.2 + '@esbuild/android-arm64': 0.20.2 + '@esbuild/android-x64': 0.20.2 + '@esbuild/darwin-arm64': 0.20.2 + '@esbuild/darwin-x64': 0.20.2 + '@esbuild/freebsd-arm64': 0.20.2 + '@esbuild/freebsd-x64': 0.20.2 + '@esbuild/linux-arm': 0.20.2 + '@esbuild/linux-arm64': 0.20.2 + '@esbuild/linux-ia32': 0.20.2 + '@esbuild/linux-loong64': 0.20.2 + '@esbuild/linux-mips64el': 0.20.2 + '@esbuild/linux-ppc64': 0.20.2 + '@esbuild/linux-riscv64': 0.20.2 + '@esbuild/linux-s390x': 0.20.2 + '@esbuild/linux-x64': 0.20.2 + '@esbuild/netbsd-x64': 0.20.2 + '@esbuild/openbsd-x64': 0.20.2 + '@esbuild/sunos-x64': 0.20.2 + '@esbuild/win32-arm64': 0.20.2 + '@esbuild/win32-ia32': 0.20.2 + '@esbuild/win32-x64': 0.20.2 + + escalade@3.1.2: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@4.0.0: {} + + eslint-config-next@14.2.3(eslint@9.1.1)(typescript@5.4.5): + dependencies: + '@next/eslint-plugin-next': 14.2.3 + '@rushstack/eslint-patch': 1.10.2 + '@typescript-eslint/parser': 7.2.0(eslint@9.1.1)(typescript@5.4.5) + eslint: 9.1.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.19.1)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.56.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.19.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) - eslint-plugin-jsx-a11y: 6.8.0(eslint@8.56.0) - eslint-plugin-react: 7.33.2(eslint@8.56.0) - eslint-plugin-react-hooks: 4.6.0(eslint@8.56.0) - typescript: 5.3.3 + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.1.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.1.1) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@9.1.1)(typescript@5.4.5))(eslint-import-resolver-typescript@3.6.1)(eslint@9.1.1) + eslint-plugin-jsx-a11y: 6.8.0(eslint@9.1.1) + eslint-plugin-react: 7.34.1(eslint@9.1.1) + eslint-plugin-react-hooks: 4.6.2(eslint@9.1.1) + optionalDependencies: + typescript: 5.4.5 transitivePeerDependencies: - eslint-import-resolver-webpack - supports-color - dev: false - /eslint-config-prettier@9.1.0(eslint@8.56.0): - resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} - hasBin: true - peerDependencies: - eslint: '>=7.0.0' + eslint-config-prettier@9.1.0(eslint@9.1.1): dependencies: - eslint: 8.56.0 - dev: false + eslint: 9.1.1 - /eslint-config-turbo@1.11.3(eslint@8.56.0): - resolution: {integrity: sha512-v7CHpAHodBKlj+r+R3B2DJlZbCjpZLnK7gO/vCRk/Lc+tlD/f04wM6rmHlerevOlchtmwARilRLBnmzNLffTyQ==} - peerDependencies: - eslint: '>6.6.0' + eslint-config-turbo@1.13.3(eslint@9.1.1): dependencies: - eslint: 8.56.0 - eslint-plugin-turbo: 1.11.3(eslint@8.56.0) - dev: false + eslint: 9.1.1 + eslint-plugin-turbo: 1.13.3(eslint@9.1.1) - /eslint-import-resolver-node@0.3.9: - resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + eslint-import-resolver-node@0.3.9: dependencies: debug: 3.2.7 is-core-module: 2.13.1 resolve: 1.22.8 transitivePeerDependencies: - supports-color - dev: false - /eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.19.1)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.56.0): - resolution: {integrity: sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - eslint: '*' - eslint-plugin-import: '*' + eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.1.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.1.1): dependencies: debug: 4.3.4 - enhanced-resolve: 5.15.0 - eslint: 8.56.0 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.19.1)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.19.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) + enhanced-resolve: 5.16.0 + eslint: 9.1.1 + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@9.1.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.1.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.1.1))(eslint@9.1.1) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@9.1.1)(typescript@5.4.5))(eslint-import-resolver-typescript@3.6.1)(eslint@9.1.1) fast-glob: 3.3.2 - get-tsconfig: 4.7.2 + get-tsconfig: 4.7.3 is-core-module: 2.13.1 is-glob: 4.0.3 transitivePeerDependencies: @@ -2072,236 +3997,167 @@ packages: - eslint-import-resolver-node - eslint-import-resolver-webpack - supports-color - dev: false - /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.19.1)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0): - resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: '*' - eslint-import-resolver-node: '*' - eslint-import-resolver-typescript: '*' - eslint-import-resolver-webpack: '*' - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - eslint: - optional: true - eslint-import-resolver-node: - optional: true - eslint-import-resolver-typescript: - optional: true - eslint-import-resolver-webpack: - optional: true + eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@9.1.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.1.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.1.1))(eslint@9.1.1): dependencies: - '@typescript-eslint/parser': 6.19.1(eslint@8.56.0)(typescript@5.3.3) debug: 3.2.7 - eslint: 8.56.0 + optionalDependencies: + '@typescript-eslint/parser': 7.2.0(eslint@9.1.1)(typescript@5.4.5) + eslint: 9.1.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.19.1)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.56.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.1.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.1.1) transitivePeerDependencies: - supports-color - dev: false - /eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.19.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0): - resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true + eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@9.1.1)(typescript@5.4.5))(eslint-import-resolver-typescript@3.6.1)(eslint@9.1.1): dependencies: - '@typescript-eslint/parser': 6.19.1(eslint@8.56.0)(typescript@5.3.3) - array-includes: 3.1.7 - array.prototype.findlastindex: 1.2.3 + array-includes: 3.1.8 + array.prototype.findlastindex: 1.2.5 array.prototype.flat: 1.3.2 array.prototype.flatmap: 1.3.2 debug: 3.2.7 doctrine: 2.1.0 - eslint: 8.56.0 + eslint: 9.1.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.19.1)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) - hasown: 2.0.0 + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@9.1.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.1.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.1.1))(eslint@9.1.1) + hasown: 2.0.2 is-core-module: 2.13.1 is-glob: 4.0.3 minimatch: 3.1.2 - object.fromentries: 2.0.7 - object.groupby: 1.0.1 - object.values: 1.1.7 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.0 semver: 6.3.1 tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 7.2.0(eslint@9.1.1)(typescript@5.4.5) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - dev: false - /eslint-plugin-jsx-a11y@6.8.0(eslint@8.56.0): - resolution: {integrity: sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA==} - engines: {node: '>=4.0'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + eslint-plugin-jsx-a11y@6.8.0(eslint@9.1.1): dependencies: - '@babel/runtime': 7.23.9 + '@babel/runtime': 7.24.5 aria-query: 5.3.0 - array-includes: 3.1.7 + array-includes: 3.1.8 array.prototype.flatmap: 1.3.2 ast-types-flow: 0.0.8 axe-core: 4.7.0 axobject-query: 3.2.1 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 - es-iterator-helpers: 1.0.15 - eslint: 8.56.0 - hasown: 2.0.0 + es-iterator-helpers: 1.0.19 + eslint: 9.1.1 + hasown: 2.0.2 jsx-ast-utils: 3.3.5 language-tags: 1.0.9 minimatch: 3.1.2 - object.entries: 1.1.7 - object.fromentries: 2.0.7 - dev: false + object.entries: 1.1.8 + object.fromentries: 2.0.8 - /eslint-plugin-react-hooks@4.6.0(eslint@8.56.0): - resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} - engines: {node: '>=10'} - peerDependencies: - eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + eslint-plugin-react-hooks@4.6.2(eslint@9.1.1): dependencies: - eslint: 8.56.0 + eslint: 9.1.1 - /eslint-plugin-react-refresh@0.4.5(eslint@8.56.0): - resolution: {integrity: sha512-D53FYKJa+fDmZMtriODxvhwrO+IOqrxoEo21gMA0sjHdU6dPVH4OhyFip9ypl8HOF5RV5KdTo+rBQLvnY2cO8w==} - peerDependencies: - eslint: '>=7' + eslint-plugin-react-refresh@0.4.6(eslint@9.1.1): dependencies: - eslint: 8.56.0 - dev: true + eslint: 9.1.1 - /eslint-plugin-react@7.33.2(eslint@8.56.0): - resolution: {integrity: sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==} - engines: {node: '>=4'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + eslint-plugin-react@7.34.1(eslint@9.1.1): dependencies: - array-includes: 3.1.7 + array-includes: 3.1.8 + array.prototype.findlast: 1.2.5 array.prototype.flatmap: 1.3.2 - array.prototype.tosorted: 1.1.2 + array.prototype.toreversed: 1.1.2 + array.prototype.tosorted: 1.1.3 doctrine: 2.1.0 - es-iterator-helpers: 1.0.15 - eslint: 8.56.0 + es-iterator-helpers: 1.0.19 + eslint: 9.1.1 estraverse: 5.3.0 jsx-ast-utils: 3.3.5 minimatch: 3.1.2 - object.entries: 1.1.7 - object.fromentries: 2.0.7 - object.hasown: 1.1.3 - object.values: 1.1.7 + object.entries: 1.1.8 + object.fromentries: 2.0.8 + object.hasown: 1.1.4 + object.values: 1.2.0 prop-types: 15.8.1 resolve: 2.0.0-next.5 semver: 6.3.1 - string.prototype.matchall: 4.0.10 - dev: false + string.prototype.matchall: 4.0.11 - /eslint-plugin-turbo@1.11.3(eslint@8.56.0): - resolution: {integrity: sha512-R5ftTTWQzEYaKzF5g6m/MInCU8pIN+2TLL+S50AYBr1enwUovdZmnZ1HDwFMaxIjJ8x5ah+jvAzql5IJE9VWaA==} - peerDependencies: - eslint: '>6.6.0' + eslint-plugin-turbo@1.13.3(eslint@9.1.1): dependencies: dotenv: 16.0.3 - eslint: 8.56.0 - dev: false + eslint: 9.1.1 - /eslint-scope@7.2.2: - resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-scope@8.0.1: dependencies: esrecurse: 4.3.0 estraverse: 5.3.0 - /eslint-visitor-keys@3.4.3: - resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-visitor-keys@3.4.3: {} - /eslint@8.56.0: - resolution: {integrity: sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - hasBin: true + eslint-visitor-keys@4.0.0: {} + + eslint@9.1.1: dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.1.1) '@eslint-community/regexpp': 4.10.0 - '@eslint/eslintrc': 2.1.4 - '@eslint/js': 8.56.0 - '@humanwhocodes/config-array': 0.11.14 + '@eslint/eslintrc': 3.0.2 + '@eslint/js': 9.1.1 + '@humanwhocodes/config-array': 0.13.0 '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.2.3 '@nodelib/fs.walk': 1.2.8 - '@ungap/structured-clone': 1.2.0 ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 debug: 4.3.4 - doctrine: 3.0.0 escape-string-regexp: 4.0.0 - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 + eslint-scope: 8.0.1 + eslint-visitor-keys: 4.0.0 + espree: 10.0.1 esquery: 1.5.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 + file-entry-cache: 8.0.0 find-up: 5.0.0 glob-parent: 6.0.2 - globals: 13.24.0 - graphemer: 1.4.0 - ignore: 5.3.0 + ignore: 5.3.1 imurmurhash: 0.1.4 is-glob: 4.0.3 is-path-inside: 3.0.3 - js-yaml: 4.1.0 json-stable-stringify-without-jsonify: 1.0.1 levn: 0.4.1 lodash.merge: 4.6.2 minimatch: 3.1.2 natural-compare: 1.4.0 - optionator: 0.9.3 + optionator: 0.9.4 strip-ansi: 6.0.1 text-table: 0.2.0 transitivePeerDependencies: - supports-color - /espree@9.6.1: - resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + espree@10.0.1: dependencies: acorn: 8.11.3 acorn-jsx: 5.3.2(acorn@8.11.3) - eslint-visitor-keys: 3.4.3 + eslint-visitor-keys: 4.0.0 - /esquery@1.5.0: - resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} - engines: {node: '>=0.10'} + esquery@1.5.0: dependencies: estraverse: 5.3.0 - /esrecurse@4.3.0: - resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} - engines: {node: '>=4.0'} + esrecurse@4.3.0: dependencies: estraverse: 5.3.0 - /estraverse@5.3.0: - resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} - engines: {node: '>=4.0'} + estraverse@5.3.0: {} - /esutils@2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} + esutils@2.0.3: {} - /execa@5.1.1: - resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} - engines: {node: '>=10'} + execa@5.1.1: dependencies: cross-spawn: 7.0.3 get-stream: 6.0.1 @@ -2312,14 +4168,10 @@ packages: onetime: 5.1.2 signal-exit: 3.0.7 strip-final-newline: 2.0.0 - dev: true - /fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-deep-equal@3.1.3: {} - /fast-glob@3.3.2: - resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} - engines: {node: '>=8.6.0'} + fast-glob@3.3.2: dependencies: '@nodelib/fs.stat': 2.0.5 '@nodelib/fs.walk': 1.2.8 @@ -2327,896 +4179,550 @@ packages: merge2: 1.4.1 micromatch: 4.0.5 - /fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + fast-json-stable-stringify@2.1.0: {} - /fast-levenshtein@2.0.6: - resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-levenshtein@2.0.6: {} - /fastq@1.16.0: - resolution: {integrity: sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==} + fastq@1.17.1: dependencies: reusify: 1.0.4 - /fault@1.0.4: - resolution: {integrity: sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==} + fault@1.0.4: dependencies: format: 0.2.2 - dev: false - /file-entry-cache@6.0.1: - resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} - engines: {node: ^10.12.0 || >=12.0.0} + file-entry-cache@8.0.0: dependencies: - flat-cache: 3.2.0 + flat-cache: 4.0.1 - /fill-range@7.0.1: - resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} - engines: {node: '>=8'} + fill-range@7.0.1: dependencies: to-regex-range: 5.0.1 - /find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} + find-up@5.0.0: dependencies: locate-path: 6.0.0 path-exists: 4.0.0 - /flat-cache@3.2.0: - resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} - engines: {node: ^10.12.0 || >=12.0.0} + flat-cache@4.0.1: dependencies: - flatted: 3.2.9 + flatted: 3.3.1 keyv: 4.5.4 - rimraf: 3.0.2 - /flatted@3.2.9: - resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==} + flatted@3.3.1: {} - /for-each@0.3.3: - resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + for-each@0.3.3: dependencies: is-callable: 1.2.7 - dev: false - /foreground-child@3.1.1: - resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} - engines: {node: '>=14'} + foreground-child@3.1.1: dependencies: cross-spawn: 7.0.3 signal-exit: 4.1.0 - /format@0.2.2: - resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} - engines: {node: '>=0.4.x'} - dev: false - - /fraction.js@4.3.7: - resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} - dev: true + format@0.2.2: {} - /fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + fraction.js@4.3.7: {} - /fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - requiresBuild: true + fsevents@2.3.3: optional: true - /function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + function-bind@1.1.2: {} - /function.prototype.name@1.1.6: - resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} - engines: {node: '>= 0.4'} + function.prototype.name@1.1.6: dependencies: - call-bind: 1.0.5 + call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.3 + es-abstract: 1.23.3 functions-have-names: 1.2.3 - dev: false - /functions-have-names@1.2.3: - resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} - dev: false + functions-have-names@1.2.3: {} - /gensync@1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} - engines: {node: '>=6.9.0'} - dev: true + gensync@1.0.0-beta.2: {} - /get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - dev: true + get-caller-file@2.0.5: {} - /get-intrinsic@1.2.2: - resolution: {integrity: sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==} + get-intrinsic@1.2.4: dependencies: + es-errors: 1.3.0 function-bind: 1.1.2 - has-proto: 1.0.1 + has-proto: 1.0.3 has-symbols: 1.0.3 - hasown: 2.0.0 - dev: false + hasown: 2.0.2 - /get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} - dev: true + get-stream@6.0.1: {} - /get-symbol-description@1.0.0: - resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} - engines: {node: '>= 0.4'} + get-symbol-description@1.0.2: dependencies: - call-bind: 1.0.5 - get-intrinsic: 1.2.2 - dev: false + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 - /get-tsconfig@4.7.2: - resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==} + get-tsconfig@4.7.3: dependencies: resolve-pkg-maps: 1.0.0 - dev: false - /glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 - /glob-parent@6.0.2: - resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} - engines: {node: '>=10.13.0'} + glob-parent@6.0.2: dependencies: is-glob: 4.0.3 - /glob@10.3.10: - resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==} - engines: {node: '>=16 || 14 >=14.17'} - hasBin: true + glob@10.3.10: dependencies: foreground-child: 3.1.1 jackspeak: 2.3.6 - minimatch: 9.0.3 + minimatch: 9.0.4 minipass: 7.0.4 - path-scurry: 1.10.1 + path-scurry: 1.10.2 - /glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + glob@10.3.12: dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 + foreground-child: 3.1.1 + jackspeak: 2.3.6 + minimatch: 9.0.4 + minipass: 7.0.4 + path-scurry: 1.10.2 - /globals@11.12.0: - resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} - engines: {node: '>=4'} - dev: true + globals@11.12.0: {} - /globals@13.24.0: - resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} - engines: {node: '>=8'} - dependencies: - type-fest: 0.20.2 + globals@14.0.0: {} - /globalthis@1.0.3: - resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} - engines: {node: '>= 0.4'} + globalthis@1.0.4: dependencies: define-properties: 1.2.1 - dev: false + gopd: 1.0.1 - /globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} + globby@11.1.0: dependencies: array-union: 2.1.0 dir-glob: 3.0.1 fast-glob: 3.3.2 - ignore: 5.3.0 + ignore: 5.3.1 merge2: 1.4.1 slash: 3.0.0 - /gopd@1.0.1: - resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + gopd@1.0.1: dependencies: - get-intrinsic: 1.2.2 - dev: false + get-intrinsic: 1.2.4 - /graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - dev: false + graceful-fs@4.2.11: {} - /graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + graphemer@1.4.0: {} - /has-bigints@1.0.2: - resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} - dev: false + has-bigints@1.0.2: {} - /has-flag@3.0.0: - resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} - engines: {node: '>=4'} - dev: true + has-flag@3.0.0: {} - /has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} + has-flag@4.0.0: {} - /has-property-descriptors@1.0.1: - resolution: {integrity: sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==} + has-property-descriptors@1.0.2: dependencies: - get-intrinsic: 1.2.2 - dev: false + es-define-property: 1.0.0 - /has-proto@1.0.1: - resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} - engines: {node: '>= 0.4'} - dev: false + has-proto@1.0.3: {} - /has-symbols@1.0.3: - resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} - engines: {node: '>= 0.4'} - dev: false + has-symbols@1.0.3: {} - /has-tostringtag@1.0.0: - resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} - engines: {node: '>= 0.4'} + has-tostringtag@1.0.2: dependencies: has-symbols: 1.0.3 - dev: false - /hasown@2.0.0: - resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==} - engines: {node: '>= 0.4'} + hasown@2.0.2: dependencies: function-bind: 1.1.2 - /hast-util-parse-selector@2.2.5: - resolution: {integrity: sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==} - dev: false + hast-util-parse-selector@2.2.5: {} - /hastscript@6.0.0: - resolution: {integrity: sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==} + hastscript@6.0.0: dependencies: - '@types/hast': 2.3.9 + '@types/hast': 2.3.10 comma-separated-tokens: 1.0.8 hast-util-parse-selector: 2.2.5 property-information: 5.6.0 space-separated-tokens: 1.1.5 - dev: false - /highlight.js@10.7.3: - resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} - dev: false + highlight.js@10.7.3: {} - /human-signals@2.1.0: - resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} - engines: {node: '>=10.17.0'} - dev: true + human-signals@2.1.0: {} - /ignore@5.3.0: - resolution: {integrity: sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==} - engines: {node: '>= 4'} + ignore@5.3.1: {} - /import-fresh@3.3.0: - resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} - engines: {node: '>=6'} + import-fresh@3.3.0: dependencies: parent-module: 1.0.1 resolve-from: 4.0.0 - /imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - - /inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - - /inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + imurmurhash@0.1.4: {} - /internal-slot@1.0.6: - resolution: {integrity: sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==} - engines: {node: '>= 0.4'} + internal-slot@1.0.7: dependencies: - get-intrinsic: 1.2.2 - hasown: 2.0.0 - side-channel: 1.0.4 - dev: false + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.0.6 - /is-alphabetical@1.0.4: - resolution: {integrity: sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==} - dev: false + is-alphabetical@1.0.4: {} - /is-alphanumerical@1.0.4: - resolution: {integrity: sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==} + is-alphanumerical@1.0.4: dependencies: is-alphabetical: 1.0.4 is-decimal: 1.0.4 - dev: false - /is-array-buffer@3.0.2: - resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} + is-array-buffer@3.0.4: dependencies: - call-bind: 1.0.5 - get-intrinsic: 1.2.2 - is-typed-array: 1.1.12 - dev: false + call-bind: 1.0.7 + get-intrinsic: 1.2.4 - /is-async-function@2.0.0: - resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} - engines: {node: '>= 0.4'} + is-async-function@2.0.0: dependencies: - has-tostringtag: 1.0.0 - dev: false + has-tostringtag: 1.0.2 - /is-bigint@1.0.4: - resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + is-bigint@1.0.4: dependencies: has-bigints: 1.0.2 - dev: false - /is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} + is-binary-path@2.1.0: dependencies: - binary-extensions: 2.2.0 + binary-extensions: 2.3.0 - /is-boolean-object@1.1.2: - resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} - engines: {node: '>= 0.4'} + is-boolean-object@1.1.2: dependencies: - call-bind: 1.0.5 - has-tostringtag: 1.0.0 - dev: false + call-bind: 1.0.7 + has-tostringtag: 1.0.2 - /is-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} - dev: false + is-callable@1.2.7: {} - /is-core-module@2.13.1: - resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} + is-core-module@2.13.1: dependencies: - hasown: 2.0.0 + hasown: 2.0.2 - /is-date-object@1.0.5: - resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} - engines: {node: '>= 0.4'} + is-data-view@1.0.1: dependencies: - has-tostringtag: 1.0.0 - dev: false + is-typed-array: 1.1.13 - /is-decimal@1.0.4: - resolution: {integrity: sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==} - dev: false + is-date-object@1.0.5: + dependencies: + has-tostringtag: 1.0.2 - /is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} + is-decimal@1.0.4: {} - /is-finalizationregistry@1.0.2: - resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==} + is-extglob@2.1.1: {} + + is-finalizationregistry@1.0.2: dependencies: - call-bind: 1.0.5 - dev: false + call-bind: 1.0.7 - /is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} + is-fullwidth-code-point@3.0.0: {} - /is-generator-function@1.0.10: - resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} - engines: {node: '>= 0.4'} + is-generator-function@1.0.10: dependencies: - has-tostringtag: 1.0.0 - dev: false + has-tostringtag: 1.0.2 - /is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} + is-glob@4.0.3: dependencies: is-extglob: 2.1.1 - /is-hexadecimal@1.0.4: - resolution: {integrity: sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==} - dev: false + is-hexadecimal@1.0.4: {} - /is-map@2.0.2: - resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==} - dev: false + is-map@2.0.3: {} - /is-negative-zero@2.0.2: - resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} - engines: {node: '>= 0.4'} - dev: false + is-negative-zero@2.0.3: {} - /is-number-object@1.0.7: - resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} - engines: {node: '>= 0.4'} + is-number-object@1.0.7: dependencies: - has-tostringtag: 1.0.0 - dev: false + has-tostringtag: 1.0.2 - /is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} + is-number@7.0.0: {} - /is-path-inside@3.0.3: - resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} - engines: {node: '>=8'} + is-path-inside@3.0.3: {} - /is-regex@1.1.4: - resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} - engines: {node: '>= 0.4'} + is-regex@1.1.4: dependencies: - call-bind: 1.0.5 - has-tostringtag: 1.0.0 - dev: false + call-bind: 1.0.7 + has-tostringtag: 1.0.2 - /is-set@2.0.2: - resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==} - dev: false + is-set@2.0.3: {} - /is-shared-array-buffer@1.0.2: - resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} + is-shared-array-buffer@1.0.3: dependencies: - call-bind: 1.0.5 - dev: false + call-bind: 1.0.7 - /is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - dev: true + is-stream@2.0.1: {} - /is-string@1.0.7: - resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} - engines: {node: '>= 0.4'} + is-string@1.0.7: dependencies: - has-tostringtag: 1.0.0 - dev: false + has-tostringtag: 1.0.2 - /is-symbol@1.0.4: - resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} - engines: {node: '>= 0.4'} + is-symbol@1.0.4: dependencies: has-symbols: 1.0.3 - dev: false - /is-typed-array@1.1.12: - resolution: {integrity: sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==} - engines: {node: '>= 0.4'} + is-typed-array@1.1.13: dependencies: - which-typed-array: 1.1.13 - dev: false + which-typed-array: 1.1.15 - /is-weakmap@2.0.1: - resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==} - dev: false + is-weakmap@2.0.2: {} - /is-weakref@1.0.2: - resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + is-weakref@1.0.2: dependencies: - call-bind: 1.0.5 - dev: false + call-bind: 1.0.7 - /is-weakset@2.0.2: - resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==} + is-weakset@2.0.3: dependencies: - call-bind: 1.0.5 - get-intrinsic: 1.2.2 - dev: false + call-bind: 1.0.7 + get-intrinsic: 1.2.4 - /isarray@2.0.5: - resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - dev: false + isarray@2.0.5: {} - /isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isexe@2.0.0: {} - /iterator.prototype@1.1.2: - resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} + iterator.prototype@1.1.2: dependencies: define-properties: 1.2.1 - get-intrinsic: 1.2.2 + get-intrinsic: 1.2.4 has-symbols: 1.0.3 - reflect.getprototypeof: 1.0.4 - set-function-name: 2.0.1 - dev: false + reflect.getprototypeof: 1.0.6 + set-function-name: 2.0.2 - /jackspeak@2.3.6: - resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} - engines: {node: '>=14'} + jackspeak@2.3.6: dependencies: '@isaacs/cliui': 8.0.2 optionalDependencies: '@pkgjs/parseargs': 0.11.0 - /jiti@1.21.0: - resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==} - hasBin: true + jiti@1.21.0: {} - /joycon@3.1.1: - resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} - engines: {node: '>=10'} - dev: true + joycon@3.1.1: {} - /js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + js-tokens@4.0.0: {} - /js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} - hasBin: true + js-yaml@4.1.0: dependencies: argparse: 2.0.1 - /jsesc@2.5.2: - resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} - engines: {node: '>=4'} - hasBin: true - dev: true + jsesc@2.5.2: {} - /json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + json-buffer@3.0.1: {} - /json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-schema-traverse@0.4.1: {} - /json-stable-stringify-without-jsonify@1.0.1: - resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + json-stable-stringify-without-jsonify@1.0.1: {} - /json5@1.0.2: - resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} - hasBin: true + json5@1.0.2: dependencies: minimist: 1.2.8 - dev: false - /json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - dev: true + json5@2.2.3: {} - /jsx-ast-utils@3.3.5: - resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} - engines: {node: '>=4.0'} + jsx-ast-utils@3.3.5: dependencies: - array-includes: 3.1.7 + array-includes: 3.1.8 array.prototype.flat: 1.3.2 object.assign: 4.1.5 - object.values: 1.1.7 - dev: false + object.values: 1.2.0 - /keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + keyv@4.5.4: dependencies: json-buffer: 3.0.1 - /language-subtag-registry@0.3.22: - resolution: {integrity: sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==} - dev: false + language-subtag-registry@0.3.22: {} - /language-tags@1.0.9: - resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} - engines: {node: '>=0.10'} + language-tags@1.0.9: dependencies: language-subtag-registry: 0.3.22 - dev: false - /levn@0.4.1: - resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} - engines: {node: '>= 0.8.0'} + levn@0.4.1: dependencies: prelude-ls: 1.2.1 type-check: 0.4.0 - /lilconfig@2.1.0: - resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} - engines: {node: '>=10'} + lilconfig@2.1.0: {} - /lilconfig@3.0.0: - resolution: {integrity: sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==} - engines: {node: '>=14'} + lilconfig@3.1.1: {} - /lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + lines-and-columns@1.2.4: {} - /load-tsconfig@0.2.5: - resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dev: true + load-tsconfig@0.2.5: {} - /locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} + locate-path@6.0.0: dependencies: p-locate: 5.0.0 - /lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + lodash.merge@4.6.2: {} - /lodash.sortby@4.7.0: - resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} - dev: true + lodash.sortby@4.7.0: {} - /lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - dev: true + lodash@4.17.21: {} - /loose-envify@1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} - hasBin: true + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 - dev: false - /lowlight@1.20.0: - resolution: {integrity: sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==} + lowlight@1.20.0: dependencies: fault: 1.0.4 highlight.js: 10.7.3 - dev: false - /lru-cache@10.1.0: - resolution: {integrity: sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==} - engines: {node: 14 || >=16.14} + lru-cache@10.2.2: {} - /lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 - dev: true - /lru-cache@6.0.0: - resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} - engines: {node: '>=10'} + lru-cache@6.0.0: dependencies: yallist: 4.0.0 - /merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - dev: true + merge-stream@2.0.0: {} - /merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} + merge2@1.4.1: {} - /micromatch@4.0.5: - resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} - engines: {node: '>=8.6'} + micromatch@4.0.5: dependencies: braces: 3.0.2 picomatch: 2.3.1 - /mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - dev: true + mimic-fn@2.1.0: {} - /minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 - /minimatch@9.0.3: - resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} - engines: {node: '>=16 || 14 >=14.17'} + minimatch@9.0.3: dependencies: brace-expansion: 2.0.1 - /minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - dev: false + minimatch@9.0.4: + dependencies: + brace-expansion: 2.0.1 - /minipass@7.0.4: - resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==} - engines: {node: '>=16 || 14 >=14.17'} + minimist@1.2.8: {} - /ms@2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + minipass@7.0.4: {} - /ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - dev: false + ms@2.1.2: {} - /mz@2.7.0: - resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + ms@2.1.3: {} + + mz@2.7.0: dependencies: any-promise: 1.3.0 object-assign: 4.1.1 thenify-all: 1.6.0 - /nanoid@3.3.7: - resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true + nanoid@3.3.7: {} - /natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + natural-compare@1.4.0: {} - /next@14.1.0(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-wlzrsbfeSU48YQBjZhDzOwhWhGsy+uQycR8bHAOt1LY1bn3zZEcDyHQOEoN3aWzQ8LHCAJ1nqrWCc9XF2+O45Q==} - engines: {node: '>=18.17.0'} - hasBin: true - peerDependencies: - '@opentelemetry/api': ^1.1.0 - react: ^18.2.0 - react-dom: ^18.2.0 - sass: ^1.3.0 - peerDependenciesMeta: - '@opentelemetry/api': - optional: true - sass: - optional: true + next@14.2.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@next/env': 14.1.0 - '@swc/helpers': 0.5.2 + '@next/env': 14.2.3 + '@swc/helpers': 0.5.5 busboy: 1.6.0 - caniuse-lite: 1.0.30001580 + caniuse-lite: 1.0.30001614 graceful-fs: 4.2.11 postcss: 8.4.31 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - styled-jsx: 5.1.1(react@18.2.0) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + styled-jsx: 5.1.1(react@18.3.1) optionalDependencies: - '@next/swc-darwin-arm64': 14.1.0 - '@next/swc-darwin-x64': 14.1.0 - '@next/swc-linux-arm64-gnu': 14.1.0 - '@next/swc-linux-arm64-musl': 14.1.0 - '@next/swc-linux-x64-gnu': 14.1.0 - '@next/swc-linux-x64-musl': 14.1.0 - '@next/swc-win32-arm64-msvc': 14.1.0 - '@next/swc-win32-ia32-msvc': 14.1.0 - '@next/swc-win32-x64-msvc': 14.1.0 + '@next/swc-darwin-arm64': 14.2.3 + '@next/swc-darwin-x64': 14.2.3 + '@next/swc-linux-arm64-gnu': 14.2.3 + '@next/swc-linux-arm64-musl': 14.2.3 + '@next/swc-linux-x64-gnu': 14.2.3 + '@next/swc-linux-x64-musl': 14.2.3 + '@next/swc-win32-arm64-msvc': 14.2.3 + '@next/swc-win32-ia32-msvc': 14.2.3 + '@next/swc-win32-x64-msvc': 14.2.3 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros - dev: false - /node-releases@2.0.14: - resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} - dev: true + node-releases@2.0.14: {} - /normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} + normalize-path@3.0.0: {} - /normalize-range@0.1.2: - resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} - engines: {node: '>=0.10.0'} - dev: true + normalize-range@0.1.2: {} - /npm-run-path@4.0.1: - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} - engines: {node: '>=8'} + npm-run-path@4.0.1: dependencies: path-key: 3.1.1 - dev: true - /object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} + object-assign@4.1.1: {} - /object-hash@3.0.0: - resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} - engines: {node: '>= 6'} + object-hash@3.0.0: {} - /object-inspect@1.13.1: - resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} - dev: false + object-inspect@1.13.1: {} - /object-keys@1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} - dev: false + object-keys@1.1.1: {} - /object.assign@4.1.5: - resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} - engines: {node: '>= 0.4'} + object.assign@4.1.5: dependencies: - call-bind: 1.0.5 + call-bind: 1.0.7 define-properties: 1.2.1 has-symbols: 1.0.3 object-keys: 1.1.1 - dev: false - /object.entries@1.1.7: - resolution: {integrity: sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==} - engines: {node: '>= 0.4'} + object.entries@1.1.8: dependencies: - call-bind: 1.0.5 + call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.3 - dev: false + es-object-atoms: 1.0.0 - /object.fromentries@2.0.7: - resolution: {integrity: sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==} - engines: {node: '>= 0.4'} + object.fromentries@2.0.8: dependencies: - call-bind: 1.0.5 + call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.3 - dev: false + es-abstract: 1.23.3 + es-object-atoms: 1.0.0 - /object.groupby@1.0.1: - resolution: {integrity: sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==} + object.groupby@1.0.3: dependencies: - call-bind: 1.0.5 + call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.3 - get-intrinsic: 1.2.2 - dev: false + es-abstract: 1.23.3 - /object.hasown@1.1.3: - resolution: {integrity: sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==} + object.hasown@1.1.4: dependencies: define-properties: 1.2.1 - es-abstract: 1.22.3 - dev: false + es-abstract: 1.23.3 + es-object-atoms: 1.0.0 - /object.values@1.1.7: - resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==} - engines: {node: '>= 0.4'} + object.values@1.2.0: dependencies: - call-bind: 1.0.5 + call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.3 - dev: false - - /once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - dependencies: - wrappy: 1.0.2 + es-object-atoms: 1.0.0 - /onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} + onetime@5.1.2: dependencies: mimic-fn: 2.1.0 - dev: true - /optionator@0.9.3: - resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} - engines: {node: '>= 0.8.0'} + optionator@0.9.4: dependencies: - '@aashutoshrathi/word-wrap': 1.2.6 deep-is: 0.1.4 fast-levenshtein: 2.0.6 levn: 0.4.1 prelude-ls: 1.2.1 type-check: 0.4.0 + word-wrap: 1.2.5 - /p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 - /p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} + p-locate@5.0.0: dependencies: p-limit: 3.1.0 - /parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} + parent-module@1.0.1: dependencies: callsites: 3.1.0 - /parse-entities@2.0.0: - resolution: {integrity: sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==} + parse-entities@2.0.0: dependencies: character-entities: 1.2.4 character-entities-legacy: 1.1.4 @@ -3224,580 +4730,369 @@ packages: is-alphanumerical: 1.0.4 is-decimal: 1.0.4 is-hexadecimal: 1.0.4 - dev: false - - /path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - /path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} + path-exists@4.0.0: {} - /path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} + path-key@3.1.1: {} - /path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-parse@1.0.7: {} - /path-scurry@1.10.1: - resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==} - engines: {node: '>=16 || 14 >=14.17'} + path-scurry@1.10.2: dependencies: - lru-cache: 10.1.0 + lru-cache: 10.2.2 minipass: 7.0.4 - /path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} + path-type@4.0.0: {} - /picocolors@1.0.0: - resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + picocolors@1.0.0: {} - /picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} + picomatch@2.3.1: {} - /pify@2.3.0: - resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} - engines: {node: '>=0.10.0'} + pify@2.3.0: {} - /pirates@4.0.6: - resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} - engines: {node: '>= 6'} + pirates@4.0.6: {} - /postcss-import@15.1.0(postcss@8.4.33): - resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} - engines: {node: '>=14.0.0'} - peerDependencies: - postcss: ^8.0.0 - dependencies: - postcss: 8.4.33 - postcss-value-parser: 4.2.0 - read-cache: 1.0.0 - resolve: 1.22.8 + possible-typed-array-names@1.0.0: {} - /postcss-js@4.0.1(postcss@8.4.33): - resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} - engines: {node: ^12 || ^14 || >= 16} - peerDependencies: - postcss: ^8.4.21 + postcss-import@15.1.0(postcss@8.4.38): dependencies: - camelcase-css: 2.0.1 - postcss: 8.4.33 - - /postcss-load-config@4.0.2(postcss@8.4.33): - resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} - engines: {node: '>= 14'} - peerDependencies: - postcss: '>=8.0.9' - ts-node: '>=9.0.0' - peerDependenciesMeta: - postcss: - optional: true - ts-node: - optional: true + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.8 + + postcss-js@4.0.1(postcss@8.4.38): dependencies: - lilconfig: 3.0.0 - postcss: 8.4.33 - yaml: 2.3.4 + camelcase-css: 2.0.1 + postcss: 8.4.38 - /postcss-nested@6.0.1(postcss@8.4.33): - resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==} - engines: {node: '>=12.0'} - peerDependencies: - postcss: ^8.2.14 + postcss-load-config@4.0.2(postcss@8.4.38): dependencies: - postcss: 8.4.33 - postcss-selector-parser: 6.0.15 + lilconfig: 3.1.1 + yaml: 2.4.2 + optionalDependencies: + postcss: 8.4.38 - /postcss-selector-parser@6.0.15: - resolution: {integrity: sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==} - engines: {node: '>=4'} + postcss-nested@6.0.1(postcss@8.4.38): + dependencies: + postcss: 8.4.38 + postcss-selector-parser: 6.0.16 + + postcss-selector-parser@6.0.16: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 - /postcss-value-parser@4.2.0: - resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + postcss-value-parser@4.2.0: {} - /postcss@8.4.31: - resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} - engines: {node: ^10 || ^12 || >=14} + postcss@8.4.31: dependencies: nanoid: 3.3.7 picocolors: 1.0.0 - source-map-js: 1.0.2 - dev: false + source-map-js: 1.2.0 - /postcss@8.4.33: - resolution: {integrity: sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==} - engines: {node: ^10 || ^12 || >=14} + postcss@8.4.38: dependencies: nanoid: 3.3.7 picocolors: 1.0.0 - source-map-js: 1.0.2 + source-map-js: 1.2.0 - /prelude-ls@1.2.1: - resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} - engines: {node: '>= 0.8.0'} + prelude-ls@1.2.1: {} - /prettier@3.2.4: - resolution: {integrity: sha512-FWu1oLHKCrtpO1ypU6J0SbK2d9Ckwysq6bHj/uaCP26DxrPpppCLQRGVuqAxSTvhF00AcvDRyYrLNW7ocBhFFQ==} - engines: {node: '>=14'} - hasBin: true - dev: true + prettier@3.2.5: {} - /prismjs@1.27.0: - resolution: {integrity: sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==} - engines: {node: '>=6'} - dev: false + prismjs@1.27.0: {} - /prismjs@1.29.0: - resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==} - engines: {node: '>=6'} - dev: false + prismjs@1.29.0: {} - /prop-types@15.8.1: - resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + prop-types@15.8.1: dependencies: loose-envify: 1.4.0 object-assign: 4.1.1 react-is: 16.13.1 - dev: false - /property-information@5.6.0: - resolution: {integrity: sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==} + property-information@5.6.0: dependencies: xtend: 4.0.2 - dev: false - /punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} + punycode@2.3.1: {} - /queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + queue-microtask@1.2.3: {} - /react-dom@18.2.0(react@18.2.0): - resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} - peerDependencies: - react: ^18.2.0 + react-dom@18.3.1(react@18.3.1): dependencies: loose-envify: 1.4.0 - react: 18.2.0 - scheduler: 0.23.0 - dev: false + react: 18.3.1 + scheduler: 0.23.2 - /react-is@16.13.1: - resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - dev: false + react-is@16.13.1: {} - /react-refresh@0.14.0: - resolution: {integrity: sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==} - engines: {node: '>=0.10.0'} - dev: true + react-refresh@0.14.2: {} - /react-syntax-highlighter@15.5.0(react@18.2.0): - resolution: {integrity: sha512-+zq2myprEnQmH5yw6Gqc8lD55QHnpKaU8TOcFeC/Lg/MQSs8UknEA0JC4nTZGFAXC2J2Hyj/ijJ7NlabyPi2gg==} - peerDependencies: - react: '>= 0.14.0' + react-syntax-highlighter@15.5.0(react@18.3.1): dependencies: - '@babel/runtime': 7.23.9 + '@babel/runtime': 7.24.5 highlight.js: 10.7.3 lowlight: 1.20.0 prismjs: 1.29.0 - react: 18.2.0 + react: 18.3.1 refractor: 3.6.0 - dev: false - /react@18.2.0: - resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} - engines: {node: '>=0.10.0'} + react@18.3.1: dependencies: loose-envify: 1.4.0 - dev: false - /read-cache@1.0.0: - resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + read-cache@1.0.0: dependencies: pify: 2.3.0 - /readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} + readdirp@3.6.0: dependencies: picomatch: 2.3.1 - /reflect.getprototypeof@1.0.4: - resolution: {integrity: sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==} - engines: {node: '>= 0.4'} + reflect.getprototypeof@1.0.6: dependencies: - call-bind: 1.0.5 + call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.3 - get-intrinsic: 1.2.2 - globalthis: 1.0.3 + es-abstract: 1.23.3 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + globalthis: 1.0.4 which-builtin-type: 1.1.3 - dev: false - /refractor@3.6.0: - resolution: {integrity: sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==} + refractor@3.6.0: dependencies: hastscript: 6.0.0 parse-entities: 2.0.0 prismjs: 1.27.0 - dev: false - /regenerator-runtime@0.14.1: - resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + regenerator-runtime@0.14.1: {} - /regexp.prototype.flags@1.5.1: - resolution: {integrity: sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==} - engines: {node: '>= 0.4'} + regexp.prototype.flags@1.5.2: dependencies: - call-bind: 1.0.5 + call-bind: 1.0.7 define-properties: 1.2.1 - set-function-name: 2.0.1 - dev: false + es-errors: 1.3.0 + set-function-name: 2.0.2 - /require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - dev: true + require-directory@2.1.1: {} - /resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} + resolve-from@4.0.0: {} - /resolve-from@5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} - engines: {node: '>=8'} - dev: true + resolve-from@5.0.0: {} - /resolve-pkg-maps@1.0.0: - resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - dev: false + resolve-pkg-maps@1.0.0: {} - /resolve@1.22.8: - resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} - hasBin: true + resolve@1.22.8: dependencies: is-core-module: 2.13.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - /resolve@2.0.0-next.5: - resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} - hasBin: true + resolve@2.0.0-next.5: dependencies: is-core-module: 2.13.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - dev: false - - /reusify@1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - /rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - hasBin: true - dependencies: - glob: 7.2.3 + reusify@1.0.4: {} - /rollup@4.9.6: - resolution: {integrity: sha512-05lzkCS2uASX0CiLFybYfVkwNbKZG5NFQ6Go0VWyogFTXXbR039UVsegViTntkk4OglHBdF54ccApXRRuXRbsg==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true + rollup@4.17.2: dependencies: '@types/estree': 1.0.5 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.9.6 - '@rollup/rollup-android-arm64': 4.9.6 - '@rollup/rollup-darwin-arm64': 4.9.6 - '@rollup/rollup-darwin-x64': 4.9.6 - '@rollup/rollup-linux-arm-gnueabihf': 4.9.6 - '@rollup/rollup-linux-arm64-gnu': 4.9.6 - '@rollup/rollup-linux-arm64-musl': 4.9.6 - '@rollup/rollup-linux-riscv64-gnu': 4.9.6 - '@rollup/rollup-linux-x64-gnu': 4.9.6 - '@rollup/rollup-linux-x64-musl': 4.9.6 - '@rollup/rollup-win32-arm64-msvc': 4.9.6 - '@rollup/rollup-win32-ia32-msvc': 4.9.6 - '@rollup/rollup-win32-x64-msvc': 4.9.6 + '@rollup/rollup-android-arm-eabi': 4.17.2 + '@rollup/rollup-android-arm64': 4.17.2 + '@rollup/rollup-darwin-arm64': 4.17.2 + '@rollup/rollup-darwin-x64': 4.17.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.17.2 + '@rollup/rollup-linux-arm-musleabihf': 4.17.2 + '@rollup/rollup-linux-arm64-gnu': 4.17.2 + '@rollup/rollup-linux-arm64-musl': 4.17.2 + '@rollup/rollup-linux-powerpc64le-gnu': 4.17.2 + '@rollup/rollup-linux-riscv64-gnu': 4.17.2 + '@rollup/rollup-linux-s390x-gnu': 4.17.2 + '@rollup/rollup-linux-x64-gnu': 4.17.2 + '@rollup/rollup-linux-x64-musl': 4.17.2 + '@rollup/rollup-win32-arm64-msvc': 4.17.2 + '@rollup/rollup-win32-ia32-msvc': 4.17.2 + '@rollup/rollup-win32-x64-msvc': 4.17.2 fsevents: 2.3.3 - dev: true - /run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 - /rxjs@7.8.1: - resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + rxjs@7.8.1: dependencies: tslib: 2.6.2 - dev: true - /safe-array-concat@1.1.0: - resolution: {integrity: sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==} - engines: {node: '>=0.4'} + safe-array-concat@1.1.2: dependencies: - call-bind: 1.0.5 - get-intrinsic: 1.2.2 + call-bind: 1.0.7 + get-intrinsic: 1.2.4 has-symbols: 1.0.3 isarray: 2.0.5 - dev: false - /safe-regex-test@1.0.2: - resolution: {integrity: sha512-83S9w6eFq12BBIJYvjMux6/dkirb8+4zJRA9cxNBVb7Wq5fJBW+Xze48WqR8pxua7bDuAaaAxtVVd4Idjp1dBQ==} - engines: {node: '>= 0.4'} + safe-regex-test@1.0.3: dependencies: - call-bind: 1.0.5 - get-intrinsic: 1.2.2 + call-bind: 1.0.7 + es-errors: 1.3.0 is-regex: 1.1.4 - dev: false - /scheduler@0.23.0: - resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} + scheduler@0.23.2: dependencies: loose-envify: 1.4.0 - dev: false - /semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true + semver@6.3.1: {} - /semver@7.5.4: - resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} - engines: {node: '>=10'} - hasBin: true + semver@7.6.0: dependencies: lru-cache: 6.0.0 - /set-function-length@1.2.0: - resolution: {integrity: sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==} - engines: {node: '>= 0.4'} + set-function-length@1.2.2: dependencies: - define-data-property: 1.1.1 + define-data-property: 1.1.4 + es-errors: 1.3.0 function-bind: 1.1.2 - get-intrinsic: 1.2.2 + get-intrinsic: 1.2.4 gopd: 1.0.1 - has-property-descriptors: 1.0.1 - dev: false + has-property-descriptors: 1.0.2 - /set-function-name@2.0.1: - resolution: {integrity: sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==} - engines: {node: '>= 0.4'} + set-function-name@2.0.2: dependencies: - define-data-property: 1.1.1 + define-data-property: 1.1.4 + es-errors: 1.3.0 functions-have-names: 1.2.3 - has-property-descriptors: 1.0.1 - dev: false + has-property-descriptors: 1.0.2 - /shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 - /shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} + shebang-regex@3.0.0: {} - /shell-quote@1.8.1: - resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} - dev: true + shell-quote@1.8.1: {} - /side-channel@1.0.4: - resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + side-channel@1.0.6: dependencies: - call-bind: 1.0.5 - get-intrinsic: 1.2.2 + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 object-inspect: 1.13.1 - dev: false - /signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - dev: true + signal-exit@3.0.7: {} - /signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} + signal-exit@4.1.0: {} - /slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} + slash@3.0.0: {} - /source-map-js@1.0.2: - resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} - engines: {node: '>=0.10.0'} + source-map-js@1.2.0: {} - /source-map@0.8.0-beta.0: - resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} - engines: {node: '>= 8'} + source-map@0.8.0-beta.0: dependencies: whatwg-url: 7.1.0 - dev: true - /space-separated-tokens@1.1.5: - resolution: {integrity: sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==} - dev: false + space-separated-tokens@1.1.5: {} - /spawn-command@0.0.2: - resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==} - dev: true + spawn-command@0.0.2: {} - /streamsearch@1.1.0: - resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} - engines: {node: '>=10.0.0'} - dev: false + streamsearch@1.1.0: {} - /string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - /string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} + string-width@5.1.2: dependencies: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 strip-ansi: 7.1.0 - /string.prototype.matchall@4.0.10: - resolution: {integrity: sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==} + string.prototype.matchall@4.0.11: dependencies: - call-bind: 1.0.5 + call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.3 - get-intrinsic: 1.2.2 + es-abstract: 1.23.3 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + get-intrinsic: 1.2.4 + gopd: 1.0.1 has-symbols: 1.0.3 - internal-slot: 1.0.6 - regexp.prototype.flags: 1.5.1 - set-function-name: 2.0.1 - side-channel: 1.0.4 - dev: false - - /string.prototype.trim@1.2.8: - resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==} - engines: {node: '>= 0.4'} + internal-slot: 1.0.7 + regexp.prototype.flags: 1.5.2 + set-function-name: 2.0.2 + side-channel: 1.0.6 + + string.prototype.trim@1.2.9: dependencies: - call-bind: 1.0.5 + call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.3 - dev: false + es-abstract: 1.23.3 + es-object-atoms: 1.0.0 - /string.prototype.trimend@1.0.7: - resolution: {integrity: sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==} + string.prototype.trimend@1.0.8: dependencies: - call-bind: 1.0.5 + call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.3 - dev: false + es-object-atoms: 1.0.0 - /string.prototype.trimstart@1.0.7: - resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==} + string.prototype.trimstart@1.0.8: dependencies: - call-bind: 1.0.5 + call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.3 - dev: false + es-object-atoms: 1.0.0 - /strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 - /strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} - engines: {node: '>=12'} + strip-ansi@7.1.0: dependencies: ansi-regex: 6.0.1 - /strip-bom@3.0.0: - resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} - engines: {node: '>=4'} - dev: false + strip-bom@3.0.0: {} - /strip-final-newline@2.0.0: - resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} - engines: {node: '>=6'} - dev: true + strip-final-newline@2.0.0: {} - /strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} + strip-json-comments@3.1.1: {} - /styled-jsx@5.1.1(react@18.2.0): - resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} - engines: {node: '>= 12.0.0'} - peerDependencies: - '@babel/core': '*' - babel-plugin-macros: '*' - react: '>= 16.8.0 || 17.x.x || ^18.0.0-0' - peerDependenciesMeta: - '@babel/core': - optional: true - babel-plugin-macros: - optional: true + styled-jsx@5.1.1(react@18.3.1): dependencies: client-only: 0.0.1 - react: 18.2.0 - dev: false + react: 18.3.1 - /sucrase@3.35.0: - resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} - engines: {node: '>=16 || 14 >=14.17'} - hasBin: true + sucrase@3.35.0: dependencies: - '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/gen-mapping': 0.3.5 commander: 4.1.1 - glob: 10.3.10 + glob: 10.3.12 lines-and-columns: 1.2.4 mz: 2.7.0 pirates: 4.0.6 ts-interface-checker: 0.1.13 - /supports-color@5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} + supports-color@5.5.0: dependencies: has-flag: 3.0.0 - dev: true - /supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} + supports-color@7.2.0: dependencies: has-flag: 4.0.0 - /supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} + supports-color@8.1.1: dependencies: has-flag: 4.0.0 - dev: true - /supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} + supports-preserve-symlinks-flag@1.0.0: {} - /tailwindcss@3.4.1: - resolution: {integrity: sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==} - engines: {node: '>=14.0.0'} - hasBin: true + tailwindcss@3.4.3: dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 - chokidar: 3.5.3 + chokidar: 3.6.0 didyoumean: 1.2.2 dlv: 1.1.3 fast-glob: 3.3.2 @@ -3809,345 +5104,203 @@ packages: normalize-path: 3.0.0 object-hash: 3.0.0 picocolors: 1.0.0 - postcss: 8.4.33 - postcss-import: 15.1.0(postcss@8.4.33) - postcss-js: 4.0.1(postcss@8.4.33) - postcss-load-config: 4.0.2(postcss@8.4.33) - postcss-nested: 6.0.1(postcss@8.4.33) - postcss-selector-parser: 6.0.15 + postcss: 8.4.38 + postcss-import: 15.1.0(postcss@8.4.38) + postcss-js: 4.0.1(postcss@8.4.38) + postcss-load-config: 4.0.2(postcss@8.4.38) + postcss-nested: 6.0.1(postcss@8.4.38) + postcss-selector-parser: 6.0.16 resolve: 1.22.8 sucrase: 3.35.0 transitivePeerDependencies: - ts-node - /tapable@2.2.1: - resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} - engines: {node: '>=6'} - dev: false + tapable@2.2.1: {} - /text-table@0.2.0: - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + text-table@0.2.0: {} - /thenify-all@1.6.0: - resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} - engines: {node: '>=0.8'} + thenify-all@1.6.0: dependencies: thenify: 3.3.1 - /thenify@3.3.1: - resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + thenify@3.3.1: dependencies: any-promise: 1.3.0 - /to-fast-properties@2.0.0: - resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} - engines: {node: '>=4'} - dev: true + to-fast-properties@2.0.0: {} - /to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 - /toggle-selection@1.0.6: - resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} - dev: false + toggle-selection@1.0.6: {} - /tr46@1.0.1: - resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} + tr46@1.0.1: dependencies: punycode: 2.3.1 - dev: true - /tree-kill@1.2.2: - resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} - hasBin: true - dev: true + tree-kill@1.2.2: {} - /ts-api-utils@1.0.3(typescript@5.3.3): - resolution: {integrity: sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==} - engines: {node: '>=16.13.0'} - peerDependencies: - typescript: '>=4.2.0' + ts-api-utils@1.3.0(typescript@5.4.5): dependencies: - typescript: 5.3.3 + typescript: 5.4.5 - /ts-interface-checker@0.1.13: - resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + ts-interface-checker@0.1.13: {} - /tsconfig-paths@3.15.0: - resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + tsconfig-paths@3.15.0: dependencies: '@types/json5': 0.0.29 json5: 1.0.2 minimist: 1.2.8 strip-bom: 3.0.0 - dev: false - /tslib@2.6.2: - resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + tslib@2.6.2: {} - /tsup@8.0.1(typescript@5.3.3): - resolution: {integrity: sha512-hvW7gUSG96j53ZTSlT4j/KL0q1Q2l6TqGBFc6/mu/L46IoNWqLLUzLRLP1R8Q7xrJTmkDxxDoojV5uCVs1sVOg==} - engines: {node: '>=18'} - hasBin: true - peerDependencies: - '@microsoft/api-extractor': ^7.36.0 - '@swc/core': ^1 - postcss: ^8.4.12 - typescript: '>=4.5.0' - peerDependenciesMeta: - '@microsoft/api-extractor': - optional: true - '@swc/core': - optional: true - postcss: - optional: true - typescript: - optional: true + tsup@8.0.2(@swc/core@1.4.17(@swc/helpers@0.5.11))(postcss@8.4.38)(typescript@5.4.5): dependencies: - bundle-require: 4.0.2(esbuild@0.19.12) + bundle-require: 4.0.3(esbuild@0.19.12) cac: 6.7.14 - chokidar: 3.5.3 + chokidar: 3.6.0 debug: 4.3.4 esbuild: 0.19.12 execa: 5.1.1 globby: 11.1.0 joycon: 3.1.1 - postcss-load-config: 4.0.2(postcss@8.4.33) + postcss-load-config: 4.0.2(postcss@8.4.38) resolve-from: 5.0.0 - rollup: 4.9.6 + rollup: 4.17.2 source-map: 0.8.0-beta.0 sucrase: 3.35.0 tree-kill: 1.2.2 - typescript: 5.3.3 + optionalDependencies: + '@swc/core': 1.4.17(@swc/helpers@0.5.11) + postcss: 8.4.38 + typescript: 5.4.5 transitivePeerDependencies: - supports-color - ts-node - dev: true - /turbo-darwin-64@1.11.3: - resolution: {integrity: sha512-IsOOg2bVbIt3o/X8Ew9fbQp5t1hTHN3fGNQYrPQwMR2W1kIAC6RfbVD4A9OeibPGyEPUpwOH79hZ9ydFH5kifw==} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true + turbo-darwin-64@1.13.3: optional: true - /turbo-darwin-arm64@1.11.3: - resolution: {integrity: sha512-FsJL7k0SaPbJzI/KCnrf/fi3PgCDCjTliMc/kEFkuWVA6Httc3Q4lxyLIIinz69q6JTx8wzh6yznUMzJRI3+dg==} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true + turbo-darwin-arm64@1.13.3: optional: true - /turbo-linux-64@1.11.3: - resolution: {integrity: sha512-SvW7pvTVRGsqtSkII5w+wriZXvxqkluw5FO/MNAdFw0qmoov+PZ237+37/NgArqE3zVn1GX9P6nUx9VO+xcQAg==} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true + turbo-linux-64@1.13.3: optional: true - /turbo-linux-arm64@1.11.3: - resolution: {integrity: sha512-YhUfBi1deB3m+3M55X458J6B7RsIS7UtM3P1z13cUIhF+pOt65BgnaSnkHLwETidmhRh8Dl3GelaQGrB3RdCDw==} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true + turbo-linux-arm64@1.13.3: optional: true - /turbo-windows-64@1.11.3: - resolution: {integrity: sha512-s+vEnuM2TiZuAUUUpmBHDr6vnNbJgj+5JYfnYmVklYs16kXh+EppafYQOAkcRIMAh7GjV3pLq5/uGqc7seZeHA==} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true + turbo-windows-64@1.13.3: optional: true - /turbo-windows-arm64@1.11.3: - resolution: {integrity: sha512-ZR5z5Zpc7cASwfdRAV5yNScCZBsgGSbcwiA/u3farCacbPiXsfoWUkz28iyrx21/TRW0bi6dbsB2v17swa8bjw==} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true + turbo-windows-arm64@1.13.3: optional: true - /turbo@1.11.3: - resolution: {integrity: sha512-RCJOUFcFMQNIGKSjC9YmA5yVP1qtDiBA0Lv9VIgrXraI5Da1liVvl3VJPsoDNIR9eFMyA/aagx1iyj6UWem5hA==} - hasBin: true + turbo@1.13.3: optionalDependencies: - turbo-darwin-64: 1.11.3 - turbo-darwin-arm64: 1.11.3 - turbo-linux-64: 1.11.3 - turbo-linux-arm64: 1.11.3 - turbo-windows-64: 1.11.3 - turbo-windows-arm64: 1.11.3 - dev: true - - /type-check@0.4.0: - resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} - engines: {node: '>= 0.8.0'} + turbo-darwin-64: 1.13.3 + turbo-darwin-arm64: 1.13.3 + turbo-linux-64: 1.13.3 + turbo-linux-arm64: 1.13.3 + turbo-windows-64: 1.13.3 + turbo-windows-arm64: 1.13.3 + + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 - /type-fest@0.20.2: - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} - engines: {node: '>=10'} - - /typed-array-buffer@1.0.0: - resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==} - engines: {node: '>= 0.4'} + typed-array-buffer@1.0.2: dependencies: - call-bind: 1.0.5 - get-intrinsic: 1.2.2 - is-typed-array: 1.1.12 - dev: false + call-bind: 1.0.7 + es-errors: 1.3.0 + is-typed-array: 1.1.13 - /typed-array-byte-length@1.0.0: - resolution: {integrity: sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==} - engines: {node: '>= 0.4'} + typed-array-byte-length@1.0.1: dependencies: - call-bind: 1.0.5 + call-bind: 1.0.7 for-each: 0.3.3 - has-proto: 1.0.1 - is-typed-array: 1.1.12 - dev: false + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 - /typed-array-byte-offset@1.0.0: - resolution: {integrity: sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==} - engines: {node: '>= 0.4'} + typed-array-byte-offset@1.0.2: dependencies: - available-typed-arrays: 1.0.5 - call-bind: 1.0.5 + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 for-each: 0.3.3 - has-proto: 1.0.1 - is-typed-array: 1.1.12 - dev: false + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 - /typed-array-length@1.0.4: - resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} + typed-array-length@1.0.6: dependencies: - call-bind: 1.0.5 + call-bind: 1.0.7 for-each: 0.3.3 - is-typed-array: 1.1.12 - dev: false + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + possible-typed-array-names: 1.0.0 - /typescript@5.3.3: - resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} - engines: {node: '>=14.17'} - hasBin: true + typescript@5.4.5: {} - /unbox-primitive@1.0.2: - resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + unbox-primitive@1.0.2: dependencies: - call-bind: 1.0.5 + call-bind: 1.0.7 has-bigints: 1.0.2 has-symbols: 1.0.3 which-boxed-primitive: 1.0.2 - dev: false - /undici-types@5.26.5: - resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - dev: true + undici-types@5.26.5: {} - /update-browserslist-db@1.0.13(browserslist@4.22.2): - resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' + update-browserslist-db@1.0.14(browserslist@4.23.0): dependencies: - browserslist: 4.22.2 - escalade: 3.1.1 + browserslist: 4.23.0 + escalade: 3.1.2 picocolors: 1.0.0 - dev: true - /uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + uri-js@4.4.1: dependencies: punycode: 2.3.1 - /util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + util-deprecate@1.0.2: {} - /vite-plugin-singlefile@1.0.0(rollup@4.9.6)(vite@5.0.12): - resolution: {integrity: sha512-iVAxl0t7gndQrznI4kI14hqAGxhVL7FQndeTdvTIH5/CbwpR2asgD3XSTsd7iWC83YTCmwAmsHIQvKdIdnHu7w==} - engines: {node: '>18.0.0'} - peerDependencies: - rollup: ^4.9.5 - vite: ^5.0.11 + vite-plugin-singlefile@2.0.1(rollup@4.17.2)(vite@5.2.10(@types/node@20.12.8)): dependencies: micromatch: 4.0.5 - rollup: 4.9.6 - vite: 5.0.12(@types/node@20.11.6) - dev: true + rollup: 4.17.2 + vite: 5.2.10(@types/node@20.12.8) - /vite@5.0.12(@types/node@20.11.6): - resolution: {integrity: sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' - lightningcss: ^1.21.0 - sass: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true + vite@5.2.10(@types/node@20.12.8): dependencies: - '@types/node': 20.11.6 - esbuild: 0.19.12 - postcss: 8.4.33 - rollup: 4.9.6 + esbuild: 0.20.2 + postcss: 8.4.38 + rollup: 4.17.2 optionalDependencies: + '@types/node': 20.12.8 fsevents: 2.3.3 - dev: true - /webidl-conversions@4.0.2: - resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} - dev: true + webidl-conversions@4.0.2: {} - /whatwg-url@7.1.0: - resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} + whatwg-url@7.1.0: dependencies: lodash.sortby: 4.7.0 tr46: 1.0.1 webidl-conversions: 4.0.2 - dev: true - /which-boxed-primitive@1.0.2: - resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + which-boxed-primitive@1.0.2: dependencies: is-bigint: 1.0.4 is-boolean-object: 1.1.2 is-number-object: 1.0.7 is-string: 1.0.7 is-symbol: 1.0.4 - dev: false - /which-builtin-type@1.1.3: - resolution: {integrity: sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==} - engines: {node: '>= 0.4'} + which-builtin-type@1.1.3: dependencies: function.prototype.name: 1.1.6 - has-tostringtag: 1.0.0 + has-tostringtag: 1.0.2 is-async-function: 2.0.0 is-date-object: 1.0.5 is-finalizationregistry: 1.0.2 @@ -4156,95 +5309,62 @@ packages: is-weakref: 1.0.2 isarray: 2.0.5 which-boxed-primitive: 1.0.2 - which-collection: 1.0.1 - which-typed-array: 1.1.13 - dev: false + which-collection: 1.0.2 + which-typed-array: 1.1.15 - /which-collection@1.0.1: - resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==} + which-collection@1.0.2: dependencies: - is-map: 2.0.2 - is-set: 2.0.2 - is-weakmap: 2.0.1 - is-weakset: 2.0.2 - dev: false + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.3 - /which-typed-array@1.1.13: - resolution: {integrity: sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==} - engines: {node: '>= 0.4'} + which-typed-array@1.1.15: dependencies: - available-typed-arrays: 1.0.5 - call-bind: 1.0.5 + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 for-each: 0.3.3 gopd: 1.0.1 - has-tostringtag: 1.0.0 - dev: false + has-tostringtag: 1.0.2 - /which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true + which@2.0.2: dependencies: isexe: 2.0.0 - /wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} + word-wrap@1.2.5: {} + + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - /wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} + wrap-ansi@8.1.0: dependencies: ansi-styles: 6.2.1 string-width: 5.1.2 strip-ansi: 7.1.0 - /wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - - /xtend@4.0.2: - resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} - engines: {node: '>=0.4'} - dev: false + xtend@4.0.2: {} - /y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - dev: true + y18n@5.0.8: {} - /yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - dev: true + yallist@3.1.1: {} - /yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yallist@4.0.0: {} - /yaml@2.3.4: - resolution: {integrity: sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==} - engines: {node: '>= 14'} + yaml@2.4.2: {} - /yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} - dev: true + yargs-parser@21.1.1: {} - /yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} + yargs@17.7.2: dependencies: cliui: 8.0.1 - escalade: 3.1.1 + escalade: 3.1.2 get-caller-file: 2.0.5 require-directory: 2.1.1 string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 21.1.1 - dev: true - /yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} + yocto-queue@0.1.0: {} diff --git a/turbo.json b/turbo.json index 048a903d..6f46983a 100644 --- a/turbo.json +++ b/turbo.json @@ -1,33 +1,9 @@ { "$schema": "https://turbo.build/schema.json", - "globalDependencies": ["**/.env.*local"], - "globalEnv": ["PORT"], "pipeline": { "build": { - "outputs": [ - "dist/**", - ".next/**", - "!.next/cache/**", - "build/**", - "api/**", - "public/build/**" - ], - "dependsOn": ["^build"] + "outputs": [".next/**", "!.next/cache/**"] }, - "test": { - "outputs": ["coverage/**"], - "dependsOn": [] - }, - "lint": { - "dependsOn": ["^build"] - }, - "dev": { - "dependsOn": ["^build"], - "cache": false, - "persistent": true - }, - "clean": { - "cache": false - } + "type-check": {} } -} +} \ No newline at end of file From 29dfb0f0c0df4277ff6f159bb77a1b7c12acfeda Mon Sep 17 00:00:00 2001 From: Dave Stewart Date: Wed, 3 Jul 2024 00:32:38 +0100 Subject: [PATCH 002/168] Add Tailwind support for horizontal auto-layout hug (#111) Co-authored-by: Dave Stewart --- packages/backend/src/tailwind/builderImpl/tailwindSize.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/tailwind/builderImpl/tailwindSize.ts b/packages/backend/src/tailwind/builderImpl/tailwindSize.ts index 609508e5..0e043cb4 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindSize.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindSize.ts @@ -14,7 +14,11 @@ export const tailwindSizePartial = ( : null) ?? node.parent; let w = ""; - if (typeof size.width === "number") { + if ( + typeof size.width === "number" && + 'layoutSizingHorizontal' in node && + node.layoutSizingHorizontal === 'FIXED' + ) { w = `w-${pxToLayoutSize(size.width)}`; } else if (size.width === "fill") { if ( From d2c3f5292f204f8bbcf8b8fef28ecf53e64fdca2 Mon Sep 17 00:00:00 2001 From: Dave Stewart Date: Thu, 11 Jul 2024 15:57:41 +0100 Subject: [PATCH 003/168] Add Tailwind support for Figma variables (#109) * Modify Tailwind color functions to support Figma variables * Refactor function to arrow function * Fix fill opacity code * Add new Tailwind preferences * Prefer Tailwind color when color matches exactly * Update UI with shorter labels and tooltips --------- Co-authored-by: Dave Stewart --- apps/plugin/plugin-src/code.ts | 4 +- packages/backend/src/code.ts | 4 +- .../src/common/retrieveUI/retrieveColors.ts | 10 ++- .../src/tailwind/builderImpl/tailwindColor.ts | 80 +++++++++++-------- .../backend/src/tailwind/conversionTables.ts | 4 +- packages/plugin-ui/src/PluginUI.tsx | 37 +++++++-- 6 files changed, 94 insertions(+), 45 deletions(-) diff --git a/apps/plugin/plugin-src/code.ts b/apps/plugin/plugin-src/code.ts index c98f51ec..27eefa34 100644 --- a/apps/plugin/plugin-src/code.ts +++ b/apps/plugin/plugin-src/code.ts @@ -24,7 +24,9 @@ const defaultPluginSettings: PluginSettings = { responsiveRoot: false, flutterGenerationMode: "snippet", swiftUIGenerationMode: "snippet", - roundTailwind: false, + roundTailwindValues: false, + roundTailwindColors: false, + customTailwindColors: false, }; // A helper type guard to ensure the key belongs to the PluginSettings type diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index 89e7f556..6189b2a1 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -19,7 +19,9 @@ export type PluginSettings = { responsiveRoot: boolean; flutterGenerationMode: string; swiftUIGenerationMode: string; - roundTailwind: boolean; + roundTailwindValues: boolean; + roundTailwindColors: boolean, + customTailwindColors: boolean, }; export const run = (settings: PluginSettings) => { diff --git a/packages/backend/src/common/retrieveUI/retrieveColors.ts b/packages/backend/src/common/retrieveUI/retrieveColors.ts index 2fb9d7d2..ea59e1a3 100644 --- a/packages/backend/src/common/retrieveUI/retrieveColors.ts +++ b/packages/backend/src/common/retrieveUI/retrieveColors.ts @@ -4,10 +4,11 @@ import { swiftuiGradient, } from "../../swiftui/builderImpl/swiftuiColor"; import { + getTailwindFromVariable, tailwindColors, tailwindGradient, tailwindNearestColor, - tailwindSolidColor, + tailwindSolidColor } from "../../tailwind/builderImpl/tailwindColor"; import { flutterColor, @@ -68,8 +69,11 @@ const convertSolidColor = ( const kind = "solid"; const hex = rgbTo6hex(fill.color); const hexNearestColor = tailwindNearestColor(hex); - exported = tailwindSolidColor(fill.color, fill.opacity, kind); - colorName = tailwindColors[hexNearestColor]; + const colorVar = fill.boundVariables?.color + exported = tailwindSolidColor(fill, kind); + colorName = colorVar + ? getTailwindFromVariable(colorVar) + : tailwindColors[hexNearestColor]; } else if (framework === "SwiftUI") { exported = swiftuiColor(fill.color, opacity); } diff --git a/packages/backend/src/tailwind/builderImpl/tailwindColor.ts b/packages/backend/src/tailwind/builderImpl/tailwindColor.ts index c8d644f2..d69db90c 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindColor.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindColor.ts @@ -1,7 +1,8 @@ import { nearestColorFrom } from "../../nearest-color/nearestColor"; import { retrieveTopFill } from "../../common/retrieveFill"; -import { gradientAngle } from "../../common/color"; +import { gradientAngle, rgbTo6hex } from "../../common/color"; import { nearestOpacity, nearestValue } from "../conversionTables"; +import { localTailwindSettings } from "../tailwindMain"; // retrieve the SOLID color for tailwind export const tailwindColorFromFills = ( @@ -13,7 +14,7 @@ export const tailwindColorFromFills = ( const fill = retrieveTopFill(fills); if (fill && fill.type === "SOLID") { - return tailwindSolidColor(fill.color, fill.opacity, kind); + return tailwindSolidColor(fill, kind); } else if ( fill && (fill.type === "GRADIENT_LINEAR" || @@ -22,31 +23,45 @@ export const tailwindColorFromFills = ( fill.type === "GRADIENT_DIAMOND") ) { if (fill.gradientStops.length > 0) { - return tailwindSolidColor( - fill.gradientStops[0].color, - fill.opacity, - kind - ); + return tailwindSolidColor(fill.gradientStops[0], kind); } } return ""; }; -export const tailwindSolidColor = ( - color: RGB, - opacity: number | undefined, - kind: string -): string => { - // example: text-opacity-50 - // ignore the 100. If opacity was changed, let it be visible. - const opacityProp = - opacity !== 1.0 ? `opacity-${nearestOpacity(opacity ?? 1.0)}` : null; +/** + * Get the tailwind token name for a given color + * + * - variables: uses the tokenised variable name + * - colors: uses the nearest Tailwind color name + */ +export const tailwindSolidColor = (fill: SolidPaint | ColorStop, kind?: string): string => { + // example: stone-500 or custom-color-700 + let color: string; + if (localTailwindSettings.customTailwindColors && fill.boundVariables?.color) { + color = getTailwindFromVariable(fill.boundVariables.color); + } else { + const colorValue = "#" + rgbTo6hex(fill.color); + const { name, value } = getTailwindFromFigmaRGB(fill.color); + if (localTailwindSettings.roundTailwindColors || colorValue === value) { + color = name; + } else { + color = `[#${rgbTo6hex(fill.color)}]`; + } + } - // example: text-red-500 - const colorProp = `${kind}-${getTailwindFromFigmaRGB(color)}${opacityProp ? `/${opacityProp}` : ""}`; + // if no kind, it's a variable stop, so just return the name + if (!kind) { + return color; + } - // if fill isn't visible, it shouldn't be painted. - return colorProp; + // grab opacity, or set it to full + const opacity = "opacity" in fill && fill.opacity !== 1.0 + ? `/${nearestOpacity(fill.opacity ?? 1.0)}` + : ""; + + // example: text-red-500, text-custom-color-700/25 + return `${kind}-${color}${opacity}`; }; /** @@ -71,24 +86,22 @@ export const tailwindGradient = (fill: GradientPaint): string => { const direction = gradientDirection(gradientAngle(fill)); if (fill.gradientStops.length === 1) { - const fromColor = getTailwindFromFigmaRGB(fill.gradientStops[0].color); + const fromColor = tailwindSolidColor(fill.gradientStops[0]); return `${direction} from-${fromColor}`; } else if (fill.gradientStops.length === 2) { - const fromColor = getTailwindFromFigmaRGB(fill.gradientStops[0].color); - const toColor = getTailwindFromFigmaRGB(fill.gradientStops[1].color); + const fromColor = tailwindSolidColor(fill.gradientStops[0]); + const toColor = tailwindSolidColor(fill.gradientStops[1]); return `${direction} from-${fromColor} to-${toColor}`; } else { - const fromColor = getTailwindFromFigmaRGB(fill.gradientStops[0].color); + const fromColor = tailwindSolidColor(fill.gradientStops[0]); // middle (second color) - const viaColor = getTailwindFromFigmaRGB(fill.gradientStops[1].color); + const viaColor = tailwindSolidColor(fill.gradientStops[1]); // last - const toColor = getTailwindFromFigmaRGB( - fill.gradientStops[fill.gradientStops.length - 1].color - ); + const toColor = tailwindSolidColor(fill.gradientStops[fill.gradientStops.length - 1]); return `${direction} from-${fromColor} via-${viaColor} to-${toColor}`; } @@ -368,16 +381,17 @@ export const tailwindNearestColor = nearestColorFrom( ); // figma uses r,g,b in [0, 1], while nearestColor uses it in [0, 255] -export const getTailwindFromFigmaRGB = (color: RGB): string => { +export const getTailwindFromFigmaRGB = (color: RGB) => { const colorMultiplied = { r: color.r * 255, g: color.g * 255, b: color.b * 255, }; - - return tailwindColors[tailwindNearestColor(colorMultiplied)]; + const value = tailwindNearestColor(colorMultiplied); + const name = tailwindColors[value]; + return { name, value }; }; -export const getTailwindColor = (color: string | RGB): string => { - return tailwindColors[tailwindNearestColor(color)]; +export const getTailwindFromVariable = (alias: VariableAlias) => { + return figma.variables.getVariableById(alias.id)?.name.replaceAll("/", "-") || alias.id; }; diff --git a/packages/backend/src/tailwind/conversionTables.ts b/packages/backend/src/tailwind/conversionTables.ts index ff6727b9..0f232a00 100644 --- a/packages/backend/src/tailwind/conversionTables.ts +++ b/packages/backend/src/tailwind/conversionTables.ts @@ -37,7 +37,7 @@ const pxToRemToTailwind = ( if (convertedValue) { return conversionMap[convertedValue]; - } else if (localTailwindSettings.roundTailwind) { + } else if (localTailwindSettings.roundTailwindValues) { return conversionMap[nearestValue(value / 16, keys)]; } @@ -53,7 +53,7 @@ const pxToTailwind = ( if (convertedValue) { return conversionMap[convertedValue]; - } else if (localTailwindSettings.roundTailwind) { + } else if (localTailwindSettings.roundTailwindValues) { return conversionMap[nearestValue(value, keys)]; } diff --git a/packages/plugin-ui/src/PluginUI.tsx b/packages/plugin-ui/src/PluginUI.tsx index c011f09f..5139c9bc 100644 --- a/packages/plugin-ui/src/PluginUI.tsx +++ b/packages/plugin-ui/src/PluginUI.tsx @@ -15,7 +15,9 @@ export type PluginSettings = { responsiveRoot: boolean; flutterGenerationMode: string; swiftUIGenerationMode: string; - roundTailwind: boolean; + roundTailwindValues: boolean; + roundTailwindColors: boolean; + customTailwindColors: boolean; }; type PluginUIProps = { @@ -161,6 +163,7 @@ type LocalCodegenPreference = "framework" | "flutterGenerationMode" | "swiftUIGenerationMode" >; label: string; + description: string; value?: boolean; isDefault?: boolean; includedLanguages?: FrameworkTypes[]; @@ -171,6 +174,7 @@ export const preferenceOptions: LocalCodegenPreference[] = [ itemType: "individual_select", propertyName: "jsx", label: "React (JSX)", + description: 'Render "class" attributes as "className"', isDefault: false, includedLanguages: ["HTML", "Tailwind"], }, @@ -191,21 +195,40 @@ export const preferenceOptions: LocalCodegenPreference[] = [ { itemType: "individual_select", propertyName: "optimizeLayout", - label: "Optimize Layout", + label: "Optimize layout", + description: 'Attempt to auto-layout suitable element groups', isDefault: true, includedLanguages: ["HTML", "Tailwind", "Flutter", "SwiftUI"], }, { itemType: "individual_select", propertyName: "layerName", - label: "Layer Names", + label: "Layer names", + description: 'Include layer names in classes', isDefault: false, includedLanguages: ["HTML", "Tailwind"], }, { itemType: "individual_select", - propertyName: "roundTailwind", - label: "Round to Tailwind Values", + propertyName: "roundTailwindValues", + label: "Round values", + description: 'Round pixel values to nearest Tailwind sizes', + isDefault: false, + includedLanguages: ["Tailwind"], + }, + { + itemType: "individual_select", + propertyName: "roundTailwindColors", + label: "Round colors", + description: 'Round color values to nearest Tailwind colors', + isDefault: false, + includedLanguages: ["Tailwind"], + }, + { + itemType: "individual_select", + propertyName: "customTailwindColors", + label: "Custom colors", + description: 'Use color variable names as custom color names', isDefault: false, includedLanguages: ["Tailwind"], }, @@ -318,6 +341,7 @@ export const CodePanel = (props: { void; isSelected?: boolean; title: string; + description?: string; buttonClass: string; checkClass: string; }; @@ -555,6 +580,7 @@ const SelectableToggle = ({ onSelect, isSelected = false, title, + description, buttonClass, checkClass, }: SelectableToggleProps) => { @@ -565,6 +591,7 @@ const SelectableToggle = ({ return ( ); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b1b9c74c..cbb67d35 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,17 +9,17 @@ importers: .: devDependencies: eslint: - specifier: ^9.1.1 - version: 9.1.1 + specifier: ^9.7.0 + version: 9.7.0 eslint-config-custom: specifier: workspace:* version: link:packages/eslint-config-custom prettier: - specifier: ^3.2.5 - version: 3.2.5 + specifier: ^3.3.3 + version: 3.3.3 turbo: - specifier: ^1.13.3 - version: 1.13.3 + specifier: ^2.0.9 + version: 2.0.9 apps/debug: dependencies: @@ -27,8 +27,8 @@ importers: specifier: workspace:* version: link:../../packages/backend next: - specifier: ^14.2.3 - version: 14.2.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^14.2.5 + version: 14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) plugin-ui: specifier: workspace:* version: link:../../packages/plugin-ui @@ -40,38 +40,38 @@ importers: version: 18.3.1(react@18.3.1) devDependencies: '@types/node': - specifier: ^20.12.8 - version: 20.12.8 + specifier: ^20.14.11 + version: 20.14.11 '@types/react': - specifier: ^18.3.1 - version: 18.3.1 + specifier: ^18.3.3 + version: 18.3.3 '@types/react-dom': specifier: ^18.3.0 version: 18.3.0 autoprefixer: specifier: ^10.4.19 - version: 10.4.19(postcss@8.4.38) + version: 10.4.19(postcss@8.4.39) eslint-config-custom: specifier: workspace:* version: link:../../packages/eslint-config-custom postcss: - specifier: ^8.4.38 - version: 8.4.38 + specifier: ^8.4.39 + version: 8.4.39 tailwindcss: - specifier: 3.4.3 - version: 3.4.3 + specifier: 3.4.6 + version: 3.4.6 tsconfig: specifier: workspace:* version: link:../../packages/tsconfig typescript: - specifier: ^5.4.5 - version: 5.4.5 + specifier: ^5.5.4 + version: 5.5.4 apps/plugin: dependencies: '@figma/plugin-typings': - specifier: ^1.92.0 - version: 1.92.0 + specifier: ^1.97.0 + version: 1.97.0 backend: specifier: workspace:* version: link:../../packages/backend @@ -86,68 +86,68 @@ importers: version: 18.3.1(react@18.3.1) devDependencies: '@types/node': - specifier: ^20.12.8 - version: 20.12.8 + specifier: ^20.14.11 + version: 20.14.11 '@types/react': - specifier: ^18.3.1 - version: 18.3.1 + specifier: ^18.3.3 + version: 18.3.3 '@types/react-dom': specifier: ^18.3.0 version: 18.3.0 '@typescript-eslint/eslint-plugin': - specifier: ^7.8.0 - version: 7.8.0(@typescript-eslint/parser@7.8.0(eslint@9.1.1)(typescript@5.4.5))(eslint@9.1.1)(typescript@5.4.5) + specifier: ^7.17.0 + version: 7.17.0(@typescript-eslint/parser@7.17.0(eslint@9.7.0)(typescript@5.5.4))(eslint@9.7.0)(typescript@5.5.4) '@typescript-eslint/parser': - specifier: ^7.8.0 - version: 7.8.0(eslint@9.1.1)(typescript@5.4.5) + specifier: ^7.17.0 + version: 7.17.0(eslint@9.7.0)(typescript@5.5.4) '@vitejs/plugin-react': - specifier: ^4.2.1 - version: 4.2.1(vite@5.2.10(@types/node@20.12.8)) + specifier: ^4.3.1 + version: 4.3.1(vite@5.3.4(@types/node@20.14.11)) '@vitejs/plugin-react-swc': - specifier: ^3.6.0 - version: 3.6.0(@swc/helpers@0.5.11)(vite@5.2.10(@types/node@20.12.8)) + specifier: ^3.7.0 + version: 3.7.0(@swc/helpers@0.5.12)(vite@5.3.4(@types/node@20.14.11)) autoprefixer: specifier: ^10.4.19 - version: 10.4.19(postcss@8.4.38) + version: 10.4.19(postcss@8.4.39) concurrently: specifier: ^8.2.2 version: 8.2.2 esbuild: - specifier: ^0.20.2 - version: 0.20.2 + specifier: ^0.23.0 + version: 0.23.0 eslint-config-custom: specifier: workspace:* version: link:../../packages/eslint-config-custom eslint-plugin-react-hooks: specifier: ^4.6.2 - version: 4.6.2(eslint@9.1.1) + version: 4.6.2(eslint@9.7.0) eslint-plugin-react-refresh: - specifier: ^0.4.6 - version: 0.4.6(eslint@9.1.1) + specifier: ^0.4.9 + version: 0.4.9(eslint@9.7.0) postcss: - specifier: ^8.4.38 - version: 8.4.38 + specifier: ^8.4.39 + version: 8.4.39 tailwindcss: - specifier: 3.4.3 - version: 3.4.3 + specifier: 3.4.6 + version: 3.4.6 tsconfig: specifier: workspace:* version: link:../../packages/tsconfig typescript: - specifier: ^5.4.5 - version: 5.4.5 + specifier: ^5.5.4 + version: 5.5.4 vite: - specifier: ^5.2.10 - version: 5.2.10(@types/node@20.12.8) + specifier: ^5.3.4 + version: 5.3.4(@types/node@20.14.11) vite-plugin-singlefile: - specifier: ^2.0.1 - version: 2.0.1(rollup@4.17.2)(vite@5.2.10(@types/node@20.12.8)) + specifier: ^2.0.2 + version: 2.0.2(rollup@4.19.0)(vite@5.3.4(@types/node@20.14.11)) packages/backend: dependencies: '@figma/plugin-typings': - specifier: ^1.92.0 - version: 1.92.0 + specifier: ^1.97.0 + version: 1.97.0 react: specifier: 18.3.1 version: 18.3.1 @@ -156,14 +156,14 @@ importers: version: 18.3.1(react@18.3.1) devDependencies: '@types/react': - specifier: ^18.3.1 - version: 18.3.1 + specifier: ^18.3.3 + version: 18.3.3 '@types/react-dom': specifier: ^18.3.0 version: 18.3.0 eslint: - specifier: ^9.1.1 - version: 9.1.1 + specifier: ^9.7.0 + version: 9.7.0 eslint-config-custom: specifier: workspace:* version: link:../eslint-config-custom @@ -171,26 +171,26 @@ importers: specifier: workspace:* version: link:../tsconfig tsup: - specifier: ^8.0.2 - version: 8.0.2(@swc/core@1.4.17(@swc/helpers@0.5.11))(postcss@8.4.38)(typescript@5.4.5) + specifier: ^8.2.2 + version: 8.2.2(@swc/core@1.7.0)(jiti@1.21.6)(postcss@8.4.39)(typescript@5.5.4)(yaml@2.4.5) typescript: - specifier: ^5.4.5 - version: 5.4.5 + specifier: ^5.5.4 + version: 5.5.4 packages/eslint-config-custom: dependencies: eslint-config-next: - specifier: ^14.2.3 - version: 14.2.3(eslint@9.1.1)(typescript@5.4.5) + specifier: ^14.2.5 + version: 14.2.5(eslint@9.7.0)(typescript@5.5.4) eslint-config-prettier: specifier: ^9.1.0 - version: 9.1.0(eslint@9.1.1) + version: 9.1.0(eslint@9.7.0) eslint-config-turbo: - specifier: ^1.13.3 - version: 1.13.3(eslint@9.1.1) + specifier: ^2.0.9 + version: 2.0.9(eslint@9.7.0) eslint-plugin-react: - specifier: 7.34.1 - version: 7.34.1(eslint@9.1.1) + specifier: 7.35.0 + version: 7.35.0(eslint@9.7.0) packages/plugin-ui: dependencies: @@ -204,21 +204,21 @@ importers: specifier: ^15.5.0 version: 15.5.0(react@18.3.1) tailwindcss: - specifier: 3.4.3 - version: 3.4.3 + specifier: 3.4.6 + version: 3.4.6 devDependencies: '@types/react': - specifier: ^18.3.1 - version: 18.3.1 + specifier: ^18.3.3 + version: 18.3.3 '@types/react-dom': specifier: ^18.3.0 version: 18.3.0 '@types/react-syntax-highlighter': - specifier: 15.5.11 - version: 15.5.11 + specifier: 15.5.13 + version: 15.5.13 eslint: - specifier: ^9.1.1 - version: 9.1.1 + specifier: ^9.7.0 + version: 9.7.0 eslint-config-custom: specifier: workspace:* version: link:../eslint-config-custom @@ -226,8 +226,8 @@ importers: specifier: workspace:* version: link:../tsconfig typescript: - specifier: ^5.4.5 - version: 5.4.5 + specifier: ^5.5.4 + version: 5.5.4 packages/tsconfig: {} @@ -241,386 +241,392 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@babel/code-frame@7.24.2': - resolution: {integrity: sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==} + '@babel/code-frame@7.24.7': + resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.24.4': - resolution: {integrity: sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==} + '@babel/compat-data@7.24.9': + resolution: {integrity: sha512-e701mcfApCJqMMueQI0Fb68Amflj83+dvAvHawoBpAz+GDjCIyGHzNwnefjsWJ3xiYAqqiQFoWbspGYBdb2/ng==} engines: {node: '>=6.9.0'} - '@babel/core@7.24.5': - resolution: {integrity: sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA==} + '@babel/core@7.24.9': + resolution: {integrity: sha512-5e3FI4Q3M3Pbr21+5xJwCv6ZT6KmGkI0vw3Tozy5ODAQFTIWe37iT8Cr7Ice2Ntb+M3iSKCEWMB1MBgKrW3whg==} engines: {node: '>=6.9.0'} - '@babel/generator@7.24.5': - resolution: {integrity: sha512-x32i4hEXvr+iI0NEoEfDKzlemF8AmtOP8CcrRaEcpzysWuoEb1KknpcvMsHKPONoKZiDuItklgWhB18xEhr9PA==} + '@babel/generator@7.24.10': + resolution: {integrity: sha512-o9HBZL1G2129luEUlG1hB4N/nlYNWHnpwlND9eOMclRqqu1YDy2sSYVCFUZwl8I1Gxh+QSRrP2vD7EpUmFVXxg==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.23.6': - resolution: {integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==} + '@babel/helper-compilation-targets@7.24.8': + resolution: {integrity: sha512-oU+UoqCHdp+nWVDkpldqIQL/i/bvAv53tRqLG/s+cOXxe66zOYLU7ar/Xs3LdmBihrUMEUhwu6dMZwbNOYDwvw==} engines: {node: '>=6.9.0'} - '@babel/helper-environment-visitor@7.22.20': - resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} + '@babel/helper-environment-visitor@7.24.7': + resolution: {integrity: sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==} engines: {node: '>=6.9.0'} - '@babel/helper-function-name@7.23.0': - resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} + '@babel/helper-function-name@7.24.7': + resolution: {integrity: sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==} engines: {node: '>=6.9.0'} - '@babel/helper-hoist-variables@7.22.5': - resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} + '@babel/helper-hoist-variables@7.24.7': + resolution: {integrity: sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==} engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.24.3': - resolution: {integrity: sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==} + '@babel/helper-module-imports@7.24.7': + resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} engines: {node: '>=6.9.0'} - '@babel/helper-module-transforms@7.24.5': - resolution: {integrity: sha512-9GxeY8c2d2mdQUP1Dye0ks3VDyIMS98kt/llQ2nUId8IsWqTF0l1LkSX0/uP7l7MCDrzXS009Hyhe2gzTiGW8A==} + '@babel/helper-module-transforms@7.24.9': + resolution: {integrity: sha512-oYbh+rtFKj/HwBQkFlUzvcybzklmVdVV3UU+mN7n2t/q3yGHbuVdNxyFvSBO1tfvjyArpHNcWMAzsSPdyI46hw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-plugin-utils@7.24.5': - resolution: {integrity: sha512-xjNLDopRzW2o6ba0gKbkZq5YWEBaK3PCyTOY1K2P/O07LGMhMqlMXPxwN4S5/RhWuCobT8z0jrlKGlYmeR1OhQ==} + '@babel/helper-plugin-utils@7.24.8': + resolution: {integrity: sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==} engines: {node: '>=6.9.0'} - '@babel/helper-simple-access@7.24.5': - resolution: {integrity: sha512-uH3Hmf5q5n7n8mz7arjUlDOCbttY/DW4DYhE6FUsjKJ/oYC1kQQUvwEQWxRwUpX9qQKRXeqLwWxrqilMrf32sQ==} + '@babel/helper-simple-access@7.24.7': + resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==} engines: {node: '>=6.9.0'} - '@babel/helper-split-export-declaration@7.24.5': - resolution: {integrity: sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q==} + '@babel/helper-split-export-declaration@7.24.7': + resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==} engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.24.1': - resolution: {integrity: sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==} + '@babel/helper-string-parser@7.24.8': + resolution: {integrity: sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.24.5': - resolution: {integrity: sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==} + '@babel/helper-validator-identifier@7.24.7': + resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.23.5': - resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==} + '@babel/helper-validator-option@7.24.8': + resolution: {integrity: sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.24.5': - resolution: {integrity: sha512-CiQmBMMpMQHwM5m01YnrM6imUG1ebgYJ+fAIW4FZe6m4qHTPaRHti+R8cggAwkdz4oXhtO4/K9JWlh+8hIfR2Q==} + '@babel/helpers@7.24.8': + resolution: {integrity: sha512-gV2265Nkcz7weJJfvDoAEVzC1e2OTDpkGbEsebse8koXUJUXPsCMi7sRo/+SPMuMZ9MtUPnGwITTnQnU5YjyaQ==} engines: {node: '>=6.9.0'} - '@babel/highlight@7.24.5': - resolution: {integrity: sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==} + '@babel/highlight@7.24.7': + resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} engines: {node: '>=6.9.0'} - '@babel/parser@7.24.5': - resolution: {integrity: sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==} + '@babel/parser@7.24.8': + resolution: {integrity: sha512-WzfbgXOkGzZiXXCqk43kKwZjzwx4oulxZi3nq2TYL9mOjQv6kYwul9mz6ID36njuL7Xkp6nJEfok848Zj10j/w==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/plugin-transform-react-jsx-self@7.24.5': - resolution: {integrity: sha512-RtCJoUO2oYrYwFPtR1/jkoBEcFuI1ae9a9IMxeyAVa3a1Ap4AnxmyIKG2b2FaJKqkidw/0cxRbWN+HOs6ZWd1w==} + '@babel/plugin-transform-react-jsx-self@7.24.7': + resolution: {integrity: sha512-fOPQYbGSgH0HUp4UJO4sMBFjY6DuWq+2i8rixyUMb3CdGixs/gccURvYOAhajBdKDoGajFr3mUq5rH3phtkGzw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-jsx-source@7.24.1': - resolution: {integrity: sha512-1v202n7aUq4uXAieRTKcwPzNyphlCuqHHDcdSNc+vdhoTEZcFMh+L5yZuCmGaIO7bs1nJUNfHB89TZyoL48xNA==} + '@babel/plugin-transform-react-jsx-source@7.24.7': + resolution: {integrity: sha512-J2z+MWzZHVOemyLweMqngXrgGC42jQ//R0KdxqkIz/OrbVIIlhFI3WigZ5fO+nwFvBlncr4MGapd8vTyc7RPNQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/runtime@7.24.5': - resolution: {integrity: sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==} + '@babel/runtime@7.24.8': + resolution: {integrity: sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==} engines: {node: '>=6.9.0'} - '@babel/template@7.24.0': - resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==} + '@babel/template@7.24.7': + resolution: {integrity: sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.24.5': - resolution: {integrity: sha512-7aaBLeDQ4zYcUFDUD41lJc1fG8+5IU9DaNSJAgal866FGvmD5EbWQgnEC6kO1gGLsX0esNkfnJSndbTXA3r7UA==} + '@babel/traverse@7.24.8': + resolution: {integrity: sha512-t0P1xxAPzEDcEPmjprAQq19NWum4K0EQPjMwZQZbHt+GiZqvjCHjj755Weq1YRPVzBI+3zSfvScfpnuIecVFJQ==} engines: {node: '>=6.9.0'} - '@babel/types@7.24.5': - resolution: {integrity: sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==} + '@babel/types@7.24.9': + resolution: {integrity: sha512-xm8XrMKz0IlUdocVbYJe0Z9xEgidU7msskG8BbhnTPK/HZ2z/7FP7ykqPgrUH+C+r414mNfNWam1f2vqOjqjYQ==} engines: {node: '>=6.9.0'} - '@esbuild/aix-ppc64@0.19.12': - resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==} + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} engines: {node: '>=12'} cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.20.2': - resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==} - engines: {node: '>=12'} + '@esbuild/aix-ppc64@0.23.0': + resolution: {integrity: sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ==} + engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.19.12': - resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==} + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} engines: {node: '>=12'} cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.20.2': - resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==} - engines: {node: '>=12'} + '@esbuild/android-arm64@0.23.0': + resolution: {integrity: sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ==} + engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.19.12': - resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==} + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} engines: {node: '>=12'} cpu: [arm] os: [android] - '@esbuild/android-arm@0.20.2': - resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==} - engines: {node: '>=12'} + '@esbuild/android-arm@0.23.0': + resolution: {integrity: sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g==} + engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.19.12': - resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==} + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} engines: {node: '>=12'} cpu: [x64] os: [android] - '@esbuild/android-x64@0.20.2': - resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==} - engines: {node: '>=12'} + '@esbuild/android-x64@0.23.0': + resolution: {integrity: sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ==} + engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.19.12': - resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==} + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} engines: {node: '>=12'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.20.2': - resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==} - engines: {node: '>=12'} + '@esbuild/darwin-arm64@0.23.0': + resolution: {integrity: sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow==} + engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.19.12': - resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==} + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} engines: {node: '>=12'} cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.20.2': - resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==} - engines: {node: '>=12'} + '@esbuild/darwin-x64@0.23.0': + resolution: {integrity: sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ==} + engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.19.12': - resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==} + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} engines: {node: '>=12'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.20.2': - resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==} - engines: {node: '>=12'} + '@esbuild/freebsd-arm64@0.23.0': + resolution: {integrity: sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw==} + engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.19.12': - resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==} + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} engines: {node: '>=12'} cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.20.2': - resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==} - engines: {node: '>=12'} + '@esbuild/freebsd-x64@0.23.0': + resolution: {integrity: sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ==} + engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.19.12': - resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==} + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} engines: {node: '>=12'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.20.2': - resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==} - engines: {node: '>=12'} + '@esbuild/linux-arm64@0.23.0': + resolution: {integrity: sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw==} + engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.19.12': - resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==} + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} engines: {node: '>=12'} cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.20.2': - resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==} - engines: {node: '>=12'} + '@esbuild/linux-arm@0.23.0': + resolution: {integrity: sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw==} + engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.19.12': - resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==} + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} engines: {node: '>=12'} cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.20.2': - resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==} - engines: {node: '>=12'} + '@esbuild/linux-ia32@0.23.0': + resolution: {integrity: sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA==} + engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.19.12': - resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==} + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} engines: {node: '>=12'} cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.20.2': - resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==} - engines: {node: '>=12'} + '@esbuild/linux-loong64@0.23.0': + resolution: {integrity: sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A==} + engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.19.12': - resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==} + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} engines: {node: '>=12'} cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.20.2': - resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==} - engines: {node: '>=12'} + '@esbuild/linux-mips64el@0.23.0': + resolution: {integrity: sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w==} + engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.19.12': - resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==} + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} engines: {node: '>=12'} cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.20.2': - resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==} - engines: {node: '>=12'} + '@esbuild/linux-ppc64@0.23.0': + resolution: {integrity: sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw==} + engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.19.12': - resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==} + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} engines: {node: '>=12'} cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.20.2': - resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==} - engines: {node: '>=12'} + '@esbuild/linux-riscv64@0.23.0': + resolution: {integrity: sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw==} + engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.19.12': - resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==} + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} engines: {node: '>=12'} cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.20.2': - resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==} - engines: {node: '>=12'} + '@esbuild/linux-s390x@0.23.0': + resolution: {integrity: sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg==} + engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.19.12': - resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==} + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} engines: {node: '>=12'} cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.20.2': - resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==} - engines: {node: '>=12'} + '@esbuild/linux-x64@0.23.0': + resolution: {integrity: sha512-a3pMQhUEJkITgAw6e0bWA+F+vFtCciMjW/LPtoj99MhVt+Mfb6bbL9hu2wmTZgNd994qTAEw+U/r6k3qHWWaOQ==} + engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-x64@0.19.12': - resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==} + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} engines: {node: '>=12'} cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.20.2': - resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==} - engines: {node: '>=12'} + '@esbuild/netbsd-x64@0.23.0': + resolution: {integrity: sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw==} + engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-x64@0.19.12': - resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==} + '@esbuild/openbsd-arm64@0.23.0': + resolution: {integrity: sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} engines: {node: '>=12'} cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.20.2': - resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==} - engines: {node: '>=12'} + '@esbuild/openbsd-x64@0.23.0': + resolution: {integrity: sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg==} + engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/sunos-x64@0.19.12': - resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==} + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} engines: {node: '>=12'} cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.20.2': - resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==} - engines: {node: '>=12'} + '@esbuild/sunos-x64@0.23.0': + resolution: {integrity: sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA==} + engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.19.12': - resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==} + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} engines: {node: '>=12'} cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.20.2': - resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==} - engines: {node: '>=12'} + '@esbuild/win32-arm64@0.23.0': + resolution: {integrity: sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==} + engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.19.12': - resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==} + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} engines: {node: '>=12'} cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.20.2': - resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==} - engines: {node: '>=12'} + '@esbuild/win32-ia32@0.23.0': + resolution: {integrity: sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==} + engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.19.12': - resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==} + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} engines: {node: '>=12'} cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.20.2': - resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==} - engines: {node: '>=12'} + '@esbuild/win32-x64@0.23.0': + resolution: {integrity: sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g==} + engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -630,34 +636,35 @@ packages: peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - '@eslint-community/regexpp@4.10.0': - resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} + '@eslint-community/regexpp@4.11.0': + resolution: {integrity: sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/eslintrc@3.0.2': - resolution: {integrity: sha512-wV19ZEGEMAC1eHgrS7UQPqsdEiCIbTKTasEfcXAigzoXICcqZSjBZEHlZwNVvKg6UBCjSlos84XiLqsRJnIcIg==} + '@eslint/config-array@0.17.1': + resolution: {integrity: sha512-BlYOpej8AQ8Ev9xVqroV7a02JK3SkBAaN9GfMMH9W6Ch8FlQlkjGw4Ir7+FgYwfirivAf4t+GtzuAxqfukmISA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.1.0': + resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.1.1': - resolution: {integrity: sha512-5WoDz3Y19Bg2BnErkZTp0en+c/i9PvgFS7MBe1+m60HjFr0hrphlAGp4yzI7pxpt4xShln4ZyYp4neJm8hmOkQ==} + '@eslint/js@9.7.0': + resolution: {integrity: sha512-ChuWDQenef8OSFnvuxv0TCVxEwmu3+hPNKvM9B34qpM0rDRbjL8t5QkQeHHeAfsKQjuH9wS82WeCi1J/owatng==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@figma/plugin-typings@1.92.0': - resolution: {integrity: sha512-W02/uSt+n33Iro0/Goj+Dl1Fp+1i9+jjciw//BcBh0VOz2QzuiaurhftnboO8gKucOloBOROMbxe/Vylu4ItZw==} + '@eslint/object-schema@2.1.4': + resolution: {integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@humanwhocodes/config-array@0.13.0': - resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} - engines: {node: '>=10.10.0'} + '@figma/plugin-typings@1.97.0': + resolution: {integrity: sha512-AcmZey7TBbc43g2dO+9hjrcTbgb0UFY32do3to3rFU1OXb9hinsrmmbddyhD5105DHzRDac4oT7A5+VOow7p1Q==} '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} - '@humanwhocodes/object-schema@2.0.3': - resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} - - '@humanwhocodes/retry@0.2.3': - resolution: {integrity: sha512-X38nUbachlb01YMlvPFojKoiXq+LzZvuSce70KPMPdeM1Rj03k4dR7lDslhbqXn3Ang4EU3+EAmwEAsbrjHW3g==} + '@humanwhocodes/retry@0.3.0': + resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==} engines: {node: '>=18.18'} '@isaacs/cliui@8.0.2': @@ -676,68 +683,68 @@ packages: resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} engines: {node: '>=6.0.0'} - '@jridgewell/sourcemap-codec@1.4.15': - resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} - '@next/env@14.2.3': - resolution: {integrity: sha512-W7fd7IbkfmeeY2gXrzJYDx8D2lWKbVoTIj1o1ScPHNzvp30s1AuoEFSdr39bC5sjxJaxTtq3OTCZboNp0lNWHA==} + '@next/env@14.2.5': + resolution: {integrity: sha512-/zZGkrTOsraVfYjGP8uM0p6r0BDT6xWpkjdVbcz66PJVSpwXX3yNiRycxAuDfBKGWBrZBXRuK/YVlkNgxHGwmA==} - '@next/eslint-plugin-next@14.2.3': - resolution: {integrity: sha512-L3oDricIIjgj1AVnRdRor21gI7mShlSwU/1ZGHmqM3LzHhXXhdkrfeNY5zif25Bi5Dd7fiJHsbhoZCHfXYvlAw==} + '@next/eslint-plugin-next@14.2.5': + resolution: {integrity: sha512-LY3btOpPh+OTIpviNojDpUdIbHW9j0JBYBjsIp8IxtDFfYFyORvw3yNq6N231FVqQA7n7lwaf7xHbVJlA1ED7g==} - '@next/swc-darwin-arm64@14.2.3': - resolution: {integrity: sha512-3pEYo/RaGqPP0YzwnlmPN2puaF2WMLM3apt5jLW2fFdXD9+pqcoTzRk+iZsf8ta7+quAe4Q6Ms0nR0SFGFdS1A==} + '@next/swc-darwin-arm64@14.2.5': + resolution: {integrity: sha512-/9zVxJ+K9lrzSGli1///ujyRfon/ZneeZ+v4ptpiPoOU+GKZnm8Wj8ELWU1Pm7GHltYRBklmXMTUqM/DqQ99FQ==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@14.2.3': - resolution: {integrity: sha512-6adp7waE6P1TYFSXpY366xwsOnEXM+y1kgRpjSRVI2CBDOcbRjsJ67Z6EgKIqWIue52d2q/Mx8g9MszARj8IEA==} + '@next/swc-darwin-x64@14.2.5': + resolution: {integrity: sha512-vXHOPCwfDe9qLDuq7U1OYM2wUY+KQ4Ex6ozwsKxp26BlJ6XXbHleOUldenM67JRyBfVjv371oneEvYd3H2gNSA==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@14.2.3': - resolution: {integrity: sha512-cuzCE/1G0ZSnTAHJPUT1rPgQx1w5tzSX7POXSLaS7w2nIUJUD+e25QoXD/hMfxbsT9rslEXugWypJMILBj/QsA==} + '@next/swc-linux-arm64-gnu@14.2.5': + resolution: {integrity: sha512-vlhB8wI+lj8q1ExFW8lbWutA4M2ZazQNvMWuEDqZcuJJc78iUnLdPPunBPX8rC4IgT6lIx/adB+Cwrl99MzNaA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-arm64-musl@14.2.3': - resolution: {integrity: sha512-0D4/oMM2Y9Ta3nGuCcQN8jjJjmDPYpHX9OJzqk42NZGJocU2MqhBq5tWkJrUQOQY9N+In9xOdymzapM09GeiZw==} + '@next/swc-linux-arm64-musl@14.2.5': + resolution: {integrity: sha512-NpDB9NUR2t0hXzJJwQSGu1IAOYybsfeB+LxpGsXrRIb7QOrYmidJz3shzY8cM6+rO4Aojuef0N/PEaX18pi9OA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-x64-gnu@14.2.3': - resolution: {integrity: sha512-ENPiNnBNDInBLyUU5ii8PMQh+4XLr4pG51tOp6aJ9xqFQ2iRI6IH0Ds2yJkAzNV1CfyagcyzPfROMViS2wOZ9w==} + '@next/swc-linux-x64-gnu@14.2.5': + resolution: {integrity: sha512-8XFikMSxWleYNryWIjiCX+gU201YS+erTUidKdyOVYi5qUQo/gRxv/3N1oZFCgqpesN6FPeqGM72Zve+nReVXQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-linux-x64-musl@14.2.3': - resolution: {integrity: sha512-BTAbq0LnCbF5MtoM7I/9UeUu/8ZBY0i8SFjUMCbPDOLv+un67e2JgyN4pmgfXBwy/I+RHu8q+k+MCkDN6P9ViQ==} + '@next/swc-linux-x64-musl@14.2.5': + resolution: {integrity: sha512-6QLwi7RaYiQDcRDSU/os40r5o06b5ue7Jsk5JgdRBGGp8l37RZEh9JsLSM8QF0YDsgcosSeHjglgqi25+m04IQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-win32-arm64-msvc@14.2.3': - resolution: {integrity: sha512-AEHIw/dhAMLNFJFJIJIyOFDzrzI5bAjI9J26gbO5xhAKHYTZ9Or04BesFPXiAYXDNdrwTP2dQceYA4dL1geu8A==} + '@next/swc-win32-arm64-msvc@14.2.5': + resolution: {integrity: sha512-1GpG2VhbspO+aYoMOQPQiqc/tG3LzmsdBH0LhnDS3JrtDx2QmzXe0B6mSZZiN3Bq7IOMXxv1nlsjzoS1+9mzZw==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-ia32-msvc@14.2.3': - resolution: {integrity: sha512-vga40n1q6aYb0CLrM+eEmisfKCR45ixQYXuBXxOOmmoV8sYST9k7E3US32FsY+CkkF7NtzdcebiFT4CHuMSyZw==} + '@next/swc-win32-ia32-msvc@14.2.5': + resolution: {integrity: sha512-Igh9ZlxwvCDsu6438FXlQTHlRno4gFpJzqPjSIBZooD22tKeI4fE/YMRoHVJHmrQ2P5YL1DoZ0qaOKkbeFWeMg==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] - '@next/swc-win32-x64-msvc@14.2.3': - resolution: {integrity: sha512-Q1/zm43RWynxrO7lW4ehciQVj+5ePBhOK+/K2P7pLFX3JaJ/IZVC69SHidrmZSOkqz7ECIOhhy7XhAFG4JYyHA==} + '@next/swc-win32-x64-msvc@14.2.5': + resolution: {integrity: sha512-tEQ7oinq1/CjSG9uSTerca3v4AZ+dFa+4Yu6ihaG8Ud8ddqLQgFGcnwYls13H5X5CPDPZJdYxyeMui6muOLd4g==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -758,154 +765,154 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@rollup/rollup-android-arm-eabi@4.17.2': - resolution: {integrity: sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==} + '@rollup/rollup-android-arm-eabi@4.19.0': + resolution: {integrity: sha512-JlPfZ/C7yn5S5p0yKk7uhHTTnFlvTgLetl2VxqE518QgyM7C9bSfFTYvB/Q/ftkq0RIPY4ySxTz+/wKJ/dXC0w==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.17.2': - resolution: {integrity: sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==} + '@rollup/rollup-android-arm64@4.19.0': + resolution: {integrity: sha512-RDxUSY8D1tWYfn00DDi5myxKgOk6RvWPxhmWexcICt/MEC6yEMr4HNCu1sXXYLw8iAsg0D44NuU+qNq7zVWCrw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.17.2': - resolution: {integrity: sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==} + '@rollup/rollup-darwin-arm64@4.19.0': + resolution: {integrity: sha512-emvKHL4B15x6nlNTBMtIaC9tLPRpeA5jMvRLXVbl/W9Ie7HhkrE7KQjvgS9uxgatL1HmHWDXk5TTS4IaNJxbAA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.17.2': - resolution: {integrity: sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==} + '@rollup/rollup-darwin-x64@4.19.0': + resolution: {integrity: sha512-fO28cWA1dC57qCd+D0rfLC4VPbh6EOJXrreBmFLWPGI9dpMlER2YwSPZzSGfq11XgcEpPukPTfEVFtw2q2nYJg==} cpu: [x64] os: [darwin] - '@rollup/rollup-linux-arm-gnueabihf@4.17.2': - resolution: {integrity: sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==} + '@rollup/rollup-linux-arm-gnueabihf@4.19.0': + resolution: {integrity: sha512-2Rn36Ubxdv32NUcfm0wB1tgKqkQuft00PtM23VqLuCUR4N5jcNWDoV5iBC9jeGdgS38WK66ElncprqgMUOyomw==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.17.2': - resolution: {integrity: sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==} + '@rollup/rollup-linux-arm-musleabihf@4.19.0': + resolution: {integrity: sha512-gJuzIVdq/X1ZA2bHeCGCISe0VWqCoNT8BvkQ+BfsixXwTOndhtLUpOg0A1Fcx/+eA6ei6rMBzlOz4JzmiDw7JQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.17.2': - resolution: {integrity: sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==} + '@rollup/rollup-linux-arm64-gnu@4.19.0': + resolution: {integrity: sha512-0EkX2HYPkSADo9cfeGFoQ7R0/wTKb7q6DdwI4Yn/ULFE1wuRRCHybxpl2goQrx4c/yzK3I8OlgtBu4xvted0ug==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.17.2': - resolution: {integrity: sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==} + '@rollup/rollup-linux-arm64-musl@4.19.0': + resolution: {integrity: sha512-GlIQRj9px52ISomIOEUq/IojLZqzkvRpdP3cLgIE1wUWaiU5Takwlzpz002q0Nxxr1y2ZgxC2obWxjr13lvxNQ==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.17.2': - resolution: {integrity: sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==} + '@rollup/rollup-linux-powerpc64le-gnu@4.19.0': + resolution: {integrity: sha512-N6cFJzssruDLUOKfEKeovCKiHcdwVYOT1Hs6dovDQ61+Y9n3Ek4zXvtghPPelt6U0AH4aDGnDLb83uiJMkWYzQ==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.17.2': - resolution: {integrity: sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==} + '@rollup/rollup-linux-riscv64-gnu@4.19.0': + resolution: {integrity: sha512-2DnD3mkS2uuam/alF+I7M84koGwvn3ZVD7uG+LEWpyzo/bq8+kKnus2EVCkcvh6PlNB8QPNFOz6fWd5N8o1CYg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.17.2': - resolution: {integrity: sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==} + '@rollup/rollup-linux-s390x-gnu@4.19.0': + resolution: {integrity: sha512-D6pkaF7OpE7lzlTOFCB2m3Ngzu2ykw40Nka9WmKGUOTS3xcIieHe82slQlNq69sVB04ch73thKYIWz/Ian8DUA==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.17.2': - resolution: {integrity: sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==} + '@rollup/rollup-linux-x64-gnu@4.19.0': + resolution: {integrity: sha512-HBndjQLP8OsdJNSxpNIN0einbDmRFg9+UQeZV1eiYupIRuZsDEoeGU43NQsS34Pp166DtwQOnpcbV/zQxM+rWA==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.17.2': - resolution: {integrity: sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==} + '@rollup/rollup-linux-x64-musl@4.19.0': + resolution: {integrity: sha512-HxfbvfCKJe/RMYJJn0a12eiOI9OOtAUF4G6ozrFUK95BNyoJaSiBjIOHjZskTUffUrB84IPKkFG9H9nEvJGW6A==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.17.2': - resolution: {integrity: sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==} + '@rollup/rollup-win32-arm64-msvc@4.19.0': + resolution: {integrity: sha512-HxDMKIhmcguGTiP5TsLNolwBUK3nGGUEoV/BO9ldUBoMLBssvh4J0X8pf11i1fTV7WShWItB1bKAKjX4RQeYmg==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.17.2': - resolution: {integrity: sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==} + '@rollup/rollup-win32-ia32-msvc@4.19.0': + resolution: {integrity: sha512-xItlIAZZaiG/u0wooGzRsx11rokP4qyc/79LkAOdznGRAbOFc+SfEdfUOszG1odsHNgwippUJavag/+W/Etc6Q==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.17.2': - resolution: {integrity: sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==} + '@rollup/rollup-win32-x64-msvc@4.19.0': + resolution: {integrity: sha512-xNo5fV5ycvCCKqiZcpB65VMR11NJB+StnxHz20jdqRAktfdfzhgjTiJ2doTDQE/7dqGaV5I7ZGqKpgph6lCIag==} cpu: [x64] os: [win32] - '@rushstack/eslint-patch@1.10.2': - resolution: {integrity: sha512-hw437iINopmQuxWPSUEvqE56NCPsiU8N4AYtfHmJFckclktzK9YQJieD3XkDCDH4OjL+C7zgPUh73R/nrcHrqw==} + '@rushstack/eslint-patch@1.10.3': + resolution: {integrity: sha512-qC/xYId4NMebE6w/V33Fh9gWxLgURiNYgVNObbJl2LZv0GUUItCcCqC5axQSwRaAgaxl2mELq1rMzlswaQ0Zxg==} - '@swc/core-darwin-arm64@1.4.17': - resolution: {integrity: sha512-HVl+W4LezoqHBAYg2JCqR+s9ife9yPfgWSj37iIawLWzOmuuJ7jVdIB7Ee2B75bEisSEKyxRlTl6Y1Oq3owBgw==} + '@swc/core-darwin-arm64@1.7.0': + resolution: {integrity: sha512-2ylhM7f0HwUwLrFYZAe/dse8PCbPsYcJS3Dt7Q8NT3PUn7vy6QOMxNcOPPuDrnmaXqQQO3oxdmRapguTxaat9g==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] - '@swc/core-darwin-x64@1.4.17': - resolution: {integrity: sha512-WYRO9Fdzq4S/he8zjW5I95G1zcvyd9yyD3Tgi4/ic84P5XDlSMpBDpBLbr/dCPjmSg7aUXxNQqKqGkl6dQxYlA==} + '@swc/core-darwin-x64@1.7.0': + resolution: {integrity: sha512-SgVnN4gT1Rb9YfTkp4FCUITqSs7Yj0uB2SUciu5CV3HuGvS5YXCUzh+KrwpLFtx8NIgivISKcNnb41mJi98X8Q==} engines: {node: '>=10'} cpu: [x64] os: [darwin] - '@swc/core-linux-arm-gnueabihf@1.4.17': - resolution: {integrity: sha512-cgbvpWOvtMH0XFjvwppUCR+Y+nf6QPaGu6AQ5hqCP+5Lv2zO5PG0RfasC4zBIjF53xgwEaaWmGP5/361P30X8Q==} + '@swc/core-linux-arm-gnueabihf@1.7.0': + resolution: {integrity: sha512-+Z9Dayart1iKJQEJJ9N/KS4z5EdXJE3WPFikY0jonKTo4Dd8RuyVz5yLvqcIMeVdz/SwximATaL6iJXw7hZS9A==} engines: {node: '>=10'} cpu: [arm] os: [linux] - '@swc/core-linux-arm64-gnu@1.4.17': - resolution: {integrity: sha512-l7zHgaIY24cF9dyQ/FOWbmZDsEj2a9gRFbmgx2u19e3FzOPuOnaopFj0fRYXXKCmtdx+anD750iBIYnTR+pq/Q==} + '@swc/core-linux-arm64-gnu@1.7.0': + resolution: {integrity: sha512-UnLrCiZ1EI4shznJn0xP6DLgsXUSwtfsdgHhGYCrvbgVBBve3S9iFgVFEB3SPl7Q/TdowNbrN4zHU0oChfiNfw==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-arm64-musl@1.4.17': - resolution: {integrity: sha512-qhH4gr9gAlVk8MBtzXbzTP3BJyqbAfUOATGkyUtohh85fPXQYuzVlbExix3FZXTwFHNidGHY8C+ocscI7uDaYw==} + '@swc/core-linux-arm64-musl@1.7.0': + resolution: {integrity: sha512-H724UANA+ptsfwKRr9mnaDa9cb5fw0oFysiGKTgb3DMYcgk3Od0jMTnXVPFSVpo7FlmyxeC9K8ueUPBOoOK6XA==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-x64-gnu@1.4.17': - resolution: {integrity: sha512-vRDFATL1oN5oZMImkwbgSHEkp8xG1ofEASBypze01W1Tqto8t+yo6gsp69wzCZBlxldsvPpvFZW55Jq0Rn+UnA==} + '@swc/core-linux-x64-gnu@1.7.0': + resolution: {integrity: sha512-SY3HA0K0Dpqt1HIfMLGpwL4hd4UaL2xHP5oZXPlRQPhUDZrbb4PbI3ZJnh66c63eL4ZR8EJ+HRFI0Alx5p69Zw==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-linux-x64-musl@1.4.17': - resolution: {integrity: sha512-zQNPXAXn3nmPqv54JVEN8k2JMEcMTQ6veVuU0p5O+A7KscJq+AGle/7ZQXzpXSfUCXlLMX4wvd+rwfGhh3J4cw==} + '@swc/core-linux-x64-musl@1.7.0': + resolution: {integrity: sha512-cEJ2ebtV1v/5Ilb55E05J6F5SrHKQWzUttIhR5Mkayyo+yvPslcpByuFC3D+J7X1ebziTOBpWuMpUdjLfh3SMQ==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-win32-arm64-msvc@1.4.17': - resolution: {integrity: sha512-z86n7EhOwyzxwm+DLE5NoLkxCTme2lq7QZlDjbQyfCxOt6isWz8rkW5QowTX8w9Rdmk34ncrjSLvnHOeLY17+w==} + '@swc/core-win32-arm64-msvc@1.7.0': + resolution: {integrity: sha512-ecQOOmzEssz+m0pR4xDYCGuvn3E/l0nQ3tk5jp1NA1lsAy4bMV0YbYCHjptYvWL/UjhIerIp3IlCJ8x5DodSog==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - '@swc/core-win32-ia32-msvc@1.4.17': - resolution: {integrity: sha512-JBwuSTJIgiJJX6wtr4wmXbfvOswHFj223AumUrK544QV69k60FJ9q2adPW9Csk+a8wm1hLxq4HKa2K334UHJ/g==} + '@swc/core-win32-ia32-msvc@1.7.0': + resolution: {integrity: sha512-gz81seZkRn3zMnVOc7L5k6F4vQC82gIxmHiL+GedK+A37XI/X26AASU3zxvORnqQbwQYXQ+AEVckxBmFlz3v2g==} engines: {node: '>=10'} cpu: [ia32] os: [win32] - '@swc/core-win32-x64-msvc@1.4.17': - resolution: {integrity: sha512-jFkOnGQamtVDBm3MF5Kq1lgW8vx4Rm1UvJWRUfg+0gx7Uc3Jp3QMFeMNw/rDNQYRDYPG3yunCC+2463ycd5+dg==} + '@swc/core-win32-x64-msvc@1.7.0': + resolution: {integrity: sha512-b5Fd1xEOw9uqBpj2lqsaR4Iq9UhiL84hNDcEsi6DQA7Y1l85waQAslTbS0E4/pJ1PISAs0jW0zIGLco1eaWBOg==} engines: {node: '>=10'} cpu: [x64] os: [win32] - '@swc/core@1.4.17': - resolution: {integrity: sha512-tq+mdWvodMBNBBZbwFIMTVGYHe9N7zvEaycVVjfvAx20k1XozHbHhRv+9pEVFJjwRxLdXmtvFZd3QZHRAOpoNQ==} + '@swc/core@1.7.0': + resolution: {integrity: sha512-d4vMzH6ICllDwlPuhset2h8gu/USHdbyfJim+2hQEdxC0UONtfpmu38XBgNqRjStrji1Q5M10jfeUZL3cu1i8g==} engines: {node: '>=10'} peerDependencies: - '@swc/helpers': ^0.5.0 + '@swc/helpers': '*' peerDependenciesMeta: '@swc/helpers': optional: true @@ -913,14 +920,14 @@ packages: '@swc/counter@0.1.3': resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} - '@swc/helpers@0.5.11': - resolution: {integrity: sha512-YNlnKRWF2sVojTpIyzwou9XoTNbzbzONwRhOoniEioF1AtaitTvVZblaQRrAzChWQ1bLYyYSWzM18y4WwgzJ+A==} + '@swc/helpers@0.5.12': + resolution: {integrity: sha512-KMZNXiGibsW9kvZAO1Pam2JPTDBm+KSHMMHWdsyI/1DbIZjT2A6Gy3hblVXUMEDvUAKq+e0vL0X0o54owWji7g==} '@swc/helpers@0.5.5': resolution: {integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==} - '@swc/types@0.1.6': - resolution: {integrity: sha512-/JLo/l2JsT/LRd80C3HfbmVpxOAJ11FO2RCEslFrgzLltoP9j8XIbsyDcfCt2WWyX+CM96rBoNM+IToAkFOugg==} + '@swc/types@0.1.12': + resolution: {integrity: sha512-wBJA+SdtkbFhHjTMYH+dEH1y4VpfGdAc2Kw/LK09i9bXd/K6j6PkDcFCEzb6iVfZMkPRrl/q0e3toqTAJdkIVA==} '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -931,8 +938,8 @@ packages: '@types/babel__template@7.4.4': resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} - '@types/babel__traverse@7.20.5': - resolution: {integrity: sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==} + '@types/babel__traverse@7.20.6': + resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} '@types/estree@1.0.5': resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} @@ -940,14 +947,11 @@ packages: '@types/hast@2.3.10': resolution: {integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==} - '@types/json-schema@7.0.15': - resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} - '@types/node@20.12.8': - resolution: {integrity: sha512-NU0rJLJnshZWdE/097cdCBbyW1h4hEg0xpovcoAQYHl8dnEyp/NAOiE45pvc+Bd1Dt+2r94v2eGFpQJ4R7g+2w==} + '@types/node@20.14.11': + resolution: {integrity: sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==} '@types/prop-types@15.7.12': resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} @@ -955,20 +959,17 @@ packages: '@types/react-dom@18.3.0': resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==} - '@types/react-syntax-highlighter@15.5.11': - resolution: {integrity: sha512-ZqIJl+Pg8kD+47kxUjvrlElrraSUrYa4h0dauY/U/FTUuprSCqvUj+9PNQNQzVc6AJgIWUUxn87/gqsMHNbRjw==} + '@types/react-syntax-highlighter@15.5.13': + resolution: {integrity: sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA==} - '@types/react@18.3.1': - resolution: {integrity: sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==} - - '@types/semver@7.5.8': - resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} + '@types/react@18.3.3': + resolution: {integrity: sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==} '@types/unist@2.0.10': resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==} - '@typescript-eslint/eslint-plugin@7.8.0': - resolution: {integrity: sha512-gFTT+ezJmkwutUPmB0skOj3GZJtlEGnlssems4AjkVweUPGj7jRwwqg0Hhg7++kPGJqKtTYx+R05Ftww372aIg==} + '@typescript-eslint/eslint-plugin@7.17.0': + resolution: {integrity: sha512-pyiDhEuLM3PuANxH7uNYan1AaFs5XE0zw1hq69JBvGvE7gSuEoQl1ydtEe/XQeoC3GQxLXyOVa5kNOATgM638A==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: '@typescript-eslint/parser': ^7.0.0 @@ -978,9 +979,9 @@ packages: typescript: optional: true - '@typescript-eslint/parser@7.2.0': - resolution: {integrity: sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg==} - engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/parser@7.17.0': + resolution: {integrity: sha512-puiYfGeg5Ydop8eusb/Hy1k7QmOU6X3nvsqCgzrB2K4qMavK//21+PzNE8qeECgNOIoertJPUC1SpegHDI515A==} + engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: eslint: ^8.56.0 typescript: '*' @@ -988,9 +989,9 @@ packages: typescript: optional: true - '@typescript-eslint/parser@7.8.0': - resolution: {integrity: sha512-KgKQly1pv0l4ltcftP59uQZCi4HUYswCLbTqVZEJu7uLX8CTLyswqMLqLN+2QFz4jCptqWVV4SB7vdxcH2+0kQ==} - engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/parser@7.2.0': + resolution: {integrity: sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg==} + engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^8.56.0 typescript: '*' @@ -998,16 +999,16 @@ packages: typescript: optional: true + '@typescript-eslint/scope-manager@7.17.0': + resolution: {integrity: sha512-0P2jTTqyxWp9HiKLu/Vemr2Rg1Xb5B7uHItdVZ6iAenXmPo4SZ86yOPCJwMqpCyaMiEHTNqizHfsbmCFT1x9SA==} + engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/scope-manager@7.2.0': resolution: {integrity: sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==} engines: {node: ^16.0.0 || >=18.0.0} - '@typescript-eslint/scope-manager@7.8.0': - resolution: {integrity: sha512-viEmZ1LmwsGcnr85gIq+FCYI7nO90DVbE37/ll51hjv9aG+YZMb4WDE2fyWpUR4O/UrhGRpYXK/XajcGTk2B8g==} - engines: {node: ^18.18.0 || >=20.0.0} - - '@typescript-eslint/type-utils@7.8.0': - resolution: {integrity: sha512-H70R3AefQDQpz9mGv13Uhi121FNMh+WEaRqcXTX09YEDky21km4dV1ZXJIp8QjXc4ZaVkXVdohvWDzbnbHDS+A==} + '@typescript-eslint/type-utils@7.17.0': + resolution: {integrity: sha512-XD3aaBt+orgkM/7Cei0XNEm1vwUxQ958AOLALzPlbPqb8C1G8PZK85tND7Jpe69Wualri81PLU+Zc48GVKIMMA==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: eslint: ^8.56.0 @@ -1016,53 +1017,53 @@ packages: typescript: optional: true + '@typescript-eslint/types@7.17.0': + resolution: {integrity: sha512-a29Ir0EbyKTKHnZWbNsrc/gqfIBqYPwj3F2M+jWE/9bqfEHg0AMtXzkbUkOG6QgEScxh2+Pz9OXe11jHDnHR7A==} + engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/types@7.2.0': resolution: {integrity: sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==} engines: {node: ^16.0.0 || >=18.0.0} - '@typescript-eslint/types@7.8.0': - resolution: {integrity: sha512-wf0peJ+ZGlcH+2ZS23aJbOv+ztjeeP8uQ9GgwMJGVLx/Nj9CJt17GWgWWoSmoRVKAX2X+7fzEnAjxdvK2gqCLw==} + '@typescript-eslint/typescript-estree@7.17.0': + resolution: {integrity: sha512-72I3TGq93t2GoSBWI093wmKo0n6/b7O4j9o8U+f65TVD0FS6bI2180X5eGEr8MA8PhKMvYe9myZJquUT2JkCZw==} engines: {node: ^18.18.0 || >=20.0.0} - - '@typescript-eslint/typescript-estree@7.2.0': - resolution: {integrity: sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==} - engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: typescript: '*' peerDependenciesMeta: typescript: optional: true - '@typescript-eslint/typescript-estree@7.8.0': - resolution: {integrity: sha512-5pfUCOwK5yjPaJQNy44prjCwtr981dO8Qo9J9PwYXZ0MosgAbfEMB008dJ5sNo3+/BN6ytBPuSvXUg9SAqB0dg==} - engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/typescript-estree@7.2.0': + resolution: {integrity: sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==} + engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: typescript: '*' peerDependenciesMeta: typescript: optional: true - '@typescript-eslint/utils@7.8.0': - resolution: {integrity: sha512-L0yFqOCflVqXxiZyXrDr80lnahQfSOfc9ELAAZ75sqicqp2i36kEZZGuUymHNFoYOqxRT05up760b4iGsl02nQ==} + '@typescript-eslint/utils@7.17.0': + resolution: {integrity: sha512-r+JFlm5NdB+JXc7aWWZ3fKSm1gn0pkswEwIYsrGPdsT2GjsRATAKXiNtp3vgAAO1xZhX8alIOEQnNMl3kbTgJw==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: eslint: ^8.56.0 + '@typescript-eslint/visitor-keys@7.17.0': + resolution: {integrity: sha512-RVGC9UhPOCsfCdI9pU++K4nD7to+jTcMIbXTSOcrLqUEW6gF2pU1UUbYJKc9cvcRSK1UDeMJ7pdMxf4bhMpV/A==} + engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/visitor-keys@7.2.0': resolution: {integrity: sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==} engines: {node: ^16.0.0 || >=18.0.0} - '@typescript-eslint/visitor-keys@7.8.0': - resolution: {integrity: sha512-q4/gibTNBQNA0lGyYQCmWRS5D15n8rXh4QjK3KV+MBPlTYHpfBUT3D3PaPR/HeNiI9W6R7FvlkcGhNyAoP+caA==} - engines: {node: ^18.18.0 || >=20.0.0} - - '@vitejs/plugin-react-swc@3.6.0': - resolution: {integrity: sha512-XFRbsGgpGxGzEV5i5+vRiro1bwcIaZDIdBRP16qwm+jP68ue/S8FJTBEgOeojtVDYrbSua3XFp71kC8VJE6v+g==} + '@vitejs/plugin-react-swc@3.7.0': + resolution: {integrity: sha512-yrknSb3Dci6svCd/qhHqhFPDSw0QtjumcqdKMoNNzmOl5lMXTTiqzjWtG4Qask2HdvvzaNgSunbQGet8/GrKdA==} peerDependencies: vite: ^4 || ^5 - '@vitejs/plugin-react@4.2.1': - resolution: {integrity: sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ==} + '@vitejs/plugin-react@4.3.1': + resolution: {integrity: sha512-m/V2syj5CuVnaxcUJOQRel/Wr31FFXRFlnOoq1TVtkCxsY5veGMTEmpWHndrhB2U8ScHtCQB1e+4hWYExQc6Lg==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: vite: ^4.2.0 || ^5.0.0 @@ -1072,8 +1073,8 @@ packages: peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - acorn@8.11.3: - resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} + acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} engines: {node: '>=0.4.0'} hasBin: true @@ -1113,8 +1114,8 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - aria-query@5.3.0: - resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + aria-query@5.1.3: + resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==} array-buffer-byte-length@1.0.1: resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} @@ -1144,11 +1145,9 @@ packages: resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} engines: {node: '>= 0.4'} - array.prototype.toreversed@1.1.2: - resolution: {integrity: sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==} - - array.prototype.tosorted@1.1.3: - resolution: {integrity: sha512-/DdH4TiTmOKzyQbp/eadcCVexiCb36xJg7HshYOYJnNZFDj33GEv0P7GxsynpShhq4OLYJzbGcBDkLsDt7MnNg==} + array.prototype.tosorted@1.1.4: + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} arraybuffer.prototype.slice@1.0.3: resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} @@ -1168,12 +1167,12 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - axe-core@4.7.0: - resolution: {integrity: sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==} + axe-core@4.9.1: + resolution: {integrity: sha512-QbUdXJVTpvUTHU7871ppZkdOLBeGUKBQWHkHrvN2V9IQWGMt61zf3B45BtzjxEJzYuj0JBjBZP/hmYS/R9pmAw==} engines: {node: '>=4'} - axobject-query@3.2.1: - resolution: {integrity: sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==} + axobject-query@3.1.1: + resolution: {integrity: sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==} balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -1188,20 +1187,20 @@ packages: brace-expansion@2.0.1: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} - braces@3.0.2: - resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.23.0: - resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==} + browserslist@4.23.2: + resolution: {integrity: sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - bundle-require@4.0.3: - resolution: {integrity: sha512-2iscZ3fcthP2vka4Y7j277YJevwmsby/FpFDwjgw34Nl7dtCpt7zz/4TexmHMzY6KZEih7En9ImlbbgUNNQGtA==} + bundle-require@5.0.0: + resolution: {integrity: sha512-GuziW3fSSmopcx4KRymQEJVbZUfqlCqcq7dvs6TYwKRZiegK/2buMxQTPs6MGlNv50wms1699qYO54R8XfRX4w==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} peerDependencies: - esbuild: '>=0.17' + esbuild: '>=0.18' busboy@1.6.0: resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} @@ -1223,8 +1222,8 @@ packages: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} - caniuse-lite@1.0.30001614: - resolution: {integrity: sha512-jmZQ1VpmlRwHgdP1/uiKzgiAuGOfLEJsYFP4+GBou/QQ4U6IOJCB4NP1c+1p9RGLpwObcT94jA5/uO+F1vBbog==} + caniuse-lite@1.0.30001643: + resolution: {integrity: sha512-ERgWGNleEilSrHM6iUz/zJNSQTP8Mr21wDWpdgvRwcTXGAq6jMtOUPP4dqFPTdKqZ2wKTdtB+uucZ3MRpAUSmg==} chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} @@ -1282,6 +1281,10 @@ packages: engines: {node: ^14.13.0 || >=16.0.0} hasBin: true + consola@3.2.3: + resolution: {integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==} + engines: {node: ^14.18.0 || >=16.10.0} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -1327,8 +1330,8 @@ packages: supports-color: optional: true - debug@4.3.4: - resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + debug@4.3.5: + resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -1336,6 +1339,10 @@ packages: supports-color: optional: true + deep-equal@2.2.3: + resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==} + engines: {node: '>= 0.4'} + deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -1347,10 +1354,6 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} - dequal@2.0.3: - resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} - engines: {node: '>=6'} - didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} @@ -1372,8 +1375,8 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - electron-to-chromium@1.4.753: - resolution: {integrity: sha512-Wn1XKa0Lc5kMe5UIwQc4+i5lhhBggF0l82C1bE3oOMASt4JVqdOyRIVc8mh0kiuL5CCptqwQJBmFbaPglLrN0Q==} + electron-to-chromium@1.4.832: + resolution: {integrity: sha512-cTen3SB0H2SGU7x467NRe1eVcQgcuS6jckKfWJHia2eo0cHIGOqHoAxevIYZD4eRHcWjkvFzo93bi3vJ9W+1lA==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -1381,8 +1384,8 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - enhanced-resolve@5.16.0: - resolution: {integrity: sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==} + enhanced-resolve@5.17.0: + resolution: {integrity: sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==} engines: {node: '>=10.13.0'} es-abstract@1.23.3: @@ -1397,6 +1400,9 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} + es-get-iterator@1.1.3: + resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} + es-iterator-helpers@1.0.19: resolution: {integrity: sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==} engines: {node: '>= 0.4'} @@ -1416,14 +1422,14 @@ packages: resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} engines: {node: '>= 0.4'} - esbuild@0.19.12: - resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} engines: {node: '>=12'} hasBin: true - esbuild@0.20.2: - resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==} - engines: {node: '>=12'} + esbuild@0.23.0: + resolution: {integrity: sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==} + engines: {node: '>=18'} hasBin: true escalade@3.1.2: @@ -1438,8 +1444,8 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - eslint-config-next@14.2.3: - resolution: {integrity: sha512-ZkNztm3Q7hjqvB1rRlOX8P9E/cXRL9ajRcs8jufEtwMfTVYRqnmtnaSu57QqHyBlovMuiB8LEzfLBkh5RYV6Fg==} + eslint-config-next@14.2.5: + resolution: {integrity: sha512-zogs9zlOiZ7ka+wgUnmcM0KBEDjo4Jis7kxN1jvC0N4wynQ2MIx/KBkg4mVF63J5EK4W0QMCn7xO3vNisjaAoA==} peerDependencies: eslint: ^7.23.0 || ^8.0.0 typescript: '>=3.3.1' @@ -1453,8 +1459,8 @@ packages: peerDependencies: eslint: '>=7.0.0' - eslint-config-turbo@1.13.3: - resolution: {integrity: sha512-if/QtwEiWZ5b7Bg8yZBPSvS0TeCG2Zvfa/+XBYANS7uSYucjmW+BBC8enJB0PqpB/YLGGOumeo3x7h1Nuba9iw==} + eslint-config-turbo@2.0.9: + resolution: {integrity: sha512-FoIMElI8md/dR5DxjB5Om52KJfi7Qf7RInXeE+PGU6lN388rumppwyqEJsZ7vnR5GhGa9cLPt0vNZwEK9iXtKg==} peerDependencies: eslint: '>6.6.0' @@ -1499,8 +1505,8 @@ packages: '@typescript-eslint/parser': optional: true - eslint-plugin-jsx-a11y@6.8.0: - resolution: {integrity: sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA==} + eslint-plugin-jsx-a11y@6.9.0: + resolution: {integrity: sha512-nOFOCaJG2pYqORjK19lqPqxMO/JpvdCZdPtNdxY3kvom3jTvkAbOvQvD8wuD0G8BYR0IGAGYDlzqWJOh/ybn2g==} engines: {node: '>=4.0'} peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 @@ -1511,24 +1517,24 @@ packages: peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 - eslint-plugin-react-refresh@0.4.6: - resolution: {integrity: sha512-NjGXdm7zgcKRkKMua34qVO9doI7VOxZ6ancSvBELJSSoX97jyndXcSoa8XBh69JoB31dNz3EEzlMcizZl7LaMA==} + eslint-plugin-react-refresh@0.4.9: + resolution: {integrity: sha512-QK49YrBAo5CLNLseZ7sZgvgTy21E6NEw22eZqc4teZfH8pxV3yXc9XXOYfUI6JNpw7mfHNkAeWtBxrTyykB6HA==} peerDependencies: eslint: '>=7' - eslint-plugin-react@7.34.1: - resolution: {integrity: sha512-N97CxlouPT1AHt8Jn0mhhN2RrADlUAsk1/atcT2KyA/l9Q/E6ll7OIGwNumFmWfZ9skV3XXccYS19h80rHtgkw==} + eslint-plugin-react@7.35.0: + resolution: {integrity: sha512-v501SSMOWv8gerHkk+IIQBkcGRGrO2nfybfj5pLxuJNFTPxxA3PSryhXTK+9pNbtkggheDdsC0E9Q8CuPk6JKA==} engines: {node: '>=4'} peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 - eslint-plugin-turbo@1.13.3: - resolution: {integrity: sha512-RjmlnqYsEqnJ+U3M3IS5jLJDjWv5NsvReCpsC61n5pJ4JMHTZ/lU0EIoL1ccuL1L5wP0APzdXdByBxERcPQ+Nw==} + eslint-plugin-turbo@2.0.9: + resolution: {integrity: sha512-q4s4mg6JcXzz5zK4LC3c6FcWehGAWjGj7kIM76ZvG0KiR9Ks0znzjnAKW0NoiDP4s/gt3r4YPOpI357qWt167Q==} peerDependencies: eslint: '>6.6.0' - eslint-scope@8.0.1: - resolution: {integrity: sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==} + eslint-scope@8.0.2: + resolution: {integrity: sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} eslint-visitor-keys@3.4.3: @@ -1539,17 +1545,17 @@ packages: resolution: {integrity: sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.1.1: - resolution: {integrity: sha512-b4cRQ0BeZcSEzPpY2PjFY70VbO32K7BStTGtBsnIGdTSEEQzBi8hPBcGQmTG2zUvFr9uLe0TK42bw8YszuHEqg==} + eslint@9.7.0: + resolution: {integrity: sha512-FzJ9D/0nGiCGBf8UXO/IGLTgLVzIxze1zpfA8Ton2mjLovXdAPlYDv+MQDcqj3TmrhAGYfOpz9RfR+ent0AgAw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true - espree@10.0.1: - resolution: {integrity: sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==} + espree@10.1.0: + resolution: {integrity: sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - esquery@1.5.0: - resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} engines: {node: '>=0.10'} esrecurse@4.3.0: @@ -1591,8 +1597,8 @@ packages: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} - fill-range@7.0.1: - resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} find-up@5.0.0: @@ -1609,8 +1615,8 @@ packages: for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} - foreground-child@3.1.1: - resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} + foreground-child@3.2.1: + resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} engines: {node: '>=14'} format@0.2.2: @@ -1655,8 +1661,8 @@ packages: resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} engines: {node: '>= 0.4'} - get-tsconfig@4.7.3: - resolution: {integrity: sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==} + get-tsconfig@4.7.6: + resolution: {integrity: sha512-ZAqrLlu18NbDdRaHq+AKXzAmqIUPswPWKUchfytdAjiRFnCe5ojG2bstg6mRiZabkKfCoL/e98pbBELIV/YCeA==} glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} @@ -1671,9 +1677,8 @@ packages: engines: {node: '>=16 || 14 >=14.17'} hasBin: true - glob@10.3.12: - resolution: {integrity: sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==} - engines: {node: '>=16 || 14 >=14.17'} + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} hasBin: true globals@11.12.0: @@ -1766,6 +1771,10 @@ packages: is-alphanumerical@1.0.4: resolution: {integrity: sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==} + is-arguments@1.1.1: + resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} + engines: {node: '>= 0.4'} + is-array-buffer@3.0.4: resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} engines: {node: '>= 0.4'} @@ -1789,8 +1798,9 @@ packages: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} - is-core-module@2.13.1: - resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} + is-core-module@2.15.0: + resolution: {integrity: sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==} + engines: {node: '>= 0.4'} is-data-view@1.0.1: resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} @@ -1897,8 +1907,11 @@ packages: resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} engines: {node: '>=14'} - jiti@1.21.0: - resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==} + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jiti@1.21.6: + resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} hasBin: true joycon@3.1.1: @@ -1942,8 +1955,8 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - language-subtag-registry@0.3.22: - resolution: {integrity: sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==} + language-subtag-registry@0.3.23: + resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} language-tags@1.0.9: resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} @@ -1957,8 +1970,8 @@ packages: resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} engines: {node: '>=10'} - lilconfig@3.1.1: - resolution: {integrity: sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==} + lilconfig@3.1.2: + resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==} engines: {node: '>=14'} lines-and-columns@1.2.4: @@ -1988,17 +2001,12 @@ packages: lowlight@1.20.0: resolution: {integrity: sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==} - lru-cache@10.2.2: - resolution: {integrity: sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==} - engines: {node: 14 || >=16.14} + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - lru-cache@6.0.0: - resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} - engines: {node: '>=10'} - merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -2006,8 +2014,8 @@ packages: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} - micromatch@4.0.5: - resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + micromatch@4.0.7: + resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} engines: {node: '>=8.6'} mimic-fn@2.1.0: @@ -2021,15 +2029,15 @@ packages: resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} engines: {node: '>=16 || 14 >=14.17'} - minimatch@9.0.4: - resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==} + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - minipass@7.0.4: - resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==} + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} ms@2.1.2: @@ -2049,8 +2057,8 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - next@14.2.3: - resolution: {integrity: sha512-dowFkFTR8v79NPJO4QsBUtxv0g9BrS/phluVpMAt2ku7H+cbcBJlopXjkWlwxrk/xGqMemr7JkGPGemPrLLX7A==} + next@14.2.5: + resolution: {integrity: sha512-0f8aRfBVL+mpzfBjYfQuLWh2WyAwtJXCRfkPF4UJ5qd2YwrHczsrSzXU4tRMV0OAxR8ZJZWPFn6uhSC56UTsLA==} engines: {node: '>=18.17.0'} hasBin: true peerDependencies: @@ -2067,8 +2075,8 @@ packages: sass: optional: true - node-releases@2.0.14: - resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + node-releases@2.0.18: + resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} @@ -2090,8 +2098,13 @@ packages: resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} engines: {node: '>= 6'} - object-inspect@1.13.1: - resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} + object-inspect@1.13.2: + resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} + engines: {node: '>= 0.4'} + + object-is@1.1.6: + resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} + engines: {node: '>= 0.4'} object-keys@1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} @@ -2113,10 +2126,6 @@ packages: resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} engines: {node: '>= 0.4'} - object.hasown@1.1.4: - resolution: {integrity: sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==} - engines: {node: '>= 0.4'} - object.values@1.2.0: resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} engines: {node: '>= 0.4'} @@ -2137,6 +2146,9 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} + package-json-from-dist@1.0.0: + resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -2155,16 +2167,16 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - path-scurry@1.10.2: - resolution: {integrity: sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==} - engines: {node: '>=16 || 14 >=14.17'} + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} - picocolors@1.0.0: - resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + picocolors@1.0.1: + resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} @@ -2206,14 +2218,32 @@ packages: ts-node: optional: true - postcss-nested@6.0.1: - resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==} + postcss-load-config@6.0.1: + resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} + engines: {node: '>= 18'} + peerDependencies: + jiti: '>=1.21.0' + postcss: '>=8.0.9' + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + jiti: + optional: true + postcss: + optional: true + tsx: + optional: true + yaml: + optional: true + + postcss-nested@6.2.0: + resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} engines: {node: '>=12.0'} peerDependencies: postcss: ^8.2.14 - postcss-selector-parser@6.0.16: - resolution: {integrity: sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==} + postcss-selector-parser@6.1.1: + resolution: {integrity: sha512-b4dlw/9V8A71rLIDsSwVmak9z2DuBUB7CA1/wSdelNEzqsjoSPeADTWNO09lpH49Diy3/JIZ2bSPB1dI3LJCHg==} engines: {node: '>=4'} postcss-value-parser@4.2.0: @@ -2223,16 +2253,16 @@ packages: resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} engines: {node: ^10 || ^12 || >=14} - postcss@8.4.38: - resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} + postcss@8.4.39: + resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} engines: {node: ^10 || ^12 || >=14} prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - prettier@3.2.5: - resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==} + prettier@3.3.3: + resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==} engines: {node: '>=14'} hasBin: true @@ -2326,8 +2356,8 @@ packages: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rollup@4.17.2: - resolution: {integrity: sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==} + rollup@4.19.0: + resolution: {integrity: sha512-5r7EYSQIowHsK4eTZ0Y81qpZuJz+MUuYeqmmYmRMl1nwhdmbiYqt5jwzf6u7wyOzJgYqtCRMtVRKOtHANBz7rA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -2352,8 +2382,8 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.6.0: - resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==} + semver@7.6.3: + resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} engines: {node: '>=10'} hasBin: true @@ -2405,6 +2435,10 @@ packages: spawn-command@0.0.2: resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==} + stop-iteration-iterator@1.0.0: + resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==} + engines: {node: '>= 0.4'} + streamsearch@1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} @@ -2417,10 +2451,16 @@ packages: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} engines: {node: '>=12'} + string.prototype.includes@2.0.0: + resolution: {integrity: sha512-E34CkBgyeqNDcrbU76cDjL5JLcVrtSdYq0MEh/B10r17pRP4ciHLwTgnuLV8Ay6cgEMLkcBkFCKyFZ43YldYzg==} + string.prototype.matchall@4.0.11: resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==} engines: {node: '>= 0.4'} + string.prototype.repeat@1.0.0: + resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} + string.prototype.trim@1.2.9: resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} engines: {node: '>= 0.4'} @@ -2486,8 +2526,8 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - tailwindcss@3.4.3: - resolution: {integrity: sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==} + tailwindcss@3.4.6: + resolution: {integrity: sha512-1uRHzPB+Vzu57ocybfZ4jh5Q3SdlH7XW23J5sQoM9LhE9eIOlzxer/3XPSsycvih3rboRsvt0QCmzSrqyOYUIA==} engines: {node: '>=14.0.0'} hasBin: true @@ -2535,11 +2575,11 @@ packages: tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} - tslib@2.6.2: - resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + tslib@2.6.3: + resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} - tsup@8.0.2: - resolution: {integrity: sha512-NY8xtQXdH7hDUAZwcQdY/Vzlw9johQsaqf7iwZ6g1DOUlFYQ5/AtVAjTvihhEyeRlGo4dLRVHtrRaL35M1daqQ==} + tsup@8.2.2: + resolution: {integrity: sha512-MufIuzdSt6HYPOeOtjUXLR4rqRJySi6XsRNZdwvjC2XR+xghsu2L3vSmYmX+k4S1mO6j0OlUEyVQ3Fc0H66XcA==} engines: {node: '>=18'} hasBin: true peerDependencies: @@ -2557,38 +2597,38 @@ packages: typescript: optional: true - turbo-darwin-64@1.13.3: - resolution: {integrity: sha512-glup8Qx1qEFB5jerAnXbS8WrL92OKyMmg5Hnd4PleLljAeYmx+cmmnsmLT7tpaVZIN58EAAwu8wHC6kIIqhbWA==} + turbo-darwin-64@2.0.9: + resolution: {integrity: sha512-owlGsOaExuVGBUfrnJwjkL1BWlvefjSKczEAcpLx4BI7Oh6ttakOi+JyomkPkFlYElRpjbvlR2gP8WIn6M/+xQ==} cpu: [x64] os: [darwin] - turbo-darwin-arm64@1.13.3: - resolution: {integrity: sha512-/np2xD+f/+9qY8BVtuOQXRq5f9LehCFxamiQnwdqWm5iZmdjygC5T3uVSYuagVFsZKMvX3ycySwh8dylGTl6lg==} + turbo-darwin-arm64@2.0.9: + resolution: {integrity: sha512-XAXkKkePth5ZPPE/9G9tTnPQx0C8UTkGWmNGYkpmGgRr8NedW+HrPsi9N0HcjzzIH9A4TpNYvtiV+WcwdaEjKA==} cpu: [arm64] os: [darwin] - turbo-linux-64@1.13.3: - resolution: {integrity: sha512-G+HGrau54iAnbXLfl+N/PynqpDwi/uDzb6iM9hXEDG+yJnSJxaHMShhOkXYJPk9offm9prH33Khx2scXrYVW1g==} + turbo-linux-64@2.0.9: + resolution: {integrity: sha512-l9wSgEjrCFM1aG16zItBsZ206ZlhSSx1owB8Cgskfv0XyIXRGHRkluihiaxkp+UeU5WoEfz4EN5toc+ICA0q0w==} cpu: [x64] os: [linux] - turbo-linux-arm64@1.13.3: - resolution: {integrity: sha512-qWwEl5VR02NqRyl68/3pwp3c/olZuSp+vwlwrunuoNTm6JXGLG5pTeme4zoHNnk0qn4cCX7DFrOboArlYxv0wQ==} + turbo-linux-arm64@2.0.9: + resolution: {integrity: sha512-gRnjxXRne18B27SwxXMqL3fJu7jw/8kBrOBTBNRSmZZiG1Uu3nbnP7b4lgrA/bCku6C0Wligwqurvtpq6+nFHA==} cpu: [arm64] os: [linux] - turbo-windows-64@1.13.3: - resolution: {integrity: sha512-Nudr4bRChfJzBPzEmpVV85VwUYRCGKecwkBFpbp2a4NtrJ3+UP1VZES653ckqCu2FRyRuS0n03v9euMbAvzH+Q==} + turbo-windows-64@2.0.9: + resolution: {integrity: sha512-ZVo0apxUvaRq4Vm1qhsfqKKhtRgReYlBVf9MQvVU1O9AoyydEQvLDO1ryqpXDZWpcHoFxHAQc9msjAMtE5K2lA==} cpu: [x64] os: [win32] - turbo-windows-arm64@1.13.3: - resolution: {integrity: sha512-ouJCgsVLd3icjRLmRvHQDDZnmGzT64GBupM1Y+TjtYn2LVaEBoV6hicFy8x5DUpnqdLy+YpCzRMkWlwhmkX7sQ==} + turbo-windows-arm64@2.0.9: + resolution: {integrity: sha512-sGRz7c5Pey6y7y9OKi8ypbWNuIRPF9y8xcMqL56OZifSUSo+X2EOsOleR9MKxQXVaqHPGOUKWsE6y8hxBi9pag==} cpu: [arm64] os: [win32] - turbo@1.13.3: - resolution: {integrity: sha512-n17HJv4F4CpsYTvKzUJhLbyewbXjq1oLCi90i5tW1TiWDz16ML1eDG7wi5dHaKxzh5efIM56SITnuVbMq5dk4g==} + turbo@2.0.9: + resolution: {integrity: sha512-QaLaUL1CqblSKKPgLrFW3lZWkWG4pGBQNW+q1ScJB5v1D/nFWtsrD/yZljW/bdawg90ihi4/ftQJ3h6fz1FamA==} hasBin: true type-check@0.4.0: @@ -2611,8 +2651,8 @@ packages: resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} engines: {node: '>= 0.4'} - typescript@5.4.5: - resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} + typescript@5.5.4: + resolution: {integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==} engines: {node: '>=14.17'} hasBin: true @@ -2622,8 +2662,8 @@ packages: undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - update-browserslist-db@1.0.14: - resolution: {integrity: sha512-JixKH8GR2pWYshIPUg/NujK3JO7JiqEEUiNArE86NQyrgUuZeTlZQN3xuS/yiV5Kb48ev9K6RqNkaJjXsdg7Jw==} + update-browserslist-db@1.1.0: + resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -2634,15 +2674,15 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - vite-plugin-singlefile@2.0.1: - resolution: {integrity: sha512-J74tfN6TE4fz0Hp7E1+dmVTmCpyazv4yuIpR6jd22Kq76d2CQDSQx3wDiHX8LT02f922V+YrLhRq2VIk/UYrig==} + vite-plugin-singlefile@2.0.2: + resolution: {integrity: sha512-Z2ou6HcvED5CF0hM+vcFSaFa+klyS8RyyLxW0PbMRLnMbvzTI6ueWyxdYNFhpuXZgz/aj6+E/dHFTdEcw6gb9w==} engines: {node: '>18.0.0'} peerDependencies: - rollup: ^4.12.0 - vite: ^5.1.4 + rollup: ^4.18.0 + vite: ^5.3.1 - vite@5.2.10: - resolution: {integrity: sha512-PAzgUZbP7msvQvqdSD+ErD5qGnSFiGOoWmV5yAKUEI0kdhjbH6nMWVyZQC/hSc4aXwc0oJ9aEdIiF9Oje0JFCw==} + vite@5.3.4: + resolution: {integrity: sha512-Cw+7zL3ZG9/NZBB8C+8QbQZmR54GwqIz+WMI4b3JgdYJvX+ny9AjJXqkGQlDXSXRP9rP0B4tbciRMOVEKulVOA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -2718,11 +2758,8 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - - yaml@2.4.2: - resolution: {integrity: sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA==} + yaml@2.4.5: + resolution: {integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==} engines: {node: '>= 14'} hasBin: true @@ -2747,298 +2784,316 @@ snapshots: '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 - '@babel/code-frame@7.24.2': + '@babel/code-frame@7.24.7': dependencies: - '@babel/highlight': 7.24.5 - picocolors: 1.0.0 + '@babel/highlight': 7.24.7 + picocolors: 1.0.1 - '@babel/compat-data@7.24.4': {} + '@babel/compat-data@7.24.9': {} - '@babel/core@7.24.5': + '@babel/core@7.24.9': dependencies: '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.24.2 - '@babel/generator': 7.24.5 - '@babel/helper-compilation-targets': 7.23.6 - '@babel/helper-module-transforms': 7.24.5(@babel/core@7.24.5) - '@babel/helpers': 7.24.5 - '@babel/parser': 7.24.5 - '@babel/template': 7.24.0 - '@babel/traverse': 7.24.5 - '@babel/types': 7.24.5 + '@babel/code-frame': 7.24.7 + '@babel/generator': 7.24.10 + '@babel/helper-compilation-targets': 7.24.8 + '@babel/helper-module-transforms': 7.24.9(@babel/core@7.24.9) + '@babel/helpers': 7.24.8 + '@babel/parser': 7.24.8 + '@babel/template': 7.24.7 + '@babel/traverse': 7.24.8 + '@babel/types': 7.24.9 convert-source-map: 2.0.0 - debug: 4.3.4 + debug: 4.3.5 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/generator@7.24.5': + '@babel/generator@7.24.10': dependencies: - '@babel/types': 7.24.5 + '@babel/types': 7.24.9 '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 jsesc: 2.5.2 - '@babel/helper-compilation-targets@7.23.6': + '@babel/helper-compilation-targets@7.24.8': dependencies: - '@babel/compat-data': 7.24.4 - '@babel/helper-validator-option': 7.23.5 - browserslist: 4.23.0 + '@babel/compat-data': 7.24.9 + '@babel/helper-validator-option': 7.24.8 + browserslist: 4.23.2 lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-environment-visitor@7.22.20': {} + '@babel/helper-environment-visitor@7.24.7': + dependencies: + '@babel/types': 7.24.9 - '@babel/helper-function-name@7.23.0': + '@babel/helper-function-name@7.24.7': dependencies: - '@babel/template': 7.24.0 - '@babel/types': 7.24.5 + '@babel/template': 7.24.7 + '@babel/types': 7.24.9 - '@babel/helper-hoist-variables@7.22.5': + '@babel/helper-hoist-variables@7.24.7': dependencies: - '@babel/types': 7.24.5 + '@babel/types': 7.24.9 - '@babel/helper-module-imports@7.24.3': + '@babel/helper-module-imports@7.24.7': dependencies: - '@babel/types': 7.24.5 + '@babel/traverse': 7.24.8 + '@babel/types': 7.24.9 + transitivePeerDependencies: + - supports-color - '@babel/helper-module-transforms@7.24.5(@babel/core@7.24.5)': + '@babel/helper-module-transforms@7.24.9(@babel/core@7.24.9)': dependencies: - '@babel/core': 7.24.5 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-module-imports': 7.24.3 - '@babel/helper-simple-access': 7.24.5 - '@babel/helper-split-export-declaration': 7.24.5 - '@babel/helper-validator-identifier': 7.24.5 + '@babel/core': 7.24.9 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-module-imports': 7.24.7 + '@babel/helper-simple-access': 7.24.7 + '@babel/helper-split-export-declaration': 7.24.7 + '@babel/helper-validator-identifier': 7.24.7 + transitivePeerDependencies: + - supports-color - '@babel/helper-plugin-utils@7.24.5': {} + '@babel/helper-plugin-utils@7.24.8': {} - '@babel/helper-simple-access@7.24.5': + '@babel/helper-simple-access@7.24.7': dependencies: - '@babel/types': 7.24.5 + '@babel/traverse': 7.24.8 + '@babel/types': 7.24.9 + transitivePeerDependencies: + - supports-color - '@babel/helper-split-export-declaration@7.24.5': + '@babel/helper-split-export-declaration@7.24.7': dependencies: - '@babel/types': 7.24.5 + '@babel/types': 7.24.9 - '@babel/helper-string-parser@7.24.1': {} + '@babel/helper-string-parser@7.24.8': {} - '@babel/helper-validator-identifier@7.24.5': {} + '@babel/helper-validator-identifier@7.24.7': {} - '@babel/helper-validator-option@7.23.5': {} + '@babel/helper-validator-option@7.24.8': {} - '@babel/helpers@7.24.5': + '@babel/helpers@7.24.8': dependencies: - '@babel/template': 7.24.0 - '@babel/traverse': 7.24.5 - '@babel/types': 7.24.5 - transitivePeerDependencies: - - supports-color + '@babel/template': 7.24.7 + '@babel/types': 7.24.9 - '@babel/highlight@7.24.5': + '@babel/highlight@7.24.7': dependencies: - '@babel/helper-validator-identifier': 7.24.5 + '@babel/helper-validator-identifier': 7.24.7 chalk: 2.4.2 js-tokens: 4.0.0 - picocolors: 1.0.0 + picocolors: 1.0.1 - '@babel/parser@7.24.5': + '@babel/parser@7.24.8': dependencies: - '@babel/types': 7.24.5 + '@babel/types': 7.24.9 - '@babel/plugin-transform-react-jsx-self@7.24.5(@babel/core@7.24.5)': + '@babel/plugin-transform-react-jsx-self@7.24.7(@babel/core@7.24.9)': dependencies: - '@babel/core': 7.24.5 - '@babel/helper-plugin-utils': 7.24.5 + '@babel/core': 7.24.9 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-react-jsx-source@7.24.1(@babel/core@7.24.5)': + '@babel/plugin-transform-react-jsx-source@7.24.7(@babel/core@7.24.9)': dependencies: - '@babel/core': 7.24.5 - '@babel/helper-plugin-utils': 7.24.5 + '@babel/core': 7.24.9 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/runtime@7.24.5': + '@babel/runtime@7.24.8': dependencies: regenerator-runtime: 0.14.1 - '@babel/template@7.24.0': + '@babel/template@7.24.7': dependencies: - '@babel/code-frame': 7.24.2 - '@babel/parser': 7.24.5 - '@babel/types': 7.24.5 + '@babel/code-frame': 7.24.7 + '@babel/parser': 7.24.8 + '@babel/types': 7.24.9 - '@babel/traverse@7.24.5': + '@babel/traverse@7.24.8': dependencies: - '@babel/code-frame': 7.24.2 - '@babel/generator': 7.24.5 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-function-name': 7.23.0 - '@babel/helper-hoist-variables': 7.22.5 - '@babel/helper-split-export-declaration': 7.24.5 - '@babel/parser': 7.24.5 - '@babel/types': 7.24.5 - debug: 4.3.4 + '@babel/code-frame': 7.24.7 + '@babel/generator': 7.24.10 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-function-name': 7.24.7 + '@babel/helper-hoist-variables': 7.24.7 + '@babel/helper-split-export-declaration': 7.24.7 + '@babel/parser': 7.24.8 + '@babel/types': 7.24.9 + debug: 4.3.5 globals: 11.12.0 transitivePeerDependencies: - supports-color - '@babel/types@7.24.5': + '@babel/types@7.24.9': dependencies: - '@babel/helper-string-parser': 7.24.1 - '@babel/helper-validator-identifier': 7.24.5 + '@babel/helper-string-parser': 7.24.8 + '@babel/helper-validator-identifier': 7.24.7 to-fast-properties: 2.0.0 - '@esbuild/aix-ppc64@0.19.12': + '@esbuild/aix-ppc64@0.21.5': + optional: true + + '@esbuild/aix-ppc64@0.23.0': optional: true - '@esbuild/aix-ppc64@0.20.2': + '@esbuild/android-arm64@0.21.5': optional: true - '@esbuild/android-arm64@0.19.12': + '@esbuild/android-arm64@0.23.0': optional: true - '@esbuild/android-arm64@0.20.2': + '@esbuild/android-arm@0.21.5': optional: true - '@esbuild/android-arm@0.19.12': + '@esbuild/android-arm@0.23.0': optional: true - '@esbuild/android-arm@0.20.2': + '@esbuild/android-x64@0.21.5': optional: true - '@esbuild/android-x64@0.19.12': + '@esbuild/android-x64@0.23.0': optional: true - '@esbuild/android-x64@0.20.2': + '@esbuild/darwin-arm64@0.21.5': optional: true - '@esbuild/darwin-arm64@0.19.12': + '@esbuild/darwin-arm64@0.23.0': optional: true - '@esbuild/darwin-arm64@0.20.2': + '@esbuild/darwin-x64@0.21.5': optional: true - '@esbuild/darwin-x64@0.19.12': + '@esbuild/darwin-x64@0.23.0': optional: true - '@esbuild/darwin-x64@0.20.2': + '@esbuild/freebsd-arm64@0.21.5': optional: true - '@esbuild/freebsd-arm64@0.19.12': + '@esbuild/freebsd-arm64@0.23.0': optional: true - '@esbuild/freebsd-arm64@0.20.2': + '@esbuild/freebsd-x64@0.21.5': optional: true - '@esbuild/freebsd-x64@0.19.12': + '@esbuild/freebsd-x64@0.23.0': optional: true - '@esbuild/freebsd-x64@0.20.2': + '@esbuild/linux-arm64@0.21.5': optional: true - '@esbuild/linux-arm64@0.19.12': + '@esbuild/linux-arm64@0.23.0': optional: true - '@esbuild/linux-arm64@0.20.2': + '@esbuild/linux-arm@0.21.5': optional: true - '@esbuild/linux-arm@0.19.12': + '@esbuild/linux-arm@0.23.0': optional: true - '@esbuild/linux-arm@0.20.2': + '@esbuild/linux-ia32@0.21.5': optional: true - '@esbuild/linux-ia32@0.19.12': + '@esbuild/linux-ia32@0.23.0': optional: true - '@esbuild/linux-ia32@0.20.2': + '@esbuild/linux-loong64@0.21.5': optional: true - '@esbuild/linux-loong64@0.19.12': + '@esbuild/linux-loong64@0.23.0': optional: true - '@esbuild/linux-loong64@0.20.2': + '@esbuild/linux-mips64el@0.21.5': optional: true - '@esbuild/linux-mips64el@0.19.12': + '@esbuild/linux-mips64el@0.23.0': optional: true - '@esbuild/linux-mips64el@0.20.2': + '@esbuild/linux-ppc64@0.21.5': optional: true - '@esbuild/linux-ppc64@0.19.12': + '@esbuild/linux-ppc64@0.23.0': optional: true - '@esbuild/linux-ppc64@0.20.2': + '@esbuild/linux-riscv64@0.21.5': optional: true - '@esbuild/linux-riscv64@0.19.12': + '@esbuild/linux-riscv64@0.23.0': optional: true - '@esbuild/linux-riscv64@0.20.2': + '@esbuild/linux-s390x@0.21.5': optional: true - '@esbuild/linux-s390x@0.19.12': + '@esbuild/linux-s390x@0.23.0': optional: true - '@esbuild/linux-s390x@0.20.2': + '@esbuild/linux-x64@0.21.5': optional: true - '@esbuild/linux-x64@0.19.12': + '@esbuild/linux-x64@0.23.0': optional: true - '@esbuild/linux-x64@0.20.2': + '@esbuild/netbsd-x64@0.21.5': optional: true - '@esbuild/netbsd-x64@0.19.12': + '@esbuild/netbsd-x64@0.23.0': optional: true - '@esbuild/netbsd-x64@0.20.2': + '@esbuild/openbsd-arm64@0.23.0': optional: true - '@esbuild/openbsd-x64@0.19.12': + '@esbuild/openbsd-x64@0.21.5': optional: true - '@esbuild/openbsd-x64@0.20.2': + '@esbuild/openbsd-x64@0.23.0': optional: true - '@esbuild/sunos-x64@0.19.12': + '@esbuild/sunos-x64@0.21.5': optional: true - '@esbuild/sunos-x64@0.20.2': + '@esbuild/sunos-x64@0.23.0': optional: true - '@esbuild/win32-arm64@0.19.12': + '@esbuild/win32-arm64@0.21.5': optional: true - '@esbuild/win32-arm64@0.20.2': + '@esbuild/win32-arm64@0.23.0': optional: true - '@esbuild/win32-ia32@0.19.12': + '@esbuild/win32-ia32@0.21.5': optional: true - '@esbuild/win32-ia32@0.20.2': + '@esbuild/win32-ia32@0.23.0': optional: true - '@esbuild/win32-x64@0.19.12': + '@esbuild/win32-x64@0.21.5': optional: true - '@esbuild/win32-x64@0.20.2': + '@esbuild/win32-x64@0.23.0': optional: true - '@eslint-community/eslint-utils@4.4.0(eslint@9.1.1)': + '@eslint-community/eslint-utils@4.4.0(eslint@9.7.0)': dependencies: - eslint: 9.1.1 + eslint: 9.7.0 eslint-visitor-keys: 3.4.3 - '@eslint-community/regexpp@4.10.0': {} + '@eslint-community/regexpp@4.11.0': {} - '@eslint/eslintrc@3.0.2': + '@eslint/config-array@0.17.1': + dependencies: + '@eslint/object-schema': 2.1.4 + debug: 4.3.5 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/eslintrc@3.1.0': dependencies: ajv: 6.12.6 - debug: 4.3.4 - espree: 10.0.1 + debug: 4.3.5 + espree: 10.1.0 globals: 14.0.0 ignore: 5.3.1 import-fresh: 3.3.0 @@ -3048,23 +3103,15 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.1.1': {} + '@eslint/js@9.7.0': {} - '@figma/plugin-typings@1.92.0': {} + '@eslint/object-schema@2.1.4': {} - '@humanwhocodes/config-array@0.13.0': - dependencies: - '@humanwhocodes/object-schema': 2.0.3 - debug: 4.3.4 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color + '@figma/plugin-typings@1.97.0': {} '@humanwhocodes/module-importer@1.0.1': {} - '@humanwhocodes/object-schema@2.0.3': {} - - '@humanwhocodes/retry@0.2.3': {} + '@humanwhocodes/retry@0.3.0': {} '@isaacs/cliui@8.0.2': dependencies: @@ -3078,51 +3125,51 @@ snapshots: '@jridgewell/gen-mapping@0.3.5': dependencies: '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.5.0 '@jridgewell/trace-mapping': 0.3.25 '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/set-array@1.2.1': {} - '@jridgewell/sourcemap-codec@1.4.15': {} + '@jridgewell/sourcemap-codec@1.5.0': {} '@jridgewell/trace-mapping@0.3.25': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.5.0 - '@next/env@14.2.3': {} + '@next/env@14.2.5': {} - '@next/eslint-plugin-next@14.2.3': + '@next/eslint-plugin-next@14.2.5': dependencies: glob: 10.3.10 - '@next/swc-darwin-arm64@14.2.3': + '@next/swc-darwin-arm64@14.2.5': optional: true - '@next/swc-darwin-x64@14.2.3': + '@next/swc-darwin-x64@14.2.5': optional: true - '@next/swc-linux-arm64-gnu@14.2.3': + '@next/swc-linux-arm64-gnu@14.2.5': optional: true - '@next/swc-linux-arm64-musl@14.2.3': + '@next/swc-linux-arm64-musl@14.2.5': optional: true - '@next/swc-linux-x64-gnu@14.2.3': + '@next/swc-linux-x64-gnu@14.2.5': optional: true - '@next/swc-linux-x64-musl@14.2.3': + '@next/swc-linux-x64-musl@14.2.5': optional: true - '@next/swc-win32-arm64-msvc@14.2.3': + '@next/swc-win32-arm64-msvc@14.2.5': optional: true - '@next/swc-win32-ia32-msvc@14.2.3': + '@next/swc-win32-ia32-msvc@14.2.5': optional: true - '@next/swc-win32-x64-msvc@14.2.3': + '@next/swc-win32-x64-msvc@14.2.5': optional: true '@nodelib/fs.scandir@2.1.5': @@ -3140,139 +3187,139 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@rollup/rollup-android-arm-eabi@4.17.2': + '@rollup/rollup-android-arm-eabi@4.19.0': optional: true - '@rollup/rollup-android-arm64@4.17.2': + '@rollup/rollup-android-arm64@4.19.0': optional: true - '@rollup/rollup-darwin-arm64@4.17.2': + '@rollup/rollup-darwin-arm64@4.19.0': optional: true - '@rollup/rollup-darwin-x64@4.17.2': + '@rollup/rollup-darwin-x64@4.19.0': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.17.2': + '@rollup/rollup-linux-arm-gnueabihf@4.19.0': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.17.2': + '@rollup/rollup-linux-arm-musleabihf@4.19.0': optional: true - '@rollup/rollup-linux-arm64-gnu@4.17.2': + '@rollup/rollup-linux-arm64-gnu@4.19.0': optional: true - '@rollup/rollup-linux-arm64-musl@4.17.2': + '@rollup/rollup-linux-arm64-musl@4.19.0': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.17.2': + '@rollup/rollup-linux-powerpc64le-gnu@4.19.0': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.17.2': + '@rollup/rollup-linux-riscv64-gnu@4.19.0': optional: true - '@rollup/rollup-linux-s390x-gnu@4.17.2': + '@rollup/rollup-linux-s390x-gnu@4.19.0': optional: true - '@rollup/rollup-linux-x64-gnu@4.17.2': + '@rollup/rollup-linux-x64-gnu@4.19.0': optional: true - '@rollup/rollup-linux-x64-musl@4.17.2': + '@rollup/rollup-linux-x64-musl@4.19.0': optional: true - '@rollup/rollup-win32-arm64-msvc@4.17.2': + '@rollup/rollup-win32-arm64-msvc@4.19.0': optional: true - '@rollup/rollup-win32-ia32-msvc@4.17.2': + '@rollup/rollup-win32-ia32-msvc@4.19.0': optional: true - '@rollup/rollup-win32-x64-msvc@4.17.2': + '@rollup/rollup-win32-x64-msvc@4.19.0': optional: true - '@rushstack/eslint-patch@1.10.2': {} + '@rushstack/eslint-patch@1.10.3': {} - '@swc/core-darwin-arm64@1.4.17': + '@swc/core-darwin-arm64@1.7.0': optional: true - '@swc/core-darwin-x64@1.4.17': + '@swc/core-darwin-x64@1.7.0': optional: true - '@swc/core-linux-arm-gnueabihf@1.4.17': + '@swc/core-linux-arm-gnueabihf@1.7.0': optional: true - '@swc/core-linux-arm64-gnu@1.4.17': + '@swc/core-linux-arm64-gnu@1.7.0': optional: true - '@swc/core-linux-arm64-musl@1.4.17': + '@swc/core-linux-arm64-musl@1.7.0': optional: true - '@swc/core-linux-x64-gnu@1.4.17': + '@swc/core-linux-x64-gnu@1.7.0': optional: true - '@swc/core-linux-x64-musl@1.4.17': + '@swc/core-linux-x64-musl@1.7.0': optional: true - '@swc/core-win32-arm64-msvc@1.4.17': + '@swc/core-win32-arm64-msvc@1.7.0': optional: true - '@swc/core-win32-ia32-msvc@1.4.17': + '@swc/core-win32-ia32-msvc@1.7.0': optional: true - '@swc/core-win32-x64-msvc@1.4.17': + '@swc/core-win32-x64-msvc@1.7.0': optional: true - '@swc/core@1.4.17(@swc/helpers@0.5.11)': + '@swc/core@1.7.0(@swc/helpers@0.5.12)': dependencies: '@swc/counter': 0.1.3 - '@swc/types': 0.1.6 + '@swc/types': 0.1.12 optionalDependencies: - '@swc/core-darwin-arm64': 1.4.17 - '@swc/core-darwin-x64': 1.4.17 - '@swc/core-linux-arm-gnueabihf': 1.4.17 - '@swc/core-linux-arm64-gnu': 1.4.17 - '@swc/core-linux-arm64-musl': 1.4.17 - '@swc/core-linux-x64-gnu': 1.4.17 - '@swc/core-linux-x64-musl': 1.4.17 - '@swc/core-win32-arm64-msvc': 1.4.17 - '@swc/core-win32-ia32-msvc': 1.4.17 - '@swc/core-win32-x64-msvc': 1.4.17 - '@swc/helpers': 0.5.11 + '@swc/core-darwin-arm64': 1.7.0 + '@swc/core-darwin-x64': 1.7.0 + '@swc/core-linux-arm-gnueabihf': 1.7.0 + '@swc/core-linux-arm64-gnu': 1.7.0 + '@swc/core-linux-arm64-musl': 1.7.0 + '@swc/core-linux-x64-gnu': 1.7.0 + '@swc/core-linux-x64-musl': 1.7.0 + '@swc/core-win32-arm64-msvc': 1.7.0 + '@swc/core-win32-ia32-msvc': 1.7.0 + '@swc/core-win32-x64-msvc': 1.7.0 + '@swc/helpers': 0.5.12 '@swc/counter@0.1.3': {} - '@swc/helpers@0.5.11': + '@swc/helpers@0.5.12': dependencies: - tslib: 2.6.2 + tslib: 2.6.3 optional: true '@swc/helpers@0.5.5': dependencies: '@swc/counter': 0.1.3 - tslib: 2.6.2 + tslib: 2.6.3 - '@swc/types@0.1.6': + '@swc/types@0.1.12': dependencies: '@swc/counter': 0.1.3 '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.24.5 - '@babel/types': 7.24.5 + '@babel/parser': 7.24.8 + '@babel/types': 7.24.9 '@types/babel__generator': 7.6.8 '@types/babel__template': 7.4.4 - '@types/babel__traverse': 7.20.5 + '@types/babel__traverse': 7.20.6 '@types/babel__generator@7.6.8': dependencies: - '@babel/types': 7.24.5 + '@babel/types': 7.24.9 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.24.5 - '@babel/types': 7.24.5 + '@babel/parser': 7.24.8 + '@babel/types': 7.24.9 - '@types/babel__traverse@7.20.5': + '@types/babel__traverse@7.20.6': dependencies: - '@babel/types': 7.24.5 + '@babel/types': 7.24.9 '@types/estree@1.0.5': {} @@ -3280,11 +3327,9 @@ snapshots: dependencies: '@types/unist': 2.0.10 - '@types/json-schema@7.0.15': {} - '@types/json5@0.0.29': {} - '@types/node@20.12.8': + '@types/node@20.14.11': dependencies: undici-types: 5.26.5 @@ -3292,170 +3337,163 @@ snapshots: '@types/react-dom@18.3.0': dependencies: - '@types/react': 18.3.1 + '@types/react': 18.3.3 - '@types/react-syntax-highlighter@15.5.11': + '@types/react-syntax-highlighter@15.5.13': dependencies: - '@types/react': 18.3.1 + '@types/react': 18.3.3 - '@types/react@18.3.1': + '@types/react@18.3.3': dependencies: '@types/prop-types': 15.7.12 csstype: 3.1.3 - '@types/semver@7.5.8': {} - '@types/unist@2.0.10': {} - '@typescript-eslint/eslint-plugin@7.8.0(@typescript-eslint/parser@7.8.0(eslint@9.1.1)(typescript@5.4.5))(eslint@9.1.1)(typescript@5.4.5)': + '@typescript-eslint/eslint-plugin@7.17.0(@typescript-eslint/parser@7.17.0(eslint@9.7.0)(typescript@5.5.4))(eslint@9.7.0)(typescript@5.5.4)': dependencies: - '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 7.8.0(eslint@9.1.1)(typescript@5.4.5) - '@typescript-eslint/scope-manager': 7.8.0 - '@typescript-eslint/type-utils': 7.8.0(eslint@9.1.1)(typescript@5.4.5) - '@typescript-eslint/utils': 7.8.0(eslint@9.1.1)(typescript@5.4.5) - '@typescript-eslint/visitor-keys': 7.8.0 - debug: 4.3.4 - eslint: 9.1.1 + '@eslint-community/regexpp': 4.11.0 + '@typescript-eslint/parser': 7.17.0(eslint@9.7.0)(typescript@5.5.4) + '@typescript-eslint/scope-manager': 7.17.0 + '@typescript-eslint/type-utils': 7.17.0(eslint@9.7.0)(typescript@5.5.4) + '@typescript-eslint/utils': 7.17.0(eslint@9.7.0)(typescript@5.5.4) + '@typescript-eslint/visitor-keys': 7.17.0 + eslint: 9.7.0 graphemer: 1.4.0 ignore: 5.3.1 natural-compare: 1.4.0 - semver: 7.6.0 - ts-api-utils: 1.3.0(typescript@5.4.5) + ts-api-utils: 1.3.0(typescript@5.5.4) optionalDependencies: - typescript: 5.4.5 + typescript: 5.5.4 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@7.2.0(eslint@9.1.1)(typescript@5.4.5)': + '@typescript-eslint/parser@7.17.0(eslint@9.7.0)(typescript@5.5.4)': dependencies: - '@typescript-eslint/scope-manager': 7.2.0 - '@typescript-eslint/types': 7.2.0 - '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.4.5) - '@typescript-eslint/visitor-keys': 7.2.0 - debug: 4.3.4 - eslint: 9.1.1 + '@typescript-eslint/scope-manager': 7.17.0 + '@typescript-eslint/types': 7.17.0 + '@typescript-eslint/typescript-estree': 7.17.0(typescript@5.5.4) + '@typescript-eslint/visitor-keys': 7.17.0 + debug: 4.3.5 + eslint: 9.7.0 optionalDependencies: - typescript: 5.4.5 + typescript: 5.5.4 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@7.8.0(eslint@9.1.1)(typescript@5.4.5)': + '@typescript-eslint/parser@7.2.0(eslint@9.7.0)(typescript@5.5.4)': dependencies: - '@typescript-eslint/scope-manager': 7.8.0 - '@typescript-eslint/types': 7.8.0 - '@typescript-eslint/typescript-estree': 7.8.0(typescript@5.4.5) - '@typescript-eslint/visitor-keys': 7.8.0 - debug: 4.3.4 - eslint: 9.1.1 + '@typescript-eslint/scope-manager': 7.2.0 + '@typescript-eslint/types': 7.2.0 + '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.5.4) + '@typescript-eslint/visitor-keys': 7.2.0 + debug: 4.3.5 + eslint: 9.7.0 optionalDependencies: - typescript: 5.4.5 + typescript: 5.5.4 transitivePeerDependencies: - supports-color + '@typescript-eslint/scope-manager@7.17.0': + dependencies: + '@typescript-eslint/types': 7.17.0 + '@typescript-eslint/visitor-keys': 7.17.0 + '@typescript-eslint/scope-manager@7.2.0': dependencies: '@typescript-eslint/types': 7.2.0 '@typescript-eslint/visitor-keys': 7.2.0 - '@typescript-eslint/scope-manager@7.8.0': + '@typescript-eslint/type-utils@7.17.0(eslint@9.7.0)(typescript@5.5.4)': dependencies: - '@typescript-eslint/types': 7.8.0 - '@typescript-eslint/visitor-keys': 7.8.0 - - '@typescript-eslint/type-utils@7.8.0(eslint@9.1.1)(typescript@5.4.5)': - dependencies: - '@typescript-eslint/typescript-estree': 7.8.0(typescript@5.4.5) - '@typescript-eslint/utils': 7.8.0(eslint@9.1.1)(typescript@5.4.5) - debug: 4.3.4 - eslint: 9.1.1 - ts-api-utils: 1.3.0(typescript@5.4.5) + '@typescript-eslint/typescript-estree': 7.17.0(typescript@5.5.4) + '@typescript-eslint/utils': 7.17.0(eslint@9.7.0)(typescript@5.5.4) + debug: 4.3.5 + eslint: 9.7.0 + ts-api-utils: 1.3.0(typescript@5.5.4) optionalDependencies: - typescript: 5.4.5 + typescript: 5.5.4 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@7.2.0': {} + '@typescript-eslint/types@7.17.0': {} - '@typescript-eslint/types@7.8.0': {} + '@typescript-eslint/types@7.2.0': {} - '@typescript-eslint/typescript-estree@7.2.0(typescript@5.4.5)': + '@typescript-eslint/typescript-estree@7.17.0(typescript@5.5.4)': dependencies: - '@typescript-eslint/types': 7.2.0 - '@typescript-eslint/visitor-keys': 7.2.0 - debug: 4.3.4 + '@typescript-eslint/types': 7.17.0 + '@typescript-eslint/visitor-keys': 7.17.0 + debug: 4.3.5 globby: 11.1.0 is-glob: 4.0.3 - minimatch: 9.0.3 - semver: 7.6.0 - ts-api-utils: 1.3.0(typescript@5.4.5) + minimatch: 9.0.5 + semver: 7.6.3 + ts-api-utils: 1.3.0(typescript@5.5.4) optionalDependencies: - typescript: 5.4.5 + typescript: 5.5.4 transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@7.8.0(typescript@5.4.5)': + '@typescript-eslint/typescript-estree@7.2.0(typescript@5.5.4)': dependencies: - '@typescript-eslint/types': 7.8.0 - '@typescript-eslint/visitor-keys': 7.8.0 - debug: 4.3.4 + '@typescript-eslint/types': 7.2.0 + '@typescript-eslint/visitor-keys': 7.2.0 + debug: 4.3.5 globby: 11.1.0 is-glob: 4.0.3 - minimatch: 9.0.4 - semver: 7.6.0 - ts-api-utils: 1.3.0(typescript@5.4.5) + minimatch: 9.0.3 + semver: 7.6.3 + ts-api-utils: 1.3.0(typescript@5.5.4) optionalDependencies: - typescript: 5.4.5 + typescript: 5.5.4 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@7.8.0(eslint@9.1.1)(typescript@5.4.5)': + '@typescript-eslint/utils@7.17.0(eslint@9.7.0)(typescript@5.5.4)': dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.1.1) - '@types/json-schema': 7.0.15 - '@types/semver': 7.5.8 - '@typescript-eslint/scope-manager': 7.8.0 - '@typescript-eslint/types': 7.8.0 - '@typescript-eslint/typescript-estree': 7.8.0(typescript@5.4.5) - eslint: 9.1.1 - semver: 7.6.0 + '@eslint-community/eslint-utils': 4.4.0(eslint@9.7.0) + '@typescript-eslint/scope-manager': 7.17.0 + '@typescript-eslint/types': 7.17.0 + '@typescript-eslint/typescript-estree': 7.17.0(typescript@5.5.4) + eslint: 9.7.0 transitivePeerDependencies: - supports-color - typescript - '@typescript-eslint/visitor-keys@7.2.0': + '@typescript-eslint/visitor-keys@7.17.0': dependencies: - '@typescript-eslint/types': 7.2.0 + '@typescript-eslint/types': 7.17.0 eslint-visitor-keys: 3.4.3 - '@typescript-eslint/visitor-keys@7.8.0': + '@typescript-eslint/visitor-keys@7.2.0': dependencies: - '@typescript-eslint/types': 7.8.0 + '@typescript-eslint/types': 7.2.0 eslint-visitor-keys: 3.4.3 - '@vitejs/plugin-react-swc@3.6.0(@swc/helpers@0.5.11)(vite@5.2.10(@types/node@20.12.8))': + '@vitejs/plugin-react-swc@3.7.0(@swc/helpers@0.5.12)(vite@5.3.4(@types/node@20.14.11))': dependencies: - '@swc/core': 1.4.17(@swc/helpers@0.5.11) - vite: 5.2.10(@types/node@20.12.8) + '@swc/core': 1.7.0(@swc/helpers@0.5.12) + vite: 5.3.4(@types/node@20.14.11) transitivePeerDependencies: - '@swc/helpers' - '@vitejs/plugin-react@4.2.1(vite@5.2.10(@types/node@20.12.8))': + '@vitejs/plugin-react@4.3.1(vite@5.3.4(@types/node@20.14.11))': dependencies: - '@babel/core': 7.24.5 - '@babel/plugin-transform-react-jsx-self': 7.24.5(@babel/core@7.24.5) - '@babel/plugin-transform-react-jsx-source': 7.24.1(@babel/core@7.24.5) + '@babel/core': 7.24.9 + '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.24.9) + '@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.24.9) '@types/babel__core': 7.20.5 react-refresh: 0.14.2 - vite: 5.2.10(@types/node@20.12.8) + vite: 5.3.4(@types/node@20.14.11) transitivePeerDependencies: - supports-color - acorn-jsx@5.3.2(acorn@8.11.3): + acorn-jsx@5.3.2(acorn@8.12.1): dependencies: - acorn: 8.11.3 + acorn: 8.12.1 - acorn@8.11.3: {} + acorn@8.12.1: {} ajv@6.12.6: dependencies: @@ -3489,9 +3527,9 @@ snapshots: argparse@2.0.1: {} - aria-query@5.3.0: + aria-query@5.1.3: dependencies: - dequal: 2.0.3 + deep-equal: 2.2.3 array-buffer-byte-length@1.0.1: dependencies: @@ -3541,14 +3579,7 @@ snapshots: es-abstract: 1.23.3 es-shim-unscopables: 1.0.2 - array.prototype.toreversed@1.1.2: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-shim-unscopables: 1.0.2 - - array.prototype.tosorted@1.1.3: + array.prototype.tosorted@1.1.4: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 @@ -3569,25 +3600,25 @@ snapshots: ast-types-flow@0.0.8: {} - autoprefixer@10.4.19(postcss@8.4.38): + autoprefixer@10.4.19(postcss@8.4.39): dependencies: - browserslist: 4.23.0 - caniuse-lite: 1.0.30001614 + browserslist: 4.23.2 + caniuse-lite: 1.0.30001643 fraction.js: 4.3.7 normalize-range: 0.1.2 - picocolors: 1.0.0 - postcss: 8.4.38 + picocolors: 1.0.1 + postcss: 8.4.39 postcss-value-parser: 4.2.0 available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.0.0 - axe-core@4.7.0: {} + axe-core@4.9.1: {} - axobject-query@3.2.1: + axobject-query@3.1.1: dependencies: - dequal: 2.0.3 + deep-equal: 2.2.3 balanced-match@1.0.2: {} @@ -3602,20 +3633,20 @@ snapshots: dependencies: balanced-match: 1.0.2 - braces@3.0.2: + braces@3.0.3: dependencies: - fill-range: 7.0.1 + fill-range: 7.1.1 - browserslist@4.23.0: + browserslist@4.23.2: dependencies: - caniuse-lite: 1.0.30001614 - electron-to-chromium: 1.4.753 - node-releases: 2.0.14 - update-browserslist-db: 1.0.14(browserslist@4.23.0) + caniuse-lite: 1.0.30001643 + electron-to-chromium: 1.4.832 + node-releases: 2.0.18 + update-browserslist-db: 1.1.0(browserslist@4.23.2) - bundle-require@4.0.3(esbuild@0.19.12): + bundle-require@5.0.0(esbuild@0.23.0): dependencies: - esbuild: 0.19.12 + esbuild: 0.23.0 load-tsconfig: 0.2.5 busboy@1.6.0: @@ -3636,7 +3667,7 @@ snapshots: camelcase-css@2.0.1: {} - caniuse-lite@1.0.30001614: {} + caniuse-lite@1.0.30001643: {} chalk@2.4.2: dependencies: @@ -3658,7 +3689,7 @@ snapshots: chokidar@3.6.0: dependencies: anymatch: 3.1.3 - braces: 3.0.2 + braces: 3.0.3 glob-parent: 5.1.2 is-binary-path: 2.1.0 is-glob: 4.0.3 @@ -3705,6 +3736,8 @@ snapshots: tree-kill: 1.2.2 yargs: 17.7.2 + consola@3.2.3: {} + convert-source-map@2.0.0: {} copy-to-clipboard@3.3.3: @@ -3743,16 +3776,37 @@ snapshots: date-fns@2.30.0: dependencies: - '@babel/runtime': 7.24.5 + '@babel/runtime': 7.24.8 debug@3.2.7: dependencies: ms: 2.1.3 - debug@4.3.4: + debug@4.3.5: dependencies: ms: 2.1.2 + deep-equal@2.2.3: + dependencies: + array-buffer-byte-length: 1.0.1 + call-bind: 1.0.7 + es-get-iterator: 1.1.3 + get-intrinsic: 1.2.4 + is-arguments: 1.1.1 + is-array-buffer: 3.0.4 + is-date-object: 1.0.5 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.3 + isarray: 2.0.5 + object-is: 1.1.6 + object-keys: 1.1.1 + object.assign: 4.1.5 + regexp.prototype.flags: 1.5.2 + side-channel: 1.0.6 + which-boxed-primitive: 1.0.2 + which-collection: 1.0.2 + which-typed-array: 1.1.15 + deep-is@0.1.4: {} define-data-property@1.1.4: @@ -3767,8 +3821,6 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 - dequal@2.0.3: {} - didyoumean@1.2.2: {} dir-glob@3.0.1: @@ -3785,13 +3837,13 @@ snapshots: eastasianwidth@0.2.0: {} - electron-to-chromium@1.4.753: {} + electron-to-chromium@1.4.832: {} emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} - enhanced-resolve@5.16.0: + enhanced-resolve@5.17.0: dependencies: graceful-fs: 4.2.11 tapable: 2.2.1 @@ -3829,7 +3881,7 @@ snapshots: is-string: 1.0.7 is-typed-array: 1.1.13 is-weakref: 1.0.2 - object-inspect: 1.13.1 + object-inspect: 1.13.2 object-keys: 1.1.1 object.assign: 4.1.5 regexp.prototype.flags: 1.5.2 @@ -3851,6 +3903,18 @@ snapshots: es-errors@1.3.0: {} + es-get-iterator@1.1.3: + dependencies: + call-bind: 1.0.7 + get-intrinsic: 1.2.4 + has-symbols: 1.0.3 + is-arguments: 1.1.1 + is-map: 2.0.3 + is-set: 2.0.3 + is-string: 1.0.7 + isarray: 2.0.5 + stop-iteration-iterator: 1.0.0 + es-iterator-helpers@1.0.19: dependencies: call-bind: 1.0.7 @@ -3888,57 +3952,58 @@ snapshots: is-date-object: 1.0.5 is-symbol: 1.0.4 - esbuild@0.19.12: + esbuild@0.21.5: optionalDependencies: - '@esbuild/aix-ppc64': 0.19.12 - '@esbuild/android-arm': 0.19.12 - '@esbuild/android-arm64': 0.19.12 - '@esbuild/android-x64': 0.19.12 - '@esbuild/darwin-arm64': 0.19.12 - '@esbuild/darwin-x64': 0.19.12 - '@esbuild/freebsd-arm64': 0.19.12 - '@esbuild/freebsd-x64': 0.19.12 - '@esbuild/linux-arm': 0.19.12 - '@esbuild/linux-arm64': 0.19.12 - '@esbuild/linux-ia32': 0.19.12 - '@esbuild/linux-loong64': 0.19.12 - '@esbuild/linux-mips64el': 0.19.12 - '@esbuild/linux-ppc64': 0.19.12 - '@esbuild/linux-riscv64': 0.19.12 - '@esbuild/linux-s390x': 0.19.12 - '@esbuild/linux-x64': 0.19.12 - '@esbuild/netbsd-x64': 0.19.12 - '@esbuild/openbsd-x64': 0.19.12 - '@esbuild/sunos-x64': 0.19.12 - '@esbuild/win32-arm64': 0.19.12 - '@esbuild/win32-ia32': 0.19.12 - '@esbuild/win32-x64': 0.19.12 - - esbuild@0.20.2: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + + esbuild@0.23.0: optionalDependencies: - '@esbuild/aix-ppc64': 0.20.2 - '@esbuild/android-arm': 0.20.2 - '@esbuild/android-arm64': 0.20.2 - '@esbuild/android-x64': 0.20.2 - '@esbuild/darwin-arm64': 0.20.2 - '@esbuild/darwin-x64': 0.20.2 - '@esbuild/freebsd-arm64': 0.20.2 - '@esbuild/freebsd-x64': 0.20.2 - '@esbuild/linux-arm': 0.20.2 - '@esbuild/linux-arm64': 0.20.2 - '@esbuild/linux-ia32': 0.20.2 - '@esbuild/linux-loong64': 0.20.2 - '@esbuild/linux-mips64el': 0.20.2 - '@esbuild/linux-ppc64': 0.20.2 - '@esbuild/linux-riscv64': 0.20.2 - '@esbuild/linux-s390x': 0.20.2 - '@esbuild/linux-x64': 0.20.2 - '@esbuild/netbsd-x64': 0.20.2 - '@esbuild/openbsd-x64': 0.20.2 - '@esbuild/sunos-x64': 0.20.2 - '@esbuild/win32-arm64': 0.20.2 - '@esbuild/win32-ia32': 0.20.2 - '@esbuild/win32-x64': 0.20.2 + '@esbuild/aix-ppc64': 0.23.0 + '@esbuild/android-arm': 0.23.0 + '@esbuild/android-arm64': 0.23.0 + '@esbuild/android-x64': 0.23.0 + '@esbuild/darwin-arm64': 0.23.0 + '@esbuild/darwin-x64': 0.23.0 + '@esbuild/freebsd-arm64': 0.23.0 + '@esbuild/freebsd-x64': 0.23.0 + '@esbuild/linux-arm': 0.23.0 + '@esbuild/linux-arm64': 0.23.0 + '@esbuild/linux-ia32': 0.23.0 + '@esbuild/linux-loong64': 0.23.0 + '@esbuild/linux-mips64el': 0.23.0 + '@esbuild/linux-ppc64': 0.23.0 + '@esbuild/linux-riscv64': 0.23.0 + '@esbuild/linux-s390x': 0.23.0 + '@esbuild/linux-x64': 0.23.0 + '@esbuild/netbsd-x64': 0.23.0 + '@esbuild/openbsd-arm64': 0.23.0 + '@esbuild/openbsd-x64': 0.23.0 + '@esbuild/sunos-x64': 0.23.0 + '@esbuild/win32-arm64': 0.23.0 + '@esbuild/win32-ia32': 0.23.0 + '@esbuild/win32-x64': 0.23.0 escalade@3.1.2: {} @@ -3946,51 +4011,51 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-config-next@14.2.3(eslint@9.1.1)(typescript@5.4.5): + eslint-config-next@14.2.5(eslint@9.7.0)(typescript@5.5.4): dependencies: - '@next/eslint-plugin-next': 14.2.3 - '@rushstack/eslint-patch': 1.10.2 - '@typescript-eslint/parser': 7.2.0(eslint@9.1.1)(typescript@5.4.5) - eslint: 9.1.1 + '@next/eslint-plugin-next': 14.2.5 + '@rushstack/eslint-patch': 1.10.3 + '@typescript-eslint/parser': 7.2.0(eslint@9.7.0)(typescript@5.5.4) + eslint: 9.7.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.1.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.1.1) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@9.1.1)(typescript@5.4.5))(eslint-import-resolver-typescript@3.6.1)(eslint@9.1.1) - eslint-plugin-jsx-a11y: 6.8.0(eslint@9.1.1) - eslint-plugin-react: 7.34.1(eslint@9.1.1) - eslint-plugin-react-hooks: 4.6.2(eslint@9.1.1) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.7.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.7.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@9.7.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@9.7.0) + eslint-plugin-jsx-a11y: 6.9.0(eslint@9.7.0) + eslint-plugin-react: 7.35.0(eslint@9.7.0) + eslint-plugin-react-hooks: 4.6.2(eslint@9.7.0) optionalDependencies: - typescript: 5.4.5 + typescript: 5.5.4 transitivePeerDependencies: - eslint-import-resolver-webpack - supports-color - eslint-config-prettier@9.1.0(eslint@9.1.1): + eslint-config-prettier@9.1.0(eslint@9.7.0): dependencies: - eslint: 9.1.1 + eslint: 9.7.0 - eslint-config-turbo@1.13.3(eslint@9.1.1): + eslint-config-turbo@2.0.9(eslint@9.7.0): dependencies: - eslint: 9.1.1 - eslint-plugin-turbo: 1.13.3(eslint@9.1.1) + eslint: 9.7.0 + eslint-plugin-turbo: 2.0.9(eslint@9.7.0) eslint-import-resolver-node@0.3.9: dependencies: debug: 3.2.7 - is-core-module: 2.13.1 + is-core-module: 2.15.0 resolve: 1.22.8 transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.1.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.1.1): + eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.7.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.7.0): dependencies: - debug: 4.3.4 - enhanced-resolve: 5.16.0 - eslint: 9.1.1 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@9.1.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.1.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.1.1))(eslint@9.1.1) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@9.1.1)(typescript@5.4.5))(eslint-import-resolver-typescript@3.6.1)(eslint@9.1.1) + debug: 4.3.5 + enhanced-resolve: 5.17.0 + eslint: 9.7.0 + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@9.7.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.7.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.7.0))(eslint@9.7.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@9.7.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@9.7.0) fast-glob: 3.3.2 - get-tsconfig: 4.7.3 - is-core-module: 2.13.1 + get-tsconfig: 4.7.6 + is-core-module: 2.15.0 is-glob: 4.0.3 transitivePeerDependencies: - '@typescript-eslint/parser' @@ -3998,18 +4063,18 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@9.1.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.1.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.1.1))(eslint@9.1.1): + eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@9.7.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.7.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.7.0))(eslint@9.7.0): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 7.2.0(eslint@9.1.1)(typescript@5.4.5) - eslint: 9.1.1 + '@typescript-eslint/parser': 7.2.0(eslint@9.7.0)(typescript@5.5.4) + eslint: 9.7.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.1.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.1.1) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.7.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.7.0) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@9.1.1)(typescript@5.4.5))(eslint-import-resolver-typescript@3.6.1)(eslint@9.1.1): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@9.7.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@9.7.0): dependencies: array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 @@ -4017,11 +4082,11 @@ snapshots: array.prototype.flatmap: 1.3.2 debug: 3.2.7 doctrine: 2.1.0 - eslint: 9.1.1 + eslint: 9.7.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@9.1.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.1.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.1.1))(eslint@9.1.1) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@9.7.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.7.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.7.0))(eslint@9.7.0) hasown: 2.0.2 - is-core-module: 2.13.1 + is-core-module: 2.15.0 is-glob: 4.0.3 minimatch: 3.1.2 object.fromentries: 2.0.8 @@ -4030,68 +4095,68 @@ snapshots: semver: 6.3.1 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 7.2.0(eslint@9.1.1)(typescript@5.4.5) + '@typescript-eslint/parser': 7.2.0(eslint@9.7.0)(typescript@5.5.4) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-jsx-a11y@6.8.0(eslint@9.1.1): + eslint-plugin-jsx-a11y@6.9.0(eslint@9.7.0): dependencies: - '@babel/runtime': 7.24.5 - aria-query: 5.3.0 + aria-query: 5.1.3 array-includes: 3.1.8 array.prototype.flatmap: 1.3.2 ast-types-flow: 0.0.8 - axe-core: 4.7.0 - axobject-query: 3.2.1 + axe-core: 4.9.1 + axobject-query: 3.1.1 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 es-iterator-helpers: 1.0.19 - eslint: 9.1.1 + eslint: 9.7.0 hasown: 2.0.2 jsx-ast-utils: 3.3.5 language-tags: 1.0.9 minimatch: 3.1.2 - object.entries: 1.1.8 object.fromentries: 2.0.8 + safe-regex-test: 1.0.3 + string.prototype.includes: 2.0.0 - eslint-plugin-react-hooks@4.6.2(eslint@9.1.1): + eslint-plugin-react-hooks@4.6.2(eslint@9.7.0): dependencies: - eslint: 9.1.1 + eslint: 9.7.0 - eslint-plugin-react-refresh@0.4.6(eslint@9.1.1): + eslint-plugin-react-refresh@0.4.9(eslint@9.7.0): dependencies: - eslint: 9.1.1 + eslint: 9.7.0 - eslint-plugin-react@7.34.1(eslint@9.1.1): + eslint-plugin-react@7.35.0(eslint@9.7.0): dependencies: array-includes: 3.1.8 array.prototype.findlast: 1.2.5 array.prototype.flatmap: 1.3.2 - array.prototype.toreversed: 1.1.2 - array.prototype.tosorted: 1.1.3 + array.prototype.tosorted: 1.1.4 doctrine: 2.1.0 es-iterator-helpers: 1.0.19 - eslint: 9.1.1 + eslint: 9.7.0 estraverse: 5.3.0 + hasown: 2.0.2 jsx-ast-utils: 3.3.5 minimatch: 3.1.2 object.entries: 1.1.8 object.fromentries: 2.0.8 - object.hasown: 1.1.4 object.values: 1.2.0 prop-types: 15.8.1 resolve: 2.0.0-next.5 semver: 6.3.1 string.prototype.matchall: 4.0.11 + string.prototype.repeat: 1.0.0 - eslint-plugin-turbo@1.13.3(eslint@9.1.1): + eslint-plugin-turbo@2.0.9(eslint@9.7.0): dependencies: dotenv: 16.0.3 - eslint: 9.1.1 + eslint: 9.7.0 - eslint-scope@8.0.1: + eslint-scope@8.0.2: dependencies: esrecurse: 4.3.0 estraverse: 5.3.0 @@ -4100,25 +4165,25 @@ snapshots: eslint-visitor-keys@4.0.0: {} - eslint@9.1.1: + eslint@9.7.0: dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.1.1) - '@eslint-community/regexpp': 4.10.0 - '@eslint/eslintrc': 3.0.2 - '@eslint/js': 9.1.1 - '@humanwhocodes/config-array': 0.13.0 + '@eslint-community/eslint-utils': 4.4.0(eslint@9.7.0) + '@eslint-community/regexpp': 4.11.0 + '@eslint/config-array': 0.17.1 + '@eslint/eslintrc': 3.1.0 + '@eslint/js': 9.7.0 '@humanwhocodes/module-importer': 1.0.1 - '@humanwhocodes/retry': 0.2.3 + '@humanwhocodes/retry': 0.3.0 '@nodelib/fs.walk': 1.2.8 ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.4 + debug: 4.3.5 escape-string-regexp: 4.0.0 - eslint-scope: 8.0.1 + eslint-scope: 8.0.2 eslint-visitor-keys: 4.0.0 - espree: 10.0.1 - esquery: 1.5.0 + espree: 10.1.0 + esquery: 1.6.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 file-entry-cache: 8.0.0 @@ -4139,13 +4204,13 @@ snapshots: transitivePeerDependencies: - supports-color - espree@10.0.1: + espree@10.1.0: dependencies: - acorn: 8.11.3 - acorn-jsx: 5.3.2(acorn@8.11.3) + acorn: 8.12.1 + acorn-jsx: 5.3.2(acorn@8.12.1) eslint-visitor-keys: 4.0.0 - esquery@1.5.0: + esquery@1.6.0: dependencies: estraverse: 5.3.0 @@ -4177,7 +4242,7 @@ snapshots: '@nodelib/fs.walk': 1.2.8 glob-parent: 5.1.2 merge2: 1.4.1 - micromatch: 4.0.5 + micromatch: 4.0.7 fast-json-stable-stringify@2.1.0: {} @@ -4195,7 +4260,7 @@ snapshots: dependencies: flat-cache: 4.0.1 - fill-range@7.0.1: + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -4215,7 +4280,7 @@ snapshots: dependencies: is-callable: 1.2.7 - foreground-child@3.1.1: + foreground-child@3.2.1: dependencies: cross-spawn: 7.0.3 signal-exit: 4.1.0 @@ -4258,7 +4323,7 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.2.4 - get-tsconfig@4.7.3: + get-tsconfig@4.7.6: dependencies: resolve-pkg-maps: 1.0.0 @@ -4272,19 +4337,20 @@ snapshots: glob@10.3.10: dependencies: - foreground-child: 3.1.1 + foreground-child: 3.2.1 jackspeak: 2.3.6 - minimatch: 9.0.4 - minipass: 7.0.4 - path-scurry: 1.10.2 + minimatch: 9.0.5 + minipass: 7.1.2 + path-scurry: 1.11.1 - glob@10.3.12: + glob@10.4.5: dependencies: - foreground-child: 3.1.1 - jackspeak: 2.3.6 - minimatch: 9.0.4 - minipass: 7.0.4 - path-scurry: 1.10.2 + foreground-child: 3.2.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.0 + path-scurry: 1.11.1 globals@11.12.0: {} @@ -4370,6 +4436,11 @@ snapshots: is-alphabetical: 1.0.4 is-decimal: 1.0.4 + is-arguments@1.1.1: + dependencies: + call-bind: 1.0.7 + has-tostringtag: 1.0.2 + is-array-buffer@3.0.4: dependencies: call-bind: 1.0.7 @@ -4394,7 +4465,7 @@ snapshots: is-callable@1.2.7: {} - is-core-module@2.13.1: + is-core-module@2.15.0: dependencies: hasown: 2.0.2 @@ -4492,7 +4563,13 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 - jiti@1.21.0: {} + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jiti@1.21.6: {} joycon@3.1.1: {} @@ -4527,11 +4604,11 @@ snapshots: dependencies: json-buffer: 3.0.1 - language-subtag-registry@0.3.22: {} + language-subtag-registry@0.3.23: {} language-tags@1.0.9: dependencies: - language-subtag-registry: 0.3.22 + language-subtag-registry: 0.3.23 levn@0.4.1: dependencies: @@ -4540,7 +4617,7 @@ snapshots: lilconfig@2.1.0: {} - lilconfig@3.1.1: {} + lilconfig@3.1.2: {} lines-and-columns@1.2.4: {} @@ -4565,23 +4642,19 @@ snapshots: fault: 1.0.4 highlight.js: 10.7.3 - lru-cache@10.2.2: {} + lru-cache@10.4.3: {} lru-cache@5.1.1: dependencies: yallist: 3.1.1 - lru-cache@6.0.0: - dependencies: - yallist: 4.0.0 - merge-stream@2.0.0: {} merge2@1.4.1: {} - micromatch@4.0.5: + micromatch@4.0.7: dependencies: - braces: 3.0.2 + braces: 3.0.3 picomatch: 2.3.1 mimic-fn@2.1.0: {} @@ -4594,13 +4667,13 @@ snapshots: dependencies: brace-expansion: 2.0.1 - minimatch@9.0.4: + minimatch@9.0.5: dependencies: brace-expansion: 2.0.1 minimist@1.2.8: {} - minipass@7.0.4: {} + minipass@7.1.2: {} ms@2.1.2: {} @@ -4616,32 +4689,32 @@ snapshots: natural-compare@1.4.0: {} - next@14.2.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + next@14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@next/env': 14.2.3 + '@next/env': 14.2.5 '@swc/helpers': 0.5.5 busboy: 1.6.0 - caniuse-lite: 1.0.30001614 + caniuse-lite: 1.0.30001643 graceful-fs: 4.2.11 postcss: 8.4.31 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) styled-jsx: 5.1.1(react@18.3.1) optionalDependencies: - '@next/swc-darwin-arm64': 14.2.3 - '@next/swc-darwin-x64': 14.2.3 - '@next/swc-linux-arm64-gnu': 14.2.3 - '@next/swc-linux-arm64-musl': 14.2.3 - '@next/swc-linux-x64-gnu': 14.2.3 - '@next/swc-linux-x64-musl': 14.2.3 - '@next/swc-win32-arm64-msvc': 14.2.3 - '@next/swc-win32-ia32-msvc': 14.2.3 - '@next/swc-win32-x64-msvc': 14.2.3 + '@next/swc-darwin-arm64': 14.2.5 + '@next/swc-darwin-x64': 14.2.5 + '@next/swc-linux-arm64-gnu': 14.2.5 + '@next/swc-linux-arm64-musl': 14.2.5 + '@next/swc-linux-x64-gnu': 14.2.5 + '@next/swc-linux-x64-musl': 14.2.5 + '@next/swc-win32-arm64-msvc': 14.2.5 + '@next/swc-win32-ia32-msvc': 14.2.5 + '@next/swc-win32-x64-msvc': 14.2.5 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros - node-releases@2.0.14: {} + node-releases@2.0.18: {} normalize-path@3.0.0: {} @@ -4655,7 +4728,12 @@ snapshots: object-hash@3.0.0: {} - object-inspect@1.13.1: {} + object-inspect@1.13.2: {} + + object-is@1.1.6: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 object-keys@1.1.1: {} @@ -4685,12 +4763,6 @@ snapshots: define-properties: 1.2.1 es-abstract: 1.23.3 - object.hasown@1.1.4: - dependencies: - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-object-atoms: 1.0.0 - object.values@1.2.0: dependencies: call-bind: 1.0.7 @@ -4718,6 +4790,8 @@ snapshots: dependencies: p-limit: 3.1.0 + package-json-from-dist@1.0.0: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -4737,14 +4811,14 @@ snapshots: path-parse@1.0.7: {} - path-scurry@1.10.2: + path-scurry@1.11.1: dependencies: - lru-cache: 10.2.2 - minipass: 7.0.4 + lru-cache: 10.4.3 + minipass: 7.1.2 path-type@4.0.0: {} - picocolors@1.0.0: {} + picocolors@1.0.1: {} picomatch@2.3.1: {} @@ -4754,31 +4828,39 @@ snapshots: possible-typed-array-names@1.0.0: {} - postcss-import@15.1.0(postcss@8.4.38): + postcss-import@15.1.0(postcss@8.4.39): dependencies: - postcss: 8.4.38 + postcss: 8.4.39 postcss-value-parser: 4.2.0 read-cache: 1.0.0 resolve: 1.22.8 - postcss-js@4.0.1(postcss@8.4.38): + postcss-js@4.0.1(postcss@8.4.39): dependencies: camelcase-css: 2.0.1 - postcss: 8.4.38 + postcss: 8.4.39 - postcss-load-config@4.0.2(postcss@8.4.38): + postcss-load-config@4.0.2(postcss@8.4.39): dependencies: - lilconfig: 3.1.1 - yaml: 2.4.2 + lilconfig: 3.1.2 + yaml: 2.4.5 optionalDependencies: - postcss: 8.4.38 + postcss: 8.4.39 - postcss-nested@6.0.1(postcss@8.4.38): + postcss-load-config@6.0.1(jiti@1.21.6)(postcss@8.4.39)(yaml@2.4.5): dependencies: - postcss: 8.4.38 - postcss-selector-parser: 6.0.16 + lilconfig: 3.1.2 + optionalDependencies: + jiti: 1.21.6 + postcss: 8.4.39 + yaml: 2.4.5 - postcss-selector-parser@6.0.16: + postcss-nested@6.2.0(postcss@8.4.39): + dependencies: + postcss: 8.4.39 + postcss-selector-parser: 6.1.1 + + postcss-selector-parser@6.1.1: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 @@ -4788,18 +4870,18 @@ snapshots: postcss@8.4.31: dependencies: nanoid: 3.3.7 - picocolors: 1.0.0 + picocolors: 1.0.1 source-map-js: 1.2.0 - postcss@8.4.38: + postcss@8.4.39: dependencies: nanoid: 3.3.7 - picocolors: 1.0.0 + picocolors: 1.0.1 source-map-js: 1.2.0 prelude-ls@1.2.1: {} - prettier@3.2.5: {} + prettier@3.3.3: {} prismjs@1.27.0: {} @@ -4831,7 +4913,7 @@ snapshots: react-syntax-highlighter@15.5.0(react@18.3.1): dependencies: - '@babel/runtime': 7.24.5 + '@babel/runtime': 7.24.8 highlight.js: 10.7.3 lowlight: 1.20.0 prismjs: 1.29.0 @@ -4885,38 +4967,38 @@ snapshots: resolve@1.22.8: dependencies: - is-core-module: 2.13.1 + is-core-module: 2.15.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 resolve@2.0.0-next.5: dependencies: - is-core-module: 2.13.1 + is-core-module: 2.15.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 reusify@1.0.4: {} - rollup@4.17.2: + rollup@4.19.0: dependencies: '@types/estree': 1.0.5 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.17.2 - '@rollup/rollup-android-arm64': 4.17.2 - '@rollup/rollup-darwin-arm64': 4.17.2 - '@rollup/rollup-darwin-x64': 4.17.2 - '@rollup/rollup-linux-arm-gnueabihf': 4.17.2 - '@rollup/rollup-linux-arm-musleabihf': 4.17.2 - '@rollup/rollup-linux-arm64-gnu': 4.17.2 - '@rollup/rollup-linux-arm64-musl': 4.17.2 - '@rollup/rollup-linux-powerpc64le-gnu': 4.17.2 - '@rollup/rollup-linux-riscv64-gnu': 4.17.2 - '@rollup/rollup-linux-s390x-gnu': 4.17.2 - '@rollup/rollup-linux-x64-gnu': 4.17.2 - '@rollup/rollup-linux-x64-musl': 4.17.2 - '@rollup/rollup-win32-arm64-msvc': 4.17.2 - '@rollup/rollup-win32-ia32-msvc': 4.17.2 - '@rollup/rollup-win32-x64-msvc': 4.17.2 + '@rollup/rollup-android-arm-eabi': 4.19.0 + '@rollup/rollup-android-arm64': 4.19.0 + '@rollup/rollup-darwin-arm64': 4.19.0 + '@rollup/rollup-darwin-x64': 4.19.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.19.0 + '@rollup/rollup-linux-arm-musleabihf': 4.19.0 + '@rollup/rollup-linux-arm64-gnu': 4.19.0 + '@rollup/rollup-linux-arm64-musl': 4.19.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.19.0 + '@rollup/rollup-linux-riscv64-gnu': 4.19.0 + '@rollup/rollup-linux-s390x-gnu': 4.19.0 + '@rollup/rollup-linux-x64-gnu': 4.19.0 + '@rollup/rollup-linux-x64-musl': 4.19.0 + '@rollup/rollup-win32-arm64-msvc': 4.19.0 + '@rollup/rollup-win32-ia32-msvc': 4.19.0 + '@rollup/rollup-win32-x64-msvc': 4.19.0 fsevents: 2.3.3 run-parallel@1.2.0: @@ -4925,7 +5007,7 @@ snapshots: rxjs@7.8.1: dependencies: - tslib: 2.6.2 + tslib: 2.6.3 safe-array-concat@1.1.2: dependencies: @@ -4946,9 +5028,7 @@ snapshots: semver@6.3.1: {} - semver@7.6.0: - dependencies: - lru-cache: 6.0.0 + semver@7.6.3: {} set-function-length@1.2.2: dependencies: @@ -4979,7 +5059,7 @@ snapshots: call-bind: 1.0.7 es-errors: 1.3.0 get-intrinsic: 1.2.4 - object-inspect: 1.13.1 + object-inspect: 1.13.2 signal-exit@3.0.7: {} @@ -4997,6 +5077,10 @@ snapshots: spawn-command@0.0.2: {} + stop-iteration-iterator@1.0.0: + dependencies: + internal-slot: 1.0.7 + streamsearch@1.1.0: {} string-width@4.2.3: @@ -5011,6 +5095,11 @@ snapshots: emoji-regex: 9.2.2 strip-ansi: 7.1.0 + string.prototype.includes@2.0.0: + dependencies: + define-properties: 1.2.1 + es-abstract: 1.23.3 + string.prototype.matchall@4.0.11: dependencies: call-bind: 1.0.7 @@ -5026,6 +5115,11 @@ snapshots: set-function-name: 2.0.2 side-channel: 1.0.6 + string.prototype.repeat@1.0.0: + dependencies: + define-properties: 1.2.1 + es-abstract: 1.23.3 + string.prototype.trim@1.2.9: dependencies: call-bind: 1.0.7 @@ -5068,7 +5162,7 @@ snapshots: dependencies: '@jridgewell/gen-mapping': 0.3.5 commander: 4.1.1 - glob: 10.3.12 + glob: 10.4.5 lines-and-columns: 1.2.4 mz: 2.7.0 pirates: 4.0.6 @@ -5088,7 +5182,7 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - tailwindcss@3.4.3: + tailwindcss@3.4.6: dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -5098,18 +5192,18 @@ snapshots: fast-glob: 3.3.2 glob-parent: 6.0.2 is-glob: 4.0.3 - jiti: 1.21.0 + jiti: 1.21.6 lilconfig: 2.1.0 - micromatch: 4.0.5 + micromatch: 4.0.7 normalize-path: 3.0.0 object-hash: 3.0.0 - picocolors: 1.0.0 - postcss: 8.4.38 - postcss-import: 15.1.0(postcss@8.4.38) - postcss-js: 4.0.1(postcss@8.4.38) - postcss-load-config: 4.0.2(postcss@8.4.38) - postcss-nested: 6.0.1(postcss@8.4.38) - postcss-selector-parser: 6.0.16 + picocolors: 1.0.1 + postcss: 8.4.39 + postcss-import: 15.1.0(postcss@8.4.39) + postcss-js: 4.0.1(postcss@8.4.39) + postcss-load-config: 4.0.2(postcss@8.4.39) + postcss-nested: 6.2.0(postcss@8.4.39) + postcss-selector-parser: 6.1.1 resolve: 1.22.8 sucrase: 3.35.0 transitivePeerDependencies: @@ -5141,9 +5235,9 @@ snapshots: tree-kill@1.2.2: {} - ts-api-utils@1.3.0(typescript@5.4.5): + ts-api-utils@1.3.0(typescript@5.5.4): dependencies: - typescript: 5.4.5 + typescript: 5.5.4 ts-interface-checker@0.1.13: {} @@ -5154,58 +5248,62 @@ snapshots: minimist: 1.2.8 strip-bom: 3.0.0 - tslib@2.6.2: {} + tslib@2.6.3: {} - tsup@8.0.2(@swc/core@1.4.17(@swc/helpers@0.5.11))(postcss@8.4.38)(typescript@5.4.5): + tsup@8.2.2(@swc/core@1.7.0)(jiti@1.21.6)(postcss@8.4.39)(typescript@5.5.4)(yaml@2.4.5): dependencies: - bundle-require: 4.0.3(esbuild@0.19.12) + bundle-require: 5.0.0(esbuild@0.23.0) cac: 6.7.14 chokidar: 3.6.0 - debug: 4.3.4 - esbuild: 0.19.12 + consola: 3.2.3 + debug: 4.3.5 + esbuild: 0.23.0 execa: 5.1.1 globby: 11.1.0 joycon: 3.1.1 - postcss-load-config: 4.0.2(postcss@8.4.38) + picocolors: 1.0.1 + postcss-load-config: 6.0.1(jiti@1.21.6)(postcss@8.4.39)(yaml@2.4.5) resolve-from: 5.0.0 - rollup: 4.17.2 + rollup: 4.19.0 source-map: 0.8.0-beta.0 sucrase: 3.35.0 tree-kill: 1.2.2 optionalDependencies: - '@swc/core': 1.4.17(@swc/helpers@0.5.11) - postcss: 8.4.38 - typescript: 5.4.5 + '@swc/core': 1.7.0(@swc/helpers@0.5.12) + postcss: 8.4.39 + typescript: 5.5.4 transitivePeerDependencies: + - jiti - supports-color - - ts-node + - tsx + - yaml - turbo-darwin-64@1.13.3: + turbo-darwin-64@2.0.9: optional: true - turbo-darwin-arm64@1.13.3: + turbo-darwin-arm64@2.0.9: optional: true - turbo-linux-64@1.13.3: + turbo-linux-64@2.0.9: optional: true - turbo-linux-arm64@1.13.3: + turbo-linux-arm64@2.0.9: optional: true - turbo-windows-64@1.13.3: + turbo-windows-64@2.0.9: optional: true - turbo-windows-arm64@1.13.3: + turbo-windows-arm64@2.0.9: optional: true - turbo@1.13.3: + turbo@2.0.9: optionalDependencies: - turbo-darwin-64: 1.13.3 - turbo-darwin-arm64: 1.13.3 - turbo-linux-64: 1.13.3 - turbo-linux-arm64: 1.13.3 - turbo-windows-64: 1.13.3 - turbo-windows-arm64: 1.13.3 + turbo-darwin-64: 2.0.9 + turbo-darwin-arm64: 2.0.9 + turbo-linux-64: 2.0.9 + turbo-linux-arm64: 2.0.9 + turbo-windows-64: 2.0.9 + turbo-windows-arm64: 2.0.9 type-check@0.4.0: dependencies: @@ -5243,7 +5341,7 @@ snapshots: is-typed-array: 1.1.13 possible-typed-array-names: 1.0.0 - typescript@5.4.5: {} + typescript@5.5.4: {} unbox-primitive@1.0.2: dependencies: @@ -5254,11 +5352,11 @@ snapshots: undici-types@5.26.5: {} - update-browserslist-db@1.0.14(browserslist@4.23.0): + update-browserslist-db@1.1.0(browserslist@4.23.2): dependencies: - browserslist: 4.23.0 + browserslist: 4.23.2 escalade: 3.1.2 - picocolors: 1.0.0 + picocolors: 1.0.1 uri-js@4.4.1: dependencies: @@ -5266,19 +5364,19 @@ snapshots: util-deprecate@1.0.2: {} - vite-plugin-singlefile@2.0.1(rollup@4.17.2)(vite@5.2.10(@types/node@20.12.8)): + vite-plugin-singlefile@2.0.2(rollup@4.19.0)(vite@5.3.4(@types/node@20.14.11)): dependencies: - micromatch: 4.0.5 - rollup: 4.17.2 - vite: 5.2.10(@types/node@20.12.8) + micromatch: 4.0.7 + rollup: 4.19.0 + vite: 5.3.4(@types/node@20.14.11) - vite@5.2.10(@types/node@20.12.8): + vite@5.3.4(@types/node@20.14.11): dependencies: - esbuild: 0.20.2 - postcss: 8.4.38 - rollup: 4.17.2 + esbuild: 0.21.5 + postcss: 8.4.39 + rollup: 4.19.0 optionalDependencies: - '@types/node': 20.12.8 + '@types/node': 20.14.11 fsevents: 2.3.3 webidl-conversions@4.0.2: {} @@ -5351,9 +5449,7 @@ snapshots: yallist@3.1.1: {} - yallist@4.0.0: {} - - yaml@2.4.2: {} + yaml@2.4.5: {} yargs-parser@21.1.1: {} From 17ac70da05a6d1b17f2636432990f0be82b56d41 Mon Sep 17 00:00:00 2001 From: Allain Lalonde Date: Mon, 21 Oct 2024 13:20:12 -0400 Subject: [PATCH 007/168] fixing styled html bug in tailwindText (#124) --- packages/backend/src/tailwind/tailwindMain.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/tailwind/tailwindMain.ts b/packages/backend/src/tailwind/tailwindMain.ts index ed1ef67c..caeb3584 100644 --- a/packages/backend/src/tailwind/tailwindMain.ts +++ b/packages/backend/src/tailwind/tailwindMain.ts @@ -127,7 +127,7 @@ export const tailwindText = (node: TextNode, isJsx: boolean): string => { content = styledHtml[0].text; } else { content = styledHtml - .map((style) => `${style.text}`) + .map((style) => `${style.text}`) .join(""); } From d02ff6a3b0183220516c9d5e999427e84a050f8e Mon Sep 17 00:00:00 2001 From: "Mims H. Wright" Date: Fri, 13 Dec 2024 13:21:59 +0100 Subject: [PATCH 008/168] Build instructions (#132) * updates to build instructions, readme, package.json, turbo.json and added a .node-version file. * Added details about different packages to README * added .prettierrc to trigger prettier in vscode --- .node-version | 1 + .prettierrc | 1 + README.md | 30 +++++++++++++++++++++++++++++- package.json | 4 +++- turbo.json | 11 ++++++++++- 5 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 .node-version create mode 100644 .prettierrc diff --git a/.node-version b/.node-version new file mode 100644 index 00000000..dc0bb0f4 --- /dev/null +++ b/.node-version @@ -0,0 +1 @@ +v22.12.0 diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/.prettierrc @@ -0,0 +1 @@ +{} diff --git a/README.md b/README.md index 7bd98db6..76e02c7c 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,35 @@ When finding the unknown (a `Group` or `Frame` with more than one child and no v ## How to build the project -The project is configured to be built with Webpack or Rollup. The author couldn't find a way to correctly configure Svelte in Webpack, so Rollup was added. But Rollup is a lot less stable than Webpack and crashes regularly in watch mode when editing Typescript files. So, if you are going to work with Typescript only, I recommend sticking with Webpack. If you are going to make changes in the UI, you **need** to use Rollup for now. +### Package Manager + +The project is configured for [pnpm](https://pnpm.io/). To install, see the [installation notes for pnpm](https://pnpm.io/installation). + +### Monorepo + +The plugin is organized as a monorepo. There are several packages: + +- packages/backend - Contains the business logic that reads the Figma API and converts nodes +- packages/plugin-ui - Contains the common UI for the plugin +- packages/eslint-config-custom - Config file for ESLint +- packages/tsconfig - Collection of TSConfig files used throughout the project + +- apps/plugin - This is the actual plugin assembled from the parts in backend & plugin-ui. Within this folder it's divided between: + - plugin-src - loads the backend and compiles to code.js + - ui-src - loads the common plugin-ui and compiles to index.html +- apps/debug - This is a debug mode plugin that is a more convenient way to see all the UI elements. + +The plugin is built using Turbo which in turn builds the internal packages. + +#### Commands + +`pnpm run ...` + +- `dev` - runs the app in dev mode +- `build` +- `build:watch` +- `lint` +- `format` - formats with prettier (warning: may edit files!) ## Issues diff --git a/package.json b/package.json index cf32898e..76de04ed 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,13 @@ { + "name": "figma-to-code", "private": true, + "packageManager": "pnpm@9.14.4", "scripts": { "build": "turbo run build", "build:watch": "turbo run build:watch", "dev": "turbo run dev --concurrency 20", "lint": "turbo run lint", - "format": "prettier --write \"**/*.{ts,tsx,md}\"" + "format": "prettier --write \"**/*.{ts,tsx,css,md}\"" }, "devDependencies": { "eslint": "^9.7.0", diff --git a/turbo.json b/turbo.json index 6f46983a..91751d8e 100644 --- a/turbo.json +++ b/turbo.json @@ -1,9 +1,18 @@ { "$schema": "https://turbo.build/schema.json", - "pipeline": { + "tasks": { "build": { "outputs": [".next/**", "!.next/cache/**"] }, + "dev": { + "cache": false, + "persistent": true + }, + "lint": { + "outputs": [], + "dependsOn": ["^build"], + "inputs": ["src/**/*.tsx", "src/**/*.ts", "test/**/*.ts", "test/**/*.tsx", ".eslintrc.js"] + }, "type-check": {} } } \ No newline at end of file From d430e1d97abbde75952e314c61a5b5945aba29f4 Mon Sep 17 00:00:00 2001 From: "Mims H. Wright" Date: Fri, 13 Dec 2024 17:47:10 +0100 Subject: [PATCH 009/168] Added debug mode to readme (#135) * Added a note about debug mode * Update README.md Added image of debug mode --- README.md | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 76e02c7c..0ab9378c 100644 --- a/README.md +++ b/README.md @@ -60,15 +60,15 @@ The project is configured for [pnpm](https://pnpm.io/). To install, see the [ins The plugin is organized as a monorepo. There are several packages: -- packages/backend - Contains the business logic that reads the Figma API and converts nodes -- packages/plugin-ui - Contains the common UI for the plugin -- packages/eslint-config-custom - Config file for ESLint -- packages/tsconfig - Collection of TSConfig files used throughout the project +- `packages/backend` - Contains the business logic that reads the Figma API and converts nodes +- `packages/plugin-ui` - Contains the common UI for the plugin +- `packages/eslint-config-custom` - Config file for ESLint +- `packages/tsconfig` - Collection of TSConfig files used throughout the project -- apps/plugin - This is the actual plugin assembled from the parts in backend & plugin-ui. Within this folder it's divided between: - - plugin-src - loads the backend and compiles to code.js - - ui-src - loads the common plugin-ui and compiles to index.html -- apps/debug - This is a debug mode plugin that is a more convenient way to see all the UI elements. +- `apps/plugin` - This is the actual plugin assembled from the parts in `backend` & `plugin-ui`. Within this folder it's divided between: + - `plugin-src` - loads from `backend` and compiles to `code.js` + - `ui-src` - loads the common `plugin-ui` and compiles to `index.html` +- `apps/debug` - This is a debug mode plugin that is a more convenient way to see all the UI elements. The plugin is built using Turbo which in turn builds the internal packages. @@ -76,12 +76,18 @@ The plugin is built using Turbo which in turn builds the internal packages. `pnpm run ...` -- `dev` - runs the app in dev mode +- `dev` - runs the app in dev mode. This can be run in the Figma editor. - `build` - `build:watch` - `lint` - `format` - formats with prettier (warning: may edit files!) +#### Debug mode + +When running the `dev` task, you can open `http://localhost:3000` to see the debug version of the UI. + +Screenshot 2024-12-13 at 16 26 43 + ## Issues The Figma file for this README and icon is also open and welcome to changes! [Check it here.](https://www.figma.com/file/8buWpm6Mpq4yK9MhbkcdJB/Figma-to-Code) From 9babd6d7adcbd3684a53d1b0854c58a097cdbfdd Mon Sep 17 00:00:00 2001 From: "Mims H. Wright" Date: Fri, 13 Dec 2024 21:47:07 +0100 Subject: [PATCH 010/168] Prettier (#140) * Added a note about debug mode * Update README.md Added image of debug mode * Ran prettier on the files --- __tests__/altNodes/altConversions.test.ts | 16 +++--- .../altNodes/convertNodesOnRectangle.test.ts | 8 +-- .../altNodes/convertToAutoLayout.test.ts | 10 ++-- .../flutter/builderImpl/flutterBlend.test.ts | 10 ++-- .../flutter/builderImpl/flutterBorder.test.ts | 10 ++-- .../flutter/builderImpl/flutterColor.test.ts | 24 ++++----- .../builderImpl/flutterPadding.test.ts | 10 ++-- .../builderImpl/flutterPosition.test.ts | 24 ++++----- .../flutter/builderImpl/flutterShadow.test.ts | 2 +- __tests__/flutter/flutterMain.test.ts | 2 +- __tests__/flutter/flutterText.test.ts | 18 +++---- __tests__/html/builderImpl/htmlBorder.test.ts | 10 ++-- __tests__/html/builderImpl/htmlColor.test.ts | 6 +-- .../html/builderImpl/htmlPadding.test.ts | 4 +- __tests__/html/htmlMain.test.ts | 6 +-- __tests__/html/htmlText.test.ts | 30 +++++------ .../swiftui/builderImpl/swiftuiBorder.test.ts | 12 ++--- .../swiftui/builderImpl/swiftuiColor.test.ts | 26 +++++----- .../builderImpl/swiftuiEffects.test.ts | 2 +- .../builderImpl/swiftuiPadding.test.ts | 2 +- __tests__/swiftui/swiftuiText.test.ts | 10 ++-- .../builderImpl/tailwindColor.test.ts | 26 +++++----- .../builderImpl/tailwindPosition.test.ts | 10 ++-- __tests__/tailwind/tailwindMain.test.ts | 4 +- __tests__/tailwind/tailwindText.test.ts | 8 +-- apps/plugin/plugin-src/code.ts | 12 ++--- apps/plugin/ui-src/App.tsx | 12 ++--- .../backend/src/altNodes/altConversion.ts | 20 ++++---- .../src/altNodes/convertNodesOnRectangle.ts | 6 +-- packages/backend/src/code.ts | 8 +-- packages/backend/src/common/color.ts | 4 +- .../backend/src/common/commonChildrenOrder.ts | 4 +- packages/backend/src/common/commonPadding.ts | 2 +- packages/backend/src/common/commonPosition.ts | 6 +-- packages/backend/src/common/commonStroke.ts | 5 +- .../src/common/commonTextHeightSpacing.ts | 4 +- .../backend/src/common/convertFontWeight.ts | 2 +- packages/backend/src/common/indentString.ts | 2 +- .../backend/src/common/nodeWidthHeight.ts | 12 ++--- packages/backend/src/common/numToAutoFixed.ts | 8 +-- .../backend/src/common/parentCoordinates.ts | 2 +- packages/backend/src/common/parseJSX.ts | 6 +-- packages/backend/src/common/retrieveFill.ts | 2 +- .../src/common/retrieveUI/retrieveColors.ts | 30 +++++++---- .../src/common/retrieveUI/retrieveTexts.ts | 2 +- .../flutter/builderImpl/flutterAutoLayout.ts | 6 +-- .../src/flutter/builderImpl/flutterBlend.ts | 4 +- .../src/flutter/builderImpl/flutterBorder.ts | 6 +-- .../src/flutter/builderImpl/flutterColor.ts | 6 +-- .../src/flutter/builderImpl/flutterPadding.ts | 2 +- .../src/flutter/builderImpl/flutterShadow.ts | 4 +- .../src/flutter/builderImpl/flutterSize.ts | 2 +- .../backend/src/flutter/flutterContainer.ts | 31 ++++++------ packages/backend/src/flutter/flutterMain.ts | 23 +++++---- .../backend/src/flutter/flutterTextBuilder.ts | 10 ++-- .../src/html/builderImpl/htmlAutoLayout.ts | 6 +-- .../backend/src/html/builderImpl/htmlBlend.ts | 11 ++-- .../src/html/builderImpl/htmlBorderRadius.ts | 2 +- .../backend/src/html/builderImpl/htmlColor.ts | 11 ++-- .../src/html/builderImpl/htmlPadding.ts | 2 +- .../src/html/builderImpl/htmlShadow.ts | 2 +- .../backend/src/html/builderImpl/htmlSize.ts | 2 +- .../backend/src/html/htmlDefaultBuilder.ts | 50 +++++++++---------- packages/backend/src/html/htmlMain.ts | 30 +++++------ packages/backend/src/html/htmlTextBuilder.ts | 4 +- .../backend/src/nearest-color/nearestColor.ts | 2 +- .../src/swiftui/builderImpl/swiftuiBorder.ts | 4 +- .../src/swiftui/builderImpl/swiftuiColor.ts | 8 +-- .../src/swiftui/builderImpl/swiftuiEffects.ts | 4 +- .../src/swiftui/builderImpl/swiftuiPadding.ts | 2 +- .../src/swiftui/builderImpl/swiftuiParser.ts | 14 +++--- .../src/swiftui/builderImpl/swiftuiSize.ts | 2 +- .../src/swiftui/swiftuiDefaultBuilder.ts | 10 ++-- packages/backend/src/swiftui/swiftuiMain.ts | 38 +++++++------- .../backend/src/swiftui/swiftuiTextBuilder.ts | 6 +-- .../builderImpl/tailwindAutoLayout.ts | 4 +- .../src/tailwind/builderImpl/tailwindColor.ts | 30 +++++++---- .../tailwind/builderImpl/tailwindPadding.ts | 4 +- .../tailwind/builderImpl/tailwindShadow.ts | 2 +- .../src/tailwind/builderImpl/tailwindSize.ts | 8 +-- .../backend/src/tailwind/conversionTables.ts | 36 +++++++------ .../src/tailwind/retrieveUI/retrieveTexts.ts | 4 +- .../backend/src/tailwind/tailwindConfig.ts | 18 +++---- .../src/tailwind/tailwindDefaultBuilder.ts | 16 +++--- packages/backend/src/tailwind/tailwindMain.ts | 39 +++++++++------ .../src/tailwind/tailwindTextBuilder.ts | 6 +-- packages/backend/src/tailwind/vector.ts | 2 +- packages/plugin-ui/src/PluginUI.tsx | 18 +++---- 88 files changed, 483 insertions(+), 447 deletions(-) diff --git a/__tests__/altNodes/altConversions.test.ts b/__tests__/altNodes/altConversions.test.ts index aaebdb18..39f6e86e 100644 --- a/__tests__/altNodes/altConversions.test.ts +++ b/__tests__/altNodes/altConversions.test.ts @@ -16,7 +16,7 @@ describe("AltConversions", () => { rectangle.resize(20, 20); expect(tailwindMain(convertIntoAltNodes([rectangle]))).toEqual( - '
' + '
', ); }); @@ -42,7 +42,7 @@ describe("AltConversions", () => { expect(htmlMain(convertIntoAltNodes([frame]))).toEqual( `
-
` +`, ); }); @@ -100,7 +100,7 @@ describe("AltConversions", () => { node.characters = ""; expect( - tailwindMain(convertIntoAltNodes([node], new AltFrameNode())) + tailwindMain(convertIntoAltNodes([node], new AltFrameNode())), ).toEqual(`

`); }); @@ -125,7 +125,7 @@ describe("AltConversions", () => { Object.defineProperty(node, "height", { value: 20 }); expect( - tailwindMain(convertIntoAltNodes([node], new AltFrameNode())) + tailwindMain(convertIntoAltNodes([node], new AltFrameNode())), ).toEqual(`
`); }); @@ -145,7 +145,7 @@ describe("AltConversions", () => { Object.defineProperty(node, "width", { value: 20 }); expect( - tailwindMain(convertIntoAltNodes([node], new AltFrameNode())) + tailwindMain(convertIntoAltNodes([node], new AltFrameNode())), ).toEqual(`
`); }); @@ -172,9 +172,7 @@ describe("AltConversions", () => { Object.defineProperty(node, "height", { value: 20 }); expect( - tailwindMain(convertIntoAltNodes([node], new AltFrameNode())) - ).toEqual( - `
` - ); + tailwindMain(convertIntoAltNodes([node], new AltFrameNode())), + ).toEqual(`
`); }); }); diff --git a/__tests__/altNodes/convertNodesOnRectangle.test.ts b/__tests__/altNodes/convertNodesOnRectangle.test.ts index 02b6c24e..a22191f1 100644 --- a/__tests__/altNodes/convertNodesOnRectangle.test.ts +++ b/__tests__/altNodes/convertNodesOnRectangle.test.ts @@ -47,7 +47,7 @@ describe("convert node if child is big rect", () => { expect(tailwindMain([converted])).toEqual( `
-
` +`, ); }); @@ -105,7 +105,7 @@ describe("convert node if child is big rect", () => {
-` +`, ); }); @@ -163,7 +163,7 @@ describe("convert node if child is big rect", () => {
-` +`, ); }); @@ -256,7 +256,7 @@ describe("convert node if child is big rect", () => { expect(tailwindMain([conv])).toEqual( `
-
` +`, ); }); diff --git a/__tests__/altNodes/convertToAutoLayout.test.ts b/__tests__/altNodes/convertToAutoLayout.test.ts index 3c65137c..10cbce4f 100644 --- a/__tests__/altNodes/convertToAutoLayout.test.ts +++ b/__tests__/altNodes/convertToAutoLayout.test.ts @@ -60,7 +60,7 @@ describe("Convert to AutoLayout", () => { `
-
` +`, ); // output should be VERTICAL @@ -73,7 +73,7 @@ describe("Convert to AutoLayout", () => { `
-
` +`, ); // horizontally align while vertical @@ -88,7 +88,7 @@ describe("Convert to AutoLayout", () => { `
-
` +`, ); // vertically align while horizontal @@ -104,7 +104,7 @@ describe("Convert to AutoLayout", () => { `
-
` +`, ); node1.height = 20; @@ -119,7 +119,7 @@ describe("Convert to AutoLayout", () => { `
-
` +`, ); }); diff --git a/__tests__/flutter/builderImpl/flutterBlend.test.ts b/__tests__/flutter/builderImpl/flutterBlend.test.ts index d384ad6f..5b73b85c 100644 --- a/__tests__/flutter/builderImpl/flutterBlend.test.ts +++ b/__tests__/flutter/builderImpl/flutterBlend.test.ts @@ -17,7 +17,7 @@ describe("Flutter Blend", () => { `Opacity( opacity: 0.50, child: test -),` +),`, ); node.opacity = 1.0; @@ -33,7 +33,7 @@ describe("Flutter Blend", () => { `Visibility( visible: false, child: test -),` +),`, ); node.visible = true; @@ -53,7 +53,7 @@ describe("Flutter Blend", () => { `Transform.rotate( angle: -0.79, child: test -),` +),`, ); node.rotation = -45; @@ -61,7 +61,7 @@ describe("Flutter Blend", () => { `Transform.rotate( angle: 0.79, child: test -),` +),`, ); node.rotation = 90; @@ -69,7 +69,7 @@ describe("Flutter Blend", () => { `Transform.rotate( angle: -1.57, child: test -),` +),`, ); }); }); diff --git a/__tests__/flutter/builderImpl/flutterBorder.test.ts b/__tests__/flutter/builderImpl/flutterBorder.test.ts index 898ecaa3..7d08b53b 100644 --- a/__tests__/flutter/builderImpl/flutterBorder.test.ts +++ b/__tests__/flutter/builderImpl/flutterBorder.test.ts @@ -21,7 +21,7 @@ describe("Flutter Border", () => { node.cornerRadius = 2; expect(flutterBorderRadius(node)).toEqual( - "\nborderRadius: BorderRadius.circular(2)," + "\nborderRadius: BorderRadius.circular(2),", ); node.cornerRadius = figma.mixed; @@ -30,7 +30,7 @@ describe("Flutter Border", () => { node.bottomLeftRadius = 0; node.bottomRightRadius = 0; expect(flutterBorderRadius(node)).toEqual( - "\nborderRadius: BorderRadius.only(topLeft: Radius.circular(2), topRight: Radius.circular(0), bottomLeft: Radius.circular(0), bottomRight: Radius.circular(0), )," + "\nborderRadius: BorderRadius.only(topLeft: Radius.circular(2), topRight: Radius.circular(0), bottomLeft: Radius.circular(0), bottomRight: Radius.circular(0), ),", ); const ellipseNode = new AltEllipseNode(); @@ -47,7 +47,7 @@ describe("Flutter Border", () => { }, ]; expect(flutterBorder(node)).toEqual( - "\nborder: Border.all(color: Colors.black, width: 2, )," + "\nborder: Border.all(color: Colors.black, width: 2, ),", ); node.strokeWeight = 0; @@ -67,7 +67,7 @@ describe("Flutter Border", () => { expect(flutterShape(node)).toEqual( `\nshape: RoundedRectangleBorder( borderRadius: BorderRadius.only(topLeft: Radius.circular(4), topRight: Radius.circular(0), bottomLeft: Radius.circular(0), bottomRight: Radius.circular(0), ), -),` +),`, ); const ellipseNode = new AltEllipseNode(); @@ -81,7 +81,7 @@ describe("Flutter Border", () => { expect(flutterShape(ellipseNode)).toEqual( `\nshape: CircleBorder( side: BorderSide(width: 4, color: Color(0xff3f3f3f), ), -),` +),`, ); }); }); diff --git a/__tests__/flutter/builderImpl/flutterColor.test.ts b/__tests__/flutter/builderImpl/flutterColor.test.ts index 6a0f04fb..886e1f60 100644 --- a/__tests__/flutter/builderImpl/flutterColor.test.ts +++ b/__tests__/flutter/builderImpl/flutterColor.test.ts @@ -25,7 +25,7 @@ describe("Flutter Color", () => { ]; expect(flutterBoxDecorationColor(node.fills)).toEqual( - "\ncolor: Color(0xffef5138)," + "\ncolor: Color(0xffef5138),", ); }); @@ -107,7 +107,7 @@ describe("Flutter Color", () => { }, ]; expect(flutterBoxDecorationColor(node.fills)).toEqual( - "\ncolor: Color(0x00000000)," + "\ncolor: Color(0x00000000),", ); }); @@ -135,7 +135,7 @@ describe("Flutter Color", () => { node.fills = [gradientFill]; expect(flutterBoxDecorationColor(node.fills)).toEqual( - "\ngradient: LinearGradient(begin: Alignment.centerLeft, end: Alignment.centerRight, colors: [Colors.black], )," + "\ngradient: LinearGradient(begin: Alignment.centerLeft, end: Alignment.centerRight, colors: [Colors.black], ),", ); // topLeft to bottomRight (135) @@ -144,7 +144,7 @@ describe("Flutter Color", () => { [1.3402682542800903, -1.4652644395828247, 0.5407097935676575], ]); expect(flutterBoxDecorationColor(node.fills)).toEqual( - "\ngradient: LinearGradient(begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [Colors.black], )," + "\ngradient: LinearGradient(begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [Colors.black], ),", ); // bottom to top (-90) @@ -153,7 +153,7 @@ describe("Flutter Color", () => { [-2.3507132530212402, -1.0997783306265774e-7, 1.6796307563781738], ]); expect(flutterBoxDecorationColor(node.fills)).toEqual( - "\ngradient: LinearGradient(begin: Alignment.bottomCenter, end: Alignment.topCenter, colors: [Colors.black], )," + "\ngradient: LinearGradient(begin: Alignment.bottomCenter, end: Alignment.topCenter, colors: [Colors.black], ),", ); // top to bottom (90) @@ -162,7 +162,7 @@ describe("Flutter Color", () => { [3.9725232124328613, -1.4210854715202004e-14, -0.8289895057678223], ]); expect(flutterBoxDecorationColor(node.fills)).toEqual( - "\ngradient: LinearGradient(begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Colors.black], )," + "\ngradient: LinearGradient(begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Colors.black], ),", ); // left to right (0) @@ -171,7 +171,7 @@ describe("Flutter Color", () => { [6.030897026221282e-8, -3.364259719848633, 2.188383102416992], ]); expect(flutterBoxDecorationColor(node.fills)).toEqual( - "\ngradient: LinearGradient(begin: Alignment.centerLeft, end: Alignment.centerRight, colors: [Colors.black], )," + "\ngradient: LinearGradient(begin: Alignment.centerLeft, end: Alignment.centerRight, colors: [Colors.black], ),", ); // right to left (180) @@ -180,7 +180,7 @@ describe("Flutter Color", () => { [0.07747448235750198, 4.357592582702637, -1.0299113988876343], ]); expect(flutterBoxDecorationColor(node.fills)).toEqual( - "\ngradient: LinearGradient(begin: Alignment.centerRight, end: Alignment.centerLeft, colors: [Colors.black], )," + "\ngradient: LinearGradient(begin: Alignment.centerRight, end: Alignment.centerLeft, colors: [Colors.black], ),", ); // bottom left to top right (-135) @@ -189,7 +189,7 @@ describe("Flutter Color", () => { [-3.7344324588775635, 2.3110527992248535, 0.4661891460418701], ]); expect(flutterBoxDecorationColor(node.fills)).toEqual( - "\ngradient: LinearGradient(begin: Alignment.bottomRight, end: Alignment.topLeft, colors: [Colors.black], )," + "\ngradient: LinearGradient(begin: Alignment.bottomRight, end: Alignment.topLeft, colors: [Colors.black], ),", ); // bottom left to top right (-45) @@ -198,7 +198,7 @@ describe("Flutter Color", () => { [-1.3051068782806396, -1.3525396585464478, 1.8345310688018799], ]); expect(flutterBoxDecorationColor(node.fills)).toEqual( - "\ngradient: LinearGradient(begin: Alignment.bottomLeft, end: Alignment.topRight, colors: [Colors.black], )," + "\ngradient: LinearGradient(begin: Alignment.bottomLeft, end: Alignment.topRight, colors: [Colors.black], ),", ); // top right to bottom left (-45) @@ -207,7 +207,7 @@ describe("Flutter Color", () => { [1.5028705596923828, 1.2872726917266846, -1.0877336263656616], ]); expect(flutterBoxDecorationColor(node.fills)).toEqual( - "\ngradient: LinearGradient(begin: Alignment.topRight, end: Alignment.bottomLeft, colors: [Colors.black], )," + "\ngradient: LinearGradient(begin: Alignment.topRight, end: Alignment.bottomLeft, colors: [Colors.black], ),", ); }); @@ -264,7 +264,7 @@ describe("Flutter Color", () => { border: Border.all(color: Color(0xff3f3f3f), width: 4, ), gradient: LinearGradient(begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [Colors.black, Color(0xffff0000)], ), ), -)` +)`, ); }); diff --git a/__tests__/flutter/builderImpl/flutterPadding.test.ts b/__tests__/flutter/builderImpl/flutterPadding.test.ts index e4f62cd0..518f5515 100644 --- a/__tests__/flutter/builderImpl/flutterPadding.test.ts +++ b/__tests__/flutter/builderImpl/flutterPadding.test.ts @@ -19,7 +19,7 @@ describe("Flutter Padding", () => { frameNode.paddingTop = 2; frameNode.paddingBottom = 2; expect(flutterPadding(frameNode)).toEqual( - "\npadding: const EdgeInsets.all(2)," + "\npadding: const EdgeInsets.all(2),", ); frameNode.paddingLeft = 1; @@ -27,7 +27,7 @@ describe("Flutter Padding", () => { frameNode.paddingTop = 3; frameNode.paddingBottom = 4; expect(flutterPadding(frameNode)).toEqual( - "\npadding: const EdgeInsets.only(left: 1, right: 2, top: 3, bottom: 4, )," + "\npadding: const EdgeInsets.only(left: 1, right: 2, top: 3, bottom: 4, ),", ); frameNode.paddingLeft = 2; @@ -35,7 +35,7 @@ describe("Flutter Padding", () => { frameNode.paddingTop = 4; frameNode.paddingBottom = 4; expect(flutterPadding(frameNode)).toEqual( - "\npadding: const EdgeInsets.symmetric(horizontal: 2, vertical: 4, )," + "\npadding: const EdgeInsets.symmetric(horizontal: 2, vertical: 4, ),", ); frameNode.paddingLeft = 2; @@ -43,7 +43,7 @@ describe("Flutter Padding", () => { frameNode.paddingTop = 0; frameNode.paddingBottom = 0; expect(flutterPadding(frameNode)).toEqual( - "\npadding: const EdgeInsets.symmetric(horizontal: 2, )," + "\npadding: const EdgeInsets.symmetric(horizontal: 2, ),", ); frameNode.paddingLeft = 0; @@ -51,7 +51,7 @@ describe("Flutter Padding", () => { frameNode.paddingTop = 2; frameNode.paddingBottom = 2; expect(flutterPadding(frameNode)).toEqual( - "\npadding: const EdgeInsets.symmetric(vertical: 2, )," + "\npadding: const EdgeInsets.symmetric(vertical: 2, ),", ); frameNode.paddingLeft = 0; diff --git a/__tests__/flutter/builderImpl/flutterPosition.test.ts b/__tests__/flutter/builderImpl/flutterPosition.test.ts index fc5f7723..b33b78c5 100644 --- a/__tests__/flutter/builderImpl/flutterPosition.test.ts +++ b/__tests__/flutter/builderImpl/flutterPosition.test.ts @@ -75,7 +75,7 @@ describe("Flutter Position", () => { alignment: Alignment.center, child: child ), -),` +),`, ); // top-left @@ -87,7 +87,7 @@ describe("Flutter Position", () => { alignment: Alignment.topLeft, child: child ), -),` +),`, ); // top-right @@ -99,7 +99,7 @@ describe("Flutter Position", () => { alignment: Alignment.topRight, child: child ), -),` +),`, ); // bottom-left @@ -111,7 +111,7 @@ describe("Flutter Position", () => { alignment: Alignment.bottomLeft, child: child ), -),` +),`, ); // bottom-right @@ -123,7 +123,7 @@ describe("Flutter Position", () => { alignment: Alignment.bottomRight, child: child ), -),` +),`, ); // top-center @@ -135,7 +135,7 @@ describe("Flutter Position", () => { alignment: Alignment.topCenter, child: child ), -),` +),`, ); // left-center @@ -147,7 +147,7 @@ describe("Flutter Position", () => { alignment: Alignment.centerLeft, child: child ), -),` +),`, ); // bottom-center @@ -159,7 +159,7 @@ describe("Flutter Position", () => { alignment: Alignment.bottomCenter, child: child ), -),` +),`, ); // right-center @@ -171,7 +171,7 @@ describe("Flutter Position", () => { alignment: Alignment.centerRight, child: child ), -),` +),`, ); // center Y, random X @@ -182,7 +182,7 @@ describe("Flutter Position", () => { left: 22, top: 37, child: child -),` +),`, ); // center X, random Y @@ -193,7 +193,7 @@ describe("Flutter Position", () => { left: 37, top: 22, child: child -),` +),`, ); // without position @@ -204,7 +204,7 @@ describe("Flutter Position", () => { left: 45, top: 88, child: child -),` +),`, ); }); diff --git a/__tests__/flutter/builderImpl/flutterShadow.test.ts b/__tests__/flutter/builderImpl/flutterShadow.test.ts index 64ae5602..30be4408 100644 --- a/__tests__/flutter/builderImpl/flutterShadow.test.ts +++ b/__tests__/flutter/builderImpl/flutterShadow.test.ts @@ -42,7 +42,7 @@ describe("Flutter Shadow", () => { blurRadius: 8, offset: Offset(4, 4), ), -],` +],`, ); const [elev, color] = flutterElevationAndShadowColor(node); diff --git a/__tests__/flutter/flutterMain.test.ts b/__tests__/flutter/flutterMain.test.ts index af72d6e0..2eeeb2fb 100644 --- a/__tests__/flutter/flutterMain.test.ts +++ b/__tests__/flutter/flutterMain.test.ts @@ -418,7 +418,7 @@ describe("Flutter Main", () => { ], ), ), -)` +)`, ); }); }); diff --git a/__tests__/flutter/flutterText.test.ts b/__tests__/flutter/flutterText.test.ts index f7fd5191..fb02c52f 100644 --- a/__tests__/flutter/flutterText.test.ts +++ b/__tests__/flutter/flutterText.test.ts @@ -51,7 +51,7 @@ describe("Flutter Text", () => { ), ], ), -)` +)`, ); }); it("textAutoResize", () => { @@ -68,7 +68,7 @@ describe("Flutter Text", () => { child: Text( "", ), -)` +)`, ); node.textAutoResize = "HEIGHT"; @@ -78,7 +78,7 @@ describe("Flutter Text", () => { child: Text( "", ), -)` +)`, ); node.textAutoResize = "WIDTH_AND_HEIGHT"; @@ -109,7 +109,7 @@ describe("Flutter Text", () => { "", textAlign: TextAlign.center, ), -)` +)`, ); node.textAlignHorizontal = "JUSTIFIED"; @@ -121,7 +121,7 @@ describe("Flutter Text", () => { "", textAlign: TextAlign.justify, ), -)` +)`, ); }); it("fontSize", () => { @@ -138,7 +138,7 @@ describe("Flutter Text", () => { style: TextStyle( fontSize: 16, ), -)` +)`, ); }); @@ -210,7 +210,7 @@ describe("Flutter Text", () => { fontSize: 24, letterSpacing: 26.40, ), -)` +)`, ); node.letterSpacing = { @@ -224,7 +224,7 @@ describe("Flutter Text", () => { fontSize: 24, letterSpacing: 10, ), -)` +)`, ); }); @@ -312,7 +312,7 @@ describe("Flutter Text", () => { style: TextStyle( decoration: TextDecoration.underline, ), -)` +)`, ); }); diff --git a/__tests__/html/builderImpl/htmlBorder.test.ts b/__tests__/html/builderImpl/htmlBorder.test.ts index ec36afa7..f6cbcf6a 100644 --- a/__tests__/html/builderImpl/htmlBorder.test.ts +++ b/__tests__/html/builderImpl/htmlBorder.test.ts @@ -42,32 +42,32 @@ describe("HTML Border", () => { node.cornerRadius = figma.mixed; node.topLeftRadius = 4; expect(htmlBorderRadius(node, false)).toEqual( - "border-top-left-radius: 4px; " + "border-top-left-radius: 4px; ", ); node.topLeftRadius = 0; node.topRightRadius = 4; expect(htmlBorderRadius(node, false)).toEqual( - "border-top-right-radius: 4px; " + "border-top-right-radius: 4px; ", ); node.topRightRadius = 0; node.bottomLeftRadius = 4; expect(htmlBorderRadius(node, false)).toEqual( - "border-bottom-left-radius: 4px; " + "border-bottom-left-radius: 4px; ", ); node.bottomLeftRadius = 0; node.bottomRightRadius = 4; expect(htmlBorderRadius(node, false)).toEqual( - "border-bottom-right-radius: 4px; " + "border-bottom-right-radius: 4px; ", ); }); it("other nodes", () => { // Ellipses are always round expect(htmlBorderRadius(new AltEllipseNode(), false)).toEqual( - "border-radius: 9999px; " + "border-radius: 9999px; ", ); // Text is unsupported diff --git a/__tests__/html/builderImpl/htmlColor.test.ts b/__tests__/html/builderImpl/htmlColor.test.ts index 23152682..d96ca80b 100644 --- a/__tests__/html/builderImpl/htmlColor.test.ts +++ b/__tests__/html/builderImpl/htmlColor.test.ts @@ -97,7 +97,7 @@ describe("HTML Color", () => { node.fills = [gradientFill]; expect(htmlGradientFromFills(node.fills)).toEqual( - "linear-gradient(90deg, black)" + "linear-gradient(90deg, black)", ); // topLeft to bottomRight (135) @@ -106,7 +106,7 @@ describe("HTML Color", () => { [1.3402682542800903, -1.4652644395828247, 0.5407097935676575], ]); expect(htmlGradientFromFills(node.fills)).toEqual( - "linear-gradient(131deg, black)" + "linear-gradient(131deg, black)", ); }); @@ -156,7 +156,7 @@ describe("HTML Color", () => { node.dashPattern = []; expect(htmlMain([node])).toEqual( - `
` + `
`, ); }); diff --git a/__tests__/html/builderImpl/htmlPadding.test.ts b/__tests__/html/builderImpl/htmlPadding.test.ts index 1a5cb96b..c87cf17d 100644 --- a/__tests__/html/builderImpl/htmlPadding.test.ts +++ b/__tests__/html/builderImpl/htmlPadding.test.ts @@ -25,7 +25,7 @@ describe("HTML padding", () => { frameNode.paddingTop = 3; frameNode.paddingBottom = 4; expect(htmlPadding(frameNode, false)).toEqual( - "padding-top: 3px; padding-bottom: 4px; padding-left: 1px; padding-right: 2px; " + "padding-top: 3px; padding-bottom: 4px; padding-left: 1px; padding-right: 2px; ", ); frameNode.paddingLeft = 4; @@ -33,7 +33,7 @@ describe("HTML padding", () => { frameNode.paddingTop = 8; frameNode.paddingBottom = 8; expect(htmlPadding(frameNode, false)).toEqual( - "padding-left: 4px; padding-right: 4px; padding-top: 8px; padding-bottom: 8px; " + "padding-left: 4px; padding-right: 4px; padding-top: 8px; padding-bottom: 8px; ", ); frameNode.paddingLeft = 0; diff --git a/__tests__/html/htmlMain.test.ts b/__tests__/html/htmlMain.test.ts index 1e13979c..36270489 100644 --- a/__tests__/html/htmlMain.test.ts +++ b/__tests__/html/htmlMain.test.ts @@ -101,7 +101,7 @@ describe("HTML Main", () => { // undefined (unitialized, only happen on tests) expect(htmlMain([node])).toEqual( - '
' + '
', ); // todo verify if it is working properly. node.x = 0; @@ -140,12 +140,12 @@ describe("HTML Main", () => { expect(tailwindMain([frameNode])).toEqual( `

username

-
` +`, ); frameNode.name = "this is the InPuT"; expect(htmlMain([frameNode])).toEqual( - '' + '', ); }); diff --git a/__tests__/html/htmlText.test.ts b/__tests__/html/htmlText.test.ts index deaad6e8..3a968d9d 100644 --- a/__tests__/html/htmlText.test.ts +++ b/__tests__/html/htmlText.test.ts @@ -14,7 +14,7 @@ describe("HTML Text", () => { node.textAutoResize = "NONE"; expect(htmlMain([node])).toEqual( - '

' + '

', ); node.textAutoResize = "HEIGHT"; @@ -37,18 +37,18 @@ describe("HTML Text", () => { node.textAutoResize = "NONE"; node.textAlignHorizontal = "CENTER"; expect(htmlMain([node])).toEqual( - '

' + '

', ); node.textAutoResize = "NONE"; node.textAlignHorizontal = "RIGHT"; expect(htmlMain([node])).toEqual( - '

' + '

', ); node.textAlignHorizontal = "JUSTIFIED"; expect(htmlMain([node])).toEqual( - '

' + '

', ); }); it("fontSize", () => { @@ -80,7 +80,7 @@ describe("HTML Text", () => { style: "medium italic", }; expect(htmlMain([node])).toEqual( - '

' + '

', ); node.fontName = { @@ -109,7 +109,7 @@ describe("HTML Text", () => { unit: "PERCENT", }; expect(htmlMain([node])).toEqual( - '

' + '

', ); node.letterSpacing = { @@ -117,7 +117,7 @@ describe("HTML Text", () => { unit: "PIXELS", }; expect(htmlMain([node])).toEqual( - '

' + '

', ); }); @@ -134,7 +134,7 @@ describe("HTML Text", () => { unit: "PERCENT", }; expect(htmlMain([node])).toEqual( - '

' + '

', ); node.lineHeight = { @@ -142,14 +142,14 @@ describe("HTML Text", () => { unit: "PIXELS", }; expect(htmlMain([node])).toEqual( - '

' + '

', ); node.lineHeight = { unit: "AUTO", }; expect(htmlMain([node])).toEqual( - '

' + '

', ); }); @@ -159,17 +159,17 @@ describe("HTML Text", () => { node.textCase = "LOWER"; expect(htmlMain([node])).toEqual( - '

' + '

', ); node.textCase = "TITLE"; expect(htmlMain([node])).toEqual( - '

' + '

', ); node.textCase = "UPPER"; expect(htmlMain([node])).toEqual( - '

' + '

', ); node.textCase = "ORIGINAL"; @@ -185,12 +185,12 @@ describe("HTML Text", () => { node.textDecoration = "STRIKETHROUGH"; expect(htmlMain([node])).toEqual( - '

' + '

', ); node.textDecoration = "UNDERLINE"; expect(htmlMain([node])).toEqual( - '

' + '

', ); }); }); diff --git a/__tests__/swiftui/builderImpl/swiftuiBorder.test.ts b/__tests__/swiftui/builderImpl/swiftuiBorder.test.ts index f935457a..ddc39b6b 100644 --- a/__tests__/swiftui/builderImpl/swiftuiBorder.test.ts +++ b/__tests__/swiftui/builderImpl/swiftuiBorder.test.ts @@ -42,7 +42,7 @@ describe("SwiftUI Border", () => { expect(swiftuiBorder(node)).toEqual(""); expect(swiftuiShapeStroke(node)).toEqual( - "\n.stroke(Color.black, lineWidth: 10)" + "\n.stroke(Color.black, lineWidth: 10)", ); node.topLeftRadius = 0; @@ -50,7 +50,7 @@ describe("SwiftUI Border", () => { node.strokeWeight = 10; expect(swiftuiBorder(node)).toEqual( - "\n.overlay(RoundedRectangle(cornerRadius: 8).stroke(Color.black, lineWidth: 10))" + "\n.overlay(RoundedRectangle(cornerRadius: 8).stroke(Color.black, lineWidth: 10))", ); expect(swiftuiShapeStroke(node)).toEqual(""); @@ -61,7 +61,7 @@ describe("SwiftUI Border", () => { node.bottomRightRadius = 2; expect(swiftuiBorder(node)).toEqual( - `\n.overlay(RoundedRectangle(cornerRadius: 8).stroke(Color.black, lineWidth: 10))` + `\n.overlay(RoundedRectangle(cornerRadius: 8).stroke(Color.black, lineWidth: 10))`, ); expect(swiftuiShapeStroke(node)).toEqual(""); @@ -82,13 +82,13 @@ describe("SwiftUI Border", () => { expect(swiftuiBorder(node)).toEqual(""); expect(swiftuiShapeStroke(node)).toEqual( - "\n.stroke(Color.black, lineWidth: 10)" + "\n.stroke(Color.black, lineWidth: 10)", ); node.fills = [blackFill]; expect(swiftuiBorder(node)).toEqual( - "\n.overlay(Ellipse().stroke(Color.black, lineWidth: 10))" + "\n.overlay(Ellipse().stroke(Color.black, lineWidth: 10))", ); expect(swiftuiShapeStroke(node)).toEqual(""); }); @@ -110,7 +110,7 @@ describe("SwiftUI Border", () => { node.strokeWeight = 10; expect(swiftuiBorder(node)).toEqual( - "\n.overlay(RoundedRectangle(cornerRadius: 8).stroke(LinearGradient(gradient: Gradient(colors: []), startPoint: .leading, endPoint: .trailing), lineWidth: 10))" + "\n.overlay(RoundedRectangle(cornerRadius: 8).stroke(LinearGradient(gradient: Gradient(colors: []), startPoint: .leading, endPoint: .trailing), lineWidth: 10))", ); expect(swiftuiShapeStroke(node)).toEqual(""); }); diff --git a/__tests__/swiftui/builderImpl/swiftuiColor.test.ts b/__tests__/swiftui/builderImpl/swiftuiColor.test.ts index 55c1bea2..a1679daa 100644 --- a/__tests__/swiftui/builderImpl/swiftuiColor.test.ts +++ b/__tests__/swiftui/builderImpl/swiftuiColor.test.ts @@ -22,7 +22,7 @@ describe("SwiftUI Color", () => { ]; expect(swiftuiColorFromFills(node.fills)).toEqual( - "Color(red: 0.94, green: 0.32, blue: 0.22)" + "Color(red: 0.94, green: 0.32, blue: 0.22)", ); }); @@ -104,7 +104,7 @@ describe("SwiftUI Color", () => { }, ]; expect(swiftuiColorFromFills(node.fills)).toEqual( - "Color(red: 0, green: 0, blue: 0, opacity: 0)" + "Color(red: 0, green: 0, blue: 0, opacity: 0)", ); }); @@ -132,7 +132,7 @@ describe("SwiftUI Color", () => { node.fills = [gradientFill]; expect(swiftuiColorFromFills(node.fills)).toEqual( - "LinearGradient(gradient: Gradient(colors: [Color.black]), startPoint: .leading, endPoint: .trailing)" + "LinearGradient(gradient: Gradient(colors: [Color.black]), startPoint: .leading, endPoint: .trailing)", ); // topLeft to bottomRight (135) @@ -141,7 +141,7 @@ describe("SwiftUI Color", () => { [1.3402682542800903, -1.4652644395828247, 0.5407097935676575], ]); expect(swiftuiColorFromFills(node.fills)).toEqual( - "LinearGradient(gradient: Gradient(colors: [Color.black]), startPoint: .topLeading, endPoint: .bottomTrailing)" + "LinearGradient(gradient: Gradient(colors: [Color.black]), startPoint: .topLeading, endPoint: .bottomTrailing)", ); // bottom to top (-90) @@ -150,7 +150,7 @@ describe("SwiftUI Color", () => { [-2.3507132530212402, -1.0997783306265774e-7, 1.6796307563781738], ]); expect(swiftuiColorFromFills(node.fills)).toEqual( - "LinearGradient(gradient: Gradient(colors: [Color.black]), startPoint: .bottom, endPoint: .top)" + "LinearGradient(gradient: Gradient(colors: [Color.black]), startPoint: .bottom, endPoint: .top)", ); // top to bottom (90) @@ -159,7 +159,7 @@ describe("SwiftUI Color", () => { [3.9725232124328613, -1.4210854715202004e-14, -0.8289895057678223], ]); expect(swiftuiColorFromFills(node.fills)).toEqual( - "LinearGradient(gradient: Gradient(colors: [Color.black]), startPoint: .top, endPoint: .bottom)" + "LinearGradient(gradient: Gradient(colors: [Color.black]), startPoint: .top, endPoint: .bottom)", ); // left to right (0) @@ -168,7 +168,7 @@ describe("SwiftUI Color", () => { [6.030897026221282e-8, -3.364259719848633, 2.188383102416992], ]); expect(swiftuiColorFromFills(node.fills)).toEqual( - "LinearGradient(gradient: Gradient(colors: [Color.black]), startPoint: .leading, endPoint: .trailing)" + "LinearGradient(gradient: Gradient(colors: [Color.black]), startPoint: .leading, endPoint: .trailing)", ); // right to left (180) @@ -177,7 +177,7 @@ describe("SwiftUI Color", () => { [0.07747448235750198, 4.357592582702637, -1.0299113988876343], ]); expect(swiftuiColorFromFills(node.fills)).toEqual( - "LinearGradient(gradient: Gradient(colors: [Color.black]), startPoint: .trailing, endPoint: .leading)" + "LinearGradient(gradient: Gradient(colors: [Color.black]), startPoint: .trailing, endPoint: .leading)", ); // bottom left to top right (-135) @@ -186,7 +186,7 @@ describe("SwiftUI Color", () => { [-3.7344324588775635, 2.3110527992248535, 0.4661891460418701], ]); expect(swiftuiColorFromFills(node.fills)).toEqual( - "LinearGradient(gradient: Gradient(colors: [Color.black]), startPoint: .bottomTrailing, endPoint: .topLeading)" + "LinearGradient(gradient: Gradient(colors: [Color.black]), startPoint: .bottomTrailing, endPoint: .topLeading)", ); // bottom left to top right (-45) @@ -195,7 +195,7 @@ describe("SwiftUI Color", () => { [-1.3051068782806396, -1.3525396585464478, 1.8345310688018799], ]); expect(swiftuiColorFromFills(node.fills)).toEqual( - "LinearGradient(gradient: Gradient(colors: [Color.black]), startPoint: .bottomLeading, endPoint: .topTrailing)" + "LinearGradient(gradient: Gradient(colors: [Color.black]), startPoint: .bottomLeading, endPoint: .topTrailing)", ); // top right to bottom left (-45) @@ -204,7 +204,7 @@ describe("SwiftUI Color", () => { [1.5028705596923828, 1.2872726917266846, -1.0877336263656616], ]); expect(swiftuiColorFromFills(node.fills)).toEqual( - "LinearGradient(gradient: Gradient(colors: [Color.black]), startPoint: .topTrailing, endPoint: .bottomLeading)" + "LinearGradient(gradient: Gradient(colors: [Color.black]), startPoint: .topTrailing, endPoint: .bottomLeading)", ); }); @@ -255,7 +255,7 @@ describe("SwiftUI Color", () => { `RoundedRectangle(cornerRadius: 16) .fill(LinearGradient(gradient: Gradient(colors: [Color.black, Color(red: 1, green: 0, blue: 0)]), startPoint: .topLeading, endPoint: .bottomTrailing)) .frame(width: 18, height: 18) -.overlay(RoundedRectangle(cornerRadius: 16).stroke(Color(red: 0.25, green: 0.25, blue: 0.25), lineWidth: 4))` +.overlay(RoundedRectangle(cornerRadius: 16).stroke(Color(red: 0.25, green: 0.25, blue: 0.25), lineWidth: 4))`, ); }); @@ -270,7 +270,7 @@ describe("SwiftUI Color", () => { ]; expect(swiftuiColorFromFills(node.fills)).toEqual( - "Color(red: 0.50, green: 0.23, blue: 0.27, opacity: 0.50)" + "Color(red: 0.50, green: 0.23, blue: 0.27, opacity: 0.50)", ); }); }); diff --git a/__tests__/swiftui/builderImpl/swiftuiEffects.test.ts b/__tests__/swiftui/builderImpl/swiftuiEffects.test.ts index c1aba731..ddcaa078 100644 --- a/__tests__/swiftui/builderImpl/swiftuiEffects.test.ts +++ b/__tests__/swiftui/builderImpl/swiftuiEffects.test.ts @@ -70,7 +70,7 @@ describe("SwiftUI Shadow and Blur", () => { ]; expect(swiftuiShadow(node)).toEqual( - "\n.shadow(color: Color(red: 0, green: 0, blue: 0, opacity: 1), radius: 4)" + "\n.shadow(color: Color(red: 0, green: 0, blue: 0, opacity: 1), radius: 4)", ); }); diff --git a/__tests__/swiftui/builderImpl/swiftuiPadding.test.ts b/__tests__/swiftui/builderImpl/swiftuiPadding.test.ts index b8f9082b..def3e306 100644 --- a/__tests__/swiftui/builderImpl/swiftuiPadding.test.ts +++ b/__tests__/swiftui/builderImpl/swiftuiPadding.test.ts @@ -62,7 +62,7 @@ describe("SwiftUI padding", () => { `\n.padding(.leading, 1) .padding(.trailing, 2) .padding(.top, 3) -.padding(.bottom, 4)` +.padding(.bottom, 4)`, ); const notFrame = new AltRectangleNode(); diff --git a/__tests__/swiftui/swiftuiText.test.ts b/__tests__/swiftui/swiftuiText.test.ts index 3cf66f9b..34bf8cbf 100644 --- a/__tests__/swiftui/swiftuiText.test.ts +++ b/__tests__/swiftui/swiftuiText.test.ts @@ -42,7 +42,7 @@ describe("SwiftUI Text", () => { expect(swiftuiMain([node])).toEqual( `Text("") .multilineTextAlignment(.center) -.frame(width: 16)` +.frame(width: 16)`, ); }); @@ -86,7 +86,7 @@ describe("SwiftUI Text", () => { unit: "PERCENT", }; expect(swiftuiMain([node])).toEqual( - 'Text("")\n.font(.caption2)\n.tracking(11)' + 'Text("")\n.font(.caption2)\n.tracking(11)', ); node.letterSpacing = { @@ -94,7 +94,7 @@ describe("SwiftUI Text", () => { unit: "PIXELS", }; expect(swiftuiMain([node])).toEqual( - 'Text("")\n.font(.caption2)\n.tracking(10)' + 'Text("")\n.font(.caption2)\n.tracking(10)', ); }); @@ -111,7 +111,7 @@ describe("SwiftUI Text", () => { unit: "PERCENT", }; expect(swiftuiMain([node])).toEqual( - 'Text("")\n.font(.title)\n.lineSpacing(26.40)' + 'Text("")\n.font(.title)\n.lineSpacing(26.40)', ); node.lineHeight = { @@ -119,7 +119,7 @@ describe("SwiftUI Text", () => { unit: "PIXELS", }; expect(swiftuiMain([node])).toEqual( - 'Text("")\n.font(.title)\n.lineSpacing(10)' + 'Text("")\n.font(.title)\n.lineSpacing(10)', ); }); diff --git a/__tests__/tailwind/builderImpl/tailwindColor.test.ts b/__tests__/tailwind/builderImpl/tailwindColor.test.ts index 1f55f286..1a04725e 100644 --- a/__tests__/tailwind/builderImpl/tailwindColor.test.ts +++ b/__tests__/tailwind/builderImpl/tailwindColor.test.ts @@ -58,7 +58,7 @@ describe("Tailwind Color", () => { }, ]; expect(tailwindColorFromFills(node.fills, "bg")).toEqual( - "bg-black bg-opacity-0 " + "bg-black bg-opacity-0 ", ); }); @@ -86,7 +86,7 @@ describe("Tailwind Color", () => { node.fills = [gradientFill]; expect(tailwindGradientFromFills(node.fills)).toEqual( - "bg-gradient-to-r from-black " + "bg-gradient-to-r from-black ", ); // topLeft to bottomRight (135) @@ -95,7 +95,7 @@ describe("Tailwind Color", () => { [1.3402682542800903, -1.4652644395828247, 0.5407097935676575], ]); expect(tailwindGradientFromFills(node.fills)).toEqual( - "bg-gradient-to-br from-black " + "bg-gradient-to-br from-black ", ); // bottom to top (-90) @@ -104,7 +104,7 @@ describe("Tailwind Color", () => { [-2.3507132530212402, -1.0997783306265774e-7, 1.6796307563781738], ]); expect(tailwindGradientFromFills(node.fills)).toEqual( - "bg-gradient-to-t from-black " + "bg-gradient-to-t from-black ", ); // top to bottom (90) @@ -113,7 +113,7 @@ describe("Tailwind Color", () => { [3.9725232124328613, -1.4210854715202004e-14, -0.8289895057678223], ]); expect(tailwindGradientFromFills(node.fills)).toEqual( - "bg-gradient-to-b from-black " + "bg-gradient-to-b from-black ", ); // left to right (0) @@ -122,7 +122,7 @@ describe("Tailwind Color", () => { [6.030897026221282e-8, -3.364259719848633, 2.188383102416992], ]); expect(tailwindGradientFromFills(node.fills)).toEqual( - "bg-gradient-to-r from-black " + "bg-gradient-to-r from-black ", ); // right to left (180) @@ -131,7 +131,7 @@ describe("Tailwind Color", () => { [0.07747448235750198, 4.357592582702637, -1.0299113988876343], ]); expect(tailwindGradientFromFills(node.fills)).toEqual( - "bg-gradient-to-l from-black " + "bg-gradient-to-l from-black ", ); // bottom left to top right (-135) @@ -140,7 +140,7 @@ describe("Tailwind Color", () => { [-3.7344324588775635, 2.3110527992248535, 0.4661891460418701], ]); expect(tailwindGradientFromFills(node.fills)).toEqual( - "bg-gradient-to-tl from-black " + "bg-gradient-to-tl from-black ", ); // bottom left to top right (-45) @@ -149,7 +149,7 @@ describe("Tailwind Color", () => { [-1.3051068782806396, -1.3525396585464478, 1.8345310688018799], ]); expect(tailwindGradientFromFills(node.fills)).toEqual( - "bg-gradient-to-tr from-black " + "bg-gradient-to-tr from-black ", ); // top right to bottom left (-45) @@ -158,7 +158,7 @@ describe("Tailwind Color", () => { [1.5028705596923828, 1.2872726917266846, -1.0877336263656616], ]); expect(tailwindGradientFromFills(node.fills)).toEqual( - "bg-gradient-to-bl from-black " + "bg-gradient-to-bl from-black ", ); const gradientFillTwo: GradientPaint = { @@ -192,7 +192,7 @@ describe("Tailwind Color", () => { node.fills = [gradientFillTwo]; expect(tailwindGradientFromFills(node.fills)).toEqual( - "bg-gradient-to-r from-black to-white " + "bg-gradient-to-r from-black to-white ", ); const gradientFillThree: GradientPaint = { @@ -235,7 +235,7 @@ describe("Tailwind Color", () => { node.fills = [gradientFillThree]; expect(tailwindGradientFromFills(node.fills)).toEqual( - "bg-gradient-to-r from-black via-gray-500 to-white " + "bg-gradient-to-r from-black via-gray-500 to-white ", ); }); @@ -284,7 +284,7 @@ describe("Tailwind Color", () => { node.cornerRadius = 16; expect(tailwindMain([node])).toEqual( - `
` + `
`, ); }); diff --git a/__tests__/tailwind/builderImpl/tailwindPosition.test.ts b/__tests__/tailwind/builderImpl/tailwindPosition.test.ts index b33c1285..db08ffd0 100644 --- a/__tests__/tailwind/builderImpl/tailwindPosition.test.ts +++ b/__tests__/tailwind/builderImpl/tailwindPosition.test.ts @@ -42,7 +42,7 @@ describe("Tailwind Position", () => { node.x = 37; node.y = 37; expect(tailwindPosition(node, "", true)).toEqual( - "absolute m-auto inset-0 " + "absolute m-auto inset-0 ", ); expect(tailwindPosition(node, "", false)).toEqual("absoluteManualLayout"); @@ -70,7 +70,7 @@ describe("Tailwind Position", () => { node.x = 37; node.y = 0; expect(tailwindPosition(node, "", true)).toEqual( - "absolute inset-x-0 top-0 mx-auto " + "absolute inset-x-0 top-0 mx-auto ", ); expect(tailwindPosition(node)).toEqual("absoluteManualLayout"); @@ -78,7 +78,7 @@ describe("Tailwind Position", () => { node.x = 0; node.y = 37; expect(tailwindPosition(node, "", true)).toEqual( - "absolute inset-y-0 left-0 my-auto " + "absolute inset-y-0 left-0 my-auto ", ); expect(tailwindPosition(node)).toEqual("absoluteManualLayout"); @@ -86,7 +86,7 @@ describe("Tailwind Position", () => { node.x = 37; node.y = 75; expect(tailwindPosition(node, "", true)).toEqual( - "absolute inset-x-0 bottom-0 mx-auto " + "absolute inset-x-0 bottom-0 mx-auto ", ); expect(tailwindPosition(node)).toEqual("absoluteManualLayout"); @@ -94,7 +94,7 @@ describe("Tailwind Position", () => { node.x = 75; node.y = 37; expect(tailwindPosition(node, "", true)).toEqual( - "absolute inset-y-0 right-0 my-auto " + "absolute inset-y-0 right-0 my-auto ", ); expect(tailwindPosition(node)).toEqual("absoluteManualLayout"); diff --git a/__tests__/tailwind/tailwindMain.test.ts b/__tests__/tailwind/tailwindMain.test.ts index f7c37a4b..ee885602 100644 --- a/__tests__/tailwind/tailwindMain.test.ts +++ b/__tests__/tailwind/tailwindMain.test.ts @@ -134,12 +134,12 @@ describe("Tailwind Main", () => { expect(tailwindMain([frameNode])).toEqual( `

username

-
` +`, ); frameNode.name = "this is the InPuT"; expect(tailwindMain([frameNode])).toEqual( - '' + '', ); }); diff --git a/__tests__/tailwind/tailwindText.test.ts b/__tests__/tailwind/tailwindText.test.ts index 40541a4b..46c77a90 100644 --- a/__tests__/tailwind/tailwindText.test.ts +++ b/__tests__/tailwind/tailwindText.test.ts @@ -43,7 +43,7 @@ describe("Tailwind Text", () => { node.textAlignHorizontal = "JUSTIFIED"; expect(tailwindMain([node])).toEqual( - '

' + '

', ); }); it("fontSize", () => { @@ -96,7 +96,7 @@ describe("Tailwind Text", () => { unit: "PERCENT", }; expect(tailwindMain([node])).toEqual( - '

' + '

', ); node.letterSpacing = { @@ -104,7 +104,7 @@ describe("Tailwind Text", () => { unit: "PIXELS", }; expect(tailwindMain([node])).toEqual( - '

' + '

', ); }); @@ -121,7 +121,7 @@ describe("Tailwind Text", () => { unit: "PERCENT", }; expect(tailwindMain([node])).toEqual( - '

' + '

', ); node.lineHeight = { diff --git a/apps/plugin/plugin-src/code.ts b/apps/plugin/plugin-src/code.ts index c4106720..8725cd44 100644 --- a/apps/plugin/plugin-src/code.ts +++ b/apps/plugin/plugin-src/code.ts @@ -116,7 +116,7 @@ const codegenMode = async () => { code: htmlMain( convertedSelection, { ...userPluginSettings, jsx: false }, - true + true, ), language: "HTML", }, @@ -133,7 +133,7 @@ const codegenMode = async () => { code: htmlMain( convertedSelection, { ...userPluginSettings, jsx: true }, - true + true, ), language: "HTML", }, @@ -150,7 +150,7 @@ const codegenMode = async () => { title: `Code`, code: tailwindMain(convertedSelection, { ...userPluginSettings, - jsx: language === 'tailwind_jsx', + jsx: language === "tailwind_jsx", }), language: "HTML", }, @@ -163,12 +163,12 @@ const codegenMode = async () => { title: `Tailwind Colors`, code: retrieveGenericSolidUIColors("Tailwind") .map((d) => { - let str = `${d.hex};` + let str = `${d.hex};`; if (d.colorName !== d.hex) { - str += ` // ${d.colorName}` + str += ` // ${d.colorName}`; } if (d.meta) { - str += ` (${d.meta})` + str += ` (${d.meta})`; } return str; }) diff --git a/apps/plugin/ui-src/App.tsx b/apps/plugin/ui-src/App.tsx index f4c9516d..2487aeff 100644 --- a/apps/plugin/ui-src/App.tsx +++ b/apps/plugin/ui-src/App.tsx @@ -90,7 +90,7 @@ export default function App() { if (state.selectedFramework === null) { const timer = setTimeout( () => setState((prevState) => ({ ...prevState, isLoading: true })), - 300 + 300, ); return () => clearTimeout(timer); } else { @@ -121,17 +121,13 @@ export default function App() { value: updatedFramework, }, }, - "*" + "*", ); }; console.log("state.code", state.code.slice(0, 25)); return ( -
+
(node: T): T => { export const frameNodeTo = ( node: FrameNode | InstanceNode | ComponentNode | ComponentSetNode, - parent: ParentType + parent: ParentType, ): | RectangleNode | FrameNode @@ -48,7 +48,7 @@ export const frameNodeTo = ( overrideReadonlyProperty( clone, "children", - convertIntoNodes(node.children, clone) + convertIntoNodes(node.children, clone), ); return convertNodesOnRectangle(clone); }; @@ -56,7 +56,7 @@ export const frameNodeTo = ( // auto convert Frame to Rectangle when Frame has no Children const frameToRectangleNode = ( node: FrameNode | InstanceNode | ComponentNode | ComponentSetNode, - parent: ParentType + parent: ParentType, ): RectangleNode => { const clonedNode = cloneNode(node); if (parent) { @@ -70,7 +70,7 @@ const frameToRectangleNode = ( export const overrideReadonlyProperty = ( obj: T, prop: K, - value: any + value: any, ): void => { Object.defineProperty(obj, prop, { value: value, @@ -95,7 +95,7 @@ const standardClone = (node: T, parent: ParentType): T => { export const convertIntoNodes = ( sceneNode: ReadonlyArray, - parent: ParentType = null + parent: ParentType = null, ): Array => { const mapped: Array = sceneNode.map((node: SceneNode) => { switch (node.type) { @@ -132,7 +132,7 @@ export const convertIntoNodes = ( overrideReadonlyProperty( clone, "children", - convertIntoNodes(node.children, clone) + convertIntoNodes(node.children, clone), ); // try to find big rect and regardless of that result, also try to convert to autolayout. @@ -165,7 +165,7 @@ export const convertIntoNodes = ( overrideReadonlyProperty( sectionClone, "children", - convertIntoNodes(node.children, sectionClone) + convertIntoNodes(node.children, sectionClone), ); return sectionClone; case "BOOLEAN_OPERATION": @@ -196,7 +196,7 @@ export const convertIntoNodes = ( const iconToRectangle = ( node: FrameNode | InstanceNode | ComponentNode | GroupNode, - parent: ParentType + parent: ParentType, ): RectangleNode | null => { // TODO Fix this. if (false && node.children.every((d) => d.type === "VECTOR")) { @@ -236,7 +236,7 @@ const iconToRectangle = ( }; export function notEmpty( - value: TValue | null | undefined + value: TValue | null | undefined, ): value is TValue { return value !== null && value !== undefined; } @@ -255,7 +255,7 @@ const applyMatrixToPoint = (matrix: number[][], point: number[]): number[] => { // height/width // x2/y2 bottom right coordinates export const getBoundingRect = ( - node: LayoutMixin + node: LayoutMixin, ): { x: number; y: number; diff --git a/packages/backend/src/altNodes/convertNodesOnRectangle.ts b/packages/backend/src/altNodes/convertNodesOnRectangle.ts index 12899df7..538f1183 100644 --- a/packages/backend/src/altNodes/convertNodesOnRectangle.ts +++ b/packages/backend/src/altNodes/convertNodesOnRectangle.ts @@ -4,14 +4,14 @@ import { FrameNodeMock } from "./altMixins2"; * Identify all nodes that are inside Rectangles and transform those Rectangles into Frames containing those nodes. */ export const convertNodesOnRectangle = ( - node: FrameNode | GroupNode | InstanceNode | ComponentNode | ComponentSetNode + node: FrameNode | GroupNode | InstanceNode | ComponentNode | ComponentSetNode, ): FrameNode | GroupNode | InstanceNode | ComponentNode | ComponentSetNode => { if (node.children.length < 2) { return node; } if (!node.id) { throw new Error( - "Node is missing an id! This error should only happen in tests." + "Node is missing an id! This error should only happen in tests.", ); } @@ -118,7 +118,7 @@ const convertRectangleToFrame = (rect: RectangleNode) => { * A Node can only have a single parent. The order is defined by layer order. */ const retrieveCollidingItems = ( - children: ReadonlyArray + children: ReadonlyArray, ): Record> => { const used: Record = {}; const groups: Record> = {}; diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index 6189b2a1..f9d939e2 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -20,8 +20,8 @@ export type PluginSettings = { flutterGenerationMode: string; swiftUIGenerationMode: string; roundTailwindValues: boolean; - roundTailwindColors: boolean, - customTailwindColors: boolean, + roundTailwindColors: boolean; + customTailwindColors: boolean; }; export const run = (settings: PluginSettings) => { @@ -35,7 +35,7 @@ export const run = (settings: PluginSettings) => { const convertedSelection = convertIntoNodes( figma.currentPage.selection, - null + null, ); let result = ""; switch (settings.framework) { @@ -70,7 +70,7 @@ export const run = (settings: PluginSettings) => { ...settings, jsx: false, }, - true + true, ), } : null, diff --git a/packages/backend/src/common/color.ts b/packages/backend/src/common/color.ts index 8890e195..79f07d94 100644 --- a/packages/backend/src/common/color.ts +++ b/packages/backend/src/common/color.ts @@ -23,7 +23,7 @@ export const gradientAngle = (fill: GradientPaint): number => { // Thanks Gleb and Liam for helping! const decomposed = decomposeRelativeTransform( fill.gradientTransform[0], - fill.gradientTransform[1] + fill.gradientTransform[1], ); return (decomposed.rotation * 180) / Math.PI; @@ -31,7 +31,7 @@ export const gradientAngle = (fill: GradientPaint): number => { // from https://math.stackexchange.com/a/2888105 export const decomposeRelativeTransform = ( t1: [number, number, number], - t2: [number, number, number] + t2: [number, number, number], ): { translation: [number, number]; rotation: number; diff --git a/packages/backend/src/common/commonChildrenOrder.ts b/packages/backend/src/common/commonChildrenOrder.ts index 8c3bfa4c..d7a93965 100644 --- a/packages/backend/src/common/commonChildrenOrder.ts +++ b/packages/backend/src/common/commonChildrenOrder.ts @@ -1,6 +1,6 @@ export const commonSortChildrenWhenInferredAutoLayout = ( node: SceneNode & ChildrenMixin, - optimize: boolean + optimize: boolean, ) => { if (node.children.length <= 1) { return node.children; @@ -21,7 +21,7 @@ export const commonSortChildrenWhenInferredAutoLayout = ( console.log( "ordering", children.map((c) => c.name), - children.sort((a, b) => a.y - b.y).map((c) => c.name) + children.sort((a, b) => a.y - b.y).map((c) => c.name), ); return children.sort((a, b) => a.y - b.y); } diff --git a/packages/backend/src/common/commonPadding.ts b/packages/backend/src/common/commonPadding.ts index 017f56ed..3eaccccf 100644 --- a/packages/backend/src/common/commonPadding.ts +++ b/packages/backend/src/common/commonPadding.ts @@ -12,7 +12,7 @@ type PaddingType = }; export const commonPadding = ( - node: InferredAutoLayoutResult + node: InferredAutoLayoutResult, ): PaddingType | null => { if ("layoutMode" in node && node.layoutMode !== "NONE") { const paddingLeft = parseFloat((node.paddingLeft ?? 0).toFixed(2)); diff --git a/packages/backend/src/common/commonPosition.ts b/packages/backend/src/common/commonPosition.ts index 5d421530..23ae5268 100644 --- a/packages/backend/src/common/commonPosition.ts +++ b/packages/backend/src/common/commonPosition.ts @@ -14,7 +14,7 @@ type position = | "BottomEnd"; export const commonPosition = ( - node: SceneNode & DimensionAndPositionMixin + node: SceneNode & DimensionAndPositionMixin, ): position => { // if node is same size as height, position is not necessary @@ -105,7 +105,7 @@ export const commonPosition = ( }; export const getCommonPositionValue = ( - node: SceneNode + node: SceneNode, ): { x: number; y: number } => { if (node.parent && node.parent.type === "GROUP") { return { @@ -122,7 +122,7 @@ export const getCommonPositionValue = ( export const commonIsAbsolutePosition = ( node: SceneNode, - optimizeLayout: boolean + optimizeLayout: boolean, ) => { // No position when parent is inferred auto layout. if ( diff --git a/packages/backend/src/common/commonStroke.ts b/packages/backend/src/common/commonStroke.ts index cba99bfe..a843bcae 100644 --- a/packages/backend/src/common/commonStroke.ts +++ b/packages/backend/src/common/commonStroke.ts @@ -7,7 +7,10 @@ type BorderSideType = bottom: number; }; -export const commonStroke = (node: SceneNode, divideBy: number = 1): BorderSideType | null => { +export const commonStroke = ( + node: SceneNode, + divideBy: number = 1, +): BorderSideType | null => { if (!("strokes" in node) || !node.strokes || node.strokes.length === 0) { return null; } diff --git a/packages/backend/src/common/commonTextHeightSpacing.ts b/packages/backend/src/common/commonTextHeightSpacing.ts index ed69edc0..aa3314ba 100644 --- a/packages/backend/src/common/commonTextHeightSpacing.ts +++ b/packages/backend/src/common/commonTextHeightSpacing.ts @@ -1,6 +1,6 @@ export const commonLineHeight = ( lineHeight: LineHeight, - fontSize: number + fontSize: number, ): number => { switch (lineHeight.unit) { case "AUTO": @@ -14,7 +14,7 @@ export const commonLineHeight = ( export const commonLetterSpacing = ( letterSpacing: LetterSpacing, - fontSize: number + fontSize: number, ): number => { switch (letterSpacing.unit) { case "PIXELS": diff --git a/packages/backend/src/common/convertFontWeight.ts b/packages/backend/src/common/convertFontWeight.ts index ee6ba04b..77857394 100644 --- a/packages/backend/src/common/convertFontWeight.ts +++ b/packages/backend/src/common/convertFontWeight.ts @@ -1,6 +1,6 @@ // Convert generic named weights to numbers, which is the way tailwind understands export const convertFontWeight = ( - weight: number + weight: number, ): | "100" | "200" diff --git a/packages/backend/src/common/indentString.ts b/packages/backend/src/common/indentString.ts index 5a1ff999..db5cf188 100644 --- a/packages/backend/src/common/indentString.ts +++ b/packages/backend/src/common/indentString.ts @@ -11,7 +11,7 @@ export const indentString = (str: string, indentLevel: number = 2): string => { export const indentStringFlutter = ( str: string, - indentLevel: number = 2 + indentLevel: number = 2, ): string => { // const options = { // includeEmptyLines: false, diff --git a/packages/backend/src/common/nodeWidthHeight.ts b/packages/backend/src/common/nodeWidthHeight.ts index 79ff2177..69e76c84 100644 --- a/packages/backend/src/common/nodeWidthHeight.ts +++ b/packages/backend/src/common/nodeWidthHeight.ts @@ -5,7 +5,7 @@ type SizeResult = { export const nodeSize = ( node: SceneNode, - optimizeLayout: boolean + optimizeLayout: boolean, ): SizeResult => { const hasLayout = "layoutAlign" in node && node.parent && "layoutMode" in node.parent; @@ -26,7 +26,7 @@ export const nodeSize = ( // const parentLayoutMode = node.parent.layoutMode; const parentLayoutMode = optimizeLayout ? node.parent.inferredAutoLayout?.layoutMode - : null ?? node.parent.layoutMode; + : (null ?? node.parent.layoutMode); const isWidthFill = (parentLayoutMode === "HORIZONTAL" && nodeAuto.layoutGrow === 1) || @@ -46,12 +46,12 @@ export const nodeSize = ( width: isWidthFill ? "fill" : "layoutMode" in nodeAuto && nodeAuto[primaryAxisMode] === "AUTO" - ? null - : node.width, + ? null + : node.width, height: isHeightFill ? "fill" : "layoutMode" in nodeAuto && nodeAuto[counterAxisMode] === "AUTO" - ? null - : node.height, + ? null + : node.height, }; }; diff --git a/packages/backend/src/common/numToAutoFixed.ts b/packages/backend/src/common/numToAutoFixed.ts index d03bd529..0836be87 100644 --- a/packages/backend/src/common/numToAutoFixed.ts +++ b/packages/backend/src/common/numToAutoFixed.ts @@ -8,7 +8,7 @@ export const sliceNum = (num: number): string => { export const printPropertyIfNotDefault = ( propertyName: string, propertyValue: any, - defaultProperty: any + defaultProperty: any, ): string => { if (propertyValue === defaultProperty) { return ""; @@ -18,7 +18,7 @@ export const printPropertyIfNotDefault = ( export const skipDefaultProperty = ( propertyValue: T, - defaultProperty: T + defaultProperty: T, ): T | string => { if (propertyValue === defaultProperty) { return ""; @@ -28,7 +28,7 @@ export const skipDefaultProperty = ( export const propertyIfNotDefault = ( propertyValue: any, - defaultProperty: any + defaultProperty: any, ): string => { if (propertyValue === defaultProperty) { return ""; @@ -41,7 +41,7 @@ type PropertyValueType = number | string | string[]; export const generateWidgetCode = ( className: string, properties: Record, - positionedValues?: string[] + positionedValues?: string[], ): string => { console.log("properties", properties); const propertiesArray = Object.entries(properties) diff --git a/packages/backend/src/common/parentCoordinates.ts b/packages/backend/src/common/parentCoordinates.ts index f0fa315e..209897db 100644 --- a/packages/backend/src/common/parentCoordinates.ts +++ b/packages/backend/src/common/parentCoordinates.ts @@ -6,7 +6,7 @@ * Input is expected to be node.parent. */ export const parentCoordinates = ( - node: DimensionAndPositionMixin + node: DimensionAndPositionMixin, ): [number, number] => { const parentX = "layoutMode" in node ? 0 : node.x; const parentY = "layoutMode" in node ? 0 : node.y; diff --git a/packages/backend/src/common/parseJSX.ts b/packages/backend/src/common/parseJSX.ts index 71528462..dab7adab 100644 --- a/packages/backend/src/common/parseJSX.ts +++ b/packages/backend/src/common/parseJSX.ts @@ -3,7 +3,7 @@ import { sliceNum } from "./numToAutoFixed"; export const formatWithJSX = ( property: string, isJsx: boolean, - value: number | string + value: number | string, ): string => { // convert font-size to fontSize. const jsx_property = property @@ -26,7 +26,7 @@ export const formatWithJSX = ( export const formatMultipleJSXArray = ( styles: Record, - isJsx: boolean + isJsx: boolean, ): string[] => Object.entries(styles) .filter(([key, value]) => value !== "") @@ -34,7 +34,7 @@ export const formatMultipleJSXArray = ( export const formatMultipleJSX = ( styles: Record, - isJsx: boolean + isJsx: boolean, ): string => Object.entries(styles) .filter(([key, value]) => value) diff --git a/packages/backend/src/common/retrieveFill.ts b/packages/backend/src/common/retrieveFill.ts index 5da175ae..151c00c6 100644 --- a/packages/backend/src/common/retrieveFill.ts +++ b/packages/backend/src/common/retrieveFill.ts @@ -2,7 +2,7 @@ * Retrieve the first visible color that is being used by the layer, in case there are more than one. */ export const retrieveTopFill = ( - fills: ReadonlyArray | PluginAPI["mixed"] + fills: ReadonlyArray | PluginAPI["mixed"], ): Paint | undefined => { if (fills && fills !== figma.mixed && fills.length > 0) { // on Figma, the top layer is always at the last position diff --git a/packages/backend/src/common/retrieveUI/retrieveColors.ts b/packages/backend/src/common/retrieveUI/retrieveColors.ts index bc1e2d62..1f17aad7 100644 --- a/packages/backend/src/common/retrieveUI/retrieveColors.ts +++ b/packages/backend/src/common/retrieveUI/retrieveColors.ts @@ -1,8 +1,20 @@ import { rgbTo6hex } from "../color"; -import { swiftuiColor, swiftuiGradient } from "../../swiftui/builderImpl/swiftuiColor"; -import { tailwindColor, tailwindGradient } from "../../tailwind/builderImpl/tailwindColor"; -import { flutterColor, flutterGradient } from "../../flutter/builderImpl/flutterColor"; -import { htmlColor, htmlGradientFromFills } from "../../html/builderImpl/htmlColor"; +import { + swiftuiColor, + swiftuiGradient, +} from "../../swiftui/builderImpl/swiftuiColor"; +import { + tailwindColor, + tailwindGradient, +} from "../../tailwind/builderImpl/tailwindColor"; +import { + flutterColor, + flutterGradient, +} from "../../flutter/builderImpl/flutterColor"; +import { + htmlColor, + htmlGradientFromFills, +} from "../../html/builderImpl/htmlColor"; import { calculateContrastRatio } from "./commonUI"; import { FrameworkTypes } from "../../code"; @@ -12,11 +24,11 @@ export type ExportSolidColor = { exportValue: string; contrastWhite: number; contrastBlack: number; - meta?: string + meta?: string; }; export const retrieveGenericSolidUIColors = ( - framework: FrameworkTypes + framework: FrameworkTypes, ): Array => { const selectionColors = figma.getSelectionColors(); if (!selectionColors || selectionColors.paints.length === 0) return []; @@ -25,7 +37,7 @@ export const retrieveGenericSolidUIColors = ( selectionColors.paints.forEach((paint) => { const fill = convertSolidColor(paint, framework); if (fill) { - const exists = colors.find(col => col.exportValue === fill.exportValue) + const exists = colors.find((col) => col.exportValue === fill.exportValue); if (!exists) { colors.push(fill); } @@ -37,7 +49,7 @@ export const retrieveGenericSolidUIColors = ( const convertSolidColor = ( fill: Paint, - framework: FrameworkTypes + framework: FrameworkTypes, ): ExportSolidColor | null => { const black = { r: 0, g: 0, b: 0 }; const white = { r: 1, g: 1, b: 1 }; @@ -69,7 +81,7 @@ const convertSolidColor = ( type ExportLinearGradient = { cssPreview: string; exportValue: string }; export const retrieveGenericLinearGradients = ( - framework: FrameworkTypes + framework: FrameworkTypes, ): Array => { const selectionColors = figma.getSelectionColors(); const colorStr: Array = []; diff --git a/packages/backend/src/common/retrieveUI/retrieveTexts.ts b/packages/backend/src/common/retrieveUI/retrieveTexts.ts index cf960fda..d6d2b267 100644 --- a/packages/backend/src/common/retrieveUI/retrieveTexts.ts +++ b/packages/backend/src/common/retrieveUI/retrieveTexts.ts @@ -9,7 +9,7 @@ type exportFramework = "flutter" | "swiftui" | "html" | "tailwind"; export const retrieveGenericUIText = ( sceneNode: Array, - framework: exportFramework + framework: exportFramework, ): Array => { // convert to Node and then flatten it. Conversion is necessary because of [tailwindText] const selectedText = deepFlatten(sceneNode); diff --git a/packages/backend/src/flutter/builderImpl/flutterAutoLayout.ts b/packages/backend/src/flutter/builderImpl/flutterAutoLayout.ts index addc67bf..da72ae5e 100644 --- a/packages/backend/src/flutter/builderImpl/flutterAutoLayout.ts +++ b/packages/backend/src/flutter/builderImpl/flutterAutoLayout.ts @@ -1,5 +1,5 @@ export const getMainAxisAlignment = ( - node: InferredAutoLayoutResult + node: InferredAutoLayoutResult, ): string => { switch (node.primaryAxisAlignItems) { case "MIN": @@ -14,7 +14,7 @@ export const getMainAxisAlignment = ( }; export const getCrossAxisAlignment = ( - node: InferredAutoLayoutResult + node: InferredAutoLayoutResult, ): string => { switch (node.counterAxisAlignItems) { case "MIN": @@ -30,7 +30,7 @@ export const getCrossAxisAlignment = ( const getFlex = ( node: SceneNode, - autoLayout: InferredAutoLayoutResult + autoLayout: InferredAutoLayoutResult, ): string => node.parent && "layoutMode" in node.parent && diff --git a/packages/backend/src/flutter/builderImpl/flutterBlend.ts b/packages/backend/src/flutter/builderImpl/flutterBlend.ts index 0deb5db9..ce5892a6 100644 --- a/packages/backend/src/flutter/builderImpl/flutterBlend.ts +++ b/packages/backend/src/flutter/builderImpl/flutterBlend.ts @@ -5,7 +5,7 @@ import { generateWidgetCode, sliceNum } from "../../common/numToAutoFixed"; */ export const flutterOpacity = ( node: MinimalBlendMixin, - child: string + child: string, ): string => { if (node.opacity !== undefined && node.opacity !== 1 && child !== "") { return generateWidgetCode("Opacity", { @@ -44,7 +44,7 @@ export const flutterRotation = (node: LayoutMixin, child: string): string => { ) { return generateWidgetCode("Transform", { transform: `Matrix4.identity()..translate(0.0, 0.0)..rotateZ(${sliceNum( - node.rotation * (-3.14159 / 180) + node.rotation * (-3.14159 / 180), )})`, child: child, }); diff --git a/packages/backend/src/flutter/builderImpl/flutterBorder.ts b/packages/backend/src/flutter/builderImpl/flutterBorder.ts index 3371a76d..8073f4ec 100644 --- a/packages/backend/src/flutter/builderImpl/flutterBorder.ts +++ b/packages/backend/src/flutter/builderImpl/flutterBorder.ts @@ -18,12 +18,12 @@ export const flutterBorder = (node: SceneNode): string => { const color = skipDefaultProperty( flutterColorFromFills(node.strokes), - "Colors.black" + "Colors.black", ); const strokeAlign = skipDefaultProperty( getStrokeAlign(node, 2), - "BorderSide.strokeAlignInside" + "BorderSide.strokeAlignInside", ); if ("all" in stroke) { @@ -49,7 +49,7 @@ export const flutterBorder = (node: SceneNode): string => { const generateBorderSideCode = ( width: number, strokeAlign: string, - color: string + color: string, ): string => { return generateWidgetCode("BorderSide", { width: skipDefaultProperty(width, 0), diff --git a/packages/backend/src/flutter/builderImpl/flutterColor.ts b/packages/backend/src/flutter/builderImpl/flutterColor.ts index 01cd4827..b83e97e8 100644 --- a/packages/backend/src/flutter/builderImpl/flutterColor.ts +++ b/packages/backend/src/flutter/builderImpl/flutterColor.ts @@ -7,7 +7,7 @@ import { nearestValue } from "../../tailwind/conversionTables"; * Retrieve the SOLID color for Flutter when existent, otherwise "" */ export const flutterColorFromFills = ( - fills: ReadonlyArray | PluginAPI["mixed"] + fills: ReadonlyArray | PluginAPI["mixed"], ): string => { const fill = retrieveTopFill(fills); @@ -29,7 +29,7 @@ export const flutterColorFromFills = ( export const flutterBoxDecorationColor = ( node: SceneNode, - fills: ReadonlyArray | PluginAPI["mixed"] + fills: ReadonlyArray | PluginAPI["mixed"], ): Record => { const fill = retrieveTopFill(fills); @@ -52,7 +52,7 @@ export const flutterBoxDecorationColor = ( export const flutterDecorationImage = (node: SceneNode, fill: ImagePaint) => { return generateWidgetCode("DecorationImage", { image: `NetworkImage("https://via.placeholder.com/${node.width.toFixed( - 0 + 0, )}x${node.height.toFixed(0)}")`, fit: fitToBoxFit(fill), }); diff --git a/packages/backend/src/flutter/builderImpl/flutterPadding.ts b/packages/backend/src/flutter/builderImpl/flutterPadding.ts index b4aa25e9..d48db14f 100644 --- a/packages/backend/src/flutter/builderImpl/flutterPadding.ts +++ b/packages/backend/src/flutter/builderImpl/flutterPadding.ts @@ -19,7 +19,7 @@ export const flutterPadding = (node: InferredAutoLayoutResult): string => { if ("all" in padding) { return skipDefaultProperty( `const EdgeInsets.all(${sliceNum(padding.all)})`, - "const EdgeInsets.all(0)" + "const EdgeInsets.all(0)", ); } diff --git a/packages/backend/src/flutter/builderImpl/flutterShadow.ts b/packages/backend/src/flutter/builderImpl/flutterShadow.ts index 71fa028f..c1f99a1f 100644 --- a/packages/backend/src/flutter/builderImpl/flutterShadow.ts +++ b/packages/backend/src/flutter/builderImpl/flutterShadow.ts @@ -17,11 +17,11 @@ export const flutterShadow = (node: SceneNode): string => { boxShadow += generateWidgetCode("BoxShadow", { color: `Color(0x${rgbTo8hex( effect.color, - effect.color.a + effect.color.a, ).toUpperCase()})`, blurRadius: sliceNum(effect.radius), offset: `Offset(${sliceNum(effect.offset.x)}, ${sliceNum( - effect.offset.y + effect.offset.y, )})`, spreadRadius: effect.spread ? sliceNum(effect.spread) : "0", }); diff --git a/packages/backend/src/flutter/builderImpl/flutterSize.ts b/packages/backend/src/flutter/builderImpl/flutterSize.ts index ba6c2dc5..685bf685 100644 --- a/packages/backend/src/flutter/builderImpl/flutterSize.ts +++ b/packages/backend/src/flutter/builderImpl/flutterSize.ts @@ -10,7 +10,7 @@ export const flutterSizeWH = (node: SceneNode): string => { export const flutterSize = ( node: SceneNode, - optimizeLayout: boolean + optimizeLayout: boolean, ): { width: string; height: string; isExpanded: boolean } => { const size = nodeSize(node, optimizeLayout); let isExpanded: boolean = false; diff --git a/packages/backend/src/flutter/flutterContainer.ts b/packages/backend/src/flutter/flutterContainer.ts index 066126bb..7e65e5f7 100644 --- a/packages/backend/src/flutter/flutterContainer.ts +++ b/packages/backend/src/flutter/flutterContainer.ts @@ -17,7 +17,7 @@ import { commonStroke } from "../common/commonStroke"; export const flutterContainer = ( node: SceneNode, child: string, - optimizeLayout: boolean + optimizeLayout: boolean, ): string => { // ignore the view when size is zero or less // while technically it shouldn't get less than 0, due to rounding errors, @@ -41,13 +41,16 @@ export const flutterContainer = ( let propPadding = ""; if ("paddingLeft" in node) { propPadding = flutterPadding( - (optimizeLayout ? node.inferredAutoLayout : null) ?? node + (optimizeLayout ? node.inferredAutoLayout : null) ?? node, ); } let result: string; if (width || height || propBoxDecoration || clipBehavior) { - const parsedDecoration = skipDefaultProperty(propBoxDecoration, "BoxDecoration()"); + const parsedDecoration = skipDefaultProperty( + propBoxDecoration, + "BoxDecoration()", + ); result = generateWidgetCode("Container", { width: skipDefaultProperty(width, "0"), height: skipDefaultProperty(height, "0"), @@ -94,7 +97,7 @@ const getDecoration = (node: SceneNode): string => { } else if ("strokeWeight" in node && node.strokeWeight !== figma.mixed) { shapeDecorationBorder = skipDefaultProperty( generateRoundedRectangleBorder(node), - "RoundedRectangleBorder()" + "RoundedRectangleBorder()", ); } @@ -115,7 +118,7 @@ const getDecoration = (node: SceneNode): string => { }; const generateRoundedRectangleBorder = ( - node: SceneNode & MinimalStrokesMixin + node: SceneNode & MinimalStrokesMixin, ): string => { return generateWidgetCode("RoundedRectangleBorder", { side: generateBorderSideCode(node), @@ -124,7 +127,7 @@ const generateRoundedRectangleBorder = ( }; const generateBorderSideCode = ( - node: SceneNode & MinimalStrokesMixin + node: SceneNode & MinimalStrokesMixin, ): string => { const strokeWidth = getSingleStrokeWidth(node); @@ -133,14 +136,14 @@ const generateBorderSideCode = ( width: skipDefaultProperty(strokeWidth, 0), strokeAlign: skipDefaultProperty( getStrokeAlign(node, strokeWidth), - "BorderSide.strokeAlignInside" + "BorderSide.strokeAlignInside", ), color: skipDefaultProperty( flutterColorFromFills(node.strokes), - "Colors.black" + "Colors.black", ), }), - "BorderSide()" + "BorderSide()", ); }; @@ -188,7 +191,7 @@ const generateStarBorder = (node: StarNode): string => { export const getStrokeAlign = ( node: MinimalStrokesMixin, - strokeWeight: number + strokeWeight: number, ): string => { if (strokeWeight === 0) { return ""; @@ -233,19 +236,19 @@ const generateBorderRadius = (node: SceneNode): string => { return generateWidgetCode("BorderRadius.only", { topLeft: skipDefaultProperty( `Radius.circular(${sliceNum(radius.topLeft)})`, - "Radius.circular(0)" + "Radius.circular(0)", ), topRight: skipDefaultProperty( `Radius.circular(${sliceNum(radius.topRight)})`, - "Radius.circular(0)" + "Radius.circular(0)", ), bottomLeft: skipDefaultProperty( `Radius.circular(${sliceNum(radius.bottomLeft)})`, - "Radius.circular(0)" + "Radius.circular(0)", ), bottomRight: skipDefaultProperty( `Radius.circular(${sliceNum(radius.bottomRight)})`, - "Radius.circular(0)" + "Radius.circular(0)", ), }); }; diff --git a/packages/backend/src/flutter/flutterMain.ts b/packages/backend/src/flutter/flutterMain.ts index 3c9890d6..0859485b 100644 --- a/packages/backend/src/flutter/flutterMain.ts +++ b/packages/backend/src/flutter/flutterMain.ts @@ -56,7 +56,7 @@ const getStatelessTemplate = (name: string, injectCode: string): string => export const flutterMain = ( sceneNode: ReadonlyArray, - settings: PluginSettings + settings: PluginSettings, ): string => { localSettings = settings; previousExecutionCache = []; @@ -77,7 +77,7 @@ export const flutterMain = ( }; const flutterWidgetGenerator = ( - sceneNode: ReadonlyArray + sceneNode: ReadonlyArray, ): string => { let comp: string[] = []; @@ -130,7 +130,7 @@ const flutterGroup = (node: GroupNode): string => { node, generateWidgetCode("Stack", { children: widget ? [widget] : [], - }) + }), ); }; @@ -140,7 +140,7 @@ const flutterContainer = (node: SceneNode, child: string): string => { let image = ""; if ("fills" in node && retrieveTopFill(node.fills)?.type === "IMAGE") { image = `Image.network("https://via.placeholder.com/${node.width.toFixed( - 0 + 0, )}x${node.height.toFixed(0)}")`; } @@ -167,10 +167,13 @@ const flutterText = (node: TextNode): string => { }; const flutterFrame = ( - node: SceneNode & BaseFrameMixin & MinimalBlendMixin + node: SceneNode & BaseFrameMixin & MinimalBlendMixin, ): string => { const children = flutterWidgetGenerator( - commonSortChildrenWhenInferredAutoLayout(node, localSettings.optimizeLayout) + commonSortChildrenWhenInferredAutoLayout( + node, + localSettings.optimizeLayout, + ), ); if (node.layoutMode !== "NONE") { @@ -190,14 +193,14 @@ const flutterFrame = ( node, generateWidgetCode("Stack", { children: children !== "" ? [children] : [], - }) + }), ); } }; const makeRowColumn = ( autoLayout: InferredAutoLayoutResult, - children: string + children: string, ): string => { const rowOrColumn = autoLayout.layoutMode === "HORIZONTAL" ? "Row" : "Column"; @@ -214,12 +217,12 @@ const makeRowColumn = ( const addSpacingIfNeeded = ( node: SceneNode, - optimizeLayout: boolean + optimizeLayout: boolean, ): string => { const nodeParentLayout = optimizeLayout && node.parent && "itemSpacing" in node.parent ? node.parent.inferredAutoLayout - : null ?? node.parent; + : (null ?? node.parent); if ( nodeParentLayout && diff --git a/packages/backend/src/flutter/flutterTextBuilder.ts b/packages/backend/src/flutter/flutterTextBuilder.ts index dc4ec83c..7c53cae4 100644 --- a/packages/backend/src/flutter/flutterTextBuilder.ts +++ b/packages/backend/src/flutter/flutterTextBuilder.ts @@ -40,7 +40,7 @@ export class FlutterTextBuilder extends FlutterDefaultBuilder { ...basicTextStyle, style: segments[0].style, }, - [`'${segments[0].text}'`] + [`'${segments[0].text}'`], ); } else { this.child = generateWidgetCode("Text.rich", basicTextStyle, [ @@ -49,7 +49,7 @@ export class FlutterTextBuilder extends FlutterDefaultBuilder { generateWidgetCode("TextSpan", { text: `'${segment.text}'`, style: segment.style, - }) + }), ), }), ]); @@ -74,7 +74,7 @@ export class FlutterTextBuilder extends FlutterDefaultBuilder { const lineHeight = this.lineHeight(segment.lineHeight, segment.fontSize); const letterSpacing = this.letterSpacing( segment.letterSpacing, - segment.fontSize + segment.fontSize, ); const style = generateWidgetCode("TextStyle", { @@ -85,10 +85,10 @@ export class FlutterTextBuilder extends FlutterDefaultBuilder { fontWeight: fontWeight, textDecoration: skipDefaultProperty( this.getFlutterTextDecoration(segment.textDecoration), - "TextDecoration.none" + "TextDecoration.none", ), // textTransform: textTransform, - height: lineHeight/fontSize, + height: lineHeight / fontSize, letterSpacing: letterSpacing, }); diff --git a/packages/backend/src/html/builderImpl/htmlAutoLayout.ts b/packages/backend/src/html/builderImpl/htmlAutoLayout.ts index afdb522d..98eee1d4 100644 --- a/packages/backend/src/html/builderImpl/htmlAutoLayout.ts +++ b/packages/backend/src/html/builderImpl/htmlAutoLayout.ts @@ -36,7 +36,7 @@ const getGap = (node: InferredAutoLayoutResult): string | number => const getFlex = ( node: SceneNode, - autoLayout: InferredAutoLayoutResult + autoLayout: InferredAutoLayoutResult, ): string => node.parent && "layoutMode" in node.parent && @@ -47,7 +47,7 @@ const getFlex = ( export const htmlAutoLayoutProps = ( node: SceneNode, autoLayout: InferredAutoLayoutResult, - isJsx: boolean + isJsx: boolean, ): string[] => formatMultipleJSXArray( { @@ -57,5 +57,5 @@ export const htmlAutoLayoutProps = ( gap: getGap(autoLayout), display: getFlex(node, autoLayout), }, - isJsx + isJsx, ); diff --git a/packages/backend/src/html/builderImpl/htmlBlend.ts b/packages/backend/src/html/builderImpl/htmlBlend.ts index 09de5d7f..15037e38 100644 --- a/packages/backend/src/html/builderImpl/htmlBlend.ts +++ b/packages/backend/src/html/builderImpl/htmlBlend.ts @@ -9,7 +9,7 @@ import { formatWithJSX } from "../../common/parseJSX"; */ export const htmlOpacity = ( node: MinimalBlendMixin, - isJsx: boolean + isJsx: boolean, ): string => { // [when testing] node.opacity can be undefined if (node.opacity !== undefined && node.opacity !== 1) { @@ -23,7 +23,10 @@ export const htmlOpacity = ( return ""; }; -export const htmlBlendMode = (node: MinimalBlendMixin, isJsx: boolean): string => { +export const htmlBlendMode = ( + node: MinimalBlendMixin, + isJsx: boolean, +): string => { if (node.blendMode !== "NORMAL" && node.blendMode !== "PASS_THROUGH") { let blendMode = ""; switch (node.blendMode) { @@ -87,7 +90,7 @@ export const htmlBlendMode = (node: MinimalBlendMixin, isJsx: boolean): string = */ export const htmlVisibility = ( node: SceneNodeMixin, - isJsx: boolean + isJsx: boolean, ): string => { // [when testing] node.visible can be undefined @@ -113,7 +116,7 @@ export const htmlRotation = (node: LayoutMixin, isJsx: boolean): string[] => { formatWithJSX( "transform", isJsx, - `rotate(${sliceNum(-node.rotation)}deg)` + `rotate(${sliceNum(-node.rotation)}deg)`, ), formatWithJSX("transform-origin", isJsx, "0 0"), ]; diff --git a/packages/backend/src/html/builderImpl/htmlBorderRadius.ts b/packages/backend/src/html/builderImpl/htmlBorderRadius.ts index 33184f87..0d70930f 100644 --- a/packages/backend/src/html/builderImpl/htmlBorderRadius.ts +++ b/packages/backend/src/html/builderImpl/htmlBorderRadius.ts @@ -30,7 +30,7 @@ export const htmlBorderRadius = (node: SceneNode, isJsx: boolean): string[] => { "border-bottom-left-radius", ][index]; return formatWithJSX(property, isJsx, value); - }) + }), ); } diff --git a/packages/backend/src/html/builderImpl/htmlColor.ts b/packages/backend/src/html/builderImpl/htmlColor.ts index 8416443c..22730646 100644 --- a/packages/backend/src/html/builderImpl/htmlColor.ts +++ b/packages/backend/src/html/builderImpl/htmlColor.ts @@ -3,7 +3,7 @@ import { retrieveTopFill } from "../../common/retrieveFill"; // retrieve the SOLID color on HTML export const htmlColorFromFills = ( - fills: ReadonlyArray | PluginAPI["mixed"] + fills: ReadonlyArray | PluginAPI["mixed"], ): string => { // kind can be text, bg, border... // [when testing] fills can be undefined @@ -21,10 +21,7 @@ export const htmlColorFromFills = ( fill.type === "GRADIENT_DIAMOND") ) { if (fill.gradientStops.length > 0) { - return htmlColor( - fill.gradientStops[0].color, - fill.opacity - ); + return htmlColor(fill.gradientStops[0].color, fill.opacity); } } @@ -59,7 +56,7 @@ export const htmlColor = (color: RGB, alpha: number = 1): string => { }; export const htmlGradientFromFills = ( - fills: ReadonlyArray | PluginAPI["mixed"] + fills: ReadonlyArray | PluginAPI["mixed"], ): string => { const fill = retrieveTopFill(fills); if (fill?.type === "GRADIENT_LINEAR") { @@ -111,7 +108,7 @@ export const htmlLinearGradient = (fill: GradientPaint): string => { export const invertYCoordinate = (y: number): number => 1 - y; export const getGradientTransformCoordinates = ( - gradientTransform: number[][] + gradientTransform: number[][], ): { centerX: string; centerY: string; radiusX: string; radiusY: string } => { const a = gradientTransform[0][0]; const b = gradientTransform[0][1]; diff --git a/packages/backend/src/html/builderImpl/htmlPadding.ts b/packages/backend/src/html/builderImpl/htmlPadding.ts index 6d2defd7..d05d133e 100644 --- a/packages/backend/src/html/builderImpl/htmlPadding.ts +++ b/packages/backend/src/html/builderImpl/htmlPadding.ts @@ -3,7 +3,7 @@ import { formatWithJSX } from "../../common/parseJSX"; export const htmlPadding = ( node: InferredAutoLayoutResult, - isJsx: boolean + isJsx: boolean, ): string[] => { const padding = commonPadding(node); if (padding === null) { diff --git a/packages/backend/src/html/builderImpl/htmlShadow.ts b/packages/backend/src/html/builderImpl/htmlShadow.ts index d5ae6cc0..3f13b0df 100644 --- a/packages/backend/src/html/builderImpl/htmlShadow.ts +++ b/packages/backend/src/html/builderImpl/htmlShadow.ts @@ -12,7 +12,7 @@ export const htmlShadow = (node: BlendMixin): string => { (d.type === "DROP_SHADOW" || d.type === "INNER_SHADOW" || d.type === "LAYER_BLUR") && - d.visible + d.visible, ); // simple shadow from tailwind if (shadowEffects.length > 0) { diff --git a/packages/backend/src/html/builderImpl/htmlSize.ts b/packages/backend/src/html/builderImpl/htmlSize.ts index 32bea81a..97e9151d 100644 --- a/packages/backend/src/html/builderImpl/htmlSize.ts +++ b/packages/backend/src/html/builderImpl/htmlSize.ts @@ -5,7 +5,7 @@ import { isPreviewGlobal } from "../htmlMain"; export const htmlSizePartial = ( node: SceneNode, isJsx: boolean, - optimizeLayout: boolean + optimizeLayout: boolean, ): { width: string; height: string } => { if (isPreviewGlobal && node.parent === undefined) { return { diff --git a/packages/backend/src/html/htmlDefaultBuilder.ts b/packages/backend/src/html/htmlDefaultBuilder.ts index 39475821..c303df0d 100644 --- a/packages/backend/src/html/htmlDefaultBuilder.ts +++ b/packages/backend/src/html/htmlDefaultBuilder.ts @@ -38,7 +38,7 @@ export class HtmlDefaultBuilder { commonPositionStyles( node: SceneNode & LayoutMixin & MinimalBlendMixin, - optimizeLayout: boolean + optimizeLayout: boolean, ): this { this.size(node, optimizeLayout); this.autoLayoutPadding(node, optimizeLayout); @@ -50,7 +50,7 @@ export class HtmlDefaultBuilder { commonShapeStyles(node: GeometryMixin & SceneNode): this { this.applyFillsToStyle( node.fills, - node.type === "TEXT" ? "text" : "background" + node.type === "TEXT" ? "text" : "background", ); this.shadow(node); this.border(node); @@ -67,7 +67,7 @@ export class HtmlDefaultBuilder { htmlVisibility(node, this.isJSX), ...htmlRotation(node, this.isJSX), htmlOpacity(node, this.isJSX), - htmlBlendMode(node, this.isJSX) + htmlBlendMode(node, this.isJSX), ); return this; } @@ -92,7 +92,7 @@ export class HtmlDefaultBuilder { } const weight = commonBorder.all; this.addStyles( - formatWithJSX("border", this.isJSX, consolidateBorders(weight)) + formatWithJSX("border", this.isJSX, consolidateBorders(weight)), ); } else { if (commonBorder.left !== 0) { @@ -100,8 +100,8 @@ export class HtmlDefaultBuilder { formatWithJSX( "border-left", this.isJSX, - consolidateBorders(commonBorder.left) - ) + consolidateBorders(commonBorder.left), + ), ); } if (commonBorder.top !== 0) { @@ -109,8 +109,8 @@ export class HtmlDefaultBuilder { formatWithJSX( "border-top", this.isJSX, - consolidateBorders(commonBorder.top) - ) + consolidateBorders(commonBorder.top), + ), ); } if (commonBorder.right !== 0) { @@ -118,8 +118,8 @@ export class HtmlDefaultBuilder { formatWithJSX( "border-right", this.isJSX, - consolidateBorders(commonBorder.right) - ) + consolidateBorders(commonBorder.right), + ), ); } if (commonBorder.bottom !== 0) { @@ -127,8 +127,8 @@ export class HtmlDefaultBuilder { formatWithJSX( "border-bottom", this.isJSX, - consolidateBorders(commonBorder.bottom) - ) + consolidateBorders(commonBorder.bottom), + ), ); } } @@ -142,7 +142,7 @@ export class HtmlDefaultBuilder { this.addStyles( formatWithJSX("left", this.isJSX, x), formatWithJSX("top", this.isJSX, y), - formatWithJSX("position", this.isJSX, "absolute") + formatWithJSX("position", this.isJSX, "absolute"), ); } else { if ( @@ -160,11 +160,11 @@ export class HtmlDefaultBuilder { applyFillsToStyle( paintArray: ReadonlyArray | PluginAPI["mixed"], - property: "text" | "background" + property: "text" | "background", ): this { if (property === "text") { this.addStyles( - formatWithJSX("text", this.isJSX, htmlColorFromFills(paintArray)) + formatWithJSX("text", this.isJSX, htmlColorFromFills(paintArray)), ); return this; } @@ -178,7 +178,7 @@ export class HtmlDefaultBuilder { } buildBackgroundValues( - paintArray: ReadonlyArray | PluginAPI["mixed"] + paintArray: ReadonlyArray | PluginAPI["mixed"], ): string { if (paintArray === figma.mixed) { return ""; @@ -213,7 +213,7 @@ export class HtmlDefaultBuilder { const shadow = htmlShadow(node); if (shadow) { this.addStyles( - formatWithJSX("box-shadow", this.isJSX, htmlShadow(node)) + formatWithJSX("box-shadow", this.isJSX, htmlShadow(node)), ); } } @@ -247,8 +247,8 @@ export class HtmlDefaultBuilder { this.addStyles( ...htmlPadding( (optimizeLayout ? node.inferredAutoLayout : null) ?? node, - this.isJSX - ) + this.isJSX, + ), ); } return this; @@ -257,28 +257,28 @@ export class HtmlDefaultBuilder { blur(node: SceneNode) { if ("effects" in node && node.effects.length > 0) { const blur = node.effects.find( - (e) => e.type === "LAYER_BLUR" && e.visible + (e) => e.type === "LAYER_BLUR" && e.visible, ); if (blur) { this.addStyles( formatWithJSX( "filter", this.isJSX, - `blur(${sliceNum(blur.radius)}px)` - ) + `blur(${sliceNum(blur.radius)}px)`, + ), ); } const backgroundBlur = node.effects.find( - (e) => e.type === "BACKGROUND_BLUR" && e.visible + (e) => e.type === "BACKGROUND_BLUR" && e.visible, ); if (backgroundBlur) { this.addStyles( formatWithJSX( "backdrop-filter", this.isJSX, - `blur(${sliceNum(backgroundBlur.radius)}px)` - ) + `blur(${sliceNum(backgroundBlur.radius)}px)`, + ), ); } } diff --git a/packages/backend/src/html/htmlMain.ts b/packages/backend/src/html/htmlMain.ts index 92522bfd..3370f0b4 100644 --- a/packages/backend/src/html/htmlMain.ts +++ b/packages/backend/src/html/htmlMain.ts @@ -19,7 +19,7 @@ let previousExecutionCache: { style: string; text: string }[]; export const htmlMain = ( sceneNode: Array, settings: PluginSettings, - isPreview: boolean = false + isPreview: boolean = false, ): string => { showLayerName = settings.layerName; isPreviewGlobal = isPreview; @@ -39,7 +39,7 @@ export const htmlMain = ( // todo lint idea: replace BorderRadius.only(topleft: 8, topRight: 8) with BorderRadius.horizontal(8) const htmlWidgetGenerator = ( sceneNode: ReadonlyArray, - isJsx: boolean + isJsx: boolean, ): string => { let comp = ""; // filter non visible nodes. This is necessary at this step because conversion already happened. @@ -96,7 +96,7 @@ const htmlGroup = (node: GroupNode, isJsx: boolean = false): string => { const builder = new HtmlDefaultBuilder( node, showLayerName, - isJsx + isJsx, ).commonPositionStyles(node, localSettings.optimizeLayout); if (builder.styles) { @@ -134,14 +134,14 @@ export const htmlText = (node: TextNode, isJsx: boolean): string => { const htmlFrame = ( node: SceneNode & BaseFrameMixin, - isJsx: boolean = false + isJsx: boolean = false, ): string => { const childrenStr = htmlWidgetGenerator( commonSortChildrenWhenInferredAutoLayout( node, - localSettings.optimizeLayout + localSettings.optimizeLayout, ), - isJsx + isJsx, ); if (node.layoutMode !== "NONE") { @@ -152,7 +152,7 @@ const htmlFrame = ( const rowColumn = htmlAutoLayoutProps( node, node.inferredAutoLayout, - isJsx + isJsx, ); return htmlContainer(node, childrenStr, rowColumn, isJsx); } @@ -177,7 +177,7 @@ export const htmlAsset = (node: SceneNode, isJsx: boolean = false): string => { if (retrieveTopFill(node.fills)?.type === "IMAGE") { tag = "img"; src = ` src="https://via.placeholder.com/${node.width.toFixed( - 0 + 0, )}x${node.height.toFixed(0)}"`; } @@ -199,7 +199,7 @@ export const htmlContainer = ( MinimalBlendMixin, children: string, additionalStyles: string[] = [], - isJsx: boolean + isJsx: boolean, ): string => { // ignore the view when size is zero or less // while technically it shouldn't get less than 0, due to rounding errors, @@ -219,7 +219,7 @@ export const htmlContainer = ( if (!("children" in node) || node.children.length === 0) { tag = "img"; src = ` src="https://via.placeholder.com/${node.width.toFixed( - 0 + 0, )}x${node.height.toFixed(0)}"`; } else { builder.addStyles( @@ -227,9 +227,9 @@ export const htmlContainer = ( "background-image", isJsx, `url(https://via.placeholder.com/${node.width.toFixed( - 0 - )}x${node.height.toFixed(0)})` - ) + 0, + )}x${node.height.toFixed(0)})`, + ), ); } } @@ -250,7 +250,7 @@ export const htmlContainer = ( export const htmlSection = ( node: SectionNode, - isJsx: boolean = false + isJsx: boolean = false, ): string => { const childrenStr = htmlWidgetGenerator(node.children, isJsx); const builder = new HtmlDefaultBuilder(node, showLayerName, isJsx) @@ -277,7 +277,7 @@ export const htmlCodeGenTextStyles = (isJsx: boolean) => { const result = previousExecutionCache .map( (style) => - `// ${style.text}\n${style.style.split(isJsx ? "," : ";").join(";\n")}` + `// ${style.text}\n${style.style.split(isJsx ? "," : ";").join(";\n")}`, ) .join("\n---\n"); diff --git a/packages/backend/src/html/htmlTextBuilder.ts b/packages/backend/src/html/htmlTextBuilder.ts index 6b78da76..9a6bc277 100644 --- a/packages/backend/src/html/htmlTextBuilder.ts +++ b/packages/backend/src/html/htmlTextBuilder.ts @@ -31,12 +31,12 @@ export class HtmlTextBuilder extends HtmlDefaultBuilder { "line-height": this.lineHeight(segment.lineHeight, segment.fontSize), "letter-spacing": this.letterSpacing( segment.letterSpacing, - segment.fontSize + segment.fontSize, ), // "text-indent": segment.indentation, "word-wrap": "break-word", }, - this.isJSX + this.isJSX, ); const charsWithLineBreak = segment.characters.split("\n").join("
"); diff --git a/packages/backend/src/nearest-color/nearestColor.ts b/packages/backend/src/nearest-color/nearestColor.ts index 0e98eaca..28d57ae0 100644 --- a/packages/backend/src/nearest-color/nearestColor.ts +++ b/packages/backend/src/nearest-color/nearestColor.ts @@ -148,7 +148,7 @@ function mapColors(colors: Array): Array { * nearestColor.from(invalidColors); // => throws */ export const nearestColorFrom = ( - availableColors: Array + availableColors: Array, ): ((hex: string | RGB) => string) => { const colors = mapColors(availableColors); return (hex: string | RGB) => nearestColor(hex, colors); diff --git a/packages/backend/src/swiftui/builderImpl/swiftuiBorder.ts b/packages/backend/src/swiftui/builderImpl/swiftuiBorder.ts index da0a6ae1..58d46543 100644 --- a/packages/backend/src/swiftui/builderImpl/swiftuiBorder.ts +++ b/packages/backend/src/swiftui/builderImpl/swiftuiBorder.ts @@ -79,7 +79,7 @@ const getViewType = (node: SceneNode): string => { const strokeInset = ( node: MinimalStrokesMixin, - width: number + width: number, ): [string, string | null] => { switch (node.strokeAlign) { case "INSIDE": @@ -114,7 +114,7 @@ export const swiftuiCornerRadius = (node: SceneNode): string => { radius.topLeft, radius.topRight, radius.bottomLeft, - radius.bottomRight + radius.bottomRight, ); if (maxBorder > 0) { diff --git a/packages/backend/src/swiftui/builderImpl/swiftuiColor.ts b/packages/backend/src/swiftui/builderImpl/swiftuiColor.ts index 52c1fd3e..7e47b071 100644 --- a/packages/backend/src/swiftui/builderImpl/swiftuiColor.ts +++ b/packages/backend/src/swiftui/builderImpl/swiftuiColor.ts @@ -21,7 +21,7 @@ export const swiftUISolidColor = (fill: Paint): string => { }; export const swiftuiSolidColor = ( - fills: ReadonlyArray | PluginAPI["mixed"] + fills: ReadonlyArray | PluginAPI["mixed"], ): string => { const fill = retrieveTopFill(fills); @@ -38,7 +38,7 @@ export const swiftuiSolidColor = ( g: 0.23, b: 0.27, }, - 0.5 + 0.5, ); } @@ -47,7 +47,7 @@ export const swiftuiSolidColor = ( export const swiftuiBackground = ( node: SceneNode, - fills: ReadonlyArray | PluginAPI["mixed"] + fills: ReadonlyArray | PluginAPI["mixed"], ): string => { const fill = retrieveTopFill(fills); @@ -59,7 +59,7 @@ export const swiftuiBackground = ( return swiftuiGradient(fill); } else if (fill?.type === "IMAGE") { return `AsyncImage(url: URL(string: "https://via.placeholder.com/${node.width.toFixed( - 0 + 0, )}x${node.height.toFixed(0)}"))`; } diff --git a/packages/backend/src/swiftui/builderImpl/swiftuiEffects.ts b/packages/backend/src/swiftui/builderImpl/swiftuiEffects.ts index 8ea1dc2c..d36bd42a 100644 --- a/packages/backend/src/swiftui/builderImpl/swiftuiEffects.ts +++ b/packages/backend/src/swiftui/builderImpl/swiftuiEffects.ts @@ -7,7 +7,7 @@ export const swiftuiShadow = (node: SceneNode): Modifier | null => { } const dropShadow: Array = node.effects.filter( - (d): d is DropShadowEffect => d.type === "DROP_SHADOW" && d.visible + (d): d is DropShadowEffect => d.type === "DROP_SHADOW" && d.visible, ); if (dropShadow.length === 0) { @@ -50,7 +50,7 @@ export const swiftuiBlur = (node: SceneNode): Modifier | null => { } const layerBlur: Array = node.effects.filter( - (d): d is BlurEffect => d.type === "LAYER_BLUR" && d.visible + (d): d is BlurEffect => d.type === "LAYER_BLUR" && d.visible, ); if (layerBlur.length === 0) { diff --git a/packages/backend/src/swiftui/builderImpl/swiftuiPadding.ts b/packages/backend/src/swiftui/builderImpl/swiftuiPadding.ts index 2d23767e..c6e90b64 100644 --- a/packages/backend/src/swiftui/builderImpl/swiftuiPadding.ts +++ b/packages/backend/src/swiftui/builderImpl/swiftuiPadding.ts @@ -3,7 +3,7 @@ import { commonPadding } from "../../common/commonPadding"; import { Modifier } from "./swiftuiParser"; export const swiftuiPadding = ( - node: InferredAutoLayoutResult + node: InferredAutoLayoutResult, ): Modifier | null => { if (!("layoutMode" in node)) { return null; diff --git a/packages/backend/src/swiftui/builderImpl/swiftuiParser.ts b/packages/backend/src/swiftui/builderImpl/swiftuiParser.ts index 8a7f7d4d..fcf064c9 100644 --- a/packages/backend/src/swiftui/builderImpl/swiftuiParser.ts +++ b/packages/backend/src/swiftui/builderImpl/swiftuiParser.ts @@ -13,7 +13,7 @@ export class SwiftUIElement { addModifierMixed( property: string, - value: string | Modifier | Modifier[] + value: string | Modifier | Modifier[], ): this { this.modifiers.push([property, value]); return this; @@ -38,16 +38,16 @@ export class SwiftUIElement { Array.isArray(value) ? `${indent}.${property}(${new SwiftUIElement( property, - value as Modifier[] + value as Modifier[], ) .toString() .trim()})` : value.length > 60 - ? `${indent}.${property}(\n${indentString( - value, - indentLevel + 2 - )}\n${indent})` - : `${indent}.${property}(${value})` + ? `${indent}.${property}(\n${indentString( + value, + indentLevel + 2, + )}\n${indent})` + : `${indent}.${property}(${value})`, ) .join("\n"); } diff --git a/packages/backend/src/swiftui/builderImpl/swiftuiSize.ts b/packages/backend/src/swiftui/builderImpl/swiftuiSize.ts index 32bd798d..22e07d4f 100644 --- a/packages/backend/src/swiftui/builderImpl/swiftuiSize.ts +++ b/packages/backend/src/swiftui/builderImpl/swiftuiSize.ts @@ -3,7 +3,7 @@ import { sliceNum } from "../../common/numToAutoFixed"; export const swiftuiSize = ( node: SceneNode, - optimizeLayout: boolean + optimizeLayout: boolean, ): { width: string; height: string } => { const size = nodeSize(node, optimizeLayout); diff --git a/packages/backend/src/swiftui/swiftuiDefaultBuilder.ts b/packages/backend/src/swiftui/swiftuiDefaultBuilder.ts index 95a5c04e..8df1323f 100644 --- a/packages/backend/src/swiftui/swiftuiDefaultBuilder.ts +++ b/packages/backend/src/swiftui/swiftuiDefaultBuilder.ts @@ -48,7 +48,7 @@ export class SwiftuiDefaultBuilder { swiftuiVisibility(node), swiftuiRotation(node), swiftuiOpacity(node), - swiftuiBlendMode(node) + swiftuiBlendMode(node), ); return this; @@ -58,7 +58,7 @@ export class SwiftuiDefaultBuilder { x: number, y: number, node: SceneNode, - parent: (BaseNode & ChildrenMixin) | null + parent: (BaseNode & ChildrenMixin) | null, ): { centerX: number; centerY: number } { if (!parent || !("width" in parent)) { return { centerX: 0, centerY: 0 }; @@ -81,7 +81,7 @@ export class SwiftuiDefaultBuilder { x, y, node, - node.parent + node.parent, ); this.pushModifier([ @@ -151,8 +151,8 @@ export class SwiftuiDefaultBuilder { if ("paddingLeft" in node) { this.pushModifier( swiftuiPadding( - (optimizeLayout ? node.inferredAutoLayout : null) ?? node - ) + (optimizeLayout ? node.inferredAutoLayout : null) ?? node, + ), ); } return this; diff --git a/packages/backend/src/swiftui/swiftuiMain.ts b/packages/backend/src/swiftui/swiftuiMain.ts index 1ede1727..afad6ec8 100644 --- a/packages/backend/src/swiftui/swiftuiMain.ts +++ b/packages/backend/src/swiftui/swiftuiMain.ts @@ -32,7 +32,7 @@ struct ContentView_Previews: PreviewProvider { export const swiftuiMain = ( sceneNode: Array, - settings: PluginSettings + settings: PluginSettings, ): string => { localSettings = settings; previousExecutionCache = []; @@ -59,7 +59,7 @@ export const swiftuiMain = ( const swiftuiWidgetGenerator = ( sceneNode: ReadonlyArray, - indentLevel: number + indentLevel: number, ): string => { // filter non visible nodes. This is necessary at this step because conversion already happened. const visibleSceneNode = sceneNode.filter((d) => d.visible); @@ -97,7 +97,7 @@ const swiftuiWidgetGenerator = ( // sometimes a property might not exist, so it doesn't add "," export const swiftuiContainer = ( node: SceneNode, - stack: string = "" + stack: string = "", ): string => { // ignore the view when size is zero or less // while technically it shouldn't get less than 0, due to rounding errors, @@ -131,12 +131,12 @@ export const swiftuiContainer = ( const swiftuiGroup = ( node: GroupNode | SectionNode, - indentLevel: number + indentLevel: number, ): string => { const children = widgetGeneratorWithLimits(node, indentLevel); return swiftuiContainer( node, - children ? generateSwiftViewCode("ZStack", {}, children) : `ZStack() { }` + children ? generateSwiftViewCode("ZStack", {}, children) : `ZStack() { }`, ); }; @@ -151,25 +151,25 @@ const swiftuiText = (node: TextNode): string => { const swiftuiFrame = ( node: SceneNode & BaseFrameMixin, - indentLevel: number + indentLevel: number, ): string => { const children = widgetGeneratorWithLimits( node, - node.children.length > 1 ? indentLevel + 1 : indentLevel + node.children.length > 1 ? indentLevel + 1 : indentLevel, ); const anyStack = createDirectionalStack( children, localSettings.optimizeLayout && node.inferredAutoLayout !== null ? node.inferredAutoLayout - : node + : node, ); return swiftuiContainer(node, anyStack); }; const createDirectionalStack = ( children: string, - inferredAutoLayout: InferredAutoLayoutResult + inferredAutoLayout: InferredAutoLayoutResult, ): string => { if (inferredAutoLayout.layoutMode !== "NONE") { return generateSwiftViewCode( @@ -178,7 +178,7 @@ const createDirectionalStack = ( alignment: getLayoutAlignment(inferredAutoLayout), spacing: getSpacing(inferredAutoLayout), }, - children + children, ); } else { return generateSwiftViewCode("ZStack", {}, children); @@ -186,7 +186,7 @@ const createDirectionalStack = ( }; const getLayoutAlignment = ( - inferredAutoLayout: InferredAutoLayoutResult + inferredAutoLayout: InferredAutoLayoutResult, ): string => { switch (inferredAutoLayout.counterAxisAlignItems) { case "MIN": @@ -212,41 +212,41 @@ const getSpacing = (inferredAutoLayout: InferredAutoLayoutResult): number => { export const generateSwiftViewCode = ( className: string, properties: Record, - children: string + children: string, ): string => { const propertiesArray = Object.entries(properties) .filter(([, value]) => value !== "") .map( ([key, value]) => - `${key}: ${typeof value === "number" ? sliceNum(value) : value}` + `${key}: ${typeof value === "number" ? sliceNum(value) : value}`, ); const compactPropertiesArray = propertiesArray.join(", "); if (compactPropertiesArray.length > 60) { const formattedProperties = propertiesArray.join(",\n"); return `${className}(\n${formattedProperties}\n) {${indentString( - children + children, )}\n}`; } return `${className}(${compactPropertiesArray}) {\n${indentString( - children + children, )}\n}`; }; // todo should the plugin manually Group items? Ideally, it would detect the similarities and allow a ForEach. const widgetGeneratorWithLimits = ( node: SceneNode & ChildrenMixin, - indentLevel: number + indentLevel: number, ) => { if (node.children.length < 10) { // standard way return swiftuiWidgetGenerator( commonSortChildrenWhenInferredAutoLayout( node, - localSettings.optimizeLayout + localSettings.optimizeLayout, ), - indentLevel + indentLevel, ); } @@ -254,7 +254,7 @@ const widgetGeneratorWithLimits = ( let strBuilder = ""; const slicedChildren = commonSortChildrenWhenInferredAutoLayout( node, - localSettings.optimizeLayout + localSettings.optimizeLayout, ).slice(0, 100); // I believe no one should have more than 100 items in a single nesting level. If you do, please email me. diff --git a/packages/backend/src/swiftui/swiftuiTextBuilder.ts b/packages/backend/src/swiftui/swiftuiTextBuilder.ts index 25271c7d..38dcedb3 100644 --- a/packages/backend/src/swiftui/swiftuiTextBuilder.ts +++ b/packages/backend/src/swiftui/swiftuiTextBuilder.ts @@ -115,7 +115,7 @@ export class SwiftuiTextBuilder extends SwiftuiDefaultBuilder { const lineHeight = this.lineHeight(segment.lineHeight, segment.fontSize); const letterSpacing = this.letterSpacing( segment.letterSpacing, - segment.fontSize + segment.fontSize, ); let updatedText = parseTextAsCode(characters); //segment.characters); swiftUI only supports a single text. @@ -126,7 +126,7 @@ export class SwiftuiTextBuilder extends SwiftuiDefaultBuilder { } const element = new SwiftUIElement( - `Text(${parseTextAsCode(`"${characters}"`)})` + `Text(${parseTextAsCode(`"${characters}"`)})`, ) .addModifier([ "font", @@ -146,7 +146,7 @@ export class SwiftuiTextBuilder extends SwiftuiDefaultBuilder { letterSpacing = ( letterSpacing: LetterSpacing, - fontSize: number + fontSize: number, ): string | null => { const value = commonLetterSpacing(letterSpacing, fontSize); if (value > 0) { diff --git a/packages/backend/src/tailwind/builderImpl/tailwindAutoLayout.ts b/packages/backend/src/tailwind/builderImpl/tailwindAutoLayout.ts index 13c5c68a..8078968f 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindAutoLayout.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindAutoLayout.ts @@ -36,7 +36,7 @@ const getGap = (node: InferredAutoLayoutResult): string => const getFlex = ( node: SceneNode, - autoLayout: InferredAutoLayoutResult + autoLayout: InferredAutoLayoutResult, ): string => node.parent && "layoutMode" in node.parent && @@ -46,7 +46,7 @@ const getFlex = ( export const tailwindAutoLayoutProps = ( node: SceneNode, - autoLayout: InferredAutoLayoutResult + autoLayout: InferredAutoLayoutResult, ): string => Object.values({ flexDirection: getFlexDirection(autoLayout), diff --git a/packages/backend/src/tailwind/builderImpl/tailwindColor.ts b/packages/backend/src/tailwind/builderImpl/tailwindColor.ts index ae7073c8..98fcd3b8 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindColor.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindColor.ts @@ -1,8 +1,12 @@ import { retrieveTopFill } from "../../common/retrieveFill"; import { gradientAngle } from "../../common/color"; -import { getColorInfo, nearestOpacity, nearestValue } from "../conversionTables"; +import { + getColorInfo, + nearestOpacity, + nearestValue, +} from "../conversionTables"; -type Kind = "text" | "bg" | "border" | "solid" +type Kind = "text" | "bg" | "border" | "solid"; /** * Get a tailwind color value object @@ -16,7 +20,7 @@ export function tailwindColor(fill: SolidPaint) { colorName, colorType, hex, - meta + meta, }; } @@ -26,7 +30,10 @@ export function tailwindColor(fill: SolidPaint) { * - variables: uses the tokenised variable name * - colors: uses the nearest Tailwind color name */ -export const tailwindSolidColor = (fill: SolidPaint | ColorStop, kind?: Kind): string => { +export const tailwindSolidColor = ( + fill: SolidPaint | ColorStop, + kind?: Kind, +): string => { // example: stone-500 or custom-color-700 const { colorName } = getColorInfo(fill); @@ -36,9 +43,10 @@ export const tailwindSolidColor = (fill: SolidPaint | ColorStop, kind?: Kind): s } // grab opacity, or set it to full - const opacity = "opacity" in fill && fill.opacity !== 1.0 - ? `/${nearestOpacity(fill.opacity ?? 1.0)}` - : ""; + const opacity = + "opacity" in fill && fill.opacity !== 1.0 + ? `/${nearestOpacity(fill.opacity ?? 1.0)}` + : ""; // example: text-red-500, text-[#123abc], text-custom-color-700/25 return `${kind}-${colorName}${opacity}`; @@ -47,7 +55,7 @@ export const tailwindSolidColor = (fill: SolidPaint | ColorStop, kind?: Kind): s // retrieve the SOLID color for tailwind export const tailwindColorFromFills = ( fills: ReadonlyArray | PluginAPI["mixed"], - kind: Kind + kind: Kind, ): string => { // [when testing] fills can be undefined @@ -73,7 +81,7 @@ export const tailwindColorFromFills = ( * example: shadow */ export const tailwindGradientFromFills = ( - fills: ReadonlyArray | PluginAPI["mixed"] + fills: ReadonlyArray | PluginAPI["mixed"], ): string => { // [when testing] node.effects can be undefined @@ -105,7 +113,9 @@ export const tailwindGradient = (fill: GradientPaint): string => { const viaColor = tailwindSolidColor(fill.gradientStops[1]); // last - const toColor = tailwindSolidColor(fill.gradientStops[fill.gradientStops.length - 1]); + const toColor = tailwindSolidColor( + fill.gradientStops[fill.gradientStops.length - 1], + ); return `${direction} from-${fromColor} via-${viaColor} to-${toColor}`; } diff --git a/packages/backend/src/tailwind/builderImpl/tailwindPadding.ts b/packages/backend/src/tailwind/builderImpl/tailwindPadding.ts index b6558c8f..a2e812f0 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindPadding.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindPadding.ts @@ -40,7 +40,7 @@ export const tailwindPadding = (node: InferredAutoLayoutResult): string[] => { comp.push( ...(left && right && pxToLayoutSize(left) === pxToLayoutSize(right) ? [`px-${pxToLayoutSize(left)}`] - : [pl, pr]) + : [pl, pr]), ); } @@ -50,7 +50,7 @@ export const tailwindPadding = (node: InferredAutoLayoutResult): string[] => { comp.push( ...(top && bottom && pxToLayoutSize(top) === pxToLayoutSize(bottom) ? [`py-${pxToLayoutSize(top)}`] - : [pt, pb]) + : [pt, pb]), ); } diff --git a/packages/backend/src/tailwind/builderImpl/tailwindShadow.ts b/packages/backend/src/tailwind/builderImpl/tailwindShadow.ts index 79f87a34..4ff5f2d7 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindShadow.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindShadow.ts @@ -6,7 +6,7 @@ export const tailwindShadow = (node: BlendMixin): string[] => { // [when testing] node.effects can be undefined if (node.effects && node.effects.length > 0) { const dropShadow = node.effects.filter( - (d) => d.type === "DROP_SHADOW" && d.visible + (d) => d.type === "DROP_SHADOW" && d.visible, ); let boxShadow = ""; // simple shadow from tailwind diff --git a/packages/backend/src/tailwind/builderImpl/tailwindSize.ts b/packages/backend/src/tailwind/builderImpl/tailwindSize.ts index 0e043cb4..af7a183a 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindSize.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindSize.ts @@ -4,7 +4,7 @@ import { formatWithJSX } from "../../common/parseJSX"; export const tailwindSizePartial = ( node: SceneNode, - optimizeLayout: boolean + optimizeLayout: boolean, ): { width: string; height: string } => { const size = nodeSize(node, optimizeLayout); @@ -16,8 +16,8 @@ export const tailwindSizePartial = ( let w = ""; if ( typeof size.width === "number" && - 'layoutSizingHorizontal' in node && - node.layoutSizingHorizontal === 'FIXED' + "layoutSizingHorizontal" in node && + node.layoutSizingHorizontal === "FIXED" ) { w = `w-${pxToLayoutSize(size.width)}`; } else if (size.width === "fill") { @@ -53,7 +53,7 @@ export const tailwindSizePartial = ( export const htmlSizePartialForTailwind = ( node: SceneNode, - isJSX: boolean + isJSX: boolean, ): [string, string] => { return [ formatWithJSX("width", isJSX, node.width), diff --git a/packages/backend/src/tailwind/conversionTables.ts b/packages/backend/src/tailwind/conversionTables.ts index 2c3b980a..8736fc15 100644 --- a/packages/backend/src/tailwind/conversionTables.ts +++ b/packages/backend/src/tailwind/conversionTables.ts @@ -12,7 +12,7 @@ export const nearestValue = (goal: number, array: Array): number => { export const exactValue = ( goal: number, - array: Array + array: Array, ): number | null => { for (let i = 0; i < array.length; i++) { const diff = Math.abs(goal - array[i]); @@ -33,7 +33,7 @@ export const exactValue = ( */ const pxToRemToTailwind = ( value: number, - conversionMap: Record + conversionMap: Record, ): string => { const keys = Object.keys(conversionMap).map((d) => +d); const convertedValue = exactValue(value / 16, keys); @@ -49,7 +49,7 @@ const pxToRemToTailwind = ( const pxToTailwind = ( value: number, - conversionMap: Record + conversionMap: Record, ): string | null => { const keys = Object.keys(conversionMap).map((d) => +d); const convertedValue = exactValue(value, keys); @@ -65,23 +65,23 @@ const pxToTailwind = ( export const pxToLetterSpacing = (value: number): string => { return pxToRemToTailwind(value, config.letterSpacing); -} +}; export const pxToLineHeight = (value: number): string => { return pxToRemToTailwind(value, config.lineHeight); -} +}; export const pxToFontSize = (value: number): string => { return pxToRemToTailwind(value, config.fontSize); -} +}; export const pxToBorderRadius = (value: number): string => { return pxToRemToTailwind(value, config.borderRadius); -} +}; export const pxToBlur = (value: number): string | null => { return pxToTailwind(value, config.blur); -} +}; export const pxToLayoutSize = (value: number): string => { const tailwindValue = pxToTailwind(value, config.layoutSize); @@ -94,7 +94,7 @@ export const pxToLayoutSize = (value: number): string => { export const nearestOpacity = (nodeOpacity: number): number => { return nearestValue(nodeOpacity * 100, config.opacity); -} +}; export const nearestColor = nearestColorFrom(Object.keys(config.color)); @@ -111,10 +111,13 @@ export const nearestColorFromRgb = (color: RGB) => { const value = nearestColor(colorMultiplied); const name = config.color[value]; return { name, value }; -} +}; export const variableToColorName = (alias: VariableAlias) => { - return figma.variables.getVariableById(alias.id)?.name.replaceAll("/", "-") || alias.id.toLowerCase().replaceAll(":", "-"); + return ( + figma.variables.getVariableById(alias.id)?.name.replaceAll("/", "-") || + alias.id.toLowerCase().replaceAll(":", "-") + ); }; /** @@ -127,13 +130,16 @@ export function getColorInfo(fill: SolidPaint | ColorStop) { let colorName: string; let colorType: "arbitrary" | "tailwind" | "variable"; let hex: string = "#" + rgbTo6hex(fill.color); - let meta: string = '' + let meta: string = ""; // variable - if (localTailwindSettings.customTailwindColors && fill.boundVariables?.color) { + if ( + localTailwindSettings.customTailwindColors && + fill.boundVariables?.color + ) { colorName = variableToColorName(fill.boundVariables.color); colorType = "variable"; - meta = 'custom' + meta = "custom"; } // solid color @@ -146,7 +152,7 @@ export function getColorInfo(fill: SolidPaint | ColorStop) { colorName = name; colorType = "tailwind"; if (hex !== value) { - meta = 'rounded' + meta = "rounded"; } // must come last, as previous comparison diff --git a/packages/backend/src/tailwind/retrieveUI/retrieveTexts.ts b/packages/backend/src/tailwind/retrieveUI/retrieveTexts.ts index ae37bb4b..a1f7a017 100644 --- a/packages/backend/src/tailwind/retrieveUI/retrieveTexts.ts +++ b/packages/backend/src/tailwind/retrieveUI/retrieveTexts.ts @@ -5,7 +5,7 @@ import { convertFontWeight } from "../../common/convertFontWeight"; import { nearestColor } from "../conversionTables"; export const retrieveTailwindText = ( - sceneNode: Array + sceneNode: Array, ): Array => { // convert to Node and then flatten it. Conversion is necessary because of [tailwindText] const selectedText = deepFlatten(sceneNode); @@ -136,7 +136,7 @@ function deepFlatten(arr: Array): Array { } const convertColor = ( - fills: ReadonlyArray | PluginAPI["mixed"] + fills: ReadonlyArray | PluginAPI["mixed"], ): string | undefined => { // kind can be text, bg, border... // [when testing] fills can be undefined diff --git a/packages/backend/src/tailwind/tailwindConfig.ts b/packages/backend/src/tailwind/tailwindConfig.ts index d6e895f8..b33cba22 100644 --- a/packages/backend/src/tailwind/tailwindConfig.ts +++ b/packages/backend/src/tailwind/tailwindConfig.ts @@ -33,7 +33,7 @@ const layoutSize = { 256: "64", 288: "72", 320: "80", - 384: "96" + 384: "96", }; const borderRadius = { @@ -45,7 +45,7 @@ const borderRadius = { 0.75: "xl", 1.0: "2xl", 1.5: "3xl", - 10: "full" + 10: "full", }; const fontSize = { @@ -61,7 +61,7 @@ const fontSize = { 3.75: "6xl", 4.5: "7xl", 6: "8xl", - 8: "9xl" + 8: "9xl", }; const lineHeight = { @@ -74,7 +74,7 @@ const lineHeight = { 2: "loose", 1.75: "7", 2.25: "9", - 2.5: "10" + 2.5: "10", }; const letterSpacing = { @@ -83,7 +83,7 @@ const letterSpacing = { // 0: "normal", 0.025: "wide", 0.05: "wider", - 0.1: "widest" + 0.1: "widest", }; // This uses pixels. @@ -95,12 +95,10 @@ const blur = { 16: "lg", 24: "xl", 40: "2xl", - 64: "3xl" + 64: "3xl", }; -const opacity = [ - 0, 5, 10, 20, 25, 30, 40, 50, 60, 70, 75, 80, 90, 95, -]; +const opacity = [0, 5, 10, 20, 25, 30, 40, 50, 60, 70, 75, 80, 90, 95]; // AutoGenerated for Tailwind 2 via [convert_tailwind_colors.js] const color: Record = { @@ -346,7 +344,7 @@ const color: Record = { "#be123c": "rose-700", "#9f1239": "rose-800", "#881337": "rose-900", - "#4c0519": "rose-950" + "#4c0519": "rose-950", }; export const config = { diff --git a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts index bbf4d5ed..4e2ec67b 100644 --- a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts +++ b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts @@ -46,13 +46,13 @@ export class TailwindDefaultBuilder { }; blend( - node: SceneNode & SceneNodeMixin & MinimalBlendMixin & LayoutMixin + node: SceneNode & SceneNodeMixin & MinimalBlendMixin & LayoutMixin, ): this { this.addAttributes( tailwindVisibility(node), tailwindRotation(node), tailwindOpacity(node), - tailwindBlendMode(node) + tailwindBlendMode(node), ); return this; @@ -64,7 +64,7 @@ export class TailwindDefaultBuilder { BlendMixin & LayoutMixin & MinimalBlendMixin, - optimizeLayout: boolean + optimizeLayout: boolean, ): this { this.size(node, optimizeLayout); this.autoLayoutPadding(node, optimizeLayout); @@ -137,7 +137,7 @@ export class TailwindDefaultBuilder { */ customColor( paint: ReadonlyArray | PluginAPI["mixed"], - kind: string + kind: string, ): this { // visible is true or undefinied (tests) if (this.visible) { @@ -190,8 +190,8 @@ export class TailwindDefaultBuilder { if ("paddingLeft" in node) { this.addAttributes( ...tailwindPadding( - (optimizeLayout ? node.inferredAutoLayout : null) ?? node - ) + (optimizeLayout ? node.inferredAutoLayout : null) ?? node, + ), ); } return this; @@ -208,7 +208,7 @@ export class TailwindDefaultBuilder { } const backgroundBlur = node.effects.find( - (e) => e.type === "BACKGROUND_BLUR" + (e) => e.type === "BACKGROUND_BLUR", ); if (backgroundBlur) { const backgroundBlurValue = pxToBlur(backgroundBlur.radius); @@ -216,7 +216,7 @@ export class TailwindDefaultBuilder { this.addAttributes( `backdrop-blur${ backgroundBlurValue ? `-${backgroundBlurValue}` : "" - }` + }`, ); } } diff --git a/packages/backend/src/tailwind/tailwindMain.ts b/packages/backend/src/tailwind/tailwindMain.ts index caeb3584..ebf9232d 100644 --- a/packages/backend/src/tailwind/tailwindMain.ts +++ b/packages/backend/src/tailwind/tailwindMain.ts @@ -15,7 +15,7 @@ const selfClosingTags = ["img"]; export const tailwindMain = ( sceneNode: Array, - settings: PluginSettings + settings: PluginSettings, ): string => { localTailwindSettings = settings; previousExecutionCache = []; @@ -33,7 +33,7 @@ export const tailwindMain = ( // todo lint idea: replace BorderRadius.only(topleft: 8, topRight: 8) with BorderRadius.horizontal(8) const tailwindWidgetGenerator = ( sceneNode: ReadonlyArray, - isJsx: boolean + isJsx: boolean, ): string => { let comp = ""; @@ -84,7 +84,7 @@ const tailwindGroup = (node: GroupNode, isJsx: boolean = false): string => { node, localTailwindSettings.layerName, "", - isJsx + isJsx, ); if (vectorIfExists) return vectorIfExists; @@ -92,7 +92,7 @@ const tailwindGroup = (node: GroupNode, isJsx: boolean = false): string => { const builder = new TailwindDefaultBuilder( node, localTailwindSettings.layerName, - isJsx + isJsx, ) .blend(node) .size(node, localTailwindSettings.optimizeLayout) @@ -113,7 +113,7 @@ export const tailwindText = (node: TextNode, isJsx: boolean): string => { let layoutBuilder = new TailwindTextBuilder( node, localTailwindSettings.layerName, - isJsx + isJsx, ) .commonPositionStyles(node, localTailwindSettings.optimizeLayout) .textAlign(node); @@ -136,21 +136,24 @@ export const tailwindText = (node: TextNode, isJsx: boolean): string => { const tailwindFrame = ( node: FrameNode | InstanceNode | ComponentNode | ComponentSetNode, - isJsx: boolean + isJsx: boolean, ): string => { const childrenStr = tailwindWidgetGenerator( commonSortChildrenWhenInferredAutoLayout( node, - localTailwindSettings.optimizeLayout + localTailwindSettings.optimizeLayout, ), - isJsx + isJsx, ); if (node.layoutMode !== "NONE") { const rowColumn = tailwindAutoLayoutProps(node, node); return tailwindContainer(node, childrenStr, rowColumn, isJsx); } else { - if (localTailwindSettings.optimizeLayout && node.inferredAutoLayout !== null) { + if ( + localTailwindSettings.optimizeLayout && + node.inferredAutoLayout !== null + ) { const rowColumn = tailwindAutoLayoutProps(node, node.inferredAutoLayout); return tailwindContainer(node, childrenStr, rowColumn, isJsx); } @@ -172,7 +175,7 @@ export const tailwindContainer = ( MinimalBlendMixin, children: string, additionalAttr: string, - isJsx: boolean + isJsx: boolean, ): string => { // ignore the view when size is zero or less // while technically it shouldn't get less than 0, due to rounding errors, @@ -181,7 +184,11 @@ export const tailwindContainer = ( return children; } - let builder = new TailwindDefaultBuilder(node, localTailwindSettings.layerName, isJsx) + let builder = new TailwindDefaultBuilder( + node, + localTailwindSettings.layerName, + isJsx, + ) .commonPositionStyles(node, localTailwindSettings.optimizeLayout) .commonShapeStyles(node); @@ -195,13 +202,13 @@ export const tailwindContainer = ( if (!("children" in node) || node.children.length === 0) { tag = "img"; src = ` src="https://via.placeholder.com/${node.width.toFixed( - 0 + 0, )}x${node.height.toFixed(0)}"`; } else { builder.addAttributes( `bg-[url(https://via.placeholder.com/${node.width.toFixed( - 0 - )}x${node.height.toFixed(0)})]` + 0, + )}x${node.height.toFixed(0)})]`, ); } } @@ -222,7 +229,7 @@ export const tailwindLine = (node: LineNode, isJsx: boolean): string => { const builder = new TailwindDefaultBuilder( node, localTailwindSettings.layerName, - isJsx + isJsx, ) .commonPositionStyles(node, localTailwindSettings.optimizeLayout) .commonShapeStyles(node); @@ -235,7 +242,7 @@ export const tailwindSection = (node: SectionNode, isJsx: boolean): string => { const builder = new TailwindDefaultBuilder( node, localTailwindSettings.layerName, - isJsx + isJsx, ) .size(node, localTailwindSettings.optimizeLayout) .position(node, localTailwindSettings.optimizeLayout) diff --git a/packages/backend/src/tailwind/tailwindTextBuilder.ts b/packages/backend/src/tailwind/tailwindTextBuilder.ts index de35c885..8b23e940 100644 --- a/packages/backend/src/tailwind/tailwindTextBuilder.ts +++ b/packages/backend/src/tailwind/tailwindTextBuilder.ts @@ -24,11 +24,11 @@ export class TailwindTextBuilder extends TailwindDefaultBuilder { const textTransform = this.textTransform(segment.textCase); const lineHeightStyle = this.lineHeight( segment.lineHeight, - segment.fontSize + segment.fontSize, ); const letterSpacingStyle = this.letterSpacing( segment.letterSpacing, - segment.fontSize + segment.fontSize, ); // const textIndentStyle = this.indentStyle(segment.indentation); @@ -52,7 +52,7 @@ export class TailwindTextBuilder extends TailwindDefaultBuilder { } getTailwindColorFromFills = ( - fills: ReadonlyArray | PluginAPI["mixed"] + fills: ReadonlyArray | PluginAPI["mixed"], ) => { // Implement a function to convert fills to the appropriate Tailwind CSS color classes. // This can be based on your project's configuration and color palette. diff --git a/packages/backend/src/tailwind/vector.ts b/packages/backend/src/tailwind/vector.ts index 073cb815..ef2caa71 100644 --- a/packages/backend/src/tailwind/vector.ts +++ b/packages/backend/src/tailwind/vector.ts @@ -4,7 +4,7 @@ export const tailwindVector = ( node: FrameNode | GroupNode, showLayerName: boolean, parentId: string, - isJsx: boolean + isJsx: boolean, ): string => { // TODO VECTOR return ""; diff --git a/packages/plugin-ui/src/PluginUI.tsx b/packages/plugin-ui/src/PluginUI.tsx index 6f0c617f..fa15604a 100644 --- a/packages/plugin-ui/src/PluginUI.tsx +++ b/packages/plugin-ui/src/PluginUI.tsx @@ -196,7 +196,7 @@ export const preferenceOptions: LocalCodegenPreference[] = [ itemType: "individual_select", propertyName: "optimizeLayout", label: "Optimize layout", - description: 'Attempt to auto-layout suitable element groups', + description: "Attempt to auto-layout suitable element groups", isDefault: true, includedLanguages: ["HTML", "Tailwind", "Flutter", "SwiftUI"], }, @@ -204,7 +204,7 @@ export const preferenceOptions: LocalCodegenPreference[] = [ itemType: "individual_select", propertyName: "layerName", label: "Layer names", - description: 'Include layer names in classes', + description: "Include layer names in classes", isDefault: false, includedLanguages: ["HTML", "Tailwind"], }, @@ -212,7 +212,7 @@ export const preferenceOptions: LocalCodegenPreference[] = [ itemType: "individual_select", propertyName: "roundTailwindValues", label: "Round values", - description: 'Round pixel values to nearest Tailwind sizes', + description: "Round pixel values to nearest Tailwind sizes", isDefault: false, includedLanguages: ["Tailwind"], }, @@ -220,7 +220,7 @@ export const preferenceOptions: LocalCodegenPreference[] = [ itemType: "individual_select", propertyName: "roundTailwindColors", label: "Round colors", - description: 'Round color values to nearest Tailwind colors', + description: "Round color values to nearest Tailwind colors", isDefault: false, includedLanguages: ["Tailwind"], }, @@ -228,7 +228,7 @@ export const preferenceOptions: LocalCodegenPreference[] = [ itemType: "individual_select", propertyName: "customTailwindColors", label: "Custom colors", - description: 'Use color variable names as custom color names', + description: "Use color variable names as custom color names", isDefault: false, includedLanguages: ["Tailwind"], }, @@ -306,7 +306,7 @@ export const CodePanel = (props: { } else { const selectablePreferencesFiltered = selectPreferenceOptions.filter( (preference) => - preference.includedLanguages?.includes(props.selectedFramework) + preference.includedLanguages?.includes(props.selectedFramework), ); return ( @@ -335,7 +335,7 @@ export const CodePanel = (props: { {preferenceOptions .filter((preference) => - preference.includedLanguages?.includes(props.selectedFramework) + preference.includedLanguages?.includes(props.selectedFramework), ) .map((preference) => ( { props.onPreferenceChange( preference.propertyName, - option.value + option.value, ); }} buttonClass="bg-blue-100 dark:bg-black dark:ring-blue-800" @@ -644,7 +644,7 @@ export const Preview: React.FC<{ const targetHeight = props.isResponsiveExpanded ? 260 : 120; const scaleFactor = Math.min( targetWidth / props.htmlPreview.size.width, - targetHeight / props.htmlPreview.size.height + targetHeight / props.htmlPreview.size.height, ); return (
Date: Mon, 16 Dec 2024 18:20:15 +0100 Subject: [PATCH 011/168] Layer names (#142) * Added a note about debug mode * Update README.md Added image of debug mode * refacotred layerName into showLayerNames globally * Layer Names are now an attribute called data-layer rather than a class name. Refactored build() in builder classes slightly * Added layer name to data-layer and kept class layer name too --- apps/plugin/plugin-src/code.ts | 2 +- packages/backend/src/code.ts | 2 +- .../src/common/commonFormatAttributes.ts | 27 +++++++++++ packages/backend/src/common/numToAutoFixed.ts | 2 +- packages/backend/src/flutter/flutterMain.ts | 9 ++-- .../backend/src/html/htmlDefaultBuilder.ts | 40 ++++++++--------- packages/backend/src/html/htmlMain.ts | 16 +++---- packages/backend/src/html/htmlTextBuilder.ts | 4 +- packages/backend/src/swiftui/swiftuiMain.ts | 6 +-- .../src/tailwind/tailwindDefaultBuilder.ts | 45 ++++++++++++------- packages/backend/src/tailwind/tailwindMain.ts | 12 ++--- packages/backend/src/tailwind/vector.ts | 2 +- packages/plugin-ui/src/PluginUI.tsx | 4 +- 13 files changed, 105 insertions(+), 66 deletions(-) create mode 100644 packages/backend/src/common/commonFormatAttributes.ts diff --git a/apps/plugin/plugin-src/code.ts b/apps/plugin/plugin-src/code.ts index 8725cd44..7d47b2c7 100644 --- a/apps/plugin/plugin-src/code.ts +++ b/apps/plugin/plugin-src/code.ts @@ -19,7 +19,7 @@ const defaultPluginSettings: PluginSettings = { framework: "HTML", jsx: false, optimizeLayout: true, - layerName: false, + showLayerNames: false, inlineStyle: true, responsiveRoot: false, flutterGenerationMode: "snippet", diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index f9d939e2..c247264b 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -15,7 +15,7 @@ export type PluginSettings = { jsx: boolean; inlineStyle: boolean; optimizeLayout: boolean; - layerName: boolean; + showLayerNames: boolean; responsiveRoot: boolean; flutterGenerationMode: string; swiftUIGenerationMode: string; diff --git a/packages/backend/src/common/commonFormatAttributes.ts b/packages/backend/src/common/commonFormatAttributes.ts new file mode 100644 index 00000000..4f92d4be --- /dev/null +++ b/packages/backend/src/common/commonFormatAttributes.ts @@ -0,0 +1,27 @@ +import { stringToClassName as stringToClassName } from "./numToAutoFixed"; + +export const getClassLabel = (isJSX: boolean = false) => + isJSX ? "className" : "class"; + +export const joinStyles = (styles: string[], isJSX: boolean) => + styles.map((s) => s.trim()).join(isJSX ? ", " : "; "); + +export const formatStyleAttribute = ( + styles: string[], + isJSX: boolean, +): string => { + const trimmedStyles = joinStyles(styles, isJSX); + + if (trimmedStyles === "") return ""; + + return ` style=${isJSX ? `{{${trimmedStyles}}}` : `"${trimmedStyles}"`}`; +}; + +export const formatLayerNameAttribute = (name: string) => + name === "" ? "" : ` data-layer="${name}"`; + +export const formatClassAttribute = ( + classes: string[], + isJSX: boolean, +): string => + classes.length === 0 ? "" : ` ${getClassLabel(isJSX)}="${classes.join(" ")}"`; diff --git a/packages/backend/src/common/numToAutoFixed.ts b/packages/backend/src/common/numToAutoFixed.ts index 0836be87..61f704be 100644 --- a/packages/backend/src/common/numToAutoFixed.ts +++ b/packages/backend/src/common/numToAutoFixed.ts @@ -86,7 +86,7 @@ function escapeRegExp(string: string) { export const replaceAllUtil = (str: string, find: string, replace: string) => str.replace(new RegExp(escapeRegExp(find), "g"), replace); -export function className(name: string): string { +export function stringToClassName(name: string): string { const words = name.split(/[^a-zA-Z0-9]+/); const camelCaseWords = words.map((word, index) => { if (index === 0) { diff --git a/packages/backend/src/flutter/flutterMain.ts b/packages/backend/src/flutter/flutterMain.ts index 0859485b..7272db7c 100644 --- a/packages/backend/src/flutter/flutterMain.ts +++ b/packages/backend/src/flutter/flutterMain.ts @@ -1,4 +1,7 @@ -import { className, generateWidgetCode } from "../common/numToAutoFixed"; +import { + stringToClassName, + generateWidgetCode, +} from "../common/numToAutoFixed"; import { retrieveTopFill } from "../common/retrieveFill"; import { FlutterDefaultBuilder } from "./flutterDefaultBuilder"; import { FlutterTextBuilder } from "./flutterTextBuilder"; @@ -67,10 +70,10 @@ export const flutterMain = ( return result; case "stateless": result = generateWidgetCode("Column", { children: [result] }); - return getStatelessTemplate(className(sceneNode[0].name), result); + return getStatelessTemplate(stringToClassName(sceneNode[0].name), result); case "fullApp": result = generateWidgetCode("Column", { children: [result] }); - return getFullAppTemplate(className(sceneNode[0].name), result); + return getFullAppTemplate(stringToClassName(sceneNode[0].name), result); } return result; diff --git a/packages/backend/src/html/htmlDefaultBuilder.ts b/packages/backend/src/html/htmlDefaultBuilder.ts index c303df0d..3a50d9b2 100644 --- a/packages/backend/src/html/htmlDefaultBuilder.ts +++ b/packages/backend/src/html/htmlDefaultBuilder.ts @@ -7,7 +7,6 @@ import { htmlBlendMode, } from "./builderImpl/htmlBlend"; import { - htmlColor, htmlColorFromFills, htmlGradientFromFills, } from "./builderImpl/htmlColor"; @@ -18,22 +17,25 @@ import { commonIsAbsolutePosition, getCommonPositionValue, } from "../common/commonPosition"; -import { className, sliceNum } from "../common/numToAutoFixed"; +import { sliceNum, stringToClassName } from "../common/numToAutoFixed"; import { commonStroke } from "../common/commonStroke"; +import { + formatClassAttribute, + formatLayerNameAttribute, + formatStyleAttribute, +} from "../common/commonFormatAttributes"; export class HtmlDefaultBuilder { styles: Array; isJSX: boolean; visible: boolean; - name: string = ""; + name: string; - constructor(node: SceneNode, showLayerName: boolean, optIsJSX: boolean) { + constructor(node: SceneNode, showLayerNames: boolean, optIsJSX: boolean) { this.isJSX = optIsJSX; this.styles = []; this.visible = node.visible; - if (showLayerName) { - this.name = className(node.name); - } + this.name = showLayerNames ? node.name : ""; } commonPositionStyles( @@ -287,20 +289,14 @@ export class HtmlDefaultBuilder { build(additionalStyle: Array = []): string { this.addStyles(...additionalStyle); - const formattedStyles = this.styles.map((s) => s.trim()); - let formattedStyle = ""; - if (this.styles.length > 0) { - if (this.isJSX) { - formattedStyle = ` style={{${formattedStyles.join(", ")}}}`; - } else { - formattedStyle = ` style="${formattedStyles.join("; ")}"`; - } - } - if (this.name.length > 0) { - const classOrClassName = this.isJSX ? "className" : "class"; - return ` ${classOrClassName}="${this.name}"${formattedStyle}`; - } else { - return formattedStyle; - } + const layerNameAttribute = formatLayerNameAttribute(this.name); + const layerNameClass = stringToClassName(this.name); + const classAttribute = formatClassAttribute( + layerNameClass === "" ? [] : [layerNameClass], + this.isJSX, + ); + const styleAttribute = formatStyleAttribute(this.styles, this.isJSX); + + return `${layerNameAttribute}${classAttribute}${styleAttribute}`; } } diff --git a/packages/backend/src/html/htmlMain.ts b/packages/backend/src/html/htmlMain.ts index 3370f0b4..41c54fa1 100644 --- a/packages/backend/src/html/htmlMain.ts +++ b/packages/backend/src/html/htmlMain.ts @@ -7,7 +7,7 @@ import { htmlAutoLayoutProps } from "./builderImpl/htmlAutoLayout"; import { formatWithJSX } from "../common/parseJSX"; import { commonSortChildrenWhenInferredAutoLayout } from "../common/commonChildrenOrder"; -let showLayerName = false; +let showLayerNames = false; const selfClosingTags = ["img"]; @@ -21,7 +21,7 @@ export const htmlMain = ( settings: PluginSettings, isPreview: boolean = false, ): string => { - showLayerName = settings.layerName; + showLayerNames = settings.showLayerNames; isPreviewGlobal = isPreview; previousExecutionCache = []; localSettings = settings; @@ -95,7 +95,7 @@ const htmlGroup = (node: GroupNode, isJsx: boolean = false): string => { // this needs to be called after CustomNode because widthHeight depends on it const builder = new HtmlDefaultBuilder( node, - showLayerName, + showLayerNames, isJsx, ).commonPositionStyles(node, localSettings.optimizeLayout); @@ -112,7 +112,7 @@ const htmlGroup = (node: GroupNode, isJsx: boolean = false): string => { // this was split from htmlText to help the UI part, where the style is needed (without

). export const htmlText = (node: TextNode, isJsx: boolean): string => { - let layoutBuilder = new HtmlTextBuilder(node, showLayerName, isJsx) + let layoutBuilder = new HtmlTextBuilder(node, showLayerNames, isJsx) .commonPositionStyles(node, localSettings.optimizeLayout) .textAlign(node); @@ -168,7 +168,7 @@ export const htmlAsset = (node: SceneNode, isJsx: boolean = false): string => { return ""; } - const builder = new HtmlDefaultBuilder(node, showLayerName, isJsx) + const builder = new HtmlDefaultBuilder(node, showLayerNames, isJsx) .commonPositionStyles(node, localSettings.optimizeLayout) .commonShapeStyles(node); @@ -208,7 +208,7 @@ export const htmlContainer = ( return children; } - const builder = new HtmlDefaultBuilder(node, showLayerName, isJsx) + const builder = new HtmlDefaultBuilder(node, showLayerNames, isJsx) .commonPositionStyles(node, localSettings.optimizeLayout) .commonShapeStyles(node); @@ -253,7 +253,7 @@ export const htmlSection = ( isJsx: boolean = false, ): string => { const childrenStr = htmlWidgetGenerator(node.children, isJsx); - const builder = new HtmlDefaultBuilder(node, showLayerName, isJsx) + const builder = new HtmlDefaultBuilder(node, showLayerNames, isJsx) .size(node, localSettings.optimizeLayout) .position(node, localSettings.optimizeLayout) .applyFillsToStyle(node.fills, "background"); @@ -266,7 +266,7 @@ export const htmlSection = ( }; export const htmlLine = (node: LineNode, isJsx: boolean): string => { - const builder = new HtmlDefaultBuilder(node, showLayerName, isJsx) + const builder = new HtmlDefaultBuilder(node, showLayerNames, isJsx) .commonPositionStyles(node, localSettings.optimizeLayout) .commonShapeStyles(node); diff --git a/packages/backend/src/html/htmlTextBuilder.ts b/packages/backend/src/html/htmlTextBuilder.ts index 9a6bc277..46ff1846 100644 --- a/packages/backend/src/html/htmlTextBuilder.ts +++ b/packages/backend/src/html/htmlTextBuilder.ts @@ -8,8 +8,8 @@ import { } from "../common/commonTextHeightSpacing"; export class HtmlTextBuilder extends HtmlDefaultBuilder { - constructor(node: TextNode, showLayerName: boolean, optIsJSX: boolean) { - super(node, showLayerName, optIsJSX); + constructor(node: TextNode, showLayerNames: boolean, optIsJSX: boolean) { + super(node, showLayerNames, optIsJSX); } getTextSegments(id: string): { style: string; text: string }[] { diff --git a/packages/backend/src/swiftui/swiftuiMain.ts b/packages/backend/src/swiftui/swiftuiMain.ts index afad6ec8..27e17bb5 100644 --- a/packages/backend/src/swiftui/swiftuiMain.ts +++ b/packages/backend/src/swiftui/swiftuiMain.ts @@ -1,5 +1,5 @@ import { indentString } from "../common/indentString"; -import { className, sliceNum } from "../common/numToAutoFixed"; +import { stringToClassName, sliceNum } from "../common/numToAutoFixed"; import { SwiftuiTextBuilder } from "./swiftuiTextBuilder"; import { SwiftuiDefaultBuilder } from "./swiftuiDefaultBuilder"; import { PluginSettings } from "../code"; @@ -43,10 +43,10 @@ export const swiftuiMain = ( return result; case "struct": // result = generateWidgetCode("Column", { children: [result] }); - return getStructTemplate(className(sceneNode[0].name), result); + return getStructTemplate(stringToClassName(sceneNode[0].name), result); case "preview": // result = generateWidgetCode("Column", { children: [result] }); - return getPreviewTemplate(className(sceneNode[0].name), result); + return getPreviewTemplate(stringToClassName(sceneNode[0].name), result); } // remove the initial \n that is made in Container. diff --git a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts index 4e2ec67b..961ec898 100644 --- a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts +++ b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts @@ -1,4 +1,4 @@ -import { className, sliceNum } from "./../common/numToAutoFixed"; +import { stringToClassName, sliceNum } from "./../common/numToAutoFixed"; import { tailwindShadow } from "./builderImpl/tailwindShadow"; import { tailwindVisibility, @@ -21,6 +21,13 @@ import { getCommonPositionValue, } from "../common/commonPosition"; import { pxToBlur } from "./conversionTables"; +import { + getClassLabel, + formatStyleAttribute, +} from "../common/commonFormatAttributes"; + +const isNotEmpty = (s: string) => s !== ""; +const dropEmptyStrings = (strings: string[]) => strings.filter(isNotEmpty); export class TailwindDefaultBuilder { attributes: string[] = []; @@ -28,21 +35,27 @@ export class TailwindDefaultBuilder { styleSeparator: string = ""; isJSX: boolean; visible: boolean; - name: string = ""; + name: string; - constructor(node: SceneNode, showLayerName: boolean, optIsJSX: boolean) { + constructor(node: SceneNode, showLayerNames: boolean, optIsJSX: boolean) { this.isJSX = optIsJSX; this.styleSeparator = this.isJSX ? "," : ";"; this.style = ""; this.visible = node.visible; + this.name = showLayerNames ? node.name : ""; - if (showLayerName) { + /* + if (showLayerNames) { this.attributes.push(className(node.name)); } + */ } addAttributes = (...newStyles: string[]) => { - this.attributes.push(...newStyles.filter((style) => style !== "")); + this.attributes.push(...dropEmptyStrings(newStyles)); + }; + prependAttributes = (...newStyles: string[]) => { + this.attributes.unshift(...dropEmptyStrings(newStyles)); }; blend( @@ -224,21 +237,21 @@ export class TailwindDefaultBuilder { } build(additionalAttr = ""): string { - // this.attributes.unshift(this.name + additionalAttr); this.addAttributes(additionalAttr); - if (this.style.length > 0) { - this.style = ` style="${this.style}"`; - } - if (!this.attributes.length && !this.style) { - return ""; - } - const classOrClassName = this.isJSX ? "className" : "class"; - if (this.attributes.length === 0) { - return ""; + if (this.name !== "") { + this.prependAttributes(stringToClassName(this.name)); } + const layerName = this.name ? ` data-layer="${this.name}"` : ""; + + const classLabel = getClassLabel(this.isJSX); + const classNames = + this.attributes.length > 0 + ? ` ${classLabel}="${this.attributes.join(" ")}"` + : ""; + const styles = this.style.length > 0 ? ` style="${this.style}"` : ""; - return ` ${classOrClassName}="${this.attributes.join(" ")}"${this.style}`; + return `${layerName}${classNames}${styles}`; } reset(): void { diff --git a/packages/backend/src/tailwind/tailwindMain.ts b/packages/backend/src/tailwind/tailwindMain.ts index ebf9232d..83928ada 100644 --- a/packages/backend/src/tailwind/tailwindMain.ts +++ b/packages/backend/src/tailwind/tailwindMain.ts @@ -82,7 +82,7 @@ const tailwindGroup = (node: GroupNode, isJsx: boolean = false): string => { const vectorIfExists = tailwindVector( node, - localTailwindSettings.layerName, + localTailwindSettings.showLayerNames, "", isJsx, ); @@ -91,7 +91,7 @@ const tailwindGroup = (node: GroupNode, isJsx: boolean = false): string => { // this needs to be called after CustomNode because widthHeight depends on it const builder = new TailwindDefaultBuilder( node, - localTailwindSettings.layerName, + localTailwindSettings.showLayerNames, isJsx, ) .blend(node) @@ -112,7 +112,7 @@ const tailwindGroup = (node: GroupNode, isJsx: boolean = false): string => { export const tailwindText = (node: TextNode, isJsx: boolean): string => { let layoutBuilder = new TailwindTextBuilder( node, - localTailwindSettings.layerName, + localTailwindSettings.showLayerNames, isJsx, ) .commonPositionStyles(node, localTailwindSettings.optimizeLayout) @@ -186,7 +186,7 @@ export const tailwindContainer = ( let builder = new TailwindDefaultBuilder( node, - localTailwindSettings.layerName, + localTailwindSettings.showLayerNames, isJsx, ) .commonPositionStyles(node, localTailwindSettings.optimizeLayout) @@ -228,7 +228,7 @@ export const tailwindContainer = ( export const tailwindLine = (node: LineNode, isJsx: boolean): string => { const builder = new TailwindDefaultBuilder( node, - localTailwindSettings.layerName, + localTailwindSettings.showLayerNames, isJsx, ) .commonPositionStyles(node, localTailwindSettings.optimizeLayout) @@ -241,7 +241,7 @@ export const tailwindSection = (node: SectionNode, isJsx: boolean): string => { const childrenStr = tailwindWidgetGenerator(node.children, isJsx); const builder = new TailwindDefaultBuilder( node, - localTailwindSettings.layerName, + localTailwindSettings.showLayerNames, isJsx, ) .size(node, localTailwindSettings.optimizeLayout) diff --git a/packages/backend/src/tailwind/vector.ts b/packages/backend/src/tailwind/vector.ts index ef2caa71..d73946be 100644 --- a/packages/backend/src/tailwind/vector.ts +++ b/packages/backend/src/tailwind/vector.ts @@ -2,7 +2,7 @@ export const tailwindVector = ( node: FrameNode | GroupNode, - showLayerName: boolean, + showLayerNames: boolean, parentId: string, isJsx: boolean, ): string => { diff --git a/packages/plugin-ui/src/PluginUI.tsx b/packages/plugin-ui/src/PluginUI.tsx index fa15604a..a3ed7177 100644 --- a/packages/plugin-ui/src/PluginUI.tsx +++ b/packages/plugin-ui/src/PluginUI.tsx @@ -11,7 +11,7 @@ export type PluginSettings = { jsx: boolean; inlineStyle: boolean; optimizeLayout: boolean; - layerName: boolean; + showLayerNames: boolean; responsiveRoot: boolean; flutterGenerationMode: string; swiftUIGenerationMode: string; @@ -202,7 +202,7 @@ export const preferenceOptions: LocalCodegenPreference[] = [ }, { itemType: "individual_select", - propertyName: "layerName", + propertyName: "showLayerNames", label: "Layer names", description: "Include layer names in classes", isDefault: false, From 53025e9b8b144c0c49438e9e2db09eadc362a1fa Mon Sep 17 00:00:00 2001 From: "Mims H. Wright" Date: Mon, 16 Dec 2024 18:20:25 +0100 Subject: [PATCH 012/168] Delete tests (#141) * Added a note about debug mode * Update README.md Added image of debug mode * Removed Jest tests --- .eslintrc.js | 2 +- __tests__/README.md | 16 - __tests__/altNodes/altConversions.test.ts | 178 ------ .../altNodes/convertGroupToFrame.test.ts | 70 --- .../altNodes/convertNodesOnRectangle.test.ts | 386 ------------- .../altNodes/convertToAutoLayout.test.ts | 166 ------ .../flutter/builderImpl/flutterBlend.test.ts | 75 --- .../flutter/builderImpl/flutterBorder.test.ts | 87 --- .../flutter/builderImpl/flutterColor.test.ts | 283 --------- .../builderImpl/flutterPadding.test.ts | 66 --- .../builderImpl/flutterPosition.test.ts | 244 -------- .../flutter/builderImpl/flutterShadow.test.ts | 78 --- .../flutter/builderImpl/flutterSize.test.ts | 192 ------- __tests__/flutter/flutterContainer.test.ts | 90 --- __tests__/flutter/flutterMain.test.ts | 424 -------------- __tests__/flutter/flutterText.test.ts | 327 ----------- __tests__/html/builderImpl/htmlBlend.test.ts | 44 -- __tests__/html/builderImpl/htmlBorder.test.ts | 76 --- __tests__/html/builderImpl/htmlColor.test.ts | 178 ------ .../html/builderImpl/htmlPadding.test.ts | 48 -- .../html/builderImpl/htmlPosition.test.ts | 63 --- __tests__/html/builderImpl/htmlShadow.test.ts | 45 -- __tests__/html/builderImpl/htmlSize.test.ts | 105 ---- __tests__/html/htmlMain.test.ts | 353 ------------ __tests__/html/htmlText.test.ts | 196 ------- __tests__/nearest-color/nearestColor.test.ts | 21 - __tests__/retrieveUI/retrieveColors.test.ts | 191 ------- .../swiftui/builderImpl/swiftuiBlend.test.ts | 97 ---- .../swiftui/builderImpl/swiftuiBorder.test.ts | 117 ---- .../swiftui/builderImpl/swiftuiColor.test.ts | 276 --------- .../builderImpl/swiftuiEffects.test.ts | 111 ---- .../builderImpl/swiftuiPadding.test.ts | 71 --- .../builderImpl/swiftuiPosition.test.ts | 137 ----- .../swiftui/builderImpl/swiftuiSize.test.ts | 260 --------- __tests__/swiftui/swiftuiMain.test.ts | 373 ------------ __tests__/swiftui/swiftuiText.test.ts | 255 --------- .../builderImpl/tailwindBlend.test.ts | 62 -- .../builderImpl/tailwindBorder.test.ts | 86 --- .../builderImpl/tailwindColor.test.ts | 306 ---------- .../builderImpl/tailwindPadding.test.ts | 86 --- .../builderImpl/tailwindPosition.test.ts | 150 ----- .../builderImpl/tailwindShadow.test.ts | 45 -- .../tailwind/builderImpl/tailwindSize.test.ts | 299 ---------- __tests__/tailwind/colors.test.ts | 32 -- __tests__/tailwind/conversionTables.test.ts | 37 -- __tests__/tailwind/size.test.ts | 535 ------------------ __tests__/tailwind/tailwindMain.test.ts | 203 ------- __tests__/tailwind/tailwindText.test.ts | 189 ------- jest.config.js | 11 - packages/backend/package.json | 3 +- packages/plugin-ui/package.json | 3 +- 51 files changed, 3 insertions(+), 7745 deletions(-) delete mode 100644 __tests__/README.md delete mode 100644 __tests__/altNodes/altConversions.test.ts delete mode 100644 __tests__/altNodes/convertGroupToFrame.test.ts delete mode 100644 __tests__/altNodes/convertNodesOnRectangle.test.ts delete mode 100644 __tests__/altNodes/convertToAutoLayout.test.ts delete mode 100644 __tests__/flutter/builderImpl/flutterBlend.test.ts delete mode 100644 __tests__/flutter/builderImpl/flutterBorder.test.ts delete mode 100644 __tests__/flutter/builderImpl/flutterColor.test.ts delete mode 100644 __tests__/flutter/builderImpl/flutterPadding.test.ts delete mode 100644 __tests__/flutter/builderImpl/flutterPosition.test.ts delete mode 100644 __tests__/flutter/builderImpl/flutterShadow.test.ts delete mode 100644 __tests__/flutter/builderImpl/flutterSize.test.ts delete mode 100644 __tests__/flutter/flutterContainer.test.ts delete mode 100644 __tests__/flutter/flutterMain.test.ts delete mode 100644 __tests__/flutter/flutterText.test.ts delete mode 100644 __tests__/html/builderImpl/htmlBlend.test.ts delete mode 100644 __tests__/html/builderImpl/htmlBorder.test.ts delete mode 100644 __tests__/html/builderImpl/htmlColor.test.ts delete mode 100644 __tests__/html/builderImpl/htmlPadding.test.ts delete mode 100644 __tests__/html/builderImpl/htmlPosition.test.ts delete mode 100644 __tests__/html/builderImpl/htmlShadow.test.ts delete mode 100644 __tests__/html/builderImpl/htmlSize.test.ts delete mode 100644 __tests__/html/htmlMain.test.ts delete mode 100644 __tests__/html/htmlText.test.ts delete mode 100644 __tests__/nearest-color/nearestColor.test.ts delete mode 100644 __tests__/retrieveUI/retrieveColors.test.ts delete mode 100644 __tests__/swiftui/builderImpl/swiftuiBlend.test.ts delete mode 100644 __tests__/swiftui/builderImpl/swiftuiBorder.test.ts delete mode 100644 __tests__/swiftui/builderImpl/swiftuiColor.test.ts delete mode 100644 __tests__/swiftui/builderImpl/swiftuiEffects.test.ts delete mode 100644 __tests__/swiftui/builderImpl/swiftuiPadding.test.ts delete mode 100644 __tests__/swiftui/builderImpl/swiftuiPosition.test.ts delete mode 100644 __tests__/swiftui/builderImpl/swiftuiSize.test.ts delete mode 100644 __tests__/swiftui/swiftuiMain.test.ts delete mode 100644 __tests__/swiftui/swiftuiText.test.ts delete mode 100644 __tests__/tailwind/builderImpl/tailwindBlend.test.ts delete mode 100644 __tests__/tailwind/builderImpl/tailwindBorder.test.ts delete mode 100644 __tests__/tailwind/builderImpl/tailwindColor.test.ts delete mode 100644 __tests__/tailwind/builderImpl/tailwindPadding.test.ts delete mode 100644 __tests__/tailwind/builderImpl/tailwindPosition.test.ts delete mode 100644 __tests__/tailwind/builderImpl/tailwindShadow.test.ts delete mode 100644 __tests__/tailwind/builderImpl/tailwindSize.test.ts delete mode 100644 __tests__/tailwind/colors.test.ts delete mode 100644 __tests__/tailwind/conversionTables.test.ts delete mode 100644 __tests__/tailwind/size.test.ts delete mode 100644 __tests__/tailwind/tailwindMain.test.ts delete mode 100644 __tests__/tailwind/tailwindText.test.ts delete mode 100644 jest.config.js diff --git a/.eslintrc.js b/.eslintrc.js index cc83fed4..312e15c2 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,5 +1,5 @@ const config = { - extends: ["kentcdodds", "kentcdodds/jest"], + extends: ["kentcdodds"], rules: { "valid-jsdoc": "off", "max-len": "off", diff --git a/__tests__/README.md b/__tests__/README.md deleted file mode 100644 index 5520768c..00000000 --- a/__tests__/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# Tests - -This project uses [Jest](https://jestjs.io/) for tests, and has some help from [Figma API Stub](https://github.com/react-figma/figma-api-stub), since the official Figma Plugins API is untestable. - -## Coverage - -Coverage is currently at an impressive 99%. You can inspect the coverage by clicking at the Codecov badge: -[![codecov](https://codecov.io/gh/bernaferrari/FigmaToCode/branch/master/graph/badge.svg)](https://codecov.io/gh/bernaferrari/FigmaToCode) - -![Coverage](../assets/coverage.png) - -## Test commands - -- To run the tests: `yarn test` or `yarn run test` -- To calculate the coverage: `yarn run coverage` -- To run ES Lint: `yarn run lint` diff --git a/__tests__/altNodes/altConversions.test.ts b/__tests__/altNodes/altConversions.test.ts deleted file mode 100644 index 39f6e86e..00000000 --- a/__tests__/altNodes/altConversions.test.ts +++ /dev/null @@ -1,178 +0,0 @@ -import { createFigma } from "figma-api-stub"; -import { tailwindMain } from "../../src/tailwind/tailwindMain"; -import { convertIntoAltNodes } from "../../src/altNodes/altConversion"; -import { htmlMain } from "./../../src/html/htmlMain"; -import { AltFrameNode } from "./../../src/altNodes/altMixins"; - -describe("AltConversions", () => { - const figma = createFigma({ - simulateErrors: true, - isWithoutTimeout: false, - }); - // @ts-expect-error for some reason, need to override this for figma.mixed to work - global.figma = figma; - it("Rectangle", () => { - const rectangle = figma.createRectangle(); - rectangle.resize(20, 20); - - expect(tailwindMain(convertIntoAltNodes([rectangle]))).toEqual( - '
', - ); - }); - - it("Frame", () => { - const frame = figma.createFrame(); - frame.resize(20, 20); - frame.x = 0; - frame.y = 0; - frame.layoutMode = "HORIZONTAL"; - frame.counterAxisAlignItems = "CENTER"; - frame.primaryAxisAlignItems = "SPACE_BETWEEN"; - frame.counterAxisSizingMode = "FIXED"; - frame.primaryAxisSizingMode = "FIXED"; - - const rectangle = figma.createRectangle(); - rectangle.resize(20, 20); - rectangle.x = 0; - rectangle.y = 0; - rectangle.layoutGrow = 0; - rectangle.layoutAlign = "INHERIT"; - frame.appendChild(rectangle); - - expect(htmlMain(convertIntoAltNodes([frame]))).toEqual( - `
-
-
`, - ); - }); - - // todo understand why it is failing - // it("Group wrapping single item", () => { - // // single Group should disappear - // const node = figma.createFrame(); - // node.resize(20, 20); - - // const rectangle = figma.createRectangle(); - // rectangle.resize(20, 20); - - // figma.group([rectangle], node); - - // const convert = convertIntoAltNodes([node]); - - // expect(tailwindMain(convert)).toEqual(`
`); - // }); - - // todo understand why it is failing - // it("Group wrapping two items", () => { - // // single Group should disappear - // const node = figma.createFrame(); - // node.resize(20, 20); - // node.primaryAxisAlignItems = "CENTER"; - // node.counterAxisAlignItems = "CENTER"; - - // const rect1 = figma.createRectangle(); - // rect1.resize(20, 20); - - // const rect2 = figma.createRectangle(); - // rect2.resize(20, 20); - - // figma.group([rect1, rect2], node); - - // const convert = convertIntoAltNodes([node]); - - // expect(tailwindMain(convert)).toEqual( - // `
- //
- //
- //
` - // ); - // }); - - it("Text", () => { - const node = figma.createText(); - - figma.loadFontAsync({ - family: "Roboto", - style: "Regular", - }); - - node.fontName = { family: "Roboto", style: "Regular" }; - node.characters = ""; - - expect( - tailwindMain(convertIntoAltNodes([node], new AltFrameNode())), - ).toEqual(`

`); - }); - - it("Ellipse", () => { - // this test requires mocking the full EllipseNode. Figma-api-stub doesn't support VectorNode. - class EllipseNode { - readonly type = "ELLIPSE"; - } - - interface EllipseNode - extends DefaultShapeMixin, - ConstraintMixin, - CornerMixin { - readonly type: "ELLIPSE"; - clone(): EllipseNode; - arcData: ArcData; - } - - const node = new EllipseNode(); - // set read-only variables - Object.defineProperty(node, "width", { value: 20 }); - Object.defineProperty(node, "height", { value: 20 }); - - expect( - tailwindMain(convertIntoAltNodes([node], new AltFrameNode())), - ).toEqual(`
`); - }); - - it("Line", () => { - // this test requires mocking the full EllipseNode. Figma-api-stub doesn't support VectorNode. - class LineNode { - readonly type = "LINE"; - } - - interface LineNode extends DefaultShapeMixin, ConstraintMixin, CornerMixin { - readonly type: "LINE"; - clone(): LineNode; - } - - const node = new LineNode(); - // set read-only variables - Object.defineProperty(node, "width", { value: 20 }); - - expect( - tailwindMain(convertIntoAltNodes([node], new AltFrameNode())), - ).toEqual(`
`); - }); - - it("Vector", () => { - // this test requires mocking the full VectorNode. Figma-api-stub doesn't support VectorNode. - class VectorNode { - readonly type = "VECTOR"; - } - - interface VectorNode - extends DefaultShapeMixin, - ConstraintMixin, - CornerMixin { - readonly type: "VECTOR"; - clone(): VectorNode; - vectorNetwork: VectorNetwork; - vectorPaths: VectorPaths; - handleMirroring: HandleMirroring | PluginAPI["mixed"]; - } - - const node = new VectorNode(); - // set read-only variables - Object.defineProperty(node, "width", { value: 20 }); - Object.defineProperty(node, "height", { value: 20 }); - - expect( - tailwindMain(convertIntoAltNodes([node], new AltFrameNode())), - ).toEqual(`
`); - }); -}); diff --git a/__tests__/altNodes/convertGroupToFrame.test.ts b/__tests__/altNodes/convertGroupToFrame.test.ts deleted file mode 100644 index 6e706311..00000000 --- a/__tests__/altNodes/convertGroupToFrame.test.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { tailwindMain } from "../../src/tailwind/tailwindMain"; -import { AltGroupNode, AltRectangleNode } from "../../src/altNodes/altMixins"; -import { convertGroupToFrame } from "../../src/altNodes/convertGroupToFrame"; -import { convertNodesOnRectangle } from "../../src/altNodes/convertNodesOnRectangle"; - -describe("Convert Group to Frame", () => { - // @ts-expect-error for some reason, need to override this for figma.mixed to work - global.figma = { - mixed: undefined, - }; - - it("Simple conversion", () => { - const rectangle = new AltRectangleNode(); - rectangle.x = 20; - rectangle.y = 20; - rectangle.width = 20; - rectangle.height = 20; - - const group = new AltGroupNode(); - group.x = 20; - group.y = 20; - group.width = 20; - group.height = 20; - group.children = [rectangle]; - - const converted = convertGroupToFrame(group); - expect(tailwindMain([convertNodesOnRectangle(converted)])) - .toEqual(`
-
-
`); - }); - - it("Correctly position the children", () => { - const rect0 = new AltRectangleNode(); - rect0.x = 200; - rect0.y = 200; - rect0.width = 20; - rect0.height = 20; - - const rect1 = new AltRectangleNode(); - rect1.x = 220; - rect1.y = 220; - rect1.width = 20; - rect1.height = 20; - - const rect2 = new AltRectangleNode(); - rect2.x = 240; - rect2.y = 240; - rect2.width = 20; - rect2.height = 20; - - const group = new AltGroupNode(); - group.x = 200; - group.y = 200; - group.width = 260; - group.height = 260; - group.children = [rect0, rect1, rect2]; - - const newFrame = convertGroupToFrame(group); - - expect(newFrame.children[0].x).toEqual(0); - expect(newFrame.children[0].y).toEqual(0); - - expect(newFrame.children[1].x).toEqual(20); - expect(newFrame.children[1].x).toEqual(20); - - expect(newFrame.children[2].x).toEqual(40); - expect(newFrame.children[2].x).toEqual(40); - }); -}); diff --git a/__tests__/altNodes/convertNodesOnRectangle.test.ts b/__tests__/altNodes/convertNodesOnRectangle.test.ts deleted file mode 100644 index a22191f1..00000000 --- a/__tests__/altNodes/convertNodesOnRectangle.test.ts +++ /dev/null @@ -1,386 +0,0 @@ -import { tailwindSize } from "../../src/tailwind/builderImpl/tailwindSize"; -import { - AltFrameNode, - AltGroupNode, - AltRectangleNode, - AltSceneNode, - AltTextNode, -} from "../../src/altNodes/altMixins"; -import { tailwindMain } from "../../src/tailwind/tailwindMain"; -import { convertNodesOnRectangle } from "../../src/altNodes/convertNodesOnRectangle"; - -describe("convert node if child is big rect", () => { - // @ts-expect-error for some reason, need to override this for figma.mixed to work - global.figma = { - mixed: undefined, - }; - - it("frame with one child (no conversion)", () => { - const frame = new AltFrameNode(); - frame.width = 100; - frame.height = 100; - - const rectangle = new AltRectangleNode(); - rectangle.width = 100; - rectangle.height = 100; - rectangle.x = 0; - rectangle.y = 0; - rectangle.fills = [ - { - type: "SOLID", - color: { - r: 0, - g: 0, - b: 0, - }, - }, - ]; - - rectangle.parent = frame; - frame.children = [rectangle]; - - // it will only work with two or more items. - const converted = convertNodesOnRectangle(frame); - - expect(tailwindSize(converted)).toEqual("w-24 h-24 "); - - expect(tailwindMain([converted])).toEqual( - `
-
-
`, - ); - }); - - it("child is invisible", () => { - const frame = new AltFrameNode(); - frame.width = 100; - frame.height = 100; - frame.id = "frame"; - - const rect1 = new AltRectangleNode(); - rect1.id = "rect 1"; - rect1.width = 100; - rect1.height = 100; - rect1.x = 0; - rect1.y = 0; - rect1.fills = [ - { - type: "SOLID", - color: { - r: 0, - g: 0, - b: 0, - }, - }, - ]; - - const rect2 = new AltRectangleNode(); - rect2.id = "rect 2"; - rect2.width = 50; - rect2.height = 50; - rect2.x = 0; - rect2.y = 0; - rect2.fills = [ - { - type: "SOLID", - color: { - r: 1, - g: 1, - b: 1, - }, - }, - ]; - - rect1.visible = false; - - rect2.parent = frame; - rect1.parent = frame; - - frame.children = [rect1, rect2]; - - const invisibleConverted = convertNodesOnRectangle(frame); - - expect(tailwindMain([invisibleConverted])).toEqual( - `
-
-
-
-
`, - ); - }); - - it("frame with two children", () => { - const frame = new AltFrameNode(); - frame.id = "frame"; - frame.width = 20; - frame.height = 20; - - const rectangle = new AltRectangleNode(); - rectangle.id = "rectangle"; - rectangle.width = 20; - rectangle.height = 20; - rectangle.x = 0; - rectangle.y = 0; - rectangle.visible = true; - rectangle.fills = [ - { - type: "SOLID", - color: { - r: 0, - g: 0, - b: 0, - }, - }, - ]; - - const miniRect = new AltRectangleNode(); - miniRect.id = "miniRect"; - miniRect.width = 10; - miniRect.height = 10; - miniRect.x = 5; - miniRect.y = 5; - miniRect.visible = true; - miniRect.fills = [ - { - type: "SOLID", - color: { - r: 1, - g: 1, - b: 1, - }, - }, - ]; - miniRect.parent = frame; - rectangle.parent = frame; - frame.children = [rectangle, miniRect]; - - // it will only work with two or more items. - // todo should the conversion happen also when a group has a single rect? - const converted = convertNodesOnRectangle(frame); - - expect(tailwindMain([converted])).toEqual( - `
-
-
-
-
`, - ); - }); - - it("Fail", () => { - const rect1 = new AltRectangleNode(); - rect1.id = "rect 1"; - rect1.x = 0; - rect1.y = 0; - rect1.width = 100; - rect1.height = 100; - - const rect2 = new AltRectangleNode(); - rect2.id = "rect 2"; - rect2.x = 0; - rect2.y = 0; - rect2.width = 20; - rect2.height = 120; - - const group = new AltGroupNode(); - group.id = "group"; - group.x = 0; - group.y = 0; - group.width = 120; - group.height = 20; - group.children = [rect1, rect2]; - rect1.parent = group; - rect2.parent = group; - - expect(tailwindMain([convertNodesOnRectangle(group)])) - .toEqual(`
-
-
-
`); - }); - it("group with 2 children", () => { - const group = new AltGroupNode(); - group.id = "group"; - group.width = 20; - group.height = 20; - group.x = 0; - group.y = 0; - - const rectangle = new AltRectangleNode(); - rectangle.id = "rect 1"; - rectangle.width = 20; - rectangle.height = 20; - rectangle.x = 0; - rectangle.y = 0; - rectangle.visible = true; - rectangle.fills = [ - { - type: "SOLID", - color: { - r: 0, - g: 0, - b: 0, - }, - }, - ]; - - const miniRect = new AltRectangleNode(); - miniRect.id = "rect 2"; - miniRect.width = 8; - miniRect.height = 8; - miniRect.x = 0; - miniRect.y = 0; - miniRect.visible = true; - miniRect.fills = [ - { - type: "SOLID", - color: { - r: 1, - g: 1, - b: 1, - }, - }, - ]; - miniRect.parent = group; - rectangle.parent = group; - group.children = [rectangle, miniRect]; - - const pre_conv = convertNodesOnRectangle(group); - - // force Group removal. This is done automatically in AltConversion when executed in Figma. - const conv = pre_conv.children[0]; - conv.parent = null; - - // counterAxisSizingMode is AUTO, therefore bg-black doesn't contain the size - - expect(tailwindMain([conv])).toEqual( - `
-
-
`, - ); - }); - - it("simple example", () => { - const node = new AltFrameNode(); - node.id = "FRAME"; - node.width = 400; - node.height = 400; - - const child0 = new AltRectangleNode(); - child0.id = "MAIN"; - child0.width = 100; - child0.height = 100; - child0.x = 0; - child0.y = 0; - - const child1 = new AltRectangleNode(); - child1.id = "RECT 1"; - child1.width = 20; - child1.height = 20; - child1.x = 0; - child1.y = 0; - - const child2 = new AltRectangleNode(); - child2.id = "RECT 2"; - child2.width = 30; - child2.height = 30; - child2.x = 10; - child2.y = 10; - - const child3 = new AltRectangleNode(); - child3.id = "RECT 3"; - child3.width = 60; - child3.height = 60; - child3.x = 10; - child3.y = 10; - - // from most background to most foreground - node.children = [child0, child1, child2, child3]; - - const convert = convertNodesOnRectangle(node); - - expect(convert.children).toHaveLength(1); - }); - - it("multiple rectangles on top of each other", () => { - const node = new AltFrameNode(); - node.id = "FRAME"; - node.width = 400; - node.height = 400; - - const child0 = new AltRectangleNode(); - child0.id = "MAIN 1"; - child0.width = 30; - child0.height = 30; - child0.x = 0; - child0.y = 0; - - const child1 = new AltRectangleNode(); - child1.id = "RECT 1 M1"; - child1.width = 20; - child1.height = 20; - child1.x = 10; - child1.y = 10; - - const child2 = new AltRectangleNode(); - child2.id = "RECT 2 M1"; - child2.width = 10; - child2.height = 10; - child2.x = 20; - child2.y = 20; - - const child3 = new AltRectangleNode(); - child3.id = "MAIN 2"; - child3.width = 40; - child3.height = 40; - child3.x = 40; - child3.y = 40; - - const child4 = new AltRectangleNode(); - child4.id = "RECT 1 M2"; - child4.width = 10; - child4.height = 20; - child4.x = 50; - child4.y = 50; - - const child5 = new AltRectangleNode(); - child5.id = "RECT 2 M2"; - child5.width = 45; - child5.height = 20; - child5.x = 40; - child5.y = 30; - - const childIgnored = new AltTextNode(); - childIgnored.id = "RECT 2 M2"; - childIgnored.width = 100; - childIgnored.height = 100; - childIgnored.x = 0; - childIgnored.y = 0; - - // from most background to most foreground - node.children = [ - child0, - child3, - childIgnored, - child1, - child2, - child4, - child5, - ]; - - const convert = convertNodesOnRectangle(node); - - // 4, because it should include even those that are not converted. - expect(convert.children).toHaveLength(4); - }); - - it("invalid when testing without id", () => { - const node = new AltFrameNode(); - node.width = 400; - node.height = 400; - - node.children = [new AltRectangleNode(), new AltRectangleNode()]; - - expect(() => convertNodesOnRectangle(node)).toThrow(); - }); -}); diff --git a/__tests__/altNodes/convertToAutoLayout.test.ts b/__tests__/altNodes/convertToAutoLayout.test.ts deleted file mode 100644 index 10cbce4f..00000000 --- a/__tests__/altNodes/convertToAutoLayout.test.ts +++ /dev/null @@ -1,166 +0,0 @@ -import { AltFrameNode, AltRectangleNode } from "../../src/altNodes/altMixins"; -import { tailwindMain } from "../../src/tailwind/tailwindMain"; -import { convertToAutoLayout } from "../../src/altNodes/convertToAutoLayout"; - -describe("Convert to AutoLayout", () => { - // @ts-expect-error for some reason, need to override this for figma.mixed to work - global.figma = { - mixed: undefined, - }; - - it("Simple conversion", () => { - const frame = new AltFrameNode(); - frame.x = 0; - frame.y = 0; - frame.width = 50; - frame.height = 50; - frame.layoutMode = "NONE"; - - const node1 = new AltRectangleNode(); - node1.x = 0; - node1.y = 0; - node1.width = 20; - node1.height = 20; - node1.parent = frame; - node1.fills = [ - { - type: "SOLID", - color: { - r: 1.0, - g: 1.0, - b: 1.0, - }, - }, - ]; - - const node2 = new AltRectangleNode(); - node2.x = 20; - node2.y = 0; - node2.width = 20; - node2.height = 20; - node2.parent = frame; - node2.fills = [ - { - type: "SOLID", - color: { - r: 0.0, - g: 0.0, - b: 0.0, - }, - }, - ]; - - // initially they are not ordered. ConvertToAutoLayout will also order them. - frame.children = [node2, node1]; - - // convertToAutoLayout is going to add padding to the parent, which justifies the h-full. - - // output should be HORIZONTAL - expect(tailwindMain([convertToAutoLayout(frame)])).toEqual( - `
-
-
-
`, - ); - - // output should be VERTICAL - node2.x = 0; - node2.y = 20; - frame.layoutMode = "NONE"; - frame.children = [node2, node1]; - - expect(tailwindMain([convertToAutoLayout(frame)])).toEqual( - `
-
-
-
`, - ); - - // horizontally align while vertical - node1.width = 50; - - node2.x = 25; - node2.y = 25; - frame.layoutMode = "NONE"; - frame.children = [node2, node1]; - - expect(tailwindMain([convertToAutoLayout(frame)])).toEqual( - `
-
-
-
`, - ); - - // vertically align while horizontal - node1.width = 20; - node1.height = 50; - - node2.x = 20; - node2.y = 20; - frame.layoutMode = "NONE"; - frame.children = [node2, node1]; - - expect(tailwindMain([convertToAutoLayout(frame)])).toEqual( - `
-
-
-
`, - ); - - node1.height = 20; - - // output should be NOTHING - node2.x = 10; - node2.y = 10; - frame.layoutMode = "NONE"; - frame.children = [node2, node1]; - - expect(tailwindMain([convertToAutoLayout(frame)])).toEqual( - `
-
-
-
`, - ); - }); - - it("Trigger avgX", () => { - const frame = new AltFrameNode(); - frame.x = 0; - frame.y = 0; - frame.width = 50; - frame.height = 50; - frame.layoutMode = "NONE"; - - const node1 = new AltRectangleNode(); - node1.x = 0; - node1.y = 0; - node1.width = 20; - node1.height = 20; - node1.parent = frame; - - const node2 = new AltRectangleNode(); - node2.x = 16; - node2.y = 0; - node2.width = 20; - node2.height = 20; - node2.parent = frame; - - const node3 = new AltRectangleNode(); - node3.x = 40; - node3.y = 0; - node3.width = 20; - node3.height = 20; - node3.parent = frame; - - // initially they are not ordered. ConvertToAutoLayout will also order them. - frame.children = [node3, node2, node1]; - - // output should be HORIZONTAL - expect(tailwindMain([convertToAutoLayout(frame)])) - .toEqual(`
-
-
-
-
`); - }); -}); diff --git a/__tests__/flutter/builderImpl/flutterBlend.test.ts b/__tests__/flutter/builderImpl/flutterBlend.test.ts deleted file mode 100644 index 5b73b85c..00000000 --- a/__tests__/flutter/builderImpl/flutterBlend.test.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { - flutterOpacity, - flutterRotation, - flutterVisibility, -} from "../../../src/flutter/builderImpl/flutterBlend"; -import { AltRectangleNode } from "../../../src/altNodes/altMixins"; - -describe("Flutter Blend", () => { - const node = new AltRectangleNode(); - - it("opacity", () => { - // undefined (unitialized, only happen on tests) - expect(flutterOpacity(node, "")).toEqual(""); - - node.opacity = 0.5; - expect(flutterOpacity(node, "test")).toEqual( - `Opacity( - opacity: 0.50, - child: test -),`, - ); - - node.opacity = 1.0; - expect(flutterOpacity(node, "")).toEqual(""); - }); - - it("visibility", () => { - // undefined (unitialized, only happen on tests) - expect(flutterVisibility(node, "")).toEqual(""); - - node.visible = false; - expect(flutterVisibility(node, "test")).toEqual( - `Visibility( - visible: false, - child: test -),`, - ); - - node.visible = true; - expect(flutterVisibility(node, "test")).toEqual("test"); - }); - - it("rotation", () => { - // undefined (unitialized, only happen on tests) - expect(flutterRotation(node, "")).toEqual(""); - - // test small negative value to check if output will be nothing - node.rotation = -7.0167096047110005e-15; - expect(flutterRotation(node, "")).toEqual(""); - - node.rotation = 45; - expect(flutterRotation(node, "test")).toEqual( - `Transform.rotate( - angle: -0.79, - child: test -),`, - ); - - node.rotation = -45; - expect(flutterRotation(node, "test")).toEqual( - `Transform.rotate( - angle: 0.79, - child: test -),`, - ); - - node.rotation = 90; - expect(flutterRotation(node, "test")).toEqual( - `Transform.rotate( - angle: -1.57, - child: test -),`, - ); - }); -}); diff --git a/__tests__/flutter/builderImpl/flutterBorder.test.ts b/__tests__/flutter/builderImpl/flutterBorder.test.ts deleted file mode 100644 index 7d08b53b..00000000 --- a/__tests__/flutter/builderImpl/flutterBorder.test.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { - AltRectangleNode, - AltEllipseNode, - AltGroupNode, -} from "../../../src/altNodes/altMixins"; -import { - flutterBorderRadius, - flutterBorder, - flutterShape, -} from "../../../src/flutter/builderImpl/flutterBorder"; - -describe("Flutter Border", () => { - // @ts-expect-error for some reason, need to override this for figma.mixed to work - global.figma = { - mixed: undefined, - }; - - it("flutterBorderRadius", () => { - const node = new AltRectangleNode(); - expect(flutterBorderRadius(node)).toEqual(""); - - node.cornerRadius = 2; - expect(flutterBorderRadius(node)).toEqual( - "\nborderRadius: BorderRadius.circular(2),", - ); - - node.cornerRadius = figma.mixed; - node.topLeftRadius = 2; - node.topRightRadius = 0; - node.bottomLeftRadius = 0; - node.bottomRightRadius = 0; - expect(flutterBorderRadius(node)).toEqual( - "\nborderRadius: BorderRadius.only(topLeft: Radius.circular(2), topRight: Radius.circular(0), bottomLeft: Radius.circular(0), bottomRight: Radius.circular(0), ),", - ); - - const ellipseNode = new AltEllipseNode(); - expect(flutterBorderRadius(ellipseNode)).toEqual(""); - }); - - it("flutterBorder", () => { - const node = new AltRectangleNode(); - node.strokeWeight = 2; - node.strokes = [ - { - type: "SOLID", - color: { r: 0, g: 0, b: 0 }, - }, - ]; - expect(flutterBorder(node)).toEqual( - "\nborder: Border.all(color: Colors.black, width: 2, ),", - ); - - node.strokeWeight = 0; - expect(flutterBorder(node)).toEqual(""); - - expect(flutterBorder(new AltGroupNode())).toEqual(""); - }); - - it("flutterShape", () => { - const node = new AltRectangleNode(); - - node.cornerRadius = figma.mixed; - node.topLeftRadius = 4; - node.topRightRadius = 0; - node.bottomLeftRadius = 0; - node.bottomRightRadius = 0; - expect(flutterShape(node)).toEqual( - `\nshape: RoundedRectangleBorder( - borderRadius: BorderRadius.only(topLeft: Radius.circular(4), topRight: Radius.circular(0), bottomLeft: Radius.circular(0), bottomRight: Radius.circular(0), ), -),`, - ); - - const ellipseNode = new AltEllipseNode(); - ellipseNode.strokeWeight = 4; - ellipseNode.strokes = [ - { - type: "SOLID", - color: { r: 0.25, g: 0.25, b: 0.25 }, - }, - ]; - expect(flutterShape(ellipseNode)).toEqual( - `\nshape: CircleBorder( - side: BorderSide(width: 4, color: Color(0xff3f3f3f), ), -),`, - ); - }); -}); diff --git a/__tests__/flutter/builderImpl/flutterColor.test.ts b/__tests__/flutter/builderImpl/flutterColor.test.ts deleted file mode 100644 index 886e1f60..00000000 --- a/__tests__/flutter/builderImpl/flutterColor.test.ts +++ /dev/null @@ -1,283 +0,0 @@ -import { - flutterColorFromFills, - flutterBoxDecorationColor, -} from "../../../src/flutter/builderImpl/flutterColor"; -import { AltRectangleNode, AltTextNode } from "../../../src/altNodes/altMixins"; -import { flutterMain } from "./../../../src/flutter/flutterMain"; -describe("Flutter Color", () => { - // @ts-expect-error for some reason, need to override this for figma.mixed to work - global.figma = { - mixed: undefined, - }; - - it("standard color", () => { - const node = new AltRectangleNode(); - node.fills = [ - { - type: "SOLID", - color: { - r: 0.941, - g: 0.318, - b: 0.22, - }, - opacity: 1.0, - }, - ]; - - expect(flutterBoxDecorationColor(node.fills)).toEqual( - "\ncolor: Color(0xffef5138),", - ); - }); - - it("check for black and white on Text", () => { - const node = new AltTextNode(); - node.characters = ""; - node.fills = [ - { - type: "SOLID", - color: { - r: 0.0, - g: 0.0, - b: 0.0, - }, - opacity: 1.0, - }, - ]; - - expect(flutterColorFromFills(node.fills)).toEqual("color: Colors.black,"); - - node.fills = [ - { - type: "SOLID", - color: { - r: 1.0, - g: 1.0, - b: 1.0, - }, - opacity: 1.0, - }, - ]; - - expect(flutterColorFromFills(node.fills)).toEqual("color: Colors.white,"); - }); - - it("opacity and visibility changes", () => { - const node = new AltRectangleNode(); - node.fills = [ - { - type: "SOLID", - color: { - r: 0.0, - g: 0.0, - b: 0.0, - }, - opacity: 1.0, - visible: false, - }, - ]; - - expect(flutterColorFromFills(node.fills)).toEqual(""); - - node.fills = [ - { - type: "SOLID", - color: { - r: 0.0, - g: 0.0, - b: 0.0, - }, - opacity: undefined, - visible: true, - }, - ]; - - // this scenario should never happen in real life; figma allows undefined to be set, but not to be get. - expect(flutterColorFromFills(node.fills)).toEqual("color: Colors.black,"); - - node.fills = [ - { - type: "SOLID", - color: { - r: 0.0, - g: 0.0, - b: 0.0, - }, - opacity: 0.0, - visible: true, - }, - ]; - expect(flutterBoxDecorationColor(node.fills)).toEqual( - "\ncolor: Color(0x00000000),", - ); - }); - - it("Gradient Linear", () => { - const node = new AltRectangleNode(); - const gradientFill: GradientPaint = { - type: "GRADIENT_LINEAR", - gradientTransform: [ - [0, 0, 0], - [0, 0, 0], - ], - gradientStops: [ - { - position: 0, - color: { - r: 0, - g: 0, - b: 0, - a: 1, - }, - }, - ], - }; - - node.fills = [gradientFill]; - - expect(flutterBoxDecorationColor(node.fills)).toEqual( - "\ngradient: LinearGradient(begin: Alignment.centerLeft, end: Alignment.centerRight, colors: [Colors.black], ),", - ); - - // topLeft to bottomRight (135) - Object.assign(gradientFill.gradientTransform, [ - [0.8038461208343506, 0.7035384774208069, -0.2932307720184326], - [1.3402682542800903, -1.4652644395828247, 0.5407097935676575], - ]); - expect(flutterBoxDecorationColor(node.fills)).toEqual( - "\ngradient: LinearGradient(begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [Colors.black], ),", - ); - - // bottom to top (-90) - Object.assign(gradientFill.gradientTransform, [ - [7.734507789791678e-8, -1.2339448928833008, 1.1376146078109741], - [-2.3507132530212402, -1.0997783306265774e-7, 1.6796307563781738], - ]); - expect(flutterBoxDecorationColor(node.fills)).toEqual( - "\ngradient: LinearGradient(begin: Alignment.bottomCenter, end: Alignment.topCenter, colors: [Colors.black], ),", - ); - - // top to bottom (90) - Object.assign(gradientFill.gradientTransform, [ - [6.851496436866e-8, 2.085271120071411, -0.6976743936538696], - [3.9725232124328613, -1.4210854715202004e-14, -0.8289895057678223], - ]); - expect(flutterBoxDecorationColor(node.fills)).toEqual( - "\ngradient: LinearGradient(begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Colors.black], ),", - ); - - // left to right (0) - Object.assign(gradientFill.gradientTransform, [ - [1.845637559890747, 1.9779233184635814e-7, -0.45637592673301697], - [6.030897026221282e-8, -3.364259719848633, 2.188383102416992], - ]); - expect(flutterBoxDecorationColor(node.fills)).toEqual( - "\ngradient: LinearGradient(begin: Alignment.centerLeft, end: Alignment.centerRight, colors: [Colors.black], ),", - ); - - // right to left (180) - Object.assign(gradientFill.gradientTransform, [ - [-2.3905811309814453, 0.04066795855760574, 1.707460880279541], - [0.07747448235750198, 4.357592582702637, -1.0299113988876343], - ]); - expect(flutterBoxDecorationColor(node.fills)).toEqual( - "\ngradient: LinearGradient(begin: Alignment.centerRight, end: Alignment.centerLeft, colors: [Colors.black], ),", - ); - - // bottom left to top right (-135) - Object.assign(gradientFill.gradientTransform, [ - [-1.2678464651107788, -1.9602917432785034, 1.6415824890136719], - [-3.7344324588775635, 2.3110527992248535, 0.4661891460418701], - ]); - expect(flutterBoxDecorationColor(node.fills)).toEqual( - "\ngradient: LinearGradient(begin: Alignment.bottomRight, end: Alignment.topLeft, colors: [Colors.black], ),", - ); - - // bottom left to top right (-45) - Object.assign(gradientFill.gradientTransform, [ - [0.7420053482055664, -0.6850813031196594, 0.4412658214569092], - [-1.3051068782806396, -1.3525396585464478, 1.8345310688018799], - ]); - expect(flutterBoxDecorationColor(node.fills)).toEqual( - "\ngradient: LinearGradient(begin: Alignment.bottomLeft, end: Alignment.topRight, colors: [Colors.black], ),", - ); - - // top right to bottom left (-45) - Object.assign(gradientFill.gradientTransform, [ - [-0.7061997652053833, 0.7888921499252319, 0.5180976986885071], - [1.5028705596923828, 1.2872726917266846, -1.0877336263656616], - ]); - expect(flutterBoxDecorationColor(node.fills)).toEqual( - "\ngradient: LinearGradient(begin: Alignment.topRight, end: Alignment.bottomLeft, colors: [Colors.black], ),", - ); - }); - - it("Execute Main with Linear Gradient, corners and stroke", () => { - const node = new AltRectangleNode(); - const gradientFill: GradientPaint = { - type: "GRADIENT_LINEAR", - gradientTransform: [ - [0.8038461208343506, 0.7035384774208069, -0.2932307720184326], - [1.3402682542800903, -1.4652644395828247, 0.5407097935676575], - ], - gradientStops: [ - { - position: 0, - color: { - r: 0, - g: 0, - b: 0, - a: 1, - }, - }, - { - position: 1, - color: { - r: 1, - g: 0, - b: 0, - a: 1, - }, - }, - ], - }; - - // width is going be 18 because 10 + 4 + 4 of stroke. - node.height = 10; - node.width = 10; - node.fills = [gradientFill]; - node.strokeWeight = 4; - node.strokeAlign = "OUTSIDE"; - node.strokes = [ - { - type: "SOLID", - color: { r: 0.25, g: 0.25, b: 0.25 }, - }, - ]; - node.cornerRadius = 16; - - expect(flutterMain([node])).toEqual( - `Container( - width: 18, - height: 18, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(16), - border: Border.all(color: Color(0xff3f3f3f), width: 4, ), - gradient: LinearGradient(begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [Colors.black, Color(0xffff0000)], ), - ), -)`, - ); - }); - - it("fail with other types", () => { - const node = new AltRectangleNode(); - node.fills = [ - { - type: "IMAGE", - scaleMode: "FILL", - imageHash: null, - }, - ]; - - expect(flutterColorFromFills(node.fills)).toEqual(""); - }); -}); diff --git a/__tests__/flutter/builderImpl/flutterPadding.test.ts b/__tests__/flutter/builderImpl/flutterPadding.test.ts deleted file mode 100644 index 518f5515..00000000 --- a/__tests__/flutter/builderImpl/flutterPadding.test.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { - AltRectangleNode, - AltFrameNode, -} from "../../../src/altNodes/altMixins"; -import { flutterPadding } from "../../../src/flutter/builderImpl/flutterPadding"; - -describe("Flutter Padding", () => { - it("test padding", () => { - const frameNode = new AltFrameNode(); - expect(flutterPadding(frameNode)).toEqual(""); - - frameNode.layoutMode = "NONE"; - expect(flutterPadding(frameNode)).toEqual(""); - - frameNode.layoutMode = "VERTICAL"; - - frameNode.paddingLeft = 2; - frameNode.paddingRight = 2; - frameNode.paddingTop = 2; - frameNode.paddingBottom = 2; - expect(flutterPadding(frameNode)).toEqual( - "\npadding: const EdgeInsets.all(2),", - ); - - frameNode.paddingLeft = 1; - frameNode.paddingRight = 2; - frameNode.paddingTop = 3; - frameNode.paddingBottom = 4; - expect(flutterPadding(frameNode)).toEqual( - "\npadding: const EdgeInsets.only(left: 1, right: 2, top: 3, bottom: 4, ),", - ); - - frameNode.paddingLeft = 2; - frameNode.paddingRight = 2; - frameNode.paddingTop = 4; - frameNode.paddingBottom = 4; - expect(flutterPadding(frameNode)).toEqual( - "\npadding: const EdgeInsets.symmetric(horizontal: 2, vertical: 4, ),", - ); - - frameNode.paddingLeft = 2; - frameNode.paddingRight = 2; - frameNode.paddingTop = 0; - frameNode.paddingBottom = 0; - expect(flutterPadding(frameNode)).toEqual( - "\npadding: const EdgeInsets.symmetric(horizontal: 2, ),", - ); - - frameNode.paddingLeft = 0; - frameNode.paddingRight = 0; - frameNode.paddingTop = 2; - frameNode.paddingBottom = 2; - expect(flutterPadding(frameNode)).toEqual( - "\npadding: const EdgeInsets.symmetric(vertical: 2, ),", - ); - - frameNode.paddingLeft = 0; - frameNode.paddingRight = 0; - frameNode.paddingTop = 0; - frameNode.paddingBottom = 0; - expect(flutterPadding(frameNode)).toEqual(""); - - const notFrame = new AltRectangleNode(); - expect(flutterPadding(notFrame)).toEqual(""); - }); -}); diff --git a/__tests__/flutter/builderImpl/flutterPosition.test.ts b/__tests__/flutter/builderImpl/flutterPosition.test.ts deleted file mode 100644 index b33b78c5..00000000 --- a/__tests__/flutter/builderImpl/flutterPosition.test.ts +++ /dev/null @@ -1,244 +0,0 @@ -import { AltFrameNode } from "../../../src/altNodes/altMixins"; -import { flutterPosition } from "../../../src/flutter/builderImpl/flutterPosition"; - -describe("Flutter Position", () => { - // @ts-expect-error for some reason, need to override this for figma.mixed to work - global.figma = { - mixed: undefined, - }; - - it("Frame AutoLayout Position", () => { - const parent = new AltFrameNode(); - parent.width = 100; - parent.height = 100; - parent.x = 0; - parent.y = 0; - parent.layoutMode = "NONE"; - - const node = new AltFrameNode(); - node.width = 100; - node.height = 100; - node.parent = parent; - - parent.children = [node]; - - // node.parent.id === parent.id, so return "" - expect(flutterPosition(node, "", parent.id)).toEqual(""); - - // todo improve this? - - node.layoutAlign = "MIN"; - expect(flutterPosition(node, "")).toEqual(""); - - node.layoutAlign = "MAX"; - expect(flutterPosition(node, "")).toEqual(""); - - node.layoutAlign = "CENTER"; - expect(flutterPosition(node, "")).toEqual(""); - }); - - it("Frame Absolute Position", () => { - const parent = new AltFrameNode(); - parent.width = 100; - parent.height = 100; - parent.x = 0; - parent.y = 0; - parent.id = "root"; - parent.layoutMode = "NONE"; - parent.isRelative = true; - - const node = new AltFrameNode(); - parent.id = "node"; - node.parent = parent; - - // child equals parent - node.width = 100; - node.height = 100; - expect(flutterPosition(node, "child")).toEqual("child"); - - node.width = 25; - node.height = 25; - - const nodeF2 = new AltFrameNode(); - nodeF2.width = 25; - nodeF2.height = 25; - nodeF2.parent = parent; - - parent.children = [node, nodeF2]; - - // center - node.x = 37; - node.y = 37; - expect(flutterPosition(node, "child")).toEqual( - `Positioned.fill( - child: Align( - alignment: Alignment.center, - child: child - ), -),`, - ); - - // top-left - node.x = 0; - node.y = 0; - expect(flutterPosition(node, "child")).toEqual( - `Positioned.fill( - child: Align( - alignment: Alignment.topLeft, - child: child - ), -),`, - ); - - // top-right - node.x = 75; - node.y = 0; - expect(flutterPosition(node, "child")).toEqual( - `Positioned.fill( - child: Align( - alignment: Alignment.topRight, - child: child - ), -),`, - ); - - // bottom-left - node.x = 0; - node.y = 75; - expect(flutterPosition(node, "child")).toEqual( - `Positioned.fill( - child: Align( - alignment: Alignment.bottomLeft, - child: child - ), -),`, - ); - - // bottom-right - node.x = 75; - node.y = 75; - expect(flutterPosition(node, "child")).toEqual( - `Positioned.fill( - child: Align( - alignment: Alignment.bottomRight, - child: child - ), -),`, - ); - - // top-center - node.x = 37; - node.y = 0; - expect(flutterPosition(node, "child")).toEqual( - `Positioned.fill( - child: Align( - alignment: Alignment.topCenter, - child: child - ), -),`, - ); - - // left-center - node.x = 0; - node.y = 37; - expect(flutterPosition(node, "child")).toEqual( - `Positioned.fill( - child: Align( - alignment: Alignment.centerLeft, - child: child - ), -),`, - ); - - // bottom-center - node.x = 37; - node.y = 75; - expect(flutterPosition(node, "child")).toEqual( - `Positioned.fill( - child: Align( - alignment: Alignment.bottomCenter, - child: child - ), -),`, - ); - - // right-center - node.x = 75; - node.y = 37; - expect(flutterPosition(node, "child")).toEqual( - `Positioned.fill( - child: Align( - alignment: Alignment.centerRight, - child: child - ), -),`, - ); - - // center Y, random X - node.x = 22; - node.y = 37; - expect(flutterPosition(node, "child")).toEqual( - `Positioned( - left: 22, - top: 37, - child: child -),`, - ); - - // center X, random Y - node.x = 37; - node.y = 22; - expect(flutterPosition(node, "child")).toEqual( - `Positioned( - left: 37, - top: 22, - child: child -),`, - ); - - // without position - node.x = 45; - node.y = 88; - expect(flutterPosition(node, "child")).toEqual( - `Positioned( - left: 45, - top: 88, - child: child -),`, - ); - }); - - it("Position: node has same size as parent", () => { - const parent = new AltFrameNode(); - parent.width = 100; - parent.height = 100; - parent.layoutMode = "NONE"; - - const node = new AltFrameNode(); - node.width = 100; - node.height = 100; - node.parent = parent; - - const nodeF2 = new AltFrameNode(); - nodeF2.width = 100; - nodeF2.height = 100; - nodeF2.parent = parent; - - parent.children = [node, nodeF2]; - - expect(flutterPosition(node, "")).toEqual(""); - }); - - it("No position when parent is root", () => { - const node = new AltFrameNode(); - node.layoutMode = "NONE"; - - const parent = new AltFrameNode(); - parent.id = "root"; - parent.layoutMode = "NONE"; - - node.parent = parent; - - expect(flutterPosition(node, "", parent.id)).toEqual(""); - }); -}); diff --git a/__tests__/flutter/builderImpl/flutterShadow.test.ts b/__tests__/flutter/builderImpl/flutterShadow.test.ts deleted file mode 100644 index 30be4408..00000000 --- a/__tests__/flutter/builderImpl/flutterShadow.test.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { - flutterBoxShadow, - flutterElevationAndShadowColor, -} from "../../../src/flutter/builderImpl/flutterShadow"; -import { AltFrameNode } from "../../../src/altNodes/altMixins"; - -describe("Flutter Shadow", () => { - it("drop shadow", () => { - const node = new AltFrameNode(); - - node.effects = []; - expect(flutterBoxShadow(node)).toEqual(""); - - node.effects = [ - { - type: "DROP_SHADOW", - blendMode: "NORMAL", - color: { r: 0, g: 0, b: 0, a: 0.25 }, - offset: { x: 0, y: 4 }, - radius: 4, - visible: true, - }, - { - type: "DROP_SHADOW", - blendMode: "NORMAL", - color: { r: 1, g: 1, b: 0, a: 0.25 }, - offset: { x: 4, y: 4 }, - radius: 8, - visible: true, - }, - ]; - - expect(flutterBoxShadow(node)).toEqual( - `\nboxShadow: [ - BoxShadow( - color: Color(0x3f000000), - blurRadius: 4, - offset: Offset(0, 4), - ), - BoxShadow( - color: Color(0x3fffff00), - blurRadius: 8, - offset: Offset(4, 4), - ), -],`, - ); - - const [elev, color] = flutterElevationAndShadowColor(node); - expect(elev).toEqual("\nelevation: 4, "); - expect(color).toEqual("\ncolor: Color(0x3f000000), "); - }); - - it("inner shadow", () => { - const node = new AltFrameNode(); - - node.effects = []; - const [elev1, color1] = flutterElevationAndShadowColor(node); - expect(elev1).toEqual(""); - expect(color1).toEqual(""); - - node.effects = [ - { - type: "INNER_SHADOW", - blendMode: "NORMAL", - color: { r: 0, g: 0, b: 0, a: 0.25 }, - offset: { x: 0, y: 4 }, - radius: 4, - visible: true, - }, - ]; - - expect(flutterBoxShadow(node)).toEqual(""); - - const [elev2, color2] = flutterElevationAndShadowColor(node); - expect(elev2).toEqual(""); - expect(color2).toEqual(""); - }); -}); diff --git a/__tests__/flutter/builderImpl/flutterSize.test.ts b/__tests__/flutter/builderImpl/flutterSize.test.ts deleted file mode 100644 index b13379d7..00000000 --- a/__tests__/flutter/builderImpl/flutterSize.test.ts +++ /dev/null @@ -1,192 +0,0 @@ -import { - flutterSize, - flutterSizeWH, -} from "../../../src/flutter/builderImpl/flutterSize"; -import { - AltRectangleNode, - AltFrameNode, -} from "../../../src/altNodes/altMixins"; - -describe("Flutter Size", () => { - // @ts-expect-error for some reason, need to override this for figma.mixed to work - global.figma = { - mixed: undefined, - }; - - it("size for a rectangle", () => { - const node = new AltRectangleNode(); - - node.width = 16; - node.height = 16; - expect(flutterSizeWH(node)).toEqual("\nwidth: 16,\nheight: 16,"); - - node.width = 100; - node.height = 200; - expect(flutterSizeWH(node)).toEqual("\nwidth: 100,\nheight: 200,"); - - node.width = 300; - node.height = 300; - expect(flutterSizeWH(node)).toEqual("\nwidth: 300,\nheight: 300,"); - }); - - it("STRETCH inside AutoLayout", () => { - const node = new AltFrameNode(); - node.layoutMode = "HORIZONTAL"; - node.layoutAlign = "INHERIT"; - node.primaryAxisSizingMode = "FIXED"; - node.counterAxisSizingMode = "FIXED"; - node.width = 10; - node.height = 10; - - const child = new AltRectangleNode(); - child.layoutAlign = "STRETCH"; - child.layoutGrow = 1; - child.width = 10; - child.height = 10; - - child.parent = node; - node.children = [child]; - - const fSize1 = flutterSize(child); - expect(fSize1.width).toEqual(""); - expect(fSize1.height).toEqual("\nheight: double.infinity,"); - expect(fSize1.isExpanded).toEqual(true); - - node.layoutMode = "VERTICAL"; - - const fSize2 = flutterSize(child); - expect(fSize2.width).toEqual("\nwidth: double.infinity,"); - expect(fSize2.height).toEqual(""); - expect(fSize2.isExpanded).toEqual(true); - }); - - it("Fixed size when children are absolute", () => { - const node = new AltFrameNode(); - node.layoutMode = "NONE"; - node.width = 48; - node.height = 48; - node.children = [new AltRectangleNode(), new AltRectangleNode()]; - - expect(flutterSizeWH(node)).toEqual("\nwidth: 48,\nheight: 48,"); - }); - - it("counterAxisSizingMode is FIXED", () => { - const node = new AltFrameNode(); - node.counterAxisSizingMode = "FIXED"; - node.width = 48; - node.height = 48; - node.children = [new AltRectangleNode(), new AltRectangleNode()]; - - node.layoutMode = "HORIZONTAL"; - expect(flutterSizeWH(node)).toEqual("\nheight: 48,"); - - node.layoutMode = "VERTICAL"; - expect(flutterSizeWH(node)).toEqual("\nwidth: 48,"); - - node.layoutMode = "NONE"; - expect(flutterSizeWH(node)).toEqual("\nwidth: 48,\nheight: 48,"); - }); - - it("counterAxisSizingMode is AUTO", () => { - const node = new AltFrameNode(); - node.layoutMode = "HORIZONTAL"; - node.counterAxisSizingMode = "AUTO"; - node.primaryAxisSizingMode = "AUTO"; - node.x = 0; - node.y = 0; - node.width = 48; - node.height = 48; - node.children = [new AltRectangleNode(), new AltRectangleNode()]; - - expect(flutterSizeWH(node)).toEqual(""); - - // responsive - const parentNode = new AltFrameNode(); - parentNode.counterAxisSizingMode = "FIXED"; - parentNode.primaryAxisSizingMode = "FIXED"; - parentNode.x = 0; - parentNode.y = 0; - parentNode.width = 48; - parentNode.height = 48; - parentNode.children = [node]; - node.parent = parentNode; - expect(flutterSizeWH(node)).toEqual(""); - expect(flutterSizeWH(parentNode)).toEqual("\nwidth: 48,\nheight: 48,"); - }); - - it("width changes when there are strokes", () => { - const node = new AltRectangleNode(); - node.x = 0; - node.y = 0; - node.width = 8; - node.height = 8; - - expect(flutterSizeWH(node)).toEqual("\nwidth: 8,\nheight: 8,"); - - node.strokeWeight = 4; - node.strokes = [ - { - type: "SOLID", - color: { r: 0.25, g: 0.25, b: 0.25 }, - }, - ]; - - node.strokeAlign = "CENTER"; - expect(flutterSizeWH(node)).toEqual("\nwidth: 12,\nheight: 12,"); - - node.strokeAlign = "OUTSIDE"; - expect(flutterSizeWH(node)).toEqual("\nwidth: 16,\nheight: 16,"); - }); - - it("adjust parent if children's size + stroke > parent size", () => { - const node = new AltRectangleNode(); - node.width = 8; - node.height = 8; - - node.strokeWeight = 4; - node.strokeAlign = "OUTSIDE"; - node.strokes = [ - { - type: "SOLID", - color: { r: 0.25, g: 0.25, b: 0.25 }, - }, - ]; - - const parentNode = new AltFrameNode(); - parentNode.width = 8; - parentNode.height = 8; - parentNode.children = [node]; - node.parent = parentNode; - - const fSize1 = flutterSize(parentNode); - - expect(fSize1.width).toEqual("\nwidth: 16,"); - expect(fSize1.height).toEqual("\nheight: 16,"); - expect(fSize1.isExpanded).toEqual(false); - - node.strokeAlign = "CENTER"; - const fSize2 = flutterSize(parentNode); - expect(fSize2.width).toEqual("\nwidth: 12,"); - expect(fSize2.height).toEqual("\nheight: 12,"); - expect(fSize2.isExpanded).toEqual(false); - }); - - it("full width when width is same to the parent", () => { - const parentNode = new AltFrameNode(); - parentNode.layoutMode = "NONE"; - parentNode.width = 12; - parentNode.height = 12; - parentNode.counterAxisSizingMode = "AUTO"; - parentNode.primaryAxisSizingMode = "AUTO"; - - const node = new AltFrameNode(); - node.width = 12; - node.height = 12; - node.parent = parentNode; - - parentNode.children = [node]; - - expect(flutterSizeWH(parentNode)).toEqual("\nwidth: 12,\nheight: 12,"); - expect(flutterSizeWH(node)).toEqual("\nwidth: 12,\nheight: 12,"); - }); -}); diff --git a/__tests__/flutter/flutterContainer.test.ts b/__tests__/flutter/flutterContainer.test.ts deleted file mode 100644 index 3be16552..00000000 --- a/__tests__/flutter/flutterContainer.test.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { - AltRectangleNode, - AltFrameNode, - AltEllipseNode, -} from "../../src/altNodes/altMixins"; -import { flutterContainer } from "../../src/flutter/flutterContainer"; - -describe("Flutter Container", () => { - it("no size", () => { - const node = new AltRectangleNode(); - - // undefined (unitialized, only happen on tests) - expect(flutterContainer(node, "")).toEqual(""); - - node.width = 0; - node.height = 10; - expect(flutterContainer(node, "")).toEqual(""); - - node.width = 10; - node.height = 0; - expect(flutterContainer(node, "")).toEqual(""); - }); - - it("padding only", () => { - const node = new AltRectangleNode(); - node.width = 10; - node.height = 10; - - const parent = new AltFrameNode(); - parent.layoutMode = "HORIZONTAL"; - parent.width = 30; - parent.height = 30; - parent.x = 0; - parent.y = 0; - parent.paddingLeft = 10; - parent.paddingRight = 10; - parent.paddingTop = 10; - parent.paddingBottom = 10; - - parent.children = [node]; - node.parent = parent; - - expect(flutterContainer(parent, "")).toEqual(`Padding( - padding: const EdgeInsets.all(10), -),`); - - node.layoutGrow = 1; - node.layoutAlign = "STRETCH"; - - parent.primaryAxisSizingMode = "FIXED"; - parent.counterAxisSizingMode = "FIXED"; - - expect(flutterContainer(node, "")).toEqual(`Expanded( - child: Container( - height: double.infinity, - ), -),`); - }); - - it("standard scenario", () => { - const node = new AltRectangleNode(); - node.width = 10; - node.height = 10; - - expect(flutterContainer(node, "")).toEqual(`Container( - width: 10, - height: 10, -),`); - - expect(flutterContainer(node, "child")).toEqual(`Container( - width: 10, - height: 10, - child: child -),`); - }); - - it("ellipse", () => { - const node = new AltEllipseNode(); - node.width = 10; - node.height = 10; - - expect(flutterContainer(node, "")).toEqual(`Container( - width: 10, - height: 10, - decoration: BoxDecoration( - shape: BoxShape.circle, - ), -),`); - }); -}); diff --git a/__tests__/flutter/flutterMain.test.ts b/__tests__/flutter/flutterMain.test.ts deleted file mode 100644 index 2eeeb2fb..00000000 --- a/__tests__/flutter/flutterMain.test.ts +++ /dev/null @@ -1,424 +0,0 @@ -import { flutterMain } from "../../src/flutter/flutterMain"; -import { convertToAutoLayout } from "../../src/altNodes/convertToAutoLayout"; -import { - AltRectangleNode, - AltFrameNode, - AltGroupNode, -} from "../../src/altNodes/altMixins"; - -describe("Flutter Main", () => { - // @ts-expect-error for some reason, need to override this for figma.mixed to work - global.figma = { - mixed: undefined, - }; - it("Standard flow", () => { - const node = new AltFrameNode(); - node.width = 32; - node.height = 32; - node.x = 0; - node.y = 0; - node.name = "FRAME"; - node.layoutMode = "NONE"; - node.counterAxisSizingMode = "FIXED"; - - const child1 = new AltRectangleNode(); - child1.width = 4; - child1.height = 4; - child1.x = 9; - child1.y = 9; - child1.name = "RECT1"; - child1.fills = [ - { - type: "SOLID", - color: { - r: 1, - g: 1, - b: 1, - }, - }, - ]; - - const child2 = new AltRectangleNode(); - child2.width = 4; - child2.height = 4; - child2.x = 9; - child2.y = 9; - child2.name = "RECT2"; - - // this works as a test for JSX, but should never happen in reality. In reality Frame would need to have 2 children and be relative. - node.children = [child1, child2]; - child1.parent = node; - child2.parent = node; - - expect(flutterMain([convertToAutoLayout(node)], "", false)) - .toEqual(`Container( - width: 32, - height: 32, - child: Stack( - children:[ - Positioned( - left: 9, - top: 9, - child: Container( - width: 4, - height: 4, - color: Colors.white, - ), - ), - Positioned( - left: 9, - top: 9, - child: Container( - width: 4, - height: 4, - ), - ), - ], - ), -)`); - }); - - it("children is larger than 384", () => { - const node = new AltFrameNode(); - node.width = 420; - node.height = 420; - node.name = "FRAME"; - node.layoutMode = "NONE"; - node.counterAxisSizingMode = "FIXED"; - - const child1 = new AltRectangleNode(); - child1.width = 385; - child1.height = 8; - child1.x = 9; - child1.y = 9; - child1.fills = [ - { - type: "SOLID", - color: { - r: 1, - g: 1, - b: 1, - }, - }, - ]; - - const child2 = new AltRectangleNode(); - child2.width = 8; - child2.height = 385; - child2.x = 9; - child2.y = 9; - - // this works as a test for JSX, but should never happen in reality. In reality Frame would need to have 2 children and be relative. - node.children = [child1, child2]; - child1.parent = node; - child2.parent = node; - - expect(flutterMain([convertToAutoLayout(node)])).toEqual(`Container( - width: 420, - height: 420, - child: Stack( - children:[ - Positioned( - left: 9, - top: 9, - child: Container( - width: 385, - height: 8, - color: Colors.white, - ), - ), - Positioned( - left: 9, - top: 9, - child: Container( - width: 8, - height: 385, - ), - ), - ], - ), -)`); - }); - - it("Group with relative position", () => { - // this also should neve happen in reality, because Group must have the same size as the children. - - const node = new AltGroupNode(); - node.width = 32; - node.height = 32; - node.x = 0; - node.y = 0; - node.name = "GROUP"; - node.isRelative = true; - - const child = new AltRectangleNode(); - child.width = 4; - child.height = 4; - child.x = 9; - child.y = 9; - child.name = "RECT"; - child.fills = [ - { - type: "SOLID", - color: { - r: 1, - g: 1, - b: 1, - }, - }, - ]; - - node.children = [child]; - child.parent = node; - expect(flutterMain([node])).toEqual(`Container( - width: 32, - height: 32, - child: Stack( - children:[Positioned( - left: 9, - top: 9, - child: Container( - width: 4, - height: 4, - color: Colors.white, - ), - ),], - ), -)`); - }); - - it("Row and Column with 2 children", () => { - // this also should neve happen in reality, because Group must have the same size as the children. - - const node = new AltFrameNode(); - node.width = 32; - node.height = 8; - node.x = 0; - node.y = 0; - node.layoutMode = "HORIZONTAL"; - node.counterAxisSizingMode = "AUTO"; - node.primaryAxisSizingMode = "AUTO"; - node.primaryAxisAlignItems = "MIN"; - node.counterAxisAlignItems = "MIN"; - node.itemSpacing = 8; - - const child1 = new AltRectangleNode(); - child1.width = 8; - child1.height = 8; - child1.x = 0; - child1.y = 0; - child1.layoutAlign = "INHERIT"; - child1.fills = [ - { - type: "SOLID", - color: { - r: 1, - g: 1, - b: 1, - }, - }, - ]; - - const child2 = new AltRectangleNode(); - child2.width = 8; - child2.height = 8; - child2.x = 16; - child2.y = 0; - child2.fills = [ - { - type: "SOLID", - color: { - r: 0, - g: 0, - b: 0, - }, - }, - ]; - - node.children = [child1, child2]; - child1.parent = node; - child2.parent = node; - - expect(flutterMain([node])).toEqual(`Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children:[ - Container( - width: 8, - height: 8, - color: Colors.white, - ), - SizedBox(width: 8), - Container( - width: 8, - height: 8, - color: Colors.black, - ), - ], -)`); - - // variations for test coverage - node.layoutMode = "VERTICAL"; - node.layoutGrow = 1; - node.primaryAxisAlignItems = "CENTER"; - node.counterAxisAlignItems = "CENTER"; - - expect(flutterMain([node])).toEqual(`Column( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children:[ - Container( - width: 8, - height: 8, - color: Colors.white, - ), - SizedBox(height: 8), - Container( - width: 8, - height: 8, - color: Colors.black, - ), - ], -)`); - - node.primaryAxisAlignItems = "MAX"; - node.counterAxisAlignItems = "MAX"; - - expect(flutterMain([node])).toEqual(`Column( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.end, - children:[ - Container( - width: 8, - height: 8, - color: Colors.white, - ), - SizedBox(height: 8), - Container( - width: 8, - height: 8, - color: Colors.black, - ), - ], -)`); - - node.primaryAxisAlignItems = "SPACE_BETWEEN"; - node.counterAxisAlignItems = "CENTER"; - - expect(flutterMain([node])).toEqual(`Column( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children:[ - Container( - width: 8, - height: 8, - color: Colors.white, - ), - SizedBox(height: 8), - Container( - width: 8, - height: 8, - color: Colors.black, - ), - ], -)`); - }); - - it("Row with 1 children", () => { - // this also should neve happen in reality, because Group must have the same size as the children. - - const node = new AltFrameNode(); - node.width = 32; - node.height = 8; - node.x = 0; - node.y = 0; - node.layoutMode = "HORIZONTAL"; - node.primaryAxisSizingMode = "FIXED"; - node.primaryAxisAlignItems = "CENTER"; - node.counterAxisSizingMode = "AUTO"; - node.counterAxisAlignItems = "CENTER"; - node.itemSpacing = 8; - node.paddingBottom = 0; - node.paddingTop = 0; - node.paddingLeft = 0; - node.paddingRight = 0; - node.visible = true; - node.layoutAlign = "INHERIT"; - node.fills = [ - { - type: "SOLID", - color: { - r: 1, - g: 1, - b: 1, - }, - }, - ]; - - const child1 = new AltRectangleNode(); - child1.width = 8; - child1.height = 8; - child1.x = 0; - child1.y = 0; - child1.layoutGrow = 1; - child1.visible = true; - child1.layoutAlign = "STRETCH"; - child1.fills = [ - { - type: "SOLID", - color: { - r: 1, - g: 1, - b: 1, - }, - }, - ]; - child1.parent = node; - - const child2 = new AltRectangleNode(); - child2.width = 8; - child2.height = 8; - child2.x = 12; - child2.y = 12; - child2.layoutGrow = 0; - child2.visible = true; - child2.layoutAlign = "INHERIT"; - child2.fills = []; - child2.parent = node; - - node.children = [child1, child2]; - - expect(flutterMain([node], "", true)).toEqual( - `SizedBox( - width: 32, - child: Material( - color: Colors.white, - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children:[ - Expanded( - child: SizedBox( - height: double.infinity, - child: Material( - color: Colors.white, - ), - ), - ), - SizedBox(width: 8), - Container( - width: 8, - height: 8, - ), - ], - ), - ), -)`, - ); - }); -}); diff --git a/__tests__/flutter/flutterText.test.ts b/__tests__/flutter/flutterText.test.ts deleted file mode 100644 index fb02c52f..00000000 --- a/__tests__/flutter/flutterText.test.ts +++ /dev/null @@ -1,327 +0,0 @@ -import { AltFrameNode, AltTextNode } from "../../src/altNodes/altMixins"; -import { FlutterTextBuilder } from "./../../src/flutter/flutterTextBuilder"; -import { flutterMain } from "./../../src/flutter/flutterMain"; - -describe("Flutter Text", () => { - // @ts-expect-error for some reason, need to override this for figma.mixed to work - global.figma = { - mixed: undefined, - }; - - it("inside AutoLayout", () => { - const node = new AltFrameNode(); - node.width = 32; - node.height = 8; - node.x = 0; - node.y = 0; - node.layoutMode = "HORIZONTAL"; - node.counterAxisSizingMode = "FIXED"; - node.primaryAxisSizingMode = "FIXED"; - node.primaryAxisAlignItems = "MIN"; - node.counterAxisAlignItems = "MIN"; - node.itemSpacing = 8; - - const textNode = new AltTextNode(); - textNode.characters = ""; - textNode.width = 16; - textNode.height = 16; - textNode.layoutAlign = "STRETCH"; - textNode.layoutGrow = 1; - - node.children = [textNode]; - textNode.parent = node; - - textNode.textAutoResize = "NONE"; - expect(flutterMain([node])).toEqual( - `Container( - width: 32, - height: 8, - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children:[ - Expanded( - child: SizedBox( - height: double.infinity, - child: Text( - "", - ), - ), - ), - ], - ), -)`, - ); - }); - it("textAutoResize", () => { - const node = new AltTextNode(); - node.characters = ""; - node.width = 16; - node.height = 16; - - node.textAutoResize = "NONE"; - expect(flutterMain([node])).toEqual( - `SizedBox( - width: 16, - height: 16, - child: Text( - "", - ), -)`, - ); - - node.textAutoResize = "HEIGHT"; - expect(flutterMain([node])).toEqual( - `SizedBox( - width: 16, - child: Text( - "", - ), -)`, - ); - - node.textAutoResize = "WIDTH_AND_HEIGHT"; - expect(flutterMain([node])).toEqual(`Text( - "", -)`); - }); - - it("textAlignHorizontal", () => { - const node = new AltTextNode(); - node.characters = ""; - node.width = 16; - node.height = 16; - - node.textAutoResize = "WIDTH_AND_HEIGHT"; - node.textAlignHorizontal = "LEFT"; - expect(flutterMain([node])).toEqual(`Text( - "", -)`); - - node.textAutoResize = "NONE"; - node.textAlignHorizontal = "CENTER"; - expect(flutterMain([node])).toEqual( - `SizedBox( - width: 16, - height: 16, - child: Text( - "", - textAlign: TextAlign.center, - ), -)`, - ); - - node.textAlignHorizontal = "JUSTIFIED"; - expect(flutterMain([node])).toEqual( - `SizedBox( - width: 16, - height: 16, - child: Text( - "", - textAlign: TextAlign.justify, - ), -)`, - ); - }); - it("fontSize", () => { - const node = new AltTextNode(); - node.characters = ""; - node.width = 16; - node.height = 16; - node.fontSize = 16; - node.textAutoResize = "WIDTH_AND_HEIGHT"; - - expect(flutterMain([node])).toEqual( - `Text( - "", - style: TextStyle( - fontSize: 16, - ), -)`, - ); - }); - - it("fontName", () => { - const node = new AltTextNode(); - node.characters = ""; - node.width = 16; - node.height = 16; - node.textAutoResize = "WIDTH_AND_HEIGHT"; - - node.fontName = { - family: "inter", - style: "bold", - }; - expect(flutterMain([node])).toEqual(`Text( - "", - style: TextStyle( - fontFamily: "inter", - fontWeight: FontWeight.w700, - ), -)`); - - node.fontName = { - family: "inter", - style: "medium italic", - }; - expect(flutterMain([node])).toEqual(`Text( - "", - style: TextStyle( - fontStyle: FontStyle.italic, - fontFamily: "inter", - fontWeight: FontWeight.w500, - ), -)`); - - node.fontName = { - family: "inter", - style: "regular", - }; - expect(flutterMain([node])).toEqual(`Text( - "", -)`); - - node.fontName = { - family: "inter", - style: "doesn't exist", - }; - expect(flutterMain([node])).toEqual(`Text( - "", -)`); - }); - - it("letterSpacing", () => { - const node = new AltTextNode(); - node.characters = ""; - node.width = 16; - node.height = 16; - node.textAutoResize = "WIDTH_AND_HEIGHT"; - node.fontSize = 24; - - node.letterSpacing = { - value: 110, - unit: "PERCENT", - }; - expect(flutterMain([node])).toEqual( - `Text( - "", - style: TextStyle( - fontSize: 24, - letterSpacing: 26.40, - ), -)`, - ); - - node.letterSpacing = { - value: 10, - unit: "PIXELS", - }; - expect(flutterMain([node])).toEqual( - `Text( - "", - style: TextStyle( - fontSize: 24, - letterSpacing: 10, - ), -)`, - ); - }); - - it("lineHeight", () => { - const node = new AltTextNode(); - node.characters = ""; - node.width = 16; - node.height = 16; - node.textAutoResize = "WIDTH_AND_HEIGHT"; - - node.lineHeight = { - value: 110, - unit: "PERCENT", - }; - expect(flutterMain([node])).toEqual(`Text( - "", -)`); - - node.lineHeight = { - value: 10, - unit: "PIXELS", - }; - - expect(flutterMain([node])).toEqual(`Text( - "", -)`); - }); - - it("textCase", () => { - const node = new AltTextNode(); - node.characters = "aA"; - - node.textCase = "LOWER"; - expect(flutterMain([node])).toEqual(`Text( - "aa", -)`); - - // todo implement it - // node.textCase = "TITLE"; - // expect(flutterMain([node])).toEqual('Text("Aa", ),'); - - node.textCase = "UPPER"; - expect(flutterMain([node])).toEqual(`Text( - "AA", -)`); - - node.textCase = "ORIGINAL"; - expect(flutterMain([node])).toEqual(`Text( - "aA", -)`); - - node.textAlignHorizontal = "CENTER"; - node.layoutAlign = "INHERIT"; - expect(flutterMain([node])).toEqual(`Text( - "aA", - textAlign: TextAlign.center, -)`); - - node.textAlignHorizontal = "JUSTIFIED"; - node.layoutAlign = "INHERIT"; - expect(flutterMain([node])).toEqual(`Text( - "aA", - textAlign: TextAlign.justify, -)`); - }); - - it("textDecoration", () => { - const node = new AltTextNode(); - node.characters = ""; - - node.textDecoration = "NONE"; - expect(flutterMain([node])).toEqual(`Text( - "", -)`); - - node.textDecoration = "STRIKETHROUGH"; - expect(flutterMain([node])).toEqual(`Text( - "", -)`); - - node.textDecoration = "UNDERLINE"; - expect(flutterMain([node])).toEqual( - `Text( - "", - style: TextStyle( - decoration: TextDecoration.underline, - ), -)`, - ); - }); - - it("reset", () => { - const node = new AltTextNode(); - node.characters = ""; - - const builder = new FlutterTextBuilder(""); - builder.reset(); - expect(builder.child).toEqual(""); - }); -}); diff --git a/__tests__/html/builderImpl/htmlBlend.test.ts b/__tests__/html/builderImpl/htmlBlend.test.ts deleted file mode 100644 index 48fd7223..00000000 --- a/__tests__/html/builderImpl/htmlBlend.test.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { AltRectangleNode } from "../../../src/altNodes/altMixins"; -import { - htmlOpacity, - htmlRotation, - htmlVisibility, -} from "./../../../src/html/builderImpl/htmlBlend"; - -describe("HTML Blend", () => { - const node = new AltRectangleNode(); - - it("opacity", () => { - node.opacity = 0.1; - expect(htmlOpacity(node, false)).toEqual("opacity: 0.10; "); - - node.opacity = 0.3; - expect(htmlOpacity(node, true)).toEqual("opacity: 0.30, "); - - node.opacity = 1; - expect(htmlOpacity(node, false)).toEqual(""); - }); - - it("visibility", () => { - // undefined (unitialized, only happen on tests) - expect(htmlVisibility(node, false)).toEqual(""); - - node.visible = false; - expect(htmlVisibility(node, false)).toEqual("visibility: hidden; "); - - node.visible = false; - expect(htmlVisibility(node, true)).toEqual("visibility: 'hidden', "); - }); - - it("rotation", () => { - // avoid rounding errors - node.rotation = -7.0167096047110005e-15; - expect(htmlRotation(node, false)).toEqual(""); - - node.rotation = 45; - expect(htmlRotation(node, false)).toEqual("transform: rotate(45deg); "); - - node.rotation = -90; - expect(htmlRotation(node, true)).toEqual("transform: 'rotate(-90deg)', "); - }); -}); diff --git a/__tests__/html/builderImpl/htmlBorder.test.ts b/__tests__/html/builderImpl/htmlBorder.test.ts deleted file mode 100644 index f6cbcf6a..00000000 --- a/__tests__/html/builderImpl/htmlBorder.test.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { - AltRectangleNode, - AltTextNode, - AltEllipseNode, -} from "../../../src/altNodes/altMixins"; -import { htmlBorderRadius } from "../../../src/html/builderImpl/htmlBorderRadius"; -describe("HTML Border", () => { - // @ts-expect-error for some reason, need to override this for figma.mixed to work - global.figma = { - mixed: undefined, - }; - - const node = new AltRectangleNode(); - node.topRightRadius = 0; - node.bottomLeftRadius = 0; - node.bottomRightRadius = 0; - - node.strokes = [ - { - type: "SOLID", - color: { r: 0, g: 0, b: 0 }, - }, - ]; - - it("standard corner radius", () => { - node.cornerRadius = 0; - expect(htmlBorderRadius(node, false)).toEqual(""); - - node.height = 90; - node.cornerRadius = 45; - expect(htmlBorderRadius(node, false)).toEqual("border-radius: 45px; "); - - node.topLeftRadius = 0; - node.cornerRadius = 0; - expect(htmlBorderRadius(node, false)).toEqual(""); - - node.cornerRadius = 10; - expect(htmlBorderRadius(node, false)).toEqual("border-radius: 10px; "); - }); - - it("custom corner radius", () => { - node.cornerRadius = figma.mixed; - node.topLeftRadius = 4; - expect(htmlBorderRadius(node, false)).toEqual( - "border-top-left-radius: 4px; ", - ); - - node.topLeftRadius = 0; - node.topRightRadius = 4; - expect(htmlBorderRadius(node, false)).toEqual( - "border-top-right-radius: 4px; ", - ); - - node.topRightRadius = 0; - node.bottomLeftRadius = 4; - expect(htmlBorderRadius(node, false)).toEqual( - "border-bottom-left-radius: 4px; ", - ); - - node.bottomLeftRadius = 0; - node.bottomRightRadius = 4; - expect(htmlBorderRadius(node, false)).toEqual( - "border-bottom-right-radius: 4px; ", - ); - }); - - it("other nodes", () => { - // Ellipses are always round - expect(htmlBorderRadius(new AltEllipseNode(), false)).toEqual( - "border-radius: 9999px; ", - ); - - // Text is unsupported - expect(htmlBorderRadius(new AltTextNode(), false)).toEqual(""); - }); -}); diff --git a/__tests__/html/builderImpl/htmlColor.test.ts b/__tests__/html/builderImpl/htmlColor.test.ts deleted file mode 100644 index d96ca80b..00000000 --- a/__tests__/html/builderImpl/htmlColor.test.ts +++ /dev/null @@ -1,178 +0,0 @@ -import { AltRectangleNode, AltTextNode } from "../../../src/altNodes/altMixins"; -import { htmlMain } from "./../../../src/html/htmlMain"; -import { - htmlColorFromFills, - htmlGradientFromFills, -} from "./../../../src/html/builderImpl/htmlColor"; -describe("HTML Color", () => { - // @ts-expect-error for some reason, need to override this for figma.mixed to work - global.figma = { - mixed: undefined, - }; - - it("white and black", () => { - const node = new AltTextNode(); - node.characters = ""; - node.fills = [ - { - type: "SOLID", - color: { - r: 1.0, - g: 1.0, - b: 1.0, - }, - opacity: 1.0, - }, - ]; - - expect(htmlColorFromFills(node.fills)).toEqual("white"); - - node.fills = [ - { - type: "SOLID", - color: { - r: 0.0, - g: 0.0, - b: 0.0, - }, - opacity: 1.0, - }, - ]; - expect(htmlColorFromFills(node.fills)).toEqual("black"); - }); - - it("opacity and visibility changes", () => { - const node = new AltRectangleNode(); - node.fills = [ - { - type: "SOLID", - color: { - r: 1.0, - g: 0.0, - b: 0.0, - }, - opacity: 1.0, - visible: false, - }, - ]; - - expect(htmlColorFromFills(node.fills)).toEqual(""); - - node.fills = [ - { - type: "SOLID", - color: { - r: 1.0, - g: 0.0, - b: 0.0, - }, - opacity: 0.0, - visible: true, - }, - ]; - expect(htmlColorFromFills(node.fills)).toEqual("rgba(255, 0, 0, 0)"); - }); - - it("Gradient Linear", () => { - const node = new AltRectangleNode(); - const gradientFill: GradientPaint = { - type: "GRADIENT_LINEAR", - gradientTransform: [ - [0, 0, 0], - [0, 0, 0], - ], - gradientStops: [ - { - position: 0, - color: { - r: 0, - g: 0, - b: 0, - a: 1, - }, - }, - ], - }; - - node.fills = [gradientFill]; - - expect(htmlGradientFromFills(node.fills)).toEqual( - "linear-gradient(90deg, black)", - ); - - // topLeft to bottomRight (135) - Object.assign(gradientFill.gradientTransform, [ - [0.8038461208343506, 0.7035384774208069, -0.2932307720184326], - [1.3402682542800903, -1.4652644395828247, 0.5407097935676575], - ]); - expect(htmlGradientFromFills(node.fills)).toEqual( - "linear-gradient(131deg, black)", - ); - }); - - it("Execute Main with Linear Gradient, corners and stroke", () => { - const node = new AltRectangleNode(); - const gradientFill: GradientPaint = { - type: "GRADIENT_LINEAR", - gradientTransform: [ - [0.8038461208343506, 0.7035384774208069, -0.2932307720184326], - [1.3402682542800903, -1.4652644395828247, 0.5407097935676575], - ], - gradientStops: [ - { - position: 0, - color: { - r: 0, - g: 0, - b: 0, - a: 1, - }, - }, - { - position: 1, - color: { - r: 1, - g: 0, - b: 0, - a: 1, - }, - }, - ], - }; - - // width is going be 18 because 10 + 4 + 4 of stroke. - node.height = 10; - node.width = 10; - node.fills = [gradientFill]; - node.strokeWeight = 4; - node.strokeAlign = "OUTSIDE"; - node.strokes = [ - { - type: "SOLID", - color: { r: 0.25, g: 0.25, b: 0.25 }, - }, - ]; - node.cornerRadius = 16; - node.dashPattern = []; - - expect(htmlMain([node])).toEqual( - `
`, - ); - }); - - it("fail with other fill types", () => { - const node = new AltRectangleNode(); - node.fills = [ - { - type: "GRADIENT_LINEAR", - gradientTransform: [ - [0, 0, 0], - [0, 0, 0], - ], - gradientStops: [], - }, - ]; - - expect(htmlColorFromFills(node.fills)).toEqual(""); - }); -}); diff --git a/__tests__/html/builderImpl/htmlPadding.test.ts b/__tests__/html/builderImpl/htmlPadding.test.ts deleted file mode 100644 index c87cf17d..00000000 --- a/__tests__/html/builderImpl/htmlPadding.test.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { - AltRectangleNode, - AltFrameNode, -} from "../../../src/altNodes/altMixins"; -import { htmlPadding } from "./../../../src/html/builderImpl/htmlPadding"; - -describe("HTML padding", () => { - it("test html padding", () => { - const frameNode = new AltFrameNode(); - expect(htmlPadding(frameNode, false)).toEqual(""); - - frameNode.layoutMode = "NONE"; - expect(htmlPadding(frameNode, false)).toEqual(""); - - frameNode.layoutMode = "VERTICAL"; - - frameNode.paddingLeft = 4; - frameNode.paddingRight = 4; - frameNode.paddingTop = 4; - frameNode.paddingBottom = 4; - expect(htmlPadding(frameNode, false)).toEqual("padding: 4px; "); - - frameNode.paddingLeft = 1; - frameNode.paddingRight = 2; - frameNode.paddingTop = 3; - frameNode.paddingBottom = 4; - expect(htmlPadding(frameNode, false)).toEqual( - "padding-top: 3px; padding-bottom: 4px; padding-left: 1px; padding-right: 2px; ", - ); - - frameNode.paddingLeft = 4; - frameNode.paddingRight = 4; - frameNode.paddingTop = 8; - frameNode.paddingBottom = 8; - expect(htmlPadding(frameNode, false)).toEqual( - "padding-left: 4px; padding-right: 4px; padding-top: 8px; padding-bottom: 8px; ", - ); - - frameNode.paddingLeft = 0; - frameNode.paddingRight = 0; - frameNode.paddingTop = 0; - frameNode.paddingBottom = 0; - expect(htmlPadding(frameNode, false)).toEqual(""); - - const notFrame = new AltRectangleNode(); - expect(htmlPadding(notFrame, false)).toEqual(""); - }); -}); diff --git a/__tests__/html/builderImpl/htmlPosition.test.ts b/__tests__/html/builderImpl/htmlPosition.test.ts deleted file mode 100644 index 0c294bf3..00000000 --- a/__tests__/html/builderImpl/htmlPosition.test.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { AltFrameNode } from "../../../src/altNodes/altMixins"; -import { htmlPosition } from "./../../../src/html/builderImpl/htmlPosition"; - -describe("HTML Position", () => { - // @ts-expect-error for some reason, need to override this for figma.mixed to work - global.figma = { - mixed: undefined, - }; - - it("Frame Absolute Position", () => { - const parent = new AltFrameNode(); - parent.width = 100; - parent.height = 100; - parent.x = 0; - parent.y = 0; - parent.id = "root"; - parent.layoutMode = "NONE"; - parent.isRelative = true; - - const node = new AltFrameNode(); - parent.id = "node"; - node.parent = parent; - - // child equals parent - node.width = 100; - node.height = 100; - expect(htmlPosition(node)).toEqual("absoluteManualLayout"); - }); - - it("Position: node has same size as parent", () => { - const parent = new AltFrameNode(); - parent.width = 100; - parent.height = 100; - parent.layoutMode = "NONE"; - - const node = new AltFrameNode(); - node.width = 100; - node.height = 100; - node.parent = parent; - - const nodeF2 = new AltFrameNode(); - nodeF2.width = 100; - nodeF2.height = 100; - nodeF2.parent = parent; - - parent.children = [node, nodeF2]; - - expect(htmlPosition(node)).toEqual(""); - }); - - it("No position when parent is root", () => { - const node = new AltFrameNode(); - node.layoutMode = "NONE"; - - const parent = new AltFrameNode(); - parent.id = "root"; - parent.layoutMode = "NONE"; - - node.parent = parent; - - expect(htmlPosition(node, parent.id)).toEqual(""); - }); -}); diff --git a/__tests__/html/builderImpl/htmlShadow.test.ts b/__tests__/html/builderImpl/htmlShadow.test.ts deleted file mode 100644 index edbab5b1..00000000 --- a/__tests__/html/builderImpl/htmlShadow.test.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { AltRectangleNode } from "../../../src/altNodes/altMixins"; -import { htmlShadow } from "./../../../src/html/builderImpl/htmlShadow"; -describe("HTML Shadow", () => { - // @ts-expect-error for some reason, need to override this for figma.mixed to work - global.figma = { - mixed: undefined, - }; - - it("drop shadow", () => { - const node = new AltRectangleNode(); - - // no shadow - expect(htmlShadow(node)).toEqual(""); - - node.effects = [ - { - type: "DROP_SHADOW", - blendMode: "NORMAL", - color: { r: 0, g: 0, b: 0, a: 0.25 }, - offset: { x: 0, y: 4 }, - radius: 4, - visible: true, - }, - ]; - - expect(htmlShadow(node)).toEqual("0px 4px 4px rgba(0, 0, 0, 0.25)"); - }); - - it("inner shadow", () => { - const node = new AltRectangleNode(); - - node.effects = [ - { - blendMode: "NORMAL", - color: { r: 0, g: 0, b: 0, a: 0.25 }, - offset: { x: 0, y: 4 }, - radius: 4, - type: "INNER_SHADOW", - visible: true, - }, - ]; - - expect(htmlShadow(node)).toEqual("0px 4px 4px rgba(0, 0, 0, 0.25) inset"); - }); -}); diff --git a/__tests__/html/builderImpl/htmlSize.test.ts b/__tests__/html/builderImpl/htmlSize.test.ts deleted file mode 100644 index ee9fcc53..00000000 --- a/__tests__/html/builderImpl/htmlSize.test.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { - AltRectangleNode, - AltFrameNode, -} from "../../../src/altNodes/altMixins"; -import { htmlSize } from "../../../src/html/builderImpl/htmlSize"; - -describe("HTML Size", () => { - // @ts-expect-error for some reason, need to override this for figma.mixed to work - global.figma = { - mixed: undefined, - }; - - it("size for a rectangle", () => { - const node = new AltRectangleNode(); - - node.width = 16; - node.height = 16; - expect(htmlSize(node, false)).toEqual("width: 16px; height: 16px; "); - }); - - it("STRETCH inside AutoLayout", () => { - const node = new AltFrameNode(); - node.layoutMode = "HORIZONTAL"; - node.counterAxisSizingMode = "FIXED"; - node.primaryAxisSizingMode = "FIXED"; - node.width = 100; - node.height = 100; - node.paddingLeft = 0; - node.paddingRight = 0; - node.paddingTop = 0; - node.paddingBottom = 0; - - const child = new AltRectangleNode(); - child.layoutAlign = "STRETCH"; - child.layoutGrow = 1; - child.width = 100; - child.height = 100; - - child.parent = node; - node.children = [child]; - - expect(htmlSize(child, false)).toEqual("flex: 1 1 0%; height: 100%; "); - - // fail - node.layoutMode = "VERTICAL"; - child.width = 16; - child.height = 16; - expect(htmlSize(child, false)).toEqual("width: 100%; flex: 1 1 0%; "); - }); - - it("counterAxisSizingMode is AUTO", () => { - const node = new AltFrameNode(); - node.layoutMode = "HORIZONTAL"; - node.primaryAxisSizingMode = "AUTO"; - node.counterAxisSizingMode = "AUTO"; - node.x = 0; - node.y = 0; - node.width = 48; - node.height = 48; - node.children = [new AltRectangleNode(), new AltRectangleNode()]; - - expect(htmlSize(node, false)).toEqual(""); - - // responsive - const parentNode = new AltFrameNode(); - parentNode.counterAxisSizingMode = "AUTO"; - parentNode.primaryAxisSizingMode = "FIXED"; - parentNode.x = 0; - parentNode.y = 0; - parentNode.width = 48; - parentNode.height = 48; - parentNode.children = [node]; - node.parent = parentNode; - expect(htmlSize(node, false)).toEqual(""); - expect(htmlSize(parentNode, false)).toEqual("width: 48px; height: 48px; "); - }); - - it("adjust parent if children's size + stroke > parent size", () => { - const parentNode = new AltFrameNode(); - parentNode.width = 8; - parentNode.height = 8; - - const node = new AltRectangleNode(); - node.width = 8; - node.height = 8; - - node.strokeWeight = 4; - node.strokeAlign = "CENTER"; - node.strokes = [ - { - type: "SOLID", - color: { r: 0.25, g: 0.25, b: 0.25 }, - }, - ]; - - expect(htmlSize(parentNode, false)).toEqual("width: 8px; height: 8px; "); - - parentNode.children = [node]; - node.parent = parentNode; - expect(htmlSize(parentNode, false)).toEqual("width: 12px; height: 12px; "); - - node.strokeAlign = "OUTSIDE"; - expect(htmlSize(parentNode, false)).toEqual("width: 16px; height: 16px; "); - }); -}); diff --git a/__tests__/html/htmlMain.test.ts b/__tests__/html/htmlMain.test.ts deleted file mode 100644 index 36270489..00000000 --- a/__tests__/html/htmlMain.test.ts +++ /dev/null @@ -1,353 +0,0 @@ -import { - AltEllipseNode, - AltTextNode, - AltRectangleNode, - AltFrameNode, - AltGroupNode, -} from "../../src/altNodes/altMixins"; -import { convertToAutoLayout } from "../../src/altNodes/convertToAutoLayout"; -import { TailwindDefaultBuilder } from "../../src/tailwind/tailwindDefaultBuilder"; -import { tailwindMain } from "../../src/tailwind/tailwindMain"; -import { htmlMain } from "./../../src/html/htmlMain"; - -describe("HTML Main", () => { - // @ts-expect-error for some reason, need to override this for figma.mixed to work - global.figma = { - mixed: undefined, - }; - - it("children is larger than 256", () => { - const node = new AltFrameNode(); - node.width = 320; - node.height = 320; - node.name = "FRAME"; - node.layoutMode = "NONE"; - node.counterAxisSizingMode = "FIXED"; - - const child1 = new AltRectangleNode(); - child1.width = 385; - child1.height = 8; - child1.x = 9; - child1.y = 9; - child1.name = "RECT1"; - child1.fills = [ - { - type: "SOLID", - color: { - r: 1, - g: 1, - b: 1, - }, - }, - ]; - - const child2 = new AltRectangleNode(); - child2.width = 8; - child2.height = 385; - child2.x = 9; - child2.y = 9; - child2.name = "RECT2"; - - // this works as a test for JSX, but should never happen in reality. In reality Frame would need to have 2 children and be relative. - node.children = [child1, child2]; - child1.parent = node; - child2.parent = node; - - expect(htmlMain([convertToAutoLayout(node)])) - .toEqual(`
-
-
-
`); - }); - - it("Group with relative position", () => { - // this also should neve happen in reality, because Group must have the same size as the children. - - const node = new AltGroupNode(); - node.width = 32; - node.height = 32; - node.x = 0; - node.y = 0; - node.name = "GROUP"; - node.isRelative = true; - - const child = new AltRectangleNode(); - child.width = 4; - child.height = 4; - child.x = 9; - child.y = 9; - child.name = "RECT"; - child.fills = [ - { - type: "SOLID", - color: { - r: 1, - g: 1, - b: 1, - }, - }, - ]; - - node.children = [child]; - child.parent = node; - expect(htmlMain([node], "", true, true)) - .toEqual(`
-
-
`); - }); - - it("ellipse with no size", () => { - const node = new AltEllipseNode(); - - // undefined (unitialized, only happen on tests) - expect(htmlMain([node])).toEqual( - '
', - ); - // todo verify if it is working properly. - node.x = 0; - node.y = 0; - - node.width = 0; - node.height = 10; - expect(htmlMain([node])).toEqual(""); - - node.width = 10; - node.height = 0; - expect(htmlMain([node])).toEqual(""); - }); - - it("input", () => { - const textNode = new AltTextNode(); - textNode.characters = "username"; - textNode.fontSize = 26; - textNode.x = 0; - textNode.y = 0; - - const frameNode = new AltFrameNode(); - frameNode.layoutMode = "HORIZONTAL"; - frameNode.width = 100; - frameNode.height = 40; - frameNode.counterAxisSizingMode = "AUTO"; - frameNode.primaryAxisSizingMode = "AUTO"; - - frameNode.primaryAxisAlignItems = "SPACE_BETWEEN"; - frameNode.counterAxisAlignItems = "CENTER"; - - frameNode.children = [textNode]; - textNode.parent = frameNode; - - // In real life, justify-between would be converted to justify-center in the altConversion. - expect(tailwindMain([frameNode])).toEqual( - `
-

username

-
`, - ); - - frameNode.name = "this is the InPuT"; - expect(htmlMain([frameNode])).toEqual( - '', - ); - }); - - it("JSX", () => { - const node = new AltRectangleNode(); - node.name = "RECT"; - - const builder = new TailwindDefaultBuilder(node, true, true); - - expect(builder.build()).toEqual(' className="RECT"'); - - builder.reset(); - expect(builder.attributes).toEqual(""); - }); - - it("JSX with relative position", () => { - const node = new AltFrameNode(); - node.width = 32; - node.height = 32; - node.x = 0; - node.y = 0; - node.name = "FRAME"; - node.layoutMode = "NONE"; - node.counterAxisSizingMode = "FIXED"; - - const child1 = new AltRectangleNode(); - child1.width = 4; - child1.height = 4; - child1.x = 9; - child1.y = 9; - child1.name = "RECT1"; - child1.fills = [ - { - type: "SOLID", - color: { - r: 1, - g: 1, - b: 1, - }, - }, - ]; - - const child2 = new AltRectangleNode(); - child2.width = 4; - child2.height = 4; - child2.x = 9; - child2.y = 9; - child2.name = "RECT2"; - - // this works as a test for JSX, but should never happen in reality. In reality Frame would need to have 2 children and be relative. - node.children = [child1, child2]; - child1.parent = node; - child2.parent = node; - - expect(htmlMain([convertToAutoLayout(node)], "", true, true)) - .toEqual(`
-
-
-
`); - }); - - it("AutoLayout", () => { - const node = new AltFrameNode(); - node.width = 32; - node.height = 32; - node.x = 0; - node.y = 0; - node.name = "FRAME"; - node.layoutMode = "HORIZONTAL"; - node.itemSpacing = 4; - node.primaryAxisAlignItems = "MIN"; - node.counterAxisAlignItems = "MIN"; - node.counterAxisSizingMode = "FIXED"; - node.primaryAxisSizingMode = "FIXED"; - - const child1 = new AltRectangleNode(); - child1.width = 4; - child1.height = 4; - child1.x = 0; - child1.y = 0; - child1.name = "RECT1"; - child1.fills = [ - { - type: "SOLID", - color: { - r: 1, - g: 1, - b: 1, - }, - }, - ]; - - const child2 = new AltFrameNode(); - child2.width = 4; - child2.height = 4; - child2.x = 8; - child2.y = 0; - child2.name = "RECT2"; - child2.counterAxisSizingMode = "FIXED"; - child2.primaryAxisSizingMode = "FIXED"; - child2.primaryAxisAlignItems = "CENTER"; - child2.counterAxisAlignItems = "CENTER"; - child2.layoutGrow = 0; - child2.layoutAlign = "INHERIT"; - child2.children = []; - - // this works as a test for JSX, but should never happen in reality. In reality Frame would need to have 2 children and be relative. - node.children = [child1, child2]; - child1.parent = node; - child2.parent = node; - - expect(htmlMain([node], "", false, true)) - .toEqual(`
-
-
-
-
`); - - node.primaryAxisAlignItems = "MAX"; - node.counterAxisAlignItems = "MAX"; - - child2.primaryAxisAlignItems = "SPACE_BETWEEN"; - child2.counterAxisAlignItems = "CENTER"; - - expect(htmlMain([node], "", false, true)) - .toEqual(`
-
-
-
-
`); - }); - - it("Gradient Background with Gradient Text", () => { - const gradientFill: GradientPaint = { - type: "GRADIENT_LINEAR", - gradientTransform: [ - [0.8038461208343506, 0.7035384774208069, -0.2932307720184326], - [1.3402682542800903, -1.4652644395828247, 0.5407097935676575], - ], - gradientStops: [ - { - position: 0, - color: { - r: 0, - g: 0, - b: 1, - a: 1, - }, - }, - { - position: 1, - color: { - r: 1, - g: 0, - b: 0, - a: 1, - }, - }, - ], - }; - - const node = new AltFrameNode(); - node.width = 32; - node.height = 32; - node.x = 0; - node.y = 0; - node.name = "FRAME"; - node.layoutMode = "HORIZONTAL"; - node.itemSpacing = 4; - node.primaryAxisAlignItems = "MIN"; - node.counterAxisAlignItems = "MIN"; - node.counterAxisSizingMode = "FIXED"; - node.primaryAxisSizingMode = "FIXED"; - node.fills = [gradientFill]; - node.effects = [ - { - blendMode: "NORMAL", - color: { r: 0, g: 0, b: 0, a: 0.25 }, - offset: { x: 0, y: 4 }, - radius: 4, - type: "DROP_SHADOW", - visible: true, - }, - ]; - node.cornerRadius = 8; - - const text = new AltTextNode(); - text.width = 20; - text.height = 4; - text.x = 0; - text.y = 0; - text.name = "TEXT"; - text.fills = [gradientFill]; - text.characters = "gradient"; - - // this works as a test for JSX, but should never happen in reality. In reality Frame would need to have 2 children and be relative. - node.children = [text]; - text.parent = node; - - expect(htmlMain([node], "", false, true)) - .toEqual(`
-

gradient

-
`); - }); -}); diff --git a/__tests__/html/htmlText.test.ts b/__tests__/html/htmlText.test.ts deleted file mode 100644 index 3a968d9d..00000000 --- a/__tests__/html/htmlText.test.ts +++ /dev/null @@ -1,196 +0,0 @@ -import { AltTextNode } from "../../src/altNodes/altMixins"; -import { htmlMain } from "./../../src/html/htmlMain"; - -describe("HTML Text", () => { - // @ts-expect-error for some reason, need to override this for figma.mixed to work - global.figma = { - mixed: undefined, - }; - it("textAutoResize", () => { - const node = new AltTextNode(); - node.characters = ""; - node.width = 16; - node.height = 16; - - node.textAutoResize = "NONE"; - expect(htmlMain([node])).toEqual( - '

', - ); - - node.textAutoResize = "HEIGHT"; - expect(htmlMain([node])).toEqual('

'); - - node.textAutoResize = "WIDTH_AND_HEIGHT"; - expect(htmlMain([node])).toEqual("

"); - }); - - it("textAlignHorizontal", () => { - const node = new AltTextNode(); - node.characters = ""; - node.width = 16; - node.height = 16; - - node.textAutoResize = "WIDTH_AND_HEIGHT"; - node.textAlignHorizontal = "LEFT"; - expect(htmlMain([node])).toEqual("

"); - - node.textAutoResize = "NONE"; - node.textAlignHorizontal = "CENTER"; - expect(htmlMain([node])).toEqual( - '

', - ); - - node.textAutoResize = "NONE"; - node.textAlignHorizontal = "RIGHT"; - expect(htmlMain([node])).toEqual( - '

', - ); - - node.textAlignHorizontal = "JUSTIFIED"; - expect(htmlMain([node])).toEqual( - '

', - ); - }); - it("fontSize", () => { - const node = new AltTextNode(); - node.characters = ""; - node.width = 16; - node.height = 16; - node.fontSize = 16; - node.textAutoResize = "WIDTH_AND_HEIGHT"; - - expect(htmlMain([node])).toEqual('

'); - }); - - it("fontName", () => { - const node = new AltTextNode(); - node.characters = ""; - node.width = 16; - node.height = 16; - node.textAutoResize = "WIDTH_AND_HEIGHT"; - - node.fontName = { - family: "inter", - style: "bold", - }; - expect(htmlMain([node])).toEqual('

'); - - node.fontName = { - family: "inter", - style: "medium italic", - }; - expect(htmlMain([node])).toEqual( - '

', - ); - - node.fontName = { - family: "inter", - style: "regular", - }; - expect(htmlMain([node])).toEqual("

"); - - node.fontName = { - family: "inter", - style: "doesn't exist", - }; - expect(htmlMain([node])).toEqual("

"); - }); - - it("letterSpacing", () => { - const node = new AltTextNode(); - node.characters = ""; - node.width = 16; - node.height = 16; - node.fontSize = 24; - node.textAutoResize = "WIDTH_AND_HEIGHT"; - - node.letterSpacing = { - value: 110, - unit: "PERCENT", - }; - expect(htmlMain([node])).toEqual( - '

', - ); - - node.letterSpacing = { - value: 10, - unit: "PIXELS", - }; - expect(htmlMain([node])).toEqual( - '

', - ); - }); - - it("lineHeight", () => { - const node = new AltTextNode(); - node.characters = ""; - node.width = 16; - node.height = 16; - node.textAutoResize = "WIDTH_AND_HEIGHT"; - node.fontSize = 24; - - node.lineHeight = { - value: 110, - unit: "PERCENT", - }; - expect(htmlMain([node])).toEqual( - '

', - ); - - node.lineHeight = { - value: 10, - unit: "PIXELS", - }; - expect(htmlMain([node])).toEqual( - '

', - ); - - node.lineHeight = { - unit: "AUTO", - }; - expect(htmlMain([node])).toEqual( - '

', - ); - }); - - it("textCase", () => { - const node = new AltTextNode(); - node.characters = ""; - - node.textCase = "LOWER"; - expect(htmlMain([node])).toEqual( - '

', - ); - - node.textCase = "TITLE"; - expect(htmlMain([node])).toEqual( - '

', - ); - - node.textCase = "UPPER"; - expect(htmlMain([node])).toEqual( - '

', - ); - - node.textCase = "ORIGINAL"; - expect(htmlMain([node])).toEqual("

"); - }); - - it("textDecoration", () => { - const node = new AltTextNode(); - node.characters = ""; - - node.textDecoration = "NONE"; - expect(htmlMain([node])).toEqual("

"); - - node.textDecoration = "STRIKETHROUGH"; - expect(htmlMain([node])).toEqual( - '

', - ); - - node.textDecoration = "UNDERLINE"; - expect(htmlMain([node])).toEqual( - '

', - ); - }); -}); diff --git a/__tests__/nearest-color/nearestColor.test.ts b/__tests__/nearest-color/nearestColor.test.ts deleted file mode 100644 index fe7c3bc0..00000000 --- a/__tests__/nearest-color/nearestColor.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { nearestColorFrom } from "../../src/nearest-color/nearestColor"; - -// the own developer didn't test it, but I'm testing. -describe("Nearest color", () => { - it("3 hex", () => { - const nearest = nearestColorFrom(["fff", "000"]); - expect(nearest("ff0")).toEqual("fff"); - expect(nearest("0f0")).toEqual("000"); - }); - - it("objects", () => { - const nearest = nearestColorFrom(["fff", "000"]); - expect(nearest({ r: 0, g: 0, b: 100 })).toEqual("000"); - expect(nearest({ r: 250, g: 220, b: 180 })).toEqual("fff"); - }); - - it("invalid", () => { - const nearest = nearestColorFrom(["fff", "000"]); - expect(() => nearest("ff111111111")).toThrow(); - }); -}); diff --git a/__tests__/retrieveUI/retrieveColors.test.ts b/__tests__/retrieveUI/retrieveColors.test.ts deleted file mode 100644 index 8c0c8aa3..00000000 --- a/__tests__/retrieveUI/retrieveColors.test.ts +++ /dev/null @@ -1,191 +0,0 @@ -import { - retrieveGenericLinearGradients, - retrieveGenericSolidUIColors, -} from "../../src/common/retrieveUI/retrieveColors"; -import { - AltFrameNode, - AltRectangleNode, - AltTextNode, -} from "../../src/altNodes/altMixins"; - -describe("Retrieve Colors for UI", () => { - // @ts-expect-error for some reason, need to override this for figma.mixed to work - global.figma = { - mixed: undefined, - }; - - const child0 = new AltFrameNode(); - - const child1 = new AltRectangleNode(); - child1.parent = child0; - - const child2 = new AltFrameNode(); - child2.parent = child0; - - const child3 = new AltTextNode(); - child3.parent = child2; - - child2.children = [child3]; - - const child4 = new AltRectangleNode(); - child4.fills = []; - child4.strokes = []; - child4.parent = child0; - - child0.children = [child1, child2, child4]; - it("Solid Colors", () => { - const fills1: ReadonlyArray = [ - { - type: "SOLID", - color: { - r: 1, - g: 1, - b: 1, - }, - }, - ]; - - const fills2: ReadonlyArray = [ - { - type: "SOLID", - color: { - r: 0, - g: 0, - b: 0, - }, - }, - ]; - - child1.fills = fills1; - child3.fills = fills2; - child3.strokes = fills1; - - expect(retrieveGenericSolidUIColors([child0], "html")).toEqual([ - { - colorName: "", - contrastBlack: 0, - contrastWhite: 0, - exported: "black", - hex: "000000", - }, - { - colorName: "", - contrastBlack: 0, - contrastWhite: 0, - exported: "white", - hex: "ffffff", - }, - ]); - - expect(retrieveGenericSolidUIColors([child0], "tailwind")).toEqual([ - { - colorName: "black", - contrastBlack: 0, - contrastWhite: 0, - exported: "text-black ", - hex: "000000", - }, - { - colorName: "white", - contrastBlack: 0, - contrastWhite: 0, - exported: "bg-white ", - hex: "ffffff", - }, - ]); - - expect(retrieveGenericSolidUIColors([child0], "flutter")).toEqual([ - { - colorName: "", - contrastBlack: 1, - contrastWhite: 21, - exported: "Colors.black", - hex: "000000", - }, - { - colorName: "", - contrastBlack: 21, - contrastWhite: 1, - exported: "Colors.white", - hex: "ffffff", - }, - ]); - - expect(retrieveGenericSolidUIColors([child0], "swiftui")).toEqual([ - { - colorName: "", - contrastBlack: 0, - contrastWhite: 0, - exported: "Color.black", - hex: "000000", - }, - { - colorName: "", - contrastBlack: 0, - contrastWhite: 0, - exported: "Color.white", - hex: "ffffff", - }, - ]); - - // Wrong - expect(retrieveGenericLinearGradients([child0], "swiftui")).toEqual([]); - }); - - it("Linear Gradients", () => { - const gradientFill: GradientPaint = { - type: "GRADIENT_LINEAR", - gradientTransform: [ - [0, 0, 0], - [0, 0, 0], - ], - gradientStops: [ - { - position: 0, - color: { - r: 0, - g: 0, - b: 0, - a: 1, - }, - }, - ], - }; - child1.fills = [gradientFill]; - child3.fills = [gradientFill]; - child3.strokes = [gradientFill]; - - expect(retrieveGenericLinearGradients([child0], "html")).toEqual([ - { - css: "linear-gradient(90deg, black)", - exported: "linear-gradient(90deg, black)", - }, - ]); - - expect(retrieveGenericLinearGradients([child0], "tailwind")).toEqual([ - { - css: "linear-gradient(90deg, black)", - exported: "bg-gradient-to-r from-black ", - }, - ]); - - expect(retrieveGenericLinearGradients([child0], "flutter")).toEqual([ - { - css: "linear-gradient(90deg, black)", - exported: - "LinearGradient(begin: Alignment.centerLeft, end: Alignment.centerRight, colors: [Colors.black], )", - }, - ]); - - expect(retrieveGenericLinearGradients([child0], "swiftui")).toEqual([ - { - css: "linear-gradient(90deg, black)", - exported: - "LinearGradient(gradient: Gradient(colors: [Color.black]), startPoint: .leading, endPoint: .trailing)", - }, - ]); - - // Wrong - expect(retrieveGenericSolidUIColors([child0], "swiftui")).toEqual([]); - }); -}); diff --git a/__tests__/swiftui/builderImpl/swiftuiBlend.test.ts b/__tests__/swiftui/builderImpl/swiftuiBlend.test.ts deleted file mode 100644 index 9afb6dcc..00000000 --- a/__tests__/swiftui/builderImpl/swiftuiBlend.test.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { swiftuiVisibility , - swiftuiOpacity, - swiftuiRotation, -, swiftuiBlendMode } from "../../../src/swiftui/builderImpl/swiftuiBlend"; -import { AltRectangleNode } from "../../../src/altNodes/altMixins"; - - -describe("SwiftUI Blend", () => { - const node = new AltRectangleNode(); - - it("opacity", () => { - node.opacity = 0.1; - expect(swiftuiOpacity(node)).toEqual("\n.opacity(0.10)"); - - node.opacity = 0.45; - expect(swiftuiOpacity(node)).toEqual("\n.opacity(0.45)"); - - node.opacity = 0.0; - expect(swiftuiOpacity(node)).toEqual("\n.opacity(0)"); - }); - - it("visibility", () => { - // undefined (unitialized, only happen on tests) - expect(swiftuiVisibility(node)).toEqual(""); - - node.visible = false; - expect(swiftuiVisibility(node)).toEqual("\n.hidden()"); - - node.visible = true; - expect(swiftuiVisibility(node)).toEqual(""); - }); - - it("rotation", () => { - // avoid rounding errors - node.rotation = -7.0167096047110005e-15; - expect(swiftuiRotation(node)).toEqual(""); - - node.rotation = 45; - expect(swiftuiRotation(node)).toEqual(".rotationEffect(.degrees(45))"); - - node.rotation = -45; - expect(swiftuiRotation(node)).toEqual(".rotationEffect(.degrees(-45))"); - - node.rotation = -180; - expect(swiftuiRotation(node)).toEqual(".rotationEffect(.degrees(-180))"); - }); - - it("blend modes", () => { - node.blendMode = "PASS_THROUGH"; - expect(swiftuiBlendMode(node)).toEqual(""); - - node.blendMode = "COLOR"; - expect(swiftuiBlendMode(node)).toEqual("\n.blendMode(.color)"); - - node.blendMode = "COLOR_BURN"; - expect(swiftuiBlendMode(node)).toEqual("\n.blendMode(.colorBurn)"); - - node.blendMode = "COLOR_DODGE"; - expect(swiftuiBlendMode(node)).toEqual("\n.blendMode(.colorDodge)"); - - node.blendMode = "DIFFERENCE"; - expect(swiftuiBlendMode(node)).toEqual("\n.blendMode(.difference)"); - - node.blendMode = "EXCLUSION"; - expect(swiftuiBlendMode(node)).toEqual("\n.blendMode(.exclusion)"); - - node.blendMode = "HARD_LIGHT"; - expect(swiftuiBlendMode(node)).toEqual("\n.blendMode(.hardLight)"); - - node.blendMode = "HUE"; - expect(swiftuiBlendMode(node)).toEqual("\n.blendMode(.hue)"); - - node.blendMode = "LIGHTEN"; - expect(swiftuiBlendMode(node)).toEqual("\n.blendMode(.lighten)"); - - node.blendMode = "LIGHTEN"; - expect(swiftuiBlendMode(node)).toEqual("\n.blendMode(.lighten)"); - - node.blendMode = "LUMINOSITY"; - expect(swiftuiBlendMode(node)).toEqual("\n.blendMode(.luminosity)"); - - node.blendMode = "MULTIPLY"; - expect(swiftuiBlendMode(node)).toEqual("\n.blendMode(.multiply)"); - - node.blendMode = "OVERLAY"; - expect(swiftuiBlendMode(node)).toEqual("\n.blendMode(.overlay)"); - - node.blendMode = "SATURATION"; - expect(swiftuiBlendMode(node)).toEqual("\n.blendMode(.saturation)"); - - node.blendMode = "SCREEN"; - expect(swiftuiBlendMode(node)).toEqual("\n.blendMode(.screen)"); - - node.blendMode = "SOFT_LIGHT"; - expect(swiftuiBlendMode(node)).toEqual("\n.blendMode(.softLight)"); - }); -}); diff --git a/__tests__/swiftui/builderImpl/swiftuiBorder.test.ts b/__tests__/swiftui/builderImpl/swiftuiBorder.test.ts deleted file mode 100644 index ddc39b6b..00000000 --- a/__tests__/swiftui/builderImpl/swiftuiBorder.test.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { - swiftuiBorder, - swiftuiShapeStroke, -} from "../../../src/swiftui/builderImpl/swiftuiBorder"; -import { - AltRectangleNode, - AltEllipseNode, -} from "../../../src/altNodes/altMixins"; -describe("SwiftUI Border", () => { - // @ts-expect-error for some reason, need to override this for figma.mixed to work - global.figma = { - mixed: undefined, - }; - - const blackFill: Paint = { - type: "SOLID", - color: { r: 0, g: 0, b: 0 }, - opacity: 1, - }; - - it("border without corner", () => { - const node = new AltRectangleNode(); - node.strokes = [blackFill]; - - node.cornerRadius = 0; - node.strokeWeight = 0; - expect(swiftuiBorder(node)).toEqual(""); - expect(swiftuiShapeStroke(node)).toEqual(""); - - node.fills = [blackFill]; - node.strokeWeight = 10; - expect(swiftuiBorder(node)).toEqual("\n.border(Color.black, width: 10)"); - expect(swiftuiShapeStroke(node)).toEqual(""); - }); - - it("border with corner radius", () => { - const node = new AltRectangleNode(); - node.strokes = [blackFill]; - - node.cornerRadius = 0; - node.strokeWeight = 10; - - expect(swiftuiBorder(node)).toEqual(""); - expect(swiftuiShapeStroke(node)).toEqual( - "\n.stroke(Color.black, lineWidth: 10)", - ); - - node.topLeftRadius = 0; - node.cornerRadius = 8; - node.strokeWeight = 10; - - expect(swiftuiBorder(node)).toEqual( - "\n.overlay(RoundedRectangle(cornerRadius: 8).stroke(Color.black, lineWidth: 10))", - ); - expect(swiftuiShapeStroke(node)).toEqual(""); - - node.cornerRadius = figma.mixed; - node.topLeftRadius = 8; - node.topRightRadius = 6; - node.bottomLeftRadius = 4; - node.bottomRightRadius = 2; - - expect(swiftuiBorder(node)).toEqual( - `\n.overlay(RoundedRectangle(cornerRadius: 8).stroke(Color.black, lineWidth: 10))`, - ); - expect(swiftuiShapeStroke(node)).toEqual(""); - - node.fills = [blackFill]; - node.topLeftRadius = 0; - node.topRightRadius = 0; - node.bottomLeftRadius = 0; - node.bottomRightRadius = 0; - - expect(swiftuiBorder(node)).toEqual(`\n.border(Color.black, width: 10)`); - expect(swiftuiShapeStroke(node)).toEqual(""); - }); - - it("Ellipse", () => { - const node = new AltEllipseNode(); - node.strokes = [blackFill]; - node.strokeWeight = 10; - - expect(swiftuiBorder(node)).toEqual(""); - expect(swiftuiShapeStroke(node)).toEqual( - "\n.stroke(Color.black, lineWidth: 10)", - ); - - node.fills = [blackFill]; - - expect(swiftuiBorder(node)).toEqual( - "\n.overlay(Ellipse().stroke(Color.black, lineWidth: 10))", - ); - expect(swiftuiShapeStroke(node)).toEqual(""); - }); - - it("border with random fill", () => { - const node = new AltRectangleNode(); - node.strokes = [ - { - type: "GRADIENT_LINEAR", - gradientTransform: [ - [0, 0, 0], - [0, 0, 0], - ], - gradientStops: [], - }, - ]; - - node.cornerRadius = 8; - node.strokeWeight = 10; - - expect(swiftuiBorder(node)).toEqual( - "\n.overlay(RoundedRectangle(cornerRadius: 8).stroke(LinearGradient(gradient: Gradient(colors: []), startPoint: .leading, endPoint: .trailing), lineWidth: 10))", - ); - expect(swiftuiShapeStroke(node)).toEqual(""); - }); -}); diff --git a/__tests__/swiftui/builderImpl/swiftuiColor.test.ts b/__tests__/swiftui/builderImpl/swiftuiColor.test.ts deleted file mode 100644 index a1679daa..00000000 --- a/__tests__/swiftui/builderImpl/swiftuiColor.test.ts +++ /dev/null @@ -1,276 +0,0 @@ -import { swiftuiColorFromFills } from "../../../src/swiftui/builderImpl/swiftuiColor"; -import { AltRectangleNode, AltTextNode } from "../../../src/altNodes/altMixins"; -import { swiftuiMain } from "./../../../src/swiftui/swiftuiMain"; -describe("SwiftUI Color", () => { - // @ts-expect-error for some reason, need to override this for figma.mixed to work - global.figma = { - mixed: undefined, - }; - - it("standard set color", () => { - const node = new AltRectangleNode(); - node.fills = [ - { - type: "SOLID", - color: { - r: 0.941, - g: 0.318, - b: 0.22, - }, - opacity: 1.0, - }, - ]; - - expect(swiftuiColorFromFills(node.fills)).toEqual( - "Color(red: 0.94, green: 0.32, blue: 0.22)", - ); - }); - - it("check for black and white on Text", () => { - const node = new AltTextNode(); - node.characters = ""; - node.fills = [ - { - type: "SOLID", - color: { - r: 0.0, - g: 0.0, - b: 0.0, - }, - opacity: 1.0, - }, - ]; - - expect(swiftuiColorFromFills(node.fills)).toEqual("Color.black"); - - node.fills = [ - { - type: "SOLID", - color: { - r: 1.0, - g: 1.0, - b: 1.0, - }, - opacity: 1.0, - }, - ]; - - expect(swiftuiColorFromFills(node.fills)).toEqual("Color.white"); - }); - - it("opacity and visibility changes", () => { - const node = new AltRectangleNode(); - node.fills = [ - { - type: "SOLID", - color: { - r: 0.0, - g: 0.0, - b: 0.0, - }, - opacity: 1.0, - visible: false, - }, - ]; - - expect(swiftuiColorFromFills(node.fills)).toEqual(""); - - node.fills = [ - { - type: "SOLID", - color: { - r: 0.0, - g: 0.0, - b: 0.0, - }, - opacity: undefined, - visible: true, - }, - ]; - - // this scenario should never happen in real life; figma allows undefined to be set, but not to be get. - expect(swiftuiColorFromFills(node.fills)).toEqual("Color.black"); - - node.fills = [ - { - type: "SOLID", - color: { - r: 0.0, - g: 0.0, - b: 0.0, - }, - opacity: 0.0, - visible: true, - }, - ]; - expect(swiftuiColorFromFills(node.fills)).toEqual( - "Color(red: 0, green: 0, blue: 0, opacity: 0)", - ); - }); - - it("Gradient Linear", () => { - const node = new AltRectangleNode(); - const gradientFill: GradientPaint = { - type: "GRADIENT_LINEAR", - gradientTransform: [ - [0, 0, 0], - [0, 0, 0], - ], - gradientStops: [ - { - position: 0, - color: { - r: 0, - g: 0, - b: 0, - a: 1, - }, - }, - ], - }; - - node.fills = [gradientFill]; - - expect(swiftuiColorFromFills(node.fills)).toEqual( - "LinearGradient(gradient: Gradient(colors: [Color.black]), startPoint: .leading, endPoint: .trailing)", - ); - - // topLeft to bottomRight (135) - Object.assign(gradientFill.gradientTransform, [ - [0.8038461208343506, 0.7035384774208069, -0.2932307720184326], - [1.3402682542800903, -1.4652644395828247, 0.5407097935676575], - ]); - expect(swiftuiColorFromFills(node.fills)).toEqual( - "LinearGradient(gradient: Gradient(colors: [Color.black]), startPoint: .topLeading, endPoint: .bottomTrailing)", - ); - - // bottom to top (-90) - Object.assign(gradientFill.gradientTransform, [ - [7.734507789791678e-8, -1.2339448928833008, 1.1376146078109741], - [-2.3507132530212402, -1.0997783306265774e-7, 1.6796307563781738], - ]); - expect(swiftuiColorFromFills(node.fills)).toEqual( - "LinearGradient(gradient: Gradient(colors: [Color.black]), startPoint: .bottom, endPoint: .top)", - ); - - // top to bottom (90) - Object.assign(gradientFill.gradientTransform, [ - [6.851496436866e-8, 2.085271120071411, -0.6976743936538696], - [3.9725232124328613, -1.4210854715202004e-14, -0.8289895057678223], - ]); - expect(swiftuiColorFromFills(node.fills)).toEqual( - "LinearGradient(gradient: Gradient(colors: [Color.black]), startPoint: .top, endPoint: .bottom)", - ); - - // left to right (0) - Object.assign(gradientFill.gradientTransform, [ - [1.845637559890747, 1.9779233184635814e-7, -0.45637592673301697], - [6.030897026221282e-8, -3.364259719848633, 2.188383102416992], - ]); - expect(swiftuiColorFromFills(node.fills)).toEqual( - "LinearGradient(gradient: Gradient(colors: [Color.black]), startPoint: .leading, endPoint: .trailing)", - ); - - // right to left (180) - Object.assign(gradientFill.gradientTransform, [ - [-2.3905811309814453, 0.04066795855760574, 1.707460880279541], - [0.07747448235750198, 4.357592582702637, -1.0299113988876343], - ]); - expect(swiftuiColorFromFills(node.fills)).toEqual( - "LinearGradient(gradient: Gradient(colors: [Color.black]), startPoint: .trailing, endPoint: .leading)", - ); - - // bottom left to top right (-135) - Object.assign(gradientFill.gradientTransform, [ - [-1.2678464651107788, -1.9602917432785034, 1.6415824890136719], - [-3.7344324588775635, 2.3110527992248535, 0.4661891460418701], - ]); - expect(swiftuiColorFromFills(node.fills)).toEqual( - "LinearGradient(gradient: Gradient(colors: [Color.black]), startPoint: .bottomTrailing, endPoint: .topLeading)", - ); - - // bottom left to top right (-45) - Object.assign(gradientFill.gradientTransform, [ - [0.7420053482055664, -0.6850813031196594, 0.4412658214569092], - [-1.3051068782806396, -1.3525396585464478, 1.8345310688018799], - ]); - expect(swiftuiColorFromFills(node.fills)).toEqual( - "LinearGradient(gradient: Gradient(colors: [Color.black]), startPoint: .bottomLeading, endPoint: .topTrailing)", - ); - - // top right to bottom left (-45) - Object.assign(gradientFill.gradientTransform, [ - [-0.7061997652053833, 0.7888921499252319, 0.5180976986885071], - [1.5028705596923828, 1.2872726917266846, -1.0877336263656616], - ]); - expect(swiftuiColorFromFills(node.fills)).toEqual( - "LinearGradient(gradient: Gradient(colors: [Color.black]), startPoint: .topTrailing, endPoint: .bottomLeading)", - ); - }); - - it("Execute Main with Linear Gradient, corners and stroke", () => { - const node = new AltRectangleNode(); - const gradientFill: GradientPaint = { - type: "GRADIENT_LINEAR", - gradientTransform: [ - [0.8038461208343506, 0.7035384774208069, -0.2932307720184326], - [1.3402682542800903, -1.4652644395828247, 0.5407097935676575], - ], - gradientStops: [ - { - position: 0, - color: { - r: 0, - g: 0, - b: 0, - a: 1, - }, - }, - { - position: 1, - color: { - r: 1, - g: 0, - b: 0, - a: 1, - }, - }, - ], - }; - - node.fills = [gradientFill]; - node.width = 10; - node.height = 10; - node.strokeWeight = 4; - node.strokeAlign = "OUTSIDE"; - node.strokes = [ - { - type: "SOLID", - color: { r: 0.25, g: 0.25, b: 0.25 }, - }, - ]; - node.cornerRadius = 16; - - expect(swiftuiMain([node])).toEqual( - `RoundedRectangle(cornerRadius: 16) -.fill(LinearGradient(gradient: Gradient(colors: [Color.black, Color(red: 1, green: 0, blue: 0)]), startPoint: .topLeading, endPoint: .bottomTrailing)) -.frame(width: 18, height: 18) -.overlay(RoundedRectangle(cornerRadius: 16).stroke(Color(red: 0.25, green: 0.25, blue: 0.25), lineWidth: 4))`, - ); - }); - - it("fail with other types", () => { - const node = new AltRectangleNode(); - node.fills = [ - { - type: "IMAGE", - scaleMode: "FILL", - imageHash: null, - }, - ]; - - expect(swiftuiColorFromFills(node.fills)).toEqual( - "Color(red: 0.50, green: 0.23, blue: 0.27, opacity: 0.50)", - ); - }); -}); diff --git a/__tests__/swiftui/builderImpl/swiftuiEffects.test.ts b/__tests__/swiftui/builderImpl/swiftuiEffects.test.ts deleted file mode 100644 index ddcaa078..00000000 --- a/__tests__/swiftui/builderImpl/swiftuiEffects.test.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { AltRectangleNode } from "../../../src/altNodes/altMixins"; -import { - swiftuiShadow, - swiftuiBlur, -} from "./../../../src/swiftui/builderImpl/swiftuiEffects"; -describe("SwiftUI Shadow and Blur", () => { - // @ts-expect-error for some reason, need to override this for figma.mixed to work - global.figma = { - mixed: undefined, - }; - - it("drop shadow", () => { - const node = new AltRectangleNode(); - - // no shadow - expect(swiftuiShadow(node)).toEqual(""); - - // x is zero (default) y is not zero - node.effects = [ - { - type: "DROP_SHADOW", - blendMode: "NORMAL", - color: { r: 0, g: 0, b: 0, a: 0.25 }, - offset: { x: 0, y: 4 }, - radius: 4, - visible: true, - }, - ]; - - expect(swiftuiShadow(node)).toEqual("\n.shadow(radius: 4, y: 4)"); - - // x is not zero y is zero (default) - node.effects = [ - { - type: "DROP_SHADOW", - blendMode: "NORMAL", - color: { r: 0, g: 0, b: 0, a: 0.25 }, - offset: { x: 4, y: 0 }, - radius: 4, - visible: true, - }, - ]; - - expect(swiftuiShadow(node)).toEqual("\n.shadow(radius: 4, x: 4)"); - - // x and y are different and both are not zero (default) - node.effects = [ - { - type: "DROP_SHADOW", - blendMode: "NORMAL", - color: { r: 0, g: 0, b: 0, a: 0.25 }, - offset: { x: 2, y: 4 }, - radius: 4, - visible: true, - }, - ]; - - expect(swiftuiShadow(node)).toEqual("\n.shadow(radius: 4, x: 2, y: 4)"); - - // x and y are the same, but not zero - node.effects = [ - { - type: "DROP_SHADOW", - blendMode: "NORMAL", - color: { r: 0, g: 0, b: 0, a: 1 }, - offset: { x: 4, y: 4 }, - radius: 4, - visible: true, - }, - ]; - - expect(swiftuiShadow(node)).toEqual( - "\n.shadow(color: Color(red: 0, green: 0, blue: 0, opacity: 1), radius: 4)", - ); - }); - - it("blur", () => { - const node = new AltRectangleNode(); - - // no shadow - expect(swiftuiBlur(node)).toEqual(""); - - node.effects = [ - { - type: "LAYER_BLUR", - radius: 4, - visible: true, - }, - ]; - - expect(swiftuiBlur(node)).toEqual("\n.blur(radius: 4)"); - }); - - it("inner shadow (invalid)", () => { - const node = new AltRectangleNode(); - - node.effects = [ - { - blendMode: "NORMAL", - color: { r: 0, g: 0, b: 0, a: 0.25 }, - offset: { x: 0, y: 4 }, - radius: 4, - type: "INNER_SHADOW", - visible: true, - }, - ]; - - expect(swiftuiShadow(node)).toEqual(""); - expect(swiftuiBlur(node)).toEqual(""); - }); -}); diff --git a/__tests__/swiftui/builderImpl/swiftuiPadding.test.ts b/__tests__/swiftui/builderImpl/swiftuiPadding.test.ts deleted file mode 100644 index def3e306..00000000 --- a/__tests__/swiftui/builderImpl/swiftuiPadding.test.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { swiftuiPadding } from "../../../src/swiftui/builderImpl/swiftuiPadding"; -import { - AltRectangleNode, - AltFrameNode, -} from "../../../src/altNodes/altMixins"; - -describe("SwiftUI padding", () => { - it("test all possible variations", () => { - const frameNode = new AltFrameNode(); - expect(swiftuiPadding(frameNode)).toEqual(""); - - frameNode.layoutMode = "NONE"; - expect(swiftuiPadding(frameNode)).toEqual(""); - - frameNode.layoutMode = "VERTICAL"; - - frameNode.paddingLeft = 2; - frameNode.paddingRight = 2; - frameNode.paddingTop = 0; - frameNode.paddingBottom = 0; - expect(swiftuiPadding(frameNode)).toEqual("\n.padding(.horizontal, 2)"); - - frameNode.paddingLeft = 0; - frameNode.paddingRight = 0; - frameNode.paddingTop = 2; - frameNode.paddingBottom = 2; - expect(swiftuiPadding(frameNode)).toEqual("\n.padding(.vertical, 2)"); - - frameNode.paddingLeft = 2; - frameNode.paddingRight = 2; - frameNode.paddingTop = 2; - frameNode.paddingBottom = 2; - expect(swiftuiPadding(frameNode)).toEqual("\n.padding(2)"); - - frameNode.paddingLeft = 0; - frameNode.paddingRight = 0; - frameNode.paddingTop = 0; - frameNode.paddingBottom = 0; - expect(swiftuiPadding(frameNode)).toEqual(""); - - frameNode.paddingLeft = 2; - frameNode.paddingRight = 2; - frameNode.paddingTop = 3; - frameNode.paddingBottom = 2; - expect(swiftuiPadding(frameNode)).toEqual(`\n.padding(.horizontal, 2) -.padding(.top, 3) -.padding(.bottom, 2)`); - - frameNode.paddingLeft = 3; - frameNode.paddingRight = 2; - frameNode.paddingTop = 2; - frameNode.paddingBottom = 2; - expect(swiftuiPadding(frameNode)).toEqual(`\n.padding(.vertical, 2) -.padding(.leading, 3) -.padding(.trailing, 2)`); - - frameNode.paddingLeft = 1; - frameNode.paddingRight = 2; - frameNode.paddingTop = 3; - frameNode.paddingBottom = 4; - expect(swiftuiPadding(frameNode)).toEqual( - `\n.padding(.leading, 1) -.padding(.trailing, 2) -.padding(.top, 3) -.padding(.bottom, 4)`, - ); - - const notFrame = new AltRectangleNode(); - expect(swiftuiPadding(notFrame)).toEqual(""); - }); -}); diff --git a/__tests__/swiftui/builderImpl/swiftuiPosition.test.ts b/__tests__/swiftui/builderImpl/swiftuiPosition.test.ts deleted file mode 100644 index 763c74c2..00000000 --- a/__tests__/swiftui/builderImpl/swiftuiPosition.test.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { swiftuiPosition } from "../../../src/swiftui/builderImpl/swiftuiPosition"; -import { AltFrameNode } from "../../../src/altNodes/altMixins"; - -describe("SwiftUI Position", () => { - // @ts-expect-error for some reason, need to override this for figma.mixed to work - global.figma = { - mixed: undefined, - }; - - it("Frame Absolute Position", () => { - const parent = new AltFrameNode(); - parent.width = 100; - parent.height = 100; - parent.x = 0; - parent.y = 0; - parent.id = "root"; - parent.layoutMode = "NONE"; - parent.isRelative = true; - - const node = new AltFrameNode(); - parent.id = "node"; - node.parent = parent; - node.x = 0; - node.y = 0; - - // child equals parent - node.width = 100; - node.height = 100; - expect(swiftuiPosition(node)).toEqual(""); - - node.width = 25; - node.height = 25; - - const nodeF2 = new AltFrameNode(); - nodeF2.width = 25; - nodeF2.height = 25; - nodeF2.parent = parent; - - parent.children = [node, nodeF2]; - - // position is set after the conversion to avoid AutoLayout auto converison - - // center - node.x = 37; - node.y = 37; - expect(swiftuiPosition(node)).toEqual(""); - - // top-left - node.x = 0; - node.y = 0; - expect(swiftuiPosition(node)).toEqual("\n.offset(x: -37.50, y: -37.50)"); - - // top-right - node.x = 75; - node.y = 0; - expect(swiftuiPosition(node)).toEqual("\n.offset(x: 37.50, y: -37.50)"); - - // bottom-left - node.x = 0; - node.y = 75; - expect(swiftuiPosition(node)).toEqual("\n.offset(x: -37.50, y: 37.50)"); - - // bottom-right - node.x = 75; - node.y = 75; - expect(swiftuiPosition(node)).toEqual("\n.offset(x: 37.50, y: 37.50)"); - - // top-center - node.x = 37; - node.y = 0; - expect(swiftuiPosition(node)).toEqual("\n.offset(x: -0.50, y: -37.50)"); - - // left-center - node.x = 0; - node.y = 37; - expect(swiftuiPosition(node)).toEqual("\n.offset(x: -37.50, y: -0.50)"); - - // bottom-center - node.x = 37; - node.y = 75; - expect(swiftuiPosition(node)).toEqual("\n.offset(x: -0.50, y: 37.50)"); - - // right-center - node.x = 75; - node.y = 37; - expect(swiftuiPosition(node)).toEqual("\n.offset(x: 37.50, y: -0.50)"); - - // center Y, random X - node.x = 22; - node.y = 37; - expect(swiftuiPosition(node)).toEqual("\n.offset(x: -15.50, y: -0.50)"); - - // center X, random Y - node.x = 37; - node.y = 22; - expect(swiftuiPosition(node)).toEqual("\n.offset(x: -0.50, y: -15.50)"); - - // without position - node.x = 45; - node.y = 88; - expect(swiftuiPosition(node)).toEqual("\n.offset(x: 7.50, y: 50.50)"); - }); - - it("Position: node has same size as parent", () => { - const parent = new AltFrameNode(); - parent.width = 100; - parent.height = 100; - parent.layoutMode = "NONE"; - - const node = new AltFrameNode(); - node.width = 100; - node.height = 100; - node.parent = parent; - - const nodeF2 = new AltFrameNode(); - nodeF2.width = 100; - nodeF2.height = 100; - nodeF2.parent = parent; - - parent.children = [node, nodeF2]; - - expect(swiftuiPosition(node, "")).toEqual(""); - }); - - it("No position when parent is root", () => { - const node = new AltFrameNode(); - node.layoutMode = "NONE"; - - const parent = new AltFrameNode(); - parent.id = "root"; - parent.layoutMode = "NONE"; - - node.parent = parent; - - expect(swiftuiPosition(node, parent.id)).toEqual(""); - }); -}); diff --git a/__tests__/swiftui/builderImpl/swiftuiSize.test.ts b/__tests__/swiftui/builderImpl/swiftuiSize.test.ts deleted file mode 100644 index 3d66c34b..00000000 --- a/__tests__/swiftui/builderImpl/swiftuiSize.test.ts +++ /dev/null @@ -1,260 +0,0 @@ -import { - AltRectangleNode, - AltFrameNode, -} from "../../../src/altNodes/altMixins"; -import { swiftuiSize } from "../../../src/swiftui/builderImpl/swiftuiSize"; - -describe("swiftui Builder", () => { - // @ts-expect-error for some reason, need to override this for figma.mixed to work - global.figma = { - mixed: undefined, - }; - - it("size for a rectangle", () => { - const node = new AltRectangleNode(); - - node.width = 16; - node.height = 16; - expect(swiftuiSize(node)).toEqual(["width: 16", "height: 16"]); - }); - - it("STRETCH inside AutoLayout", () => { - const node = new AltFrameNode(); - node.layoutMode = "HORIZONTAL"; - node.counterAxisSizingMode = "FIXED"; - node.primaryAxisSizingMode = "FIXED"; - node.paddingLeft = 0; - node.paddingRight = 0; - node.paddingTop = 0; - node.paddingBottom = 0; - node.width = 100; - node.height = 100; - - const child = new AltRectangleNode(); - child.layoutAlign = "STRETCH"; - child.layoutGrow = 1; - child.width = 100; - child.height = 100; - - child.parent = node; - node.children = [child]; - - expect(swiftuiSize(child)).toEqual([ - "maxWidth: .infinity", - "maxHeight: .infinity", - ]); - - child.layoutGrow = 0; - expect(swiftuiSize(child)).toEqual([ - "maxWidth: 100", - "maxHeight: .infinity", - ]); - - child.layoutGrow = 1; - child.layoutAlign = "INHERIT"; - expect(swiftuiSize(child)).toEqual([ - "maxWidth: .infinity", - "maxHeight: 100", - ]); - - // fail - node.layoutMode = "VERTICAL"; - child.layoutAlign = "INHERIT"; - child.layoutGrow = 0; - child.width = 16; - child.height = 16; - expect(swiftuiSize(child)).toEqual(["width: 16", "height: 16"]); - - // child is relative, therefore it must have a value - expect(swiftuiSize(node)).toEqual(["width: 100", "height: 100"]); - }); - - it("Vertical layout with FIXED counterAxis", () => { - const node = new AltFrameNode(); - node.layoutMode = "VERTICAL"; - node.counterAxisSizingMode = "FIXED"; - node.width = 16; - node.height = 16; - - const child = new AltRectangleNode(); - child.width = 8; - child.height = 8; - - child.parent = node; - node.children = [child]; - - expect(swiftuiSize(node)).toEqual(["width: 16", ""]); - }); - - it("Children are rectangles, size shouldn't be relative", () => { - const node = new AltFrameNode(); - node.layoutMode = "NONE"; - node.width = 48; - node.height = 48; - node.children = [new AltRectangleNode(), new AltRectangleNode()]; - - expect(swiftuiSize(node)).toEqual(["width: 48", "height: 48"]); - }); - - it("counterAxisSizingMode is FIXED", () => { - const node = new AltFrameNode(); - node.counterAxisSizingMode = "FIXED"; - node.width = 48; - node.height = 48; - node.children = [new AltRectangleNode(), new AltRectangleNode()]; - - node.layoutMode = "HORIZONTAL"; - expect(swiftuiSize(node)).toEqual(["", "height: 48"]); - - node.layoutMode = "VERTICAL"; - expect(swiftuiSize(node)).toEqual(["width: 48", ""]); - - node.layoutMode = "NONE"; - expect(swiftuiSize(node)).toEqual(["width: 48", "height: 48"]); - }); - - it("counterAxisSizingMode is AUTO", () => { - const node = new AltFrameNode(); - node.layoutMode = "HORIZONTAL"; - node.counterAxisSizingMode = "FIXED"; - node.primaryAxisSizingMode = "FIXED"; - node.x = 0; - node.y = 0; - node.width = 48; - node.height = 48; - node.children = [new AltRectangleNode(), new AltRectangleNode()]; - - expect(swiftuiSize(node)).toEqual(["width: 48", "height: 48"]); - - // responsive - const parentNode = new AltFrameNode(); - parentNode.counterAxisSizingMode = "AUTO"; - parentNode.primaryAxisSizingMode = "AUTO"; - parentNode.x = 0; - parentNode.y = 0; - parentNode.width = 48; - parentNode.height = 48; - parentNode.children = [node]; - node.parent = parentNode; - - expect(swiftuiSize(node)).toEqual(["width: 48", "height: 48"]); - expect(swiftuiSize(parentNode)).toEqual(["width: 48", "height: 48"]); - }); - - it("width changes when there are strokes", () => { - const node = new AltRectangleNode(); - node.x = 0; - node.y = 0; - node.width = 8; - node.height = 8; - - expect(swiftuiSize(node)).toEqual(["width: 8", "height: 8"]); - - node.strokeWeight = 4; - node.strokes = [ - { - type: "SOLID", - color: { r: 0.25, g: 0.25, b: 0.25 }, - }, - ]; - - node.strokeAlign = "CENTER"; - expect(swiftuiSize(node)).toEqual(["width: 12", "height: 12"]); - - node.strokeAlign = "OUTSIDE"; - expect(swiftuiSize(node)).toEqual(["width: 16", "height: 16"]); - }); - - it("adjust parent if children's size + stroke > parent size", () => { - const parentNode = new AltFrameNode(); - parentNode.width = 8; - parentNode.height = 8; - - const node = new AltRectangleNode(); - node.width = 8; - node.height = 8; - - node.strokeWeight = 4; - node.strokeAlign = "CENTER"; - node.strokes = [ - { - type: "SOLID", - color: { r: 0.25, g: 0.25, b: 0.25 }, - }, - ]; - - expect(swiftuiSize(node)).toEqual(["width: 12", "height: 12"]); - - parentNode.children = [node]; - node.parent = parentNode; - expect(swiftuiSize(parentNode)).toEqual(["width: 12", "height: 12"]); - - node.strokeAlign = "OUTSIDE"; - expect(swiftuiSize(parentNode)).toEqual(["width: 16", "height: 16"]); - }); - - it("all branches with children's size + stroke < children's size", () => { - const parentNode = new AltFrameNode(); - parentNode.width = 8; - parentNode.height = 8; - - const node = new AltRectangleNode(); - node.width = 4; - node.height = 4; - - node.strokeWeight = 2; - node.strokeAlign = "CENTER"; - node.strokes = [ - { - type: "SOLID", - color: { r: 0.25, g: 0.25, b: 0.25 }, - }, - ]; - - expect(swiftuiSize(parentNode)).toEqual(["width: 8", "height: 8"]); - - parentNode.children = [node]; - node.parent = parentNode; - expect(swiftuiSize(parentNode)).toEqual(["width: 8", "height: 8"]); - - node.strokeAlign = "OUTSIDE"; - expect(swiftuiSize(parentNode)).toEqual(["width: 8", "height: 8"]); - - node.strokeAlign = "INSIDE"; - expect(swiftuiSize(parentNode)).toEqual(["width: 8", "height: 8"]); - }); - - it("full width when width is same to the parent", () => { - const node = new AltFrameNode(); - node.width = 12; - node.height = 12; - - const parentNode = new AltFrameNode(); - parentNode.layoutMode = "NONE"; - parentNode.width = 12; - parentNode.height = 12; - parentNode.children = [node]; - node.parent = parentNode; - - expect(swiftuiSize(parentNode)).toEqual(["width: 12", "height: 12"]); - expect(swiftuiSize(node)).toEqual(["width: 12", "height: 12"]); - }); - - it("set the width to max if the view is near the corner", () => { - const node = new AltFrameNode(); - node.width = 100; - node.height = 100; - node.x = 0; - node.y = 0; - - const parentNode = new AltFrameNode(); - parentNode.layoutMode = "NONE"; - parentNode.width = 120; - parentNode.height = 120; - - parentNode.children = [node]; - node.parent = parentNode; - - expect(swiftuiSize(node)).toEqual(["width: 100", "height: 100"]); - }); -}); diff --git a/__tests__/swiftui/swiftuiMain.test.ts b/__tests__/swiftui/swiftuiMain.test.ts deleted file mode 100644 index be9351ed..00000000 --- a/__tests__/swiftui/swiftuiMain.test.ts +++ /dev/null @@ -1,373 +0,0 @@ -import { convertToAutoLayout } from "../../src/altNodes/convertToAutoLayout"; -import { - AltRectangleNode, - AltFrameNode, - AltGroupNode, - AltEllipseNode, - AltTextNode, -} from "../../src/altNodes/altMixins"; -import { swiftuiMain } from "./../../src/swiftui/swiftuiMain"; - -describe("SwiftUI Main", () => { - // @ts-expect-error for some reason, need to override this for figma.mixed to work - global.figma = { - mixed: undefined, - }; - it("Standard flow", () => { - const node = new AltFrameNode(); - node.width = 32; - node.height = 32; - node.x = 0; - node.y = 0; - node.name = "FRAME"; - node.layoutMode = "NONE"; - node.counterAxisSizingMode = "FIXED"; - - const child1 = new AltRectangleNode(); - child1.width = 4; - child1.height = 4; - child1.x = 9; - child1.y = 9; - child1.name = "RECT1"; - child1.fills = [ - { - type: "SOLID", - color: { - r: 1, - g: 1, - b: 1, - }, - }, - ]; - - const child2 = new AltRectangleNode(); - child2.width = 4; - child2.height = 4; - child2.x = 9; - child2.y = 9; - child2.name = "RECT2"; - - // this works as a test for JSX, but should never happen in reality. In reality Frame would need to have 2 children and be relative. - node.children = [child1, child2]; - child1.parent = node; - child2.parent = node; - - expect(swiftuiMain([convertToAutoLayout(node)])).toEqual(`ZStack { - Rectangle() - .fill(Color.white) - .offset(x: -5, y: -5) - .frame(width: 4, height: 4) - - Rectangle() - .offset(x: -5, y: -5) - .frame(width: 4, height: 4) -} -.frame(width: 32, height: 32)`); - }); - it("Group with relative position", () => { - // this also should neve happen in reality, because Group must have the same size as the children. - - const node = new AltGroupNode(); - node.width = 32; - node.height = 32; - node.x = 0; - node.y = 0; - node.name = "GROUP"; - node.isRelative = true; - - const child = new AltRectangleNode(); - child.width = 4; - child.height = 4; - child.x = 9; - child.y = 9; - child.name = "RECT"; - child.fills = [ - { - type: "SOLID", - color: { - r: 1, - g: 1, - b: 1, - }, - }, - ]; - - node.children = [child]; - child.parent = node; - expect(swiftuiMain([node])).toEqual(`ZStack { -Rectangle() -.fill(Color.white) -.offset(x: -5, y: -5) -.frame(width: 4, height: 4) -} -.frame(width: 32, height: 32)`); - }); - - it("Row and Column with 2 children", () => { - // this also should neve happen in reality, because Group must have the same size as the children. - - const node = new AltFrameNode(); - node.width = 32; - node.height = 8; - node.x = 0; - node.y = 0; - node.layoutMode = "VERTICAL"; - node.primaryAxisAlignItems = "MAX"; - node.counterAxisAlignItems = "MAX"; - node.counterAxisSizingMode = "AUTO"; - node.primaryAxisSizingMode = "AUTO"; - node.itemSpacing = 8; - - const child1 = new AltRectangleNode(); - child1.width = 8; - child1.height = 8; - child1.x = 0; - child1.y = 0; - child1.layoutAlign = "INHERIT"; - child1.fills = [ - { - type: "SOLID", - color: { - r: 1, - g: 1, - b: 1, - }, - }, - ]; - - const child2 = new AltRectangleNode(); - child2.width = 8; - child2.height = 8; - child2.x = 16; - child2.y = 0; - child2.layoutAlign = "INHERIT"; - child2.fills = [ - { - type: "SOLID", - color: { - r: 0, - g: 0, - b: 0, - }, - }, - ]; - - node.children = [child1, child2]; - child1.parent = node; - child2.parent = node; - - expect(swiftuiMain([node])) - .toEqual(`VStack(alignment: .trailing, spacing: 8) { - Rectangle() - .fill(Color.white) - .frame(width: 8, height: 8) - - Rectangle() - .fill(Color.black) - .frame(width: 8, height: 8) -}`); - - // variations in layoutAlign for test coverage - node.primaryAxisAlignItems = "CENTER"; - node.counterAxisAlignItems = "CENTER"; - node.itemSpacing = 16; - - expect(swiftuiMain([node])).toEqual(`VStack() { - Rectangle() - .fill(Color.white) - .frame(width: 8, height: 8) - - Rectangle() - .fill(Color.black) - .frame(width: 8, height: 8) -}`); - - // variations in layoutAlign and spacing for coverage - node.primaryAxisAlignItems = "MIN"; - node.counterAxisAlignItems = "MIN"; - node.itemSpacing = 0; - node.fills = [ - { - type: "SOLID", - color: { r: 0, g: 0, b: 0 }, - opacity: 1, - }, - ]; - - expect(swiftuiMain([node])) - .toEqual(`VStack(alignment: .leading, spacing: 0) { - Rectangle() - .fill(Color.white) - .frame(width: 8, height: 8) - - Rectangle() - .fill(Color.black) - .frame(width: 8, height: 8) -} -.background(Color.black)`); - - // change orientation - node.layoutMode = "HORIZONTAL"; - node.primaryAxisAlignItems = "MIN"; - node.counterAxisAlignItems = "MIN"; - - expect(swiftuiMain([node])).toEqual(`HStack(alignment: .top, spacing: 0) { - Rectangle() - .fill(Color.white) - .frame(width: 8, height: 8) - - Rectangle() - .fill(Color.black) - .frame(width: 8, height: 8) -} -.background(Color.black)`); - - node.primaryAxisAlignItems = "CENTER"; - node.counterAxisAlignItems = "CENTER"; - - expect(swiftuiMain([node])).toEqual(`HStack(spacing: 0) { - Rectangle() - .fill(Color.white) - .frame(width: 8, height: 8) - - Rectangle() - .fill(Color.black) - .frame(width: 8, height: 8) -} -.background(Color.black)`); - - node.primaryAxisAlignItems = "MAX"; - node.counterAxisAlignItems = "MAX"; - - expect(swiftuiMain([node])) - .toEqual(`HStack(alignment: .bottom, spacing: 0) { - Rectangle() - .fill(Color.white) - .frame(width: 8, height: 8) - - Rectangle() - .fill(Color.black) - .frame(width: 8, height: 8) -} -.background(Color.black)`); - }); - - it("Row with 1 children", () => { - // this also should neve happen in reality, because Group must have the same size as the children. - - const node = new AltFrameNode(); - node.width = 32; - node.height = 8; - node.x = 0; - node.y = 0; - node.layoutMode = "HORIZONTAL"; - node.counterAxisSizingMode = "AUTO"; - node.itemSpacing = 8; - node.paddingLeft = 8; - node.paddingRight = 8; - node.paddingTop = 8; - node.paddingBottom = 8; - - const child1 = new AltRectangleNode(); - child1.width = 8; - child1.height = 8; - child1.x = 0; - child1.y = 0; - child1.cornerRadius = 8; - child1.fills = [ - { - type: "SOLID", - color: { - r: 1, - g: 1, - b: 1, - }, - }, - ]; - - node.children = [child1]; - child1.parent = node; - - expect(swiftuiMain([node])).toEqual(`RoundedRectangle(cornerRadius: 8) -.fill(Color.white) -.frame(width: 8, height: 8) -.padding(8)`); - }); - - it("101 items", () => { - const parent = new AltFrameNode(); - parent.layoutMode = "NONE"; - - const node = new AltRectangleNode(); - node.width = 20; - node.height = 20; - - parent.children = []; - for (let i = 0; i < 101; i++) { - parent.children.push(node); - } - - const conversion = swiftuiMain([parent]); - // detect if there are the slashes of the comment that only appears at > 100. - expect(conversion.match("//") !== null).toBe(true); - - // check the length. It is supposed to be long. - expect(conversion).toHaveLength(5429); - - // todo count the number of "Groups {" - }); - - it("ellipse with no size", () => { - const node = new AltEllipseNode(); - - // undefined (unitialized, only happen on tests) - expect(swiftuiMain([node])).toEqual("Ellipse()"); - - node.width = 0; - node.height = 10; - expect(swiftuiMain([node])).toEqual(""); - - node.width = 10; - node.height = 0; - expect(swiftuiMain([node])).toEqual(""); - }); - - it("Frame with round corners", () => { - const node = new AltFrameNode(); - node.width = 20; - node.height = 20; - node.layoutMode = "NONE"; - node.cornerRadius = 20; - node.fills = [ - { - type: "SOLID", - color: { - r: 0.0, - g: 0.0, - b: 0.0, - }, - opacity: 1.0, - visible: true, - }, - ]; - - const child = new AltTextNode(); - child.characters = ""; - child.width = 10; - child.height = 10; - child.x = 0; - child.y = 0; - child.parent = node; - - node.children = [child]; - - // undefined (unitialized, only happen on tests) - expect(swiftuiMain([convertToAutoLayout(node)])).toEqual(`Text("") -.frame(width: 10) -.padding(.trailing, 10) -.padding(.bottom, 10) -.frame(width: 20, height: 20) -.background(Color.black) -.cornerRadius(20)`); - }); -}); diff --git a/__tests__/swiftui/swiftuiText.test.ts b/__tests__/swiftui/swiftuiText.test.ts deleted file mode 100644 index 34bf8cbf..00000000 --- a/__tests__/swiftui/swiftuiText.test.ts +++ /dev/null @@ -1,255 +0,0 @@ -import { swiftuiMain } from "../../src/swiftui/swiftuiMain"; -import { AltTextNode } from "../../src/altNodes/altMixins"; -import { SwiftuiTextBuilder } from "../../src/swiftui/swiftuiTextBuilder"; -import { - swiftuiFontMatcher, - swiftuiWeightMatcher, -} from "../../src/swiftui/builderImpl/swiftuiTextWeight"; - -describe("SwiftUI Text", () => { - // @ts-expect-error for some reason, need to override this for figma.mixed to work - global.figma = { - mixed: undefined, - }; - it("textAlignHorizontal and textAlignVertical", () => { - const node = new AltTextNode(); - node.characters = ""; - node.width = 16; - node.height = 16; - - node.characters = ""; - node.textAutoResize = "HEIGHT"; - - node.textAlignHorizontal = "LEFT"; - node.textAlignVertical = "TOP"; - expect(swiftuiMain([node])).toEqual(`Text("") -.frame(width: 16, alignment: .topLeading)`); - - node.textAlignHorizontal = "CENTER"; - node.textAlignVertical = "TOP"; - expect(swiftuiMain([node])).toEqual(`Text("") -.multilineTextAlignment(.center) -.frame(width: 16, alignment: .top)`); - - node.textAlignHorizontal = "RIGHT"; - node.textAlignVertical = "BOTTOM"; - expect(swiftuiMain([node])).toEqual(`Text("") -.multilineTextAlignment(.trailing) -.frame(width: 16, alignment: .bottomTrailing)`); - - node.textAlignHorizontal = "CENTER"; - node.textAlignVertical = "CENTER"; - expect(swiftuiMain([node])).toEqual( - `Text("") -.multilineTextAlignment(.center) -.frame(width: 16)`, - ); - }); - - // it("fontName", () => { - // const node = new AltTextNode(); - // node.characters = ""; - // node.width = 16; - // node.height = 16; - // node.textAutoResize = "WIDTH_AND_HEIGHT"; - - // node.fontName = { - // family: "inter", - // style: "bold", - // }; - // expect(swiftuiMain([node])).toEqual(`Text("") - // .fontWeight(.bold)`); - - // node.fontName = { - // family: "inter", - // style: "medium italic", - // }; - // expect(swiftuiMain([node])).toEqual('

'); - - // node.fontName = { - // family: "inter", - // style: "regular", - // }; - // expect(swiftuiMain([node])).toEqual("

"); - // }); - - it("letterSpacing/tracking", () => { - const node = new AltTextNode(); - node.characters = ""; - node.width = 16; - node.height = 16; - node.textAutoResize = "WIDTH_AND_HEIGHT"; - node.fontSize = 10; - - node.letterSpacing = { - value: 110, - unit: "PERCENT", - }; - expect(swiftuiMain([node])).toEqual( - 'Text("")\n.font(.caption2)\n.tracking(11)', - ); - - node.letterSpacing = { - value: 10, - unit: "PIXELS", - }; - expect(swiftuiMain([node])).toEqual( - 'Text("")\n.font(.caption2)\n.tracking(10)', - ); - }); - - it("lineHeight/lineSpacing", () => { - const node = new AltTextNode(); - node.characters = ""; - node.width = 16; - node.height = 16; - node.textAutoResize = "WIDTH_AND_HEIGHT"; - node.fontSize = 24; - - node.lineHeight = { - value: 110, - unit: "PERCENT", - }; - expect(swiftuiMain([node])).toEqual( - 'Text("")\n.font(.title)\n.lineSpacing(26.40)', - ); - - node.lineHeight = { - value: 10, - unit: "PIXELS", - }; - expect(swiftuiMain([node])).toEqual( - 'Text("")\n.font(.title)\n.lineSpacing(10)', - ); - }); - - it("textCase", () => { - const node = new AltTextNode(); - node.characters = "ThInK dIfFeReNt"; - - node.textCase = "LOWER"; - expect(swiftuiMain([node])).toEqual('Text("think different")'); - - node.textCase = "TITLE"; - // todo solve this - expect(swiftuiMain([node])).toEqual('Text("ThInK dIfFeReNt")'); - - node.textCase = "UPPER"; - expect(swiftuiMain([node])).toEqual('Text("THINK DIFFERENT")'); - - node.textCase = "ORIGINAL"; - expect(swiftuiMain([node])).toEqual('Text("ThInK dIfFeReNt")'); - }); - - it("textDecoration", () => { - const node = new AltTextNode(); - node.characters = ""; - - node.textDecoration = "NONE"; - expect(swiftuiMain([node])).toEqual('Text("")'); - - node.textDecoration = "STRIKETHROUGH"; - expect(swiftuiMain([node])).toEqual(`Text("")\n.strikethrough()`); - - node.textDecoration = "UNDERLINE"; - expect(swiftuiMain([node])).toEqual(`Text("")\n.underline()`); - - node.textDecoration = "NONE"; - node.fontName = { - family: "inter", - style: "medium italic", - }; - expect(swiftuiMain([node])).toEqual(`Text("")\n.italic()`); - }); - - it("more complex examples", () => { - const node = new AltTextNode(); - node.width = 100; - node.height = 100; - node.characters = ""; - node.fontSize = 12; - node.fontName = { - family: "inter", - style: "bold", - }; - node.textAlignVertical = "CENTER"; - node.textAlignHorizontal = "RIGHT"; - node.textAutoResize = "NONE"; - - expect(swiftuiMain([node])).toEqual(`Text("") -.fontWeight(.bold) -.font(.caption) -.multilineTextAlignment(.trailing) -.frame(width: 100, height: 100, alignment: .trailing)`); - - node.textAlignHorizontal = "CENTER"; - node.fontName = figma.mixed; - - expect(swiftuiMain([node])).toEqual(`Text("") -.font(.caption) -.multilineTextAlignment(.center) -.frame(width: 100, height: 100)`); - - node.characters = "a\nb\nc"; - node.textAutoResize = "WIDTH_AND_HEIGHT"; - expect(swiftuiMain([node])).toEqual('Text("a\\nb\\nc")\n.font(.caption)'); - }); - - it("swiftuiFontMatcher", () => { - const node = new AltTextNode(); - node.characters = ""; - - node.fontSize = figma.mixed; - expect(swiftuiFontMatcher(node)).toEqual(""); - - node.fontSize = 11; - expect(swiftuiFontMatcher(node)).toEqual(".caption2"); - - node.fontSize = 12; - expect(swiftuiFontMatcher(node)).toEqual(`.caption`); - - node.fontSize = 13; - expect(swiftuiFontMatcher(node)).toEqual(`.footnote`); - - node.fontSize = 15; - expect(swiftuiFontMatcher(node)).toEqual(`.subheadline`); - - node.fontSize = 16; - expect(swiftuiFontMatcher(node)).toEqual(`.callout`); - - node.fontSize = 17; - expect(swiftuiFontMatcher(node)).toEqual(`.body`); - - node.fontSize = 20; - expect(swiftuiFontMatcher(node)).toEqual(`.title3`); - - node.fontSize = 22; - expect(swiftuiFontMatcher(node)).toEqual(`.title2`); - - node.fontSize = 28; - expect(swiftuiFontMatcher(node)).toEqual(`.title`); - - node.fontSize = 34; - expect(swiftuiFontMatcher(node)).toEqual(`.largeTitle`); - }); - - it("swiftuiWeightMatcher", () => { - expect(swiftuiWeightMatcher("100")).toEqual(".ultraLight"); - expect(swiftuiWeightMatcher("200")).toEqual(".thin"); - expect(swiftuiWeightMatcher("300")).toEqual(".light"); - expect(swiftuiWeightMatcher("400")).toEqual(".regular"); - expect(swiftuiWeightMatcher("500")).toEqual(".medium"); - expect(swiftuiWeightMatcher("600")).toEqual(".semibold"); - expect(swiftuiWeightMatcher("700")).toEqual(".bold"); - expect(swiftuiWeightMatcher("800")).toEqual(".heavy"); - expect(swiftuiWeightMatcher("900")).toEqual(".black"); - }); - it("reset", () => { - const node = new AltTextNode(); - node.characters = ""; - - const builder = new SwiftuiTextBuilder(); - builder.reset(); - expect(builder.build()).toEqual(""); - }); -}); diff --git a/__tests__/tailwind/builderImpl/tailwindBlend.test.ts b/__tests__/tailwind/builderImpl/tailwindBlend.test.ts deleted file mode 100644 index 0636349b..00000000 --- a/__tests__/tailwind/builderImpl/tailwindBlend.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { - tailwindVisibility, - tailwindOpacity, - tailwindRotation, -} from "../../../src/tailwind/builderImpl/tailwindBlend"; -import { AltRectangleNode } from "../../../src/altNodes/altMixins"; - -describe("Tailwind Blend", () => { - const node = new AltRectangleNode(); - - it("opacity", () => { - node.opacity = 0.1; - expect(tailwindOpacity(node)).toEqual("opacity-10 "); - - node.opacity = 0.3; - expect(tailwindOpacity(node)).toEqual("opacity-30 "); - - node.opacity = 0.45; - expect(tailwindOpacity(node)).toEqual("opacity-40 "); - - node.opacity = 0.65; - expect(tailwindOpacity(node)).toEqual("opacity-60 "); - - node.opacity = 0.95; - expect(tailwindOpacity(node)).toEqual("opacity-95 "); - }); - - it("visibility", () => { - // undefined (unitialized, only happen on tests) - expect(tailwindVisibility(node)).toEqual(""); - - node.visible = false; - expect(tailwindVisibility(node)).toEqual("invisible "); - - node.visible = true; - expect(tailwindVisibility(node)).toEqual(""); - }); - - it("rotation", () => { - // avoid rounding errors - node.rotation = -7.0167096047110005e-15; - expect(tailwindRotation(node)).toEqual(""); - - node.rotation = 45; - expect(tailwindRotation(node)).toEqual("transform rotate-45 "); - - node.rotation = 90; - expect(tailwindRotation(node)).toEqual("transform rotate-90 "); - - node.rotation = 180; - expect(tailwindRotation(node)).toEqual("transform rotate-180 "); - - node.rotation = -45; - expect(tailwindRotation(node)).toEqual("transform -rotate-45 "); - - node.rotation = -90; - expect(tailwindRotation(node)).toEqual("transform -rotate-90 "); - - node.rotation = -180; - expect(tailwindRotation(node)).toEqual("transform -rotate-180 "); - }); -}); diff --git a/__tests__/tailwind/builderImpl/tailwindBorder.test.ts b/__tests__/tailwind/builderImpl/tailwindBorder.test.ts deleted file mode 100644 index b88e9adf..00000000 --- a/__tests__/tailwind/builderImpl/tailwindBorder.test.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { - AltRectangleNode, - AltTextNode, - AltEllipseNode, -} from "../../../src/altNodes/altMixins"; -import { - tailwindBorderWidth, - tailwindBorderRadius, -} from "../../../src/tailwind/builderImpl/tailwindBorder"; -describe("Tailwind Border", () => { - // @ts-expect-error for some reason, need to override this for figma.mixed to work - global.figma = { - mixed: undefined, - }; - - const node = new AltRectangleNode(); - node.topRightRadius = 0; - node.bottomLeftRadius = 0; - node.bottomRightRadius = 0; - - node.strokes = [ - { - type: "SOLID", - color: { r: 0, g: 0, b: 0 }, - }, - ]; - it("borderWidth", () => { - node.strokeWeight = 1; - expect(tailwindBorderWidth(node)).toEqual("border "); - - node.strokeWeight = 2; - expect(tailwindBorderWidth(node)).toEqual("border-2 "); - - node.strokeWeight = 4; - expect(tailwindBorderWidth(node)).toEqual("border-4 "); - - node.strokeWeight = 8; - expect(tailwindBorderWidth(node)).toEqual("border-8 "); - - // random large value to show the limit - node.strokeWeight = 22; - expect(tailwindBorderWidth(node)).toEqual("border-8 "); - }); - - it("standard corner radius", () => { - node.cornerRadius = 0; - expect(tailwindBorderRadius(node)).toEqual(""); - - node.height = 90; - node.cornerRadius = 45; - expect(tailwindBorderRadius(node)).toEqual("rounded-full "); - - node.topLeftRadius = 0; - node.cornerRadius = 0; - expect(tailwindBorderRadius(node)).toEqual(""); - - node.cornerRadius = 10; - expect(tailwindBorderRadius(node)).toEqual("rounded-lg "); - }); - - it("custom corner radius", () => { - node.cornerRadius = figma.mixed; - node.topLeftRadius = 4; - expect(tailwindBorderRadius(node)).toEqual("rounded-tl "); - - node.topLeftRadius = 0; - node.topRightRadius = 4; - expect(tailwindBorderRadius(node)).toEqual("rounded-tr "); - - node.topRightRadius = 0; - node.bottomLeftRadius = 4; - expect(tailwindBorderRadius(node)).toEqual("rounded-bl "); - - node.bottomLeftRadius = 0; - node.bottomRightRadius = 4; - expect(tailwindBorderRadius(node)).toEqual("rounded-br "); - }); - - it("other nodes", () => { - // Ellipses are always round - expect(tailwindBorderRadius(new AltEllipseNode())).toEqual("rounded-full "); - - // Text is unsupported - expect(tailwindBorderRadius(new AltTextNode())).toEqual(""); - }); -}); diff --git a/__tests__/tailwind/builderImpl/tailwindColor.test.ts b/__tests__/tailwind/builderImpl/tailwindColor.test.ts deleted file mode 100644 index 1a04725e..00000000 --- a/__tests__/tailwind/builderImpl/tailwindColor.test.ts +++ /dev/null @@ -1,306 +0,0 @@ -import { - tailwindColorFromFills, - tailwindGradientFromFills, -} from "../../../src/tailwind/builderImpl/tailwindColor"; -import { AltRectangleNode, AltTextNode } from "../../../src/altNodes/altMixins"; -import { tailwindMain } from "./../../../src/tailwind/tailwindMain"; -describe("Tailwind Color", () => { - // @ts-expect-error for some reason, need to override this for figma.mixed to work - global.figma = { - mixed: undefined, - }; - - it("no text color when black", () => { - const node = new AltTextNode(); - node.characters = ""; - node.fills = [ - { - type: "SOLID", - color: { - r: 0.0, - g: 0.0, - b: 0.0, - }, - opacity: 1.0, - }, - ]; - - expect(tailwindColorFromFills(node.fills, "text")).toEqual(""); - }); - - it("opacity and visibility changes", () => { - const node = new AltRectangleNode(); - node.fills = [ - { - type: "SOLID", - color: { - r: 0.0, - g: 0.0, - b: 0.0, - }, - opacity: 1.0, - visible: false, - }, - ]; - - expect(tailwindColorFromFills(node.fills, "")).toEqual(""); - - node.fills = [ - { - type: "SOLID", - color: { - r: 0.0, - g: 0.0, - b: 0.0, - }, - opacity: 0.0, - visible: true, - }, - ]; - expect(tailwindColorFromFills(node.fills, "bg")).toEqual( - "bg-black bg-opacity-0 ", - ); - }); - - it("Gradient Linear", () => { - const node = new AltRectangleNode(); - const gradientFill: GradientPaint = { - type: "GRADIENT_LINEAR", - gradientTransform: [ - [0, 0, 0], - [0, 0, 0], - ], - gradientStops: [ - { - position: 0, - color: { - r: 0, - g: 0, - b: 0, - a: 1, - }, - }, - ], - }; - - node.fills = [gradientFill]; - - expect(tailwindGradientFromFills(node.fills)).toEqual( - "bg-gradient-to-r from-black ", - ); - - // topLeft to bottomRight (135) - Object.assign(gradientFill.gradientTransform, [ - [0.8038461208343506, 0.7035384774208069, -0.2932307720184326], - [1.3402682542800903, -1.4652644395828247, 0.5407097935676575], - ]); - expect(tailwindGradientFromFills(node.fills)).toEqual( - "bg-gradient-to-br from-black ", - ); - - // bottom to top (-90) - Object.assign(gradientFill.gradientTransform, [ - [7.734507789791678e-8, -1.2339448928833008, 1.1376146078109741], - [-2.3507132530212402, -1.0997783306265774e-7, 1.6796307563781738], - ]); - expect(tailwindGradientFromFills(node.fills)).toEqual( - "bg-gradient-to-t from-black ", - ); - - // top to bottom (90) - Object.assign(gradientFill.gradientTransform, [ - [6.851496436866e-8, 2.085271120071411, -0.6976743936538696], - [3.9725232124328613, -1.4210854715202004e-14, -0.8289895057678223], - ]); - expect(tailwindGradientFromFills(node.fills)).toEqual( - "bg-gradient-to-b from-black ", - ); - - // left to right (0) - Object.assign(gradientFill.gradientTransform, [ - [1.845637559890747, 1.9779233184635814e-7, -0.45637592673301697], - [6.030897026221282e-8, -3.364259719848633, 2.188383102416992], - ]); - expect(tailwindGradientFromFills(node.fills)).toEqual( - "bg-gradient-to-r from-black ", - ); - - // right to left (180) - Object.assign(gradientFill.gradientTransform, [ - [-2.3905811309814453, 0.04066795855760574, 1.707460880279541], - [0.07747448235750198, 4.357592582702637, -1.0299113988876343], - ]); - expect(tailwindGradientFromFills(node.fills)).toEqual( - "bg-gradient-to-l from-black ", - ); - - // bottom left to top right (-135) - Object.assign(gradientFill.gradientTransform, [ - [-1.2678464651107788, -1.9602917432785034, 1.6415824890136719], - [-3.7344324588775635, 2.3110527992248535, 0.4661891460418701], - ]); - expect(tailwindGradientFromFills(node.fills)).toEqual( - "bg-gradient-to-tl from-black ", - ); - - // bottom left to top right (-45) - Object.assign(gradientFill.gradientTransform, [ - [0.7420053482055664, -0.6850813031196594, 0.4412658214569092], - [-1.3051068782806396, -1.3525396585464478, 1.8345310688018799], - ]); - expect(tailwindGradientFromFills(node.fills)).toEqual( - "bg-gradient-to-tr from-black ", - ); - - // top right to bottom left (-45) - Object.assign(gradientFill.gradientTransform, [ - [-0.7061997652053833, 0.7888921499252319, 0.5180976986885071], - [1.5028705596923828, 1.2872726917266846, -1.0877336263656616], - ]); - expect(tailwindGradientFromFills(node.fills)).toEqual( - "bg-gradient-to-bl from-black ", - ); - - const gradientFillTwo: GradientPaint = { - type: "GRADIENT_LINEAR", - gradientTransform: [ - [0, 0, 0], - [0, 0, 0], - ], - gradientStops: [ - { - position: 0, - color: { - r: 0, - g: 0, - b: 0, - a: 1, - }, - }, - { - position: 1, - color: { - r: 1, - g: 1, - b: 1, - a: 1, - }, - }, - ], - }; - - node.fills = [gradientFillTwo]; - - expect(tailwindGradientFromFills(node.fills)).toEqual( - "bg-gradient-to-r from-black to-white ", - ); - - const gradientFillThree: GradientPaint = { - type: "GRADIENT_LINEAR", - gradientTransform: [ - [0, 0, 0], - [0, 0, 0], - ], - gradientStops: [ - { - position: 0, - color: { - r: 0, - g: 0, - b: 0, - a: 1, - }, - }, - { - position: 0.5, - color: { - r: 0.5, - g: 0.5, - b: 0.5, - a: 1, - }, - }, - { - position: 1, - color: { - r: 1, - g: 1, - b: 1, - a: 1, - }, - }, - ], - }; - - node.fills = [gradientFillThree]; - - expect(tailwindGradientFromFills(node.fills)).toEqual( - "bg-gradient-to-r from-black via-gray-500 to-white ", - ); - }); - - it("Execute Main with Linear Gradient, corners and stroke", () => { - const node = new AltRectangleNode(); - const gradientFill: GradientPaint = { - type: "GRADIENT_LINEAR", - gradientTransform: [ - [0.8038461208343506, 0.7035384774208069, -0.2932307720184326], - [1.3402682542800903, -1.4652644395828247, 0.5407097935676575], - ], - gradientStops: [ - { - position: 0, - color: { - r: 0, - g: 0, - b: 0, - a: 1, - }, - }, - { - position: 1, - color: { - r: 1, - g: 0, - b: 0, - a: 1, - }, - }, - ], - }; - - // width is going be 18 because 10 + 4 + 4 of stroke. - node.height = 10; - node.width = 10; - node.fills = [gradientFill]; - node.strokeWeight = 4; - node.strokeAlign = "OUTSIDE"; - node.strokes = [ - { - type: "SOLID", - color: { r: 0.25, g: 0.25, b: 0.25 }, - }, - ]; - node.cornerRadius = 16; - - expect(tailwindMain([node])).toEqual( - `
`, - ); - }); - - it("fail with other fill types", () => { - const node = new AltRectangleNode(); - node.fills = [ - { - type: "GRADIENT_LINEAR", - gradientTransform: [ - [0, 0, 0], - [0, 0, 0], - ], - gradientStops: [], - }, - ]; - - expect(tailwindColorFromFills(node.fills, "")).toEqual(""); - }); -}); diff --git a/__tests__/tailwind/builderImpl/tailwindPadding.test.ts b/__tests__/tailwind/builderImpl/tailwindPadding.test.ts deleted file mode 100644 index a7cb0077..00000000 --- a/__tests__/tailwind/builderImpl/tailwindPadding.test.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { tailwindPadding } from "../../../src/tailwind/builderImpl/tailwindPadding"; -import { - AltRectangleNode, - AltFrameNode, -} from "../../../src/altNodes/altMixins"; - -describe("Tailwind padding", () => { - it("test tailwind padding", () => { - const frameNode = new AltFrameNode(); - expect(tailwindPadding(frameNode)).toEqual(""); - - frameNode.layoutMode = "NONE"; - expect(tailwindPadding(frameNode)).toEqual(""); - - frameNode.layoutMode = "VERTICAL"; - - frameNode.paddingLeft = 0; - frameNode.paddingRight = 0; - frameNode.paddingTop = 4.1; - frameNode.paddingBottom = 4.2; - expect(tailwindPadding(frameNode)).toEqual("py-1 "); - - frameNode.paddingLeft = 8; - frameNode.paddingRight = 8.01; - frameNode.paddingTop = 4; - frameNode.paddingBottom = 4; - expect(tailwindPadding(frameNode)).toEqual("px-2 py-1 "); - - frameNode.paddingLeft = 4; - frameNode.paddingRight = 4; - frameNode.paddingTop = 0; - frameNode.paddingBottom = 0; - expect(tailwindPadding(frameNode)).toEqual("px-1 "); - - frameNode.paddingLeft = 4; - frameNode.paddingRight = 4; - frameNode.paddingTop = 8; - frameNode.paddingBottom = 8; - expect(tailwindPadding(frameNode)).toEqual("px-1 py-2 "); - - frameNode.paddingLeft = 4; - frameNode.paddingRight = 4; - frameNode.paddingTop = 8; - frameNode.paddingBottom = 8; - expect(tailwindPadding(frameNode)).toEqual("px-1 py-2 "); - - frameNode.paddingLeft = 4; - frameNode.paddingRight = 4; - frameNode.paddingTop = 4; - frameNode.paddingBottom = 4; - expect(tailwindPadding(frameNode)).toEqual("p-1 "); - - frameNode.paddingLeft = 4; - frameNode.paddingRight = 4.5; - frameNode.paddingTop = 4; - frameNode.paddingBottom = 4.5; - expect(tailwindPadding(frameNode)).toEqual("px-1 py-1 "); - - frameNode.paddingLeft = 4; - frameNode.paddingRight = 8; - frameNode.paddingTop = 4; - frameNode.paddingBottom = 8; - expect(tailwindPadding(frameNode)).toEqual("pl-1 pr-2 pt-1 pb-2 "); - - frameNode.paddingLeft = 0; - frameNode.paddingRight = 4; - frameNode.paddingTop = 0; - frameNode.paddingBottom = 4; - expect(tailwindPadding(frameNode)).toEqual("pr-1 pb-1 "); - - frameNode.paddingLeft = 4; - frameNode.paddingRight = 0; - frameNode.paddingTop = 4; - frameNode.paddingBottom = 0; - expect(tailwindPadding(frameNode)).toEqual("pl-1 pt-1 "); - - frameNode.paddingLeft = 0; - frameNode.paddingRight = 0; - frameNode.paddingTop = 0; - frameNode.paddingBottom = 0; - expect(tailwindPadding(frameNode)).toEqual(""); - - const notFrame = new AltRectangleNode(); - expect(tailwindPadding(notFrame)).toEqual(""); - }); -}); diff --git a/__tests__/tailwind/builderImpl/tailwindPosition.test.ts b/__tests__/tailwind/builderImpl/tailwindPosition.test.ts deleted file mode 100644 index db08ffd0..00000000 --- a/__tests__/tailwind/builderImpl/tailwindPosition.test.ts +++ /dev/null @@ -1,150 +0,0 @@ -import { tailwindPosition } from "../../../src/tailwind/builderImpl/tailwindPosition"; -import { AltFrameNode } from "../../../src/altNodes/altMixins"; - -describe("Tailwind Position", () => { - // @ts-expect-error for some reason, need to override this for figma.mixed to work - global.figma = { - mixed: undefined, - }; - - it("Frame Absolute Position", () => { - const parent = new AltFrameNode(); - parent.width = 100; - parent.height = 100; - parent.x = 0; - parent.y = 0; - parent.id = "root"; - parent.layoutMode = "NONE"; - parent.isRelative = true; - - const node = new AltFrameNode(); - parent.id = "node"; - node.parent = parent; - - // child equals parent - node.width = 100; - node.height = 100; - expect(tailwindPosition(node)).toEqual(""); - - node.width = 25; - node.height = 25; - - const nodeF2 = new AltFrameNode(); - nodeF2.width = 25; - nodeF2.height = 25; - nodeF2.parent = parent; - - parent.children = [node, nodeF2]; - - // position is set after the conversion to avoid AutoLayout auto converison - - // center - node.x = 37; - node.y = 37; - expect(tailwindPosition(node, "", true)).toEqual( - "absolute m-auto inset-0 ", - ); - expect(tailwindPosition(node, "", false)).toEqual("absoluteManualLayout"); - - // top-left - node.x = 0; - node.y = 0; - expect(tailwindPosition(node)).toEqual("absolute left-0 top-0 "); - - // top-right - node.x = 75; - node.y = 0; - expect(tailwindPosition(node)).toEqual("absolute right-0 top-0 "); - - // bottom-left - node.x = 0; - node.y = 75; - expect(tailwindPosition(node)).toEqual("absolute left-0 bottom-0 "); - - // bottom-right - node.x = 75; - node.y = 75; - expect(tailwindPosition(node)).toEqual("absolute right-0 bottom-0 "); - - // top-center - node.x = 37; - node.y = 0; - expect(tailwindPosition(node, "", true)).toEqual( - "absolute inset-x-0 top-0 mx-auto ", - ); - expect(tailwindPosition(node)).toEqual("absoluteManualLayout"); - - // left-center - node.x = 0; - node.y = 37; - expect(tailwindPosition(node, "", true)).toEqual( - "absolute inset-y-0 left-0 my-auto ", - ); - expect(tailwindPosition(node)).toEqual("absoluteManualLayout"); - - // bottom-center - node.x = 37; - node.y = 75; - expect(tailwindPosition(node, "", true)).toEqual( - "absolute inset-x-0 bottom-0 mx-auto ", - ); - expect(tailwindPosition(node)).toEqual("absoluteManualLayout"); - - // right-center - node.x = 75; - node.y = 37; - expect(tailwindPosition(node, "", true)).toEqual( - "absolute inset-y-0 right-0 my-auto ", - ); - expect(tailwindPosition(node)).toEqual("absoluteManualLayout"); - - // center Y, random X - node.x = 22; - node.y = 37; - expect(tailwindPosition(node)).toEqual("absoluteManualLayout"); - - // center X, random Y - node.x = 37; - node.y = 22; - expect(tailwindPosition(node)).toEqual("absoluteManualLayout"); - - // without position - node.x = 45; - node.y = 88; - expect(tailwindPosition(node)).toEqual("absoluteManualLayout"); - }); - - it("Position: node has same size as parent", () => { - const parent = new AltFrameNode(); - parent.width = 100; - parent.height = 100; - parent.layoutMode = "NONE"; - - const node = new AltFrameNode(); - node.width = 100; - node.height = 100; - node.parent = parent; - - const nodeF2 = new AltFrameNode(); - nodeF2.width = 100; - nodeF2.height = 100; - nodeF2.parent = parent; - - parent.children = [node, nodeF2]; - - expect(tailwindPosition(node, "")).toEqual(""); - }); - - it("No position when parent is root", () => { - const node = new AltFrameNode(); - node.layoutMode = "NONE"; - - const parent = new AltFrameNode(); - parent.id = "root"; - parent.layoutMode = "NONE"; - - node.parent = parent; - - expect(tailwindPosition(node, parent.id)).toEqual(""); - }); -}); diff --git a/__tests__/tailwind/builderImpl/tailwindShadow.test.ts b/__tests__/tailwind/builderImpl/tailwindShadow.test.ts deleted file mode 100644 index 95c99ebf..00000000 --- a/__tests__/tailwind/builderImpl/tailwindShadow.test.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { tailwindShadow } from "../../../src/tailwind/builderImpl/tailwindShadow"; -import { AltRectangleNode } from "../../../src/altNodes/altMixins"; -describe("Tailwind Shadow", () => { - // @ts-expect-error for some reason, need to override this for figma.mixed to work - global.figma = { - mixed: undefined, - }; - - it("drop shadow", () => { - const node = new AltRectangleNode(); - - // no shadow - expect(tailwindShadow(node)).toEqual(""); - - node.effects = [ - { - type: "DROP_SHADOW", - blendMode: "NORMAL", - color: { r: 0, g: 0, b: 0, a: 0.25 }, - offset: { x: 0, y: 4 }, - radius: 4, - visible: true, - }, - ]; - - expect(tailwindShadow(node)).toEqual("shadow "); - }); - - it("inner shadow", () => { - const node = new AltRectangleNode(); - - node.effects = [ - { - blendMode: "NORMAL", - color: { r: 0, g: 0, b: 0, a: 0.25 }, - offset: { x: 0, y: 4 }, - radius: 4, - type: "INNER_SHADOW", - visible: true, - }, - ]; - - expect(tailwindShadow(node)).toEqual("shadow-inner "); - }); -}); diff --git a/__tests__/tailwind/builderImpl/tailwindSize.test.ts b/__tests__/tailwind/builderImpl/tailwindSize.test.ts deleted file mode 100644 index 7a02d7b8..00000000 --- a/__tests__/tailwind/builderImpl/tailwindSize.test.ts +++ /dev/null @@ -1,299 +0,0 @@ -import { - AltRectangleNode, - AltFrameNode, -} from "../../../src/altNodes/altMixins"; -import { tailwindSize } from "../../../src/tailwind/builderImpl/tailwindSize"; - -describe("Tailwind Builder", () => { - // @ts-expect-error for some reason, need to override this for figma.mixed to work - global.figma = { - mixed: undefined, - }; - - it("size for a rectangle", () => { - const node = new AltRectangleNode(); - - node.width = 16; - node.height = 16; - expect(tailwindSize(node)).toEqual("w-4 h-4 "); - - node.width = 100; - node.height = 200; - expect(tailwindSize(node)).toEqual("w-24 h-48 "); - - node.width = 500; - node.height = 500; - expect(tailwindSize(node)).toEqual("w-full h-96 "); - }); - - it("STRETCH inside AutoLayout", () => { - const node = new AltFrameNode(); - node.layoutMode = "HORIZONTAL"; - node.counterAxisSizingMode = "FIXED"; - node.primaryAxisSizingMode = "FIXED"; - node.width = 100; - node.height = 100; - node.paddingLeft = 0; - node.paddingRight = 0; - node.paddingTop = 0; - node.paddingBottom = 0; - - const child = new AltRectangleNode(); - child.layoutAlign = "STRETCH"; - child.width = 100; - child.height = 100; - - child.parent = node; - node.children = [child]; - - expect(tailwindSize(child)).toEqual("flex-1 h-full "); - - // fail - node.layoutMode = "VERTICAL"; - child.width = 16; - child.height = 16; - expect(tailwindSize(child)).toEqual("w-full h-1/6 "); - - // child is relative, therefore it must have a value - expect(tailwindSize(node)).toEqual("w-24 h-24 "); - }); - - it("Vertical layout with FIXED counterAxis", () => { - const node = new AltFrameNode(); - node.layoutMode = "VERTICAL"; - node.counterAxisSizingMode = "FIXED"; - node.width = 16; - node.height = 16; - - const child = new AltRectangleNode(); - child.width = 8; - child.height = 8; - - child.parent = node; - node.children = [child]; - - expect(tailwindSize(node)).toEqual("w-4 "); - }); - - it("Children are rectangles, size shouldn't be relative", () => { - const node = new AltFrameNode(); - node.layoutMode = "NONE"; - node.width = 48; - node.height = 48; - node.children = [new AltRectangleNode(), new AltRectangleNode()]; - - expect(tailwindSize(node)).toEqual("w-12 h-12 "); - }); - - it("counterAxisSizingMode is FIXED", () => { - const node = new AltFrameNode(); - node.counterAxisSizingMode = "FIXED"; - node.width = 48; - node.height = 48; - node.children = [new AltRectangleNode(), new AltRectangleNode()]; - - node.layoutMode = "HORIZONTAL"; - expect(tailwindSize(node)).toEqual("h-12 "); - - node.layoutMode = "VERTICAL"; - expect(tailwindSize(node)).toEqual("w-12 "); - - node.layoutMode = "NONE"; - expect(tailwindSize(node)).toEqual("w-12 h-12 "); - }); - - it("counterAxisSizingMode is AUTO", () => { - const node = new AltFrameNode(); - node.layoutMode = "HORIZONTAL"; - node.counterAxisSizingMode = "AUTO"; - node.primaryAxisSizingMode = "AUTO"; - node.x = 0; - node.y = 0; - node.width = 48; - node.height = 48; - node.children = [new AltRectangleNode(), new AltRectangleNode()]; - - expect(tailwindSize(node)).toEqual(""); - - // responsive - const parentNode = new AltFrameNode(); - parentNode.counterAxisSizingMode = "FIXED"; - parentNode.primaryAxisSizingMode = "FIXED"; - parentNode.x = 0; - parentNode.y = 0; - parentNode.width = 48; - parentNode.height = 48; - parentNode.children = [node]; - node.parent = parentNode; - expect(tailwindSize(node)).toEqual(""); - expect(tailwindSize(parentNode)).toEqual("w-12 h-12 "); - }); - - it("width changes when there are strokes", () => { - const node = new AltRectangleNode(); - node.x = 0; - node.y = 0; - node.width = 8; - node.height = 8; - - expect(tailwindSize(node)).toEqual("w-2 h-2 "); - - node.strokeWeight = 4; - node.strokes = [ - { - type: "SOLID", - color: { r: 0.25, g: 0.25, b: 0.25 }, - }, - ]; - - node.strokeAlign = "CENTER"; - expect(tailwindSize(node)).toEqual("w-3 h-3 "); - - node.strokeAlign = "OUTSIDE"; - expect(tailwindSize(node)).toEqual("w-4 h-4 "); - }); - - it("adjust parent if children's size + stroke > parent size", () => { - const parentNode = new AltFrameNode(); - parentNode.width = 8; - parentNode.height = 8; - - const node = new AltRectangleNode(); - node.width = 8; - node.height = 8; - - node.strokeWeight = 4; - node.strokeAlign = "CENTER"; - node.strokes = [ - { - type: "SOLID", - color: { r: 0.25, g: 0.25, b: 0.25 }, - }, - ]; - - expect(tailwindSize(parentNode)).toEqual("w-2 h-2 "); - - parentNode.children = [node]; - node.parent = parentNode; - expect(tailwindSize(parentNode)).toEqual("w-3 h-3 "); - - node.strokeAlign = "OUTSIDE"; - expect(tailwindSize(parentNode)).toEqual("w-4 h-4 "); - }); - - it("all branches with children's size + stroke < children's size", () => { - const parentNode = new AltFrameNode(); - parentNode.width = 8; - parentNode.height = 8; - - const node = new AltRectangleNode(); - node.width = 4; - node.height = 4; - - node.strokeWeight = 2; - node.strokeAlign = "CENTER"; - node.strokes = [ - { - type: "SOLID", - color: { r: 0.25, g: 0.25, b: 0.25 }, - }, - ]; - - expect(tailwindSize(parentNode)).toEqual("w-2 h-2 "); - - parentNode.children = [node]; - node.parent = parentNode; - expect(tailwindSize(parentNode)).toEqual("w-2 h-2 "); - - node.strokeAlign = "OUTSIDE"; - expect(tailwindSize(parentNode)).toEqual("w-2 h-2 "); - - node.strokeAlign = "INSIDE"; - expect(tailwindSize(parentNode)).toEqual("w-2 h-2 "); - }); - - it("full width when width is same to the parent", () => { - const node = new AltFrameNode(); - node.width = 12; - node.height = 12; - - const parentNode = new AltFrameNode(); - parentNode.layoutMode = "NONE"; - parentNode.width = 12; - parentNode.height = 12; - parentNode.children = [node]; - node.parent = parentNode; - - expect(tailwindSize(parentNode)).toEqual("w-3 h-3 "); - expect(tailwindSize(node)).toEqual("w-full h-full "); - }); - - it("set the width to max if the view is near the corner", () => { - const parentNode = new AltFrameNode(); - parentNode.layoutMode = "NONE"; - parentNode.width = 120; - parentNode.height = 120; - - const node = new AltFrameNode(); - node.width = 100; - node.height = 100; - node.x = 0; - node.y = 0; - - node.parent = parentNode; - parentNode.children = [node]; - - expect(tailwindSize(node)).toEqual("w-5/6 h-5/6 "); - }); - - it("responsive width", () => { - const node = new AltFrameNode(); - node.width = 20; - node.height = 20; - node.primaryAxisSizingMode = "FIXED"; - node.counterAxisSizingMode = "FIXED"; - - const parentNode = new AltFrameNode(); - parentNode.layoutMode = "NONE"; - parentNode.primaryAxisSizingMode = "FIXED"; - parentNode.counterAxisSizingMode = "FIXED"; - parentNode.width = 20; - parentNode.height = 20; - parentNode.children = [node]; - node.parent = parentNode; - - expect(tailwindSize(node)).toEqual("w-full h-full "); - - node.width = 10; - node.height = 10; - expect(tailwindSize(node)).toEqual("w-1/2 h-1/2 "); - - node.width = 20 / 3; - node.height = 20 / 3; - expect(tailwindSize(node)).toEqual("w-1/3 h-1/3 "); - - node.width = 40 / 3; - node.height = 40 / 3; - expect(tailwindSize(node)).toEqual("w-2/3 h-2/3 "); - - node.width = 5; - node.height = 5; - expect(tailwindSize(node)).toEqual("w-1/4 h-1/4 "); - - node.width = 15; - node.height = 15; - expect(tailwindSize(node)).toEqual("w-3/4 h-3/4 "); - - node.width = 4; - node.height = 4; - expect(tailwindSize(node)).toEqual("w-1/5 h-1/5 "); - - node.width = 10 / 3; - node.height = 10 / 3; - expect(tailwindSize(node)).toEqual("w-1/6 h-1/6 "); - - node.width = 50 / 3; - node.height = 50 / 3; - expect(tailwindSize(node)).toEqual("w-5/6 h-5/6 "); - }); -}); diff --git a/__tests__/tailwind/colors.test.ts b/__tests__/tailwind/colors.test.ts deleted file mode 100644 index dc8eee0d..00000000 --- a/__tests__/tailwind/colors.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { - tailwindNearestColor, - getTailwindColor, -} from "../../src/tailwind/builderImpl/tailwindColor"; - -describe("Nearest colors", () => { - it("can it identify nearby colors?", () => { - expect(tailwindNearestColor("#fff5f5")).toEqual("#fef2f2"); - expect(tailwindNearestColor("#fff5f4")).toEqual("#fef2f2"); - expect(tailwindNearestColor("#fff5f6")).toEqual("#fdf2f8"); - }); - - it("can it identify tailwind colors?", () => { - const tailwindCompare = (color: string | RGB, equals: string) => { - expect(getTailwindColor(color)).toEqual(equals); - }; - - tailwindCompare({ r: 255, g: 245, b: 244 }, "red-50"); - - tailwindCompare("#fed7d6", "red-100"); - tailwindCompare("#fed7d7", "red-100"); - tailwindCompare("#fed7d8", "red-100"); - - tailwindCompare("#feb2b1", "red-300"); - tailwindCompare("#feb2b2", "red-300"); - tailwindCompare("#feb2b3", "red-300"); - - tailwindCompare("#fc8180", "red-400"); - tailwindCompare("#fc8181", "red-400"); - tailwindCompare("#fc8182", "red-400"); - }); -}); diff --git a/__tests__/tailwind/conversionTables.test.ts b/__tests__/tailwind/conversionTables.test.ts deleted file mode 100644 index eb8d265c..00000000 --- a/__tests__/tailwind/conversionTables.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { - nearestValue, - pxToFontSize, - pxToBorderRadius, - pxToLayoutSize, - pxToLetterSpacing, - pxToLineHeight, -} from "../../src/tailwind/conversionTables"; - -describe("Tailwind Conversion Table", () => { - it("test nearestValue", () => { - expect(nearestValue(1, [0, 2])).toEqual(0); - expect(nearestValue(1, [0, 3])).toEqual(0); - expect(nearestValue(2, [0, 3])).toEqual(3); - - expect(nearestValue(0.3, [0, 0.5])).toEqual(0.5); - expect(nearestValue(0.25, [0, 0.5])).toEqual(0); - expect(nearestValue(0, [0, 0.01])).toEqual(0); - }); - - it("convert pixels to tailwind values", () => { - expect(pxToLineHeight(16)).toEqual("none"); - expect(pxToLineHeight(40)).toEqual("10"); - - expect(pxToLetterSpacing(-0.4)).toEqual("tight"); - expect(pxToLetterSpacing(0.4)).toEqual("wide"); - - expect(pxToFontSize(14)).toEqual("sm"); - expect(pxToFontSize(18)).toEqual("lg"); - - expect(pxToBorderRadius(2)).toEqual("-sm"); - expect(pxToBorderRadius(8)).toEqual("-lg"); - - expect(pxToLayoutSize(4, false)).toEqual("1"); - expect(pxToLayoutSize(385, false)).toEqual("96"); - }); -}); diff --git a/__tests__/tailwind/size.test.ts b/__tests__/tailwind/size.test.ts deleted file mode 100644 index 099ac476..00000000 --- a/__tests__/tailwind/size.test.ts +++ /dev/null @@ -1,535 +0,0 @@ -import { createFigma } from "figma-api-stub"; -import { - frameNodeToAlt, - convertSingleNodeToAlt, -} from "../../src/altNodes/altConversion"; -import { tailwindSize } from "../../src/tailwind/builderImpl/tailwindSize"; -import { tailwindMain } from "./../../src/tailwind/tailwindMain"; -import { AltFrameNode, AltRectangleNode } from "./../../src/altNodes/altMixins"; - -describe("Tailwind Size", () => { - const figma = createFigma({ - simulateErrors: true, - isWithoutTimeout: false, - }); - - // @ts-expect-error for some reason, need to override this for figma.mixed to work - global.figma = figma; - - it("rect", () => { - const node = figma.createRectangle(); - node.resize(16, 16); - const updatedNode = convertSingleNodeToAlt(node); - expect(tailwindSize(updatedNode)).toEqual("w-4 h-4 "); - }); - - it("frame", () => { - const node = figma.createFrame(); - node.resize(16, 16); - expect(tailwindSize(frameNodeToAlt(node))).toEqual("w-4 h-4 "); - }); - - // todo figure out why it is failing - // it("frame inside frame", () => { - // const node = figma.createFrame(); - // node.resize(16, 16); - // node.paddingLeft = 0; - // node.paddingRight = 0; - // node.paddingTop = 0; - // node.paddingBottom = 0; - // node.primaryAxisSizingMode = "FIXED"; - // node.counterAxisSizingMode = "FIXED"; - - // const subnode = figma.createFrame(); - // subnode.resize(16, 16); - // subnode.paddingLeft = 0; - // subnode.paddingRight = 0; - // subnode.paddingTop = 0; - // subnode.paddingBottom = 0; - // subnode.primaryAxisSizingMode = "FIXED"; - // subnode.counterAxisSizingMode = "FIXED"; - // node.appendChild(subnode); - - // expect(tailwindSize(frameNodeToAlt(node))).toEqual("w-4 "); - // expect(tailwindSize(frameNodeToAlt(subnode))).toEqual("w-4 h-4 "); - // }); - - it("frame inside frame (1/2)", () => { - const node = new AltFrameNode(); - node.width = 8; - node.height = 8; - node.primaryAxisSizingMode = "AUTO"; - node.counterAxisSizingMode = "AUTO"; - - const subnode = new AltFrameNode(); - subnode.width = 8; - subnode.height = 8; - node.primaryAxisSizingMode = "FIXED"; - node.counterAxisSizingMode = "FIXED"; - - subnode.parent = node; - node.children = [subnode]; - - expect(tailwindSize(node)).toEqual("w-2 h-2 "); - expect(tailwindSize(subnode)).toEqual("w-full h-full "); - }); - - it("small frame inside large frame", () => { - const node = figma.createFrame(); - node.resize(500, 500); - node.layoutMode = "NONE"; - node.counterAxisSizingMode = "FIXED"; - node.x = 0; - node.y = 0; - - const subnode = figma.createFrame(); - subnode.resize(8, 8); - subnode.x = 246; - subnode.y = 246; - - node.appendChild(subnode); - - expect(tailwindMain([frameNodeToAlt(node)])) - .toEqual(`
- -
`); - - expect(tailwindSize(frameNodeToAlt(subnode))).toEqual("w-2 h-2 "); - }); - - describe("when layoutAlign is STRETCH", () => { - it("autolayout is horizontal, width should be full", () => { - const node = figma.createFrame(); - node.resize(500, 500); - node.layoutMode = "HORIZONTAL"; - - const subnode = figma.createRectangle(); - subnode.resize(25, 25); - subnode.layoutAlign = "STRETCH"; - // todo is this correct? Maybe it should be w-full - node.appendChild(subnode); - - expect(tailwindSize(frameNodeToAlt(node))).toEqual(""); - expect(tailwindSize(convertSingleNodeToAlt(subnode))).toEqual("w-6 h-6 "); - }); - - it("autolayout is vertical, height should be ???", () => { - const node = figma.createFrame(); - node.resize(500, 500); - node.layoutMode = "VERTICAL"; - - const subnode = figma.createRectangle(); - subnode.resize(25, 25); - subnode.layoutAlign = "STRETCH"; - // todo is this correct? Maybe it should be w-full - node.appendChild(subnode); - - expect(tailwindSize(frameNodeToAlt(node))).toEqual(""); - expect(tailwindSize(convertSingleNodeToAlt(subnode))).toEqual("w-6 h-6 "); - }); - }); - - describe("parent is frame, node is frame, both the same layoutMode", () => { - it("when parent is horizontal and node is horizontal, child defines the size", () => { - const node = figma.createFrame(); - node.resize(500, 500); - node.layoutMode = "HORIZONTAL"; - - const subnode = figma.createFrame(); - subnode.resize(500, 250); - subnode.layoutGrow = 1; - subnode.layoutAlign = "INHERIT"; - subnode.layoutMode = "HORIZONTAL"; - - const child = figma.createFrame(); - child.resize(16, 16); - child.layoutGrow = 0; - child.layoutAlign = "INHERIT"; - - subnode.appendChild(child); - node.appendChild(subnode); - - expect(tailwindSize(frameNodeToAlt(node))).toEqual(""); - expect(tailwindSize(frameNodeToAlt(subnode))).toEqual(""); - expect(tailwindSize(frameNodeToAlt(child))).toEqual("w-4 h-4 "); - }); - - it("when parent is vertical and node is vertical, child defines the size", () => { - const node = new AltFrameNode(); - node.width = 500; - node.height = 500; - node.counterAxisSizingMode = "FIXED"; - node.layoutMode = "VERTICAL"; - - const subnode = new AltFrameNode(); - subnode.width = 500; - subnode.height = 255; - subnode.counterAxisSizingMode = "FIXED"; - subnode.layoutMode = "VERTICAL"; - - const child = new AltFrameNode(); - child.width = 16; - child.height = 255; - child.layoutGrow = 1; - child.layoutMode = "NONE"; - - node.children = [subnode]; - subnode.parent = node; - - subnode.children = [child]; - child.parent = subnode; - - expect(tailwindSize(node)).toEqual("w-full "); - expect(tailwindSize(subnode)).toEqual(""); - expect(tailwindSize(child)).toEqual("w-4 flex-1 "); - - // additional test for layoutGrow - subnode.layoutMode = "HORIZONTAL"; - expect(tailwindSize(child)).toEqual("flex-1 h-64 "); - }); - - it("complex autolayout example", () => { - const node = new AltFrameNode(); - node.width = 225; - node.height = 300; - node.counterAxisSizingMode = "FIXED"; - node.primaryAxisSizingMode = "FIXED"; - node.counterAxisAlignItems = "CENTER"; - node.primaryAxisAlignItems = "CENTER"; - node.layoutMode = "VERTICAL"; - node.layoutAlign = "INHERIT"; - node.paddingLeft = 10; - node.paddingRight = 10; - node.itemSpacing = 10; - node.fills = [ - { - type: "SOLID", - color: { - r: 1, - g: 0, - b: 0, - }, - }, - ]; - - const fills: ReadonlyArray = [ - { - type: "SOLID", - color: { - r: 1, - g: 1, - b: 1, - }, - }, - ]; - - const child1 = new AltRectangleNode(); - child1.width = 205; - child1.height = 20; - child1.x = 10; - child1.y = 10; - child1.layoutAlign = "STRETCH"; - child1.fills = fills; - child1.parent = node; - - const child2 = new AltRectangleNode(); - child2.width = 205; - child2.height = 20; - child2.x = 10; - child2.y = 10; - child2.layoutAlign = "STRETCH"; - child2.fills = fills; - child2.parent = node; - - const child3 = new AltRectangleNode(); - child3.width = 100; - child3.height = 20; - child3.x = 10; - child3.y = 10; - child3.layoutAlign = "INHERIT"; - child3.fills = fills; - child3.parent = node; - - const child4 = new AltRectangleNode(); - child4.width = 30; - child4.height = 20; - child4.x = 10; - child4.y = 10; - child4.layoutAlign = "INHERIT"; - child4.fills = fills; - child4.parent = node; - - node.children = [child1, child2, child3, child4]; - - expect(tailwindMain([node])) - .toEqual(`
-
-
-
-
-
`); - }); - }); - - // describe("frame is too large for Tailwind to handle", () => { - // it("frame with no children becomes a rectangle (too large width)", () => { - // const node = figma.createFrame(); - // node.resize(500, 64); - // node.layoutMode = "NONE"; - // node.counterAxisSizingMode = "FIXED"; - - // expect(getContainerSizeProp(frameNodeToAlt(node))).toEqual( - // "w-full h-16 " - // ); - // }); - - // it("frame with no children becomes a rectangle (too large height)", () => { - // const node = figma.createFrame(); - // node.resize(64, 500); - // node.layoutMode = "NONE"; - - // // max of h-64 - // expect(getContainerSizeProp(frameNodeToAlt(node))).toEqual("w-16 h-64 "); - // }); - - // it("if height is too large with children", () => { - // const node = figma.createFrame(); - // node.resize(64, 500); - // node.layoutMode = "NONE"; - // node.counterAxisSizingMode = "FIXED"; - - // const subnode = figma.createFrame(); - // subnode.resize(64, 250); - // subnode.layoutMode = "NONE"; - // node.appendChild(subnode); - - // // h-auto - // expect(getContainerSizeProp(convertSingleNodeToAlt(node))).toEqual( - // "w-16 " - // ); - // }); - // // todo improve this. Try to set the parent height to be the same as children before h-auto - // it("children are higher than node", () => { - // const node = figma.createFrame(); - // node.resize(16, 16); - // node.layoutMode = "NONE"; - // node.counterAxisSizingMode = "FIXED"; - - // const subnode = figma.createFrame(); - // subnode.resize(32, 32); - // subnode.layoutMode = "NONE"; - // node.appendChild(subnode); - - // // h-auto - // expect(getContainerSizeProp(frameNodeToAlt(node))).toEqual("w-4 h-4 "); - // }); - - // it("child is way larger than node", () => { - // const node = figma.createFrame(); - // node.resize(16, 16); - // node.layoutMode = "NONE"; - // node.counterAxisSizingMode = "FIXED"; - - // const subnode = figma.createFrame(); - // subnode.resize(257, 257); - // subnode.layoutMode = "NONE"; - // node.appendChild(subnode); - - // // todo this seems wrong - // // h-auto - // expect(getContainerSizeProp(frameNodeToAlt(node))).toEqual("w-4 h-4 "); - // }); - // }); - - // // todo stroke - - // // when parent is HORIZONTAL and child is HORIZONTAL, let the child define the size - // }); - - // describe("Complex CustomAutoLayout Tests", () => { - // const figma = createFigma({ - // simulateErrors: true, - // isWithoutTimeout: false, - // }); - - // // @ts-ignore for some reason, need to override this for figma.mixed to work - // global.figma = figma; - - // it("Frame 73 (black frame with gray rect with two rects inside should become a black frame with a gray autolayout)", () => { - // const node = figma.createFrame(); - // node.resize(200, 200); - // node.counterAxisSizingMode = "FIXED"; - // node.fills = [ - // { - // type: "SOLID", - // color: { - // r: 0, - // g: 0, - // b: 0, - // }, - // }, - // ]; - - // const grayLargeRect = figma.createRectangle(); - // grayLargeRect.resize(150, 150); - // grayLargeRect.x = 0; - // grayLargeRect.y = 50; - // grayLargeRect.fills = [ - // { - // type: "SOLID", - // color: { - // r: 0.25, - // g: 0.25, - // b: 0.25, - // }, - // }, - // ]; - - // node.appendChild(grayLargeRect); - - // const redSmallRect = figma.createRectangle(); - // redSmallRect.resize(100, 50); - // redSmallRect.x = 25; - // redSmallRect.y = 125; - // redSmallRect.fills = [ - // { - // type: "SOLID", - // color: { - // r: 1, - // g: 0, - // b: 0, - // }, - // }, - // ]; - - // node.appendChild(redSmallRect); - - // const blueSmallRect = figma.createRectangle(); - // blueSmallRect.resize(100, 50); - // blueSmallRect.x = 25; - // blueSmallRect.y = 62; - // blueSmallRect.fills = [ - // { - // type: "SOLID", - // color: { - // r: 0, - // g: 0, - // b: 1, - // }, - // }, - // ]; - - // node.appendChild(blueSmallRect); - - // expect(tailwindMain(node.id, [frameNodeToAlt(node)], false, false)).toEqual( - // `\n
- //
- //
- //
` - // ); - // }); - - // // imagine a Rect, Text and Frame. Rect will be changed to become the Frame. - // // The parent of Rect is the Frame, and the parent of Text will be Rect. - // it("Test rect becoming Frame", () => { - // const node = figma.createFrame(); - // node.resize(100, 50); - // node.x = 0; - // node.y = 0; - // node.counterAxisSizingMode = "FIXED"; - // node.layoutMode = "NONE"; - - // const grayLargeRect = figma.createRectangle(); - // grayLargeRect.resize(80, 40); - // grayLargeRect.x = 0; - // grayLargeRect.y = 0; - // grayLargeRect.fills = [ - // { - // type: "SOLID", - // color: { - // r: 0.25, - // g: 0.25, - // b: 0.25, - // }, - // }, - // ]; - - // node.appendChild(grayLargeRect); - - // const blueSmallRect = figma.createRectangle(); - // blueSmallRect.resize(50, 20); - // blueSmallRect.x = 20; - // blueSmallRect.y = 20; - // blueSmallRect.fills = [ - // { - // type: "SOLID", - // color: { - // r: 0, - // g: 0, - // b: 1, - // }, - // }, - // ]; - // node.appendChild(blueSmallRect); - - // const superNode = figma.createFrame(); - // superNode.resize(100, 50); - // superNode.x = 0; - // superNode.y = 0; - // superNode.counterAxisSizingMode = "FIXED"; - // superNode.layoutMode = "NONE"; - - // superNode.appendChild(node); - - // expect(tailwindMain(superNode.parent?.id ?? "", [superNode])).toEqual( - // `\n
- //
` - // ); - // }); - - // it("Test rect becoming Frame in a Group", () => { - // const node = figma.createFrame(); - // node.resize(100, 50); - // node.x = 0; - // node.y = 0; - // node.counterAxisSizingMode = "FIXED"; - // node.layoutMode = "NONE"; - - // const grayLargeRect = figma.createRectangle(); - // grayLargeRect.resize(80, 40); - // grayLargeRect.x = 0; - // grayLargeRect.y = 0; - // grayLargeRect.fills = [ - // { - // type: "SOLID", - // color: { - // r: 0.25, - // g: 0.25, - // b: 0.25, - // }, - // }, - // ]; - - // const blueSmallRect = figma.createRectangle(); - // blueSmallRect.resize(50, 20); - // blueSmallRect.x = 20; - // blueSmallRect.y = 20; - // blueSmallRect.fills = [ - // { - // type: "SOLID", - // color: { - // r: 0, - // g: 0, - // b: 1, - // }, - // }, - // ]; - - // const group = figma.group([grayLargeRect, blueSmallRect], node); - - // expect(tailwindMain(node.id, [group])).toEqual( - // `\n
- //
` - // ); - // }); -}); diff --git a/__tests__/tailwind/tailwindMain.test.ts b/__tests__/tailwind/tailwindMain.test.ts deleted file mode 100644 index ee885602..00000000 --- a/__tests__/tailwind/tailwindMain.test.ts +++ /dev/null @@ -1,203 +0,0 @@ -import { - AltRectangleNode, - AltFrameNode, - AltGroupNode, - AltEllipseNode, - AltTextNode, -} from "../../src/altNodes/altMixins"; -import { TailwindDefaultBuilder } from "../../src/tailwind/tailwindDefaultBuilder"; -import { tailwindMain } from "../../src/tailwind/tailwindMain"; -import { convertToAutoLayout } from "./../../src/altNodes/convertToAutoLayout"; - -describe("Tailwind Main", () => { - // @ts-expect-error for some reason, need to override this for figma.mixed to work - global.figma = { - mixed: undefined, - }; - - it("children is larger than 256", () => { - const node = new AltFrameNode(); - node.width = 320; - node.height = 320; - node.name = "FRAME"; - node.layoutMode = "NONE"; - node.counterAxisSizingMode = "FIXED"; - - const child1 = new AltRectangleNode(); - child1.width = 385; - child1.height = 8; - child1.x = 9; - child1.y = 9; - child1.name = "RECT1"; - child1.fills = [ - { - type: "SOLID", - color: { - r: 1, - g: 1, - b: 1, - }, - }, - ]; - - const child2 = new AltRectangleNode(); - child2.width = 8; - child2.height = 385; - child2.x = 9; - child2.y = 9; - child2.name = "RECT2"; - - // this works as a test for JSX, but should never happen in reality. In reality Frame would need to have 2 children and be relative. - node.children = [child1, child2]; - child1.parent = node; - child2.parent = node; - - expect(tailwindMain([convertToAutoLayout(node)])) - .toEqual(`
-
-
-
`); - }); - - it("Group with relative position", () => { - // this also should neve happen in reality, because Group must have the same size as the children. - - const node = new AltGroupNode(); - node.width = 32; - node.height = 32; - node.x = 0; - node.y = 0; - node.name = "GROUP"; - node.isRelative = true; - - const child = new AltRectangleNode(); - child.width = 4; - child.height = 4; - child.x = 9; - child.y = 9; - child.name = "RECT"; - child.fills = [ - { - type: "SOLID", - color: { - r: 1, - g: 1, - b: 1, - }, - }, - ]; - - node.children = [child]; - child.parent = node; - expect(tailwindMain([node], "", true, true)) - .toEqual(`
-
-
`); - }); - - it("ellipse with no size", () => { - const node = new AltEllipseNode(); - - // undefined (unitialized, only happen on tests) - expect(tailwindMain([node])).toEqual('
'); - - node.width = 0; - node.height = 10; - expect(tailwindMain([node])).toEqual(""); - - node.width = 10; - node.height = 0; - expect(tailwindMain([node])).toEqual(""); - }); - - it("input", () => { - const textNode = new AltTextNode(); - textNode.characters = "username"; - textNode.fontSize = 26; - textNode.x = 0; - textNode.y = 0; - - const frameNode = new AltFrameNode(); - frameNode.layoutMode = "HORIZONTAL"; - frameNode.width = 100; - frameNode.height = 40; - frameNode.counterAxisSizingMode = "AUTO"; - frameNode.primaryAxisSizingMode = "AUTO"; - - frameNode.primaryAxisAlignItems = "SPACE_BETWEEN"; - frameNode.counterAxisAlignItems = "CENTER"; - - frameNode.children = [textNode]; - textNode.parent = frameNode; - - // In real life, justify-between would be converted to justify-center in the altConversion. - expect(tailwindMain([frameNode])).toEqual( - `
-

username

-
`, - ); - - frameNode.name = "this is the InPuT"; - expect(tailwindMain([frameNode])).toEqual( - '', - ); - }); - - it("JSX", () => { - const node = new AltRectangleNode(); - node.name = "RECT"; - - const builder = new TailwindDefaultBuilder(node, true, true); - - expect(builder.build()).toEqual(' className="RECT"'); - - builder.reset(); - expect(builder.attributes).toEqual(""); - }); - - it("JSX with relative position", () => { - const node = new AltFrameNode(); - node.width = 32; - node.height = 32; - node.x = 0; - node.y = 0; - node.name = "FRAME"; - node.layoutMode = "NONE"; - node.counterAxisSizingMode = "FIXED"; - - const child1 = new AltRectangleNode(); - child1.width = 4; - child1.height = 4; - child1.x = 9; - child1.y = 9; - child1.name = "RECT1"; - child1.fills = [ - { - type: "SOLID", - color: { - r: 1, - g: 1, - b: 1, - }, - }, - ]; - - const child2 = new AltRectangleNode(); - child2.width = 4; - child2.height = 4; - child2.x = 9; - child2.y = 9; - child2.name = "RECT2"; - - // this works as a test for JSX, but should never happen in reality. In reality Frame would need to have 2 children and be relative. - node.children = [child1, child2]; - child1.parent = node; - child2.parent = node; - - expect(tailwindMain([convertToAutoLayout(node)], "", true, true)) - .toEqual(`
-
-
-
`); - }); -}); diff --git a/__tests__/tailwind/tailwindText.test.ts b/__tests__/tailwind/tailwindText.test.ts deleted file mode 100644 index 46c77a90..00000000 --- a/__tests__/tailwind/tailwindText.test.ts +++ /dev/null @@ -1,189 +0,0 @@ -import { TailwindTextBuilder } from "../../src/tailwind/tailwindTextBuilder"; -import { tailwindMain } from "../../src/tailwind/tailwindMain"; -import { AltTextNode } from "../../src/altNodes/altMixins"; -import { convertFontWeight } from "../../src/common/convertFontWeight"; - -describe("Tailwind Text", () => { - // @ts-expect-error for some reason, need to override this for figma.mixed to work - global.figma = { - mixed: undefined, - }; - it("textAutoResize", () => { - const node = new AltTextNode(); - node.characters = ""; - node.width = 16; - node.height = 16; - - node.textAutoResize = "NONE"; - expect(tailwindMain([node])).toEqual('

'); - - node.textAutoResize = "HEIGHT"; - expect(tailwindMain([node])).toEqual('

'); - - node.textAutoResize = "WIDTH_AND_HEIGHT"; - expect(tailwindMain([node])).toEqual("

"); - }); - - it("textAlignHorizontal", () => { - const node = new AltTextNode(); - node.characters = ""; - node.width = 16; - node.height = 16; - - node.textAutoResize = "WIDTH_AND_HEIGHT"; - node.textAlignHorizontal = "LEFT"; - expect(tailwindMain([node])).toEqual("

"); - - node.textAutoResize = "NONE"; - node.textAlignHorizontal = "CENTER"; - expect(tailwindMain([node])).toEqual('

'); - - node.textAlignHorizontal = "RIGHT"; - expect(tailwindMain([node])).toEqual('

'); - - node.textAlignHorizontal = "JUSTIFIED"; - expect(tailwindMain([node])).toEqual( - '

', - ); - }); - it("fontSize", () => { - const node = new AltTextNode(); - node.characters = ""; - node.width = 16; - node.height = 16; - node.fontSize = 16; - node.textAutoResize = "WIDTH_AND_HEIGHT"; - - expect(tailwindMain([node])).toEqual('

'); - }); - - it("fontName", () => { - const node = new AltTextNode(); - node.characters = ""; - node.width = 16; - node.height = 16; - node.textAutoResize = "WIDTH_AND_HEIGHT"; - - node.fontName = { - family: "inter", - style: "bold", - }; - expect(tailwindMain([node])).toEqual('

'); - - node.fontName = { - family: "inter", - style: "medium italic", - }; - expect(tailwindMain([node])).toEqual('

'); - - node.fontName = { - family: "inter", - style: "regular", - }; - expect(tailwindMain([node])).toEqual("

"); - }); - - it("letterSpacing", () => { - const node = new AltTextNode(); - node.characters = ""; - node.width = 16; - node.height = 16; - node.fontSize = 24; - node.textAutoResize = "WIDTH_AND_HEIGHT"; - - node.letterSpacing = { - value: 110, - unit: "PERCENT", - }; - expect(tailwindMain([node])).toEqual( - '

', - ); - - node.letterSpacing = { - value: 10, - unit: "PIXELS", - }; - expect(tailwindMain([node])).toEqual( - '

', - ); - }); - - it("lineHeight", () => { - const node = new AltTextNode(); - node.characters = ""; - node.width = 16; - node.height = 16; - node.textAutoResize = "WIDTH_AND_HEIGHT"; - node.fontSize = 24; - - node.lineHeight = { - value: 110, - unit: "PERCENT", - }; - expect(tailwindMain([node])).toEqual( - '

', - ); - - node.lineHeight = { - value: 10, - unit: "PIXELS", - }; - expect(tailwindMain([node])).toEqual('

'); - }); - - it("textCase", () => { - const node = new AltTextNode(); - node.characters = ""; - - node.textCase = "LOWER"; - expect(tailwindMain([node])).toEqual('

'); - - node.textCase = "TITLE"; - expect(tailwindMain([node])).toEqual('

'); - - node.textCase = "UPPER"; - expect(tailwindMain([node])).toEqual('

'); - - node.textCase = "ORIGINAL"; - expect(tailwindMain([node])).toEqual("

"); - }); - - it("textDecoration", () => { - const node = new AltTextNode(); - node.characters = ""; - - node.textDecoration = "NONE"; - expect(tailwindMain([node])).toEqual("

"); - - node.textDecoration = "STRIKETHROUGH"; - expect(tailwindMain([node])).toEqual('

'); - - node.textDecoration = "UNDERLINE"; - expect(tailwindMain([node])).toEqual('

'); - }); - - it("weight", () => { - expect(convertFontWeight("tHIN")).toEqual("100"); - expect(convertFontWeight("Default")).toEqual(null); - - expect(convertFontWeight("Thin")).toEqual("100"); - expect(convertFontWeight("Extra Light")).toEqual("200"); - expect(convertFontWeight("Light")).toEqual("300"); - expect(convertFontWeight("Regular")).toEqual("400"); - expect(convertFontWeight("Medium")).toEqual("500"); - expect(convertFontWeight("Semi Bold")).toEqual("600"); - expect(convertFontWeight("SemiBold")).toEqual("600"); - expect(convertFontWeight("Bold")).toEqual("700"); - expect(convertFontWeight("Heavy")).toEqual("800"); - expect(convertFontWeight("Extra Bold")).toEqual("800"); - expect(convertFontWeight("Black")).toEqual("900"); - }); - it("reset", () => { - const node = new AltTextNode(); - node.characters = ""; - - const builder = new TailwindTextBuilder(node, false, false); - builder.reset(); - expect(builder.build()).toEqual(""); - }); -}); diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index 6090f9c0..00000000 --- a/jest.config.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - preset: "ts-jest", - testEnvironment: "node", - globals: { - "ts-jest": { - diagnostics: { - ignoreCodes: ["151001"], - }, - }, - }, -}; diff --git a/packages/backend/package.json b/packages/backend/package.json index 0346968a..0813a106 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -10,8 +10,7 @@ "dist/**" ], "scripts": { - "lint": "eslint \"src/**/*.ts*\"", - "test": "jest" + "lint": "eslint \"src/**/*.ts*\"" }, "dependencies": { "@figma/plugin-typings": "^1.97.0", diff --git a/packages/plugin-ui/package.json b/packages/plugin-ui/package.json index 179236d9..a0cc2440 100644 --- a/packages/plugin-ui/package.json +++ b/packages/plugin-ui/package.json @@ -7,8 +7,7 @@ "main": "./src/index.tsx", "types": "./src/index.tsx", "scripts": { - "lint": "eslint \"src/**/*.ts*\"", - "test": "jest" + "lint": "eslint \"src/**/*.ts*\"" }, "dependencies": { "copy-to-clipboard": "^3.3.3", From 3db5f612856fe78ab90f155fd0782b351e453738 Mon Sep 17 00:00:00 2001 From: Mario Date: Tue, 17 Dec 2024 17:07:41 +0100 Subject: [PATCH 013/168] Enhancements to Tailwind Class Generation and Figma Plugin Updates (#131) * Added Tailwind class names for serif and mono fonts When the user select a specific font family in Figma and it matches with the default font family defined in Tailwind, the generated code in the plugin uses the Tailwind utility classes. https://tailwindcss.com/docs/font-family * Added Tailwind class for overflow hidden When the option "Clip content" in Figma on a frame, or component is enabled, the plugin prints the class "overflow-hidden" in the generated code. https://tailwindcss.com/docs/overflow https://www.delasign.com/blog/figma-frame-clip-content/ * Improvement to css class name generation for custom color names Up until now the plugin would generate class names for custom color names as "hello-hello world" if the Figma variable name is "hello/hello world". This created 2 css class names. With this code change spaces would be converted to dashes, so that the Figma variable name "hello/hello world" will generate "hello-hello-world". * Added Figma "documentonchange" listener Every time something is being updated in the Figma file, the generated plugin code updates. --- apps/plugin/plugin-src/code.ts | 8 ++++ .../backend/src/tailwind/conversionTables.ts | 2 +- packages/backend/src/tailwind/tailwindMain.ts | 9 +++-- .../src/tailwind/tailwindTextBuilder.ts | 40 +++++++++++++++++++ 4 files changed, 55 insertions(+), 4 deletions(-) diff --git a/apps/plugin/plugin-src/code.ts b/apps/plugin/plugin-src/code.ts index 7d47b2c7..2a82adfc 100644 --- a/apps/plugin/plugin-src/code.ts +++ b/apps/plugin/plugin-src/code.ts @@ -83,9 +83,17 @@ const safeRun = (settings: PluginSettings) => { const standardMode = async () => { figma.showUI(__html__, { width: 450, height: 550, themeColors: true }); await initSettings(); + + // Listen for selection changes figma.on("selectionchange", () => { safeRun(userPluginSettings); }); + + // Listen for document changes + figma.on("documentchange", () => { + safeRun(userPluginSettings); + }); + figma.ui.onmessage = (msg) => { console.log("[node] figma.ui.onmessage", msg); diff --git a/packages/backend/src/tailwind/conversionTables.ts b/packages/backend/src/tailwind/conversionTables.ts index 8736fc15..2fb71897 100644 --- a/packages/backend/src/tailwind/conversionTables.ts +++ b/packages/backend/src/tailwind/conversionTables.ts @@ -115,7 +115,7 @@ export const nearestColorFromRgb = (color: RGB) => { export const variableToColorName = (alias: VariableAlias) => { return ( - figma.variables.getVariableById(alias.id)?.name.replaceAll("/", "-") || + figma.variables.getVariableById(alias.id)?.name.replaceAll("/", "-").replaceAll(" ", "-") || alias.id.toLowerCase().replaceAll(":", "-") ); }; diff --git a/packages/backend/src/tailwind/tailwindMain.ts b/packages/backend/src/tailwind/tailwindMain.ts index 83928ada..77c35ecd 100644 --- a/packages/backend/src/tailwind/tailwindMain.ts +++ b/packages/backend/src/tailwind/tailwindMain.ts @@ -146,21 +146,24 @@ const tailwindFrame = ( isJsx, ); + // Add overflow-hidden class if clipsContent is true + const clipsContentClass = node.clipsContent ? " overflow-hidden" : ""; + if (node.layoutMode !== "NONE") { const rowColumn = tailwindAutoLayoutProps(node, node); - return tailwindContainer(node, childrenStr, rowColumn, isJsx); + return tailwindContainer(node, childrenStr, rowColumn + clipsContentClass, isJsx); } else { if ( localTailwindSettings.optimizeLayout && node.inferredAutoLayout !== null ) { const rowColumn = tailwindAutoLayoutProps(node, node.inferredAutoLayout); - return tailwindContainer(node, childrenStr, rowColumn, isJsx); + return tailwindContainer(node, childrenStr, rowColumn + clipsContentClass, isJsx); } // node.layoutMode === "NONE" && node.children.length > 1 // children needs to be absolute - return tailwindContainer(node, childrenStr, "", isJsx); + return tailwindContainer(node, childrenStr, clipsContentClass, isJsx); } }; diff --git a/packages/backend/src/tailwind/tailwindTextBuilder.ts b/packages/backend/src/tailwind/tailwindTextBuilder.ts index 8b23e940..24d09ddd 100644 --- a/packages/backend/src/tailwind/tailwindTextBuilder.ts +++ b/packages/backend/src/tailwind/tailwindTextBuilder.ts @@ -97,6 +97,46 @@ export class TailwindTextBuilder extends TailwindDefaultBuilder { }; fontFamily = (fontName: FontName): string => { + const sansSerif = [ + 'ui-sans-serif', + 'system-ui', + 'sans-serif', + 'Apple Color Emoji', + 'Segoe UI Emoji', + 'Segoe UI Symbol', + 'Noto Color Emoji' + ]; + + const serif = [ + 'ui-serif', + 'Georgia', + 'Cambria', + 'Times New Roman', + 'Times', + 'serif' + ]; + + const mono = [ + 'ui-monospace', + 'SFMono-Regular', + 'Menlo', + 'Monaco', + 'Consolas', + 'Liberation Mono', + 'Courier New', + 'monospace' + ]; + + if (sansSerif.includes(fontName.family)) { + return 'font-sans'; + } + if (serif.includes(fontName.family)) { + return 'font-serif'; + } + if (mono.includes(fontName.family)) { + return 'font-mono'; + } + return "font-['" + fontName.family + "']"; }; From 378bd93a6f9d45f6e0790fcf954570e824938a5b Mon Sep 17 00:00:00 2001 From: Mario Date: Tue, 17 Dec 2024 20:51:47 +0100 Subject: [PATCH 014/168] Enhance Tailwind Configuration and Color Handling (#143) - Added checks for pure black and white colors in getColorInfo function to return appropriate Tailwind color names and hex values. - Introduced fontWeight and fontFamily configurations in tailwindConfig for better management of font styles. - Refactored fontWeight and fontFamily methods in TailwindTextBuilder to utilize the new configuration. - Improved shadow handling in tailwindShadow to support various shadow types and conditions, including custom shadow values. These changes streamline the Tailwind class generation process and enhance the overall functionality of the Figma plugin. Co-authored-by: Mario --- .../tailwind/builderImpl/tailwindShadow.ts | 126 +++++++++++++++--- .../backend/src/tailwind/conversionTables.ts | 19 +++ .../backend/src/tailwind/tailwindConfig.ts | 46 ++++++- .../src/tailwind/tailwindDefaultBuilder.ts | 4 +- .../src/tailwind/tailwindTextBuilder.ts | 61 +-------- 5 files changed, 184 insertions(+), 72 deletions(-) diff --git a/packages/backend/src/tailwind/builderImpl/tailwindShadow.ts b/packages/backend/src/tailwind/builderImpl/tailwindShadow.ts index 4ff5f2d7..2862dde4 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindShadow.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindShadow.ts @@ -5,24 +5,120 @@ export const tailwindShadow = (node: BlendMixin): string[] => { // [when testing] node.effects can be undefined if (node.effects && node.effects.length > 0) { - const dropShadow = node.effects.filter( - (d) => d.type === "DROP_SHADOW" && d.visible, - ); - let boxShadow = ""; - // simple shadow from tailwind - if (dropShadow.length > 0) { - boxShadow = "shadow"; - } + const EPSILON = 0.0001; // Small tolerance for floating-point comparison - const innerShadow = - node.effects.filter((d) => d.type === "INNER_SHADOW").length > 0 - ? "shadow-inner" - : ""; + const dropShadow = node.effects.map((d) => { + if (d.type === "DROP_SHADOW") { + if ( + d.offset?.x === 0 && + d.offset?.y === 1 && + d.radius === 2 && + d.spread === 0 && + d.color?.r === 0 && + d.color?.g === 0 && + d.color?.b === 0 && + Math.abs(d.color?.a - 0.05) < EPSILON + ) { + return "shadow-sm"; + } else if ( + d.offset?.x === 0 && + d.offset?.y === 1 && + d.radius === 3 && + d.spread === 0 && + d.color?.r === 0 && + d.color?.g === 0 && + d.color?.b === 0 && + Math.abs(d.color?.a - 0.1) < EPSILON + ) { + return "shadow"; + } else if ( + d.offset?.x === 0 && + d.offset?.y === 4 && + d.radius === 6 && + d.spread === -1 && + d.color?.r === 0 && + d.color?.g === 0 && + d.color?.b === 0 && + Math.abs(d.color?.a - 0.1) < EPSILON + ) { + return "shadow-md"; + } else if ( + d.offset?.x === 0 && + d.offset?.y === 10 && + d.radius === 15 && + d.spread === -3 && + d.color?.r === 0 && + d.color?.g === 0 && + d.color?.b === 0 && + Math.abs(d.color?.a - 0.1) < EPSILON + ) { + return "shadow-lg"; + } else if ( + d.offset?.x === 0 && + d.offset?.y === 20 && + d.radius === 25 && + d.spread === -5 && + d.color?.r === 0 && + d.color?.g === 0 && + d.color?.b === 0 && + Math.abs(d.color?.a - 0.1) < EPSILON + ) { + return "shadow-xl"; + } else if ( + d.offset?.x === 0 && + d.offset?.y === 25 && + d.radius === 50 && + d.spread === -12 && + d.color?.r === 0 && + d.color?.g === 0 && + d.color?.b === 0 && + Math.abs(d.color?.a - 0.25) < EPSILON + ) { + return "shadow-2xl"; + } else { + const offsetX = d.offset?.x || 0; + const offsetY = d.offset?.y || 0; + const radius = d.radius || 0; + const spread = d.spread || 0; + const r = Math.round((d.color?.r || 0) * 255); + const g = Math.round((d.color?.g || 0) * 255); + const b = Math.round((d.color?.b || 0) * 255); + const a = (d.color?.a || 0).toFixed(2); // Limit alpha to 2 decimal, otherwise we will get values like 0.12356587999 + return `shadow-[${offsetX}px_${offsetY}px_${radius}px_${spread}px_rgba(${r},${g},${b},${a})]`; + } + } + return ""; + }).filter(Boolean); - return [boxShadow, innerShadow]; + const innerShadow = node.effects.map((d) => { + if (d.type === "INNER_SHADOW") { + if ( + d.offset?.x === 0 && + d.offset?.y === 2 && + d.radius === 4 && + d.spread === 0 && + d.color?.r === 0 && + d.color?.g === 0 && + d.color?.b === 0 && + Math.abs(d.color?.a - 0.05) < EPSILON + ) { + return "shadow-inner"; + } else { + const offsetX = d.offset?.x || 0; + const offsetY = d.offset?.y || 0; + const radius = d.radius || 0; + const spread = d.spread || 0; + const r = Math.round((d.color?.r || 0) * 255); + const g = Math.round((d.color?.g || 0) * 255); + const b = Math.round((d.color?.b || 0) * 255); + const a = (d.color?.a || 0).toFixed(2); // Limit alpha to 2 decimal, otherwise we will get values like 0.12356587999 + return `shadow-[inset_${offsetX}px_${offsetY}px_${radius}px_${spread}px_rgba(${r},${g},${b},${a})]`; + } + } + return ""; + }).filter(Boolean); - // todo customize the shadow - // TODO layer blur, shadow-outline + return [...dropShadow, ...innerShadow]; } return []; }; diff --git a/packages/backend/src/tailwind/conversionTables.ts b/packages/backend/src/tailwind/conversionTables.ts index 2fb71897..ba814e8d 100644 --- a/packages/backend/src/tailwind/conversionTables.ts +++ b/packages/backend/src/tailwind/conversionTables.ts @@ -132,6 +132,25 @@ export function getColorInfo(fill: SolidPaint | ColorStop) { let hex: string = "#" + rgbTo6hex(fill.color); let meta: string = ""; + // Check for pure black/white first + if (fill.color.r === 0 && fill.color.g === 0 && fill.color.b === 0) { + return { + colorType: "tailwind", + colorName: "black", + hex: "#000000", + meta: '' + }; + } + + if (fill.color.r === 1 && fill.color.g === 1 && fill.color.b === 1) { + return { + colorType: "tailwind", + colorName: "white", + hex: "#ffffff", + meta: '' + }; + } + // variable if ( localTailwindSettings.customTailwindColors && diff --git a/packages/backend/src/tailwind/tailwindConfig.ts b/packages/backend/src/tailwind/tailwindConfig.ts index b33cba22..5660ae41 100644 --- a/packages/backend/src/tailwind/tailwindConfig.ts +++ b/packages/backend/src/tailwind/tailwindConfig.ts @@ -90,7 +90,7 @@ const letterSpacing = { const blur = { 0: "none", 4: "sm", - 8: "", + 8: "blur", // This is not the official Tailwind class name suffix, but currently needed for the blurValue variable to work. 12: "md", 16: "lg", 24: "xl", @@ -347,6 +347,48 @@ const color: Record = { "#4c0519": "rose-950", }; +const fontWeight: Record = { + 100: "thin", + 200: "extralight", + 300: "light", + 400: "normal", + 500: "medium", + 600: "semibold", + 700: "bold", + 800: "extrabold", + 900: "black" +}; + +const fontFamily = { + sans: [ + 'ui-sans-serif', + 'system-ui', + 'sans-serif', + 'Apple Color Emoji', + 'Segoe UI Emoji', + 'Segoe UI Symbol', + 'Noto Color Emoji' + ], + serif: [ + 'ui-serif', + 'Georgia', + 'Cambria', + 'Times New Roman', + 'Times', + 'serif' + ], + mono: [ + 'ui-monospace', + 'SFMono-Regular', + 'Menlo', + 'Monaco', + 'Consolas', + 'Liberation Mono', + 'Courier New', + 'monospace' + ] +}; + export const config = { layoutSize, borderRadius, @@ -356,4 +398,6 @@ export const config = { blur, opacity, color, + fontWeight, + fontFamily, }; diff --git a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts index 961ec898..8248ed3e 100644 --- a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts +++ b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts @@ -216,7 +216,9 @@ export class TailwindDefaultBuilder { if (blur) { const blurValue = pxToBlur(blur.radius); if (blurValue) { - this.addAttributes(`blur${blurValue ? `-${blurValue}` : ""}`); + this.addAttributes( + blurValue === "blur" ? "blur" : `blur-${blurValue}` + ); // If blur value is 8, it will be "blur". Otherwise, it will be "blur-sm", "blur-md", etc. or "blur-[Xpx]" } } diff --git a/packages/backend/src/tailwind/tailwindTextBuilder.ts b/packages/backend/src/tailwind/tailwindTextBuilder.ts index 24d09ddd..06425abd 100644 --- a/packages/backend/src/tailwind/tailwindTextBuilder.ts +++ b/packages/backend/src/tailwind/tailwindTextBuilder.ts @@ -10,6 +10,7 @@ import { pxToLineHeight, } from "./conversionTables"; import { TailwindDefaultBuilder } from "./tailwindDefaultBuilder"; +import { config } from "./tailwindConfig"; export class TailwindTextBuilder extends TailwindDefaultBuilder { getTextSegments(id: string): { style: string; text: string }[] { @@ -65,28 +66,8 @@ export class TailwindTextBuilder extends TailwindDefaultBuilder { }; fontWeight = (fontWeight: number): string => { - switch (fontWeight) { - case 100: - return "font-thin"; - case 200: - return "font-extralight"; - case 300: - return "font-light"; - case 400: - return "font-normal"; - case 500: - return "font-medium"; - case 600: - return "font-semibold"; - case 700: - return "font-bold"; - case 800: - return "font-extrabold"; - case 900: - return "font-black"; - default: - return ""; - } + const weight = config.fontWeight[fontWeight]; + return weight ? `font-${weight}` : ""; }; indentStyle = (indentation: number) => { @@ -97,43 +78,13 @@ export class TailwindTextBuilder extends TailwindDefaultBuilder { }; fontFamily = (fontName: FontName): string => { - const sansSerif = [ - 'ui-sans-serif', - 'system-ui', - 'sans-serif', - 'Apple Color Emoji', - 'Segoe UI Emoji', - 'Segoe UI Symbol', - 'Noto Color Emoji' - ]; - - const serif = [ - 'ui-serif', - 'Georgia', - 'Cambria', - 'Times New Roman', - 'Times', - 'serif' - ]; - - const mono = [ - 'ui-monospace', - 'SFMono-Regular', - 'Menlo', - 'Monaco', - 'Consolas', - 'Liberation Mono', - 'Courier New', - 'monospace' - ]; - - if (sansSerif.includes(fontName.family)) { + if (config.fontFamily.sans.includes(fontName.family)) { return 'font-sans'; } - if (serif.includes(fontName.family)) { + if (config.fontFamily.serif.includes(fontName.family)) { return 'font-serif'; } - if (mono.includes(fontName.family)) { + if (config.fontFamily.mono.includes(fontName.family)) { return 'font-mono'; } From 741883f248f7d923353680fbe0691c3f3424c053 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Wed, 18 Dec 2024 15:44:39 -0300 Subject: [PATCH 015/168] Bring subs/sups --- apps/plugin/package.json | 30 +- packages/backend/package.json | 12 +- .../backend/src/altNodes/altConversion.ts | 2 + packages/backend/src/flutter/flutterMain.ts | 2 +- .../backend/src/flutter/flutterTextBuilder.ts | 23 +- packages/backend/src/html/htmlMain.ts | 22 +- packages/backend/src/html/htmlTextBuilder.ts | 14 +- packages/backend/src/tailwind/tailwindMain.ts | 36 +- .../src/tailwind/tailwindTextBuilder.ts | 18 +- packages/plugin-ui/src/PluginUI.tsx | 4 +- pnpm-lock.yaml | 2110 ++++++++++------- 11 files changed, 1441 insertions(+), 832 deletions(-) diff --git a/apps/plugin/package.json b/apps/plugin/package.json index fd042de5..d2be2892 100644 --- a/apps/plugin/package.json +++ b/apps/plugin/package.json @@ -10,31 +10,31 @@ "dev": "pnpm build:watch" }, "dependencies": { - "@figma/plugin-typings": "^1.97.0", + "@figma/plugin-typings": "^1.105.0", "backend": "workspace:*", "plugin-ui": "workspace:*", "react": "^18.3.1", "react-dom": "^18.3.1" }, "devDependencies": { - "@types/node": "^20.14.11", - "@types/react": "^18.3.3", - "@types/react-dom": "^18.3.0", - "@typescript-eslint/eslint-plugin": "^7.17.0", - "@typescript-eslint/parser": "^7.17.0", - "@vitejs/plugin-react": "^4.3.1", - "@vitejs/plugin-react-swc": "^3.7.0", - "autoprefixer": "^10.4.19", + "@types/node": "^20.17.10", + "@types/react": "^18.3.17", + "@types/react-dom": "^18.3.5", + "@typescript-eslint/eslint-plugin": "^7.18.0", + "@typescript-eslint/parser": "^7.18.0", + "@vitejs/plugin-react": "^4.3.4", + "@vitejs/plugin-react-swc": "^3.7.2", + "autoprefixer": "^10.4.20", "concurrently": "^8.2.2", - "esbuild": "^0.23.0", + "esbuild": "^0.23.1", "eslint-config-custom": "workspace:*", "eslint-plugin-react-hooks": "^4.6.2", - "eslint-plugin-react-refresh": "^0.4.9", - "postcss": "^8.4.39", + "eslint-plugin-react-refresh": "^0.4.16", + "postcss": "^8.4.49", "tailwindcss": "3.4.6", "tsconfig": "workspace:*", - "typescript": "^5.5.4", - "vite": "^5.3.4", - "vite-plugin-singlefile": "^2.0.2" + "typescript": "^5.7.2", + "vite": "^5.4.11", + "vite-plugin-singlefile": "^2.1.0" } } diff --git a/packages/backend/package.json b/packages/backend/package.json index 0813a106..e5e37a3a 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -13,17 +13,17 @@ "lint": "eslint \"src/**/*.ts*\"" }, "dependencies": { - "@figma/plugin-typings": "^1.97.0", + "@figma/plugin-typings": "^1.105.0", "react": "18.3.1", "react-dom": "18.3.1" }, "devDependencies": { - "@types/react": "^18.3.3", - "@types/react-dom": "^18.3.0", - "eslint": "^9.7.0", + "@types/react": "^18.3.17", + "@types/react-dom": "^18.3.5", + "eslint": "^9.17.0", "eslint-config-custom": "workspace:*", "tsconfig": "workspace:*", - "tsup": "^8.2.2", - "typescript": "^5.5.4" + "tsup": "^8.3.5", + "typescript": "^5.7.2" } } diff --git a/packages/backend/src/altNodes/altConversion.ts b/packages/backend/src/altNodes/altConversion.ts index 665d1487..2a3210e0 100644 --- a/packages/backend/src/altNodes/altConversion.ts +++ b/packages/backend/src/altNodes/altConversion.ts @@ -17,6 +17,7 @@ export const cloneNode = (node: T): T => { prop !== "mainComponent" && prop !== "masterComponent" && prop !== "variantProperties" && + prop !== "get_annotations" && prop !== "componentPropertyDefinitions" && prop !== "exposedInstances" && prop !== "componentProperties" && @@ -154,6 +155,7 @@ export const convertIntoNodes = ( "textDecoration", "textStyleId", "fillStyleId", + "openTypeFeatures", ]); return standardClone(node, parent); case "STAR": diff --git a/packages/backend/src/flutter/flutterMain.ts b/packages/backend/src/flutter/flutterMain.ts index 7272db7c..9bc8d949 100644 --- a/packages/backend/src/flutter/flutterMain.ts +++ b/packages/backend/src/flutter/flutterMain.ts @@ -225,7 +225,7 @@ const addSpacingIfNeeded = ( const nodeParentLayout = optimizeLayout && node.parent && "itemSpacing" in node.parent ? node.parent.inferredAutoLayout - : (null ?? node.parent); + : node.parent; if ( nodeParentLayout && diff --git a/packages/backend/src/flutter/flutterTextBuilder.ts b/packages/backend/src/flutter/flutterTextBuilder.ts index 7c53cae4..58eb667d 100644 --- a/packages/backend/src/flutter/flutterTextBuilder.ts +++ b/packages/backend/src/flutter/flutterTextBuilder.ts @@ -58,7 +58,11 @@ export class FlutterTextBuilder extends FlutterDefaultBuilder { return this; } - getTextSegments(id: string): { style: string; text: string }[] { + getTextSegments(id: string): { + style: string; + text: string; + openTypeFeatures: { [key: string]: boolean }; + }[] { const segments = globalTextStyleSegments[id]; if (!segments) { return []; @@ -77,7 +81,7 @@ export class FlutterTextBuilder extends FlutterDefaultBuilder { segment.fontSize, ); - const style = generateWidgetCode("TextStyle", { + const styleProperties: { [key: string]: string } = { color: color, fontSize: fontSize, fontStyle: fontStyle, @@ -88,9 +92,17 @@ export class FlutterTextBuilder extends FlutterDefaultBuilder { "TextDecoration.none", ), // textTransform: textTransform, - height: lineHeight / fontSize, + height: lineHeight, letterSpacing: letterSpacing, - }); + }; + + if (segment.openTypeFeatures.SUBS === true) { + styleProperties.fontFeatures = `[FontFeature.enable("subs")]`; + } else if (segment.openTypeFeatures.SUPS === true) { + styleProperties.fontFeatures = `[FontFeature.enable("sups")]`; + } + + const style = generateWidgetCode("TextStyle", styleProperties); let text = segment.characters; if (segment.textCase === "LOWER") { @@ -102,6 +114,7 @@ export class FlutterTextBuilder extends FlutterDefaultBuilder { return { style: style, text: parseTextAsCode(text).replace(/\$/g, "\\$"), + openTypeFeatures: segment.openTypeFeatures, }; }); } @@ -151,7 +164,7 @@ export class FlutterTextBuilder extends FlutterDefaultBuilder { } export const wrapTextAutoResize = (node: TextNode, child: string): string => { - const { width, height, isExpanded } = flutterSize(node); + const { width, height, isExpanded } = flutterSize(node, false); let result = ""; switch (node.textAutoResize) { diff --git a/packages/backend/src/html/htmlMain.ts b/packages/backend/src/html/htmlMain.ts index 41c54fa1..fd5af99c 100644 --- a/packages/backend/src/html/htmlMain.ts +++ b/packages/backend/src/html/htmlMain.ts @@ -123,9 +123,29 @@ export const htmlText = (node: TextNode, isJsx: boolean): string => { if (styledHtml.length === 1) { layoutBuilder.addStyles(styledHtml[0].style); content = styledHtml[0].text; + + const additionalTag = + styledHtml[0].openTypeFeatures.SUBS === true + ? "sub" + : styledHtml[0].openTypeFeatures.SUPS === true + ? "sup" + : ""; + + if (additionalTag) { + content = `<${additionalTag}>${content}`; + } } else { content = styledHtml - .map((style) => `${style.text}`) + .map((style) => { + const tag = + style.openTypeFeatures.SUBS === true + ? "sub" + : style.openTypeFeatures.SUPS === true + ? "sup" + : "span"; + + return `<${tag} style="${style.style}">${style.text}`; + }) .join(""); } diff --git a/packages/backend/src/html/htmlTextBuilder.ts b/packages/backend/src/html/htmlTextBuilder.ts index 46ff1846..e3e6a33d 100644 --- a/packages/backend/src/html/htmlTextBuilder.ts +++ b/packages/backend/src/html/htmlTextBuilder.ts @@ -12,7 +12,13 @@ export class HtmlTextBuilder extends HtmlDefaultBuilder { super(node, showLayerNames, optIsJSX); } - getTextSegments(id: string): { style: string; text: string }[] { + getTextSegments( + id: string, + ): { + style: string; + text: string; + openTypeFeatures: { [key: string]: boolean }; + }[] { const segments = globalTextStyleSegments[id]; if (!segments) { return []; @@ -40,7 +46,11 @@ export class HtmlTextBuilder extends HtmlDefaultBuilder { ); const charsWithLineBreak = segment.characters.split("\n").join("
"); - return { style: styleAttributes, text: charsWithLineBreak }; + return { + style: styleAttributes, + text: charsWithLineBreak, + openTypeFeatures: segment.openTypeFeatures, + }; }); } diff --git a/packages/backend/src/tailwind/tailwindMain.ts b/packages/backend/src/tailwind/tailwindMain.ts index 77c35ecd..31711701 100644 --- a/packages/backend/src/tailwind/tailwindMain.ts +++ b/packages/backend/src/tailwind/tailwindMain.ts @@ -125,9 +125,29 @@ export const tailwindText = (node: TextNode, isJsx: boolean): string => { if (styledHtml.length === 1) { layoutBuilder.addAttributes(styledHtml[0].style); content = styledHtml[0].text; + + const additionalTag = + styledHtml[0].openTypeFeatures.SUBS === true + ? "sub" + : styledHtml[0].openTypeFeatures.SUPS === true + ? "sup" + : ""; + + if (additionalTag) { + content = `<${additionalTag}>${content}`; + } } else { content = styledHtml - .map((style) => `${style.text}`) + .map((style) => { + const tag = + style.openTypeFeatures.SUBS === true + ? "sub" + : style.openTypeFeatures.SUPS === true + ? "sup" + : "span"; + + return `<${tag} class="${style.style}">${style.text}`; + }) .join(""); } @@ -151,14 +171,24 @@ const tailwindFrame = ( if (node.layoutMode !== "NONE") { const rowColumn = tailwindAutoLayoutProps(node, node); - return tailwindContainer(node, childrenStr, rowColumn + clipsContentClass, isJsx); + return tailwindContainer( + node, + childrenStr, + rowColumn + clipsContentClass, + isJsx, + ); } else { if ( localTailwindSettings.optimizeLayout && node.inferredAutoLayout !== null ) { const rowColumn = tailwindAutoLayoutProps(node, node.inferredAutoLayout); - return tailwindContainer(node, childrenStr, rowColumn + clipsContentClass, isJsx); + return tailwindContainer( + node, + childrenStr, + rowColumn + clipsContentClass, + isJsx, + ); } // node.layoutMode === "NONE" && node.children.length > 1 diff --git a/packages/backend/src/tailwind/tailwindTextBuilder.ts b/packages/backend/src/tailwind/tailwindTextBuilder.ts index 06425abd..dbfc24b2 100644 --- a/packages/backend/src/tailwind/tailwindTextBuilder.ts +++ b/packages/backend/src/tailwind/tailwindTextBuilder.ts @@ -13,7 +13,11 @@ import { TailwindDefaultBuilder } from "./tailwindDefaultBuilder"; import { config } from "./tailwindConfig"; export class TailwindTextBuilder extends TailwindDefaultBuilder { - getTextSegments(id: string): { style: string; text: string }[] { + getTextSegments(id: string): { + style: string; + text: string; + openTypeFeatures: { [key: string]: boolean }; + }[] { const segments = globalTextStyleSegments[id]; if (!segments) { return []; @@ -48,7 +52,11 @@ export class TailwindTextBuilder extends TailwindDefaultBuilder { .join(" "); const charsWithLineBreak = segment.characters.split("\n").join("
"); - return { style: styleClasses, text: charsWithLineBreak }; + return { + style: styleClasses, + text: charsWithLineBreak, + openTypeFeatures: segment.openTypeFeatures, + }; }); } @@ -79,13 +87,13 @@ export class TailwindTextBuilder extends TailwindDefaultBuilder { fontFamily = (fontName: FontName): string => { if (config.fontFamily.sans.includes(fontName.family)) { - return 'font-sans'; + return "font-sans"; } if (config.fontFamily.serif.includes(fontName.family)) { - return 'font-serif'; + return "font-serif"; } if (config.fontFamily.mono.includes(fontName.family)) { - return 'font-mono'; + return "font-mono"; } return "font-['" + fontName.family + "']"; diff --git a/packages/plugin-ui/src/PluginUI.tsx b/packages/plugin-ui/src/PluginUI.tsx index a3ed7177..5e36c35d 100644 --- a/packages/plugin-ui/src/PluginUI.tsx +++ b/packages/plugin-ui/src/PluginUI.tsx @@ -370,8 +370,8 @@ export const CodePanel = (props: { title={option.label} isSelected={ option.value === - props.preferences?.[preference.propertyName] ?? - option.isDefault + (props.preferences?.[preference.propertyName] ?? + option.isDefault) } onSelect={() => { props.onPreferenceChange( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cbb67d35..a6758e26 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -70,8 +70,8 @@ importers: apps/plugin: dependencies: '@figma/plugin-typings': - specifier: ^1.97.0 - version: 1.97.0 + specifier: ^1.105.0 + version: 1.105.0 backend: specifier: workspace:* version: link:../../packages/backend @@ -86,47 +86,47 @@ importers: version: 18.3.1(react@18.3.1) devDependencies: '@types/node': - specifier: ^20.14.11 - version: 20.14.11 + specifier: ^20.17.10 + version: 20.17.10 '@types/react': - specifier: ^18.3.3 - version: 18.3.3 + specifier: ^18.3.17 + version: 18.3.17 '@types/react-dom': - specifier: ^18.3.0 - version: 18.3.0 + specifier: ^18.3.5 + version: 18.3.5(@types/react@18.3.17) '@typescript-eslint/eslint-plugin': - specifier: ^7.17.0 - version: 7.17.0(@typescript-eslint/parser@7.17.0(eslint@9.7.0)(typescript@5.5.4))(eslint@9.7.0)(typescript@5.5.4) + specifier: ^7.18.0 + version: 7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2) '@typescript-eslint/parser': - specifier: ^7.17.0 - version: 7.17.0(eslint@9.7.0)(typescript@5.5.4) + specifier: ^7.18.0 + version: 7.18.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2) '@vitejs/plugin-react': - specifier: ^4.3.1 - version: 4.3.1(vite@5.3.4(@types/node@20.14.11)) + specifier: ^4.3.4 + version: 4.3.4(vite@5.4.11(@types/node@20.17.10)) '@vitejs/plugin-react-swc': - specifier: ^3.7.0 - version: 3.7.0(@swc/helpers@0.5.12)(vite@5.3.4(@types/node@20.14.11)) + specifier: ^3.7.2 + version: 3.7.2(@swc/helpers@0.5.12)(vite@5.4.11(@types/node@20.17.10)) autoprefixer: - specifier: ^10.4.19 - version: 10.4.19(postcss@8.4.39) + specifier: ^10.4.20 + version: 10.4.20(postcss@8.4.49) concurrently: specifier: ^8.2.2 version: 8.2.2 esbuild: - specifier: ^0.23.0 - version: 0.23.0 + specifier: ^0.23.1 + version: 0.23.1 eslint-config-custom: specifier: workspace:* version: link:../../packages/eslint-config-custom eslint-plugin-react-hooks: specifier: ^4.6.2 - version: 4.6.2(eslint@9.7.0) + version: 4.6.2(eslint@9.17.0(jiti@1.21.7)) eslint-plugin-react-refresh: - specifier: ^0.4.9 - version: 0.4.9(eslint@9.7.0) + specifier: ^0.4.16 + version: 0.4.16(eslint@9.17.0(jiti@1.21.7)) postcss: - specifier: ^8.4.39 - version: 8.4.39 + specifier: ^8.4.49 + version: 8.4.49 tailwindcss: specifier: 3.4.6 version: 3.4.6 @@ -134,20 +134,20 @@ importers: specifier: workspace:* version: link:../../packages/tsconfig typescript: - specifier: ^5.5.4 - version: 5.5.4 + specifier: ^5.7.2 + version: 5.7.2 vite: - specifier: ^5.3.4 - version: 5.3.4(@types/node@20.14.11) + specifier: ^5.4.11 + version: 5.4.11(@types/node@20.17.10) vite-plugin-singlefile: - specifier: ^2.0.2 - version: 2.0.2(rollup@4.19.0)(vite@5.3.4(@types/node@20.14.11)) + specifier: ^2.1.0 + version: 2.1.0(rollup@4.28.1)(vite@5.4.11(@types/node@20.17.10)) packages/backend: dependencies: '@figma/plugin-typings': - specifier: ^1.97.0 - version: 1.97.0 + specifier: ^1.105.0 + version: 1.105.0 react: specifier: 18.3.1 version: 18.3.1 @@ -156,14 +156,14 @@ importers: version: 18.3.1(react@18.3.1) devDependencies: '@types/react': - specifier: ^18.3.3 - version: 18.3.3 + specifier: ^18.3.17 + version: 18.3.17 '@types/react-dom': - specifier: ^18.3.0 - version: 18.3.0 + specifier: ^18.3.5 + version: 18.3.5(@types/react@18.3.17) eslint: - specifier: ^9.7.0 - version: 9.7.0 + specifier: ^9.17.0 + version: 9.17.0(jiti@1.21.7) eslint-config-custom: specifier: workspace:* version: link:../eslint-config-custom @@ -171,26 +171,26 @@ importers: specifier: workspace:* version: link:../tsconfig tsup: - specifier: ^8.2.2 - version: 8.2.2(@swc/core@1.7.0)(jiti@1.21.6)(postcss@8.4.39)(typescript@5.5.4)(yaml@2.4.5) + specifier: ^8.3.5 + version: 8.3.5(@swc/core@1.10.1(@swc/helpers@0.5.12))(jiti@1.21.7)(postcss@8.4.49)(typescript@5.7.2)(yaml@2.6.1) typescript: - specifier: ^5.5.4 - version: 5.5.4 + specifier: ^5.7.2 + version: 5.7.2 packages/eslint-config-custom: dependencies: eslint-config-next: specifier: ^14.2.5 - version: 14.2.5(eslint@9.7.0)(typescript@5.5.4) + version: 14.2.5(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2) eslint-config-prettier: specifier: ^9.1.0 - version: 9.1.0(eslint@9.7.0) + version: 9.1.0(eslint@9.17.0(jiti@1.21.7)) eslint-config-turbo: specifier: ^2.0.9 - version: 2.0.9(eslint@9.7.0) + version: 2.0.9(eslint@9.17.0(jiti@1.21.7)) eslint-plugin-react: specifier: 7.35.0 - version: 7.35.0(eslint@9.7.0) + version: 7.35.0(eslint@9.17.0(jiti@1.21.7)) packages/plugin-ui: dependencies: @@ -241,93 +241,69 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@babel/code-frame@7.24.7': - resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} + '@babel/code-frame@7.26.2': + resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.24.9': - resolution: {integrity: sha512-e701mcfApCJqMMueQI0Fb68Amflj83+dvAvHawoBpAz+GDjCIyGHzNwnefjsWJ3xiYAqqiQFoWbspGYBdb2/ng==} + '@babel/compat-data@7.26.3': + resolution: {integrity: sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==} engines: {node: '>=6.9.0'} - '@babel/core@7.24.9': - resolution: {integrity: sha512-5e3FI4Q3M3Pbr21+5xJwCv6ZT6KmGkI0vw3Tozy5ODAQFTIWe37iT8Cr7Ice2Ntb+M3iSKCEWMB1MBgKrW3whg==} + '@babel/core@7.26.0': + resolution: {integrity: sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==} engines: {node: '>=6.9.0'} - '@babel/generator@7.24.10': - resolution: {integrity: sha512-o9HBZL1G2129luEUlG1hB4N/nlYNWHnpwlND9eOMclRqqu1YDy2sSYVCFUZwl8I1Gxh+QSRrP2vD7EpUmFVXxg==} + '@babel/generator@7.26.3': + resolution: {integrity: sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.24.8': - resolution: {integrity: sha512-oU+UoqCHdp+nWVDkpldqIQL/i/bvAv53tRqLG/s+cOXxe66zOYLU7ar/Xs3LdmBihrUMEUhwu6dMZwbNOYDwvw==} + '@babel/helper-compilation-targets@7.25.9': + resolution: {integrity: sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==} engines: {node: '>=6.9.0'} - '@babel/helper-environment-visitor@7.24.7': - resolution: {integrity: sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==} + '@babel/helper-module-imports@7.25.9': + resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==} engines: {node: '>=6.9.0'} - '@babel/helper-function-name@7.24.7': - resolution: {integrity: sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-hoist-variables@7.24.7': - resolution: {integrity: sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-imports@7.24.7': - resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-transforms@7.24.9': - resolution: {integrity: sha512-oYbh+rtFKj/HwBQkFlUzvcybzklmVdVV3UU+mN7n2t/q3yGHbuVdNxyFvSBO1tfvjyArpHNcWMAzsSPdyI46hw==} + '@babel/helper-module-transforms@7.26.0': + resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-plugin-utils@7.24.8': - resolution: {integrity: sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==} - engines: {node: '>=6.9.0'} - - '@babel/helper-simple-access@7.24.7': - resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==} - engines: {node: '>=6.9.0'} - - '@babel/helper-split-export-declaration@7.24.7': - resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-string-parser@7.24.8': - resolution: {integrity: sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==} + '@babel/helper-plugin-utils@7.25.9': + resolution: {integrity: sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.24.7': - resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} + '@babel/helper-string-parser@7.25.9': + resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.24.8': - resolution: {integrity: sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==} + '@babel/helper-validator-identifier@7.25.9': + resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.24.8': - resolution: {integrity: sha512-gV2265Nkcz7weJJfvDoAEVzC1e2OTDpkGbEsebse8koXUJUXPsCMi7sRo/+SPMuMZ9MtUPnGwITTnQnU5YjyaQ==} + '@babel/helper-validator-option@7.25.9': + resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==} engines: {node: '>=6.9.0'} - '@babel/highlight@7.24.7': - resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} + '@babel/helpers@7.26.0': + resolution: {integrity: sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==} engines: {node: '>=6.9.0'} - '@babel/parser@7.24.8': - resolution: {integrity: sha512-WzfbgXOkGzZiXXCqk43kKwZjzwx4oulxZi3nq2TYL9mOjQv6kYwul9mz6ID36njuL7Xkp6nJEfok848Zj10j/w==} + '@babel/parser@7.26.3': + resolution: {integrity: sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/plugin-transform-react-jsx-self@7.24.7': - resolution: {integrity: sha512-fOPQYbGSgH0HUp4UJO4sMBFjY6DuWq+2i8rixyUMb3CdGixs/gccURvYOAhajBdKDoGajFr3mUq5rH3phtkGzw==} + '@babel/plugin-transform-react-jsx-self@7.25.9': + resolution: {integrity: sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-jsx-source@7.24.7': - resolution: {integrity: sha512-J2z+MWzZHVOemyLweMqngXrgGC42jQ//R0KdxqkIz/OrbVIIlhFI3WigZ5fO+nwFvBlncr4MGapd8vTyc7RPNQ==} + '@babel/plugin-transform-react-jsx-source@7.25.9': + resolution: {integrity: sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -336,16 +312,20 @@ packages: resolution: {integrity: sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==} engines: {node: '>=6.9.0'} - '@babel/template@7.24.7': - resolution: {integrity: sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==} + '@babel/runtime@7.26.0': + resolution: {integrity: sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.24.8': - resolution: {integrity: sha512-t0P1xxAPzEDcEPmjprAQq19NWum4K0EQPjMwZQZbHt+GiZqvjCHjj755Weq1YRPVzBI+3zSfvScfpnuIecVFJQ==} + '@babel/template@7.25.9': + resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==} engines: {node: '>=6.9.0'} - '@babel/types@7.24.9': - resolution: {integrity: sha512-xm8XrMKz0IlUdocVbYJe0Z9xEgidU7msskG8BbhnTPK/HZ2z/7FP7ykqPgrUH+C+r414mNfNWam1f2vqOjqjYQ==} + '@babel/traverse@7.26.4': + resolution: {integrity: sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.26.3': + resolution: {integrity: sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==} engines: {node: '>=6.9.0'} '@esbuild/aix-ppc64@0.21.5': @@ -354,8 +334,14 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.23.0': - resolution: {integrity: sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ==} + '@esbuild/aix-ppc64@0.23.1': + resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/aix-ppc64@0.24.0': + resolution: {integrity: sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] @@ -366,8 +352,14 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.23.0': - resolution: {integrity: sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ==} + '@esbuild/android-arm64@0.23.1': + resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm64@0.24.0': + resolution: {integrity: sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==} engines: {node: '>=18'} cpu: [arm64] os: [android] @@ -378,8 +370,14 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.23.0': - resolution: {integrity: sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g==} + '@esbuild/android-arm@0.23.1': + resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-arm@0.24.0': + resolution: {integrity: sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==} engines: {node: '>=18'} cpu: [arm] os: [android] @@ -390,8 +388,14 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.23.0': - resolution: {integrity: sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ==} + '@esbuild/android-x64@0.23.1': + resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/android-x64@0.24.0': + resolution: {integrity: sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==} engines: {node: '>=18'} cpu: [x64] os: [android] @@ -402,8 +406,14 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.23.0': - resolution: {integrity: sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow==} + '@esbuild/darwin-arm64@0.23.1': + resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-arm64@0.24.0': + resolution: {integrity: sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] @@ -414,8 +424,14 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.23.0': - resolution: {integrity: sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ==} + '@esbuild/darwin-x64@0.23.1': + resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/darwin-x64@0.24.0': + resolution: {integrity: sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==} engines: {node: '>=18'} cpu: [x64] os: [darwin] @@ -426,8 +442,14 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.23.0': - resolution: {integrity: sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw==} + '@esbuild/freebsd-arm64@0.23.1': + resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-arm64@0.24.0': + resolution: {integrity: sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] @@ -438,8 +460,14 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.23.0': - resolution: {integrity: sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ==} + '@esbuild/freebsd-x64@0.23.1': + resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.24.0': + resolution: {integrity: sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] @@ -450,8 +478,14 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.23.0': - resolution: {integrity: sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw==} + '@esbuild/linux-arm64@0.23.1': + resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm64@0.24.0': + resolution: {integrity: sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==} engines: {node: '>=18'} cpu: [arm64] os: [linux] @@ -462,8 +496,14 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.23.0': - resolution: {integrity: sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw==} + '@esbuild/linux-arm@0.23.1': + resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-arm@0.24.0': + resolution: {integrity: sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==} engines: {node: '>=18'} cpu: [arm] os: [linux] @@ -474,8 +514,14 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.23.0': - resolution: {integrity: sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA==} + '@esbuild/linux-ia32@0.23.1': + resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-ia32@0.24.0': + resolution: {integrity: sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==} engines: {node: '>=18'} cpu: [ia32] os: [linux] @@ -486,8 +532,14 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.23.0': - resolution: {integrity: sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A==} + '@esbuild/linux-loong64@0.23.1': + resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-loong64@0.24.0': + resolution: {integrity: sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==} engines: {node: '>=18'} cpu: [loong64] os: [linux] @@ -498,8 +550,14 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.23.0': - resolution: {integrity: sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w==} + '@esbuild/linux-mips64el@0.23.1': + resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-mips64el@0.24.0': + resolution: {integrity: sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] @@ -510,8 +568,14 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.23.0': - resolution: {integrity: sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw==} + '@esbuild/linux-ppc64@0.23.1': + resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-ppc64@0.24.0': + resolution: {integrity: sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] @@ -522,8 +586,14 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.23.0': - resolution: {integrity: sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw==} + '@esbuild/linux-riscv64@0.23.1': + resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-riscv64@0.24.0': + resolution: {integrity: sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] @@ -534,8 +604,14 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.23.0': - resolution: {integrity: sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg==} + '@esbuild/linux-s390x@0.23.1': + resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-s390x@0.24.0': + resolution: {integrity: sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==} engines: {node: '>=18'} cpu: [s390x] os: [linux] @@ -546,8 +622,14 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.23.0': - resolution: {integrity: sha512-a3pMQhUEJkITgAw6e0bWA+F+vFtCciMjW/LPtoj99MhVt+Mfb6bbL9hu2wmTZgNd994qTAEw+U/r6k3qHWWaOQ==} + '@esbuild/linux-x64@0.23.1': + resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/linux-x64@0.24.0': + resolution: {integrity: sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==} engines: {node: '>=18'} cpu: [x64] os: [linux] @@ -558,14 +640,26 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.23.0': - resolution: {integrity: sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw==} + '@esbuild/netbsd-x64@0.23.1': + resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.23.0': - resolution: {integrity: sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ==} + '@esbuild/netbsd-x64@0.24.0': + resolution: {integrity: sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.23.1': + resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-arm64@0.24.0': + resolution: {integrity: sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] @@ -576,8 +670,14 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.23.0': - resolution: {integrity: sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg==} + '@esbuild/openbsd-x64@0.23.1': + resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.24.0': + resolution: {integrity: sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] @@ -588,8 +688,14 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.23.0': - resolution: {integrity: sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA==} + '@esbuild/sunos-x64@0.23.1': + resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/sunos-x64@0.24.0': + resolution: {integrity: sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] @@ -600,8 +706,14 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.23.0': - resolution: {integrity: sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==} + '@esbuild/win32-arm64@0.23.1': + resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-arm64@0.24.0': + resolution: {integrity: sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==} engines: {node: '>=18'} cpu: [arm64] os: [win32] @@ -612,8 +724,14 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.23.0': - resolution: {integrity: sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==} + '@esbuild/win32-ia32@0.23.1': + resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.24.0': + resolution: {integrity: sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==} engines: {node: '>=18'} cpu: [ia32] os: [win32] @@ -624,8 +742,14 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.23.0': - resolution: {integrity: sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g==} + '@esbuild/win32-x64@0.23.1': + resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.24.0': + resolution: {integrity: sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -636,18 +760,44 @@ packages: peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/eslint-utils@4.4.1': + resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/regexpp@4.11.0': resolution: {integrity: sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + '@eslint-community/regexpp@4.12.1': + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + '@eslint/config-array@0.17.1': resolution: {integrity: sha512-BlYOpej8AQ8Ev9xVqroV7a02JK3SkBAaN9GfMMH9W6Ch8FlQlkjGw4Ir7+FgYwfirivAf4t+GtzuAxqfukmISA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/config-array@0.19.1': + resolution: {integrity: sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.9.1': + resolution: {integrity: sha512-GuUdqkyyzQI5RMIWkHhvTWLCyLo1jNK3vzkSyaExH5kHPDHcuL2VOpHjmMY+y3+NC69qAKToBqldTBgYeLSr9Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/eslintrc@3.1.0': resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/eslintrc@3.2.0': + resolution: {integrity: sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.17.0': + resolution: {integrity: sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/js@9.7.0': resolution: {integrity: sha512-ChuWDQenef8OSFnvuxv0TCVxEwmu3+hPNKvM9B34qpM0rDRbjL8t5QkQeHHeAfsKQjuH9wS82WeCi1J/owatng==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -656,8 +806,24 @@ packages: resolution: {integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@figma/plugin-typings@1.97.0': - resolution: {integrity: sha512-AcmZey7TBbc43g2dO+9hjrcTbgb0UFY32do3to3rFU1OXb9hinsrmmbddyhD5105DHzRDac4oT7A5+VOow7p1Q==} + '@eslint/object-schema@2.1.5': + resolution: {integrity: sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.2.4': + resolution: {integrity: sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@figma/plugin-typings@1.105.0': + resolution: {integrity: sha512-UpeS7TUvc4Mv9OP+RMI6GtBTKpD9EMTwsa3H1M6GPE5zTicr/PnBUrrkU52PFXqb9FeVZhMopCdPm7AXQeDa7Q==} + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.6': + resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} + engines: {node: '>=18.18.0'} '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} @@ -667,12 +833,20 @@ packages: resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==} engines: {node: '>=18.18'} + '@humanwhocodes/retry@0.3.1': + resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} + engines: {node: '>=18.18'} + + '@humanwhocodes/retry@0.4.1': + resolution: {integrity: sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==} + engines: {node: '>=18.18'} + '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} - '@jridgewell/gen-mapping@0.3.5': - resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} + '@jridgewell/gen-mapping@0.3.8': + resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} engines: {node: '>=6.0.0'} '@jridgewell/resolve-uri@3.1.2': @@ -765,151 +939,166 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@rollup/rollup-android-arm-eabi@4.19.0': - resolution: {integrity: sha512-JlPfZ/C7yn5S5p0yKk7uhHTTnFlvTgLetl2VxqE518QgyM7C9bSfFTYvB/Q/ftkq0RIPY4ySxTz+/wKJ/dXC0w==} + '@rollup/rollup-android-arm-eabi@4.28.1': + resolution: {integrity: sha512-2aZp8AES04KI2dy3Ss6/MDjXbwBzj+i0GqKtWXgw2/Ma6E4jJvujryO6gJAghIRVz7Vwr9Gtl/8na3nDUKpraQ==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.19.0': - resolution: {integrity: sha512-RDxUSY8D1tWYfn00DDi5myxKgOk6RvWPxhmWexcICt/MEC6yEMr4HNCu1sXXYLw8iAsg0D44NuU+qNq7zVWCrw==} + '@rollup/rollup-android-arm64@4.28.1': + resolution: {integrity: sha512-EbkK285O+1YMrg57xVA+Dp0tDBRB93/BZKph9XhMjezf6F4TpYjaUSuPt5J0fZXlSag0LmZAsTmdGGqPp4pQFA==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.19.0': - resolution: {integrity: sha512-emvKHL4B15x6nlNTBMtIaC9tLPRpeA5jMvRLXVbl/W9Ie7HhkrE7KQjvgS9uxgatL1HmHWDXk5TTS4IaNJxbAA==} + '@rollup/rollup-darwin-arm64@4.28.1': + resolution: {integrity: sha512-prduvrMKU6NzMq6nxzQw445zXgaDBbMQvmKSJaxpaZ5R1QDM8w+eGxo6Y/jhT/cLoCvnZI42oEqf9KQNYz1fqQ==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.19.0': - resolution: {integrity: sha512-fO28cWA1dC57qCd+D0rfLC4VPbh6EOJXrreBmFLWPGI9dpMlER2YwSPZzSGfq11XgcEpPukPTfEVFtw2q2nYJg==} + '@rollup/rollup-darwin-x64@4.28.1': + resolution: {integrity: sha512-WsvbOunsUk0wccO/TV4o7IKgloJ942hVFK1CLatwv6TJspcCZb9umQkPdvB7FihmdxgaKR5JyxDjWpCOp4uZlQ==} cpu: [x64] os: [darwin] - '@rollup/rollup-linux-arm-gnueabihf@4.19.0': - resolution: {integrity: sha512-2Rn36Ubxdv32NUcfm0wB1tgKqkQuft00PtM23VqLuCUR4N5jcNWDoV5iBC9jeGdgS38WK66ElncprqgMUOyomw==} + '@rollup/rollup-freebsd-arm64@4.28.1': + resolution: {integrity: sha512-HTDPdY1caUcU4qK23FeeGxCdJF64cKkqajU0iBnTVxS8F7H/7BewvYoG+va1KPSL63kQ1PGNyiwKOfReavzvNA==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.28.1': + resolution: {integrity: sha512-m/uYasxkUevcFTeRSM9TeLyPe2QDuqtjkeoTpP9SW0XxUWfcYrGDMkO/m2tTw+4NMAF9P2fU3Mw4ahNvo7QmsQ==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.28.1': + resolution: {integrity: sha512-QAg11ZIt6mcmzpNE6JZBpKfJaKkqTm1A9+y9O+frdZJEuhQxiugM05gnCWiANHj4RmbgeVJpTdmKRmH/a+0QbA==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.19.0': - resolution: {integrity: sha512-gJuzIVdq/X1ZA2bHeCGCISe0VWqCoNT8BvkQ+BfsixXwTOndhtLUpOg0A1Fcx/+eA6ei6rMBzlOz4JzmiDw7JQ==} + '@rollup/rollup-linux-arm-musleabihf@4.28.1': + resolution: {integrity: sha512-dRP9PEBfolq1dmMcFqbEPSd9VlRuVWEGSmbxVEfiq2cs2jlZAl0YNxFzAQS2OrQmsLBLAATDMb3Z6MFv5vOcXg==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.19.0': - resolution: {integrity: sha512-0EkX2HYPkSADo9cfeGFoQ7R0/wTKb7q6DdwI4Yn/ULFE1wuRRCHybxpl2goQrx4c/yzK3I8OlgtBu4xvted0ug==} + '@rollup/rollup-linux-arm64-gnu@4.28.1': + resolution: {integrity: sha512-uGr8khxO+CKT4XU8ZUH1TTEUtlktK6Kgtv0+6bIFSeiSlnGJHG1tSFSjm41uQ9sAO/5ULx9mWOz70jYLyv1QkA==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.19.0': - resolution: {integrity: sha512-GlIQRj9px52ISomIOEUq/IojLZqzkvRpdP3cLgIE1wUWaiU5Takwlzpz002q0Nxxr1y2ZgxC2obWxjr13lvxNQ==} + '@rollup/rollup-linux-arm64-musl@4.28.1': + resolution: {integrity: sha512-QF54q8MYGAqMLrX2t7tNpi01nvq5RI59UBNx+3+37zoKX5KViPo/gk2QLhsuqok05sSCRluj0D00LzCwBikb0A==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.19.0': - resolution: {integrity: sha512-N6cFJzssruDLUOKfEKeovCKiHcdwVYOT1Hs6dovDQ61+Y9n3Ek4zXvtghPPelt6U0AH4aDGnDLb83uiJMkWYzQ==} + '@rollup/rollup-linux-loongarch64-gnu@4.28.1': + resolution: {integrity: sha512-vPul4uodvWvLhRco2w0GcyZcdyBfpfDRgNKU+p35AWEbJ/HPs1tOUrkSueVbBS0RQHAf/A+nNtDpvw95PeVKOA==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.28.1': + resolution: {integrity: sha512-pTnTdBuC2+pt1Rmm2SV7JWRqzhYpEILML4PKODqLz+C7Ou2apEV52h19CR7es+u04KlqplggmN9sqZlekg3R1A==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.19.0': - resolution: {integrity: sha512-2DnD3mkS2uuam/alF+I7M84koGwvn3ZVD7uG+LEWpyzo/bq8+kKnus2EVCkcvh6PlNB8QPNFOz6fWd5N8o1CYg==} + '@rollup/rollup-linux-riscv64-gnu@4.28.1': + resolution: {integrity: sha512-vWXy1Nfg7TPBSuAncfInmAI/WZDd5vOklyLJDdIRKABcZWojNDY0NJwruY2AcnCLnRJKSaBgf/GiJfauu8cQZA==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.19.0': - resolution: {integrity: sha512-D6pkaF7OpE7lzlTOFCB2m3Ngzu2ykw40Nka9WmKGUOTS3xcIieHe82slQlNq69sVB04ch73thKYIWz/Ian8DUA==} + '@rollup/rollup-linux-s390x-gnu@4.28.1': + resolution: {integrity: sha512-/yqC2Y53oZjb0yz8PVuGOQQNOTwxcizudunl/tFs1aLvObTclTwZ0JhXF2XcPT/zuaymemCDSuuUPXJJyqeDOg==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.19.0': - resolution: {integrity: sha512-HBndjQLP8OsdJNSxpNIN0einbDmRFg9+UQeZV1eiYupIRuZsDEoeGU43NQsS34Pp166DtwQOnpcbV/zQxM+rWA==} + '@rollup/rollup-linux-x64-gnu@4.28.1': + resolution: {integrity: sha512-fzgeABz7rrAlKYB0y2kSEiURrI0691CSL0+KXwKwhxvj92VULEDQLpBYLHpF49MSiPG4sq5CK3qHMnb9tlCjBw==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.19.0': - resolution: {integrity: sha512-HxfbvfCKJe/RMYJJn0a12eiOI9OOtAUF4G6ozrFUK95BNyoJaSiBjIOHjZskTUffUrB84IPKkFG9H9nEvJGW6A==} + '@rollup/rollup-linux-x64-musl@4.28.1': + resolution: {integrity: sha512-xQTDVzSGiMlSshpJCtudbWyRfLaNiVPXt1WgdWTwWz9n0U12cI2ZVtWe/Jgwyv/6wjL7b66uu61Vg0POWVfz4g==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.19.0': - resolution: {integrity: sha512-HxDMKIhmcguGTiP5TsLNolwBUK3nGGUEoV/BO9ldUBoMLBssvh4J0X8pf11i1fTV7WShWItB1bKAKjX4RQeYmg==} + '@rollup/rollup-win32-arm64-msvc@4.28.1': + resolution: {integrity: sha512-wSXmDRVupJstFP7elGMgv+2HqXelQhuNf+IS4V+nUpNVi/GUiBgDmfwD0UGN3pcAnWsgKG3I52wMOBnk1VHr/A==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.19.0': - resolution: {integrity: sha512-xItlIAZZaiG/u0wooGzRsx11rokP4qyc/79LkAOdznGRAbOFc+SfEdfUOszG1odsHNgwippUJavag/+W/Etc6Q==} + '@rollup/rollup-win32-ia32-msvc@4.28.1': + resolution: {integrity: sha512-ZkyTJ/9vkgrE/Rk9vhMXhf8l9D+eAhbAVbsGsXKy2ohmJaWg0LPQLnIxRdRp/bKyr8tXuPlXhIoGlEB5XpJnGA==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.19.0': - resolution: {integrity: sha512-xNo5fV5ycvCCKqiZcpB65VMR11NJB+StnxHz20jdqRAktfdfzhgjTiJ2doTDQE/7dqGaV5I7ZGqKpgph6lCIag==} + '@rollup/rollup-win32-x64-msvc@4.28.1': + resolution: {integrity: sha512-ZvK2jBafvttJjoIdKm/Q/Bh7IJ1Ose9IBOwpOXcOvW3ikGTQGmKDgxTC6oCAzW6PynbkKP8+um1du81XJHZ0JA==} cpu: [x64] os: [win32] '@rushstack/eslint-patch@1.10.3': resolution: {integrity: sha512-qC/xYId4NMebE6w/V33Fh9gWxLgURiNYgVNObbJl2LZv0GUUItCcCqC5axQSwRaAgaxl2mELq1rMzlswaQ0Zxg==} - '@swc/core-darwin-arm64@1.7.0': - resolution: {integrity: sha512-2ylhM7f0HwUwLrFYZAe/dse8PCbPsYcJS3Dt7Q8NT3PUn7vy6QOMxNcOPPuDrnmaXqQQO3oxdmRapguTxaat9g==} + '@swc/core-darwin-arm64@1.10.1': + resolution: {integrity: sha512-NyELPp8EsVZtxH/mEqvzSyWpfPJ1lugpTQcSlMduZLj1EASLO4sC8wt8hmL1aizRlsbjCX+r0PyL+l0xQ64/6Q==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] - '@swc/core-darwin-x64@1.7.0': - resolution: {integrity: sha512-SgVnN4gT1Rb9YfTkp4FCUITqSs7Yj0uB2SUciu5CV3HuGvS5YXCUzh+KrwpLFtx8NIgivISKcNnb41mJi98X8Q==} + '@swc/core-darwin-x64@1.10.1': + resolution: {integrity: sha512-L4BNt1fdQ5ZZhAk5qoDfUnXRabDOXKnXBxMDJ+PWLSxOGBbWE6aJTnu4zbGjJvtot0KM46m2LPAPY8ttknqaZA==} engines: {node: '>=10'} cpu: [x64] os: [darwin] - '@swc/core-linux-arm-gnueabihf@1.7.0': - resolution: {integrity: sha512-+Z9Dayart1iKJQEJJ9N/KS4z5EdXJE3WPFikY0jonKTo4Dd8RuyVz5yLvqcIMeVdz/SwximATaL6iJXw7hZS9A==} + '@swc/core-linux-arm-gnueabihf@1.10.1': + resolution: {integrity: sha512-Y1u9OqCHgvVp2tYQAJ7hcU9qO5brDMIrA5R31rwWQIAKDkJKtv3IlTHF0hrbWk1wPR0ZdngkQSJZple7G+Grvw==} engines: {node: '>=10'} cpu: [arm] os: [linux] - '@swc/core-linux-arm64-gnu@1.7.0': - resolution: {integrity: sha512-UnLrCiZ1EI4shznJn0xP6DLgsXUSwtfsdgHhGYCrvbgVBBve3S9iFgVFEB3SPl7Q/TdowNbrN4zHU0oChfiNfw==} + '@swc/core-linux-arm64-gnu@1.10.1': + resolution: {integrity: sha512-tNQHO/UKdtnqjc7o04iRXng1wTUXPgVd8Y6LI4qIbHVoVPwksZydISjMcilKNLKIwOoUQAkxyJ16SlOAeADzhQ==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-arm64-musl@1.7.0': - resolution: {integrity: sha512-H724UANA+ptsfwKRr9mnaDa9cb5fw0oFysiGKTgb3DMYcgk3Od0jMTnXVPFSVpo7FlmyxeC9K8ueUPBOoOK6XA==} + '@swc/core-linux-arm64-musl@1.10.1': + resolution: {integrity: sha512-x0L2Pd9weQ6n8dI1z1Isq00VHFvpBClwQJvrt3NHzmR+1wCT/gcYl1tp9P5xHh3ldM8Cn4UjWCw+7PaUgg8FcQ==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-x64-gnu@1.7.0': - resolution: {integrity: sha512-SY3HA0K0Dpqt1HIfMLGpwL4hd4UaL2xHP5oZXPlRQPhUDZrbb4PbI3ZJnh66c63eL4ZR8EJ+HRFI0Alx5p69Zw==} + '@swc/core-linux-x64-gnu@1.10.1': + resolution: {integrity: sha512-yyYEwQcObV3AUsC79rSzN9z6kiWxKAVJ6Ntwq2N9YoZqSPYph+4/Am5fM1xEQYf/kb99csj0FgOelomJSobxQA==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-linux-x64-musl@1.7.0': - resolution: {integrity: sha512-cEJ2ebtV1v/5Ilb55E05J6F5SrHKQWzUttIhR5Mkayyo+yvPslcpByuFC3D+J7X1ebziTOBpWuMpUdjLfh3SMQ==} + '@swc/core-linux-x64-musl@1.10.1': + resolution: {integrity: sha512-tcaS43Ydd7Fk7sW5ROpaf2Kq1zR+sI5K0RM+0qYLYYurvsJruj3GhBCaiN3gkzd8m/8wkqNqtVklWaQYSDsyqA==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-win32-arm64-msvc@1.7.0': - resolution: {integrity: sha512-ecQOOmzEssz+m0pR4xDYCGuvn3E/l0nQ3tk5jp1NA1lsAy4bMV0YbYCHjptYvWL/UjhIerIp3IlCJ8x5DodSog==} + '@swc/core-win32-arm64-msvc@1.10.1': + resolution: {integrity: sha512-D3Qo1voA7AkbOzQ2UGuKNHfYGKL6eejN8VWOoQYtGHHQi1p5KK/Q7V1ku55oxXBsj79Ny5FRMqiRJpVGad7bjQ==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - '@swc/core-win32-ia32-msvc@1.7.0': - resolution: {integrity: sha512-gz81seZkRn3zMnVOc7L5k6F4vQC82gIxmHiL+GedK+A37XI/X26AASU3zxvORnqQbwQYXQ+AEVckxBmFlz3v2g==} + '@swc/core-win32-ia32-msvc@1.10.1': + resolution: {integrity: sha512-WalYdFoU3454Og+sDKHM1MrjvxUGwA2oralknXkXL8S0I/8RkWZOB++p3pLaGbTvOO++T+6znFbQdR8KRaa7DA==} engines: {node: '>=10'} cpu: [ia32] os: [win32] - '@swc/core-win32-x64-msvc@1.7.0': - resolution: {integrity: sha512-b5Fd1xEOw9uqBpj2lqsaR4Iq9UhiL84hNDcEsi6DQA7Y1l85waQAslTbS0E4/pJ1PISAs0jW0zIGLco1eaWBOg==} + '@swc/core-win32-x64-msvc@1.10.1': + resolution: {integrity: sha512-JWobfQDbTnoqaIwPKQ3DVSywihVXlQMbDuwik/dDWlj33A8oEHcjPOGs4OqcA3RHv24i+lfCQpM3Mn4FAMfacA==} engines: {node: '>=10'} cpu: [x64] os: [win32] - '@swc/core@1.7.0': - resolution: {integrity: sha512-d4vMzH6ICllDwlPuhset2h8gu/USHdbyfJim+2hQEdxC0UONtfpmu38XBgNqRjStrji1Q5M10jfeUZL3cu1i8g==} + '@swc/core@1.10.1': + resolution: {integrity: sha512-rQ4dS6GAdmtzKiCRt3LFVxl37FaY1cgL9kSUTnhQ2xc3fmHOd7jdJK/V4pSZMG1ruGTd0bsi34O2R0Olg9Zo/w==} engines: {node: '>=10'} peerDependencies: '@swc/helpers': '*' @@ -926,8 +1115,8 @@ packages: '@swc/helpers@0.5.5': resolution: {integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==} - '@swc/types@0.1.12': - resolution: {integrity: sha512-wBJA+SdtkbFhHjTMYH+dEH1y4VpfGdAc2Kw/LK09i9bXd/K6j6PkDcFCEzb6iVfZMkPRrl/q0e3toqTAJdkIVA==} + '@swc/types@0.1.17': + resolution: {integrity: sha512-V5gRru+aD8YVyCOMAjMpWR1Ui577DD5KSJsHP8RAxopAH22jFz6GZd/qxqjO6MJHQhcsjvjOFXyDhyLQUnMveQ==} '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -941,35 +1130,52 @@ packages: '@types/babel__traverse@7.20.6': resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} - '@types/estree@1.0.5': - resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + '@types/estree@1.0.6': + resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} '@types/hast@2.3.10': resolution: {integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==} + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} '@types/node@20.14.11': resolution: {integrity: sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==} + '@types/node@20.17.10': + resolution: {integrity: sha512-/jrvh5h6NXhEauFFexRin69nA0uHJ5gwk4iDivp/DeoEua3uwCUto6PC86IpRITBOs4+6i2I56K5x5b6WYGXHA==} + '@types/prop-types@15.7.12': resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} + '@types/prop-types@15.7.14': + resolution: {integrity: sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==} + '@types/react-dom@18.3.0': resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==} + '@types/react-dom@18.3.5': + resolution: {integrity: sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==} + peerDependencies: + '@types/react': ^18.0.0 + '@types/react-syntax-highlighter@15.5.13': resolution: {integrity: sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA==} + '@types/react@18.3.17': + resolution: {integrity: sha512-opAQ5no6LqJNo9TqnxBKsgnkIYHozW9KSTlFVoSUJYh1Fl/sswkEoqIugRSm7tbh6pABtYjGAjW+GOS23j8qbw==} + '@types/react@18.3.3': resolution: {integrity: sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==} '@types/unist@2.0.10': resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==} - '@typescript-eslint/eslint-plugin@7.17.0': - resolution: {integrity: sha512-pyiDhEuLM3PuANxH7uNYan1AaFs5XE0zw1hq69JBvGvE7gSuEoQl1ydtEe/XQeoC3GQxLXyOVa5kNOATgM638A==} + '@typescript-eslint/eslint-plugin@7.18.0': + resolution: {integrity: sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: '@typescript-eslint/parser': ^7.0.0 @@ -979,8 +1185,8 @@ packages: typescript: optional: true - '@typescript-eslint/parser@7.17.0': - resolution: {integrity: sha512-puiYfGeg5Ydop8eusb/Hy1k7QmOU6X3nvsqCgzrB2K4qMavK//21+PzNE8qeECgNOIoertJPUC1SpegHDI515A==} + '@typescript-eslint/parser@7.18.0': + resolution: {integrity: sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: eslint: ^8.56.0 @@ -999,16 +1205,16 @@ packages: typescript: optional: true - '@typescript-eslint/scope-manager@7.17.0': - resolution: {integrity: sha512-0P2jTTqyxWp9HiKLu/Vemr2Rg1Xb5B7uHItdVZ6iAenXmPo4SZ86yOPCJwMqpCyaMiEHTNqizHfsbmCFT1x9SA==} + '@typescript-eslint/scope-manager@7.18.0': + resolution: {integrity: sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==} engines: {node: ^18.18.0 || >=20.0.0} '@typescript-eslint/scope-manager@7.2.0': resolution: {integrity: sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==} engines: {node: ^16.0.0 || >=18.0.0} - '@typescript-eslint/type-utils@7.17.0': - resolution: {integrity: sha512-XD3aaBt+orgkM/7Cei0XNEm1vwUxQ958AOLALzPlbPqb8C1G8PZK85tND7Jpe69Wualri81PLU+Zc48GVKIMMA==} + '@typescript-eslint/type-utils@7.18.0': + resolution: {integrity: sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: eslint: ^8.56.0 @@ -1017,16 +1223,16 @@ packages: typescript: optional: true - '@typescript-eslint/types@7.17.0': - resolution: {integrity: sha512-a29Ir0EbyKTKHnZWbNsrc/gqfIBqYPwj3F2M+jWE/9bqfEHg0AMtXzkbUkOG6QgEScxh2+Pz9OXe11jHDnHR7A==} + '@typescript-eslint/types@7.18.0': + resolution: {integrity: sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==} engines: {node: ^18.18.0 || >=20.0.0} '@typescript-eslint/types@7.2.0': resolution: {integrity: sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==} engines: {node: ^16.0.0 || >=18.0.0} - '@typescript-eslint/typescript-estree@7.17.0': - resolution: {integrity: sha512-72I3TGq93t2GoSBWI093wmKo0n6/b7O4j9o8U+f65TVD0FS6bI2180X5eGEr8MA8PhKMvYe9myZJquUT2JkCZw==} + '@typescript-eslint/typescript-estree@7.18.0': + resolution: {integrity: sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: typescript: '*' @@ -1043,30 +1249,30 @@ packages: typescript: optional: true - '@typescript-eslint/utils@7.17.0': - resolution: {integrity: sha512-r+JFlm5NdB+JXc7aWWZ3fKSm1gn0pkswEwIYsrGPdsT2GjsRATAKXiNtp3vgAAO1xZhX8alIOEQnNMl3kbTgJw==} + '@typescript-eslint/utils@7.18.0': + resolution: {integrity: sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: eslint: ^8.56.0 - '@typescript-eslint/visitor-keys@7.17.0': - resolution: {integrity: sha512-RVGC9UhPOCsfCdI9pU++K4nD7to+jTcMIbXTSOcrLqUEW6gF2pU1UUbYJKc9cvcRSK1UDeMJ7pdMxf4bhMpV/A==} + '@typescript-eslint/visitor-keys@7.18.0': + resolution: {integrity: sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==} engines: {node: ^18.18.0 || >=20.0.0} '@typescript-eslint/visitor-keys@7.2.0': resolution: {integrity: sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==} engines: {node: ^16.0.0 || >=18.0.0} - '@vitejs/plugin-react-swc@3.7.0': - resolution: {integrity: sha512-yrknSb3Dci6svCd/qhHqhFPDSw0QtjumcqdKMoNNzmOl5lMXTTiqzjWtG4Qask2HdvvzaNgSunbQGet8/GrKdA==} + '@vitejs/plugin-react-swc@3.7.2': + resolution: {integrity: sha512-y0byko2b2tSVVf5Gpng1eEhX1OvPC7x8yns1Fx8jDzlJp4LS6CMkCPfLw47cjyoMrshQDoQw4qcgjsU9VvlCew==} peerDependencies: - vite: ^4 || ^5 + vite: ^4 || ^5 || ^6 - '@vitejs/plugin-react@4.3.1': - resolution: {integrity: sha512-m/V2syj5CuVnaxcUJOQRel/Wr31FFXRFlnOoq1TVtkCxsY5veGMTEmpWHndrhB2U8ScHtCQB1e+4hWYExQc6Lg==} + '@vitejs/plugin-react@4.3.4': + resolution: {integrity: sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: - vite: ^4.2.0 || ^5.0.0 + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} @@ -1078,6 +1284,11 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + acorn@8.14.0: + resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} + engines: {node: '>=0.4.0'} + hasBin: true + ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} @@ -1089,10 +1300,6 @@ packages: resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} engines: {node: '>=12'} - ansi-styles@3.2.1: - resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} - engines: {node: '>=4'} - ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -1163,6 +1370,13 @@ packages: peerDependencies: postcss: ^8.1.0 + autoprefixer@10.4.20: + resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} @@ -1196,6 +1410,11 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + browserslist@4.24.3: + resolution: {integrity: sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + bundle-require@5.0.0: resolution: {integrity: sha512-GuziW3fSSmopcx4KRymQEJVbZUfqlCqcq7dvs6TYwKRZiegK/2buMxQTPs6MGlNv50wms1699qYO54R8XfRX4w==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -1225,9 +1444,8 @@ packages: caniuse-lite@1.0.30001643: resolution: {integrity: sha512-ERgWGNleEilSrHM6iUz/zJNSQTP8Mr21wDWpdgvRwcTXGAq6jMtOUPP4dqFPTdKqZ2wKTdtB+uucZ3MRpAUSmg==} - chalk@2.4.2: - resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} - engines: {node: '>=4'} + caniuse-lite@1.0.30001689: + resolution: {integrity: sha512-CmeR2VBycfa+5/jOfnp/NpWPGd06nf1XYiefUvhXFfZE4GkRc9jv+eGPS4nT558WS/8lYCzV8SlANCIPvbWP1g==} chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} @@ -1246,6 +1464,10 @@ packages: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} + chokidar@4.0.2: + resolution: {integrity: sha512-/b57FK+bblSU+dfewfFe0rT1YjVDfOmeLQwCAuC+vwvgLkXboATqqmy+Ipux6JrF6L5joe5CBnFOw+gLWH6yKg==} + engines: {node: '>= 14.16.0'} + client-only@0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} @@ -1253,16 +1475,10 @@ packages: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} - color-convert@1.9.3: - resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} - color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} - color-name@1.1.3: - resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} - color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} @@ -1295,6 +1511,10 @@ packages: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} @@ -1339,6 +1559,15 @@ packages: supports-color: optional: true + debug@4.4.0: + resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + deep-equal@2.2.3: resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==} engines: {node: '>= 0.4'} @@ -1378,6 +1607,9 @@ packages: electron-to-chromium@1.4.832: resolution: {integrity: sha512-cTen3SB0H2SGU7x467NRe1eVcQgcuS6jckKfWJHia2eo0cHIGOqHoAxevIYZD4eRHcWjkvFzo93bi3vJ9W+1lA==} + electron-to-chromium@1.5.74: + resolution: {integrity: sha512-ck3//9RC+6oss/1Bh9tiAVFy5vfSKbRHAFh7Z3/eTRkEqJeWgymloShB17Vg3Z4nmDNp35vAd1BZ6CMW4Wt6Iw==} + emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -1427,18 +1659,19 @@ packages: engines: {node: '>=12'} hasBin: true - esbuild@0.23.0: - resolution: {integrity: sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==} + esbuild@0.23.1: + resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} engines: {node: '>=18'} hasBin: true - escalade@3.1.2: - resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} - engines: {node: '>=6'} + esbuild@0.24.0: + resolution: {integrity: sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==} + engines: {node: '>=18'} + hasBin: true - escape-string-regexp@1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} @@ -1517,10 +1750,10 @@ packages: peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 - eslint-plugin-react-refresh@0.4.9: - resolution: {integrity: sha512-QK49YrBAo5CLNLseZ7sZgvgTy21E6NEw22eZqc4teZfH8pxV3yXc9XXOYfUI6JNpw7mfHNkAeWtBxrTyykB6HA==} + eslint-plugin-react-refresh@0.4.16: + resolution: {integrity: sha512-slterMlxAhov/DZO8NScf6mEeMBBXodFUolijDvrtTxyezyLoTQaa73FyYus/VbTdftd8wBgBxPMRk3poleXNQ==} peerDependencies: - eslint: '>=7' + eslint: '>=8.40' eslint-plugin-react@7.35.0: resolution: {integrity: sha512-v501SSMOWv8gerHkk+IIQBkcGRGrO2nfybfj5pLxuJNFTPxxA3PSryhXTK+9pNbtkggheDdsC0E9Q8CuPk6JKA==} @@ -1537,6 +1770,10 @@ packages: resolution: {integrity: sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + eslint-scope@8.2.0: + resolution: {integrity: sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -1545,6 +1782,20 @@ packages: resolution: {integrity: sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + eslint-visitor-keys@4.2.0: + resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.17.0: + resolution: {integrity: sha512-evtlNcpJg+cZLcnVKwsai8fExnqjGPicK7gnUtlNuzu+Fv9bI0aLpND5T44VLQtoMEnI57LoXO9XAkIXwohKrA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + eslint@9.7.0: resolution: {integrity: sha512-FzJ9D/0nGiCGBf8UXO/IGLTgLVzIxze1zpfA8Ton2mjLovXdAPlYDv+MQDcqj3TmrhAGYfOpz9RfR+ent0AgAw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1554,6 +1805,10 @@ packages: resolution: {integrity: sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + espree@10.3.0: + resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + esquery@1.6.0: resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} engines: {node: '>=0.10'} @@ -1570,10 +1825,6 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} - execa@5.1.1: - resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} - engines: {node: '>=10'} - fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -1593,6 +1844,14 @@ packages: fault@1.0.4: resolution: {integrity: sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==} + fdir@6.4.2: + resolution: {integrity: sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} @@ -1609,8 +1868,8 @@ packages: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} - flatted@3.3.1: - resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} + flatted@3.3.2: + resolution: {integrity: sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==} for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} @@ -1619,6 +1878,10 @@ packages: resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} engines: {node: '>=14'} + foreground-child@3.3.0: + resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} + engines: {node: '>=14'} + format@0.2.2: resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} engines: {node: '>=0.4.x'} @@ -1653,10 +1916,6 @@ packages: resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} engines: {node: '>= 0.4'} - get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} - get-symbol-description@1.0.2: resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} engines: {node: '>= 0.4'} @@ -1709,10 +1968,6 @@ packages: has-bigints@1.0.2: resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} - has-flag@3.0.0: - resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} - engines: {node: '>=4'} - has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -1745,14 +2000,14 @@ packages: highlight.js@10.7.3: resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} - human-signals@2.1.0: - resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} - engines: {node: '>=10.17.0'} - ignore@5.3.1: resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} engines: {node: '>= 4'} + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} @@ -1802,6 +2057,10 @@ packages: resolution: {integrity: sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==} engines: {node: '>= 0.4'} + is-core-module@2.16.0: + resolution: {integrity: sha512-urTSINYfAYgcbLb0yDQ6egFm6h3Mo1DcF9EkyXSRjjzdHbsulg01qhwWuXdOoUBuTkbQ80KDboXa0vFJ+BDH+g==} + engines: {node: '>= 0.4'} + is-data-view@1.0.1: resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} engines: {node: '>= 0.4'} @@ -1867,10 +2126,6 @@ packages: resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} engines: {node: '>= 0.4'} - is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - is-string@1.0.7: resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} engines: {node: '>= 0.4'} @@ -1910,8 +2165,8 @@ packages: jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - jiti@1.21.6: - resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} + jiti@1.21.7: + resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} hasBin: true joycon@3.1.1: @@ -1925,9 +2180,9 @@ packages: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true - jsesc@2.5.2: - resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} - engines: {node: '>=4'} + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} hasBin: true json-buffer@3.0.1: @@ -1970,8 +2225,8 @@ packages: resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} engines: {node: '>=10'} - lilconfig@3.1.2: - resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==} + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} engines: {node: '>=14'} lines-and-columns@1.2.4: @@ -2007,21 +2262,14 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} - micromatch@4.0.7: - resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} - mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -2054,6 +2302,11 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + nanoid@3.3.8: + resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -2078,6 +2331,9 @@ packages: node-releases@2.0.18: resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} + node-releases@2.0.19: + resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -2086,10 +2342,6 @@ packages: resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} engines: {node: '>=0.10.0'} - npm-run-path@4.0.1: - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} - engines: {node: '>=8'} - object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -2130,10 +2382,6 @@ packages: resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} engines: {node: '>= 0.4'} - onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} - optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -2146,8 +2394,8 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} - package-json-from-dist@1.0.0: - resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} @@ -2178,10 +2426,17 @@ packages: picocolors@1.0.1: resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + pify@2.3.0: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} @@ -2242,8 +2497,8 @@ packages: peerDependencies: postcss: ^8.2.14 - postcss-selector-parser@6.1.1: - resolution: {integrity: sha512-b4dlw/9V8A71rLIDsSwVmak9z2DuBUB7CA1/wSdelNEzqsjoSPeADTWNO09lpH49Diy3/JIZ2bSPB1dI3LJCHg==} + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} engines: {node: '>=4'} postcss-value-parser@4.2.0: @@ -2257,6 +2512,10 @@ packages: resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} engines: {node: ^10 || ^12 || >=14} + postcss@8.4.49: + resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==} + engines: {node: ^10 || ^12 || >=14} + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -2315,6 +2574,10 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} + readdirp@4.0.2: + resolution: {integrity: sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==} + engines: {node: '>= 14.16.0'} + reflect.getprototypeof@1.0.6: resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==} engines: {node: '>= 0.4'} @@ -2348,6 +2611,10 @@ packages: resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true + resolve@1.22.9: + resolution: {integrity: sha512-QxrmX1DzraFIi9PxdG5VkRfRwIgjwyud+z/iBwfRRrVmHc+P9Q7u2lSSpQ6bjr2gy5lrqIiU9vb6iAeGf2400A==} + hasBin: true + resolve@2.0.0-next.5: resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} hasBin: true @@ -2356,8 +2623,8 @@ packages: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rollup@4.19.0: - resolution: {integrity: sha512-5r7EYSQIowHsK4eTZ0Y81qpZuJz+MUuYeqmmYmRMl1nwhdmbiYqt5jwzf6u7wyOzJgYqtCRMtVRKOtHANBz7rA==} + rollup@4.28.1: + resolution: {integrity: sha512-61fXYl/qNVinKmGSTHAZ6Yy8I3YIJC/r2m9feHo6SwVAVcLT5MPwOUFe7EuURA/4m0NR8lXG4BBXuo/IZEsjMg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -2403,16 +2670,14 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - shell-quote@1.8.1: - resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} + shell-quote@1.8.2: + resolution: {integrity: sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==} + engines: {node: '>= 0.4'} side-channel@1.0.6: resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} engines: {node: '>= 0.4'} - signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} @@ -2425,6 +2690,10 @@ packages: resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} engines: {node: '>=0.10.0'} + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + source-map@0.8.0-beta.0: resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} engines: {node: '>= 8'} @@ -2484,10 +2753,6 @@ packages: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} - strip-final-newline@2.0.0: - resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} - engines: {node: '>=6'} - strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -2510,10 +2775,6 @@ packages: engines: {node: '>=16 || 14 >=14.17'} hasBin: true - supports-color@5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} - supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -2545,9 +2806,12 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} - to-fast-properties@2.0.0: - resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} - engines: {node: '>=4'} + tinyexec@0.3.1: + resolution: {integrity: sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==} + + tinyglobby@0.2.10: + resolution: {integrity: sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==} + engines: {node: '>=12.0.0'} to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} @@ -2563,8 +2827,8 @@ packages: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true - ts-api-utils@1.3.0: - resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} + ts-api-utils@1.4.3: + resolution: {integrity: sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==} engines: {node: '>=16'} peerDependencies: typescript: '>=4.2.0' @@ -2578,8 +2842,11 @@ packages: tslib@2.6.3: resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} - tsup@8.2.2: - resolution: {integrity: sha512-MufIuzdSt6HYPOeOtjUXLR4rqRJySi6XsRNZdwvjC2XR+xghsu2L3vSmYmX+k4S1mO6j0OlUEyVQ3Fc0H66XcA==} + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tsup@8.3.5: + resolution: {integrity: sha512-Tunf6r6m6tnZsG9GYWndg0z8dEV7fD733VBFzFJ5Vcm1FtlXB8xBD/rtrBi2a3YKEV7hHtxiZtW5EAVADoe1pA==} engines: {node: '>=18'} hasBin: true peerDependencies: @@ -2656,33 +2923,47 @@ packages: engines: {node: '>=14.17'} hasBin: true + typescript@5.7.2: + resolution: {integrity: sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==} + engines: {node: '>=14.17'} + hasBin: true + unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + update-browserslist-db@1.1.0: resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' + update-browserslist-db@1.1.1: + resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - vite-plugin-singlefile@2.0.2: - resolution: {integrity: sha512-Z2ou6HcvED5CF0hM+vcFSaFa+klyS8RyyLxW0PbMRLnMbvzTI6ueWyxdYNFhpuXZgz/aj6+E/dHFTdEcw6gb9w==} + vite-plugin-singlefile@2.1.0: + resolution: {integrity: sha512-7tJo+UgZABlKpY/nubth/wxJ4+pUGREPnEwNOknxwl2MM0zTvF14KTU4Ln1lc140gjLLV5mjDrvuoquU7OZqCg==} engines: {node: '>18.0.0'} peerDependencies: - rollup: ^4.18.0 - vite: ^5.3.1 + rollup: ^4.28.1 + vite: ^5.4.11 || ^6.0.0 - vite@5.3.4: - resolution: {integrity: sha512-Cw+7zL3ZG9/NZBB8C+8QbQZmR54GwqIz+WMI4b3JgdYJvX+ny9AjJXqkGQlDXSXRP9rP0B4tbciRMOVEKulVOA==} + vite@5.4.11: + resolution: {integrity: sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -2690,6 +2971,7 @@ packages: less: '*' lightningcss: ^1.21.0 sass: '*' + sass-embedded: '*' stylus: '*' sugarss: '*' terser: ^5.4.0 @@ -2702,6 +2984,8 @@ packages: optional: true sass: optional: true + sass-embedded: + optional: true stylus: optional: true sugarss: @@ -2758,8 +3042,8 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yaml@2.4.5: - resolution: {integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==} + yaml@2.6.1: + resolution: {integrity: sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==} engines: {node: '>= 14'} hasBin: true @@ -2781,297 +3065,338 @@ snapshots: '@ampproject/remapping@2.3.0': dependencies: - '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/gen-mapping': 0.3.8 '@jridgewell/trace-mapping': 0.3.25 - '@babel/code-frame@7.24.7': + '@babel/code-frame@7.26.2': dependencies: - '@babel/highlight': 7.24.7 - picocolors: 1.0.1 + '@babel/helper-validator-identifier': 7.25.9 + js-tokens: 4.0.0 + picocolors: 1.1.1 - '@babel/compat-data@7.24.9': {} + '@babel/compat-data@7.26.3': {} - '@babel/core@7.24.9': + '@babel/core@7.26.0': dependencies: '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.24.7 - '@babel/generator': 7.24.10 - '@babel/helper-compilation-targets': 7.24.8 - '@babel/helper-module-transforms': 7.24.9(@babel/core@7.24.9) - '@babel/helpers': 7.24.8 - '@babel/parser': 7.24.8 - '@babel/template': 7.24.7 - '@babel/traverse': 7.24.8 - '@babel/types': 7.24.9 + '@babel/code-frame': 7.26.2 + '@babel/generator': 7.26.3 + '@babel/helper-compilation-targets': 7.25.9 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) + '@babel/helpers': 7.26.0 + '@babel/parser': 7.26.3 + '@babel/template': 7.25.9 + '@babel/traverse': 7.26.4 + '@babel/types': 7.26.3 convert-source-map: 2.0.0 - debug: 4.3.5 + debug: 4.4.0 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/generator@7.24.10': + '@babel/generator@7.26.3': dependencies: - '@babel/types': 7.24.9 - '@jridgewell/gen-mapping': 0.3.5 + '@babel/parser': 7.26.3 + '@babel/types': 7.26.3 + '@jridgewell/gen-mapping': 0.3.8 '@jridgewell/trace-mapping': 0.3.25 - jsesc: 2.5.2 - - '@babel/helper-compilation-targets@7.24.8': - dependencies: - '@babel/compat-data': 7.24.9 - '@babel/helper-validator-option': 7.24.8 - browserslist: 4.23.2 - lru-cache: 5.1.1 - semver: 6.3.1 - - '@babel/helper-environment-visitor@7.24.7': - dependencies: - '@babel/types': 7.24.9 - - '@babel/helper-function-name@7.24.7': - dependencies: - '@babel/template': 7.24.7 - '@babel/types': 7.24.9 + jsesc: 3.1.0 - '@babel/helper-hoist-variables@7.24.7': + '@babel/helper-compilation-targets@7.25.9': dependencies: - '@babel/types': 7.24.9 - - '@babel/helper-module-imports@7.24.7': - dependencies: - '@babel/traverse': 7.24.8 - '@babel/types': 7.24.9 - transitivePeerDependencies: - - supports-color + '@babel/compat-data': 7.26.3 + '@babel/helper-validator-option': 7.25.9 + browserslist: 4.24.3 + lru-cache: 5.1.1 + semver: 6.3.1 - '@babel/helper-module-transforms@7.24.9(@babel/core@7.24.9)': + '@babel/helper-module-imports@7.25.9': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-module-imports': 7.24.7 - '@babel/helper-simple-access': 7.24.7 - '@babel/helper-split-export-declaration': 7.24.7 - '@babel/helper-validator-identifier': 7.24.7 + '@babel/traverse': 7.26.4 + '@babel/types': 7.26.3 transitivePeerDependencies: - supports-color - '@babel/helper-plugin-utils@7.24.8': {} - - '@babel/helper-simple-access@7.24.7': + '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.0)': dependencies: - '@babel/traverse': 7.24.8 - '@babel/types': 7.24.9 + '@babel/core': 7.26.0 + '@babel/helper-module-imports': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 + '@babel/traverse': 7.26.4 transitivePeerDependencies: - supports-color - '@babel/helper-split-export-declaration@7.24.7': - dependencies: - '@babel/types': 7.24.9 + '@babel/helper-plugin-utils@7.25.9': {} - '@babel/helper-string-parser@7.24.8': {} + '@babel/helper-string-parser@7.25.9': {} - '@babel/helper-validator-identifier@7.24.7': {} + '@babel/helper-validator-identifier@7.25.9': {} - '@babel/helper-validator-option@7.24.8': {} + '@babel/helper-validator-option@7.25.9': {} - '@babel/helpers@7.24.8': + '@babel/helpers@7.26.0': dependencies: - '@babel/template': 7.24.7 - '@babel/types': 7.24.9 + '@babel/template': 7.25.9 + '@babel/types': 7.26.3 - '@babel/highlight@7.24.7': + '@babel/parser@7.26.3': dependencies: - '@babel/helper-validator-identifier': 7.24.7 - chalk: 2.4.2 - js-tokens: 4.0.0 - picocolors: 1.0.1 + '@babel/types': 7.26.3 - '@babel/parser@7.24.8': + '@babel/plugin-transform-react-jsx-self@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/types': 7.24.9 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-react-jsx-self@7.24.7(@babel/core@7.24.9)': + '@babel/plugin-transform-react-jsx-source@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-react-jsx-source@7.24.7(@babel/core@7.24.9)': + '@babel/runtime@7.24.8': dependencies: - '@babel/core': 7.24.9 - '@babel/helper-plugin-utils': 7.24.8 + regenerator-runtime: 0.14.1 - '@babel/runtime@7.24.8': + '@babel/runtime@7.26.0': dependencies: regenerator-runtime: 0.14.1 - '@babel/template@7.24.7': + '@babel/template@7.25.9': dependencies: - '@babel/code-frame': 7.24.7 - '@babel/parser': 7.24.8 - '@babel/types': 7.24.9 + '@babel/code-frame': 7.26.2 + '@babel/parser': 7.26.3 + '@babel/types': 7.26.3 - '@babel/traverse@7.24.8': + '@babel/traverse@7.26.4': dependencies: - '@babel/code-frame': 7.24.7 - '@babel/generator': 7.24.10 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-function-name': 7.24.7 - '@babel/helper-hoist-variables': 7.24.7 - '@babel/helper-split-export-declaration': 7.24.7 - '@babel/parser': 7.24.8 - '@babel/types': 7.24.9 - debug: 4.3.5 + '@babel/code-frame': 7.26.2 + '@babel/generator': 7.26.3 + '@babel/parser': 7.26.3 + '@babel/template': 7.25.9 + '@babel/types': 7.26.3 + debug: 4.4.0 globals: 11.12.0 transitivePeerDependencies: - supports-color - '@babel/types@7.24.9': + '@babel/types@7.26.3': dependencies: - '@babel/helper-string-parser': 7.24.8 - '@babel/helper-validator-identifier': 7.24.7 - to-fast-properties: 2.0.0 + '@babel/helper-string-parser': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 '@esbuild/aix-ppc64@0.21.5': optional: true - '@esbuild/aix-ppc64@0.23.0': + '@esbuild/aix-ppc64@0.23.1': + optional: true + + '@esbuild/aix-ppc64@0.24.0': optional: true '@esbuild/android-arm64@0.21.5': optional: true - '@esbuild/android-arm64@0.23.0': + '@esbuild/android-arm64@0.23.1': + optional: true + + '@esbuild/android-arm64@0.24.0': optional: true '@esbuild/android-arm@0.21.5': optional: true - '@esbuild/android-arm@0.23.0': + '@esbuild/android-arm@0.23.1': + optional: true + + '@esbuild/android-arm@0.24.0': optional: true '@esbuild/android-x64@0.21.5': optional: true - '@esbuild/android-x64@0.23.0': + '@esbuild/android-x64@0.23.1': + optional: true + + '@esbuild/android-x64@0.24.0': optional: true '@esbuild/darwin-arm64@0.21.5': optional: true - '@esbuild/darwin-arm64@0.23.0': + '@esbuild/darwin-arm64@0.23.1': + optional: true + + '@esbuild/darwin-arm64@0.24.0': optional: true '@esbuild/darwin-x64@0.21.5': optional: true - '@esbuild/darwin-x64@0.23.0': + '@esbuild/darwin-x64@0.23.1': + optional: true + + '@esbuild/darwin-x64@0.24.0': optional: true '@esbuild/freebsd-arm64@0.21.5': optional: true - '@esbuild/freebsd-arm64@0.23.0': + '@esbuild/freebsd-arm64@0.23.1': + optional: true + + '@esbuild/freebsd-arm64@0.24.0': optional: true '@esbuild/freebsd-x64@0.21.5': optional: true - '@esbuild/freebsd-x64@0.23.0': + '@esbuild/freebsd-x64@0.23.1': + optional: true + + '@esbuild/freebsd-x64@0.24.0': optional: true '@esbuild/linux-arm64@0.21.5': optional: true - '@esbuild/linux-arm64@0.23.0': + '@esbuild/linux-arm64@0.23.1': + optional: true + + '@esbuild/linux-arm64@0.24.0': optional: true '@esbuild/linux-arm@0.21.5': optional: true - '@esbuild/linux-arm@0.23.0': + '@esbuild/linux-arm@0.23.1': + optional: true + + '@esbuild/linux-arm@0.24.0': optional: true '@esbuild/linux-ia32@0.21.5': optional: true - '@esbuild/linux-ia32@0.23.0': + '@esbuild/linux-ia32@0.23.1': + optional: true + + '@esbuild/linux-ia32@0.24.0': optional: true '@esbuild/linux-loong64@0.21.5': optional: true - '@esbuild/linux-loong64@0.23.0': + '@esbuild/linux-loong64@0.23.1': + optional: true + + '@esbuild/linux-loong64@0.24.0': optional: true '@esbuild/linux-mips64el@0.21.5': optional: true - '@esbuild/linux-mips64el@0.23.0': + '@esbuild/linux-mips64el@0.23.1': + optional: true + + '@esbuild/linux-mips64el@0.24.0': optional: true '@esbuild/linux-ppc64@0.21.5': optional: true - '@esbuild/linux-ppc64@0.23.0': + '@esbuild/linux-ppc64@0.23.1': + optional: true + + '@esbuild/linux-ppc64@0.24.0': optional: true '@esbuild/linux-riscv64@0.21.5': optional: true - '@esbuild/linux-riscv64@0.23.0': + '@esbuild/linux-riscv64@0.23.1': + optional: true + + '@esbuild/linux-riscv64@0.24.0': optional: true '@esbuild/linux-s390x@0.21.5': optional: true - '@esbuild/linux-s390x@0.23.0': + '@esbuild/linux-s390x@0.23.1': + optional: true + + '@esbuild/linux-s390x@0.24.0': optional: true '@esbuild/linux-x64@0.21.5': optional: true - '@esbuild/linux-x64@0.23.0': + '@esbuild/linux-x64@0.23.1': + optional: true + + '@esbuild/linux-x64@0.24.0': optional: true '@esbuild/netbsd-x64@0.21.5': optional: true - '@esbuild/netbsd-x64@0.23.0': + '@esbuild/netbsd-x64@0.23.1': + optional: true + + '@esbuild/netbsd-x64@0.24.0': + optional: true + + '@esbuild/openbsd-arm64@0.23.1': optional: true - '@esbuild/openbsd-arm64@0.23.0': + '@esbuild/openbsd-arm64@0.24.0': optional: true '@esbuild/openbsd-x64@0.21.5': optional: true - '@esbuild/openbsd-x64@0.23.0': + '@esbuild/openbsd-x64@0.23.1': + optional: true + + '@esbuild/openbsd-x64@0.24.0': optional: true '@esbuild/sunos-x64@0.21.5': optional: true - '@esbuild/sunos-x64@0.23.0': + '@esbuild/sunos-x64@0.23.1': + optional: true + + '@esbuild/sunos-x64@0.24.0': optional: true '@esbuild/win32-arm64@0.21.5': optional: true - '@esbuild/win32-arm64@0.23.0': + '@esbuild/win32-arm64@0.23.1': + optional: true + + '@esbuild/win32-arm64@0.24.0': optional: true '@esbuild/win32-ia32@0.21.5': optional: true - '@esbuild/win32-ia32@0.23.0': + '@esbuild/win32-ia32@0.23.1': + optional: true + + '@esbuild/win32-ia32@0.24.0': optional: true '@esbuild/win32-x64@0.21.5': optional: true - '@esbuild/win32-x64@0.23.0': + '@esbuild/win32-x64@0.23.1': + optional: true + + '@esbuild/win32-x64@0.24.0': optional: true '@eslint-community/eslint-utils@4.4.0(eslint@9.7.0)': @@ -3079,8 +3404,15 @@ snapshots: eslint: 9.7.0 eslint-visitor-keys: 3.4.3 + '@eslint-community/eslint-utils@4.4.1(eslint@9.17.0(jiti@1.21.7))': + dependencies: + eslint: 9.17.0(jiti@1.21.7) + eslint-visitor-keys: 3.4.3 + '@eslint-community/regexpp@4.11.0': {} + '@eslint-community/regexpp@4.12.1': {} + '@eslint/config-array@0.17.1': dependencies: '@eslint/object-schema': 2.1.4 @@ -3089,6 +3421,18 @@ snapshots: transitivePeerDependencies: - supports-color + '@eslint/config-array@0.19.1': + dependencies: + '@eslint/object-schema': 2.1.5 + debug: 4.4.0 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/core@0.9.1': + dependencies: + '@types/json-schema': 7.0.15 + '@eslint/eslintrc@3.1.0': dependencies: ajv: 6.12.6 @@ -3103,16 +3447,49 @@ snapshots: transitivePeerDependencies: - supports-color + '@eslint/eslintrc@3.2.0': + dependencies: + ajv: 6.12.6 + debug: 4.4.0 + espree: 10.3.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.17.0': {} + '@eslint/js@9.7.0': {} '@eslint/object-schema@2.1.4': {} - '@figma/plugin-typings@1.97.0': {} + '@eslint/object-schema@2.1.5': {} + + '@eslint/plugin-kit@0.2.4': + dependencies: + levn: 0.4.1 + + '@figma/plugin-typings@1.105.0': {} + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.6': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.3.1 '@humanwhocodes/module-importer@1.0.1': {} '@humanwhocodes/retry@0.3.0': {} + '@humanwhocodes/retry@0.3.1': {} + + '@humanwhocodes/retry@0.4.1': {} + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -3122,7 +3499,7 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 - '@jridgewell/gen-mapping@0.3.5': + '@jridgewell/gen-mapping@0.3.8': dependencies: '@jridgewell/set-array': 1.2.1 '@jridgewell/sourcemap-codec': 1.5.0 @@ -3187,108 +3564,117 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@rollup/rollup-android-arm-eabi@4.19.0': + '@rollup/rollup-android-arm-eabi@4.28.1': + optional: true + + '@rollup/rollup-android-arm64@4.28.1': + optional: true + + '@rollup/rollup-darwin-arm64@4.28.1': + optional: true + + '@rollup/rollup-darwin-x64@4.28.1': optional: true - '@rollup/rollup-android-arm64@4.19.0': + '@rollup/rollup-freebsd-arm64@4.28.1': optional: true - '@rollup/rollup-darwin-arm64@4.19.0': + '@rollup/rollup-freebsd-x64@4.28.1': optional: true - '@rollup/rollup-darwin-x64@4.19.0': + '@rollup/rollup-linux-arm-gnueabihf@4.28.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.19.0': + '@rollup/rollup-linux-arm-musleabihf@4.28.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.19.0': + '@rollup/rollup-linux-arm64-gnu@4.28.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.19.0': + '@rollup/rollup-linux-arm64-musl@4.28.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.19.0': + '@rollup/rollup-linux-loongarch64-gnu@4.28.1': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.19.0': + '@rollup/rollup-linux-powerpc64le-gnu@4.28.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.19.0': + '@rollup/rollup-linux-riscv64-gnu@4.28.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.19.0': + '@rollup/rollup-linux-s390x-gnu@4.28.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.19.0': + '@rollup/rollup-linux-x64-gnu@4.28.1': optional: true - '@rollup/rollup-linux-x64-musl@4.19.0': + '@rollup/rollup-linux-x64-musl@4.28.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.19.0': + '@rollup/rollup-win32-arm64-msvc@4.28.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.19.0': + '@rollup/rollup-win32-ia32-msvc@4.28.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.19.0': + '@rollup/rollup-win32-x64-msvc@4.28.1': optional: true '@rushstack/eslint-patch@1.10.3': {} - '@swc/core-darwin-arm64@1.7.0': + '@swc/core-darwin-arm64@1.10.1': optional: true - '@swc/core-darwin-x64@1.7.0': + '@swc/core-darwin-x64@1.10.1': optional: true - '@swc/core-linux-arm-gnueabihf@1.7.0': + '@swc/core-linux-arm-gnueabihf@1.10.1': optional: true - '@swc/core-linux-arm64-gnu@1.7.0': + '@swc/core-linux-arm64-gnu@1.10.1': optional: true - '@swc/core-linux-arm64-musl@1.7.0': + '@swc/core-linux-arm64-musl@1.10.1': optional: true - '@swc/core-linux-x64-gnu@1.7.0': + '@swc/core-linux-x64-gnu@1.10.1': optional: true - '@swc/core-linux-x64-musl@1.7.0': + '@swc/core-linux-x64-musl@1.10.1': optional: true - '@swc/core-win32-arm64-msvc@1.7.0': + '@swc/core-win32-arm64-msvc@1.10.1': optional: true - '@swc/core-win32-ia32-msvc@1.7.0': + '@swc/core-win32-ia32-msvc@1.10.1': optional: true - '@swc/core-win32-x64-msvc@1.7.0': + '@swc/core-win32-x64-msvc@1.10.1': optional: true - '@swc/core@1.7.0(@swc/helpers@0.5.12)': + '@swc/core@1.10.1(@swc/helpers@0.5.12)': dependencies: '@swc/counter': 0.1.3 - '@swc/types': 0.1.12 + '@swc/types': 0.1.17 optionalDependencies: - '@swc/core-darwin-arm64': 1.7.0 - '@swc/core-darwin-x64': 1.7.0 - '@swc/core-linux-arm-gnueabihf': 1.7.0 - '@swc/core-linux-arm64-gnu': 1.7.0 - '@swc/core-linux-arm64-musl': 1.7.0 - '@swc/core-linux-x64-gnu': 1.7.0 - '@swc/core-linux-x64-musl': 1.7.0 - '@swc/core-win32-arm64-msvc': 1.7.0 - '@swc/core-win32-ia32-msvc': 1.7.0 - '@swc/core-win32-x64-msvc': 1.7.0 + '@swc/core-darwin-arm64': 1.10.1 + '@swc/core-darwin-x64': 1.10.1 + '@swc/core-linux-arm-gnueabihf': 1.10.1 + '@swc/core-linux-arm64-gnu': 1.10.1 + '@swc/core-linux-arm64-musl': 1.10.1 + '@swc/core-linux-x64-gnu': 1.10.1 + '@swc/core-linux-x64-musl': 1.10.1 + '@swc/core-win32-arm64-msvc': 1.10.1 + '@swc/core-win32-ia32-msvc': 1.10.1 + '@swc/core-win32-x64-msvc': 1.10.1 '@swc/helpers': 0.5.12 '@swc/counter@0.1.3': {} '@swc/helpers@0.5.12': dependencies: - tslib: 2.6.3 + tslib: 2.8.1 optional: true '@swc/helpers@0.5.5': @@ -3296,53 +3682,70 @@ snapshots: '@swc/counter': 0.1.3 tslib: 2.6.3 - '@swc/types@0.1.12': + '@swc/types@0.1.17': dependencies: '@swc/counter': 0.1.3 '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.24.8 - '@babel/types': 7.24.9 + '@babel/parser': 7.26.3 + '@babel/types': 7.26.3 '@types/babel__generator': 7.6.8 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.20.6 '@types/babel__generator@7.6.8': dependencies: - '@babel/types': 7.24.9 + '@babel/types': 7.26.3 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.24.8 - '@babel/types': 7.24.9 + '@babel/parser': 7.26.3 + '@babel/types': 7.26.3 '@types/babel__traverse@7.20.6': dependencies: - '@babel/types': 7.24.9 + '@babel/types': 7.26.3 - '@types/estree@1.0.5': {} + '@types/estree@1.0.6': {} '@types/hast@2.3.10': dependencies: '@types/unist': 2.0.10 + '@types/json-schema@7.0.15': {} + '@types/json5@0.0.29': {} '@types/node@20.14.11': dependencies: undici-types: 5.26.5 + '@types/node@20.17.10': + dependencies: + undici-types: 6.19.8 + '@types/prop-types@15.7.12': {} + '@types/prop-types@15.7.14': {} + '@types/react-dom@18.3.0': dependencies: '@types/react': 18.3.3 + '@types/react-dom@18.3.5(@types/react@18.3.17)': + dependencies: + '@types/react': 18.3.17 + '@types/react-syntax-highlighter@15.5.13': dependencies: '@types/react': 18.3.3 + '@types/react@18.3.17': + dependencies: + '@types/prop-types': 15.7.14 + csstype: 3.1.3 + '@types/react@18.3.3': dependencies: '@types/prop-types': 15.7.12 @@ -3350,120 +3753,120 @@ snapshots: '@types/unist@2.0.10': {} - '@typescript-eslint/eslint-plugin@7.17.0(@typescript-eslint/parser@7.17.0(eslint@9.7.0)(typescript@5.5.4))(eslint@9.7.0)(typescript@5.5.4)': + '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2)': dependencies: - '@eslint-community/regexpp': 4.11.0 - '@typescript-eslint/parser': 7.17.0(eslint@9.7.0)(typescript@5.5.4) - '@typescript-eslint/scope-manager': 7.17.0 - '@typescript-eslint/type-utils': 7.17.0(eslint@9.7.0)(typescript@5.5.4) - '@typescript-eslint/utils': 7.17.0(eslint@9.7.0)(typescript@5.5.4) - '@typescript-eslint/visitor-keys': 7.17.0 - eslint: 9.7.0 + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 7.18.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2) + '@typescript-eslint/scope-manager': 7.18.0 + '@typescript-eslint/type-utils': 7.18.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2) + '@typescript-eslint/utils': 7.18.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2) + '@typescript-eslint/visitor-keys': 7.18.0 + eslint: 9.17.0(jiti@1.21.7) graphemer: 1.4.0 - ignore: 5.3.1 + ignore: 5.3.2 natural-compare: 1.4.0 - ts-api-utils: 1.3.0(typescript@5.5.4) + ts-api-utils: 1.4.3(typescript@5.7.2) optionalDependencies: - typescript: 5.5.4 + typescript: 5.7.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@7.17.0(eslint@9.7.0)(typescript@5.5.4)': + '@typescript-eslint/parser@7.18.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2)': dependencies: - '@typescript-eslint/scope-manager': 7.17.0 - '@typescript-eslint/types': 7.17.0 - '@typescript-eslint/typescript-estree': 7.17.0(typescript@5.5.4) - '@typescript-eslint/visitor-keys': 7.17.0 - debug: 4.3.5 - eslint: 9.7.0 + '@typescript-eslint/scope-manager': 7.18.0 + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.7.2) + '@typescript-eslint/visitor-keys': 7.18.0 + debug: 4.4.0 + eslint: 9.17.0(jiti@1.21.7) optionalDependencies: - typescript: 5.5.4 + typescript: 5.7.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@7.2.0(eslint@9.7.0)(typescript@5.5.4)': + '@typescript-eslint/parser@7.2.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2)': dependencies: '@typescript-eslint/scope-manager': 7.2.0 '@typescript-eslint/types': 7.2.0 - '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.5.4) + '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.7.2) '@typescript-eslint/visitor-keys': 7.2.0 - debug: 4.3.5 - eslint: 9.7.0 + debug: 4.4.0 + eslint: 9.17.0(jiti@1.21.7) optionalDependencies: - typescript: 5.5.4 + typescript: 5.7.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@7.17.0': + '@typescript-eslint/scope-manager@7.18.0': dependencies: - '@typescript-eslint/types': 7.17.0 - '@typescript-eslint/visitor-keys': 7.17.0 + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/visitor-keys': 7.18.0 '@typescript-eslint/scope-manager@7.2.0': dependencies: '@typescript-eslint/types': 7.2.0 '@typescript-eslint/visitor-keys': 7.2.0 - '@typescript-eslint/type-utils@7.17.0(eslint@9.7.0)(typescript@5.5.4)': + '@typescript-eslint/type-utils@7.18.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2)': dependencies: - '@typescript-eslint/typescript-estree': 7.17.0(typescript@5.5.4) - '@typescript-eslint/utils': 7.17.0(eslint@9.7.0)(typescript@5.5.4) - debug: 4.3.5 - eslint: 9.7.0 - ts-api-utils: 1.3.0(typescript@5.5.4) + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.7.2) + '@typescript-eslint/utils': 7.18.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2) + debug: 4.4.0 + eslint: 9.17.0(jiti@1.21.7) + ts-api-utils: 1.4.3(typescript@5.7.2) optionalDependencies: - typescript: 5.5.4 + typescript: 5.7.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@7.17.0': {} + '@typescript-eslint/types@7.18.0': {} '@typescript-eslint/types@7.2.0': {} - '@typescript-eslint/typescript-estree@7.17.0(typescript@5.5.4)': + '@typescript-eslint/typescript-estree@7.18.0(typescript@5.7.2)': dependencies: - '@typescript-eslint/types': 7.17.0 - '@typescript-eslint/visitor-keys': 7.17.0 - debug: 4.3.5 + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/visitor-keys': 7.18.0 + debug: 4.4.0 globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.5 semver: 7.6.3 - ts-api-utils: 1.3.0(typescript@5.5.4) + ts-api-utils: 1.4.3(typescript@5.7.2) optionalDependencies: - typescript: 5.5.4 + typescript: 5.7.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@7.2.0(typescript@5.5.4)': + '@typescript-eslint/typescript-estree@7.2.0(typescript@5.7.2)': dependencies: '@typescript-eslint/types': 7.2.0 '@typescript-eslint/visitor-keys': 7.2.0 - debug: 4.3.5 + debug: 4.4.0 globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.3 semver: 7.6.3 - ts-api-utils: 1.3.0(typescript@5.5.4) + ts-api-utils: 1.4.3(typescript@5.7.2) optionalDependencies: - typescript: 5.5.4 + typescript: 5.7.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@7.17.0(eslint@9.7.0)(typescript@5.5.4)': + '@typescript-eslint/utils@7.18.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2)': dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.7.0) - '@typescript-eslint/scope-manager': 7.17.0 - '@typescript-eslint/types': 7.17.0 - '@typescript-eslint/typescript-estree': 7.17.0(typescript@5.5.4) - eslint: 9.7.0 + '@eslint-community/eslint-utils': 4.4.1(eslint@9.17.0(jiti@1.21.7)) + '@typescript-eslint/scope-manager': 7.18.0 + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.7.2) + eslint: 9.17.0(jiti@1.21.7) transitivePeerDependencies: - supports-color - typescript - '@typescript-eslint/visitor-keys@7.17.0': + '@typescript-eslint/visitor-keys@7.18.0': dependencies: - '@typescript-eslint/types': 7.17.0 + '@typescript-eslint/types': 7.18.0 eslint-visitor-keys: 3.4.3 '@typescript-eslint/visitor-keys@7.2.0': @@ -3471,21 +3874,21 @@ snapshots: '@typescript-eslint/types': 7.2.0 eslint-visitor-keys: 3.4.3 - '@vitejs/plugin-react-swc@3.7.0(@swc/helpers@0.5.12)(vite@5.3.4(@types/node@20.14.11))': + '@vitejs/plugin-react-swc@3.7.2(@swc/helpers@0.5.12)(vite@5.4.11(@types/node@20.17.10))': dependencies: - '@swc/core': 1.7.0(@swc/helpers@0.5.12) - vite: 5.3.4(@types/node@20.14.11) + '@swc/core': 1.10.1(@swc/helpers@0.5.12) + vite: 5.4.11(@types/node@20.17.10) transitivePeerDependencies: - '@swc/helpers' - '@vitejs/plugin-react@4.3.1(vite@5.3.4(@types/node@20.14.11))': + '@vitejs/plugin-react@4.3.4(vite@5.4.11(@types/node@20.17.10))': dependencies: - '@babel/core': 7.24.9 - '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.24.9) - '@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.24.9) + '@babel/core': 7.26.0 + '@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.0) '@types/babel__core': 7.20.5 react-refresh: 0.14.2 - vite: 5.3.4(@types/node@20.14.11) + vite: 5.4.11(@types/node@20.17.10) transitivePeerDependencies: - supports-color @@ -3493,8 +3896,14 @@ snapshots: dependencies: acorn: 8.12.1 + acorn-jsx@5.3.2(acorn@8.14.0): + dependencies: + acorn: 8.14.0 + acorn@8.12.1: {} + acorn@8.14.0: {} + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -3506,10 +3915,6 @@ snapshots: ansi-regex@6.0.1: {} - ansi-styles@3.2.1: - dependencies: - color-convert: 1.9.3 - ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 @@ -3610,6 +4015,16 @@ snapshots: postcss: 8.4.39 postcss-value-parser: 4.2.0 + autoprefixer@10.4.20(postcss@8.4.49): + dependencies: + browserslist: 4.24.3 + caniuse-lite: 1.0.30001689 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.1.1 + postcss: 8.4.49 + postcss-value-parser: 4.2.0 + available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.0.0 @@ -3644,9 +4059,16 @@ snapshots: node-releases: 2.0.18 update-browserslist-db: 1.1.0(browserslist@4.23.2) - bundle-require@5.0.0(esbuild@0.23.0): + browserslist@4.24.3: dependencies: - esbuild: 0.23.0 + caniuse-lite: 1.0.30001689 + electron-to-chromium: 1.5.74 + node-releases: 2.0.19 + update-browserslist-db: 1.1.1(browserslist@4.24.3) + + bundle-require@5.0.0(esbuild@0.24.0): + dependencies: + esbuild: 0.24.0 load-tsconfig: 0.2.5 busboy@1.6.0: @@ -3669,11 +4091,7 @@ snapshots: caniuse-lite@1.0.30001643: {} - chalk@2.4.2: - dependencies: - ansi-styles: 3.2.1 - escape-string-regexp: 1.0.5 - supports-color: 5.5.0 + caniuse-lite@1.0.30001689: {} chalk@4.1.2: dependencies: @@ -3698,6 +4116,10 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + chokidar@4.0.2: + dependencies: + readdirp: 4.0.2 + client-only@0.0.1: {} cliui@8.0.1: @@ -3706,16 +4128,10 @@ snapshots: strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - color-convert@1.9.3: - dependencies: - color-name: 1.1.3 - color-convert@2.0.1: dependencies: color-name: 1.1.4 - color-name@1.1.3: {} - color-name@1.1.4: {} comma-separated-tokens@1.0.8: {} @@ -3730,7 +4146,7 @@ snapshots: date-fns: 2.30.0 lodash: 4.17.21 rxjs: 7.8.1 - shell-quote: 1.8.1 + shell-quote: 1.8.2 spawn-command: 0.0.2 supports-color: 8.1.1 tree-kill: 1.2.2 @@ -3750,6 +4166,12 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + cssesc@3.0.0: {} csstype@3.1.3: {} @@ -3776,7 +4198,7 @@ snapshots: date-fns@2.30.0: dependencies: - '@babel/runtime': 7.24.8 + '@babel/runtime': 7.26.0 debug@3.2.7: dependencies: @@ -3786,6 +4208,10 @@ snapshots: dependencies: ms: 2.1.2 + debug@4.4.0: + dependencies: + ms: 2.1.3 + deep-equal@2.2.3: dependencies: array-buffer-byte-length: 1.0.1 @@ -3839,6 +4265,8 @@ snapshots: electron-to-chromium@1.4.832: {} + electron-to-chromium@1.5.74: {} + emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} @@ -3978,65 +4406,90 @@ snapshots: '@esbuild/win32-ia32': 0.21.5 '@esbuild/win32-x64': 0.21.5 - esbuild@0.23.0: + esbuild@0.23.1: optionalDependencies: - '@esbuild/aix-ppc64': 0.23.0 - '@esbuild/android-arm': 0.23.0 - '@esbuild/android-arm64': 0.23.0 - '@esbuild/android-x64': 0.23.0 - '@esbuild/darwin-arm64': 0.23.0 - '@esbuild/darwin-x64': 0.23.0 - '@esbuild/freebsd-arm64': 0.23.0 - '@esbuild/freebsd-x64': 0.23.0 - '@esbuild/linux-arm': 0.23.0 - '@esbuild/linux-arm64': 0.23.0 - '@esbuild/linux-ia32': 0.23.0 - '@esbuild/linux-loong64': 0.23.0 - '@esbuild/linux-mips64el': 0.23.0 - '@esbuild/linux-ppc64': 0.23.0 - '@esbuild/linux-riscv64': 0.23.0 - '@esbuild/linux-s390x': 0.23.0 - '@esbuild/linux-x64': 0.23.0 - '@esbuild/netbsd-x64': 0.23.0 - '@esbuild/openbsd-arm64': 0.23.0 - '@esbuild/openbsd-x64': 0.23.0 - '@esbuild/sunos-x64': 0.23.0 - '@esbuild/win32-arm64': 0.23.0 - '@esbuild/win32-ia32': 0.23.0 - '@esbuild/win32-x64': 0.23.0 - - escalade@3.1.2: {} - - escape-string-regexp@1.0.5: {} + '@esbuild/aix-ppc64': 0.23.1 + '@esbuild/android-arm': 0.23.1 + '@esbuild/android-arm64': 0.23.1 + '@esbuild/android-x64': 0.23.1 + '@esbuild/darwin-arm64': 0.23.1 + '@esbuild/darwin-x64': 0.23.1 + '@esbuild/freebsd-arm64': 0.23.1 + '@esbuild/freebsd-x64': 0.23.1 + '@esbuild/linux-arm': 0.23.1 + '@esbuild/linux-arm64': 0.23.1 + '@esbuild/linux-ia32': 0.23.1 + '@esbuild/linux-loong64': 0.23.1 + '@esbuild/linux-mips64el': 0.23.1 + '@esbuild/linux-ppc64': 0.23.1 + '@esbuild/linux-riscv64': 0.23.1 + '@esbuild/linux-s390x': 0.23.1 + '@esbuild/linux-x64': 0.23.1 + '@esbuild/netbsd-x64': 0.23.1 + '@esbuild/openbsd-arm64': 0.23.1 + '@esbuild/openbsd-x64': 0.23.1 + '@esbuild/sunos-x64': 0.23.1 + '@esbuild/win32-arm64': 0.23.1 + '@esbuild/win32-ia32': 0.23.1 + '@esbuild/win32-x64': 0.23.1 + + esbuild@0.24.0: + optionalDependencies: + '@esbuild/aix-ppc64': 0.24.0 + '@esbuild/android-arm': 0.24.0 + '@esbuild/android-arm64': 0.24.0 + '@esbuild/android-x64': 0.24.0 + '@esbuild/darwin-arm64': 0.24.0 + '@esbuild/darwin-x64': 0.24.0 + '@esbuild/freebsd-arm64': 0.24.0 + '@esbuild/freebsd-x64': 0.24.0 + '@esbuild/linux-arm': 0.24.0 + '@esbuild/linux-arm64': 0.24.0 + '@esbuild/linux-ia32': 0.24.0 + '@esbuild/linux-loong64': 0.24.0 + '@esbuild/linux-mips64el': 0.24.0 + '@esbuild/linux-ppc64': 0.24.0 + '@esbuild/linux-riscv64': 0.24.0 + '@esbuild/linux-s390x': 0.24.0 + '@esbuild/linux-x64': 0.24.0 + '@esbuild/netbsd-x64': 0.24.0 + '@esbuild/openbsd-arm64': 0.24.0 + '@esbuild/openbsd-x64': 0.24.0 + '@esbuild/sunos-x64': 0.24.0 + '@esbuild/win32-arm64': 0.24.0 + '@esbuild/win32-ia32': 0.24.0 + '@esbuild/win32-x64': 0.24.0 + + escalade@3.2.0: {} escape-string-regexp@4.0.0: {} - eslint-config-next@14.2.5(eslint@9.7.0)(typescript@5.5.4): + eslint-config-next@14.2.5(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2): dependencies: '@next/eslint-plugin-next': 14.2.5 '@rushstack/eslint-patch': 1.10.3 - '@typescript-eslint/parser': 7.2.0(eslint@9.7.0)(typescript@5.5.4) - eslint: 9.7.0 + '@typescript-eslint/parser': 7.2.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2) + eslint: 9.17.0(jiti@1.21.7) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.7.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.7.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@9.7.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@9.7.0) - eslint-plugin-jsx-a11y: 6.9.0(eslint@9.7.0) - eslint-plugin-react: 7.35.0(eslint@9.7.0) - eslint-plugin-react-hooks: 4.6.2(eslint@9.7.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.17.0(jiti@1.21.7)) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint-import-resolver-typescript@3.6.1)(eslint@9.17.0(jiti@1.21.7)) + eslint-plugin-jsx-a11y: 6.9.0(eslint@9.17.0(jiti@1.21.7)) + eslint-plugin-react: 7.35.0(eslint@9.17.0(jiti@1.21.7)) + eslint-plugin-react-hooks: 4.6.2(eslint@9.17.0(jiti@1.21.7)) optionalDependencies: - typescript: 5.5.4 + typescript: 5.7.2 transitivePeerDependencies: - eslint-import-resolver-webpack - supports-color - eslint-config-prettier@9.1.0(eslint@9.7.0): + eslint-config-prettier@9.1.0(eslint@9.17.0(jiti@1.21.7)): dependencies: - eslint: 9.7.0 + eslint: 9.17.0(jiti@1.21.7) - eslint-config-turbo@2.0.9(eslint@9.7.0): + eslint-config-turbo@2.0.9(eslint@9.17.0(jiti@1.21.7)): dependencies: - eslint: 9.7.0 - eslint-plugin-turbo: 2.0.9(eslint@9.7.0) + eslint: 9.17.0(jiti@1.21.7) + eslint-plugin-turbo: 2.0.9(eslint@9.17.0(jiti@1.21.7)) eslint-import-resolver-node@0.3.9: dependencies: @@ -4046,13 +4499,13 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.7.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.7.0): + eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.17.0(jiti@1.21.7)): dependencies: - debug: 4.3.5 + debug: 4.4.0 enhanced-resolve: 5.17.0 - eslint: 9.7.0 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@9.7.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.7.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.7.0))(eslint@9.7.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@9.7.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@9.7.0) + eslint: 9.17.0(jiti@1.21.7) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@9.17.0(jiti@1.21.7)) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint-import-resolver-typescript@3.6.1)(eslint@9.17.0(jiti@1.21.7)) fast-glob: 3.3.2 get-tsconfig: 4.7.6 is-core-module: 2.15.0 @@ -4063,18 +4516,18 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@9.7.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.7.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.7.0))(eslint@9.7.0): + eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@9.17.0(jiti@1.21.7)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 7.2.0(eslint@9.7.0)(typescript@5.5.4) - eslint: 9.7.0 + '@typescript-eslint/parser': 7.2.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2) + eslint: 9.17.0(jiti@1.21.7) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.7.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.7.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.17.0(jiti@1.21.7)) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@9.7.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@9.7.0): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint-import-resolver-typescript@3.6.1)(eslint@9.17.0(jiti@1.21.7)): dependencies: array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 @@ -4082,9 +4535,9 @@ snapshots: array.prototype.flatmap: 1.3.2 debug: 3.2.7 doctrine: 2.1.0 - eslint: 9.7.0 + eslint: 9.17.0(jiti@1.21.7) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@9.7.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.7.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.7.0))(eslint@9.7.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@9.17.0(jiti@1.21.7)) hasown: 2.0.2 is-core-module: 2.15.0 is-glob: 4.0.3 @@ -4095,13 +4548,13 @@ snapshots: semver: 6.3.1 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 7.2.0(eslint@9.7.0)(typescript@5.5.4) + '@typescript-eslint/parser': 7.2.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-jsx-a11y@6.9.0(eslint@9.7.0): + eslint-plugin-jsx-a11y@6.9.0(eslint@9.17.0(jiti@1.21.7)): dependencies: aria-query: 5.1.3 array-includes: 3.1.8 @@ -4112,7 +4565,7 @@ snapshots: damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 es-iterator-helpers: 1.0.19 - eslint: 9.7.0 + eslint: 9.17.0(jiti@1.21.7) hasown: 2.0.2 jsx-ast-utils: 3.3.5 language-tags: 1.0.9 @@ -4121,15 +4574,15 @@ snapshots: safe-regex-test: 1.0.3 string.prototype.includes: 2.0.0 - eslint-plugin-react-hooks@4.6.2(eslint@9.7.0): + eslint-plugin-react-hooks@4.6.2(eslint@9.17.0(jiti@1.21.7)): dependencies: - eslint: 9.7.0 + eslint: 9.17.0(jiti@1.21.7) - eslint-plugin-react-refresh@0.4.9(eslint@9.7.0): + eslint-plugin-react-refresh@0.4.16(eslint@9.17.0(jiti@1.21.7)): dependencies: - eslint: 9.7.0 + eslint: 9.17.0(jiti@1.21.7) - eslint-plugin-react@7.35.0(eslint@9.7.0): + eslint-plugin-react@7.35.0(eslint@9.17.0(jiti@1.21.7)): dependencies: array-includes: 3.1.8 array.prototype.findlast: 1.2.5 @@ -4137,7 +4590,7 @@ snapshots: array.prototype.tosorted: 1.1.4 doctrine: 2.1.0 es-iterator-helpers: 1.0.19 - eslint: 9.7.0 + eslint: 9.17.0(jiti@1.21.7) estraverse: 5.3.0 hasown: 2.0.2 jsx-ast-utils: 3.3.5 @@ -4151,20 +4604,68 @@ snapshots: string.prototype.matchall: 4.0.11 string.prototype.repeat: 1.0.0 - eslint-plugin-turbo@2.0.9(eslint@9.7.0): + eslint-plugin-turbo@2.0.9(eslint@9.17.0(jiti@1.21.7)): dependencies: dotenv: 16.0.3 - eslint: 9.7.0 + eslint: 9.17.0(jiti@1.21.7) eslint-scope@8.0.2: dependencies: esrecurse: 4.3.0 estraverse: 5.3.0 + eslint-scope@8.2.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + eslint-visitor-keys@3.4.3: {} eslint-visitor-keys@4.0.0: {} + eslint-visitor-keys@4.2.0: {} + + eslint@9.17.0(jiti@1.21.7): + dependencies: + '@eslint-community/eslint-utils': 4.4.1(eslint@9.17.0(jiti@1.21.7)) + '@eslint-community/regexpp': 4.12.1 + '@eslint/config-array': 0.19.1 + '@eslint/core': 0.9.1 + '@eslint/eslintrc': 3.2.0 + '@eslint/js': 9.17.0 + '@eslint/plugin-kit': 0.2.4 + '@humanfs/node': 0.16.6 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.1 + '@types/estree': 1.0.6 + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.0 + escape-string-regexp: 4.0.0 + eslint-scope: 8.2.0 + eslint-visitor-keys: 4.2.0 + espree: 10.3.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 1.21.7 + transitivePeerDependencies: + - supports-color + eslint@9.7.0: dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@9.7.0) @@ -4210,6 +4711,12 @@ snapshots: acorn-jsx: 5.3.2(acorn@8.12.1) eslint-visitor-keys: 4.0.0 + espree@10.3.0: + dependencies: + acorn: 8.14.0 + acorn-jsx: 5.3.2(acorn@8.14.0) + eslint-visitor-keys: 4.2.0 + esquery@1.6.0: dependencies: estraverse: 5.3.0 @@ -4222,18 +4729,6 @@ snapshots: esutils@2.0.3: {} - execa@5.1.1: - dependencies: - cross-spawn: 7.0.3 - get-stream: 6.0.1 - human-signals: 2.1.0 - is-stream: 2.0.1 - merge-stream: 2.0.0 - npm-run-path: 4.0.1 - onetime: 5.1.2 - signal-exit: 3.0.7 - strip-final-newline: 2.0.0 - fast-deep-equal@3.1.3: {} fast-glob@3.3.2: @@ -4242,7 +4737,7 @@ snapshots: '@nodelib/fs.walk': 1.2.8 glob-parent: 5.1.2 merge2: 1.4.1 - micromatch: 4.0.7 + micromatch: 4.0.8 fast-json-stable-stringify@2.1.0: {} @@ -4256,6 +4751,10 @@ snapshots: dependencies: format: 0.2.2 + fdir@6.4.2(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -4271,10 +4770,10 @@ snapshots: flat-cache@4.0.1: dependencies: - flatted: 3.3.1 + flatted: 3.3.2 keyv: 4.5.4 - flatted@3.3.1: {} + flatted@3.3.2: {} for-each@0.3.3: dependencies: @@ -4282,7 +4781,12 @@ snapshots: foreground-child@3.2.1: dependencies: - cross-spawn: 7.0.3 + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + foreground-child@3.3.0: + dependencies: + cross-spawn: 7.0.6 signal-exit: 4.1.0 format@0.2.2: {} @@ -4315,8 +4819,6 @@ snapshots: has-symbols: 1.0.3 hasown: 2.0.2 - get-stream@6.0.1: {} - get-symbol-description@1.0.2: dependencies: call-bind: 1.0.7 @@ -4345,11 +4847,11 @@ snapshots: glob@10.4.5: dependencies: - foreground-child: 3.2.1 + foreground-child: 3.3.0 jackspeak: 3.4.3 minimatch: 9.0.5 minipass: 7.1.2 - package-json-from-dist: 1.0.0 + package-json-from-dist: 1.0.1 path-scurry: 1.11.1 globals@11.12.0: {} @@ -4366,7 +4868,7 @@ snapshots: array-union: 2.1.0 dir-glob: 3.0.1 fast-glob: 3.3.2 - ignore: 5.3.1 + ignore: 5.3.2 merge2: 1.4.1 slash: 3.0.0 @@ -4380,8 +4882,6 @@ snapshots: has-bigints@1.0.2: {} - has-flag@3.0.0: {} - has-flag@4.0.0: {} has-property-descriptors@1.0.2: @@ -4412,10 +4912,10 @@ snapshots: highlight.js@10.7.3: {} - human-signals@2.1.0: {} - ignore@5.3.1: {} + ignore@5.3.2: {} + import-fresh@3.3.0: dependencies: parent-module: 1.0.1 @@ -4469,6 +4969,10 @@ snapshots: dependencies: hasown: 2.0.2 + is-core-module@2.16.0: + dependencies: + hasown: 2.0.2 + is-data-view@1.0.1: dependencies: is-typed-array: 1.1.13 @@ -4520,8 +5024,6 @@ snapshots: dependencies: call-bind: 1.0.7 - is-stream@2.0.1: {} - is-string@1.0.7: dependencies: has-tostringtag: 1.0.2 @@ -4569,7 +5071,7 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 - jiti@1.21.6: {} + jiti@1.21.7: {} joycon@3.1.1: {} @@ -4579,7 +5081,7 @@ snapshots: dependencies: argparse: 2.0.1 - jsesc@2.5.2: {} + jsesc@3.1.0: {} json-buffer@3.0.1: {} @@ -4617,7 +5119,7 @@ snapshots: lilconfig@2.1.0: {} - lilconfig@3.1.2: {} + lilconfig@3.1.3: {} lines-and-columns@1.2.4: {} @@ -4648,17 +5150,13 @@ snapshots: dependencies: yallist: 3.1.1 - merge-stream@2.0.0: {} - merge2@1.4.1: {} - micromatch@4.0.7: + micromatch@4.0.8: dependencies: braces: 3.0.3 picomatch: 2.3.1 - mimic-fn@2.1.0: {} - minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 @@ -4687,6 +5185,8 @@ snapshots: nanoid@3.3.7: {} + nanoid@3.3.8: {} + natural-compare@1.4.0: {} next@14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): @@ -4716,14 +5216,12 @@ snapshots: node-releases@2.0.18: {} + node-releases@2.0.19: {} + normalize-path@3.0.0: {} normalize-range@0.1.2: {} - npm-run-path@4.0.1: - dependencies: - path-key: 3.1.1 - object-assign@4.1.1: {} object-hash@3.0.0: {} @@ -4769,10 +5267,6 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.0.0 - onetime@5.1.2: - dependencies: - mimic-fn: 2.1.0 - optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -4790,7 +5284,7 @@ snapshots: dependencies: p-limit: 3.1.0 - package-json-from-dist@1.0.0: {} + package-json-from-dist@1.0.1: {} parent-module@1.0.1: dependencies: @@ -4820,47 +5314,51 @@ snapshots: picocolors@1.0.1: {} + picocolors@1.1.1: {} + picomatch@2.3.1: {} + picomatch@4.0.2: {} + pify@2.3.0: {} pirates@4.0.6: {} possible-typed-array-names@1.0.0: {} - postcss-import@15.1.0(postcss@8.4.39): + postcss-import@15.1.0(postcss@8.4.49): dependencies: - postcss: 8.4.39 + postcss: 8.4.49 postcss-value-parser: 4.2.0 read-cache: 1.0.0 - resolve: 1.22.8 + resolve: 1.22.9 - postcss-js@4.0.1(postcss@8.4.39): + postcss-js@4.0.1(postcss@8.4.49): dependencies: camelcase-css: 2.0.1 - postcss: 8.4.39 + postcss: 8.4.49 - postcss-load-config@4.0.2(postcss@8.4.39): + postcss-load-config@4.0.2(postcss@8.4.49): dependencies: - lilconfig: 3.1.2 - yaml: 2.4.5 + lilconfig: 3.1.3 + yaml: 2.6.1 optionalDependencies: - postcss: 8.4.39 + postcss: 8.4.49 - postcss-load-config@6.0.1(jiti@1.21.6)(postcss@8.4.39)(yaml@2.4.5): + postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.4.49)(yaml@2.6.1): dependencies: - lilconfig: 3.1.2 + lilconfig: 3.1.3 optionalDependencies: - jiti: 1.21.6 - postcss: 8.4.39 - yaml: 2.4.5 + jiti: 1.21.7 + postcss: 8.4.49 + yaml: 2.6.1 - postcss-nested@6.2.0(postcss@8.4.39): + postcss-nested@6.2.0(postcss@8.4.49): dependencies: - postcss: 8.4.39 - postcss-selector-parser: 6.1.1 + postcss: 8.4.49 + postcss-selector-parser: 6.1.2 - postcss-selector-parser@6.1.1: + postcss-selector-parser@6.1.2: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 @@ -4869,9 +5367,9 @@ snapshots: postcss@8.4.31: dependencies: - nanoid: 3.3.7 - picocolors: 1.0.1 - source-map-js: 1.2.0 + nanoid: 3.3.8 + picocolors: 1.1.1 + source-map-js: 1.2.1 postcss@8.4.39: dependencies: @@ -4879,6 +5377,12 @@ snapshots: picocolors: 1.0.1 source-map-js: 1.2.0 + postcss@8.4.49: + dependencies: + nanoid: 3.3.8 + picocolors: 1.1.1 + source-map-js: 1.2.1 + prelude-ls@1.2.1: {} prettier@3.3.3: {} @@ -4932,6 +5436,8 @@ snapshots: dependencies: picomatch: 2.3.1 + readdirp@4.0.2: {} + reflect.getprototypeof@1.0.6: dependencies: call-bind: 1.0.7 @@ -4971,6 +5477,12 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + resolve@1.22.9: + dependencies: + is-core-module: 2.16.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + resolve@2.0.0-next.5: dependencies: is-core-module: 2.15.0 @@ -4979,26 +5491,29 @@ snapshots: reusify@1.0.4: {} - rollup@4.19.0: + rollup@4.28.1: dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.19.0 - '@rollup/rollup-android-arm64': 4.19.0 - '@rollup/rollup-darwin-arm64': 4.19.0 - '@rollup/rollup-darwin-x64': 4.19.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.19.0 - '@rollup/rollup-linux-arm-musleabihf': 4.19.0 - '@rollup/rollup-linux-arm64-gnu': 4.19.0 - '@rollup/rollup-linux-arm64-musl': 4.19.0 - '@rollup/rollup-linux-powerpc64le-gnu': 4.19.0 - '@rollup/rollup-linux-riscv64-gnu': 4.19.0 - '@rollup/rollup-linux-s390x-gnu': 4.19.0 - '@rollup/rollup-linux-x64-gnu': 4.19.0 - '@rollup/rollup-linux-x64-musl': 4.19.0 - '@rollup/rollup-win32-arm64-msvc': 4.19.0 - '@rollup/rollup-win32-ia32-msvc': 4.19.0 - '@rollup/rollup-win32-x64-msvc': 4.19.0 + '@rollup/rollup-android-arm-eabi': 4.28.1 + '@rollup/rollup-android-arm64': 4.28.1 + '@rollup/rollup-darwin-arm64': 4.28.1 + '@rollup/rollup-darwin-x64': 4.28.1 + '@rollup/rollup-freebsd-arm64': 4.28.1 + '@rollup/rollup-freebsd-x64': 4.28.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.28.1 + '@rollup/rollup-linux-arm-musleabihf': 4.28.1 + '@rollup/rollup-linux-arm64-gnu': 4.28.1 + '@rollup/rollup-linux-arm64-musl': 4.28.1 + '@rollup/rollup-linux-loongarch64-gnu': 4.28.1 + '@rollup/rollup-linux-powerpc64le-gnu': 4.28.1 + '@rollup/rollup-linux-riscv64-gnu': 4.28.1 + '@rollup/rollup-linux-s390x-gnu': 4.28.1 + '@rollup/rollup-linux-x64-gnu': 4.28.1 + '@rollup/rollup-linux-x64-musl': 4.28.1 + '@rollup/rollup-win32-arm64-msvc': 4.28.1 + '@rollup/rollup-win32-ia32-msvc': 4.28.1 + '@rollup/rollup-win32-x64-msvc': 4.28.1 fsevents: 2.3.3 run-parallel@1.2.0: @@ -5007,7 +5522,7 @@ snapshots: rxjs@7.8.1: dependencies: - tslib: 2.6.3 + tslib: 2.8.1 safe-array-concat@1.1.2: dependencies: @@ -5052,7 +5567,7 @@ snapshots: shebang-regex@3.0.0: {} - shell-quote@1.8.1: {} + shell-quote@1.8.2: {} side-channel@1.0.6: dependencies: @@ -5061,14 +5576,14 @@ snapshots: get-intrinsic: 1.2.4 object-inspect: 1.13.2 - signal-exit@3.0.7: {} - signal-exit@4.1.0: {} slash@3.0.0: {} source-map-js@1.2.0: {} + source-map-js@1.2.1: {} + source-map@0.8.0-beta.0: dependencies: whatwg-url: 7.1.0 @@ -5149,8 +5664,6 @@ snapshots: strip-bom@3.0.0: {} - strip-final-newline@2.0.0: {} - strip-json-comments@3.1.1: {} styled-jsx@5.1.1(react@18.3.1): @@ -5160,7 +5673,7 @@ snapshots: sucrase@3.35.0: dependencies: - '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/gen-mapping': 0.3.8 commander: 4.1.1 glob: 10.4.5 lines-and-columns: 1.2.4 @@ -5168,10 +5681,6 @@ snapshots: pirates: 4.0.6 ts-interface-checker: 0.1.13 - supports-color@5.5.0: - dependencies: - has-flag: 3.0.0 - supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -5192,19 +5701,19 @@ snapshots: fast-glob: 3.3.2 glob-parent: 6.0.2 is-glob: 4.0.3 - jiti: 1.21.6 + jiti: 1.21.7 lilconfig: 2.1.0 - micromatch: 4.0.7 + micromatch: 4.0.8 normalize-path: 3.0.0 object-hash: 3.0.0 - picocolors: 1.0.1 - postcss: 8.4.39 - postcss-import: 15.1.0(postcss@8.4.39) - postcss-js: 4.0.1(postcss@8.4.39) - postcss-load-config: 4.0.2(postcss@8.4.39) - postcss-nested: 6.2.0(postcss@8.4.39) - postcss-selector-parser: 6.1.1 - resolve: 1.22.8 + picocolors: 1.1.1 + postcss: 8.4.49 + postcss-import: 15.1.0(postcss@8.4.49) + postcss-js: 4.0.1(postcss@8.4.49) + postcss-load-config: 4.0.2(postcss@8.4.49) + postcss-nested: 6.2.0(postcss@8.4.49) + postcss-selector-parser: 6.1.2 + resolve: 1.22.9 sucrase: 3.35.0 transitivePeerDependencies: - ts-node @@ -5221,7 +5730,12 @@ snapshots: dependencies: any-promise: 1.3.0 - to-fast-properties@2.0.0: {} + tinyexec@0.3.1: {} + + tinyglobby@0.2.10: + dependencies: + fdir: 6.4.2(picomatch@4.0.2) + picomatch: 4.0.2 to-regex-range@5.0.1: dependencies: @@ -5235,9 +5749,9 @@ snapshots: tree-kill@1.2.2: {} - ts-api-utils@1.3.0(typescript@5.5.4): + ts-api-utils@1.4.3(typescript@5.7.2): dependencies: - typescript: 5.5.4 + typescript: 5.7.2 ts-interface-checker@0.1.13: {} @@ -5250,28 +5764,30 @@ snapshots: tslib@2.6.3: {} - tsup@8.2.2(@swc/core@1.7.0)(jiti@1.21.6)(postcss@8.4.39)(typescript@5.5.4)(yaml@2.4.5): + tslib@2.8.1: {} + + tsup@8.3.5(@swc/core@1.10.1(@swc/helpers@0.5.12))(jiti@1.21.7)(postcss@8.4.49)(typescript@5.7.2)(yaml@2.6.1): dependencies: - bundle-require: 5.0.0(esbuild@0.23.0) + bundle-require: 5.0.0(esbuild@0.24.0) cac: 6.7.14 - chokidar: 3.6.0 + chokidar: 4.0.2 consola: 3.2.3 - debug: 4.3.5 - esbuild: 0.23.0 - execa: 5.1.1 - globby: 11.1.0 + debug: 4.4.0 + esbuild: 0.24.0 joycon: 3.1.1 - picocolors: 1.0.1 - postcss-load-config: 6.0.1(jiti@1.21.6)(postcss@8.4.39)(yaml@2.4.5) + picocolors: 1.1.1 + postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.4.49)(yaml@2.6.1) resolve-from: 5.0.0 - rollup: 4.19.0 + rollup: 4.28.1 source-map: 0.8.0-beta.0 sucrase: 3.35.0 + tinyexec: 0.3.1 + tinyglobby: 0.2.10 tree-kill: 1.2.2 optionalDependencies: - '@swc/core': 1.7.0(@swc/helpers@0.5.12) - postcss: 8.4.39 - typescript: 5.5.4 + '@swc/core': 1.10.1(@swc/helpers@0.5.12) + postcss: 8.4.49 + typescript: 5.7.2 transitivePeerDependencies: - jiti - supports-color @@ -5343,6 +5859,8 @@ snapshots: typescript@5.5.4: {} + typescript@5.7.2: {} + unbox-primitive@1.0.2: dependencies: call-bind: 1.0.7 @@ -5352,31 +5870,39 @@ snapshots: undici-types@5.26.5: {} + undici-types@6.19.8: {} + update-browserslist-db@1.1.0(browserslist@4.23.2): dependencies: browserslist: 4.23.2 - escalade: 3.1.2 + escalade: 3.2.0 picocolors: 1.0.1 + update-browserslist-db@1.1.1(browserslist@4.24.3): + dependencies: + browserslist: 4.24.3 + escalade: 3.2.0 + picocolors: 1.1.1 + uri-js@4.4.1: dependencies: punycode: 2.3.1 util-deprecate@1.0.2: {} - vite-plugin-singlefile@2.0.2(rollup@4.19.0)(vite@5.3.4(@types/node@20.14.11)): + vite-plugin-singlefile@2.1.0(rollup@4.28.1)(vite@5.4.11(@types/node@20.17.10)): dependencies: - micromatch: 4.0.7 - rollup: 4.19.0 - vite: 5.3.4(@types/node@20.14.11) + micromatch: 4.0.8 + rollup: 4.28.1 + vite: 5.4.11(@types/node@20.17.10) - vite@5.3.4(@types/node@20.14.11): + vite@5.4.11(@types/node@20.17.10): dependencies: esbuild: 0.21.5 - postcss: 8.4.39 - rollup: 4.19.0 + postcss: 8.4.49 + rollup: 4.28.1 optionalDependencies: - '@types/node': 20.14.11 + '@types/node': 20.17.10 fsevents: 2.3.3 webidl-conversions@4.0.2: {} @@ -5449,14 +5975,14 @@ snapshots: yallist@3.1.1: {} - yaml@2.4.5: {} + yaml@2.6.1: {} yargs-parser@21.1.1: {} yargs@17.7.2: dependencies: cliui: 8.0.1 - escalade: 3.1.2 + escalade: 3.2.0 get-caller-file: 2.0.5 require-directory: 2.1.1 string-width: 4.2.3 From e784c5a64120eb587d09498dba2f448f02fd499f Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Wed, 18 Dec 2024 15:48:41 -0300 Subject: [PATCH 016/168] Update dependencies --- apps/debug/package.json | 14 +- package.json | 6 +- packages/eslint-config-custom/package.json | 4 +- packages/plugin-ui/package.json | 10 +- pnpm-lock.yaml | 1767 +++++++++----------- 5 files changed, 788 insertions(+), 1013 deletions(-) diff --git a/apps/debug/package.json b/apps/debug/package.json index bd259ac5..df50a569 100644 --- a/apps/debug/package.json +++ b/apps/debug/package.json @@ -11,20 +11,20 @@ }, "dependencies": { "backend": "workspace:*", - "next": "^14.2.5", + "next": "^14.2.20", "plugin-ui": "workspace:*", "react": "^18.3.1", "react-dom": "^18.3.1" }, "devDependencies": { - "@types/node": "^20.14.11", - "@types/react": "^18.3.3", - "@types/react-dom": "^18.3.0", - "autoprefixer": "^10.4.19", + "@types/node": "^20.17.10", + "@types/react": "^18.3.17", + "@types/react-dom": "^18.3.5", + "autoprefixer": "^10.4.20", "eslint-config-custom": "workspace:*", - "postcss": "^8.4.39", + "postcss": "^8.4.49", "tailwindcss": "3.4.6", "tsconfig": "workspace:*", - "typescript": "^5.5.4" + "typescript": "^5.7.2" } } diff --git a/package.json b/package.json index 76de04ed..34080c0f 100644 --- a/package.json +++ b/package.json @@ -10,9 +10,9 @@ "format": "prettier --write \"**/*.{ts,tsx,css,md}\"" }, "devDependencies": { - "eslint": "^9.7.0", + "eslint": "^9.17.0", "eslint-config-custom": "workspace:*", - "prettier": "^3.3.3", - "turbo": "^2.0.9" + "prettier": "^3.4.2", + "turbo": "^2.3.3" } } diff --git a/packages/eslint-config-custom/package.json b/packages/eslint-config-custom/package.json index 72a9b377..91fbbced 100644 --- a/packages/eslint-config-custom/package.json +++ b/packages/eslint-config-custom/package.json @@ -4,9 +4,9 @@ "main": "index.js", "license": "MIT", "dependencies": { - "eslint-config-next": "^14.2.5", + "eslint-config-next": "^14.2.20", "eslint-config-prettier": "^9.1.0", - "eslint-config-turbo": "^2.0.9", + "eslint-config-turbo": "^2.3.3", "eslint-plugin-react": "7.35.0" }, "publishConfig": { diff --git a/packages/plugin-ui/package.json b/packages/plugin-ui/package.json index a0cc2440..faa9c96f 100644 --- a/packages/plugin-ui/package.json +++ b/packages/plugin-ui/package.json @@ -12,16 +12,16 @@ "dependencies": { "copy-to-clipboard": "^3.3.3", "react": "^18.3.1", - "react-syntax-highlighter": "^15.5.0", + "react-syntax-highlighter": "^15.6.1", "tailwindcss": "3.4.6" }, "devDependencies": { - "@types/react": "^18.3.3", - "@types/react-dom": "^18.3.0", + "@types/react": "^18.3.17", + "@types/react-dom": "^18.3.5", "@types/react-syntax-highlighter": "15.5.13", - "eslint": "^9.7.0", + "eslint": "^9.17.0", "eslint-config-custom": "workspace:*", "tsconfig": "workspace:*", - "typescript": "^5.5.4" + "typescript": "^5.7.2" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a6758e26..d799e0a1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,17 +9,17 @@ importers: .: devDependencies: eslint: - specifier: ^9.7.0 - version: 9.7.0 + specifier: ^9.17.0 + version: 9.17.0(jiti@1.21.7) eslint-config-custom: specifier: workspace:* version: link:packages/eslint-config-custom prettier: - specifier: ^3.3.3 - version: 3.3.3 + specifier: ^3.4.2 + version: 3.4.2 turbo: - specifier: ^2.0.9 - version: 2.0.9 + specifier: ^2.3.3 + version: 2.3.3 apps/debug: dependencies: @@ -27,8 +27,8 @@ importers: specifier: workspace:* version: link:../../packages/backend next: - specifier: ^14.2.5 - version: 14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^14.2.20 + version: 14.2.20(react-dom@18.3.1(react@18.3.1))(react@18.3.1) plugin-ui: specifier: workspace:* version: link:../../packages/plugin-ui @@ -40,23 +40,23 @@ importers: version: 18.3.1(react@18.3.1) devDependencies: '@types/node': - specifier: ^20.14.11 - version: 20.14.11 + specifier: ^20.17.10 + version: 20.17.10 '@types/react': - specifier: ^18.3.3 - version: 18.3.3 + specifier: ^18.3.17 + version: 18.3.17 '@types/react-dom': - specifier: ^18.3.0 - version: 18.3.0 + specifier: ^18.3.5 + version: 18.3.5(@types/react@18.3.17) autoprefixer: - specifier: ^10.4.19 - version: 10.4.19(postcss@8.4.39) + specifier: ^10.4.20 + version: 10.4.20(postcss@8.4.49) eslint-config-custom: specifier: workspace:* version: link:../../packages/eslint-config-custom postcss: - specifier: ^8.4.39 - version: 8.4.39 + specifier: ^8.4.49 + version: 8.4.49 tailwindcss: specifier: 3.4.6 version: 3.4.6 @@ -64,8 +64,8 @@ importers: specifier: workspace:* version: link:../../packages/tsconfig typescript: - specifier: ^5.5.4 - version: 5.5.4 + specifier: ^5.7.2 + version: 5.7.2 apps/plugin: dependencies: @@ -180,14 +180,14 @@ importers: packages/eslint-config-custom: dependencies: eslint-config-next: - specifier: ^14.2.5 - version: 14.2.5(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2) + specifier: ^14.2.20 + version: 14.2.20(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2) eslint-config-prettier: specifier: ^9.1.0 version: 9.1.0(eslint@9.17.0(jiti@1.21.7)) eslint-config-turbo: - specifier: ^2.0.9 - version: 2.0.9(eslint@9.17.0(jiti@1.21.7)) + specifier: ^2.3.3 + version: 2.3.3(eslint@9.17.0(jiti@1.21.7)) eslint-plugin-react: specifier: 7.35.0 version: 7.35.0(eslint@9.17.0(jiti@1.21.7)) @@ -201,24 +201,24 @@ importers: specifier: ^18.3.1 version: 18.3.1 react-syntax-highlighter: - specifier: ^15.5.0 - version: 15.5.0(react@18.3.1) + specifier: ^15.6.1 + version: 15.6.1(react@18.3.1) tailwindcss: specifier: 3.4.6 version: 3.4.6 devDependencies: '@types/react': - specifier: ^18.3.3 - version: 18.3.3 + specifier: ^18.3.17 + version: 18.3.17 '@types/react-dom': - specifier: ^18.3.0 - version: 18.3.0 + specifier: ^18.3.5 + version: 18.3.5(@types/react@18.3.17) '@types/react-syntax-highlighter': specifier: 15.5.13 version: 15.5.13 eslint: - specifier: ^9.7.0 - version: 9.7.0 + specifier: ^9.17.0 + version: 9.17.0(jiti@1.21.7) eslint-config-custom: specifier: workspace:* version: link:../eslint-config-custom @@ -226,8 +226,8 @@ importers: specifier: workspace:* version: link:../tsconfig typescript: - specifier: ^5.5.4 - version: 5.5.4 + specifier: ^5.7.2 + version: 5.7.2 packages/tsconfig: {} @@ -308,10 +308,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/runtime@7.24.8': - resolution: {integrity: sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==} - engines: {node: '>=6.9.0'} - '@babel/runtime@7.26.0': resolution: {integrity: sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==} engines: {node: '>=6.9.0'} @@ -754,30 +750,16 @@ packages: cpu: [x64] os: [win32] - '@eslint-community/eslint-utils@4.4.0': - resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - '@eslint-community/eslint-utils@4.4.1': resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - '@eslint-community/regexpp@4.11.0': - resolution: {integrity: sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint-community/regexpp@4.12.1': resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/config-array@0.17.1': - resolution: {integrity: sha512-BlYOpej8AQ8Ev9xVqroV7a02JK3SkBAaN9GfMMH9W6Ch8FlQlkjGw4Ir7+FgYwfirivAf4t+GtzuAxqfukmISA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/config-array@0.19.1': resolution: {integrity: sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -786,10 +768,6 @@ packages: resolution: {integrity: sha512-GuUdqkyyzQI5RMIWkHhvTWLCyLo1jNK3vzkSyaExH5kHPDHcuL2VOpHjmMY+y3+NC69qAKToBqldTBgYeLSr9Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/eslintrc@3.1.0': - resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/eslintrc@3.2.0': resolution: {integrity: sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -798,14 +776,6 @@ packages: resolution: {integrity: sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.7.0': - resolution: {integrity: sha512-ChuWDQenef8OSFnvuxv0TCVxEwmu3+hPNKvM9B34qpM0rDRbjL8t5QkQeHHeAfsKQjuH9wS82WeCi1J/owatng==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/object-schema@2.1.4': - resolution: {integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/object-schema@2.1.5': resolution: {integrity: sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -829,10 +799,6 @@ packages: resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} - '@humanwhocodes/retry@0.3.0': - resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==} - engines: {node: '>=18.18'} - '@humanwhocodes/retry@0.3.1': resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} engines: {node: '>=18.18'} @@ -863,62 +829,62 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} - '@next/env@14.2.5': - resolution: {integrity: sha512-/zZGkrTOsraVfYjGP8uM0p6r0BDT6xWpkjdVbcz66PJVSpwXX3yNiRycxAuDfBKGWBrZBXRuK/YVlkNgxHGwmA==} + '@next/env@14.2.20': + resolution: {integrity: sha512-JfDpuOCB0UBKlEgEy/H6qcBSzHimn/YWjUHzKl1jMeUO+QVRdzmTTl8gFJaNO87c8DXmVKhFCtwxQ9acqB3+Pw==} - '@next/eslint-plugin-next@14.2.5': - resolution: {integrity: sha512-LY3btOpPh+OTIpviNojDpUdIbHW9j0JBYBjsIp8IxtDFfYFyORvw3yNq6N231FVqQA7n7lwaf7xHbVJlA1ED7g==} + '@next/eslint-plugin-next@14.2.20': + resolution: {integrity: sha512-T0JRi706KLbvR1Uc46t56VtawbhR/igdBagzOrA7G+vv4rvjwnlu/Y4/Iq6X9TDVj5UZjyot4lUdkNd3V2kLhw==} - '@next/swc-darwin-arm64@14.2.5': - resolution: {integrity: sha512-/9zVxJ+K9lrzSGli1///ujyRfon/ZneeZ+v4ptpiPoOU+GKZnm8Wj8ELWU1Pm7GHltYRBklmXMTUqM/DqQ99FQ==} + '@next/swc-darwin-arm64@14.2.20': + resolution: {integrity: sha512-WDfq7bmROa5cIlk6ZNonNdVhKmbCv38XteVFYsxea1vDJt3SnYGgxLGMTXQNfs5OkFvAhmfKKrwe7Y0Hs+rWOg==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@14.2.5': - resolution: {integrity: sha512-vXHOPCwfDe9qLDuq7U1OYM2wUY+KQ4Ex6ozwsKxp26BlJ6XXbHleOUldenM67JRyBfVjv371oneEvYd3H2gNSA==} + '@next/swc-darwin-x64@14.2.20': + resolution: {integrity: sha512-XIQlC+NAmJPfa2hruLvr1H1QJJeqOTDV+v7tl/jIdoFvqhoihvSNykLU/G6NMgoeo+e/H7p/VeWSOvMUHKtTIg==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@14.2.5': - resolution: {integrity: sha512-vlhB8wI+lj8q1ExFW8lbWutA4M2ZazQNvMWuEDqZcuJJc78iUnLdPPunBPX8rC4IgT6lIx/adB+Cwrl99MzNaA==} + '@next/swc-linux-arm64-gnu@14.2.20': + resolution: {integrity: sha512-pnzBrHTPXIMm5QX3QC8XeMkpVuoAYOmyfsO4VlPn+0NrHraNuWjdhe+3xLq01xR++iCvX+uoeZmJDKcOxI201Q==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-arm64-musl@14.2.5': - resolution: {integrity: sha512-NpDB9NUR2t0hXzJJwQSGu1IAOYybsfeB+LxpGsXrRIb7QOrYmidJz3shzY8cM6+rO4Aojuef0N/PEaX18pi9OA==} + '@next/swc-linux-arm64-musl@14.2.20': + resolution: {integrity: sha512-WhJJAFpi6yqmUx1momewSdcm/iRXFQS0HU2qlUGlGE/+98eu7JWLD5AAaP/tkK1mudS/rH2f9E3WCEF2iYDydQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-x64-gnu@14.2.5': - resolution: {integrity: sha512-8XFikMSxWleYNryWIjiCX+gU201YS+erTUidKdyOVYi5qUQo/gRxv/3N1oZFCgqpesN6FPeqGM72Zve+nReVXQ==} + '@next/swc-linux-x64-gnu@14.2.20': + resolution: {integrity: sha512-ao5HCbw9+iG1Kxm8XsGa3X174Ahn17mSYBQlY6VGsdsYDAbz/ZP13wSLfvlYoIDn1Ger6uYA+yt/3Y9KTIupRg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-linux-x64-musl@14.2.5': - resolution: {integrity: sha512-6QLwi7RaYiQDcRDSU/os40r5o06b5ue7Jsk5JgdRBGGp8l37RZEh9JsLSM8QF0YDsgcosSeHjglgqi25+m04IQ==} + '@next/swc-linux-x64-musl@14.2.20': + resolution: {integrity: sha512-CXm/kpnltKTT7945np6Td3w7shj/92TMRPyI/VvveFe8+YE+/YOJ5hyAWK5rpx711XO1jBCgXl211TWaxOtkaA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-win32-arm64-msvc@14.2.5': - resolution: {integrity: sha512-1GpG2VhbspO+aYoMOQPQiqc/tG3LzmsdBH0LhnDS3JrtDx2QmzXe0B6mSZZiN3Bq7IOMXxv1nlsjzoS1+9mzZw==} + '@next/swc-win32-arm64-msvc@14.2.20': + resolution: {integrity: sha512-upJn2HGQgKNDbXVfIgmqT2BN8f3z/mX8ddoyi1I565FHbfowVK5pnMEwauvLvaJf4iijvuKq3kw/b6E9oIVRWA==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-ia32-msvc@14.2.5': - resolution: {integrity: sha512-Igh9ZlxwvCDsu6438FXlQTHlRno4gFpJzqPjSIBZooD22tKeI4fE/YMRoHVJHmrQ2P5YL1DoZ0qaOKkbeFWeMg==} + '@next/swc-win32-ia32-msvc@14.2.20': + resolution: {integrity: sha512-igQW/JWciTGJwj3G1ipalD2V20Xfx3ywQy17IV0ciOUBbFhNfyU1DILWsTi32c8KmqgIDviUEulW/yPb2FF90w==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] - '@next/swc-win32-x64-msvc@14.2.5': - resolution: {integrity: sha512-tEQ7oinq1/CjSG9uSTerca3v4AZ+dFa+4Yu6ihaG8Ud8ddqLQgFGcnwYls13H5X5CPDPZJdYxyeMui6muOLd4g==} + '@next/swc-win32-x64-msvc@14.2.20': + resolution: {integrity: sha512-AFmqeLW6LtxeFTuoB+MXFeM5fm5052i3MU6xD0WzJDOwku6SkZaxb1bxjBaRC8uNqTRTSPl0yMFtjNowIVI67w==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -935,6 +901,10 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@nolyfill/is-core-module@1.0.39': + resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} + engines: {node: '>=12.4.0'} + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -1034,8 +1004,11 @@ packages: cpu: [x64] os: [win32] - '@rushstack/eslint-patch@1.10.3': - resolution: {integrity: sha512-qC/xYId4NMebE6w/V33Fh9gWxLgURiNYgVNObbJl2LZv0GUUItCcCqC5axQSwRaAgaxl2mELq1rMzlswaQ0Zxg==} + '@rtsao/scc@1.1.0': + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + + '@rushstack/eslint-patch@1.10.4': + resolution: {integrity: sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==} '@swc/core-darwin-arm64@1.10.1': resolution: {integrity: sha512-NyELPp8EsVZtxH/mEqvzSyWpfPJ1lugpTQcSlMduZLj1EASLO4sC8wt8hmL1aizRlsbjCX+r0PyL+l0xQ64/6Q==} @@ -1142,21 +1115,12 @@ packages: '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} - '@types/node@20.14.11': - resolution: {integrity: sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==} - '@types/node@20.17.10': resolution: {integrity: sha512-/jrvh5h6NXhEauFFexRin69nA0uHJ5gwk4iDivp/DeoEua3uwCUto6PC86IpRITBOs4+6i2I56K5x5b6WYGXHA==} - '@types/prop-types@15.7.12': - resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} - '@types/prop-types@15.7.14': resolution: {integrity: sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==} - '@types/react-dom@18.3.0': - resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==} - '@types/react-dom@18.3.5': resolution: {integrity: sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==} peerDependencies: @@ -1168,11 +1132,8 @@ packages: '@types/react@18.3.17': resolution: {integrity: sha512-opAQ5no6LqJNo9TqnxBKsgnkIYHozW9KSTlFVoSUJYh1Fl/sswkEoqIugRSm7tbh6pABtYjGAjW+GOS23j8qbw==} - '@types/react@18.3.3': - resolution: {integrity: sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==} - - '@types/unist@2.0.10': - resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==} + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} '@typescript-eslint/eslint-plugin@7.18.0': resolution: {integrity: sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==} @@ -1185,6 +1146,14 @@ packages: typescript: optional: true + '@typescript-eslint/eslint-plugin@8.18.1': + resolution: {integrity: sha512-Ncvsq5CT3Gvh+uJG0Lwlho6suwDfUXH0HztslDf5I+F2wAFAZMRwYLEorumpKLzmO2suAXZ/td1tBg4NZIi9CQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' + '@typescript-eslint/parser@7.18.0': resolution: {integrity: sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==} engines: {node: ^18.18.0 || >=20.0.0} @@ -1195,23 +1164,20 @@ packages: typescript: optional: true - '@typescript-eslint/parser@7.2.0': - resolution: {integrity: sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg==} - engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/parser@8.18.1': + resolution: {integrity: sha512-rBnTWHCdbYM2lh7hjyXqxk70wvon3p2FyaniZuey5TrcGBpfhVp0OxOa6gxr9Q9YhZFKyfbEnxc24ZnVbbUkCA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.56.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' '@typescript-eslint/scope-manager@7.18.0': resolution: {integrity: sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==} engines: {node: ^18.18.0 || >=20.0.0} - '@typescript-eslint/scope-manager@7.2.0': - resolution: {integrity: sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==} - engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/scope-manager@8.18.1': + resolution: {integrity: sha512-HxfHo2b090M5s2+/9Z3gkBhI6xBH8OJCFjH9MhQ+nnoZqxU3wNxkLT+VWXWSFWc3UF3Z+CfPAyqdCTdoXtDPCQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/type-utils@7.18.0': resolution: {integrity: sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==} @@ -1223,13 +1189,20 @@ packages: typescript: optional: true + '@typescript-eslint/type-utils@8.18.1': + resolution: {integrity: sha512-jAhTdK/Qx2NJPNOTxXpMwlOiSymtR2j283TtPqXkKBdH8OAMmhiUfP0kJjc/qSE51Xrq02Gj9NY7MwK+UxVwHQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' + '@typescript-eslint/types@7.18.0': resolution: {integrity: sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==} engines: {node: ^18.18.0 || >=20.0.0} - '@typescript-eslint/types@7.2.0': - resolution: {integrity: sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==} - engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/types@8.18.1': + resolution: {integrity: sha512-7uoAUsCj66qdNQNpH2G8MyTFlgerum8ubf21s3TSM3XmKXuIn+H2Sifh/ES2nPOPiYSRJWAk0fDkW0APBWcpfw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/typescript-estree@7.18.0': resolution: {integrity: sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==} @@ -1240,14 +1213,11 @@ packages: typescript: optional: true - '@typescript-eslint/typescript-estree@7.2.0': - resolution: {integrity: sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==} - engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/typescript-estree@8.18.1': + resolution: {integrity: sha512-z8U21WI5txzl2XYOW7i9hJhxoKKNG1kcU4RzyNvKrdZDmbjkmLBo8bgeiOJmA06kizLI76/CCBAAGlTlEeUfyg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + typescript: '>=4.8.4 <5.8.0' '@typescript-eslint/utils@7.18.0': resolution: {integrity: sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==} @@ -1255,13 +1225,20 @@ packages: peerDependencies: eslint: ^8.56.0 + '@typescript-eslint/utils@8.18.1': + resolution: {integrity: sha512-8vikiIj2ebrC4WRdcAdDcmnu9Q/MXXwg+STf40BVfT8exDqBCUPdypvzcUPxEqRGKg9ALagZ0UWcYCtn+4W2iQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' + '@typescript-eslint/visitor-keys@7.18.0': resolution: {integrity: sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==} engines: {node: ^18.18.0 || >=20.0.0} - '@typescript-eslint/visitor-keys@7.2.0': - resolution: {integrity: sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==} - engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/visitor-keys@8.18.1': + resolution: {integrity: sha512-Vj0WLm5/ZsD013YeUKn+K0y8p1M0jPpxOkKdbD1wB0ns53a5piVY02zjf072TblEweAbcYiFiPoSMF3kp+VhhQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@vitejs/plugin-react-swc@3.7.2': resolution: {integrity: sha512-y0byko2b2tSVVf5Gpng1eEhX1OvPC7x8yns1Fx8jDzlJp4LS6CMkCPfLw47cjyoMrshQDoQw4qcgjsU9VvlCew==} @@ -1279,11 +1256,6 @@ packages: peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - acorn@8.12.1: - resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} - engines: {node: '>=0.4.0'} - hasBin: true - acorn@8.14.0: resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} engines: {node: '>=0.4.0'} @@ -1296,8 +1268,8 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - ansi-regex@6.0.1: - resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + ansi-regex@6.1.0: + resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} engines: {node: '>=12'} ansi-styles@4.3.0: @@ -1321,8 +1293,9 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - aria-query@5.1.3: - resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==} + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} array-buffer-byte-length@1.0.1: resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} @@ -1344,32 +1317,25 @@ packages: resolution: {integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==} engines: {node: '>= 0.4'} - array.prototype.flat@1.3.2: - resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} + array.prototype.flat@1.3.3: + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} engines: {node: '>= 0.4'} - array.prototype.flatmap@1.3.2: - resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} + array.prototype.flatmap@1.3.3: + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} engines: {node: '>= 0.4'} array.prototype.tosorted@1.1.4: resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} engines: {node: '>= 0.4'} - arraybuffer.prototype.slice@1.0.3: - resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} engines: {node: '>= 0.4'} ast-types-flow@0.0.8: resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} - autoprefixer@10.4.19: - resolution: {integrity: sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==} - engines: {node: ^10 || ^12 || >=14} - hasBin: true - peerDependencies: - postcss: ^8.1.0 - autoprefixer@10.4.20: resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==} engines: {node: ^10 || ^12 || >=14} @@ -1381,12 +1347,13 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - axe-core@4.9.1: - resolution: {integrity: sha512-QbUdXJVTpvUTHU7871ppZkdOLBeGUKBQWHkHrvN2V9IQWGMt61zf3B45BtzjxEJzYuj0JBjBZP/hmYS/R9pmAw==} + axe-core@4.10.2: + resolution: {integrity: sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==} engines: {node: '>=4'} - axobject-query@3.1.1: - resolution: {integrity: sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==} + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -1405,11 +1372,6 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.23.2: - resolution: {integrity: sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - browserslist@4.24.3: resolution: {integrity: sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -1429,8 +1391,16 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} - call-bind@1.0.7: - resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + call-bind-apply-helpers@1.0.1: + resolution: {integrity: sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==} + engines: {node: '>= 0.4'} + + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + + call-bound@1.0.3: + resolution: {integrity: sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==} engines: {node: '>= 0.4'} callsites@3.1.0: @@ -1441,9 +1411,6 @@ packages: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} - caniuse-lite@1.0.30001643: - resolution: {integrity: sha512-ERgWGNleEilSrHM6iUz/zJNSQTP8Mr21wDWpdgvRwcTXGAq6jMtOUPP4dqFPTdKqZ2wKTdtB+uucZ3MRpAUSmg==} - caniuse-lite@1.0.30001689: resolution: {integrity: sha512-CmeR2VBycfa+5/jOfnp/NpWPGd06nf1XYiefUvhXFfZE4GkRc9jv+eGPS4nT558WS/8lYCzV8SlANCIPvbWP1g==} @@ -1507,10 +1474,6 @@ packages: copy-to-clipboard@3.3.3: resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} - cross-spawn@7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} - engines: {node: '>= 8'} - cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -1550,15 +1513,6 @@ packages: supports-color: optional: true - debug@4.3.5: - resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - debug@4.4.0: resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} engines: {node: '>=6.0'} @@ -1568,10 +1522,6 @@ packages: supports-color: optional: true - deep-equal@2.2.3: - resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==} - engines: {node: '>= 0.4'} - deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -1601,12 +1551,13 @@ packages: resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==} engines: {node: '>=12'} + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - electron-to-chromium@1.4.832: - resolution: {integrity: sha512-cTen3SB0H2SGU7x467NRe1eVcQgcuS6jckKfWJHia2eo0cHIGOqHoAxevIYZD4eRHcWjkvFzo93bi3vJ9W+1lA==} - electron-to-chromium@1.5.74: resolution: {integrity: sha512-ck3//9RC+6oss/1Bh9tiAVFy5vfSKbRHAFh7Z3/eTRkEqJeWgymloShB17Vg3Z4nmDNp35vAd1BZ6CMW4Wt6Iw==} @@ -1616,27 +1567,24 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - enhanced-resolve@5.17.0: - resolution: {integrity: sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==} + enhanced-resolve@5.17.1: + resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==} engines: {node: '>=10.13.0'} - es-abstract@1.23.3: - resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==} + es-abstract@1.23.6: + resolution: {integrity: sha512-Ifco6n3yj2tMZDWNLyloZrytt9lqqlwvS83P3HtaETR0NUOYnIULGGHpktqYGObGy+8wc1okO25p8TjemhImvA==} engines: {node: '>= 0.4'} - es-define-property@1.0.0: - resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} es-errors@1.3.0: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-get-iterator@1.1.3: - resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} - - es-iterator-helpers@1.0.19: - resolution: {integrity: sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==} + es-iterator-helpers@1.2.0: + resolution: {integrity: sha512-tpxqxncxnpw3c93u8n3VOzACmRFoVmWJqbWXvX/JfKbkhBw1oslgPrUfeSt2psuqyEJFD6N/9lg5i7bsKpoq+Q==} engines: {node: '>= 0.4'} es-object-atoms@1.0.0: @@ -1650,8 +1598,8 @@ packages: es-shim-unscopables@1.0.2: resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} - es-to-primitive@1.2.1: - resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + es-to-primitive@1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} esbuild@0.21.5: @@ -1677,8 +1625,8 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - eslint-config-next@14.2.5: - resolution: {integrity: sha512-zogs9zlOiZ7ka+wgUnmcM0KBEDjo4Jis7kxN1jvC0N4wynQ2MIx/KBkg4mVF63J5EK4W0QMCn7xO3vNisjaAoA==} + eslint-config-next@14.2.20: + resolution: {integrity: sha512-gHBvp4RDd51DAaDco7KiWFy731EwcItkDtGUaZH1EUXEnHCzsVRjMceT+b8aThjMLjOScz6Q27MGlePASvK4Aw==} peerDependencies: eslint: ^7.23.0 || ^8.0.0 typescript: '>=3.3.1' @@ -1692,23 +1640,29 @@ packages: peerDependencies: eslint: '>=7.0.0' - eslint-config-turbo@2.0.9: - resolution: {integrity: sha512-FoIMElI8md/dR5DxjB5Om52KJfi7Qf7RInXeE+PGU6lN388rumppwyqEJsZ7vnR5GhGa9cLPt0vNZwEK9iXtKg==} + eslint-config-turbo@2.3.3: + resolution: {integrity: sha512-cM9wSBYowQIrjx2MPCzFE6jTnG4vpTPJKZ/O+Ps3CqrmGK/wtNOsY6WHGMwLtKY/nNbgRahAJH6jGVF6k2coOg==} peerDependencies: eslint: '>6.6.0' eslint-import-resolver-node@0.3.9: resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} - eslint-import-resolver-typescript@3.6.1: - resolution: {integrity: sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==} + eslint-import-resolver-typescript@3.7.0: + resolution: {integrity: sha512-Vrwyi8HHxY97K5ebydMtffsWAn1SCR9eol49eCd5fJS4O1WV7PaAjbcjmbfJJSMz/t4Mal212Uz/fQZrOB8mow==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: eslint: '*' eslint-plugin-import: '*' + eslint-plugin-import-x: '*' + peerDependenciesMeta: + eslint-plugin-import: + optional: true + eslint-plugin-import-x: + optional: true - eslint-module-utils@2.8.1: - resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==} + eslint-module-utils@2.12.0: + resolution: {integrity: sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==} engines: {node: '>=4'} peerDependencies: '@typescript-eslint/parser': '*' @@ -1728,21 +1682,21 @@ packages: eslint-import-resolver-webpack: optional: true - eslint-plugin-import@2.29.1: - resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} + eslint-plugin-import@2.31.0: + resolution: {integrity: sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==} engines: {node: '>=4'} peerDependencies: '@typescript-eslint/parser': '*' - eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 peerDependenciesMeta: '@typescript-eslint/parser': optional: true - eslint-plugin-jsx-a11y@6.9.0: - resolution: {integrity: sha512-nOFOCaJG2pYqORjK19lqPqxMO/JpvdCZdPtNdxY3kvom3jTvkAbOvQvD8wuD0G8BYR0IGAGYDlzqWJOh/ybn2g==} + eslint-plugin-jsx-a11y@6.10.2: + resolution: {integrity: sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==} engines: {node: '>=4.0'} peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 eslint-plugin-react-hooks@4.6.2: resolution: {integrity: sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==} @@ -1750,6 +1704,12 @@ packages: peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + eslint-plugin-react-hooks@5.0.0-canary-7118f5dd7-20230705: + resolution: {integrity: sha512-AZYbMo/NW9chdL7vk6HQzQhT+PvTAEVqWk9ziruUoW2kAOcN5qNyelv70e0F1VNQAbvutOC9oc+xfWycI9FxDw==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + eslint-plugin-react-refresh@0.4.16: resolution: {integrity: sha512-slterMlxAhov/DZO8NScf6mEeMBBXodFUolijDvrtTxyezyLoTQaa73FyYus/VbTdftd8wBgBxPMRk3poleXNQ==} peerDependencies: @@ -1761,15 +1721,11 @@ packages: peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 - eslint-plugin-turbo@2.0.9: - resolution: {integrity: sha512-q4s4mg6JcXzz5zK4LC3c6FcWehGAWjGj7kIM76ZvG0KiR9Ks0znzjnAKW0NoiDP4s/gt3r4YPOpI357qWt167Q==} + eslint-plugin-turbo@2.3.3: + resolution: {integrity: sha512-j8UEA0Z+NNCsjZep9G5u5soDQHcXq/x4amrwulk6eHF1U91H2qAjp5I4jQcvJewmccCJbVp734PkHHTRnosjpg==} peerDependencies: eslint: '>6.6.0' - eslint-scope@8.0.2: - resolution: {integrity: sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint-scope@8.2.0: resolution: {integrity: sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1778,10 +1734,6 @@ packages: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - eslint-visitor-keys@4.0.0: - resolution: {integrity: sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint-visitor-keys@4.2.0: resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1796,15 +1748,6 @@ packages: jiti: optional: true - eslint@9.7.0: - resolution: {integrity: sha512-FzJ9D/0nGiCGBf8UXO/IGLTgLVzIxze1zpfA8Ton2mjLovXdAPlYDv+MQDcqj3TmrhAGYfOpz9RfR+ent0AgAw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - hasBin: true - - espree@10.1.0: - resolution: {integrity: sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - espree@10.3.0: resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1874,10 +1817,6 @@ packages: for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} - foreground-child@3.2.1: - resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} - engines: {node: '>=14'} - foreground-child@3.3.0: resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} engines: {node: '>=14'} @@ -1897,8 +1836,8 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - function.prototype.name@1.1.6: - resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} + function.prototype.name@1.1.7: + resolution: {integrity: sha512-2g4x+HqTJKM9zcJqBSpjoRmdcPFtJM60J3xJisTQSXBWka5XqyBN/2tNUgma1mztTXyDuUsEtYe5qcs7xYzYQA==} engines: {node: '>= 0.4'} functions-have-names@1.2.3: @@ -1912,16 +1851,16 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - get-intrinsic@1.2.4: - resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + get-intrinsic@1.2.6: + resolution: {integrity: sha512-qxsEs+9A+u85HhllWJJFicJfPDhRmjzoYdl64aMWW9yRIJmSyxdn8IEkuIM530/7T+lv0TIHd8L6Q/ra0tEoeA==} engines: {node: '>= 0.4'} - get-symbol-description@1.0.2: - resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} - get-tsconfig@4.7.6: - resolution: {integrity: sha512-ZAqrLlu18NbDdRaHq+AKXzAmqIUPswPWKUchfytdAjiRFnCe5ojG2bstg6mRiZabkKfCoL/e98pbBELIV/YCeA==} + get-tsconfig@4.8.1: + resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} @@ -1956,8 +1895,9 @@ packages: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} - gopd@1.0.1: - resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} @@ -1965,8 +1905,9 @@ packages: graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - has-bigints@1.0.2: - resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} @@ -1975,12 +1916,12 @@ packages: has-property-descriptors@1.0.2: resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - has-proto@1.0.3: - resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} engines: {node: '>= 0.4'} - has-symbols@1.0.3: - resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} has-tostringtag@1.0.2: @@ -2000,9 +1941,8 @@ packages: highlight.js@10.7.3: resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} - ignore@5.3.1: - resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} - engines: {node: '>= 4'} + highlightjs-vue@1.0.0: + resolution: {integrity: sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==} ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} @@ -2016,8 +1956,8 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} - internal-slot@1.0.7: - resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} is-alphabetical@1.0.4: @@ -2026,47 +1966,43 @@ packages: is-alphanumerical@1.0.4: resolution: {integrity: sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==} - is-arguments@1.1.1: - resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} - engines: {node: '>= 0.4'} - - is-array-buffer@3.0.4: - resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} is-async-function@2.0.0: resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} engines: {node: '>= 0.4'} - is-bigint@1.0.4: - resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} - is-boolean-object@1.1.2: - resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + is-boolean-object@1.2.1: + resolution: {integrity: sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==} engines: {node: '>= 0.4'} + is-bun-module@1.3.0: + resolution: {integrity: sha512-DgXeu5UWI0IsMQundYb5UAOzm6G2eVnarJ0byP6Tm55iZNKceD59LNPA2L4VvsScTtHcw0yEkVwSf7PC+QoLSA==} + is-callable@1.2.7: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} - is-core-module@2.15.0: - resolution: {integrity: sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==} - engines: {node: '>= 0.4'} - is-core-module@2.16.0: resolution: {integrity: sha512-urTSINYfAYgcbLb0yDQ6egFm6h3Mo1DcF9EkyXSRjjzdHbsulg01qhwWuXdOoUBuTkbQ80KDboXa0vFJ+BDH+g==} engines: {node: '>= 0.4'} - is-data-view@1.0.1: - resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} engines: {node: '>= 0.4'} - is-date-object@1.0.5: - resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} engines: {node: '>= 0.4'} is-decimal@1.0.4: @@ -2076,8 +2012,9 @@ packages: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} - is-finalizationregistry@1.0.2: - resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==} + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} @@ -2102,51 +2039,48 @@ packages: resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} engines: {node: '>= 0.4'} - is-number-object@1.0.7: - resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} engines: {node: '>= 0.4'} is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - is-path-inside@3.0.3: - resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} - engines: {node: '>=8'} - - is-regex@1.1.4: - resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} is-set@2.0.3: resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} engines: {node: '>= 0.4'} - is-shared-array-buffer@1.0.3: - resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} engines: {node: '>= 0.4'} - is-string@1.0.7: - resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} engines: {node: '>= 0.4'} - is-symbol@1.0.4: - resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} engines: {node: '>= 0.4'} - is-typed-array@1.1.13: - resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} engines: {node: '>= 0.4'} is-weakmap@2.0.2: resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} engines: {node: '>= 0.4'} - is-weakref@1.0.2: - resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + is-weakref@1.1.0: + resolution: {integrity: sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==} + engines: {node: '>= 0.4'} - is-weakset@2.0.3: - resolution: {integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==} + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} engines: {node: '>= 0.4'} isarray@2.0.5: @@ -2155,8 +2089,9 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - iterator.prototype@1.1.2: - resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} + iterator.prototype@1.1.4: + resolution: {integrity: sha512-x4WH0BWmrMmg4oHHl+duwubhrvczGlyuGAZu3nvrf0UXOfPu8IhZObFEr7DE/iv01YgVZrsOiRcqw2srkKEDIA==} + engines: {node: '>= 0.4'} jackspeak@2.3.6: resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} @@ -2262,6 +2197,10 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + math-intrinsics@1.0.0: + resolution: {integrity: sha512-4MqMiKP90ybymYvsut0CH2g4XWbfLtmlCkXmtmdcDCxNB+mQcu1w/1+L/VD7vi/PSv7X2JYV7SCcR+jiPXnQtA==} + engines: {node: '>= 0.4'} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -2273,10 +2212,6 @@ packages: minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - minimatch@9.0.3: - resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} - engines: {node: '>=16 || 14 >=14.17'} - minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} @@ -2288,20 +2223,12 @@ packages: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} - ms@2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - nanoid@3.3.7: - resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - nanoid@3.3.8: resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -2310,8 +2237,8 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - next@14.2.5: - resolution: {integrity: sha512-0f8aRfBVL+mpzfBjYfQuLWh2WyAwtJXCRfkPF4UJ5qd2YwrHczsrSzXU4tRMV0OAxR8ZJZWPFn6uhSC56UTsLA==} + next@14.2.20: + resolution: {integrity: sha512-yPvIiWsiyVYqJlSQxwmzMIReXn5HxFNq4+tlVQ812N1FbvhmE+fDpIAD7bcS2mGYQwPJ5vAsQouyme2eKsxaug==} engines: {node: '>=18.17.0'} hasBin: true peerDependencies: @@ -2328,9 +2255,6 @@ packages: sass: optional: true - node-releases@2.0.18: - resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} - node-releases@2.0.19: resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} @@ -2350,20 +2274,16 @@ packages: resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} engines: {node: '>= 6'} - object-inspect@1.13.2: - resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} - engines: {node: '>= 0.4'} - - object-is@1.1.6: - resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} + object-inspect@1.13.3: + resolution: {integrity: sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==} engines: {node: '>= 0.4'} object-keys@1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} - object.assign@4.1.5: - resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} + object.assign@4.1.6: + resolution: {integrity: sha512-yVh6qdqwYsX9rUYzK6VGV4QT16okE8zNvcX0uRPg9MmBNcvPdTJi7cErXnUw/IeQ7Cg4tYMM6BTL3JEcEA0YTQ==} engines: {node: '>= 0.4'} object.entries@1.1.8: @@ -2423,9 +2343,6 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} - picocolors@1.0.1: - resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} - picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -2508,10 +2425,6 @@ packages: resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} engines: {node: ^10 || ^12 || >=14} - postcss@8.4.39: - resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} - engines: {node: ^10 || ^12 || >=14} - postcss@8.4.49: resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==} engines: {node: ^10 || ^12 || >=14} @@ -2520,8 +2433,8 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - prettier@3.3.3: - resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==} + prettier@3.4.2: + resolution: {integrity: sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==} engines: {node: '>=14'} hasBin: true @@ -2558,8 +2471,8 @@ packages: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} - react-syntax-highlighter@15.5.0: - resolution: {integrity: sha512-+zq2myprEnQmH5yw6Gqc8lD55QHnpKaU8TOcFeC/Lg/MQSs8UknEA0JC4nTZGFAXC2J2Hyj/ijJ7NlabyPi2gg==} + react-syntax-highlighter@15.6.1: + resolution: {integrity: sha512-OqJ2/vL7lEeV5zTJyG7kmARppUjiB9h9udl4qHQjjgEos66z00Ia0OckwYfRxCSFrW8RJIBnsBwQsHZbVPspqg==} peerDependencies: react: '>= 0.14.0' @@ -2578,8 +2491,8 @@ packages: resolution: {integrity: sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==} engines: {node: '>= 14.16.0'} - reflect.getprototypeof@1.0.6: - resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==} + reflect.getprototypeof@1.0.9: + resolution: {integrity: sha512-r0Ay04Snci87djAsI4U+WNRcSw5S4pOH7qFjd/veA5gC7TbqESR3tcj28ia95L/fYUDw11JKP7uqUKUAfVvV5Q==} engines: {node: '>= 0.4'} refractor@3.6.0: @@ -2588,8 +2501,8 @@ packages: regenerator-runtime@0.14.1: resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} - regexp.prototype.flags@1.5.2: - resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} + regexp.prototype.flags@1.5.3: + resolution: {integrity: sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==} engines: {node: '>= 0.4'} require-directory@2.1.1: @@ -2607,10 +2520,6 @@ packages: resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - resolve@1.22.8: - resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} - hasBin: true - resolve@1.22.9: resolution: {integrity: sha512-QxrmX1DzraFIi9PxdG5VkRfRwIgjwyud+z/iBwfRRrVmHc+P9Q7u2lSSpQ6bjr2gy5lrqIiU9vb6iAeGf2400A==} hasBin: true @@ -2634,12 +2543,12 @@ packages: rxjs@7.8.1: resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} - safe-array-concat@1.1.2: - resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} + safe-array-concat@1.1.3: + resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} engines: {node: '>=0.4'} - safe-regex-test@1.0.3: - resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} engines: {node: '>= 0.4'} scheduler@0.23.2: @@ -2674,8 +2583,20 @@ packages: resolution: {integrity: sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==} engines: {node: '>= 0.4'} - side-channel@1.0.6: - resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} engines: {node: '>= 0.4'} signal-exit@4.1.0: @@ -2686,10 +2607,6 @@ packages: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} - source-map-js@1.2.0: - resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} - engines: {node: '>=0.10.0'} - source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -2704,9 +2621,8 @@ packages: spawn-command@0.0.2: resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==} - stop-iteration-iterator@1.0.0: - resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==} - engines: {node: '>= 0.4'} + stable-hash@0.0.4: + resolution: {integrity: sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g==} streamsearch@1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} @@ -2720,8 +2636,9 @@ packages: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} engines: {node: '>=12'} - string.prototype.includes@2.0.0: - resolution: {integrity: sha512-E34CkBgyeqNDcrbU76cDjL5JLcVrtSdYq0MEh/B10r17pRP4ciHLwTgnuLV8Ay6cgEMLkcBkFCKyFZ43YldYzg==} + string.prototype.includes@2.0.1: + resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==} + engines: {node: '>= 0.4'} string.prototype.matchall@4.0.11: resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==} @@ -2730,12 +2647,13 @@ packages: string.prototype.repeat@1.0.0: resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} - string.prototype.trim@1.2.9: - resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} + string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} engines: {node: '>= 0.4'} - string.prototype.trimend@1.0.8: - resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} + string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + engines: {node: '>= 0.4'} string.prototype.trimstart@1.0.8: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} @@ -2796,9 +2714,6 @@ packages: resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} engines: {node: '>=6'} - text-table@0.2.0: - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - thenify-all@1.6.0: resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} engines: {node: '>=0.8'} @@ -2839,9 +2754,6 @@ packages: tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} - tslib@2.6.3: - resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} - tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -2864,85 +2776,72 @@ packages: typescript: optional: true - turbo-darwin-64@2.0.9: - resolution: {integrity: sha512-owlGsOaExuVGBUfrnJwjkL1BWlvefjSKczEAcpLx4BI7Oh6ttakOi+JyomkPkFlYElRpjbvlR2gP8WIn6M/+xQ==} + turbo-darwin-64@2.3.3: + resolution: {integrity: sha512-bxX82xe6du/3rPmm4aCC5RdEilIN99VUld4HkFQuw+mvFg6darNBuQxyWSHZTtc25XgYjQrjsV05888w1grpaA==} cpu: [x64] os: [darwin] - turbo-darwin-arm64@2.0.9: - resolution: {integrity: sha512-XAXkKkePth5ZPPE/9G9tTnPQx0C8UTkGWmNGYkpmGgRr8NedW+HrPsi9N0HcjzzIH9A4TpNYvtiV+WcwdaEjKA==} + turbo-darwin-arm64@2.3.3: + resolution: {integrity: sha512-DYbQwa3NsAuWkCUYVzfOUBbSUBVQzH5HWUFy2Kgi3fGjIWVZOFk86ss+xsWu//rlEAfYwEmopigsPYSmW4X15A==} cpu: [arm64] os: [darwin] - turbo-linux-64@2.0.9: - resolution: {integrity: sha512-l9wSgEjrCFM1aG16zItBsZ206ZlhSSx1owB8Cgskfv0XyIXRGHRkluihiaxkp+UeU5WoEfz4EN5toc+ICA0q0w==} + turbo-linux-64@2.3.3: + resolution: {integrity: sha512-eHj9OIB0dFaP6BxB88jSuaCLsOQSYWBgmhy2ErCu6D2GG6xW3b6e2UWHl/1Ho9FsTg4uVgo4DB9wGsKa5erjUA==} cpu: [x64] os: [linux] - turbo-linux-arm64@2.0.9: - resolution: {integrity: sha512-gRnjxXRne18B27SwxXMqL3fJu7jw/8kBrOBTBNRSmZZiG1Uu3nbnP7b4lgrA/bCku6C0Wligwqurvtpq6+nFHA==} + turbo-linux-arm64@2.3.3: + resolution: {integrity: sha512-NmDE/NjZoDj1UWBhMtOPmqFLEBKhzGS61KObfrDEbXvU3lekwHeoPvAMfcovzswzch+kN2DrtbNIlz+/rp8OCg==} cpu: [arm64] os: [linux] - turbo-windows-64@2.0.9: - resolution: {integrity: sha512-ZVo0apxUvaRq4Vm1qhsfqKKhtRgReYlBVf9MQvVU1O9AoyydEQvLDO1ryqpXDZWpcHoFxHAQc9msjAMtE5K2lA==} + turbo-windows-64@2.3.3: + resolution: {integrity: sha512-O2+BS4QqjK3dOERscXqv7N2GXNcqHr9hXumkMxDj/oGx9oCatIwnnwx34UmzodloSnJpgSqjl8iRWiY65SmYoQ==} cpu: [x64] os: [win32] - turbo-windows-arm64@2.0.9: - resolution: {integrity: sha512-sGRz7c5Pey6y7y9OKi8ypbWNuIRPF9y8xcMqL56OZifSUSo+X2EOsOleR9MKxQXVaqHPGOUKWsE6y8hxBi9pag==} + turbo-windows-arm64@2.3.3: + resolution: {integrity: sha512-dW4ZK1r6XLPNYLIKjC4o87HxYidtRRcBeo/hZ9Wng2XM/MqqYkAyzJXJGgRMsc0MMEN9z4+ZIfnSNBrA0b08ag==} cpu: [arm64] os: [win32] - turbo@2.0.9: - resolution: {integrity: sha512-QaLaUL1CqblSKKPgLrFW3lZWkWG4pGBQNW+q1ScJB5v1D/nFWtsrD/yZljW/bdawg90ihi4/ftQJ3h6fz1FamA==} + turbo@2.3.3: + resolution: {integrity: sha512-DUHWQAcC8BTiUZDRzAYGvpSpGLiaOQPfYXlCieQbwUvmml/LRGIe3raKdrOPOoiX0DYlzxs2nH6BoWJoZrj8hA==} hasBin: true type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} - typed-array-buffer@1.0.2: - resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} - typed-array-byte-length@1.0.1: - resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} engines: {node: '>= 0.4'} - typed-array-byte-offset@1.0.2: - resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} + typed-array-byte-offset@1.0.3: + resolution: {integrity: sha512-GsvTyUHTriq6o/bHcTd0vM7OQ9JEdlvluu9YISaA7+KzDzPaIzEeDFNkTfhdE3MYcNhNi0vq/LlegYgIs5yPAw==} engines: {node: '>= 0.4'} - typed-array-length@1.0.6: - resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} + typed-array-length@1.0.7: + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} engines: {node: '>= 0.4'} - typescript@5.5.4: - resolution: {integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==} - engines: {node: '>=14.17'} - hasBin: true - typescript@5.7.2: resolution: {integrity: sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==} engines: {node: '>=14.17'} hasBin: true - unbox-primitive@1.0.2: - resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} - - undici-types@5.26.5: - resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} undici-types@6.19.8: resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} - update-browserslist-db@1.1.0: - resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - update-browserslist-db@1.1.1: resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==} hasBin: true @@ -2999,19 +2898,20 @@ packages: whatwg-url@7.1.0: resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} - which-boxed-primitive@1.0.2: - resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} - which-builtin-type@1.1.3: - resolution: {integrity: sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==} + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} engines: {node: '>= 0.4'} which-collection@1.0.2: resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} engines: {node: '>= 0.4'} - which-typed-array@1.1.15: - resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} + which-typed-array@1.1.18: + resolution: {integrity: sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==} engines: {node: '>= 0.4'} which@2.0.2: @@ -3155,10 +3055,6 @@ snapshots: '@babel/core': 7.26.0 '@babel/helper-plugin-utils': 7.25.9 - '@babel/runtime@7.24.8': - dependencies: - regenerator-runtime: 0.14.1 - '@babel/runtime@7.26.0': dependencies: regenerator-runtime: 0.14.1 @@ -3399,28 +3295,13 @@ snapshots: '@esbuild/win32-x64@0.24.0': optional: true - '@eslint-community/eslint-utils@4.4.0(eslint@9.7.0)': - dependencies: - eslint: 9.7.0 - eslint-visitor-keys: 3.4.3 - '@eslint-community/eslint-utils@4.4.1(eslint@9.17.0(jiti@1.21.7))': dependencies: eslint: 9.17.0(jiti@1.21.7) eslint-visitor-keys: 3.4.3 - '@eslint-community/regexpp@4.11.0': {} - '@eslint-community/regexpp@4.12.1': {} - '@eslint/config-array@0.17.1': - dependencies: - '@eslint/object-schema': 2.1.4 - debug: 4.3.5 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - '@eslint/config-array@0.19.1': dependencies: '@eslint/object-schema': 2.1.5 @@ -3433,20 +3314,6 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 - '@eslint/eslintrc@3.1.0': - dependencies: - ajv: 6.12.6 - debug: 4.3.5 - espree: 10.1.0 - globals: 14.0.0 - ignore: 5.3.1 - import-fresh: 3.3.0 - js-yaml: 4.1.0 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - '@eslint/eslintrc@3.2.0': dependencies: ajv: 6.12.6 @@ -3463,10 +3330,6 @@ snapshots: '@eslint/js@9.17.0': {} - '@eslint/js@9.7.0': {} - - '@eslint/object-schema@2.1.4': {} - '@eslint/object-schema@2.1.5': {} '@eslint/plugin-kit@0.2.4': @@ -3484,8 +3347,6 @@ snapshots: '@humanwhocodes/module-importer@1.0.1': {} - '@humanwhocodes/retry@0.3.0': {} - '@humanwhocodes/retry@0.3.1': {} '@humanwhocodes/retry@0.4.1': {} @@ -3516,37 +3377,37 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 - '@next/env@14.2.5': {} + '@next/env@14.2.20': {} - '@next/eslint-plugin-next@14.2.5': + '@next/eslint-plugin-next@14.2.20': dependencies: glob: 10.3.10 - '@next/swc-darwin-arm64@14.2.5': + '@next/swc-darwin-arm64@14.2.20': optional: true - '@next/swc-darwin-x64@14.2.5': + '@next/swc-darwin-x64@14.2.20': optional: true - '@next/swc-linux-arm64-gnu@14.2.5': + '@next/swc-linux-arm64-gnu@14.2.20': optional: true - '@next/swc-linux-arm64-musl@14.2.5': + '@next/swc-linux-arm64-musl@14.2.20': optional: true - '@next/swc-linux-x64-gnu@14.2.5': + '@next/swc-linux-x64-gnu@14.2.20': optional: true - '@next/swc-linux-x64-musl@14.2.5': + '@next/swc-linux-x64-musl@14.2.20': optional: true - '@next/swc-win32-arm64-msvc@14.2.5': + '@next/swc-win32-arm64-msvc@14.2.20': optional: true - '@next/swc-win32-ia32-msvc@14.2.5': + '@next/swc-win32-ia32-msvc@14.2.20': optional: true - '@next/swc-win32-x64-msvc@14.2.5': + '@next/swc-win32-x64-msvc@14.2.20': optional: true '@nodelib/fs.scandir@2.1.5': @@ -3561,6 +3422,8 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.17.1 + '@nolyfill/is-core-module@1.0.39': {} + '@pkgjs/parseargs@0.11.0': optional: true @@ -3621,7 +3484,9 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.28.1': optional: true - '@rushstack/eslint-patch@1.10.3': {} + '@rtsao/scc@1.1.0': {} + + '@rushstack/eslint-patch@1.10.4': {} '@swc/core-darwin-arm64@1.10.1': optional: true @@ -3680,7 +3545,7 @@ snapshots: '@swc/helpers@0.5.5': dependencies: '@swc/counter': 0.1.3 - tslib: 2.6.3 + tslib: 2.8.1 '@swc/types@0.1.17': dependencies: @@ -3711,47 +3576,32 @@ snapshots: '@types/hast@2.3.10': dependencies: - '@types/unist': 2.0.10 + '@types/unist': 2.0.11 '@types/json-schema@7.0.15': {} '@types/json5@0.0.29': {} - '@types/node@20.14.11': - dependencies: - undici-types: 5.26.5 - '@types/node@20.17.10': dependencies: undici-types: 6.19.8 - '@types/prop-types@15.7.12': {} - '@types/prop-types@15.7.14': {} - '@types/react-dom@18.3.0': - dependencies: - '@types/react': 18.3.3 - '@types/react-dom@18.3.5(@types/react@18.3.17)': dependencies: '@types/react': 18.3.17 '@types/react-syntax-highlighter@15.5.13': dependencies: - '@types/react': 18.3.3 + '@types/react': 18.3.17 '@types/react@18.3.17': dependencies: '@types/prop-types': 15.7.14 csstype: 3.1.3 - '@types/react@18.3.3': - dependencies: - '@types/prop-types': 15.7.12 - csstype: 3.1.3 - - '@types/unist@2.0.10': {} + '@types/unist@2.0.11': {} '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2)': dependencies: @@ -3771,6 +3621,23 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/eslint-plugin@8.18.1(@typescript-eslint/parser@8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2)': + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2) + '@typescript-eslint/scope-manager': 8.18.1 + '@typescript-eslint/type-utils': 8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2) + '@typescript-eslint/utils': 8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2) + '@typescript-eslint/visitor-keys': 8.18.1 + eslint: 9.17.0(jiti@1.21.7) + graphemer: 1.4.0 + ignore: 5.3.2 + natural-compare: 1.4.0 + ts-api-utils: 1.4.3(typescript@5.7.2) + typescript: 5.7.2 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/parser@7.18.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2)': dependencies: '@typescript-eslint/scope-manager': 7.18.0 @@ -3784,15 +3651,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@7.2.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2)': + '@typescript-eslint/parser@8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2)': dependencies: - '@typescript-eslint/scope-manager': 7.2.0 - '@typescript-eslint/types': 7.2.0 - '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.7.2) - '@typescript-eslint/visitor-keys': 7.2.0 + '@typescript-eslint/scope-manager': 8.18.1 + '@typescript-eslint/types': 8.18.1 + '@typescript-eslint/typescript-estree': 8.18.1(typescript@5.7.2) + '@typescript-eslint/visitor-keys': 8.18.1 debug: 4.4.0 eslint: 9.17.0(jiti@1.21.7) - optionalDependencies: typescript: 5.7.2 transitivePeerDependencies: - supports-color @@ -3802,10 +3668,10 @@ snapshots: '@typescript-eslint/types': 7.18.0 '@typescript-eslint/visitor-keys': 7.18.0 - '@typescript-eslint/scope-manager@7.2.0': + '@typescript-eslint/scope-manager@8.18.1': dependencies: - '@typescript-eslint/types': 7.2.0 - '@typescript-eslint/visitor-keys': 7.2.0 + '@typescript-eslint/types': 8.18.1 + '@typescript-eslint/visitor-keys': 8.18.1 '@typescript-eslint/type-utils@7.18.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2)': dependencies: @@ -3819,9 +3685,20 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/type-utils@8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2)': + dependencies: + '@typescript-eslint/typescript-estree': 8.18.1(typescript@5.7.2) + '@typescript-eslint/utils': 8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2) + debug: 4.4.0 + eslint: 9.17.0(jiti@1.21.7) + ts-api-utils: 1.4.3(typescript@5.7.2) + typescript: 5.7.2 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/types@7.18.0': {} - '@typescript-eslint/types@7.2.0': {} + '@typescript-eslint/types@8.18.1': {} '@typescript-eslint/typescript-estree@7.18.0(typescript@5.7.2)': dependencies: @@ -3838,17 +3715,16 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@7.2.0(typescript@5.7.2)': + '@typescript-eslint/typescript-estree@8.18.1(typescript@5.7.2)': dependencies: - '@typescript-eslint/types': 7.2.0 - '@typescript-eslint/visitor-keys': 7.2.0 + '@typescript-eslint/types': 8.18.1 + '@typescript-eslint/visitor-keys': 8.18.1 debug: 4.4.0 - globby: 11.1.0 + fast-glob: 3.3.2 is-glob: 4.0.3 - minimatch: 9.0.3 + minimatch: 9.0.5 semver: 7.6.3 ts-api-utils: 1.4.3(typescript@5.7.2) - optionalDependencies: typescript: 5.7.2 transitivePeerDependencies: - supports-color @@ -3864,15 +3740,26 @@ snapshots: - supports-color - typescript + '@typescript-eslint/utils@8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2)': + dependencies: + '@eslint-community/eslint-utils': 4.4.1(eslint@9.17.0(jiti@1.21.7)) + '@typescript-eslint/scope-manager': 8.18.1 + '@typescript-eslint/types': 8.18.1 + '@typescript-eslint/typescript-estree': 8.18.1(typescript@5.7.2) + eslint: 9.17.0(jiti@1.21.7) + typescript: 5.7.2 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/visitor-keys@7.18.0': dependencies: '@typescript-eslint/types': 7.18.0 eslint-visitor-keys: 3.4.3 - '@typescript-eslint/visitor-keys@7.2.0': + '@typescript-eslint/visitor-keys@8.18.1': dependencies: - '@typescript-eslint/types': 7.2.0 - eslint-visitor-keys: 3.4.3 + '@typescript-eslint/types': 8.18.1 + eslint-visitor-keys: 4.2.0 '@vitejs/plugin-react-swc@3.7.2(@swc/helpers@0.5.12)(vite@5.4.11(@types/node@20.17.10))': dependencies: @@ -3892,16 +3779,10 @@ snapshots: transitivePeerDependencies: - supports-color - acorn-jsx@5.3.2(acorn@8.12.1): - dependencies: - acorn: 8.12.1 - acorn-jsx@5.3.2(acorn@8.14.0): dependencies: acorn: 8.14.0 - acorn@8.12.1: {} - acorn@8.14.0: {} ajv@6.12.6: @@ -3913,7 +3794,7 @@ snapshots: ansi-regex@5.0.1: {} - ansi-regex@6.0.1: {} + ansi-regex@6.1.0: {} ansi-styles@4.3.0: dependencies: @@ -3932,89 +3813,76 @@ snapshots: argparse@2.0.1: {} - aria-query@5.1.3: - dependencies: - deep-equal: 2.2.3 + aria-query@5.3.2: {} array-buffer-byte-length@1.0.1: dependencies: - call-bind: 1.0.7 - is-array-buffer: 3.0.4 + call-bind: 1.0.8 + is-array-buffer: 3.0.5 array-includes@3.1.8: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.3 + es-abstract: 1.23.6 es-object-atoms: 1.0.0 - get-intrinsic: 1.2.4 - is-string: 1.0.7 + get-intrinsic: 1.2.6 + is-string: 1.1.1 array-union@2.1.0: {} array.prototype.findlast@1.2.5: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.3 + es-abstract: 1.23.6 es-errors: 1.3.0 es-object-atoms: 1.0.0 es-shim-unscopables: 1.0.2 array.prototype.findlastindex@1.2.5: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.3 + es-abstract: 1.23.6 es-errors: 1.3.0 es-object-atoms: 1.0.0 es-shim-unscopables: 1.0.2 - array.prototype.flat@1.3.2: + array.prototype.flat@1.3.3: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.3 + es-abstract: 1.23.6 es-shim-unscopables: 1.0.2 - array.prototype.flatmap@1.3.2: + array.prototype.flatmap@1.3.3: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.3 + es-abstract: 1.23.6 es-shim-unscopables: 1.0.2 array.prototype.tosorted@1.1.4: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.3 + es-abstract: 1.23.6 es-errors: 1.3.0 es-shim-unscopables: 1.0.2 - arraybuffer.prototype.slice@1.0.3: + arraybuffer.prototype.slice@1.0.4: dependencies: array-buffer-byte-length: 1.0.1 - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.3 + es-abstract: 1.23.6 es-errors: 1.3.0 - get-intrinsic: 1.2.4 - is-array-buffer: 3.0.4 - is-shared-array-buffer: 1.0.3 + get-intrinsic: 1.2.6 + is-array-buffer: 3.0.5 ast-types-flow@0.0.8: {} - autoprefixer@10.4.19(postcss@8.4.39): - dependencies: - browserslist: 4.23.2 - caniuse-lite: 1.0.30001643 - fraction.js: 4.3.7 - normalize-range: 0.1.2 - picocolors: 1.0.1 - postcss: 8.4.39 - postcss-value-parser: 4.2.0 - autoprefixer@10.4.20(postcss@8.4.49): dependencies: browserslist: 4.24.3 @@ -4029,11 +3897,9 @@ snapshots: dependencies: possible-typed-array-names: 1.0.0 - axe-core@4.9.1: {} + axe-core@4.10.2: {} - axobject-query@3.1.1: - dependencies: - deep-equal: 2.2.3 + axobject-query@4.1.0: {} balanced-match@1.0.2: {} @@ -4052,13 +3918,6 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.23.2: - dependencies: - caniuse-lite: 1.0.30001643 - electron-to-chromium: 1.4.832 - node-releases: 2.0.18 - update-browserslist-db: 1.1.0(browserslist@4.23.2) - browserslist@4.24.3: dependencies: caniuse-lite: 1.0.30001689 @@ -4077,20 +3936,27 @@ snapshots: cac@6.7.14: {} - call-bind@1.0.7: + call-bind-apply-helpers@1.0.1: dependencies: - es-define-property: 1.0.0 es-errors: 1.3.0 function-bind: 1.1.2 - get-intrinsic: 1.2.4 + + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.1 + es-define-property: 1.0.1 + get-intrinsic: 1.2.6 set-function-length: 1.2.2 + call-bound@1.0.3: + dependencies: + call-bind-apply-helpers: 1.0.1 + get-intrinsic: 1.2.6 + callsites@3.1.0: {} camelcase-css@2.0.1: {} - caniuse-lite@1.0.30001643: {} - caniuse-lite@1.0.30001689: {} chalk@4.1.2: @@ -4160,12 +4026,6 @@ snapshots: dependencies: toggle-selection: 1.0.6 - cross-spawn@7.0.3: - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -4180,21 +4040,21 @@ snapshots: data-view-buffer@1.0.1: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 es-errors: 1.3.0 - is-data-view: 1.0.1 + is-data-view: 1.0.2 data-view-byte-length@1.0.1: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 es-errors: 1.3.0 - is-data-view: 1.0.1 + is-data-view: 1.0.2 data-view-byte-offset@1.0.0: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 es-errors: 1.3.0 - is-data-view: 1.0.1 + is-data-view: 1.0.2 date-fns@2.30.0: dependencies: @@ -4204,42 +4064,17 @@ snapshots: dependencies: ms: 2.1.3 - debug@4.3.5: - dependencies: - ms: 2.1.2 - debug@4.4.0: dependencies: ms: 2.1.3 - deep-equal@2.2.3: - dependencies: - array-buffer-byte-length: 1.0.1 - call-bind: 1.0.7 - es-get-iterator: 1.1.3 - get-intrinsic: 1.2.4 - is-arguments: 1.1.1 - is-array-buffer: 3.0.4 - is-date-object: 1.0.5 - is-regex: 1.1.4 - is-shared-array-buffer: 1.0.3 - isarray: 2.0.5 - object-is: 1.1.6 - object-keys: 1.1.1 - object.assign: 4.1.5 - regexp.prototype.flags: 1.5.2 - side-channel: 1.0.6 - which-boxed-primitive: 1.0.2 - which-collection: 1.0.2 - which-typed-array: 1.1.15 - deep-is@0.1.4: {} define-data-property@1.1.4: dependencies: - es-define-property: 1.0.0 + es-define-property: 1.0.1 es-errors: 1.3.0 - gopd: 1.0.1 + gopd: 1.2.0 define-properties@1.2.1: dependencies: @@ -4261,9 +4096,13 @@ snapshots: dotenv@16.0.3: {} - eastasianwidth@0.2.0: {} + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 - electron-to-chromium@1.4.832: {} + eastasianwidth@0.2.0: {} electron-to-chromium@1.5.74: {} @@ -4271,94 +4110,83 @@ snapshots: emoji-regex@9.2.2: {} - enhanced-resolve@5.17.0: + enhanced-resolve@5.17.1: dependencies: graceful-fs: 4.2.11 tapable: 2.2.1 - es-abstract@1.23.3: + es-abstract@1.23.6: dependencies: array-buffer-byte-length: 1.0.1 - arraybuffer.prototype.slice: 1.0.3 + arraybuffer.prototype.slice: 1.0.4 available-typed-arrays: 1.0.7 - call-bind: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.3 data-view-buffer: 1.0.1 data-view-byte-length: 1.0.1 data-view-byte-offset: 1.0.0 - es-define-property: 1.0.0 + es-define-property: 1.0.1 es-errors: 1.3.0 es-object-atoms: 1.0.0 es-set-tostringtag: 2.0.3 - es-to-primitive: 1.2.1 - function.prototype.name: 1.1.6 - get-intrinsic: 1.2.4 - get-symbol-description: 1.0.2 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.7 + get-intrinsic: 1.2.6 + get-symbol-description: 1.1.0 globalthis: 1.0.4 - gopd: 1.0.1 + gopd: 1.2.0 has-property-descriptors: 1.0.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 + has-proto: 1.2.0 + has-symbols: 1.1.0 hasown: 2.0.2 - internal-slot: 1.0.7 - is-array-buffer: 3.0.4 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 is-callable: 1.2.7 - is-data-view: 1.0.1 + is-data-view: 1.0.2 is-negative-zero: 2.0.3 - is-regex: 1.1.4 - is-shared-array-buffer: 1.0.3 - is-string: 1.0.7 - is-typed-array: 1.1.13 - is-weakref: 1.0.2 - object-inspect: 1.13.2 + is-regex: 1.2.1 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.0 + math-intrinsics: 1.0.0 + object-inspect: 1.13.3 object-keys: 1.1.1 - object.assign: 4.1.5 - regexp.prototype.flags: 1.5.2 - safe-array-concat: 1.1.2 - safe-regex-test: 1.0.3 - string.prototype.trim: 1.2.9 - string.prototype.trimend: 1.0.8 + object.assign: 4.1.6 + regexp.prototype.flags: 1.5.3 + safe-array-concat: 1.1.3 + safe-regex-test: 1.1.0 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 string.prototype.trimstart: 1.0.8 - typed-array-buffer: 1.0.2 - typed-array-byte-length: 1.0.1 - typed-array-byte-offset: 1.0.2 - typed-array-length: 1.0.6 - unbox-primitive: 1.0.2 - which-typed-array: 1.1.15 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.3 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.18 - es-define-property@1.0.0: - dependencies: - get-intrinsic: 1.2.4 + es-define-property@1.0.1: {} es-errors@1.3.0: {} - es-get-iterator@1.1.3: + es-iterator-helpers@1.2.0: dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 - has-symbols: 1.0.3 - is-arguments: 1.1.1 - is-map: 2.0.3 - is-set: 2.0.3 - is-string: 1.0.7 - isarray: 2.0.5 - stop-iteration-iterator: 1.0.0 - - es-iterator-helpers@1.0.19: - dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.3 + es-abstract: 1.23.6 es-errors: 1.3.0 es-set-tostringtag: 2.0.3 function-bind: 1.1.2 - get-intrinsic: 1.2.4 + get-intrinsic: 1.2.6 globalthis: 1.0.4 + gopd: 1.2.0 has-property-descriptors: 1.0.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 - internal-slot: 1.0.7 - iterator.prototype: 1.1.2 - safe-array-concat: 1.1.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + iterator.prototype: 1.1.4 + safe-array-concat: 1.1.3 es-object-atoms@1.0.0: dependencies: @@ -4366,7 +4194,7 @@ snapshots: es-set-tostringtag@2.0.3: dependencies: - get-intrinsic: 1.2.4 + get-intrinsic: 1.2.6 has-tostringtag: 1.0.2 hasown: 2.0.2 @@ -4374,11 +4202,11 @@ snapshots: dependencies: hasown: 2.0.2 - es-to-primitive@1.2.1: + es-to-primitive@1.3.0: dependencies: is-callable: 1.2.7 - is-date-object: 1.0.5 - is-symbol: 1.0.4 + is-date-object: 1.1.0 + is-symbol: 1.1.1 esbuild@0.21.5: optionalDependencies: @@ -4464,120 +4292,126 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-config-next@14.2.5(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2): + eslint-config-next@14.2.20(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2): dependencies: - '@next/eslint-plugin-next': 14.2.5 - '@rushstack/eslint-patch': 1.10.3 - '@typescript-eslint/parser': 7.2.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2) + '@next/eslint-plugin-next': 14.2.20 + '@rushstack/eslint-patch': 1.10.4 + '@typescript-eslint/eslint-plugin': 8.18.1(@typescript-eslint/parser@8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2) + '@typescript-eslint/parser': 8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2) eslint: 9.17.0(jiti@1.21.7) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.17.0(jiti@1.21.7)) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint-import-resolver-typescript@3.6.1)(eslint@9.17.0(jiti@1.21.7)) - eslint-plugin-jsx-a11y: 6.9.0(eslint@9.17.0(jiti@1.21.7)) + eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@9.17.0(jiti@1.21.7)) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint-import-resolver-typescript@3.7.0)(eslint@9.17.0(jiti@1.21.7)) + eslint-plugin-jsx-a11y: 6.10.2(eslint@9.17.0(jiti@1.21.7)) eslint-plugin-react: 7.35.0(eslint@9.17.0(jiti@1.21.7)) - eslint-plugin-react-hooks: 4.6.2(eslint@9.17.0(jiti@1.21.7)) + eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@9.17.0(jiti@1.21.7)) optionalDependencies: typescript: 5.7.2 transitivePeerDependencies: - eslint-import-resolver-webpack + - eslint-plugin-import-x - supports-color eslint-config-prettier@9.1.0(eslint@9.17.0(jiti@1.21.7)): dependencies: eslint: 9.17.0(jiti@1.21.7) - eslint-config-turbo@2.0.9(eslint@9.17.0(jiti@1.21.7)): + eslint-config-turbo@2.3.3(eslint@9.17.0(jiti@1.21.7)): dependencies: eslint: 9.17.0(jiti@1.21.7) - eslint-plugin-turbo: 2.0.9(eslint@9.17.0(jiti@1.21.7)) + eslint-plugin-turbo: 2.3.3(eslint@9.17.0(jiti@1.21.7)) eslint-import-resolver-node@0.3.9: dependencies: debug: 3.2.7 - is-core-module: 2.15.0 - resolve: 1.22.8 + is-core-module: 2.16.0 + resolve: 1.22.9 transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.17.0(jiti@1.21.7)): + eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0)(eslint@9.17.0(jiti@1.21.7)): dependencies: + '@nolyfill/is-core-module': 1.0.39 debug: 4.4.0 - enhanced-resolve: 5.17.0 + enhanced-resolve: 5.17.1 eslint: 9.17.0(jiti@1.21.7) - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@9.17.0(jiti@1.21.7)) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint-import-resolver-typescript@3.6.1)(eslint@9.17.0(jiti@1.21.7)) fast-glob: 3.3.2 - get-tsconfig: 4.7.6 - is-core-module: 2.15.0 + get-tsconfig: 4.8.1 + is-bun-module: 1.3.0 is-glob: 4.0.3 + stable-hash: 0.0.4 + optionalDependencies: + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint-import-resolver-typescript@3.7.0)(eslint@9.17.0(jiti@1.21.7)) transitivePeerDependencies: - - '@typescript-eslint/parser' - - eslint-import-resolver-node - - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@9.17.0(jiti@1.21.7)): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@9.17.0(jiti@1.21.7)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 7.2.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2) + '@typescript-eslint/parser': 8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2) eslint: 9.17.0(jiti@1.21.7) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.17.0(jiti@1.21.7)) + eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@9.17.0(jiti@1.21.7)) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint-import-resolver-typescript@3.6.1)(eslint@9.17.0(jiti@1.21.7)): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint-import-resolver-typescript@3.7.0)(eslint@9.17.0(jiti@1.21.7)): dependencies: + '@rtsao/scc': 1.1.0 array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 - array.prototype.flat: 1.3.2 - array.prototype.flatmap: 1.3.2 + array.prototype.flat: 1.3.3 + array.prototype.flatmap: 1.3.3 debug: 3.2.7 doctrine: 2.1.0 eslint: 9.17.0(jiti@1.21.7) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@9.17.0(jiti@1.21.7)) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@9.17.0(jiti@1.21.7)) hasown: 2.0.2 - is-core-module: 2.15.0 + is-core-module: 2.16.0 is-glob: 4.0.3 minimatch: 3.1.2 object.fromentries: 2.0.8 object.groupby: 1.0.3 object.values: 1.2.0 semver: 6.3.1 + string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 7.2.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2) + '@typescript-eslint/parser': 8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-jsx-a11y@6.9.0(eslint@9.17.0(jiti@1.21.7)): + eslint-plugin-jsx-a11y@6.10.2(eslint@9.17.0(jiti@1.21.7)): dependencies: - aria-query: 5.1.3 + aria-query: 5.3.2 array-includes: 3.1.8 - array.prototype.flatmap: 1.3.2 + array.prototype.flatmap: 1.3.3 ast-types-flow: 0.0.8 - axe-core: 4.9.1 - axobject-query: 3.1.1 + axe-core: 4.10.2 + axobject-query: 4.1.0 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 - es-iterator-helpers: 1.0.19 eslint: 9.17.0(jiti@1.21.7) hasown: 2.0.2 jsx-ast-utils: 3.3.5 language-tags: 1.0.9 minimatch: 3.1.2 object.fromentries: 2.0.8 - safe-regex-test: 1.0.3 - string.prototype.includes: 2.0.0 + safe-regex-test: 1.1.0 + string.prototype.includes: 2.0.1 eslint-plugin-react-hooks@4.6.2(eslint@9.17.0(jiti@1.21.7)): dependencies: eslint: 9.17.0(jiti@1.21.7) + eslint-plugin-react-hooks@5.0.0-canary-7118f5dd7-20230705(eslint@9.17.0(jiti@1.21.7)): + dependencies: + eslint: 9.17.0(jiti@1.21.7) + eslint-plugin-react-refresh@0.4.16(eslint@9.17.0(jiti@1.21.7)): dependencies: eslint: 9.17.0(jiti@1.21.7) @@ -4586,10 +4420,10 @@ snapshots: dependencies: array-includes: 3.1.8 array.prototype.findlast: 1.2.5 - array.prototype.flatmap: 1.3.2 + array.prototype.flatmap: 1.3.3 array.prototype.tosorted: 1.1.4 doctrine: 2.1.0 - es-iterator-helpers: 1.0.19 + es-iterator-helpers: 1.2.0 eslint: 9.17.0(jiti@1.21.7) estraverse: 5.3.0 hasown: 2.0.2 @@ -4604,16 +4438,11 @@ snapshots: string.prototype.matchall: 4.0.11 string.prototype.repeat: 1.0.0 - eslint-plugin-turbo@2.0.9(eslint@9.17.0(jiti@1.21.7)): + eslint-plugin-turbo@2.3.3(eslint@9.17.0(jiti@1.21.7)): dependencies: dotenv: 16.0.3 eslint: 9.17.0(jiti@1.21.7) - eslint-scope@8.0.2: - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - eslint-scope@8.2.0: dependencies: esrecurse: 4.3.0 @@ -4621,8 +4450,6 @@ snapshots: eslint-visitor-keys@3.4.3: {} - eslint-visitor-keys@4.0.0: {} - eslint-visitor-keys@4.2.0: {} eslint@9.17.0(jiti@1.21.7): @@ -4666,51 +4493,6 @@ snapshots: transitivePeerDependencies: - supports-color - eslint@9.7.0: - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.7.0) - '@eslint-community/regexpp': 4.11.0 - '@eslint/config-array': 0.17.1 - '@eslint/eslintrc': 3.1.0 - '@eslint/js': 9.7.0 - '@humanwhocodes/module-importer': 1.0.1 - '@humanwhocodes/retry': 0.3.0 - '@nodelib/fs.walk': 1.2.8 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.3 - debug: 4.3.5 - escape-string-regexp: 4.0.0 - eslint-scope: 8.0.2 - eslint-visitor-keys: 4.0.0 - espree: 10.1.0 - esquery: 1.6.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 8.0.0 - find-up: 5.0.0 - glob-parent: 6.0.2 - ignore: 5.3.1 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - is-path-inside: 3.0.3 - json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.4 - strip-ansi: 6.0.1 - text-table: 0.2.0 - transitivePeerDependencies: - - supports-color - - espree@10.1.0: - dependencies: - acorn: 8.12.1 - acorn-jsx: 5.3.2(acorn@8.12.1) - eslint-visitor-keys: 4.0.0 - espree@10.3.0: dependencies: acorn: 8.14.0 @@ -4779,11 +4561,6 @@ snapshots: dependencies: is-callable: 1.2.7 - foreground-child@3.2.1: - dependencies: - cross-spawn: 7.0.6 - signal-exit: 4.1.0 - foreground-child@3.3.0: dependencies: cross-spawn: 7.0.6 @@ -4798,12 +4575,13 @@ snapshots: function-bind@1.1.2: {} - function.prototype.name@1.1.6: + function.prototype.name@1.1.7: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.3 functions-have-names: 1.2.3 + hasown: 2.0.2 + is-callable: 1.2.7 functions-have-names@1.2.3: {} @@ -4811,21 +4589,26 @@ snapshots: get-caller-file@2.0.5: {} - get-intrinsic@1.2.4: + get-intrinsic@1.2.6: dependencies: + call-bind-apply-helpers: 1.0.1 + dunder-proto: 1.0.1 + es-define-property: 1.0.1 es-errors: 1.3.0 + es-object-atoms: 1.0.0 function-bind: 1.1.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 + gopd: 1.2.0 + has-symbols: 1.1.0 hasown: 2.0.2 + math-intrinsics: 1.0.0 - get-symbol-description@1.0.2: + get-symbol-description@1.1.0: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.3 es-errors: 1.3.0 - get-intrinsic: 1.2.4 + get-intrinsic: 1.2.6 - get-tsconfig@4.7.6: + get-tsconfig@4.8.1: dependencies: resolve-pkg-maps: 1.0.0 @@ -4839,7 +4622,7 @@ snapshots: glob@10.3.10: dependencies: - foreground-child: 3.2.1 + foreground-child: 3.3.0 jackspeak: 2.3.6 minimatch: 9.0.5 minipass: 7.1.2 @@ -4861,7 +4644,7 @@ snapshots: globalthis@1.0.4: dependencies: define-properties: 1.2.1 - gopd: 1.0.1 + gopd: 1.2.0 globby@11.1.0: dependencies: @@ -4872,29 +4655,29 @@ snapshots: merge2: 1.4.1 slash: 3.0.0 - gopd@1.0.1: - dependencies: - get-intrinsic: 1.2.4 + gopd@1.2.0: {} graceful-fs@4.2.11: {} graphemer@1.4.0: {} - has-bigints@1.0.2: {} + has-bigints@1.1.0: {} has-flag@4.0.0: {} has-property-descriptors@1.0.2: dependencies: - es-define-property: 1.0.0 + es-define-property: 1.0.1 - has-proto@1.0.3: {} + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 - has-symbols@1.0.3: {} + has-symbols@1.1.0: {} has-tostringtag@1.0.2: dependencies: - has-symbols: 1.0.3 + has-symbols: 1.1.0 hasown@2.0.2: dependencies: @@ -4912,7 +4695,7 @@ snapshots: highlight.js@10.7.3: {} - ignore@5.3.1: {} + highlightjs-vue@1.0.0: {} ignore@5.3.2: {} @@ -4923,11 +4706,11 @@ snapshots: imurmurhash@0.1.4: {} - internal-slot@1.0.7: + internal-slot@1.1.0: dependencies: es-errors: 1.3.0 hasown: 2.0.2 - side-channel: 1.0.6 + side-channel: 1.1.0 is-alphabetical@1.0.4: {} @@ -4936,58 +4719,57 @@ snapshots: is-alphabetical: 1.0.4 is-decimal: 1.0.4 - is-arguments@1.1.1: - dependencies: - call-bind: 1.0.7 - has-tostringtag: 1.0.2 - - is-array-buffer@3.0.4: + is-array-buffer@3.0.5: dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 + call-bind: 1.0.8 + call-bound: 1.0.3 + get-intrinsic: 1.2.6 is-async-function@2.0.0: dependencies: has-tostringtag: 1.0.2 - is-bigint@1.0.4: + is-bigint@1.1.0: dependencies: - has-bigints: 1.0.2 + has-bigints: 1.1.0 is-binary-path@2.1.0: dependencies: binary-extensions: 2.3.0 - is-boolean-object@1.1.2: + is-boolean-object@1.2.1: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.3 has-tostringtag: 1.0.2 - is-callable@1.2.7: {} - - is-core-module@2.15.0: + is-bun-module@1.3.0: dependencies: - hasown: 2.0.2 + semver: 7.6.3 + + is-callable@1.2.7: {} is-core-module@2.16.0: dependencies: hasown: 2.0.2 - is-data-view@1.0.1: + is-data-view@1.0.2: dependencies: - is-typed-array: 1.1.13 + call-bound: 1.0.3 + get-intrinsic: 1.2.6 + is-typed-array: 1.1.15 - is-date-object@1.0.5: + is-date-object@1.1.0: dependencies: + call-bound: 1.0.3 has-tostringtag: 1.0.2 is-decimal@1.0.4: {} is-extglob@2.1.1: {} - is-finalizationregistry@1.0.2: + is-finalizationregistry@1.1.1: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.3 is-fullwidth-code-point@3.0.0: {} @@ -5005,58 +4787,63 @@ snapshots: is-negative-zero@2.0.3: {} - is-number-object@1.0.7: + is-number-object@1.1.1: dependencies: + call-bound: 1.0.3 has-tostringtag: 1.0.2 is-number@7.0.0: {} - is-path-inside@3.0.3: {} - - is-regex@1.1.4: + is-regex@1.2.1: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.3 + gopd: 1.2.0 has-tostringtag: 1.0.2 + hasown: 2.0.2 is-set@2.0.3: {} - is-shared-array-buffer@1.0.3: + is-shared-array-buffer@1.0.4: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.3 - is-string@1.0.7: + is-string@1.1.1: dependencies: + call-bound: 1.0.3 has-tostringtag: 1.0.2 - is-symbol@1.0.4: + is-symbol@1.1.1: dependencies: - has-symbols: 1.0.3 + call-bound: 1.0.3 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 - is-typed-array@1.1.13: + is-typed-array@1.1.15: dependencies: - which-typed-array: 1.1.15 + which-typed-array: 1.1.18 is-weakmap@2.0.2: {} - is-weakref@1.0.2: + is-weakref@1.1.0: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.3 - is-weakset@2.0.3: + is-weakset@2.0.4: dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 + call-bound: 1.0.3 + get-intrinsic: 1.2.6 isarray@2.0.5: {} isexe@2.0.0: {} - iterator.prototype@1.1.2: + iterator.prototype@1.1.4: dependencies: - define-properties: 1.2.1 - get-intrinsic: 1.2.4 - has-symbols: 1.0.3 - reflect.getprototypeof: 1.0.6 + define-data-property: 1.1.4 + es-object-atoms: 1.0.0 + get-intrinsic: 1.2.6 + has-symbols: 1.1.0 + reflect.getprototypeof: 1.0.9 set-function-name: 2.0.2 jackspeak@2.3.6: @@ -5098,8 +4885,8 @@ snapshots: jsx-ast-utils@3.3.5: dependencies: array-includes: 3.1.8 - array.prototype.flat: 1.3.2 - object.assign: 4.1.5 + array.prototype.flat: 1.3.3 + object.assign: 4.1.6 object.values: 1.2.0 keyv@4.5.4: @@ -5150,6 +4937,8 @@ snapshots: dependencies: yallist: 3.1.1 + math-intrinsics@1.0.0: {} + merge2@1.4.1: {} micromatch@4.0.8: @@ -5161,10 +4950,6 @@ snapshots: dependencies: brace-expansion: 1.1.11 - minimatch@9.0.3: - dependencies: - brace-expansion: 2.0.1 - minimatch@9.0.5: dependencies: brace-expansion: 2.0.1 @@ -5173,8 +4958,6 @@ snapshots: minipass@7.1.2: {} - ms@2.1.2: {} - ms@2.1.3: {} mz@2.7.0: @@ -5183,39 +4966,35 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 - nanoid@3.3.7: {} - nanoid@3.3.8: {} natural-compare@1.4.0: {} - next@14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + next@14.2.20(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@next/env': 14.2.5 + '@next/env': 14.2.20 '@swc/helpers': 0.5.5 busboy: 1.6.0 - caniuse-lite: 1.0.30001643 + caniuse-lite: 1.0.30001689 graceful-fs: 4.2.11 postcss: 8.4.31 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) styled-jsx: 5.1.1(react@18.3.1) optionalDependencies: - '@next/swc-darwin-arm64': 14.2.5 - '@next/swc-darwin-x64': 14.2.5 - '@next/swc-linux-arm64-gnu': 14.2.5 - '@next/swc-linux-arm64-musl': 14.2.5 - '@next/swc-linux-x64-gnu': 14.2.5 - '@next/swc-linux-x64-musl': 14.2.5 - '@next/swc-win32-arm64-msvc': 14.2.5 - '@next/swc-win32-ia32-msvc': 14.2.5 - '@next/swc-win32-x64-msvc': 14.2.5 + '@next/swc-darwin-arm64': 14.2.20 + '@next/swc-darwin-x64': 14.2.20 + '@next/swc-linux-arm64-gnu': 14.2.20 + '@next/swc-linux-arm64-musl': 14.2.20 + '@next/swc-linux-x64-gnu': 14.2.20 + '@next/swc-linux-x64-musl': 14.2.20 + '@next/swc-win32-arm64-msvc': 14.2.20 + '@next/swc-win32-ia32-msvc': 14.2.20 + '@next/swc-win32-x64-msvc': 14.2.20 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros - node-releases@2.0.18: {} - node-releases@2.0.19: {} normalize-path@3.0.0: {} @@ -5226,44 +5005,40 @@ snapshots: object-hash@3.0.0: {} - object-inspect@1.13.2: {} - - object-is@1.1.6: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 + object-inspect@1.13.3: {} object-keys@1.1.1: {} - object.assign@4.1.5: + object.assign@4.1.6: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.3 define-properties: 1.2.1 - has-symbols: 1.0.3 + has-symbols: 1.1.0 object-keys: 1.1.1 object.entries@1.1.8: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 es-object-atoms: 1.0.0 object.fromentries@2.0.8: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.3 + es-abstract: 1.23.6 es-object-atoms: 1.0.0 object.groupby@1.0.3: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.3 + es-abstract: 1.23.6 object.values@1.2.0: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 es-object-atoms: 1.0.0 @@ -5312,8 +5087,6 @@ snapshots: path-type@4.0.0: {} - picocolors@1.0.1: {} - picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -5371,12 +5144,6 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - postcss@8.4.39: - dependencies: - nanoid: 3.3.7 - picocolors: 1.0.1 - source-map-js: 1.2.0 - postcss@8.4.49: dependencies: nanoid: 3.3.8 @@ -5385,7 +5152,7 @@ snapshots: prelude-ls@1.2.1: {} - prettier@3.3.3: {} + prettier@3.4.2: {} prismjs@1.27.0: {} @@ -5415,10 +5182,11 @@ snapshots: react-refresh@0.14.2: {} - react-syntax-highlighter@15.5.0(react@18.3.1): + react-syntax-highlighter@15.6.1(react@18.3.1): dependencies: - '@babel/runtime': 7.24.8 + '@babel/runtime': 7.26.0 highlight.js: 10.7.3 + highlightjs-vue: 1.0.0 lowlight: 1.20.0 prismjs: 1.29.0 react: 18.3.1 @@ -5438,15 +5206,16 @@ snapshots: readdirp@4.0.2: {} - reflect.getprototypeof@1.0.6: + reflect.getprototypeof@1.0.9: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.3 + dunder-proto: 1.0.1 + es-abstract: 1.23.6 es-errors: 1.3.0 - get-intrinsic: 1.2.4 - globalthis: 1.0.4 - which-builtin-type: 1.1.3 + get-intrinsic: 1.2.6 + gopd: 1.2.0 + which-builtin-type: 1.2.1 refractor@3.6.0: dependencies: @@ -5456,9 +5225,9 @@ snapshots: regenerator-runtime@0.14.1: {} - regexp.prototype.flags@1.5.2: + regexp.prototype.flags@1.5.3: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 es-errors: 1.3.0 set-function-name: 2.0.2 @@ -5471,12 +5240,6 @@ snapshots: resolve-pkg-maps@1.0.0: {} - resolve@1.22.8: - dependencies: - is-core-module: 2.15.0 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - resolve@1.22.9: dependencies: is-core-module: 2.16.0 @@ -5485,7 +5248,7 @@ snapshots: resolve@2.0.0-next.5: dependencies: - is-core-module: 2.15.0 + is-core-module: 2.16.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 @@ -5524,18 +5287,19 @@ snapshots: dependencies: tslib: 2.8.1 - safe-array-concat@1.1.2: + safe-array-concat@1.1.3: dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 - has-symbols: 1.0.3 + call-bind: 1.0.8 + call-bound: 1.0.3 + get-intrinsic: 1.2.6 + has-symbols: 1.1.0 isarray: 2.0.5 - safe-regex-test@1.0.3: + safe-regex-test@1.1.0: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.3 es-errors: 1.3.0 - is-regex: 1.1.4 + is-regex: 1.2.1 scheduler@0.23.2: dependencies: @@ -5550,8 +5314,8 @@ snapshots: define-data-property: 1.1.4 es-errors: 1.3.0 function-bind: 1.1.2 - get-intrinsic: 1.2.4 - gopd: 1.0.1 + get-intrinsic: 1.2.6 + gopd: 1.2.0 has-property-descriptors: 1.0.2 set-function-name@2.0.2: @@ -5569,19 +5333,38 @@ snapshots: shell-quote@1.8.2: {} - side-channel@1.0.6: + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.3 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.3 + es-errors: 1.3.0 + get-intrinsic: 1.2.6 + object-inspect: 1.13.3 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.3 + es-errors: 1.3.0 + get-intrinsic: 1.2.6 + object-inspect: 1.13.3 + side-channel-map: 1.0.1 + + side-channel@1.1.0: dependencies: - call-bind: 1.0.7 es-errors: 1.3.0 - get-intrinsic: 1.2.4 - object-inspect: 1.13.2 + object-inspect: 1.13.3 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 signal-exit@4.1.0: {} slash@3.0.0: {} - source-map-js@1.2.0: {} - source-map-js@1.2.1: {} source-map@0.8.0-beta.0: @@ -5592,9 +5375,7 @@ snapshots: spawn-command@0.0.2: {} - stop-iteration-iterator@1.0.0: - dependencies: - internal-slot: 1.0.7 + stable-hash@0.0.4: {} streamsearch@1.1.0: {} @@ -5610,47 +5391,52 @@ snapshots: emoji-regex: 9.2.2 strip-ansi: 7.1.0 - string.prototype.includes@2.0.0: + string.prototype.includes@2.0.1: dependencies: + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.3 + es-abstract: 1.23.6 string.prototype.matchall@4.0.11: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.3 + es-abstract: 1.23.6 es-errors: 1.3.0 es-object-atoms: 1.0.0 - get-intrinsic: 1.2.4 - gopd: 1.0.1 - has-symbols: 1.0.3 - internal-slot: 1.0.7 - regexp.prototype.flags: 1.5.2 + get-intrinsic: 1.2.6 + gopd: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + regexp.prototype.flags: 1.5.3 set-function-name: 2.0.2 - side-channel: 1.0.6 + side-channel: 1.1.0 string.prototype.repeat@1.0.0: dependencies: define-properties: 1.2.1 - es-abstract: 1.23.3 + es-abstract: 1.23.6 - string.prototype.trim@1.2.9: + string.prototype.trim@1.2.10: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.3 + define-data-property: 1.1.4 define-properties: 1.2.1 - es-abstract: 1.23.3 + es-abstract: 1.23.6 es-object-atoms: 1.0.0 + has-property-descriptors: 1.0.2 - string.prototype.trimend@1.0.8: + string.prototype.trimend@1.0.9: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.3 define-properties: 1.2.1 es-object-atoms: 1.0.0 string.prototype.trimstart@1.0.8: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 es-object-atoms: 1.0.0 @@ -5660,7 +5446,7 @@ snapshots: strip-ansi@7.1.0: dependencies: - ansi-regex: 6.0.1 + ansi-regex: 6.1.0 strip-bom@3.0.0: {} @@ -5720,8 +5506,6 @@ snapshots: tapable@2.2.1: {} - text-table@0.2.0: {} - thenify-all@1.6.0: dependencies: thenify: 3.3.1 @@ -5762,8 +5546,6 @@ snapshots: minimist: 1.2.8 strip-bom: 3.0.0 - tslib@2.6.3: {} - tslib@2.8.1: {} tsup@8.3.5(@swc/core@1.10.1(@swc/helpers@0.5.12))(jiti@1.21.7)(postcss@8.4.49)(typescript@5.7.2)(yaml@2.6.1): @@ -5794,90 +5576,81 @@ snapshots: - tsx - yaml - turbo-darwin-64@2.0.9: + turbo-darwin-64@2.3.3: optional: true - turbo-darwin-arm64@2.0.9: + turbo-darwin-arm64@2.3.3: optional: true - turbo-linux-64@2.0.9: + turbo-linux-64@2.3.3: optional: true - turbo-linux-arm64@2.0.9: + turbo-linux-arm64@2.3.3: optional: true - turbo-windows-64@2.0.9: + turbo-windows-64@2.3.3: optional: true - turbo-windows-arm64@2.0.9: + turbo-windows-arm64@2.3.3: optional: true - turbo@2.0.9: + turbo@2.3.3: optionalDependencies: - turbo-darwin-64: 2.0.9 - turbo-darwin-arm64: 2.0.9 - turbo-linux-64: 2.0.9 - turbo-linux-arm64: 2.0.9 - turbo-windows-64: 2.0.9 - turbo-windows-arm64: 2.0.9 + turbo-darwin-64: 2.3.3 + turbo-darwin-arm64: 2.3.3 + turbo-linux-64: 2.3.3 + turbo-linux-arm64: 2.3.3 + turbo-windows-64: 2.3.3 + turbo-windows-arm64: 2.3.3 type-check@0.4.0: dependencies: prelude-ls: 1.2.1 - typed-array-buffer@1.0.2: + typed-array-buffer@1.0.3: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.3 es-errors: 1.3.0 - is-typed-array: 1.1.13 + is-typed-array: 1.1.15 - typed-array-byte-length@1.0.1: + typed-array-byte-length@1.0.3: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 - is-typed-array: 1.1.13 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 - typed-array-byte-offset@1.0.2: + typed-array-byte-offset@1.0.3: dependencies: available-typed-arrays: 1.0.7 - call-bind: 1.0.7 + call-bind: 1.0.8 for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 - is-typed-array: 1.1.13 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.9 - typed-array-length@1.0.6: + typed-array-length@1.0.7: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 - is-typed-array: 1.1.13 + gopd: 1.2.0 + is-typed-array: 1.1.15 possible-typed-array-names: 1.0.0 - - typescript@5.5.4: {} + reflect.getprototypeof: 1.0.9 typescript@5.7.2: {} - unbox-primitive@1.0.2: + unbox-primitive@1.1.0: dependencies: - call-bind: 1.0.7 - has-bigints: 1.0.2 - has-symbols: 1.0.3 - which-boxed-primitive: 1.0.2 - - undici-types@5.26.5: {} + call-bound: 1.0.3 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 undici-types@6.19.8: {} - update-browserslist-db@1.1.0(browserslist@4.23.2): - dependencies: - browserslist: 4.23.2 - escalade: 3.2.0 - picocolors: 1.0.1 - update-browserslist-db@1.1.1(browserslist@4.24.3): dependencies: browserslist: 4.24.3 @@ -5913,42 +5686,44 @@ snapshots: tr46: 1.0.1 webidl-conversions: 4.0.2 - which-boxed-primitive@1.0.2: + which-boxed-primitive@1.1.1: dependencies: - is-bigint: 1.0.4 - is-boolean-object: 1.1.2 - is-number-object: 1.0.7 - is-string: 1.0.7 - is-symbol: 1.0.4 + is-bigint: 1.1.0 + is-boolean-object: 1.2.1 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 - which-builtin-type@1.1.3: + which-builtin-type@1.2.1: dependencies: - function.prototype.name: 1.1.6 + call-bound: 1.0.3 + function.prototype.name: 1.1.7 has-tostringtag: 1.0.2 is-async-function: 2.0.0 - is-date-object: 1.0.5 - is-finalizationregistry: 1.0.2 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 is-generator-function: 1.0.10 - is-regex: 1.1.4 - is-weakref: 1.0.2 + is-regex: 1.2.1 + is-weakref: 1.1.0 isarray: 2.0.5 - which-boxed-primitive: 1.0.2 + which-boxed-primitive: 1.1.1 which-collection: 1.0.2 - which-typed-array: 1.1.15 + which-typed-array: 1.1.18 which-collection@1.0.2: dependencies: is-map: 2.0.3 is-set: 2.0.3 is-weakmap: 2.0.2 - is-weakset: 2.0.3 + is-weakset: 2.0.4 - which-typed-array@1.1.15: + which-typed-array@1.1.18: dependencies: available-typed-arrays: 1.0.7 - call-bind: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.3 for-each: 0.3.3 - gopd: 1.0.1 + gopd: 1.2.0 has-tostringtag: 1.0.2 which@2.0.2: From ba4b9c311dec9ab3a2366952049c47d5509de476 Mon Sep 17 00:00:00 2001 From: "Mims H. Wright" Date: Sat, 28 Dec 2024 18:45:02 +0100 Subject: [PATCH 017/168] Big fat Christmas refactoring (#148) * WIP * Fix type error * Fixed outstanding ts errors * fixed all ts errors * Added tsconfig to root which enables errors in VS Code for the project * Removed unused files * tsc no longer checks node_modules * removed unused code & description component * added messaging module * refactoring messaging * Reworked messaging on the frontend * Renamed preferences to settings for consistency * split types for conversion --- apps/debug/next-env.d.ts | 2 +- apps/debug/package.json | 1 + apps/debug/pages/index.tsx | 11 +- apps/plugin/package.json | 1 + apps/plugin/plugin-src/code.ts | 28 +- apps/plugin/ui-src/App.tsx | 107 +++-- apps/plugin/ui-src/messaging.ts | 25 ++ package.json | 3 +- packages/backend/package.json | 3 +- .../backend/src/altNodes/altConversion.ts | 43 +- packages/backend/src/altNodes/altMixins2.ts | 29 -- .../src/altNodes/convertGroupToFrame.ts | 69 --- .../src/altNodes/convertNodesOnRectangle.ts | 155 ------- packages/backend/src/code.ts | 105 ++--- packages/backend/src/common/commonPadding.ts | 13 +- packages/backend/src/common/commonPosition.ts | 16 +- packages/backend/src/common/commonRadius.ts | 11 +- packages/backend/src/common/commonStroke.ts | 11 +- .../backend/src/common/convertFontWeight.ts | 18 +- .../backend/src/common/nodeWidthHeight.ts | 10 +- packages/backend/src/common/numToAutoFixed.ts | 4 +- .../src/common/retrieveUI/retrieveColors.ts | 27 +- .../src/common/retrieveUI/retrieveTexts.ts | 83 ---- packages/backend/src/flutter/flutterMain.ts | 3 +- .../backend/src/flutter/flutterTextBuilder.ts | 8 +- packages/backend/src/html/htmlMain.ts | 15 +- packages/backend/src/index.ts | 2 +- packages/backend/src/messaging.ts | 24 ++ .../backend/src/nearest-color/nearestColor.ts | 52 +-- .../src/swiftui/builderImpl/swiftuiBlend.ts | 16 +- .../src/swiftui/builderImpl/swiftuiBorder.ts | 5 +- .../src/swiftui/builderImpl/swiftuiEffects.ts | 6 +- .../src/swiftui/builderImpl/swiftuiPadding.ts | 4 +- .../src/swiftui/builderImpl/swiftuiParser.ts | 22 +- .../src/swiftui/builderImpl/swiftuiSize.ts | 2 +- .../src/swiftui/swiftuiDefaultBuilder.ts | 5 +- packages/backend/src/swiftui/swiftuiMain.ts | 2 +- .../src/tailwind/builderImpl/tailwindColor.ts | 7 +- .../src/tailwind/retrieveUI/retrieveTexts.ts | 19 +- .../src/tailwind/tailwindDefaultBuilder.ts | 5 +- packages/backend/src/tailwind/tailwindMain.ts | 2 +- packages/plugin-ui/package.json | 7 +- packages/plugin-ui/src/PluginUI.tsx | 216 ++-------- packages/plugin-ui/src/description.tsx | 396 ------------------ packages/types/package.json | 26 ++ packages/types/src/index.ts | 1 + packages/types/src/types.ts | 151 +++++++ packages/types/tsconfig.json | 6 + pnpm-lock.yaml | 56 ++- tsconfig.json | 11 + 50 files changed, 578 insertions(+), 1266 deletions(-) create mode 100644 apps/plugin/ui-src/messaging.ts delete mode 100644 packages/backend/src/altNodes/altMixins2.ts delete mode 100644 packages/backend/src/altNodes/convertGroupToFrame.ts delete mode 100644 packages/backend/src/altNodes/convertNodesOnRectangle.ts delete mode 100644 packages/backend/src/common/retrieveUI/retrieveTexts.ts create mode 100644 packages/backend/src/messaging.ts delete mode 100644 packages/plugin-ui/src/description.tsx create mode 100644 packages/types/package.json create mode 100644 packages/types/src/index.ts create mode 100644 packages/types/src/types.ts create mode 100644 packages/types/tsconfig.json create mode 100644 tsconfig.json diff --git a/apps/debug/next-env.d.ts b/apps/debug/next-env.d.ts index 4f11a03d..a4a7b3f5 100644 --- a/apps/debug/next-env.d.ts +++ b/apps/debug/next-env.d.ts @@ -2,4 +2,4 @@ /// // NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. +// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information. diff --git a/apps/debug/package.json b/apps/debug/package.json index df50a569..044d2f36 100644 --- a/apps/debug/package.json +++ b/apps/debug/package.json @@ -25,6 +25,7 @@ "postcss": "^8.4.49", "tailwindcss": "3.4.6", "tsconfig": "workspace:*", + "types": "workspace:*", "typescript": "^5.7.2" } } diff --git a/apps/debug/pages/index.tsx b/apps/debug/pages/index.tsx index 1cab95a0..18264a93 100644 --- a/apps/debug/pages/index.tsx +++ b/apps/debug/pages/index.tsx @@ -1,5 +1,6 @@ -import { FrameworkTypes, PluginUI } from "plugin-ui"; +import { FrameworkTypes } from "types"; import * as React from "react"; +import { PluginUI } from "plugin-ui"; export default function Web() { const [selectedFramework, setSelectedFramework] = @@ -22,8 +23,8 @@ export default function Web() { selectedFramework={selectedFramework} setSelectedFramework={setSelectedFramework} htmlPreview={null} - preferences={undefined} - onPreferenceChange={() => {}} + settings={undefined} + onPreferenceChanged={() => {}} colors={[]} gradients={[]} /> @@ -39,8 +40,8 @@ export default function Web() { setSelectedFramework={setSelectedFramework} emptySelection={false} htmlPreview={null} - preferences={undefined} - onPreferenceChange={() => {}} + settings={undefined} + onPreferenceChanged={() => {}} colors={[]} gradients={[]} /> diff --git a/apps/plugin/package.json b/apps/plugin/package.json index d2be2892..12272dbc 100644 --- a/apps/plugin/package.json +++ b/apps/plugin/package.json @@ -34,6 +34,7 @@ "tailwindcss": "3.4.6", "tsconfig": "workspace:*", "typescript": "^5.7.2", + "types": "workspace:*", "vite": "^5.4.11", "vite-plugin-singlefile": "^2.1.0" } diff --git a/apps/plugin/plugin-src/code.ts b/apps/plugin/plugin-src/code.ts index 2a82adfc..f4be7a39 100644 --- a/apps/plugin/plugin-src/code.ts +++ b/apps/plugin/plugin-src/code.ts @@ -6,12 +6,13 @@ import { swiftuiMain, convertIntoNodes, htmlMain, - PluginSettings, + postSettingsChanged, } from "backend"; import { retrieveGenericSolidUIColors } from "backend/src/common/retrieveUI/retrieveColors"; import { flutterCodeGenTextStyles } from "backend/src/flutter/flutterMain"; import { htmlCodeGenTextStyles } from "backend/src/html/htmlMain"; import { swiftUICodeGenTextStyles } from "backend/src/swiftui/swiftuiMain"; +import { PluginSettings, SettingWillChangeMessage } from "types"; let userPluginSettings: PluginSettings; @@ -58,11 +59,7 @@ const getUserSettings = async () => { const initSettings = async () => { await getUserSettings(); - figma.ui.postMessage({ - type: "pluginSettingChanged", - data: userPluginSettings, - }); - + postSettingsChanged(userPluginSettings); safeRun(userPluginSettings); }; @@ -71,11 +68,9 @@ const safeRun = (settings: PluginSettings) => { run(settings); } catch (e) { if (e && typeof e === "object" && "message" in e) { - console.log("error: ", (e as any).stack); - figma.ui.postMessage({ - type: "error", - data: e.message, - }); + const error = e as Error; + console.log("error: ", error.stack); + figma.ui.postMessage({ type: "error", error: error.message }); } } }; @@ -83,7 +78,7 @@ const safeRun = (settings: PluginSettings) => { const standardMode = async () => { figma.showUI(__html__, { width: 450, height: 550, themeColors: true }); await initSettings(); - + // Listen for selection changes figma.on("selectionchange", () => { safeRun(userPluginSettings); @@ -97,13 +92,10 @@ const standardMode = async () => { figma.ui.onmessage = (msg) => { console.log("[node] figma.ui.onmessage", msg); - if (msg.type === "pluginSettingChanged") { - (userPluginSettings as any)[msg.key] = msg.value; + if (msg.type === "pluginSettingWillChange") { + const { key, value } = msg as SettingWillChangeMessage; + (userPluginSettings as any)[key] = value; figma.clientStorage.setAsync("userPluginSettings", userPluginSettings); - // figma.ui.postMessage({ - // type: "pluginSettingChanged", - // data: userPluginSettings, - // }); safeRun(userPluginSettings); } }; diff --git a/apps/plugin/ui-src/App.tsx b/apps/plugin/ui-src/App.tsx index 2487aeff..15d07255 100644 --- a/apps/plugin/ui-src/App.tsx +++ b/apps/plugin/ui-src/App.tsx @@ -1,32 +1,36 @@ import { useEffect, useState } from "react"; -import { FrameworkTypes, PluginSettings, PluginUI } from "plugin-ui"; +import { PluginUI } from "plugin-ui"; +import { + FrameworkTypes, + PluginSettings, + ConversionMessage, + Message, + HTMLPreview, + LinearGradientConversion, + SolidColorConversion, + ErrorMessage, + SettingsChangedMessage, +} from "types"; +import { postUISettingsChangingMessage } from "./messaging"; interface AppState { code: string; - selectedFramework: FrameworkTypes | null; + selectedFramework: FrameworkTypes; isLoading: boolean; - htmlPreview: { - size: { width: number; height: number }; - content: string; - } | null; - preferences: PluginSettings | null; - colors: { - hex: string; - colorName: string; - exportValue: string; - contrastWhite: number; - contrastBlack: number; - }[]; - gradients: { cssPreview: string; exportedValue: string }[]; + htmlPreview: HTMLPreview; + settings: PluginSettings | null; + colors: SolidColorConversion[]; + gradients: LinearGradientConversion[]; } +const emptyPreview = { size: { width: 0, height: 0 }, content: "" }; export default function App() { const [state, setState] = useState({ code: "", - selectedFramework: null, + selectedFramework: "HTML", isLoading: false, - htmlPreview: null, - preferences: null, + htmlPreview: emptyPreview, + settings: null, colors: [], gradients: [], }); @@ -38,42 +42,47 @@ export default function App() { useEffect(() => { window.onmessage = (event: MessageEvent) => { - const message = event.data.pluginMessage; - console.log("[ui] message received:", message); - switch (message.type) { + const untypedMessage = event.data.pluginMessage as Message; + console.log("[ui] message received:", untypedMessage); + + switch (untypedMessage.type) { case "code": + const conversionMessage = untypedMessage as ConversionMessage; setState((prevState) => ({ ...prevState, - code: message.data, - htmlPreview: message.htmlPreview, - colors: message.colors, - gradients: message.gradients, - preferences: message.preferences, - selectedFramework: message.preferences.framework, + ...conversionMessage, + selectedFramework: conversionMessage.settings.framework, })); break; + case "pluginSettingChanged": + const settingsMessage = untypedMessage as SettingsChangedMessage; setState((prevState) => ({ ...prevState, - preferences: message.data, - selectedFramework: message.data.framework, + settings: settingsMessage.settings, + selectedFramework: settingsMessage.settings.framework, })); break; + case "empty": + // const emptyMessage = untypedMessage as EmptyMessage; setState((prevState) => ({ ...prevState, code: "// No layer is selected.", - htmlPreview: null, + htmlPreview: emptyPreview, colors: [], gradients: [], })); break; + case "error": + const errorMessage = untypedMessage as ErrorMessage; + setState((prevState) => ({ ...prevState, colors: [], gradients: [], - code: `Error :(\n// ${message.data}`, + code: `Error :(\n// ${errorMessage.error}`, })); break; default: @@ -112,17 +121,9 @@ export default function App() { // code: "// Loading...", selectedFramework: updatedFramework, })); - - parent.postMessage( - { - pluginMessage: { - type: "pluginSettingChanged", - key: "framework", - value: updatedFramework, - }, - }, - "*", - ); + postUISettingsChangingMessage("framework", updatedFramework, { + targetOrigin: "*", + }); }; console.log("state.code", state.code.slice(0, 25)); @@ -134,24 +135,12 @@ export default function App() { selectedFramework={state.selectedFramework} setSelectedFramework={handleFrameworkChange} htmlPreview={state.htmlPreview} - preferences={state.preferences} - onPreferenceChange={(key: string, value: boolean | string) => { - parent.postMessage( - { - pluginMessage: { - type: "pluginSettingChanged", - key: key, - value: value, - }, - }, - "*", - ); - }} + settings={state.settings} + onPreferenceChanged={(key: string, value: boolean | string) => + postUISettingsChangingMessage(key, value, { targetOrigin: "*" }) + } colors={state.colors} - gradients={state.gradients.map((gradient) => ({ - ...gradient, - exportValue: gradient.exportedValue, - }))} + gradients={state.gradients} />
); diff --git a/apps/plugin/ui-src/messaging.ts b/apps/plugin/ui-src/messaging.ts new file mode 100644 index 00000000..bc51e23a --- /dev/null +++ b/apps/plugin/ui-src/messaging.ts @@ -0,0 +1,25 @@ +import { Message, SettingWillChangeMessage, UIMessage } from "types"; + +if (!parent || !parent.postMessage) { + throw new Error("parent.postMessage() is not defined"); +} +const postMessage = (message: UIMessage, options?: WindowPostMessageOptions) => + parent.postMessage(message, options); + +export const postUIMessage = ( + message: Message, + options?: WindowPostMessageOptions, +) => postMessage({ pluginMessage: message }, options); + +export const postUISettingsChangingMessage = ( + key: string, + value: T, + options?: WindowPostMessageOptions, +) => { + const message: SettingWillChangeMessage = { + type: "pluginSettingWillChange", + key, + value, + }; + postUIMessage(message, options); +}; diff --git a/package.json b/package.json index 34080c0f..5257cd4a 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "eslint": "^9.17.0", "eslint-config-custom": "workspace:*", "prettier": "^3.4.2", - "turbo": "^2.3.3" + "turbo": "^2.3.3", + "typescript": "^5.7.2" } } diff --git a/packages/backend/package.json b/packages/backend/package.json index e5e37a3a..7b2a6abf 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -15,7 +15,8 @@ "dependencies": { "@figma/plugin-typings": "^1.105.0", "react": "18.3.1", - "react-dom": "18.3.1" + "react-dom": "18.3.1", + "types": "workspace:*" }, "devDependencies": { "@types/react": "^18.3.17", diff --git a/packages/backend/src/altNodes/altConversion.ts b/packages/backend/src/altNodes/altConversion.ts index 2a3210e0..e2f3a903 100644 --- a/packages/backend/src/altNodes/altConversion.ts +++ b/packages/backend/src/altNodes/altConversion.ts @@ -1,8 +1,9 @@ -import { convertNodesOnRectangle } from "./convertNodesOnRectangle"; - -type ParentType = (BaseNode & ChildrenMixin) | null; - -export let globalTextStyleSegments: Record = {}; +type StyledTextSegmentSubset = Omit< + StyledTextSegment, + "listSpacing" | "paragraphIndent" | "paragraphSpacing" | "textStyleOverrides" +>; +export let globalTextStyleSegments: Record = + {}; export const cloneNode = (node: T): T => { // Create the cloned object with the correct prototype @@ -30,9 +31,27 @@ export const cloneNode = (node: T): T => { return cloned; }; +/** + * Identify all nodes that are inside Rectangles and transform those Rectangles into Frames containing those nodes. + */ +export const convertNodesOnRectangle = ( + node: FrameNode | GroupNode | InstanceNode | ComponentNode | ComponentSetNode, +): FrameNode | GroupNode | InstanceNode | ComponentNode | ComponentSetNode => { + if (node.children.length < 2) { + return node; + } + if (!node.id) { + throw new Error( + "Node is missing an id! This error should only happen in tests.", + ); + } + + return node; +}; + export const frameNodeTo = ( node: FrameNode | InstanceNode | ComponentNode | ComponentSetNode, - parent: ParentType, + parent: ParentNode, ): | RectangleNode | FrameNode @@ -57,7 +76,7 @@ export const frameNodeTo = ( // auto convert Frame to Rectangle when Frame has no Children const frameToRectangleNode = ( node: FrameNode | InstanceNode | ComponentNode | ComponentSetNode, - parent: ParentType, + parent: ParentNode, ): RectangleNode => { const clonedNode = cloneNode(node); if (parent) { @@ -80,13 +99,13 @@ export const overrideReadonlyProperty = ( }); }; -const assignParent = (node: SceneNode, parent: ParentType) => { +const assignParent = (node: SceneNode, parent: ParentNode) => { if (parent) { overrideReadonlyProperty(node, "parent", parent); } }; -const standardClone = (node: T, parent: ParentType): T => { +const standardClone = (node: T, parent: ParentNode): T => { const clonedNode = cloneNode(node); if (parent !== null) { assignParent(clonedNode, parent); @@ -94,9 +113,11 @@ const standardClone = (node: T, parent: ParentType): T => { return clonedNode; }; +type ParentNode = (BaseNode & ChildrenMixin) | null; + export const convertIntoNodes = ( sceneNode: ReadonlyArray, - parent: ParentType = null, + parent: ParentNode = null, ): Array => { const mapped: Array = sceneNode.map((node: SceneNode) => { switch (node.type) { @@ -198,7 +219,7 @@ export const convertIntoNodes = ( const iconToRectangle = ( node: FrameNode | InstanceNode | ComponentNode | GroupNode, - parent: ParentType, + parent: ParentNode, ): RectangleNode | null => { // TODO Fix this. if (false && node.children.every((d) => d.type === "VECTOR")) { diff --git a/packages/backend/src/altNodes/altMixins2.ts b/packages/backend/src/altNodes/altMixins2.ts deleted file mode 100644 index 6ec4e5fc..00000000 --- a/packages/backend/src/altNodes/altMixins2.ts +++ /dev/null @@ -1,29 +0,0 @@ -// SCENENODE -export type SceneNode = - | FrameNode - | GroupNode - | RectangleNode - | EllipseNode - | TextNode; - -export class RectangleNode { - readonly type = "RECTANGLE"; -} -export class EllipseNode { - readonly type = "ELLIPSE"; -} -export class FrameNodeMock { - readonly type = "FRAME"; -} -export interface FrameNodeMock extends DefaultFrameMixin {} - -export class GroupNode { - readonly type = "GROUP"; -} -export class TextNode { - readonly type = "TEXT"; -} - -export interface TextNode2 extends TextNode { - testRawr: string; -} diff --git a/packages/backend/src/altNodes/convertGroupToFrame.ts b/packages/backend/src/altNodes/convertGroupToFrame.ts deleted file mode 100644 index 7f158edf..00000000 --- a/packages/backend/src/altNodes/convertGroupToFrame.ts +++ /dev/null @@ -1,69 +0,0 @@ -// import { cloneNode } from "./altConversion"; -// import { FrameNodeMock } from "./altMixins2"; - -// // TODO Bernardo see if this is still necessary. -// export const convertGroupToFrame = (node: GroupNode): FrameNode => { -// const newNode = cloneNode(node); - -// newNode.id = node.id; -// newNode.name = node.name; - -// newNode.x = node.x; -// newNode.y = node.y; -// newNode.width = node.width; -// newNode.height = node.height; -// newNode.rotation = node.rotation; - -// newNode.fills = []; -// newNode.strokes = []; -// newNode.effects = []; -// newNode.cornerRadius = 0; - -// newNode.layoutMode = "NONE"; -// newNode.counterAxisSizingMode = "AUTO"; -// newNode.primaryAxisSizingMode = "AUTO"; -// newNode.primaryAxisAlignItems = "CENTER"; -// newNode.primaryAxisAlignItems = "CENTER"; -// newNode.clipsContent = false; -// newNode.layoutGrids = []; -// newNode.gridStyleId = ""; -// newNode.guides = []; - -// newNode.parent = node.parent; - -// // update the children's x and y position. Modify the 'original' node, then pass them. -// updateChildrenXY(node); -// newNode.children = node.children; - -// newNode.children.forEach((d) => { -// // update the parent of each child -// d.parent = newNode; -// }); - -// // don't need to take care of newNode.parent.children because method is recursive. -// // .children =... calls convertGroupToFrame() which returns the correct node - -// return newNode; -// }; - -// /** -// * Update all children's X and Y value from a Group. -// * Group uses relative values, while Frame use absolute. So child.x - group.x = child.x on Frames. -// * This isn't recursive, because it is going to run from the inner-most to outer-most element. Therefore, it would calculate wrongly otherwise. -// * -// * This must be called with a Groupnode. Param accepts anything because of the recurison. -// * Result of a Group with x,y = (250, 250) and child at (260, 260) must be child at (10, 10) -// */ -// const updateChildrenXY = (node: SceneNode): SceneNode => { -// // the second condition is necessary, so it can convert the root -// if (node.type === "GROUP") { -// node.children.forEach((d) => { -// d.x = d.x - node.x; -// d.y = d.y - node.y; -// updateChildrenXY(d); -// }); -// return node; -// } else { -// return node; -// } -// }; diff --git a/packages/backend/src/altNodes/convertNodesOnRectangle.ts b/packages/backend/src/altNodes/convertNodesOnRectangle.ts deleted file mode 100644 index 538f1183..00000000 --- a/packages/backend/src/altNodes/convertNodesOnRectangle.ts +++ /dev/null @@ -1,155 +0,0 @@ -import { FrameNodeMock } from "./altMixins2"; - -/** - * Identify all nodes that are inside Rectangles and transform those Rectangles into Frames containing those nodes. - */ -export const convertNodesOnRectangle = ( - node: FrameNode | GroupNode | InstanceNode | ComponentNode | ComponentSetNode, -): FrameNode | GroupNode | InstanceNode | ComponentNode | ComponentSetNode => { - if (node.children.length < 2) { - return node; - } - if (!node.id) { - throw new Error( - "Node is missing an id! This error should only happen in tests.", - ); - } - - // TODO Make a return? - // const colliding = retrieveCollidingItems(node.children); - - // const parentsKeys = Object.keys(colliding); - // // start with all children. This is going to be filtered. - // let updatedChildren: Array = [...node.children]; - - // console.log("colliding are", parentsKeys); - - // parentsKeys.forEach((key) => { - // // dangerous cast, but this is always true - // const parentNode = node.children.find((d) => d.id === key) as RectangleNode; - - // // retrieve the position. Key should always be at the left side, so even when other items are removed, the index is kept the same. - // // const indexPosition = updatedChildren.findIndex((d) => d.id === key); - - // // filter the children to remove those that are being modified - // updatedChildren = updatedChildren.filter( - // (d) => !colliding[key].map((dd) => dd.id).includes(d.id) && key !== d.id - // ); - - // console.log("updatedChildren is now ", updatedChildren); - // const frameNode = convertRectangleToFrame(parentNode); - - // // todo when the soon-to-be-parent is larger than its parent, things get weird. Happens, for example, when a large image is used in the background. Should this be handled or is this something user should never do? - // overrideReadonlyProperty(frameNode, "children", [...colliding[key]]); - - // colliding[key].forEach((d) => { - // overrideReadonlyProperty(d, "parent", frameNode); - // d.x = d.x - frameNode.x; - // d.y = d.y - frameNode.y; - // }); - // }); - - // if (updatedChildren.length > 0) { - // overrideReadonlyProperty(node, "children", updatedChildren); - // } - - return node; -}; - -const convertRectangleToFrame = (rect: RectangleNode) => { - // If a Rect with elements inside were identified, extract this Rect - // outer methods are going to use it. - - const frameNode = new FrameNodeMock(); - - Object.assign(frameNode, { - parent: rect.parent, - width: rect.width, - height: rect.height, - }); - frameNode.x = rect.x; - frameNode.y = rect.y; - frameNode.rotation = rect.rotation; - frameNode.layoutMode = "NONE"; - - // opacity should be ignored, else it will affect children - - // when invisible, add the layer but don't fill it; he designer might use invisible layers for alignment. - // visible can be undefined in tests - if (rect.visible) { - frameNode.fills = rect.fills; - frameNode.fillStyleId = rect.fillStyleId; - - frameNode.strokes = rect.strokes; - frameNode.strokeStyleId = rect.strokeStyleId; - - frameNode.effects = rect.effects; - frameNode.effectStyleId = rect.effectStyleId; - } - - // inner Rectangle shall get a FIXED size - frameNode.counterAxisAlignItems = "MIN"; - frameNode.counterAxisSizingMode = "FIXED"; - frameNode.primaryAxisAlignItems = "MIN"; - frameNode.primaryAxisSizingMode = "FIXED"; - - frameNode.strokeAlign = rect.strokeAlign; - frameNode.strokeCap = rect.strokeCap; - frameNode.strokeJoin = rect.strokeJoin; - frameNode.strokeMiterLimit = rect.strokeMiterLimit; - frameNode.strokeWeight = rect.strokeWeight; - - frameNode.cornerRadius = rect.cornerRadius; - frameNode.cornerSmoothing = rect.cornerSmoothing; - frameNode.topLeftRadius = rect.topLeftRadius; - frameNode.topRightRadius = rect.topRightRadius; - frameNode.bottomLeftRadius = rect.bottomLeftRadius; - frameNode.bottomRightRadius = rect.bottomRightRadius; - - Object.assign(frameNode, { id: rect.id }); - frameNode.name = rect.name; - - return frameNode; -}; - -/** - * Iterate over each Rectangle and check if it has any child on top. - * This is O(n^2), but is optimized to only do j=i+1 until length, and avoid repeated entries. - * A Node can only have a single parent. The order is defined by layer order. - */ -const retrieveCollidingItems = ( - children: ReadonlyArray, -): Record> => { - const used: Record = {}; - const groups: Record> = {}; - - for (let i = 0; i < children.length - 1; i++) { - const item1 = children[i]; - - // ignore items that are not Rectangles - if (item1.type !== "RECTANGLE") { - continue; - } - - for (let j = i + 1; j < children.length; j++) { - const item2 = children[j]; - - if ( - !used[item2.id] && - item1.x <= item2.x && - item1.y <= item2.y && - item1.x + item1.width >= item2.x + item2.width && - item1.y + item1.height >= item2.y + item2.height - ) { - if (!groups[item1.id]) { - groups[item1.id] = [item2]; - } else { - groups[item1.id].push(item2); - } - used[item2.id] = true; - } - } - } - - return groups; -}; diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index c247264b..0882734d 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -4,79 +4,62 @@ import { retrieveGenericLinearGradients as retrieveGenericGradients, } from "./common/retrieveUI/retrieveColors"; import { flutterMain } from "./flutter/flutterMain"; -import { htmlMain } from "./html/htmlMain"; +import { generateHTMLPreview, htmlMain } from "./html/htmlMain"; +import { postConversionComplete, postEmptyMessage } from "./messaging"; import { swiftuiMain } from "./swiftui/swiftuiMain"; import { tailwindMain } from "./tailwind/tailwindMain"; +import { PluginSettings } from "types"; -export type FrameworkTypes = "Flutter" | "SwiftUI" | "HTML" | "Tailwind"; +export const run = (settings: PluginSettings) => { + const selection = figma.currentPage.selection; -export type PluginSettings = { - framework: FrameworkTypes; - jsx: boolean; - inlineStyle: boolean; - optimizeLayout: boolean; - showLayerNames: boolean; - responsiveRoot: boolean; - flutterGenerationMode: string; - swiftUIGenerationMode: string; - roundTailwindValues: boolean; - roundTailwindColors: boolean; - customTailwindColors: boolean; -}; + const convertedSelection = convertIntoNodes(selection, null); -export const run = (settings: PluginSettings) => { // ignore when nothing was selected - if (figma.currentPage.selection.length === 0) { - figma.ui.postMessage({ - type: "empty", - }); + // If the selection was empty, the converted selection will also be empty. + if (convertedSelection.length === 0) { + postEmptyMessage(); return; } - const convertedSelection = convertIntoNodes( - figma.currentPage.selection, - null, - ); - let result = ""; + const { framework } = settings; + const code = convertToCode(convertedSelection, settings); + // Only generate HTML code if necessary + const htmlCode = + framework === "HTML" && settings.jsx === false + ? code + : generateHTMLPreview(convertedSelection, settings); + const colors = retrieveGenericSolidUIColors(framework); + const gradients = retrieveGenericGradients(framework); + + const htmlPreview = { + size: { + width: convertedSelection[0].width, + height: convertedSelection[0].height, + }, + content: htmlCode, + }; + + postConversionComplete({ + type: "code", + code, + htmlPreview, + colors, + gradients, + settings, + }); +}; + +export const convertToCode = (nodes: SceneNode[], settings: PluginSettings) => { switch (settings.framework) { - case "HTML": - result = htmlMain(convertedSelection, settings); - break; case "Tailwind": - result = tailwindMain(convertedSelection, settings); - break; + return tailwindMain(nodes, settings); case "Flutter": - result = flutterMain(convertedSelection, settings); - break; + return flutterMain(nodes, settings); case "SwiftUI": - result = swiftuiMain(convertedSelection, settings); - break; + return swiftuiMain(nodes, settings); + case "HTML": + default: + return htmlMain(nodes, settings); } - - figma.ui.postMessage({ - type: "code", - data: result, - settings: settings, - htmlPreview: - convertedSelection.length > 0 - ? { - size: convertedSelection.map((node) => ({ - width: node.width, - height: node.height, - }))[0], - content: htmlMain( - convertedSelection, - { - ...settings, - jsx: false, - }, - true, - ), - } - : null, - colors: retrieveGenericSolidUIColors(settings.framework), - gradients: retrieveGenericGradients(settings.framework), - preferences: settings, - // text: retrieveTailwindText(convertedSelection), - }); }; diff --git a/packages/backend/src/common/commonPadding.ts b/packages/backend/src/common/commonPadding.ts index 3eaccccf..b0ef91f0 100644 --- a/packages/backend/src/common/commonPadding.ts +++ b/packages/backend/src/common/commonPadding.ts @@ -1,15 +1,4 @@ -type PaddingType = - | { all: number } - | { - horizontal: number; - vertical: number; - } - | { - left: number; - right: number; - top: number; - bottom: number; - }; +import { PaddingType } from "types"; export const commonPadding = ( node: InferredAutoLayoutResult, diff --git a/packages/backend/src/common/commonPosition.ts b/packages/backend/src/common/commonPosition.ts index 23ae5268..acdb9007 100644 --- a/packages/backend/src/common/commonPosition.ts +++ b/packages/backend/src/common/commonPosition.ts @@ -1,21 +1,9 @@ +import { LayoutMode } from "types"; import { parentCoordinates } from "./parentCoordinates"; -type position = - | "" - | "Absolute" - | "TopStart" - | "TopCenter" - | "TopEnd" - | "CenterStart" - | "Center" - | "CenterEnd" - | "BottomStart" - | "BottomCenter" - | "BottomEnd"; - export const commonPosition = ( node: SceneNode & DimensionAndPositionMixin, -): position => { +): LayoutMode => { // if node is same size as height, position is not necessary // detect if Frame's width is same as Child when Frame has Padding. diff --git a/packages/backend/src/common/commonRadius.ts b/packages/backend/src/common/commonRadius.ts index 3ac4114c..90ece57a 100644 --- a/packages/backend/src/common/commonRadius.ts +++ b/packages/backend/src/common/commonRadius.ts @@ -1,13 +1,6 @@ -type RadiusType = - | { all: number } - | { - topLeft: number; - topRight: number; - bottomRight: number; - bottomLeft: number; - }; +import { CornerRadius } from "types"; -export const getCommonRadius = (node: SceneNode): RadiusType => { +export const getCommonRadius = (node: SceneNode): CornerRadius => { if ( "cornerRadius" in node && node.cornerRadius !== figma.mixed && diff --git a/packages/backend/src/common/commonStroke.ts b/packages/backend/src/common/commonStroke.ts index a843bcae..d82e1380 100644 --- a/packages/backend/src/common/commonStroke.ts +++ b/packages/backend/src/common/commonStroke.ts @@ -1,16 +1,9 @@ -type BorderSideType = - | { all: number } - | { - left: number; - top: number; - right: number; - bottom: number; - }; +import { BorderSide } from "types"; export const commonStroke = ( node: SceneNode, divideBy: number = 1, -): BorderSideType | null => { +): BorderSide | null => { if (!("strokes" in node) || !node.strokes || node.strokes.length === 0) { return null; } diff --git a/packages/backend/src/common/convertFontWeight.ts b/packages/backend/src/common/convertFontWeight.ts index 77857394..7306161a 100644 --- a/packages/backend/src/common/convertFontWeight.ts +++ b/packages/backend/src/common/convertFontWeight.ts @@ -1,17 +1,7 @@ +import { FontWeightNumber } from "types"; + // Convert generic named weights to numbers, which is the way tailwind understands -export const convertFontWeight = ( - weight: number, -): - | "100" - | "200" - | "300" - | "400" - | "500" - | "600" - | "700" - | "800" - | "900" - | null => { +export const convertFontWeight = (weight: string): FontWeightNumber | null => { // change extra-light to extralight weight = weight.replace(" ", "").replace("-", "").toLowerCase(); switch (weight) { @@ -36,6 +26,6 @@ export const convertFontWeight = ( case "black": return "900"; default: - return null; + return "400"; } }; diff --git a/packages/backend/src/common/nodeWidthHeight.ts b/packages/backend/src/common/nodeWidthHeight.ts index 69e76c84..5f2b583b 100644 --- a/packages/backend/src/common/nodeWidthHeight.ts +++ b/packages/backend/src/common/nodeWidthHeight.ts @@ -1,12 +1,6 @@ -type SizeResult = { - readonly width: "fill" | number | null; - readonly height: "fill" | number | null; -}; +import { Size } from "types"; -export const nodeSize = ( - node: SceneNode, - optimizeLayout: boolean, -): SizeResult => { +export const nodeSize = (node: SceneNode, optimizeLayout: boolean): Size => { const hasLayout = "layoutAlign" in node && node.parent && "layoutMode" in node.parent; diff --git a/packages/backend/src/common/numToAutoFixed.ts b/packages/backend/src/common/numToAutoFixed.ts index 61f704be..adba91db 100644 --- a/packages/backend/src/common/numToAutoFixed.ts +++ b/packages/backend/src/common/numToAutoFixed.ts @@ -36,11 +36,9 @@ export const propertyIfNotDefault = ( return propertyValue; }; -type PropertyValueType = number | string | string[]; - export const generateWidgetCode = ( className: string, - properties: Record, + properties: Record, positionedValues?: string[], ): string => { console.log("properties", properties); diff --git a/packages/backend/src/common/retrieveUI/retrieveColors.ts b/packages/backend/src/common/retrieveUI/retrieveColors.ts index 1f17aad7..7c523055 100644 --- a/packages/backend/src/common/retrieveUI/retrieveColors.ts +++ b/packages/backend/src/common/retrieveUI/retrieveColors.ts @@ -16,24 +16,19 @@ import { htmlGradientFromFills, } from "../../html/builderImpl/htmlColor"; import { calculateContrastRatio } from "./commonUI"; -import { FrameworkTypes } from "../../code"; - -export type ExportSolidColor = { - hex: string; - colorName: string; - exportValue: string; - contrastWhite: number; - contrastBlack: number; - meta?: string; -}; +import { + LinearGradientConversion, + SolidColorConversion, + FrameworkTypes, +} from "types"; export const retrieveGenericSolidUIColors = ( framework: FrameworkTypes, -): Array => { +): Array => { const selectionColors = figma.getSelectionColors(); if (!selectionColors || selectionColors.paints.length === 0) return []; - const colors: Array = []; + const colors: Array = []; selectionColors.paints.forEach((paint) => { const fill = convertSolidColor(paint, framework); if (fill) { @@ -50,7 +45,7 @@ export const retrieveGenericSolidUIColors = ( const convertSolidColor = ( fill: Paint, framework: FrameworkTypes, -): ExportSolidColor | null => { +): SolidColorConversion | null => { const black = { r: 0, g: 0, b: 0 }; const white = { r: 1, g: 1, b: 1 }; @@ -78,13 +73,11 @@ const convertSolidColor = ( return output; }; -type ExportLinearGradient = { cssPreview: string; exportValue: string }; - export const retrieveGenericLinearGradients = ( framework: FrameworkTypes, -): Array => { +): Array => { const selectionColors = figma.getSelectionColors(); - const colorStr: Array = []; + const colorStr: Array = []; selectionColors?.paints.forEach((paint) => { if (paint.type === "GRADIENT_LINEAR") { diff --git a/packages/backend/src/common/retrieveUI/retrieveTexts.ts b/packages/backend/src/common/retrieveUI/retrieveTexts.ts deleted file mode 100644 index d6d2b267..00000000 --- a/packages/backend/src/common/retrieveUI/retrieveTexts.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { retrieveTopFill } from "../retrieveFill"; -import { swiftuiMain } from "../../swiftui/swiftuiMain"; -import { tailwindMain } from "../../tailwind/tailwindMain"; -import { htmlMain } from "../../html/htmlMain"; -import { flutterMain } from "../../flutter/flutterMain"; -import { calculateContrastRatio, deepFlatten } from "./commonUI"; - -type exportFramework = "flutter" | "swiftui" | "html" | "tailwind"; - -export const retrieveGenericUIText = ( - sceneNode: Array, - framework: exportFramework, -): Array => { - // convert to Node and then flatten it. Conversion is necessary because of [tailwindText] - const selectedText = deepFlatten(sceneNode); - - const textStr: Array = []; - - selectedText.forEach((node) => { - if (node.type === "TEXT") { - let code = ""; - if (framework === "flutter") { - code = flutterMain([node]); - } else if (framework === "html") { - code = htmlMain([node]); - } else if (framework === "tailwind") { - code = tailwindMain([node]); - } else if (framework === "swiftui") { - code = swiftuiMain([node]); - } - - let style; - if (framework === "tailwind") { - const [builder] = htmlBuilder(node, false); - style = builder.build(); - } else { - const [builder] = htmlBuilder(node, false, true); - style = builder.build(); - } - - const black = { - r: 0, - g: 0, - b: 0, - }; - - let contrastBlack = 21; - - const fill = retrieveTopFill(node.fills); - - if (fill && fill.type === "SOLID") { - contrastBlack = calculateContrastRatio(fill.color, black); - } - - textStr.push({ - name: node.name, - style, - code, - contrastBlack, - }); - } - }); - - // retrieve only unique texts (attr + name) - // from https://stackoverflow.com/a/18923480/4418073 - const unique: Record = {}; - const distinct: Array = []; - textStr.forEach((x) => { - if (!unique[x.code + x.name]) { - distinct.push(x); - unique[x.code + x.name] = true; - } - }); - - return distinct; -}; - -type namedText = { - name: string; - code: string; - style: string; - contrastBlack: number; -}; diff --git a/packages/backend/src/flutter/flutterMain.ts b/packages/backend/src/flutter/flutterMain.ts index 9bc8d949..9a3ab5f9 100644 --- a/packages/backend/src/flutter/flutterMain.ts +++ b/packages/backend/src/flutter/flutterMain.ts @@ -6,12 +6,13 @@ import { retrieveTopFill } from "../common/retrieveFill"; import { FlutterDefaultBuilder } from "./flutterDefaultBuilder"; import { FlutterTextBuilder } from "./flutterTextBuilder"; import { indentString } from "../common/indentString"; -import { PluginSettings } from "../code"; + import { getCrossAxisAlignment, getMainAxisAlignment, } from "./builderImpl/flutterAutoLayout"; import { commonSortChildrenWhenInferredAutoLayout } from "../common/commonChildrenOrder"; +import { PluginSettings } from "types"; let localSettings: PluginSettings; let previousExecutionCache: string[]; diff --git a/packages/backend/src/flutter/flutterTextBuilder.ts b/packages/backend/src/flutter/flutterTextBuilder.ts index 58eb667d..b67e7eee 100644 --- a/packages/backend/src/flutter/flutterTextBuilder.ts +++ b/packages/backend/src/flutter/flutterTextBuilder.ts @@ -96,9 +96,13 @@ export class FlutterTextBuilder extends FlutterDefaultBuilder { letterSpacing: letterSpacing, }; - if (segment.openTypeFeatures.SUBS === true) { + if ( + (segment.openTypeFeatures as unknown as { SUBS: boolean }).SUBS === true + ) { styleProperties.fontFeatures = `[FontFeature.enable("subs")]`; - } else if (segment.openTypeFeatures.SUPS === true) { + } else if ( + (segment.openTypeFeatures as unknown as { SUPS: boolean }).SUPS === true + ) { styleProperties.fontFeatures = `[FontFeature.enable("sups")]`; } diff --git a/packages/backend/src/html/htmlMain.ts b/packages/backend/src/html/htmlMain.ts index fd5af99c..915f6c86 100644 --- a/packages/backend/src/html/htmlMain.ts +++ b/packages/backend/src/html/htmlMain.ts @@ -2,10 +2,10 @@ import { indentString } from "../common/indentString"; import { retrieveTopFill } from "../common/retrieveFill"; import { HtmlTextBuilder } from "./htmlTextBuilder"; import { HtmlDefaultBuilder } from "./htmlDefaultBuilder"; -import { PluginSettings } from "../code"; import { htmlAutoLayoutProps } from "./builderImpl/htmlAutoLayout"; import { formatWithJSX } from "../common/parseJSX"; import { commonSortChildrenWhenInferredAutoLayout } from "../common/commonChildrenOrder"; +import { PluginSettings } from "types"; let showLayerNames = false; @@ -36,6 +36,19 @@ export const htmlMain = ( return result; }; +export const generateHTMLPreview = ( + nodes: SceneNode[], + settings: PluginSettings, +) => + htmlMain( + nodes, + { + ...settings, + jsx: false, + }, + true, + ); + // todo lint idea: replace BorderRadius.only(topleft: 8, topRight: 8) with BorderRadius.horizontal(8) const htmlWidgetGenerator = ( sceneNode: ReadonlyArray, diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index 93d962ac..1d6b233e 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -3,5 +3,5 @@ export { htmlMain } from "./html/htmlMain"; export { tailwindMain } from "./tailwind/tailwindMain"; export { swiftuiMain } from "./swiftui/swiftuiMain"; export { run } from "./code"; -export type { PluginSettings } from "./code"; export { convertIntoNodes } from "./altNodes/altConversion"; +export * from "./messaging"; diff --git a/packages/backend/src/messaging.ts b/packages/backend/src/messaging.ts new file mode 100644 index 00000000..b542dcb6 --- /dev/null +++ b/packages/backend/src/messaging.ts @@ -0,0 +1,24 @@ +import { + ConversionMessage, + EmptyMessage, + ErrorMessage, + PluginSettings, + SettingsChangedMessage, +} from "types"; + +export const postBackendMessage = figma.ui.postMessage; + +export const postEmptyMessage = () => + postBackendMessage({ type: "empty" } as EmptyMessage); + +export const postConversionComplete = (conversionData: ConversionMessage) => + postBackendMessage(conversionData); + +export const postError = (error: string) => + postBackendMessage({ type: "error", error } as ErrorMessage); + +export const postSettingsChanged = (settings: PluginSettings) => + postBackendMessage({ + type: "pluginSettingsChanged", + settings, + } as SettingsChangedMessage); diff --git a/packages/backend/src/nearest-color/nearestColor.ts b/packages/backend/src/nearest-color/nearestColor.ts index 28d57ae0..e4b4f0e1 100644 --- a/packages/backend/src/nearest-color/nearestColor.ts +++ b/packages/backend/src/nearest-color/nearestColor.ts @@ -1,32 +1,8 @@ // https://github.com/dtao/nearest-color converted to ESM and Typescript // It was sligtly modified to support Typescript better. // It was also slighly simplified because many parts weren't being used. -/** - * Defines an available color. - * - * @typedef {Object} ColorSpec - * @property {string=} name A name for the color, e.g., 'red' - * @property {string} source The hex-based color string, e.g., '#FF0' - * @property {RGB} rgb The {@link RGB} color values - */ -/** - * Describes a matched color. - * - * @typedef {Object} ColorMatch - * @property {string} name The name of the matched color, e.g., 'red' - * @property {string} value The hex-based color string, e.g., '#FF0' - * @property {RGB} rgb The {@link RGB} color values. - */ - -/** - * Provides the RGB breakdown of a color. - * - * @typedef {Object} RGB - * @property {number} r The red component, from 0 to 255 - * @property {number} g The green component, from 0 to 255 - * @property {number} b The blue component, from 0 to 255 - */ +import { ColorSpec, RGB } from "types"; /** * Gets the nearest color, from the given list of {@link ColorSpec} objects @@ -207,32 +183,6 @@ function parseColor(source: RGB | string): RGB { throw Error(`"${source}" is not a valid color`); } -type RGB = { - r: number; - g: number; - b: number; -}; - -// type ColorMatch = { -// name: string; -// value: string; -// rgb: RGB; -// distance: number; -// }; - -type ColorSpec = { - source: string; - rgb: RGB; -}; - -// export function createColorSpec(input: string | RGB, name: string): ColorSpec; - -// // it can actually return a ColorMatch, but let's ignore that for simplicity -// // in this app, it is never going to return ColorMatch because the input is hex instead of red -// export function from( -// availableColors: Array | Object -// ): (attr: string) => string; - /** * Creates a {@link ColorSpec} from either a string or an {@link RGB}. * diff --git a/packages/backend/src/swiftui/builderImpl/swiftuiBlend.ts b/packages/backend/src/swiftui/builderImpl/swiftuiBlend.ts index ce28a66c..2fb1b310 100644 --- a/packages/backend/src/swiftui/builderImpl/swiftuiBlend.ts +++ b/packages/backend/src/swiftui/builderImpl/swiftuiBlend.ts @@ -1,10 +1,12 @@ +import { SwiftUIModifier } from "types"; import { sliceNum } from "../../common/numToAutoFixed"; -import { Modifier } from "./swiftuiParser"; /** * https://developer.apple.com/documentation/swiftui/view/opacity(_:) */ -export const swiftuiOpacity = (node: MinimalBlendMixin): Modifier | null => { +export const swiftuiOpacity = ( + node: MinimalBlendMixin, +): SwiftUIModifier | null => { if (node.opacity !== undefined && node.opacity !== 1) { return ["opacity", sliceNum(node.opacity)]; } @@ -14,7 +16,9 @@ export const swiftuiOpacity = (node: MinimalBlendMixin): Modifier | null => { /** * https://developer.apple.com/documentation/swiftui/view/hidden() */ -export const swiftuiVisibility = (node: SceneNodeMixin): Modifier | null => { +export const swiftuiVisibility = ( + node: SceneNodeMixin, +): SwiftUIModifier | null => { // [when testing] node.visible can be undefined if (node.visible !== undefined && !node.visible) { return ["hidden", ""]; @@ -25,7 +29,7 @@ export const swiftuiVisibility = (node: SceneNodeMixin): Modifier | null => { /** * https://developer.apple.com/documentation/swiftui/modifiedcontent/rotationeffect(_:anchor:) */ -export const swiftuiRotation = (node: LayoutMixin): Modifier | null => { +export const swiftuiRotation = (node: LayoutMixin): SwiftUIModifier | null => { if (node.rotation !== undefined && Math.round(node.rotation) !== 0) { return ["rotationEffect", `.degrees(${sliceNum(node.rotation)})`]; } @@ -35,7 +39,9 @@ export const swiftuiRotation = (node: LayoutMixin): Modifier | null => { /** * https://developer.apple.com/documentation/swiftui/blendmode */ -export const swiftuiBlendMode = (node: MinimalBlendMixin): Modifier | null => { +export const swiftuiBlendMode = ( + node: MinimalBlendMixin, +): SwiftUIModifier | null => { const fromBlendEnum = blendModeEnum(node); if (fromBlendEnum) { return ["blendMode", fromBlendEnum]; diff --git a/packages/backend/src/swiftui/builderImpl/swiftuiBorder.ts b/packages/backend/src/swiftui/builderImpl/swiftuiBorder.ts index 58d46543..fc44deab 100644 --- a/packages/backend/src/swiftui/builderImpl/swiftuiBorder.ts +++ b/packages/backend/src/swiftui/builderImpl/swiftuiBorder.ts @@ -2,7 +2,8 @@ import { commonStroke } from "./../../common/commonStroke"; import { getCommonRadius } from "../../common/commonRadius"; import { sliceNum } from "../../common/numToAutoFixed"; import { swiftUISolidColor } from "./swiftuiColor"; -import { Modifier, SwiftUIElement } from "./swiftuiParser"; +import { SwiftUIElement } from "./swiftuiParser"; +import { SwiftUIModifier } from "types"; const swiftUIStroke = (node: SceneNode): number => { if (!("strokes" in node) || !node.strokes || node.strokes.length === 0) { @@ -47,7 +48,7 @@ export const swiftuiBorder = (node: SceneNode): string[] | null => { .map((stroke) => { const strokeColor = swiftUISolidColor(stroke); - const strokeModifier: Modifier = [ + const strokeModifier: SwiftUIModifier = [ "stroke", `${strokeColor}, lineWidth: ${sliceNum(width)}`, ]; diff --git a/packages/backend/src/swiftui/builderImpl/swiftuiEffects.ts b/packages/backend/src/swiftui/builderImpl/swiftuiEffects.ts index d36bd42a..536587be 100644 --- a/packages/backend/src/swiftui/builderImpl/swiftuiEffects.ts +++ b/packages/backend/src/swiftui/builderImpl/swiftuiEffects.ts @@ -1,7 +1,7 @@ import { sliceNum } from "../../common/numToAutoFixed"; -import { Modifier } from "./swiftuiParser"; +import { SwiftUIModifier } from "types"; -export const swiftuiShadow = (node: SceneNode): Modifier | null => { +export const swiftuiShadow = (node: SceneNode): SwiftUIModifier | null => { if (!("effects" in node) || node.effects.length === 0) { return null; } @@ -44,7 +44,7 @@ export const swiftuiShadow = (node: SceneNode): Modifier | null => { return ["shadow", comp.join(", ")]; }; -export const swiftuiBlur = (node: SceneNode): Modifier | null => { +export const swiftuiBlur = (node: SceneNode): SwiftUIModifier | null => { if (!("effects" in node) || node.effects.length === 0) { return null; } diff --git a/packages/backend/src/swiftui/builderImpl/swiftuiPadding.ts b/packages/backend/src/swiftui/builderImpl/swiftuiPadding.ts index c6e90b64..83bb434f 100644 --- a/packages/backend/src/swiftui/builderImpl/swiftuiPadding.ts +++ b/packages/backend/src/swiftui/builderImpl/swiftuiPadding.ts @@ -1,10 +1,10 @@ import { sliceNum } from "../../common/numToAutoFixed"; import { commonPadding } from "../../common/commonPadding"; -import { Modifier } from "./swiftuiParser"; +import { SwiftUIModifier } from "types"; export const swiftuiPadding = ( node: InferredAutoLayoutResult, -): Modifier | null => { +): SwiftUIModifier | null => { if (!("layoutMode" in node)) { return null; } diff --git a/packages/backend/src/swiftui/builderImpl/swiftuiParser.ts b/packages/backend/src/swiftui/builderImpl/swiftuiParser.ts index fcf064c9..4864d7f2 100644 --- a/packages/backend/src/swiftui/builderImpl/swiftuiParser.ts +++ b/packages/backend/src/swiftui/builderImpl/swiftuiParser.ts @@ -1,34 +1,38 @@ import { indentString } from "../../common/indentString"; - -export type Modifier = [string, string | Modifier | Modifier[]]; +import { SwiftUIModifier } from "types"; export class SwiftUIElement { private readonly element: string; - private readonly modifiers: Modifier[]; + private readonly modifiers: SwiftUIModifier[]; - constructor(element: string = "", modifiers: Modifier[] = []) { + constructor(element: string = "", modifiers: SwiftUIModifier[] = []) { this.element = element; this.modifiers = modifiers; } addModifierMixed( property: string, - value: string | Modifier | Modifier[], + value: string | SwiftUIModifier | SwiftUIModifier[], ): this { this.modifiers.push([property, value]); return this; } - addModifier(modifier: Modifier | [string | null, string | null]): this { + addModifier( + modifier: SwiftUIModifier | [string | null, string | null], + ): this { if (modifier && modifier[0] !== null && modifier[1] !== null) { this.modifiers.push([modifier[0], modifier[1]]); } return this; } - addChildElement(element: string, ...modifiers: Modifier[]): SwiftUIElement { + addChildElement( + element: string, + ...modifiers: SwiftUIModifier[] + ): SwiftUIElement { const childModifiers = modifiers.length === 1 ? modifiers[0] : modifiers; - return this.addModifierMixed(element, childModifiers as Modifier); + return this.addModifierMixed(element, childModifiers as SwiftUIModifier); } private buildModifierLines(indentLevel: number): string { @@ -38,7 +42,7 @@ export class SwiftUIElement { Array.isArray(value) ? `${indent}.${property}(${new SwiftUIElement( property, - value as Modifier[], + value as SwiftUIModifier[], ) .toString() .trim()})` diff --git a/packages/backend/src/swiftui/builderImpl/swiftuiSize.ts b/packages/backend/src/swiftui/builderImpl/swiftuiSize.ts index 22e07d4f..febe164e 100644 --- a/packages/backend/src/swiftui/builderImpl/swiftuiSize.ts +++ b/packages/backend/src/swiftui/builderImpl/swiftuiSize.ts @@ -3,7 +3,7 @@ import { sliceNum } from "../../common/numToAutoFixed"; export const swiftuiSize = ( node: SceneNode, - optimizeLayout: boolean, + optimizeLayout: boolean = false, ): { width: string; height: string } => { const size = nodeSize(node, optimizeLayout); diff --git a/packages/backend/src/swiftui/swiftuiDefaultBuilder.ts b/packages/backend/src/swiftui/swiftuiDefaultBuilder.ts index 8df1323f..eb5eb0ae 100644 --- a/packages/backend/src/swiftui/swiftuiDefaultBuilder.ts +++ b/packages/backend/src/swiftui/swiftuiDefaultBuilder.ts @@ -18,7 +18,8 @@ import { commonIsAbsolutePosition, getCommonPositionValue, } from "../common/commonPosition"; -import { Modifier, SwiftUIElement } from "./builderImpl/swiftuiParser"; +import { SwiftUIElement } from "./builderImpl/swiftuiParser"; +import { SwiftUIModifier } from "types"; export class SwiftuiDefaultBuilder { element: SwiftUIElement; @@ -27,7 +28,7 @@ export class SwiftuiDefaultBuilder { this.element = new SwiftUIElement(kind); } - pushModifier(...args: (Modifier | null)[]): void { + pushModifier(...args: (SwiftUIModifier | null)[]): void { args.forEach((modifier) => { if (modifier) { this.element.addModifier(modifier); diff --git a/packages/backend/src/swiftui/swiftuiMain.ts b/packages/backend/src/swiftui/swiftuiMain.ts index 27e17bb5..17fc23c4 100644 --- a/packages/backend/src/swiftui/swiftuiMain.ts +++ b/packages/backend/src/swiftui/swiftuiMain.ts @@ -2,8 +2,8 @@ import { indentString } from "../common/indentString"; import { stringToClassName, sliceNum } from "../common/numToAutoFixed"; import { SwiftuiTextBuilder } from "./swiftuiTextBuilder"; import { SwiftuiDefaultBuilder } from "./swiftuiDefaultBuilder"; -import { PluginSettings } from "../code"; import { commonSortChildrenWhenInferredAutoLayout } from "../common/commonChildrenOrder"; +import { PluginSettings } from "types"; let localSettings: PluginSettings; let previousExecutionCache: string[]; diff --git a/packages/backend/src/tailwind/builderImpl/tailwindColor.ts b/packages/backend/src/tailwind/builderImpl/tailwindColor.ts index 98fcd3b8..b9b78f36 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindColor.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindColor.ts @@ -5,8 +5,7 @@ import { nearestOpacity, nearestValue, } from "../conversionTables"; - -type Kind = "text" | "bg" | "border" | "solid"; +import { TailwindColorType } from "types"; /** * Get a tailwind color value object @@ -32,7 +31,7 @@ export function tailwindColor(fill: SolidPaint) { */ export const tailwindSolidColor = ( fill: SolidPaint | ColorStop, - kind?: Kind, + kind?: TailwindColorType, ): string => { // example: stone-500 or custom-color-700 const { colorName } = getColorInfo(fill); @@ -55,7 +54,7 @@ export const tailwindSolidColor = ( // retrieve the SOLID color for tailwind export const tailwindColorFromFills = ( fills: ReadonlyArray | PluginAPI["mixed"], - kind: Kind, + kind: TailwindColorType, ): string => { // [when testing] fills can be undefined diff --git a/packages/backend/src/tailwind/retrieveUI/retrieveTexts.ts b/packages/backend/src/tailwind/retrieveUI/retrieveTexts.ts index a1f7a017..95d2fdb1 100644 --- a/packages/backend/src/tailwind/retrieveUI/retrieveTexts.ts +++ b/packages/backend/src/tailwind/retrieveUI/retrieveTexts.ts @@ -3,14 +3,15 @@ import { rgbTo6hex } from "../../common/color"; import { retrieveTopFill } from "../../common/retrieveFill"; import { convertFontWeight } from "../../common/convertFontWeight"; import { nearestColor } from "../conversionTables"; +import { TailwindTextConversion } from "types"; export const retrieveTailwindText = ( sceneNode: Array, -): Array => { +): Array => { // convert to Node and then flatten it. Conversion is necessary because of [tailwindText] const selectedText = deepFlatten(sceneNode); - const textStr: Array = []; + const textStr: Array = []; selectedText.forEach((node) => { if (node.type === "TEXT") { @@ -42,7 +43,7 @@ export const retrieveTailwindText = ( ? node.characters.split("\n").join("
") : node.characters; - const black = { + const black: RGB = { r: 0, g: 0, b: 0, @@ -69,7 +70,7 @@ export const retrieveTailwindText = ( // retrieve only unique texts (attr + name) // from https://stackoverflow.com/a/18923480/4418073 const unique: Record = {}; - const distinct: Array = []; + const distinct: Array = []; textStr.forEach((x) => { if (!unique[x.attr + x.name]) { distinct.push(x); @@ -80,14 +81,6 @@ export const retrieveTailwindText = ( return distinct; }; -type namedText = { - name: string; - attr: string; - full: string; - style: string; - contrastBlack: number; -}; - const style = (node: TextNode): string => { let comp = ""; @@ -103,7 +96,7 @@ const style = (node: TextNode): string => { .replace(" ", "") .toLowerCase(); - const weight = convertFontWeight(Number(value)); + const weight = convertFontWeight(value); if (weight) { comp += `font-weight: ${weight};`; } diff --git a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts index 8248ed3e..954e0f30 100644 --- a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts +++ b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts @@ -25,6 +25,7 @@ import { getClassLabel, formatStyleAttribute, } from "../common/commonFormatAttributes"; +import { TailwindColorType } from "types"; const isNotEmpty = (s: string) => s !== ""; const dropEmptyStrings = (strings: string[]) => strings.filter(isNotEmpty); @@ -150,7 +151,7 @@ export class TailwindDefaultBuilder { */ customColor( paint: ReadonlyArray | PluginAPI["mixed"], - kind: string, + kind: TailwindColorType, ): this { // visible is true or undefinied (tests) if (this.visible) { @@ -217,7 +218,7 @@ export class TailwindDefaultBuilder { const blurValue = pxToBlur(blur.radius); if (blurValue) { this.addAttributes( - blurValue === "blur" ? "blur" : `blur-${blurValue}` + blurValue === "blur" ? "blur" : `blur-${blurValue}`, ); // If blur value is 8, it will be "blur". Otherwise, it will be "blur-sm", "blur-md", etc. or "blur-[Xpx]" } } diff --git a/packages/backend/src/tailwind/tailwindMain.ts b/packages/backend/src/tailwind/tailwindMain.ts index 31711701..82ff2e87 100644 --- a/packages/backend/src/tailwind/tailwindMain.ts +++ b/packages/backend/src/tailwind/tailwindMain.ts @@ -3,9 +3,9 @@ import { indentString } from "../common/indentString"; import { tailwindVector } from "./vector"; import { TailwindTextBuilder } from "./tailwindTextBuilder"; import { TailwindDefaultBuilder } from "./tailwindDefaultBuilder"; -import { PluginSettings } from "../code"; import { tailwindAutoLayoutProps } from "./builderImpl/tailwindAutoLayout"; import { commonSortChildrenWhenInferredAutoLayout } from "../common/commonChildrenOrder"; +import { PluginSettings } from "types"; export let localTailwindSettings: PluginSettings; diff --git a/packages/plugin-ui/package.json b/packages/plugin-ui/package.json index faa9c96f..c3ebc43b 100644 --- a/packages/plugin-ui/package.json +++ b/packages/plugin-ui/package.json @@ -10,18 +10,19 @@ "lint": "eslint \"src/**/*.ts*\"" }, "dependencies": { + "@types/react": "^18.3.17", + "@types/react-dom": "^18.3.5", + "@types/react-syntax-highlighter": "15.5.13", "copy-to-clipboard": "^3.3.3", "react": "^18.3.1", "react-syntax-highlighter": "^15.6.1", "tailwindcss": "3.4.6" }, "devDependencies": { - "@types/react": "^18.3.17", - "@types/react-dom": "^18.3.5", - "@types/react-syntax-highlighter": "15.5.13", "eslint": "^9.17.0", "eslint-config-custom": "workspace:*", "tsconfig": "workspace:*", + "types": "workspace:*", "typescript": "^5.7.2" } } diff --git a/packages/plugin-ui/src/PluginUI.tsx b/packages/plugin-ui/src/PluginUI.tsx index 5e36c35d..a55a2633 100644 --- a/packages/plugin-ui/src/PluginUI.tsx +++ b/packages/plugin-ui/src/PluginUI.tsx @@ -2,43 +2,24 @@ import { useState } from "react"; import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; import { coldarkDark as theme } from "react-syntax-highlighter/dist/esm/styles/prism"; import copy from "copy-to-clipboard"; - -export type FrameworkTypes = "HTML" | "Tailwind" | "Flutter" | "SwiftUI"; - -// This must be kept in sync with the backend. -export type PluginSettings = { - framework: FrameworkTypes; - jsx: boolean; - inlineStyle: boolean; - optimizeLayout: boolean; - showLayerNames: boolean; - responsiveRoot: boolean; - flutterGenerationMode: string; - swiftUIGenerationMode: string; - roundTailwindValues: boolean; - roundTailwindColors: boolean; - customTailwindColors: boolean; -}; +import { + FrameworkTypes, + HTMLPreview, + LinearGradientConversion, + PluginSettings, + SolidColorConversion, +} from "types"; type PluginUIProps = { code: string; - htmlPreview: { - size: { width: number; height: number }; - content: string; - } | null; + htmlPreview: HTMLPreview; emptySelection: boolean; selectedFramework: FrameworkTypes; setSelectedFramework: (framework: FrameworkTypes) => void; - preferences: PluginSettings | null; - onPreferenceChange: (key: string, value: boolean | string) => void; - colors: { - hex: string; - colorName: string; - exportValue: string; - contrastWhite: number; - contrastBlack: number; - }[]; - gradients: { cssPreview: string; exportValue: string }[]; + settings: PluginSettings | null; + onPreferenceChanged: (key: string, value: boolean | string) => void; + colors: SolidColorConversion[]; + gradients: LinearGradientConversion[]; }; export const PluginUI = (props: PluginUIProps) => { @@ -72,10 +53,6 @@ export const PluginUI = (props: PluginUIProps) => { >
- {/*
- -
*/} - {props.htmlPreview && ( { setIsResponsiveExpanded={setIsResponsiveExpanded} /> )} - {/* */} - {/*
-
- -
*/} {props.colors.length > 0 && ( @@ -134,40 +104,18 @@ export const ResponsiveGrade = () => { ); }; -type LocalCodegenPreference = - // | { - // itemType: "alternative-unit"; - // defaultScaleFactor: number; - // scaledUnit: string; - // default?: boolean; - // includedLanguages?: FrameworkTypes[]; - // } - // | { - // itemType: "select"; - // propertyName: Exclude; - // label: string; - // options: { label: string; value: string; isDefault?: boolean }[]; - // includedLanguages?: FrameworkTypes[]; - // } - // | { - // itemType: "action"; - // propertyName: string; - // label: string; - // includedLanguages?: FrameworkTypes[]; - // } - // | - { - itemType: "individual_select"; - propertyName: Exclude< - keyof PluginSettings, - "framework" | "flutterGenerationMode" | "swiftUIGenerationMode" - >; - label: string; - description: string; - value?: boolean; - isDefault?: boolean; - includedLanguages?: FrameworkTypes[]; - }; +type LocalCodegenPreference = { + itemType: "individual_select"; + propertyName: Exclude< + keyof PluginSettings, + "framework" | "flutterGenerationMode" | "swiftUIGenerationMode" + >; + label: string; + description: string; + value?: boolean; + isDefault?: boolean; + includedLanguages?: FrameworkTypes[]; +}; export const preferenceOptions: LocalCodegenPreference[] = [ { @@ -178,20 +126,6 @@ export const preferenceOptions: LocalCodegenPreference[] = [ isDefault: false, includedLanguages: ["HTML", "Tailwind"], }, - // { - // itemType: "individual_select", - // propertyName: "inlineStyle", - // label: "Inline Style", - // isDefault: true, - // includedLanguages: ["HTML"], - // }, - // { - // itemType: "individual_select", - // propertyName: "responsiveRoot", - // label: "Responsive Root", - // isDefault: false, - // includedLanguages: ["Tailwind"], - // }, { itemType: "individual_select", propertyName: "optimizeLayout", @@ -264,23 +198,13 @@ const selectPreferenceOptions: { ], includedLanguages: ["SwiftUI"], }, - // { - // itemType: "select", - // propertyName: "htmlGenerationMode", - // label: "Mode", - // options: [ - // { label: "Component", value: "component" }, - // { label: "Snippet", value: "snippet" }, - // ], - // includedLanguages: ["HTML"], - // }, ]; export const CodePanel = (props: { code: string; selectedFramework: FrameworkTypes; - preferences: PluginSettings | null; - onPreferenceChange: (key: string, value: boolean | string) => void; + settings: PluginSettings | null; + onPreferenceChanged: (key: string, value: boolean | string) => void; }) => { const emptySelection = false; const [isPressed, setIsPressed] = useState(false); @@ -304,7 +228,7 @@ export const CodePanel = (props: {
); } else { - const selectablePreferencesFiltered = selectPreferenceOptions.filter( + const selectableSettingsFiltered = selectPreferenceOptions.filter( (preference) => preference.includedLanguages?.includes(props.selectedFramework), ); @@ -331,8 +255,6 @@ export const CodePanel = (props: {
- {/* Settings */} - {preferenceOptions .filter((preference) => preference.includedLanguages?.includes(props.selectedFramework), @@ -343,23 +265,23 @@ export const CodePanel = (props: { title={preference.label} description={preference.description} isSelected={ - props.preferences?.[preference.propertyName] ?? + props.settings?.[preference.propertyName] ?? preference.isDefault } onSelect={(value) => { - props.onPreferenceChange(preference.propertyName, value); + props.onPreferenceChanged(preference.propertyName, value); }} buttonClass="bg-green-100 dark:bg-black dark:ring-green-800 ring-green-500" checkClass="bg-green-400 dark:bg-black dark:bg-green-500 dark:border-green-500 ring-green-300 border-green-400" /> ))}
- {selectablePreferencesFiltered.length > 0 && ( + {selectableSettingsFiltered.length > 0 && ( <>
- {selectablePreferencesFiltered.map((preference) => ( + {selectableSettingsFiltered.map((preference) => ( <> {/* {preference.label} @@ -370,11 +292,11 @@ export const CodePanel = (props: { title={option.label} isSelected={ option.value === - (props.preferences?.[preference.propertyName] ?? + (props.settings?.[preference.propertyName] ?? option.isDefault) } onSelect={() => { - props.onPreferenceChange( + props.onPreferenceChanged( preference.propertyName, option.value, ); @@ -418,13 +340,7 @@ export const CodePanel = (props: { }; export const ColorsPanel = (props: { - colors: { - hex: string; - colorName: string; - exportValue: string; - contrastWhite: number; - contrastBlack: number; - }[]; + colors: SolidColorConversion[]; onColorClick: (color: string) => void; }) => { const [isPressed, setIsPressed] = useState(-1); @@ -512,61 +428,6 @@ export const GradientsPanel = (props: { ); }; -// export const PrevColorsPanel = (props: { -// colors: { -// hex: string; -// colorName: string; -// exportValue: string; -// contrastWhite: number; -// contrastBlack: number; -// }[]; -// // onColorClick: (color: string) => void; -// }) => { -// return ( -//
-//
-//
-//
-//

Text

-// {["Button1", "Button2", "Button3"].map((button, idx) => ( -// -// ))} -//
-//
-//

Colors

-//
-// {["red-500", "yellow-500", "blue-500"].map((color, idx) => ( -// -// ))} -//
-//
-//
-//
-//
-// ); -// }; - type SelectableToggleProps = { onSelect: (isSelected: boolean) => void; isSelected?: boolean; @@ -616,10 +477,7 @@ const SelectableToggle = ({ }; export const Preview: React.FC<{ - htmlPreview: { - size: { width: number; height: number }; - content: string; - }; + htmlPreview: HTMLPreview; isResponsiveExpanded: boolean; setIsResponsiveExpanded: (value: boolean) => void; }> = (props) => { diff --git a/packages/plugin-ui/src/description.tsx b/packages/plugin-ui/src/description.tsx deleted file mode 100644 index 41ed5779..00000000 --- a/packages/plugin-ui/src/description.tsx +++ /dev/null @@ -1,396 +0,0 @@ -import { FrameworkTypes } from "./PluginUI"; - -export const Description = (props: { selected: FrameworkTypes }) => { - switch (props.selected) { - case "HTML": - return ( - <> -
- {/*

- HTML forms the foundation of the Web. To test your creations, - simply paste them here: -

*/} -
- - - ); - case "Tailwind": - return ( -
- - Tailwind CSS - - - - - - - - - -

- Tailwind is a utility-addasadads CSS framework for - - rapidly building custom designs and responsive pages. - - You can test your creations by pasting them here: -

-
- ); - case "Flutter": - return ( - <> -
- - - - - - - - - - - - - - - - - - - - - -

- Flutter is {"Google's"} UI toolkit for building applications for - - mobile, web, and desktop from a single codebase. - - You can test your creations by pasting them here: -

-
- -
- - - - - - ); - case "SwiftUI": - return ( - <> -
- - - - - -

- SwiftUI an innovative, exceptionally simple way to build user - interfaces - - across all Apple platforms with the power of Swift. - - You can test your creations in Xcode or - - Swift Playgrounds - - (iPad and Mac). -

-
-

- Tip: - - how to indent the code. - -

- - ); - } -}; diff --git a/packages/types/package.json b/packages/types/package.json new file mode 100644 index 00000000..85fd9548 --- /dev/null +++ b/packages/types/package.json @@ -0,0 +1,26 @@ +{ + "name": "types", + "version": "0.0.0", + "private": true, + "license": "GPT-3", + "sideEffects": false, + "main": "./src/index.ts", + "types": "./src/index.ts", + "files": [ + "dist/**" + ], + "scripts": { + "lint": "eslint \"src/**/*.ts*\"" + }, + "dependencies": { + "@figma/plugin-typings": "^1.105.0", + "@types/react": "^18.3.17", + "@types/react-dom": "^18.3.5", + "tsconfig": "workspace:*" + }, + "devDependencies": { + "eslint": "^9.17.0", + "eslint-config-custom": "workspace:*", + "typescript": "^5.7.2" + } +} diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts new file mode 100644 index 00000000..eea524d6 --- /dev/null +++ b/packages/types/src/index.ts @@ -0,0 +1 @@ +export * from "./types"; diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts new file mode 100644 index 00000000..bbfa577c --- /dev/null +++ b/packages/types/src/types.ts @@ -0,0 +1,151 @@ +// Settings +export type FrameworkTypes = "Flutter" | "SwiftUI" | "HTML" | "Tailwind"; + +export interface PluginSettings { + framework: FrameworkTypes; + jsx: boolean; + inlineStyle: boolean; + optimizeLayout: boolean; + showLayerNames: boolean; + responsiveRoot: boolean; + flutterGenerationMode: string; + swiftUIGenerationMode: string; + roundTailwindValues: boolean; + roundTailwindColors: boolean; + customTailwindColors: boolean; +} + +// Messaging +export interface ConversionData { + code: string; + settings: PluginSettings; + htmlPreview: HTMLPreview; + colors: SolidColorConversion[]; + gradients: LinearGradientConversion[]; +} + +export interface Message { + type: string; +} +export interface UIMessage { + pluginMessage: Message; +} +export type EmptyMessage = Message & { type: "empty" }; +export type ConversionMessage = Message & { + type: "code"; +} & ConversionData; +export type SettingWillChangeMessage = Message & { + type: "pluginSettingWillChange"; + key: string; + value: T; +}; +export type SettingsChangedMessage = Message & { + type: "pluginSettingsChanged"; + settings: PluginSettings; +}; +export type ErrorMessage = Message & { + type: "error"; + error: string; +}; + +// Styles & Conversions + +export type LayoutMode = + | "" + | "Absolute" + | "TopStart" + | "TopCenter" + | "TopEnd" + | "CenterStart" + | "Center" + | "CenterEnd" + | "BottomStart" + | "BottomCenter" + | "BottomEnd"; + +interface AllSides { + all: number; +} +interface Sides { + left: number; + right: number; + top: number; + bottom: number; +} +interface Corners { + topLeft: number; + topRight: number; + bottomRight: number; + bottomLeft: number; +} +interface HorizontalAndVertical { + horizontal: number; + vertical: number; +} + +export type PaddingType = Sides | AllSides | HorizontalAndVertical; +export type BorderSide = AllSides | Sides; +export type CornerRadius = AllSides | Corners; + +export type SizeValue = number | "fill" | null; +export interface Size { + readonly width: SizeValue; + readonly height: SizeValue; +} + +export type FontWeightNumber = + | "100" + | "200" + | "300" + | "400" + | "500" + | "600" + | "700" + | "800" + | "900"; + +export interface RGB { + r: number; + g: number; + b: number; +} + +export type ColorSpec = { + source: string; + rgb: RGB; +}; + +export type SolidColorConversion = { + hex: string; + colorName: string; + exportValue: string; + contrastWhite: number; + contrastBlack: number; + meta?: string; +}; +export type LinearGradientConversion = { + cssPreview: string; + exportValue: string; +}; + +// Framework Specific + +export interface HTMLPreview { + size: { width: number; height: number }; + content: string; +} + +export interface TailwindTextConversion { + name: string; + attr: string; + full: string; + style: string; + contrastBlack: number; +} + +export type TailwindColorType = "text" | "bg" | "border" | "solid"; + +export type SwiftUIModifier = [ + string, + string | SwiftUIModifier | SwiftUIModifier[], +]; diff --git a/packages/types/tsconfig.json b/packages/types/tsconfig.json new file mode 100644 index 00000000..dcccfd0b --- /dev/null +++ b/packages/types/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "tsconfig/base.json", + "compilerOptions": { + "typeRoots": ["./node_modules/@types", "./node_modules/@figma"] + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d799e0a1..97fbcdae 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,9 @@ importers: turbo: specifier: ^2.3.3 version: 2.3.3 + typescript: + specifier: ^5.7.2 + version: 5.7.2 apps/debug: dependencies: @@ -63,6 +66,9 @@ importers: tsconfig: specifier: workspace:* version: link:../../packages/tsconfig + types: + specifier: workspace:* + version: link:../../packages/types typescript: specifier: ^5.7.2 version: 5.7.2 @@ -133,6 +139,9 @@ importers: tsconfig: specifier: workspace:* version: link:../../packages/tsconfig + types: + specifier: workspace:* + version: link:../../packages/types typescript: specifier: ^5.7.2 version: 5.7.2 @@ -154,6 +163,9 @@ importers: react-dom: specifier: 18.3.1 version: 18.3.1(react@18.3.1) + types: + specifier: workspace:* + version: link:../types devDependencies: '@types/react': specifier: ^18.3.17 @@ -194,6 +206,15 @@ importers: packages/plugin-ui: dependencies: + '@types/react': + specifier: ^18.3.17 + version: 18.3.17 + '@types/react-dom': + specifier: ^18.3.5 + version: 18.3.5(@types/react@18.3.17) + '@types/react-syntax-highlighter': + specifier: 15.5.13 + version: 15.5.13 copy-to-clipboard: specifier: ^3.3.3 version: 3.3.3 @@ -207,30 +228,49 @@ importers: specifier: 3.4.6 version: 3.4.6 devDependencies: + eslint: + specifier: ^9.17.0 + version: 9.17.0(jiti@1.21.7) + eslint-config-custom: + specifier: workspace:* + version: link:../eslint-config-custom + tsconfig: + specifier: workspace:* + version: link:../tsconfig + types: + specifier: workspace:* + version: link:../types + typescript: + specifier: ^5.7.2 + version: 5.7.2 + + packages/tsconfig: {} + + packages/types: + dependencies: + '@figma/plugin-typings': + specifier: ^1.105.0 + version: 1.105.0 '@types/react': specifier: ^18.3.17 version: 18.3.17 '@types/react-dom': specifier: ^18.3.5 version: 18.3.5(@types/react@18.3.17) - '@types/react-syntax-highlighter': - specifier: 15.5.13 - version: 15.5.13 + tsconfig: + specifier: workspace:* + version: link:../tsconfig + devDependencies: eslint: specifier: ^9.17.0 version: 9.17.0(jiti@1.21.7) eslint-config-custom: specifier: workspace:* version: link:../eslint-config-custom - tsconfig: - specifier: workspace:* - version: link:../tsconfig typescript: specifier: ^5.7.2 version: 5.7.2 - packages/tsconfig: {} - packages: '@alloc/quick-lru@5.2.0': diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..b626377b --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,11 @@ +{ + "files": [], + "references": [ + { "path": "./packages/types" }, + { "path": "./packages/backend" }, + { "path": "./packages/plugin-ui" }, + { "path": "./apps/plugin" }, + { "path": "./apps/debug" } + ], + "exclude": ["node_modules"] +} From 2886e6412dcea2f6bec90973f9aab427e7c7ae01 Mon Sep 17 00:00:00 2001 From: "Mims H. Wright" Date: Sun, 29 Dec 2024 03:10:16 +0100 Subject: [PATCH 018/168] Warning log (#144) * Added a method for recording and displaying warnings when doing conversions. * Added icon and changed color to yellow. * added dark theme (only slightly darker) * Accidentally missed several merge conflicts in rebase * more merge fixes * Fixed issue with empty selection causing weird errors with syntax highlighter --- apps/debug/pages/index.tsx | 6 +- apps/plugin/ui-src/App.tsx | 8 +- packages/backend/src/code.ts | 4 +- .../src/common/commonConversionWarnings.ts | 10 ++ .../src/flutter/builderImpl/flutterColor.ts | 4 +- packages/backend/src/flutter/flutterMain.ts | 5 + packages/backend/src/html/htmlMain.ts | 5 + .../src/swiftui/builderImpl/swiftuiColor.ts | 2 + packages/backend/src/swiftui/swiftuiMain.ts | 4 + .../src/tailwind/builderImpl/tailwindColor.ts | 5 + packages/backend/src/tailwind/tailwindMain.ts | 6 +- packages/plugin-ui/src/PluginUI.tsx | 108 ++++++++++++------ packages/types/src/types.ts | 4 + 13 files changed, 130 insertions(+), 41 deletions(-) create mode 100644 packages/backend/src/common/commonConversionWarnings.ts diff --git a/apps/debug/pages/index.tsx b/apps/debug/pages/index.tsx index 18264a93..bc5f9a28 100644 --- a/apps/debug/pages/index.tsx +++ b/apps/debug/pages/index.tsx @@ -6,6 +6,8 @@ export default function Web() { const [selectedFramework, setSelectedFramework] = React.useState("HTML"); + const testWarnings = ["This is an example of a conversion warning message."]; + return (

Debug Mode

@@ -19,7 +21,6 @@ export default function Web() { code={ "backend:dev: CJS dist/index.js 105.74 KB backend:dev: CJS ⚡️ Build success in 419ms plugin:dev: warn - Port 3000 is in use, trying 3001 instead. plugin:dev: warn - Port 3001 is in use, trying 3002 instead." } - emptySelection={false} selectedFramework={selectedFramework} setSelectedFramework={setSelectedFramework} htmlPreview={null} @@ -27,6 +28,7 @@ export default function Web() { onPreferenceChanged={() => {}} colors={[]} gradients={[]} + warnings={testWarnings} />
@@ -38,12 +40,12 @@ export default function Web() { code={"code goes hereeeee"} selectedFramework={selectedFramework} setSelectedFramework={setSelectedFramework} - emptySelection={false} htmlPreview={null} settings={undefined} onPreferenceChanged={() => {}} colors={[]} gradients={[]} + warnings={testWarnings} />
diff --git a/apps/plugin/ui-src/App.tsx b/apps/plugin/ui-src/App.tsx index 15d07255..ee9ca918 100644 --- a/apps/plugin/ui-src/App.tsx +++ b/apps/plugin/ui-src/App.tsx @@ -10,6 +10,7 @@ import { SolidColorConversion, ErrorMessage, SettingsChangedMessage, + Warning, } from "types"; import { postUISettingsChangingMessage } from "./messaging"; @@ -21,6 +22,7 @@ interface AppState { settings: PluginSettings | null; colors: SolidColorConversion[]; gradients: LinearGradientConversion[]; + warnings: Warning[]; } const emptyPreview = { size: { width: 0, height: 0 }, content: "" }; @@ -33,6 +35,7 @@ export default function App() { settings: null, colors: [], gradients: [], + warnings: [], }); const rootStyles = getComputedStyle(document.documentElement); @@ -68,8 +71,9 @@ export default function App() { // const emptyMessage = untypedMessage as EmptyMessage; setState((prevState) => ({ ...prevState, - code: "// No layer is selected.", + code: "", htmlPreview: emptyPreview, + warnings: [], colors: [], gradients: [], })); @@ -131,7 +135,7 @@ export default function App() {
{ + clearWarnings(); const selection = figma.currentPage.selection; - const convertedSelection = convertIntoNodes(selection, null); // ignore when nothing was selected @@ -47,6 +48,7 @@ export const run = (settings: PluginSettings) => { colors, gradients, settings, + warnings: [...warnings], }); }; diff --git a/packages/backend/src/common/commonConversionWarnings.ts b/packages/backend/src/common/commonConversionWarnings.ts new file mode 100644 index 00000000..ca342e91 --- /dev/null +++ b/packages/backend/src/common/commonConversionWarnings.ts @@ -0,0 +1,10 @@ +import { Warning } from "types"; + +export const warnings = new Set(); +export const addWarning = (warning: Warning) => { + if (warnings.has(warning) === false) { + console.warn(warning); + } + warnings.add(warning); +}; +export const clearWarnings = () => warnings.clear(); diff --git a/packages/backend/src/flutter/builderImpl/flutterColor.ts b/packages/backend/src/flutter/builderImpl/flutterColor.ts index b83e97e8..bbc4c4f3 100644 --- a/packages/backend/src/flutter/builderImpl/flutterColor.ts +++ b/packages/backend/src/flutter/builderImpl/flutterColor.ts @@ -1,4 +1,5 @@ import { rgbTo8hex, gradientAngle } from "../../common/color"; +import { addWarning } from "../../common/commonConversionWarnings"; import { generateWidgetCode, sliceNum } from "../../common/numToAutoFixed"; import { retrieveTopFill } from "../../common/retrieveFill"; import { nearestValue } from "../../tailwind/conversionTables"; @@ -50,6 +51,7 @@ export const flutterBoxDecorationColor = ( }; export const flutterDecorationImage = (node: SceneNode, fill: ImagePaint) => { + addWarning("Image fills are replaced with placeholders"); return generateWidgetCode("DecorationImage", { image: `NetworkImage("https://via.placeholder.com/${node.width.toFixed( 0, @@ -82,7 +84,7 @@ export const flutterGradient = (fill: GradientPaint): string => { case "GRADIENT_ANGULAR": return flutterAngularGradient(fill); default: - // Diamond gradient is unsupported. + addWarning("Diamond dradients are not supported in Flutter"); return ""; } }; diff --git a/packages/backend/src/flutter/flutterMain.ts b/packages/backend/src/flutter/flutterMain.ts index 9a3ab5f9..0abee72f 100644 --- a/packages/backend/src/flutter/flutterMain.ts +++ b/packages/backend/src/flutter/flutterMain.ts @@ -13,6 +13,7 @@ import { } from "./builderImpl/flutterAutoLayout"; import { commonSortChildrenWhenInferredAutoLayout } from "../common/commonChildrenOrder"; import { PluginSettings } from "types"; +import { addWarning } from "../common/commonConversionWarnings"; let localSettings: PluginSettings; let previousExecutionCache: string[]; @@ -113,6 +114,9 @@ const flutterWidgetGenerator = ( case "TEXT": comp.push(flutterText(node)); break; + case "VECTOR": + addWarning("VectorNodes are not supported in Flutter"); + break; default: // do nothing } @@ -143,6 +147,7 @@ const flutterContainer = (node: SceneNode, child: string): string => { let image = ""; if ("fills" in node && retrieveTopFill(node.fills)?.type === "IMAGE") { + addWarning("Image fills are replaced with placeholders"); image = `Image.network("https://via.placeholder.com/${node.width.toFixed( 0, )}x${node.height.toFixed(0)}")`; diff --git a/packages/backend/src/html/htmlMain.ts b/packages/backend/src/html/htmlMain.ts index 915f6c86..1d19e9f2 100644 --- a/packages/backend/src/html/htmlMain.ts +++ b/packages/backend/src/html/htmlMain.ts @@ -6,6 +6,7 @@ import { htmlAutoLayoutProps } from "./builderImpl/htmlAutoLayout"; import { formatWithJSX } from "../common/parseJSX"; import { commonSortChildrenWhenInferredAutoLayout } from "../common/commonChildrenOrder"; import { PluginSettings } from "types"; +import { addWarning } from "../common/commonConversionWarnings"; let showLayerNames = false; @@ -87,6 +88,8 @@ const htmlWidgetGenerator = ( break; case "VECTOR": comp += htmlAsset(node, isJsx); + addWarning("VectorNodes are not fully supported in HTML"); + break; } }); @@ -208,6 +211,7 @@ export const htmlAsset = (node: SceneNode, isJsx: boolean = false): string => { let tag = "div"; let src = ""; if (retrieveTopFill(node.fills)?.type === "IMAGE") { + addWarning("Image fills are replaced with placeholders"); tag = "img"; src = ` src="https://via.placeholder.com/${node.width.toFixed( 0, @@ -249,6 +253,7 @@ export const htmlContainer = ( let tag = "div"; let src = ""; if (retrieveTopFill(node.fills)?.type === "IMAGE") { + addWarning("Image fills are replaced with placeholders"); if (!("children" in node) || node.children.length === 0) { tag = "img"; src = ` src="https://via.placeholder.com/${node.width.toFixed( diff --git a/packages/backend/src/swiftui/builderImpl/swiftuiColor.ts b/packages/backend/src/swiftui/builderImpl/swiftuiColor.ts index 7e47b071..90e49090 100644 --- a/packages/backend/src/swiftui/builderImpl/swiftuiColor.ts +++ b/packages/backend/src/swiftui/builderImpl/swiftuiColor.ts @@ -2,6 +2,7 @@ import { retrieveTopFill } from "../../common/retrieveFill"; import { gradientAngle } from "../../common/color"; import { nearestValue } from "../../tailwind/conversionTables"; import { sliceNum } from "../../common/numToAutoFixed"; +import { addWarning } from "../../common/commonConversionWarnings"; export const swiftUISolidColor = (fill: Paint): string => { if (fill && fill.type === "SOLID") { @@ -58,6 +59,7 @@ export const swiftuiBackground = ( } else if (fill?.type === "GRADIENT_LINEAR") { return swiftuiGradient(fill); } else if (fill?.type === "IMAGE") { + addWarning("Image fills are replaced with placeholders"); return `AsyncImage(url: URL(string: "https://via.placeholder.com/${node.width.toFixed( 0, )}x${node.height.toFixed(0)}"))`; diff --git a/packages/backend/src/swiftui/swiftuiMain.ts b/packages/backend/src/swiftui/swiftuiMain.ts index 17fc23c4..91db1866 100644 --- a/packages/backend/src/swiftui/swiftuiMain.ts +++ b/packages/backend/src/swiftui/swiftuiMain.ts @@ -4,6 +4,7 @@ import { SwiftuiTextBuilder } from "./swiftuiTextBuilder"; import { SwiftuiDefaultBuilder } from "./swiftuiDefaultBuilder"; import { commonSortChildrenWhenInferredAutoLayout } from "../common/commonChildrenOrder"; import { PluginSettings } from "types"; +import { addWarning } from "../common/commonConversionWarnings"; let localSettings: PluginSettings; let previousExecutionCache: string[]; @@ -85,6 +86,9 @@ const swiftuiWidgetGenerator = ( case "TEXT": comp.push(swiftuiText(node)); break; + case "VECTOR": + addWarning("VectorNodes are not supported in SwiftUI"); + break; default: break; } diff --git a/packages/backend/src/tailwind/builderImpl/tailwindColor.ts b/packages/backend/src/tailwind/builderImpl/tailwindColor.ts index b9b78f36..a5d5cb70 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindColor.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindColor.ts @@ -6,6 +6,7 @@ import { nearestValue, } from "../conversionTables"; import { TailwindColorType } from "types"; +import { addWarning } from "../../common/commonConversionWarnings"; /** * Get a tailwind color value object @@ -90,6 +91,10 @@ export const tailwindGradientFromFills = ( return tailwindGradient(fill); } + addWarning( + "Gradients are not fully supported in Tailwind except for Linear Gradients.", + ); + return ""; }; diff --git a/packages/backend/src/tailwind/tailwindMain.ts b/packages/backend/src/tailwind/tailwindMain.ts index 82ff2e87..b7ea7e24 100644 --- a/packages/backend/src/tailwind/tailwindMain.ts +++ b/packages/backend/src/tailwind/tailwindMain.ts @@ -6,6 +6,7 @@ import { TailwindDefaultBuilder } from "./tailwindDefaultBuilder"; import { tailwindAutoLayoutProps } from "./builderImpl/tailwindAutoLayout"; import { commonSortChildrenWhenInferredAutoLayout } from "../common/commonChildrenOrder"; import { PluginSettings } from "types"; +import { addWarning } from "../common/commonConversionWarnings"; export let localTailwindSettings: PluginSettings; @@ -63,7 +64,9 @@ const tailwindWidgetGenerator = ( case "SECTION": comp += tailwindSection(node, isJsx); break; - // case "VECTOR": + case "VECTOR": + addWarning("VectorNodes are not supported in Tailwind"); + break; // comp += htmlAsset(node, isJsx); } }); @@ -232,6 +235,7 @@ export const tailwindContainer = ( let tag = "div"; let src = ""; if (retrieveTopFill(node.fills)?.type === "IMAGE") { + addWarning("Image fills are replaced with placeholders"); if (!("children" in node) || node.children.length === 0) { tag = "img"; src = ` src="https://via.placeholder.com/${node.width.toFixed( diff --git a/packages/plugin-ui/src/PluginUI.tsx b/packages/plugin-ui/src/PluginUI.tsx index a55a2633..a3eb6262 100644 --- a/packages/plugin-ui/src/PluginUI.tsx +++ b/packages/plugin-ui/src/PluginUI.tsx @@ -8,12 +8,13 @@ import { LinearGradientConversion, PluginSettings, SolidColorConversion, + Warning, } from "types"; type PluginUIProps = { code: string; htmlPreview: HTMLPreview; - emptySelection: boolean; + warnings: Warning[]; selectedFramework: FrameworkTypes; setSelectedFramework: (framework: FrameworkTypes) => void; settings: PluginSettings | null; @@ -22,13 +23,18 @@ type PluginUIProps = { gradients: LinearGradientConversion[]; }; +const frameworks: FrameworkTypes[] = ["HTML", "Tailwind", "Flutter", "SwiftUI"]; + export const PluginUI = (props: PluginUIProps) => { const [isResponsiveExpanded, setIsResponsiveExpanded] = useState(false); + const isEmpty = props.code === ""; + + const warnings = props.warnings ?? []; return (
- {["HTML", "Tailwind", "Flutter", "SwiftUI"].map((tab) => ( + {frameworks.map((tab) => (
- {props.htmlPreview && ( + {isEmpty === false && props.htmlPreview && ( )} + {warnings.length > 0 && ( +
+
+
+ +
+

Warnings:

+
+
    + {warnings.map((message: string) => ( +
  • + {message} +
  • + ))} +
+
+ )} void; }) => { - const emptySelection = false; + const isEmpty = props.code === ""; const [isPressed, setIsPressed] = useState(false); const [syntaxHovered, setSyntaxHovered] = useState(false); @@ -220,25 +243,18 @@ export const CodePanel = (props: { const handleButtonHover = () => setSyntaxHovered(true); const handleButtonLeave = () => setSyntaxHovered(false); - if (emptySelection) { - return ( -
-

Nothing is selected

-

Try selecting a layer, any layer

-
- ); - } else { - const selectableSettingsFiltered = selectPreferenceOptions.filter( - (preference) => - preference.includedLanguages?.includes(props.selectedFramework), - ); - - return ( -
-
-

- Code -

+ const selectableSettingsFiltered = selectPreferenceOptions.filter( + (preference) => + preference.includedLanguages?.includes(props.selectedFramework), + ); + + return ( +
+
+

+ Code +

+ {isEmpty === false && ( -
- + )} +
+ {isEmpty === false && (
{preferenceOptions @@ -285,7 +302,7 @@ export const CodePanel = (props: { <> {/* {preference.label} - */} + */} {preference.options.map((option) => ( )}
+ )} -
+
+ {isEmpty ? ( +

No layer is selected. Please select a layer.

+ ) : ( {props.code} -
+ )}
- ); - } +
+ ); }; export const ColorsPanel = (props: { @@ -574,3 +595,22 @@ const ExpandIcon = (props: { size: number }) => ( ); + +const WarningIcon = () => ( + + + + + +); diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index bbfa577c..64b458af 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -22,8 +22,12 @@ export interface ConversionData { htmlPreview: HTMLPreview; colors: SolidColorConversion[]; gradients: LinearGradientConversion[]; + warnings: Warning[]; } +export type Warning = string; +export type Warnings = Set; + export interface Message { type: string; } From 9e224a5a5bdb54ccef49866c4475c4e5f8539b80 Mon Sep 17 00:00:00 2001 From: "Mims H. Wright" Date: Sun, 29 Dec 2024 21:59:46 +0100 Subject: [PATCH 019/168] Spacing of plugin (#149) * made plugin taller * reduced size of warnings slightly --- apps/plugin/plugin-src/code.ts | 2 +- packages/plugin-ui/src/PluginUI.tsx | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/plugin/plugin-src/code.ts b/apps/plugin/plugin-src/code.ts index f4be7a39..15fd6c90 100644 --- a/apps/plugin/plugin-src/code.ts +++ b/apps/plugin/plugin-src/code.ts @@ -76,7 +76,7 @@ const safeRun = (settings: PluginSettings) => { }; const standardMode = async () => { - figma.showUI(__html__, { width: 450, height: 550, themeColors: true }); + figma.showUI(__html__, { width: 450, height: 700, themeColors: true }); await initSettings(); // Listen for selection changes diff --git a/packages/plugin-ui/src/PluginUI.tsx b/packages/plugin-ui/src/PluginUI.tsx index a3eb6262..d1047090 100644 --- a/packages/plugin-ui/src/PluginUI.tsx +++ b/packages/plugin-ui/src/PluginUI.tsx @@ -67,17 +67,17 @@ export const PluginUI = (props: PluginUIProps) => { /> )} {warnings.length > 0 && ( -
+
-
+
-

Warnings:

+

Warnings:

    {warnings.map((message: string) => (
  • - {message} + {message}
  • ))}
From ef552e4bc7423514ebfbb49d250d261197af7360 Mon Sep 17 00:00:00 2001 From: "Mims H. Wright" Date: Wed, 1 Jan 2025 15:40:14 +0100 Subject: [PATCH 020/168] Big fat New Years refactor (#151) * renamed Frameworks (cherry picked from commit 533bbd8e6202ad903f2783683cf82fc860c26f0e) * removed unused. fixed typo (cherry picked from commit 79d76a358a05b0544af1859ad2eb55888e077c8a) * extracted components from plugin-ui * added warning icon to components * updated tsconfig * removing unused code * Add code type automatically when posting code message * Moved convert to code to its own file. also made htmlpreview a single step. * renamed some things and removed exports from some local functions * removed unused code in alt conversion * altnode conversion WIP * continuing refactor of alt node conversion. WIP * finished refactoring alt nodes * removed AltNode type --- apps/debug/pages/index.tsx | 29 +- apps/plugin/plugin-src/code.ts | 2 +- apps/plugin/ui-src/App.tsx | 6 +- .../backend/src/altNodes/altConversion.ts | 418 ++++---------- packages/backend/src/altNodes/altNodeUtils.ts | 21 + packages/backend/src/code.ts | 40 +- packages/backend/src/common/curry.ts | 13 + .../src/common/retrieveUI/convertToCode.ts | 19 + .../src/common/retrieveUI/retrieveColors.ts | 8 +- packages/backend/src/html/htmlMain.ts | 45 +- packages/backend/src/index.ts | 2 +- packages/backend/src/messaging.ts | 5 +- .../src/tailwind/retrieveUI/retrieveTexts.ts | 165 ------ packages/backend/src/tailwind/tailwindMain.ts | 10 - packages/backend/src/tailwind/vector.ts | 124 ----- packages/plugin-ui/src/PluginUI.tsx | 525 +----------------- .../plugin-ui/src/codegenPreferenceOptions.ts | 78 +++ .../plugin-ui/src/components/CodePanel.tsx | 158 ++++++ .../plugin-ui/src/components/ColorsPanel.tsx | 52 ++ .../plugin-ui/src/components/ExpandIcon.tsx | 12 + .../src/components/GradientsPanel.tsx | 42 ++ packages/plugin-ui/src/components/Preview.tsx | 68 +++ .../src/components/SelectableToggle.tsx | 48 ++ .../plugin-ui/src/components/WarningIcon.tsx | 19 + packages/types/src/types.ts | 42 +- tsconfig.json | 2 +- 26 files changed, 743 insertions(+), 1210 deletions(-) create mode 100644 packages/backend/src/altNodes/altNodeUtils.ts create mode 100644 packages/backend/src/common/curry.ts create mode 100644 packages/backend/src/common/retrieveUI/convertToCode.ts delete mode 100644 packages/backend/src/tailwind/retrieveUI/retrieveTexts.ts delete mode 100644 packages/backend/src/tailwind/vector.ts create mode 100644 packages/plugin-ui/src/codegenPreferenceOptions.ts create mode 100644 packages/plugin-ui/src/components/CodePanel.tsx create mode 100644 packages/plugin-ui/src/components/ColorsPanel.tsx create mode 100644 packages/plugin-ui/src/components/ExpandIcon.tsx create mode 100644 packages/plugin-ui/src/components/GradientsPanel.tsx create mode 100644 packages/plugin-ui/src/components/Preview.tsx create mode 100644 packages/plugin-ui/src/components/SelectableToggle.tsx create mode 100644 packages/plugin-ui/src/components/WarningIcon.tsx diff --git a/apps/debug/pages/index.tsx b/apps/debug/pages/index.tsx index bc5f9a28..4fb4b616 100644 --- a/apps/debug/pages/index.tsx +++ b/apps/debug/pages/index.tsx @@ -1,10 +1,10 @@ -import { FrameworkTypes } from "types"; +import { Framework } from "types"; import * as React from "react"; import { PluginUI } from "plugin-ui"; export default function Web() { const [selectedFramework, setSelectedFramework] = - React.useState("HTML"); + React.useState("HTML"); const testWarnings = ["This is an example of a conversion warning message."]; @@ -18,9 +18,7 @@ export default function Web() {
- {/*
Templates for debugging
-
- {[1, 2, 3, 4, 5].map((d) => ( -
- A random image -
- ))} -
*/}
Plugin dropdown selection (each frame a different breakpoint)
@@ -97,7 +75,6 @@ export default function Web() { const PluginFigmaToolbar = (props: { variant: string }) => (
- {/*
*/} Figma to Code {props.variant}
); diff --git a/apps/plugin/plugin-src/code.ts b/apps/plugin/plugin-src/code.ts index 15fd6c90..b12a4d89 100644 --- a/apps/plugin/plugin-src/code.ts +++ b/apps/plugin/plugin-src/code.ts @@ -16,7 +16,7 @@ import { PluginSettings, SettingWillChangeMessage } from "types"; let userPluginSettings: PluginSettings; -const defaultPluginSettings: PluginSettings = { +export const defaultPluginSettings: PluginSettings = { framework: "HTML", jsx: false, optimizeLayout: true, diff --git a/apps/plugin/ui-src/App.tsx b/apps/plugin/ui-src/App.tsx index ee9ca918..7bee7558 100644 --- a/apps/plugin/ui-src/App.tsx +++ b/apps/plugin/ui-src/App.tsx @@ -1,7 +1,7 @@ import { useEffect, useState } from "react"; import { PluginUI } from "plugin-ui"; import { - FrameworkTypes, + Framework, PluginSettings, ConversionMessage, Message, @@ -16,7 +16,7 @@ import { postUISettingsChangingMessage } from "./messaging"; interface AppState { code: string; - selectedFramework: FrameworkTypes; + selectedFramework: Framework; isLoading: boolean; htmlPreview: HTMLPreview; settings: PluginSettings | null; @@ -119,7 +119,7 @@ export default function App() { ) : null; } - const handleFrameworkChange = (updatedFramework: FrameworkTypes) => { + const handleFrameworkChange = (updatedFramework: Framework) => { setState((prevState) => ({ ...prevState, // code: "// Loading...", diff --git a/packages/backend/src/altNodes/altConversion.ts b/packages/backend/src/altNodes/altConversion.ts index e2f3a903..63d97f95 100644 --- a/packages/backend/src/altNodes/altConversion.ts +++ b/packages/backend/src/altNodes/altConversion.ts @@ -1,336 +1,132 @@ -type StyledTextSegmentSubset = Omit< - StyledTextSegment, - "listSpacing" | "paragraphIndent" | "paragraphSpacing" | "textStyleOverrides" ->; +import { StyledTextSegmentSubset, ParentNode } from "types"; +import { + overrideReadonlyProperty, + assignParent, + isNotEmpty, + assignRectangleType, + assignChildren, +} from "./altNodeUtils"; +import { addWarning } from "../common/commonConversionWarnings"; + export let globalTextStyleSegments: Record = {}; -export const cloneNode = (node: T): T => { - // Create the cloned object with the correct prototype - const cloned = {} as T; - // Create a new object with only the desired descriptors (excluding 'parent' and 'children') - for (const prop in node) { - if ( - prop !== "parent" && - prop !== "children" && - prop !== "horizontalPadding" && - prop !== "verticalPadding" && - prop !== "mainComponent" && - prop !== "masterComponent" && - prop !== "variantProperties" && - prop !== "get_annotations" && - prop !== "componentPropertyDefinitions" && - prop !== "exposedInstances" && - prop !== "componentProperties" && - prop !== "componenPropertyReferences" - ) { - cloned[prop as keyof T] = node[prop as keyof T]; - } - } - - return cloned; -}; - -/** - * Identify all nodes that are inside Rectangles and transform those Rectangles into Frames containing those nodes. - */ -export const convertNodesOnRectangle = ( - node: FrameNode | GroupNode | InstanceNode | ComponentNode | ComponentSetNode, -): FrameNode | GroupNode | InstanceNode | ComponentNode | ComponentSetNode => { - if (node.children.length < 2) { - return node; - } - if (!node.id) { - throw new Error( - "Node is missing an id! This error should only happen in tests.", - ); - } - - return node; -}; - -export const frameNodeTo = ( - node: FrameNode | InstanceNode | ComponentNode | ComponentSetNode, - parent: ParentNode, -): - | RectangleNode - | FrameNode - | InstanceNode - | ComponentNode - | GroupNode - | ComponentSetNode => { - if (node.children.length === 0) { - // if it has no children, convert frame to rectangle - return frameToRectangleNode(node, parent); - } - const clone = standardClone(node, parent); - - overrideReadonlyProperty( - clone, - "children", - convertIntoNodes(node.children, clone), - ); - return convertNodesOnRectangle(clone); -}; - -// auto convert Frame to Rectangle when Frame has no Children -const frameToRectangleNode = ( - node: FrameNode | InstanceNode | ComponentNode | ComponentSetNode, - parent: ParentNode, -): RectangleNode => { - const clonedNode = cloneNode(node); - if (parent) { - assignParent(clonedNode, parent); - } - overrideReadonlyProperty(clonedNode, "type", "RECTANGLE"); - - return clonedNode as unknown as RectangleNode; -}; - -export const overrideReadonlyProperty = ( - obj: T, - prop: K, - value: any, -): void => { - Object.defineProperty(obj, prop, { - value: value, - writable: true, - configurable: true, - }); -}; - -const assignParent = (node: SceneNode, parent: ParentNode) => { - if (parent) { - overrideReadonlyProperty(node, "parent", parent); - } -}; - -const standardClone = (node: T, parent: ParentNode): T => { - const clonedNode = cloneNode(node); - if (parent !== null) { - assignParent(clonedNode, parent); - } - return clonedNode; -}; - -type ParentNode = (BaseNode & ChildrenMixin) | null; - -export const convertIntoNodes = ( - sceneNode: ReadonlyArray, - parent: ParentNode = null, -): Array => { - const mapped: Array = sceneNode.map((node: SceneNode) => { - switch (node.type) { +export const convertNodeToAltNode = + (parent: ParentNode | null) => + (node: SceneNode): SceneNode => { + const type = node.type; + switch (type) { + // Standard nodes case "RECTANGLE": case "ELLIPSE": - return standardClone(node, parent); case "LINE": - return standardClone(node, parent); + case "STAR": + case "POLYGON": + case "VECTOR": + case "BOOLEAN_OPERATION": + return cloneNode(node, parent); + + // Group nodes case "FRAME": case "INSTANCE": case "COMPONENT": case "COMPONENT_SET": - // TODO Fix asset export. Use the new API. - // const iconToRect = iconToRectangle(node, parent); - // if (iconToRect != null) { - // return iconToRect; - // } - return frameNodeTo(node, parent); - case "GROUP": - if (node.children.length === 1 && node.visible) { - // if Group is visible and has only one child, Group should disappear. - // there will be a single value anyway. - return convertIntoNodes(node.children, parent)[0]; - } - - // TODO see if necessary. - const iconToRect = iconToRectangle(node, parent); - if (iconToRect != null) { - return iconToRect; - } + // if the frame, instance etc. has no children, convert the frame to rectangle + if (node.children.length === 0) + return cloneAsRectangleNode(node, parent); + // goto SECTION - const clone = standardClone(node, parent); + case "GROUP": + // if a Group is visible and has only one child, the Group should be ungrouped. + if (type === "GROUP" && node.children.length === 1 && node.visible) + return convertNodeToAltNode(parent)(node.children[0]); + // goto SECTION - overrideReadonlyProperty( - clone, - "children", - convertIntoNodes(node.children, clone), - ); + case "SECTION": + const group = cloneNode(node, parent); + const groupChildren = convertNodesToAltNodes(node.children, group); + return assignChildren(groupChildren, group); - // try to find big rect and regardless of that result, also try to convert to autolayout. - // There is a big chance this will be returned as a Frame - // also, Group will always have at least 2 children. - return convertNodesOnRectangle(clone); + // Text Nodes case "TEXT": - globalTextStyleSegments[node.id] = node.getStyledTextSegments([ - "fontName", - "fills", - "fontSize", - "fontWeight", - "hyperlink", - "indentation", - "letterSpacing", - "lineHeight", - "listOptions", - "textCase", - "textDecoration", - "textStyleId", - "fillStyleId", - "openTypeFeatures", - ]); - return standardClone(node, parent); - case "STAR": - case "POLYGON": - case "VECTOR": - return standardClone(node, parent); - case "SECTION": - const sectionClone = standardClone(node, parent); - overrideReadonlyProperty( - sectionClone, - "children", - convertIntoNodes(node.children, sectionClone), + globalTextStyleSegments[node.id] = extractStyledTextSegments(node); + return cloneNode(node, parent); + + // Unsupported Nodes + case "SLICE": + throw new Error( + `Sorry, Slices are not supported. Type:${node.type} id:${node.id}`, ); - return sectionClone; - case "BOOLEAN_OPERATION": - const clonedOperation = standardClone(node, parent); - overrideReadonlyProperty(clonedOperation, "type", "RECTANGLE"); - clonedOperation.fills = [ - { - type: "IMAGE", - scaleMode: "FILL", - imageHash: "0", - opacity: 1, - visible: true, - blendMode: "NORMAL", - imageTransform: [ - [1, 0, 0], - [0, 1, 0], - ], - }, - ]; - return clonedOperation; default: - return null; + throw new Error( + `Sorry, an unsupported node type was selected. Type:${node.type} id:${node.id}`, + ); } - }); - - return mapped.filter(notEmpty); -}; - -const iconToRectangle = ( - node: FrameNode | InstanceNode | ComponentNode | GroupNode, - parent: ParentNode, -): RectangleNode | null => { - // TODO Fix this. - if (false && node.children.every((d) => d.type === "VECTOR")) { - // const node = new RectangleNode(); - // node.id = node.id; - // node.name = node.name; - // if (Parent) { - // node.parent = Parent; - // } - // convertBlend(Node, node); - // // width, x, y - // convertLayout(Node, node); - // // Vector support is still missing. Meanwhile, add placeholder. - // node.cornerRadius = 8; - // node.strokes = []; - // node.strokeWeight = 0; - // node.strokeMiterLimit = 0; - // node.strokeAlign = "CENTER"; - // node.strokeCap = "NONE"; - // node.strokeJoin = "BEVEL"; - // node.dashPattern = []; - // node.fillStyleId = ""; - // node.strokeStyleId = ""; - // node.fills = [ - // { - // type: "IMAGE", - // imageHash: "", - // scaleMode: "FIT", - // visible: true, - // opacity: 0.5, - // blendMode: "NORMAL", - // }, - // ]; - // return node; - } - return null; -}; - -export function notEmpty( - value: TValue | null | undefined, -): value is TValue { - return value !== null && value !== undefined; -} - -const applyMatrixToPoint = (matrix: number[][], point: number[]): number[] => { - return [ - point[0] * matrix[0][0] + point[1] * matrix[0][1] + matrix[0][2], - point[0] * matrix[1][0] + point[1] * matrix[1][1] + matrix[1][2], - ]; -}; - -/** - * this function return a bounding rect for an nodes - */ -// x/y absolute coordinates -// height/width -// x2/y2 bottom right coordinates -export const getBoundingRect = ( - node: LayoutMixin, -): { - x: number; - y: number; - // x2: number; - // y2: number; - // height: number; - // width: number; -} => { - const boundingRect = { - x: 0, - y: 0, - // x2: 0, - // y2: 0, - // height: 0, - // width: 0, }; - const halfHeight = node.height / 2; - const halfWidth = node.width / 2; - - const [[c0, s0, x], [s1, c1, y]] = node.absoluteTransform; - const matrix = [ - [c0, s0, x + halfWidth * c0 + halfHeight * s0], - [s1, c1, y + halfWidth * s1 + halfHeight * c1], +export const convertNodesToAltNodes = ( + sceneNode: ReadonlyArray, + parent: ParentNode | null, +): Array => + sceneNode.map(convertNodeToAltNode(parent)).filter(isNotEmpty); + +export const cloneNode = ( + node: T, + parent: ParentNode | null, +): T => { + // Create the cloned object with the correct prototype + const cloned = {} as T; + // Create a new object with only the desired descriptors (excluding 'parent' and 'children') + const droppedProps = [ + "parent", + "children", + "horizontalPadding", + "verticalPadding", + "mainComponent", + "masterComponent", + "variantProperties", + "get_annotations", + "componentPropertyDefinitions", + "exposedInstances", + "componentProperties", + "componenPropertyReferences", ]; - - // the coordinates of the corners of the rectangle - const XY: { - x: number[]; - y: number[]; - } = { - x: [1, -1, 1, -1], - y: [1, -1, -1, 1], - }; - - // fill in - for (let i = 0; i <= 3; i++) { - const a = applyMatrixToPoint(matrix, [ - XY.x[i] * halfWidth, - XY.y[i] * halfHeight, - ]); - XY.x[i] = a[0]; - XY.y[i] = a[1]; + for (const prop in node) { + if (prop in droppedProps === false) { + cloned[prop as keyof T] = node[prop as keyof T]; + } } + assignParent(parent, cloned); - XY.x.sort((a, b) => a - b); - XY.y.sort((a, b) => a - b); + return cloned; +}; - return { - x: XY.x[0], - y: XY.y[0], - }; +// auto convert Frame to Rectangle when Frame has no Children +const cloneAsRectangleNode = ( + node: T, + parent: ParentNode | null, +): RectangleNode => { + const clonedNode = cloneNode(node, parent); + + assignRectangleType(clonedNode); - return boundingRect; + return clonedNode as unknown as RectangleNode; }; + +const extractStyledTextSegments = (node: TextNode) => + node.getStyledTextSegments([ + "fontName", + "fills", + "fontSize", + "fontWeight", + "hyperlink", + "indentation", + "letterSpacing", + "lineHeight", + "listOptions", + "textCase", + "textDecoration", + "textStyleId", + "fillStyleId", + "openTypeFeatures", + ]); diff --git a/packages/backend/src/altNodes/altNodeUtils.ts b/packages/backend/src/altNodes/altNodeUtils.ts new file mode 100644 index 00000000..f9180cb7 --- /dev/null +++ b/packages/backend/src/altNodes/altNodeUtils.ts @@ -0,0 +1,21 @@ +import { curry } from "../common/curry"; + +export const overrideReadonlyProperty = curry( + (prop: K, value: any, obj: T): T => + Object.defineProperty(obj, prop, { + value: value, + writable: true, + configurable: true, + }), +); + +export const assignParent = overrideReadonlyProperty("parent"); +export const assignChildren = overrideReadonlyProperty("children"); +export const assignType = overrideReadonlyProperty("type"); +export const assignRectangleType = assignType("RECTANGLE"); + +export function isNotEmpty( + value: TValue | null | undefined, +): value is TValue { + return value !== null && value !== undefined; +} diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index d11ab413..8a2ef42a 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -1,20 +1,20 @@ -import { convertIntoNodes } from "./altNodes/altConversion"; +import { convertNodesToAltNodes } from "./altNodes/altConversion"; import { retrieveGenericSolidUIColors, retrieveGenericLinearGradients as retrieveGenericGradients, } from "./common/retrieveUI/retrieveColors"; -import { flutterMain } from "./flutter/flutterMain"; import { generateHTMLPreview, htmlMain } from "./html/htmlMain"; import { postConversionComplete, postEmptyMessage } from "./messaging"; -import { swiftuiMain } from "./swiftui/swiftuiMain"; -import { tailwindMain } from "./tailwind/tailwindMain"; import { clearWarnings, warnings } from "./common/commonConversionWarnings"; import { PluginSettings } from "types"; +import { convertToCode } from "./common/retrieveUI/convertToCode"; export const run = (settings: PluginSettings) => { clearWarnings(); + const { framework } = settings; const selection = figma.currentPage.selection; - const convertedSelection = convertIntoNodes(selection, null); + + const convertedSelection = convertNodesToAltNodes(selection, null); // ignore when nothing was selected // If the selection was empty, the converted selection will also be empty. @@ -23,26 +23,12 @@ export const run = (settings: PluginSettings) => { return; } - const { framework } = settings; const code = convertToCode(convertedSelection, settings); - // Only generate HTML code if necessary - const htmlCode = - framework === "HTML" && settings.jsx === false - ? code - : generateHTMLPreview(convertedSelection, settings); + const htmlPreview = generateHTMLPreview(convertedSelection, settings, code); const colors = retrieveGenericSolidUIColors(framework); const gradients = retrieveGenericGradients(framework); - const htmlPreview = { - size: { - width: convertedSelection[0].width, - height: convertedSelection[0].height, - }, - content: htmlCode, - }; - postConversionComplete({ - type: "code", code, htmlPreview, colors, @@ -51,17 +37,3 @@ export const run = (settings: PluginSettings) => { warnings: [...warnings], }); }; - -export const convertToCode = (nodes: SceneNode[], settings: PluginSettings) => { - switch (settings.framework) { - case "Tailwind": - return tailwindMain(nodes, settings); - case "Flutter": - return flutterMain(nodes, settings); - case "SwiftUI": - return swiftuiMain(nodes, settings); - case "HTML": - default: - return htmlMain(nodes, settings); - } -}; diff --git a/packages/backend/src/common/curry.ts b/packages/backend/src/common/curry.ts new file mode 100644 index 00000000..dd9045bb --- /dev/null +++ b/packages/backend/src/common/curry.ts @@ -0,0 +1,13 @@ +export function curry any>( + fn: T, + arity = fn.length, +): any { + return function curried(...args: any[]): any { + if (args.length >= arity) { + return fn(...args); + } + return function (...moreArgs: any[]) { + return curried(...args, ...moreArgs); + }; + }; +} diff --git a/packages/backend/src/common/retrieveUI/convertToCode.ts b/packages/backend/src/common/retrieveUI/convertToCode.ts new file mode 100644 index 00000000..94ef45b6 --- /dev/null +++ b/packages/backend/src/common/retrieveUI/convertToCode.ts @@ -0,0 +1,19 @@ +import { PluginSettings } from "types"; +import { flutterMain } from "../../flutter/flutterMain"; +import { htmlMain } from "../../html/htmlMain"; +import { swiftuiMain } from "../../swiftui/swiftuiMain"; +import { tailwindMain } from "../../tailwind/tailwindMain"; + +export const convertToCode = (nodes: SceneNode[], settings: PluginSettings) => { + switch (settings.framework) { + case "Tailwind": + return tailwindMain(nodes, settings); + case "Flutter": + return flutterMain(nodes, settings); + case "SwiftUI": + return swiftuiMain(nodes, settings); + case "HTML": + default: + return htmlMain(nodes, settings); + } +}; diff --git a/packages/backend/src/common/retrieveUI/retrieveColors.ts b/packages/backend/src/common/retrieveUI/retrieveColors.ts index 7c523055..579cf7d2 100644 --- a/packages/backend/src/common/retrieveUI/retrieveColors.ts +++ b/packages/backend/src/common/retrieveUI/retrieveColors.ts @@ -19,11 +19,11 @@ import { calculateContrastRatio } from "./commonUI"; import { LinearGradientConversion, SolidColorConversion, - FrameworkTypes, + Framework, } from "types"; export const retrieveGenericSolidUIColors = ( - framework: FrameworkTypes, + framework: Framework, ): Array => { const selectionColors = figma.getSelectionColors(); if (!selectionColors || selectionColors.paints.length === 0) return []; @@ -44,7 +44,7 @@ export const retrieveGenericSolidUIColors = ( const convertSolidColor = ( fill: Paint, - framework: FrameworkTypes, + framework: Framework, ): SolidColorConversion | null => { const black = { r: 0, g: 0, b: 0 }; const white = { r: 1, g: 1, b: 1 }; @@ -74,7 +74,7 @@ const convertSolidColor = ( }; export const retrieveGenericLinearGradients = ( - framework: FrameworkTypes, + framework: Framework, ): Array => { const selectionColors = figma.getSelectionColors(); const colorStr: Array = []; diff --git a/packages/backend/src/html/htmlMain.ts b/packages/backend/src/html/htmlMain.ts index 1d19e9f2..45a59a4f 100644 --- a/packages/backend/src/html/htmlMain.ts +++ b/packages/backend/src/html/htmlMain.ts @@ -5,8 +5,8 @@ import { HtmlDefaultBuilder } from "./htmlDefaultBuilder"; import { htmlAutoLayoutProps } from "./builderImpl/htmlAutoLayout"; import { formatWithJSX } from "../common/parseJSX"; import { commonSortChildrenWhenInferredAutoLayout } from "../common/commonChildrenOrder"; -import { PluginSettings } from "types"; import { addWarning } from "../common/commonConversionWarnings"; +import { PluginSettings, HTMLPreview } from "types"; let showLayerNames = false; @@ -40,15 +40,29 @@ export const htmlMain = ( export const generateHTMLPreview = ( nodes: SceneNode[], settings: PluginSettings, -) => - htmlMain( - nodes, - { - ...settings, - jsx: false, + code?: string, +): HTMLPreview => { + const htmlCodeAlreadyGenerated = + settings.framework === "HTML" && settings.jsx === false && code; + const htmlCode = htmlCodeAlreadyGenerated + ? code + : htmlMain( + nodes, + { + ...settings, + jsx: false, + }, + true, + ); + + return { + size: { + width: nodes[0].width, + height: nodes[0].height, }, - true, - ); + content: htmlCode, + }; +}; // todo lint idea: replace BorderRadius.only(topleft: 8, topRight: 8) with BorderRadius.horizontal(8) const htmlWidgetGenerator = ( @@ -127,7 +141,7 @@ const htmlGroup = (node: GroupNode, isJsx: boolean = false): string => { }; // this was split from htmlText to help the UI part, where the style is needed (without

). -export const htmlText = (node: TextNode, isJsx: boolean): string => { +const htmlText = (node: TextNode, isJsx: boolean): string => { let layoutBuilder = new HtmlTextBuilder(node, showLayerNames, isJsx) .commonPositionStyles(node, localSettings.optimizeLayout) .textAlign(node); @@ -199,7 +213,7 @@ const htmlFrame = ( } }; -export const htmlAsset = (node: SceneNode, isJsx: boolean = false): string => { +const htmlAsset = (node: SceneNode, isJsx: boolean = false): string => { if (!("opacity" in node) || !("layoutAlign" in node) || !("fills" in node)) { return ""; } @@ -227,7 +241,7 @@ export const htmlAsset = (node: SceneNode, isJsx: boolean = false): string => { // properties named propSomething always take care of "," // sometimes a property might not exist, so it doesn't add "," -export const htmlContainer = ( +const htmlContainer = ( node: SceneNode & SceneNodeMixin & BlendMixin & @@ -286,10 +300,7 @@ export const htmlContainer = ( return children; }; -export const htmlSection = ( - node: SectionNode, - isJsx: boolean = false, -): string => { +const htmlSection = (node: SectionNode, isJsx: boolean = false): string => { const childrenStr = htmlWidgetGenerator(node.children, isJsx); const builder = new HtmlDefaultBuilder(node, showLayerNames, isJsx) .size(node, localSettings.optimizeLayout) @@ -303,7 +314,7 @@ export const htmlSection = ( } }; -export const htmlLine = (node: LineNode, isJsx: boolean): string => { +const htmlLine = (node: LineNode, isJsx: boolean): string => { const builder = new HtmlDefaultBuilder(node, showLayerNames, isJsx) .commonPositionStyles(node, localSettings.optimizeLayout) .commonShapeStyles(node); diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index 1d6b233e..90f21d61 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -3,5 +3,5 @@ export { htmlMain } from "./html/htmlMain"; export { tailwindMain } from "./tailwind/tailwindMain"; export { swiftuiMain } from "./swiftui/swiftuiMain"; export { run } from "./code"; -export { convertIntoNodes } from "./altNodes/altConversion"; +export { convertNodesToAltNodes as convertIntoNodes } from "./altNodes/altConversion"; export * from "./messaging"; diff --git a/packages/backend/src/messaging.ts b/packages/backend/src/messaging.ts index b542dcb6..64adcd45 100644 --- a/packages/backend/src/messaging.ts +++ b/packages/backend/src/messaging.ts @@ -11,8 +11,9 @@ export const postBackendMessage = figma.ui.postMessage; export const postEmptyMessage = () => postBackendMessage({ type: "empty" } as EmptyMessage); -export const postConversionComplete = (conversionData: ConversionMessage) => - postBackendMessage(conversionData); +export const postConversionComplete = ( + conversionData: ConversionMessage | Omit, +) => postBackendMessage({ ...conversionData, type: "code" }); export const postError = (error: string) => postBackendMessage({ type: "error", error } as ErrorMessage); diff --git a/packages/backend/src/tailwind/retrieveUI/retrieveTexts.ts b/packages/backend/src/tailwind/retrieveUI/retrieveTexts.ts deleted file mode 100644 index 95d2fdb1..00000000 --- a/packages/backend/src/tailwind/retrieveUI/retrieveTexts.ts +++ /dev/null @@ -1,165 +0,0 @@ -import { TailwindTextBuilder } from "../tailwindTextBuilder"; -import { rgbTo6hex } from "../../common/color"; -import { retrieveTopFill } from "../../common/retrieveFill"; -import { convertFontWeight } from "../../common/convertFontWeight"; -import { nearestColor } from "../conversionTables"; -import { TailwindTextConversion } from "types"; - -export const retrieveTailwindText = ( - sceneNode: Array, -): Array => { - // convert to Node and then flatten it. Conversion is necessary because of [tailwindText] - const selectedText = deepFlatten(sceneNode); - - const textStr: Array = []; - - selectedText.forEach((node) => { - if (node.type === "TEXT") { - let layoutBuilder = new TailwindTextBuilder(node, false, false) - .commonPositionStyles(node, false) - .textAlign(node); - - const styledHtml = layoutBuilder.getTextSegments(node.id); - - let content = ""; - if (styledHtml.length === 1) { - layoutBuilder.addAttributes(styledHtml[0].style); - content = styledHtml[0].text; - } else { - content = styledHtml - .map((style) => `${style.text}`) - .join(""); - } - - // return `\n${content}
`; - - const attr = new TailwindTextBuilder(node, false, false) - .blend(node) - .position(node, true); - - const splittedChars = node.characters.split("\n"); - const charsWithLineBreak = - splittedChars.length > 1 - ? node.characters.split("\n").join("
") - : node.characters; - - const black: RGB = { - r: 0, - g: 0, - b: 0, - }; - - let contrastBlack = 21; - - const fill = retrieveTopFill(node.fills); - - if (fill && fill.type === "SOLID") { - contrastBlack = calculateContrastRatio(fill.color, black); - } - - textStr.push({ - name: node.name, - attr: attr.attributes.join(" "), - full: `${charsWithLineBreak}`, - style: style(node), - contrastBlack, - }); - } - }); - - // retrieve only unique texts (attr + name) - // from https://stackoverflow.com/a/18923480/4418073 - const unique: Record = {}; - const distinct: Array = []; - textStr.forEach((x) => { - if (!unique[x.attr + x.name]) { - distinct.push(x); - unique[x.attr + x.name] = true; - } - }); - - return distinct; -}; - -const style = (node: TextNode): string => { - let comp = ""; - - if (node.fontName !== figma.mixed) { - const lowercaseStyle = node.fontName.style.toLowerCase(); - - if (lowercaseStyle.match("italic")) { - comp += "font-style: italic; "; - } - - const value = node.fontName.style - .replace("italic", "") - .replace(" ", "") - .toLowerCase(); - - const weight = convertFontWeight(value); - if (weight) { - comp += `font-weight: ${weight};`; - } - } - - if (node.fontSize !== figma.mixed) { - comp += `font-size: ${Math.min(node.fontSize, 24)};`; - } - - const color = convertColor(node.fills); - if (color) { - comp += `color: ${color};`; - } - - return comp; -}; - -function deepFlatten(arr: Array): Array { - let result: Array = []; - - arr.forEach((d) => { - if ("children" in d) { - result = result.concat(deepFlatten([...d.children])); - } else if (d.type === "TEXT") { - result.push(d); - } - }); - - return result; -} - -const convertColor = ( - fills: ReadonlyArray | PluginAPI["mixed"], -): string | undefined => { - // kind can be text, bg, border... - // [when testing] fills can be undefined - - const fill = retrieveTopFill(fills); - - if (fill && fill.type === "SOLID") { - return nearestColor(rgbTo6hex(fill.color)); - } - - return undefined; -}; - -// from https://dev.to/alvaromontoro/building-your-own-color-contrast-checker-4j7o -function calculateContrastRatio(color1: RGB, color2: RGB) { - const color1luminance = luminance(color1); - const color2luminance = luminance(color2); - - const contrast = - color1luminance > color2luminance - ? (color2luminance + 0.05) / (color1luminance + 0.05) - : (color1luminance + 0.05) / (color2luminance + 0.05); - - return 1 / contrast; -} - -function luminance(color: RGB) { - const a = [color.r * 255, color.g * 255, color.b * 255].map((v) => { - v /= 255; - return v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4; - }); - return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722; -} diff --git a/packages/backend/src/tailwind/tailwindMain.ts b/packages/backend/src/tailwind/tailwindMain.ts index b7ea7e24..10913d22 100644 --- a/packages/backend/src/tailwind/tailwindMain.ts +++ b/packages/backend/src/tailwind/tailwindMain.ts @@ -1,6 +1,5 @@ import { retrieveTopFill } from "../common/retrieveFill"; import { indentString } from "../common/indentString"; -import { tailwindVector } from "./vector"; import { TailwindTextBuilder } from "./tailwindTextBuilder"; import { TailwindDefaultBuilder } from "./tailwindDefaultBuilder"; import { tailwindAutoLayoutProps } from "./builderImpl/tailwindAutoLayout"; @@ -67,7 +66,6 @@ const tailwindWidgetGenerator = ( case "VECTOR": addWarning("VectorNodes are not supported in Tailwind"); break; - // comp += htmlAsset(node, isJsx); } }); @@ -83,14 +81,6 @@ const tailwindGroup = (node: GroupNode, isJsx: boolean = false): string => { return ""; } - const vectorIfExists = tailwindVector( - node, - localTailwindSettings.showLayerNames, - "", - isJsx, - ); - if (vectorIfExists) return vectorIfExists; - // this needs to be called after CustomNode because widthHeight depends on it const builder = new TailwindDefaultBuilder( node, diff --git a/packages/backend/src/tailwind/vector.ts b/packages/backend/src/tailwind/vector.ts deleted file mode 100644 index d73946be..00000000 --- a/packages/backend/src/tailwind/vector.ts +++ /dev/null @@ -1,124 +0,0 @@ -// import { TailwindDefaultBuilder } from "./tailwindDefaultBuilder"; - -export const tailwindVector = ( - node: FrameNode | GroupNode, - showLayerNames: boolean, - parentId: string, - isJsx: boolean, -): string => { - // TODO VECTOR - return ""; -}; - -// import { -// FrameMixin, -// FrameNode, -// GroupNode, -// } from "./../common/Mixins"; -// import { rgbTo6hex } from "./colors"; - -// // todo improve this, positioning is wrong -// // todo support for ungroup vectors. This was reused because 80% of people are going -// export const tailwindVector = ( -// group: FrameNode | GroupNode, -// isJsx: Boolean -// ) => { -// // to use Vectors in groups (like icons) - -// // if every children is a VECTOR, no children have a child -// if ( -// group.children.length === 0 || -// !group.children.every((d) => d.type === "VECTOR") -// ) { -// return ""; -// } - -// const node = group.children[0] as VectorNode; - -// const strokeOpacity = vectorOpacity(node.strokes); -// const strokeOpacityAttr = -// strokeOpacity < 1 -// ? `${isJsx ? "strokeOpacity" : "stroke-opacity"}=${ -// isJsx ? `{${strokeOpacity}}` : `"${strokeOpacity}"` -// }\n` -// : ""; - -// const strokeWidthAttr = `${isJsx ? "strokeWidth" : "stroke-width"}=${ -// isJsx ? `{${node.strokeWeight}}` : `"${node.strokeWeight}"` -// }\n`; - -// const strokeLineCapAttr = -// node.strokeCap === "ROUND" -// ? `${isJsx ? "strokeLinecap" : "stroke-linecap"}="round"\n` -// : ""; - -// const strokeLineJoinAttr = -// node.strokeJoin !== "MITER" -// ? `${ -// isJsx ? "strokeLinejoin" : "stroke-linejoin" -// }="${node.strokeJoin.toString().toLowerCase()}"\n` -// : ""; - -// const strokeAttr = -// node.strokes.length > 0 ? `stroke="#${vectorColor(node.strokes)}"\n` : ""; - -// const sizeAttr = isJsx -// ? `height={${node.height}} width={${node.width}}` -// : `height="${node.height}" width="${node.width}"`; - -// // reduce everything into a single string -// const paths = group.children.reduce( -// (acc, n) => -// acc + -// (n as VectorNode).vectorPaths.reduce((acc, d) => { -// const fillRuleAttr = -// d.windingRule !== "NONE" -// ? `${isJsx ? "fillRule" : "fill-rule"}="${d.windingRule}"\n` -// : ""; - -// return ( -// acc + -// `\n` -// ); -// }, ""), -// "" -// ); - -// return ` -// ${paths} -// `; - -// // return `
`; -// // return ` -// // -// // `; -// }; - -// const vectorColor = ( -// fills: ReadonlyArray | PluginAPI["mixed"] -// ): string => { -// // kind can be text, bg, border... -// if (fills !== figma.mixed && fills.length > 0) { -// let fill = fills[0]; -// if (fill.type === "SOLID") { -// const hex = rgbTo6hex(fill.color); -// return fill.visible ? `${hex}` : ""; -// } -// } - -// return ""; -// }; - -// const vectorOpacity = ( -// fills: ReadonlyArray | PluginAPI["mixed"] -// ): number => { -// // kind can be text, bg, border... -// if (fills !== figma.mixed && fills.length > 0) { -// let fill = fills[0]; -// if (fill.opacity !== undefined) { -// return fill.opacity; -// } -// } - -// return 1; -// }; diff --git a/packages/plugin-ui/src/PluginUI.tsx b/packages/plugin-ui/src/PluginUI.tsx index d1047090..7042ead5 100644 --- a/packages/plugin-ui/src/PluginUI.tsx +++ b/packages/plugin-ui/src/PluginUI.tsx @@ -1,29 +1,36 @@ import { useState } from "react"; -import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; -import { coldarkDark as theme } from "react-syntax-highlighter/dist/esm/styles/prism"; import copy from "copy-to-clipboard"; +import Preview from "./components/Preview"; +import GradientsPanel from "./components/GradientsPanel"; +import ColorsPanel from "./components/ColorsPanel"; +import CodePanel from "./components/CodePanel"; +import WarningIcon from "./components/WarningIcon"; import { - FrameworkTypes, + Framework, HTMLPreview, LinearGradientConversion, PluginSettings, SolidColorConversion, Warning, } from "types"; +import { + preferenceOptions, + selectPreferenceOptions, +} from "./codegenPreferenceOptions"; type PluginUIProps = { code: string; htmlPreview: HTMLPreview; warnings: Warning[]; - selectedFramework: FrameworkTypes; - setSelectedFramework: (framework: FrameworkTypes) => void; + selectedFramework: Framework; + setSelectedFramework: (framework: Framework) => void; settings: PluginSettings | null; onPreferenceChanged: (key: string, value: boolean | string) => void; colors: SolidColorConversion[]; gradients: LinearGradientConversion[]; }; -const frameworks: FrameworkTypes[] = ["HTML", "Tailwind", "Flutter", "SwiftUI"]; +const frameworks: Framework[] = ["HTML", "Tailwind", "Flutter", "SwiftUI"]; export const PluginUI = (props: PluginUIProps) => { const [isResponsiveExpanded, setIsResponsiveExpanded] = useState(false); @@ -43,7 +50,7 @@ export const PluginUI = (props: PluginUIProps) => { : "bg-neutral-100 dark:bg-neutral-700 text-neutral-700 dark:text-neutral-200 border focus:border-0 border-neutral-300 dark:border-neutral-600 rounded-md hover:bg-green-600 dark:hover:bg-green-800 dark:hover:border-green-800 hover:text-white dark:hover:text-white font-semibold shadow-sm" }`} onClick={() => { - props.setSelectedFramework(tab as FrameworkTypes); + props.setSelectedFramework(tab as Framework); }} > {tab} @@ -86,6 +93,8 @@ export const PluginUI = (props: PluginUIProps) => { @@ -112,505 +121,3 @@ export const PluginUI = (props: PluginUIProps) => {
); }; - -export const ResponsiveGrade = () => { - return ( -
- 80% responsive -
- - -
-
- ); -}; - -type LocalCodegenPreference = { - itemType: "individual_select"; - propertyName: Exclude< - keyof PluginSettings, - "framework" | "flutterGenerationMode" | "swiftUIGenerationMode" - >; - label: string; - description: string; - value?: boolean; - isDefault?: boolean; - includedLanguages?: FrameworkTypes[]; -}; - -export const preferenceOptions: LocalCodegenPreference[] = [ - { - itemType: "individual_select", - propertyName: "jsx", - label: "React (JSX)", - description: 'Render "class" attributes as "className"', - isDefault: false, - includedLanguages: ["HTML", "Tailwind"], - }, - { - itemType: "individual_select", - propertyName: "optimizeLayout", - label: "Optimize layout", - description: "Attempt to auto-layout suitable element groups", - isDefault: true, - includedLanguages: ["HTML", "Tailwind", "Flutter", "SwiftUI"], - }, - { - itemType: "individual_select", - propertyName: "showLayerNames", - label: "Layer names", - description: "Include layer names in classes", - isDefault: false, - includedLanguages: ["HTML", "Tailwind"], - }, - { - itemType: "individual_select", - propertyName: "roundTailwindValues", - label: "Round values", - description: "Round pixel values to nearest Tailwind sizes", - isDefault: false, - includedLanguages: ["Tailwind"], - }, - { - itemType: "individual_select", - propertyName: "roundTailwindColors", - label: "Round colors", - description: "Round color values to nearest Tailwind colors", - isDefault: false, - includedLanguages: ["Tailwind"], - }, - { - itemType: "individual_select", - propertyName: "customTailwindColors", - label: "Custom colors", - description: "Use color variable names as custom color names", - isDefault: false, - includedLanguages: ["Tailwind"], - }, - // Add your preferences data here -]; - -const selectPreferenceOptions: { - itemType: "select"; - propertyName: Exclude; - label: string; - options: { label: string; value: string; isDefault?: boolean }[]; - includedLanguages?: FrameworkTypes[]; -}[] = [ - { - itemType: "select", - propertyName: "flutterGenerationMode", - label: "Mode", - options: [ - { label: "Full App", value: "fullApp" }, - { label: "Widget", value: "stateless" }, - { label: "Snippet", value: "snippet" }, - ], - includedLanguages: ["Flutter"], - }, - { - itemType: "select", - propertyName: "swiftUIGenerationMode", - label: "Mode", - options: [ - { label: "Preview", value: "preview" }, - { label: "Struct", value: "struct" }, - { label: "Snippet", value: "snippet" }, - ], - includedLanguages: ["SwiftUI"], - }, -]; - -export const CodePanel = (props: { - code: string; - selectedFramework: FrameworkTypes; - settings: PluginSettings | null; - onPreferenceChanged: (key: string, value: boolean | string) => void; -}) => { - const isEmpty = props.code === ""; - const [isPressed, setIsPressed] = useState(false); - const [syntaxHovered, setSyntaxHovered] = useState(false); - - // Add your clipboard function here or any other actions - const handleButtonClick = () => { - setIsPressed(true); - setTimeout(() => setIsPressed(false), 250); - copy(props.code); - }; - - const handleButtonHover = () => setSyntaxHovered(true); - const handleButtonLeave = () => setSyntaxHovered(false); - - const selectableSettingsFiltered = selectPreferenceOptions.filter( - (preference) => - preference.includedLanguages?.includes(props.selectedFramework), - ); - - return ( -
-
-

- Code -

- {isEmpty === false && ( - - )} -
- {isEmpty === false && ( -
-
- {preferenceOptions - .filter((preference) => - preference.includedLanguages?.includes(props.selectedFramework), - ) - .map((preference) => ( - { - props.onPreferenceChanged(preference.propertyName, value); - }} - buttonClass="bg-green-100 dark:bg-black dark:ring-green-800 ring-green-500" - checkClass="bg-green-400 dark:bg-black dark:bg-green-500 dark:border-green-500 ring-green-300 border-green-400" - /> - ))} -
- {selectableSettingsFiltered.length > 0 && ( - <> -
- -
- {selectableSettingsFiltered.map((preference) => ( - <> - {/* - {preference.label} - */} - {preference.options.map((option) => ( - { - props.onPreferenceChanged( - preference.propertyName, - option.value, - ); - }} - buttonClass="bg-blue-100 dark:bg-black dark:ring-blue-800" - checkClass="bg-blue-400 dark:bg-black dark:bg-blue-500 dark:border-blue-500 ring-blue-300 border-blue-400" - /> - ))} - - ))} -
- - )} -
- )} - -
- {isEmpty ? ( -

No layer is selected. Please select a layer.

- ) : ( - - {props.code} - - )} -
-
- ); -}; - -export const ColorsPanel = (props: { - colors: SolidColorConversion[]; - onColorClick: (color: string) => void; -}) => { - const [isPressed, setIsPressed] = useState(-1); - - const handleButtonClick = (value: string, idx: number) => { - setIsPressed(idx); - setTimeout(() => setIsPressed(-1), 250); - props.onColorClick(value); - }; - - return ( -
-

- Colors -

-
- {props.colors.map((color, idx) => ( - - ))} -
-
- ); -}; - -export const GradientsPanel = (props: { - gradients: { - cssPreview: string; - exportValue: string; - }[]; - onColorClick: (color: string) => void; -}) => { - const [isPressed, setIsPressed] = useState(-1); - - const handleButtonClick = (value: string, idx: number) => { - setIsPressed(idx); - setTimeout(() => setIsPressed(-1), 250); - props.onColorClick(value); - }; - - return ( -
-

- Gradients -

-
- {props.gradients.map((gradient, idx) => ( - - ))} -
-
- ); -}; - -type SelectableToggleProps = { - onSelect: (isSelected: boolean) => void; - isSelected?: boolean; - title: string; - description?: string; - buttonClass: string; - checkClass: string; -}; - -const SelectableToggle = ({ - onSelect, - isSelected = false, - title, - description, - buttonClass, - checkClass, -}: SelectableToggleProps) => { - const handleClick = () => { - onSelect(!isSelected); - }; - - return ( - - ); -}; - -export const Preview: React.FC<{ - htmlPreview: HTMLPreview; - isResponsiveExpanded: boolean; - setIsResponsiveExpanded: (value: boolean) => void; -}> = (props) => { - const previewWidths = [45, 80, 140]; - const labels = ["sm", "md", "lg"]; - - return ( -
-
- Responsive Preview - -
-
- {previewWidths.map((targetWidth, index) => { - const targetHeight = props.isResponsiveExpanded ? 260 : 120; - const scaleFactor = Math.min( - targetWidth / props.htmlPreview.size.width, - targetHeight / props.htmlPreview.size.height, - ); - return ( -
-
-
-
- - {labels[index]} - -
- ); - })} -
-
- ); -}; - -export const viewDocumentationWebsite = () => { - return ( -
-

- Documentation -

-

- Learn how to use our Figma plugin and explore its features in detail by - visiting our documentation website. -

- - Visit Documentation Website → - -
- ); -}; - -const ExpandIcon = (props: { size: number }) => ( - - - -); - -const WarningIcon = () => ( - - - - - -); diff --git a/packages/plugin-ui/src/codegenPreferenceOptions.ts b/packages/plugin-ui/src/codegenPreferenceOptions.ts new file mode 100644 index 00000000..9caa850a --- /dev/null +++ b/packages/plugin-ui/src/codegenPreferenceOptions.ts @@ -0,0 +1,78 @@ +import { LocalCodegenPreferenceOptions, SelectPreferenceOptions } from "types"; + +export const preferenceOptions: LocalCodegenPreferenceOptions[] = [ + { + itemType: "individual_select", + propertyName: "jsx", + label: "React (JSX)", + description: 'Render "class" attributes as "className"', + isDefault: false, + includedLanguages: ["HTML", "Tailwind"], + }, + { + itemType: "individual_select", + propertyName: "optimizeLayout", + label: "Optimize layout", + description: "Attempt to auto-layout suitable element groups", + isDefault: true, + includedLanguages: ["HTML", "Tailwind", "Flutter", "SwiftUI"], + }, + { + itemType: "individual_select", + propertyName: "showLayerNames", + label: "Layer names", + description: "Include layer names in classes", + isDefault: false, + includedLanguages: ["HTML", "Tailwind"], + }, + { + itemType: "individual_select", + propertyName: "roundTailwindValues", + label: "Round values", + description: "Round pixel values to nearest Tailwind sizes", + isDefault: false, + includedLanguages: ["Tailwind"], + }, + { + itemType: "individual_select", + propertyName: "roundTailwindColors", + label: "Round colors", + description: "Round color values to nearest Tailwind colors", + isDefault: false, + includedLanguages: ["Tailwind"], + }, + { + itemType: "individual_select", + propertyName: "customTailwindColors", + label: "Custom colors", + description: "Use color variable names as custom color names", + isDefault: false, + includedLanguages: ["Tailwind"], + }, + // Add your preferences data here +]; + +export const selectPreferenceOptions: SelectPreferenceOptions[] = [ + { + itemType: "select", + propertyName: "flutterGenerationMode", + label: "Mode", + options: [ + { label: "Full App", value: "fullApp" }, + { label: "Widget", value: "stateless" }, + { label: "Snippet", value: "snippet" }, + ], + includedLanguages: ["Flutter"], + }, + { + itemType: "select", + propertyName: "swiftUIGenerationMode", + label: "Mode", + options: [ + { label: "Preview", value: "preview" }, + { label: "Struct", value: "struct" }, + { label: "Snippet", value: "snippet" }, + ], + includedLanguages: ["SwiftUI"], + }, +]; diff --git a/packages/plugin-ui/src/components/CodePanel.tsx b/packages/plugin-ui/src/components/CodePanel.tsx new file mode 100644 index 00000000..32c84b74 --- /dev/null +++ b/packages/plugin-ui/src/components/CodePanel.tsx @@ -0,0 +1,158 @@ +import { + Framework, + LocalCodegenPreferenceOptions, + PluginSettings, + SelectPreferenceOptions, +} from "types"; +import { useState } from "react"; +import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; +import { coldarkDark as theme } from "react-syntax-highlighter/dist/esm/styles/prism"; +import copy from "copy-to-clipboard"; +import SelectableToggle from "./SelectableToggle"; + +interface CodePanelProps { + code: string; + selectedFramework: Framework; + settings: PluginSettings | null; + preferenceOptions: LocalCodegenPreferenceOptions[]; + selectPreferenceOptions: SelectPreferenceOptions[]; + onPreferenceChanged: (key: string, value: boolean | string) => void; +} + +const CodePanel = (props: CodePanelProps) => { + const [isPressed, setIsPressed] = useState(false); + const [syntaxHovered, setSyntaxHovered] = useState(false); + const { + code, + preferenceOptions, + selectPreferenceOptions, + selectedFramework, + settings, + onPreferenceChanged, + } = props; + const isEmpty = code === ""; + + // Add your clipboard function here or any other actions + const handleButtonClick = () => { + setIsPressed(true); + setTimeout(() => setIsPressed(false), 250); + copy(code); + }; + + const handleButtonHover = () => setSyntaxHovered(true); + const handleButtonLeave = () => setSyntaxHovered(false); + + const selectableSettingsFiltered = selectPreferenceOptions.filter( + (preference) => + preference.includedLanguages?.includes(props.selectedFramework), + ); + + return ( +
+
+

+ Code +

+ {isEmpty === false && ( + + )} +
+ + {isEmpty === false && ( +
+
+ {preferenceOptions + .filter((preference) => + preference.includedLanguages?.includes(selectedFramework), + ) + .map((preference) => ( + { + onPreferenceChanged(preference.propertyName, value); + }} + buttonClass="bg-green-100 dark:bg-black dark:ring-green-800 ring-green-500" + checkClass="bg-green-400 dark:bg-black dark:bg-green-500 dark:border-green-500 ring-green-300 border-green-400" + /> + ))} +
+ {selectableSettingsFiltered.length > 0 && ( + <> +
+ +
+ {selectableSettingsFiltered.map((preference) => ( + <> + {preference.options.map((option) => ( + { + onPreferenceChanged( + preference.propertyName, + option.value, + ); + }} + buttonClass="bg-blue-100 dark:bg-black dark:ring-blue-800" + checkClass="bg-blue-400 dark:bg-black dark:bg-blue-500 dark:border-blue-500 ring-blue-300 border-blue-400" + /> + ))} + + ))} +
+ + )} +
+ )} + +
+ {isEmpty ? ( +

No layer is selected. Please select a layer.

+ ) : ( + + {code} + + )} +
+
+ ); +}; +export default CodePanel; diff --git a/packages/plugin-ui/src/components/ColorsPanel.tsx b/packages/plugin-ui/src/components/ColorsPanel.tsx new file mode 100644 index 00000000..ed6463b3 --- /dev/null +++ b/packages/plugin-ui/src/components/ColorsPanel.tsx @@ -0,0 +1,52 @@ +import { useState } from "react"; +import { SolidColorConversion } from "types"; + +const ColorsPanel = (props: { + colors: SolidColorConversion[]; + onColorClick: (color: string) => void; +}) => { + const [isPressed, setIsPressed] = useState(-1); + + const handleButtonClick = (value: string, idx: number) => { + setIsPressed(idx); + setTimeout(() => setIsPressed(-1), 250); + props.onColorClick(value); + }; + + return ( +
+

+ Colors +

+
+ {props.colors.map((color, idx) => ( + + ))} +
+
+ ); +}; +export default ColorsPanel; diff --git a/packages/plugin-ui/src/components/ExpandIcon.tsx b/packages/plugin-ui/src/components/ExpandIcon.tsx new file mode 100644 index 00000000..ae9f397d --- /dev/null +++ b/packages/plugin-ui/src/components/ExpandIcon.tsx @@ -0,0 +1,12 @@ +const ExpandIcon = (props: { size: number }) => ( + + + +); +export default ExpandIcon; diff --git a/packages/plugin-ui/src/components/GradientsPanel.tsx b/packages/plugin-ui/src/components/GradientsPanel.tsx new file mode 100644 index 00000000..7a1ad9fe --- /dev/null +++ b/packages/plugin-ui/src/components/GradientsPanel.tsx @@ -0,0 +1,42 @@ +import { useState } from "react"; + +const GradientsPanel = (props: { + gradients: { + cssPreview: string; + exportValue: string; + }[]; + onColorClick: (color: string) => void; +}) => { + const [isPressed, setIsPressed] = useState(-1); + + const handleButtonClick = (value: string, idx: number) => { + setIsPressed(idx); + setTimeout(() => setIsPressed(-1), 250); + props.onColorClick(value); + }; + + return ( +
+

+ Gradients +

+
+ {props.gradients.map((gradient, idx) => ( + + ))} +
+
+ ); +}; +export default GradientsPanel; diff --git a/packages/plugin-ui/src/components/Preview.tsx b/packages/plugin-ui/src/components/Preview.tsx new file mode 100644 index 00000000..832dfc0b --- /dev/null +++ b/packages/plugin-ui/src/components/Preview.tsx @@ -0,0 +1,68 @@ +import { HTMLPreview } from "types"; +import ExpandIcon from "./ExpandIcon"; + +const Preview: React.FC<{ + htmlPreview: HTMLPreview; + isResponsiveExpanded: boolean; + setIsResponsiveExpanded: (value: boolean) => void; +}> = (props) => { + const previewWidths = [45, 80, 140]; + const labels = ["sm", "md", "lg"]; + + return ( +
+
+ Responsive Preview + +
+
+ {previewWidths.map((targetWidth, index) => { + const targetHeight = props.isResponsiveExpanded ? 260 : 120; + const scaleFactor = Math.min( + targetWidth / props.htmlPreview.size.width, + targetHeight / props.htmlPreview.size.height, + ); + return ( +
+
+
+
+ + {labels[index]} + +
+ ); + })} +
+
+ ); +}; +export default Preview; diff --git a/packages/plugin-ui/src/components/SelectableToggle.tsx b/packages/plugin-ui/src/components/SelectableToggle.tsx new file mode 100644 index 00000000..ea4e4383 --- /dev/null +++ b/packages/plugin-ui/src/components/SelectableToggle.tsx @@ -0,0 +1,48 @@ +type SelectableToggleProps = { + onSelect: (isSelected: boolean) => void; + isSelected?: boolean; + title: string; + description?: string; + buttonClass: string; + checkClass: string; +}; + +const SelectableToggle = ({ + onSelect, + isSelected = false, + title, + description, + buttonClass, + checkClass, +}: SelectableToggleProps) => { + const handleClick = () => { + onSelect(!isSelected); + }; + + return ( + + ); +}; +export default SelectableToggle; diff --git a/packages/plugin-ui/src/components/WarningIcon.tsx b/packages/plugin-ui/src/components/WarningIcon.tsx new file mode 100644 index 00000000..f6a5c1eb --- /dev/null +++ b/packages/plugin-ui/src/components/WarningIcon.tsx @@ -0,0 +1,19 @@ +const WarningIcon = () => ( + + + + + +); +export default WarningIcon; diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index 64b458af..82414dc1 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -1,8 +1,8 @@ // Settings -export type FrameworkTypes = "Flutter" | "SwiftUI" | "HTML" | "Tailwind"; +export type Framework = "Flutter" | "SwiftUI" | "HTML" | "Tailwind"; export interface PluginSettings { - framework: FrameworkTypes; + framework: Framework; jsx: boolean; inlineStyle: boolean; optimizeLayout: boolean; @@ -52,6 +52,9 @@ export type ErrorMessage = Message & { error: string; }; +// Nodes +export type ParentNode = BaseNode & ChildrenMixin; + // Styles & Conversions export type LayoutMode = @@ -67,6 +70,11 @@ export type LayoutMode = | "BottomCenter" | "BottomEnd"; +export interface BoundingRect { + x: number; + y: number; +} + interface AllSides { all: number; } @@ -97,6 +105,11 @@ export interface Size { readonly height: SizeValue; } +export type StyledTextSegmentSubset = Omit< + StyledTextSegment, + "listSpacing" | "paragraphIndent" | "paragraphSpacing" | "textStyleOverrides" +>; + export type FontWeightNumber = | "100" | "200" @@ -153,3 +166,28 @@ export type SwiftUIModifier = [ string, string | SwiftUIModifier | SwiftUIModifier[], ]; + +// UI + +export interface PreferenceOptions { + itemType: string; + label: string; + propertyName: string; + includedLanguages?: Framework[]; +} +export interface SelectPreferenceOptions extends PreferenceOptions { + itemType: "select"; + propertyName: Exclude; + options: { label: string; value: string; isDefault?: boolean }[]; +} + +export interface LocalCodegenPreferenceOptions extends PreferenceOptions { + itemType: "individual_select"; + propertyName: Exclude< + keyof PluginSettings, + "framework" | "flutterGenerationMode" | "swiftUIGenerationMode" + >; + description: string; + value?: boolean; + isDefault?: boolean; +} diff --git a/tsconfig.json b/tsconfig.json index b626377b..3d054253 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,5 +7,5 @@ { "path": "./apps/plugin" }, { "path": "./apps/debug" } ], - "exclude": ["node_modules"] + "exclude": ["./../node_modules"] } From dfcf795b163de302b8562238353fc6637cb05ba0 Mon Sep 17 00:00:00 2001 From: "Mims H. Wright" Date: Wed, 8 Jan 2025 17:38:36 +0100 Subject: [PATCH 021/168] Output viewer (#154) * Added HTML output viewer to debug * Minor tweak to html preview --- apps/debug/pages/index.tsx | 62 ++++++++++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 16 deletions(-) diff --git a/apps/debug/pages/index.tsx b/apps/debug/pages/index.tsx index 4fb4b616..176790dd 100644 --- a/apps/debug/pages/index.tsx +++ b/apps/debug/pages/index.tsx @@ -49,25 +49,55 @@ export default function Web() {
-
Plugin dropdown selection (each frame a different breakpoint)
-
-
-
-
-
+
+
+
+ Plugin dropdown selection (each frame a different breakpoint) +
+
+
+
+
+
+
+ +
+
Outputs from plugin (different screen sizes)
+
+
+
+
+
+
-
Outputs from plugin (different screen sizes)
-
-
-
-
+
+
- Experiment on dark mode (invert colors on output)
+
+
+
+
+
+
-
- Experiment on dark mode (invert colors on output)
-
-
-
-
+
+

HTML Output Tester (No JSX)

+ +
+ Empty +
); From dffe8e537cc9f089d3108a623e49e98feba64c5c Mon Sep 17 00:00:00 2001 From: "Mims H. Wright" Date: Tue, 14 Jan 2025 00:13:11 +0100 Subject: [PATCH 022/168] Replaced multiple previews with one resizable preview. (#158) --- packages/plugin-ui/src/PluginUI.tsx | 7 +- packages/plugin-ui/src/components/Preview.tsx | 83 ++++++++----------- 2 files changed, 34 insertions(+), 56 deletions(-) diff --git a/packages/plugin-ui/src/PluginUI.tsx b/packages/plugin-ui/src/PluginUI.tsx index 7042ead5..bd8fefe5 100644 --- a/packages/plugin-ui/src/PluginUI.tsx +++ b/packages/plugin-ui/src/PluginUI.tsx @@ -33,7 +33,6 @@ type PluginUIProps = { const frameworks: Framework[] = ["HTML", "Tailwind", "Flutter", "SwiftUI"]; export const PluginUI = (props: PluginUIProps) => { - const [isResponsiveExpanded, setIsResponsiveExpanded] = useState(false); const isEmpty = props.code === ""; const warnings = props.warnings ?? []; @@ -67,11 +66,7 @@ export const PluginUI = (props: PluginUIProps) => {
{isEmpty === false && props.htmlPreview && ( - + )} {warnings.length > 0 && (
diff --git a/packages/plugin-ui/src/components/Preview.tsx b/packages/plugin-ui/src/components/Preview.tsx index 832dfc0b..bea697d5 100644 --- a/packages/plugin-ui/src/components/Preview.tsx +++ b/packages/plugin-ui/src/components/Preview.tsx @@ -1,66 +1,49 @@ import { HTMLPreview } from "types"; -import ExpandIcon from "./ExpandIcon"; const Preview: React.FC<{ htmlPreview: HTMLPreview; - isResponsiveExpanded: boolean; - setIsResponsiveExpanded: (value: boolean) => void; }> = (props) => { - const previewWidths = [45, 80, 140]; - const labels = ["sm", "md", "lg"]; + const targetWidth = 240; + const targetHeight = 120; + const scaleFactor = Math.min( + targetWidth / props.htmlPreview.size.width, + targetHeight / props.htmlPreview.size.height, + ); return (
Responsive Preview -
- {previewWidths.map((targetWidth, index) => { - const targetHeight = props.isResponsiveExpanded ? 260 : 120; - const scaleFactor = Math.min( - targetWidth / props.htmlPreview.size.width, - targetHeight / props.htmlPreview.size.height, - ); - return ( +
+
-
-
-
- - {labels[index]} - -
- ); - })} + style={{ + zoom: scaleFactor, + width: "100%", + height: "100%", + }} + dangerouslySetInnerHTML={{ + __html: props.htmlPreview.content, + }} + /> +
+
); From 3f64849bf6a22b138ce9dd4004c2f42a402c171d Mon Sep 17 00:00:00 2001 From: "Mims H. Wright" Date: Wed, 15 Jan 2025 05:50:32 +0100 Subject: [PATCH 023/168] Convert Vectors to SVG, take 2 (#152) * Added a reference to the original node in the clone * tweak to structure of altNode * Vectors, stars, polygons and boolean unions are exported as SVG. Groups containing only these types will all be compressed into a single SVG. * reworked isVisible * Added positioning to html rendering. This fixed layout for simple cases of SVG graphics. Super complicated example still broken. not sure why exactly. * Instead of passing around flags and storing values globally, most functions now just take the whole settings object * Made a new type for HTML conversion settings * made a new type for tailwind settings, also flutter, swift * added the div wrapper around svgs in tailwind * converted params to use settings in most cases * simplified builder * Simplified tailwind builder --- apps/plugin/plugin-src/code.ts | 4 +- .../backend/src/altNodes/altConversion.ts | 20 +- packages/backend/src/altNodes/altNodeUtils.ts | 42 ++++ packages/backend/src/code.ts | 10 +- .../src/common/commonFormatAttributes.ts | 4 +- packages/backend/src/common/nodeVisibility.ts | 4 + packages/backend/src/common/retrieveFill.ts | 2 +- .../src/common/retrieveUI/convertToCode.ts | 13 +- .../src/html/builderImpl/htmlAutoLayout.ts | 5 +- .../src/html/builderImpl/htmlBorderRadius.ts | 22 -- .../backend/src/html/builderImpl/htmlColor.ts | 2 +- .../backend/src/html/htmlDefaultBuilder.ts | 141 +++++++----- packages/backend/src/html/htmlMain.ts | 207 +++++++++--------- packages/backend/src/html/htmlTextBuilder.ts | 12 +- .../src/tailwind/tailwindDefaultBuilder.ts | 127 ++++++----- packages/backend/src/tailwind/tailwindMain.ts | 192 ++++++++-------- .../src/tailwind/tailwindTextBuilder.ts | 4 +- packages/types/src/types.ts | 33 ++- 18 files changed, 479 insertions(+), 365 deletions(-) create mode 100644 packages/backend/src/common/nodeVisibility.ts diff --git a/apps/plugin/plugin-src/code.ts b/apps/plugin/plugin-src/code.ts index b12a4d89..4491a152 100644 --- a/apps/plugin/plugin-src/code.ts +++ b/apps/plugin/plugin-src/code.ts @@ -122,7 +122,7 @@ const codegenMode = async () => { }, { title: `Text Styles`, - code: htmlCodeGenTextStyles(false), + code: htmlCodeGenTextStyles(userPluginSettings), language: "HTML", }, ]; @@ -139,7 +139,7 @@ const codegenMode = async () => { }, { title: `Text Styles`, - code: htmlCodeGenTextStyles(true), + code: htmlCodeGenTextStyles(userPluginSettings), language: "HTML", }, ]; diff --git a/packages/backend/src/altNodes/altConversion.ts b/packages/backend/src/altNodes/altConversion.ts index 63d97f95..a6c51d71 100644 --- a/packages/backend/src/altNodes/altConversion.ts +++ b/packages/backend/src/altNodes/altConversion.ts @@ -1,16 +1,23 @@ -import { StyledTextSegmentSubset, ParentNode } from "types"; +import { StyledTextSegmentSubset, ParentNode, AltNode } from "types"; import { - overrideReadonlyProperty, assignParent, isNotEmpty, assignRectangleType, assignChildren, + isTypeOrGroupOfTypes, } from "./altNodeUtils"; -import { addWarning } from "../common/commonConversionWarnings"; export let globalTextStyleSegments: Record = {}; +// List of types that can be flattened into SVG +const canBeFlattened = isTypeOrGroupOfTypes([ + "VECTOR", + "STAR", + "POLYGON", + "BOOLEAN_OPERATION", +]); + export const convertNodeToAltNode = (parent: ParentNode | null) => (node: SceneNode): SceneNode => { @@ -98,7 +105,12 @@ export const cloneNode = ( } assignParent(parent, cloned); - return cloned; + const altNode = { + ...cloned, + originalNode: node, + canBeFlattened: canBeFlattened(node), + } as AltNode; + return altNode; }; // auto convert Frame to Rectangle when Frame has no Children diff --git a/packages/backend/src/altNodes/altNodeUtils.ts b/packages/backend/src/altNodes/altNodeUtils.ts index f9180cb7..53de7f44 100644 --- a/packages/backend/src/altNodes/altNodeUtils.ts +++ b/packages/backend/src/altNodes/altNodeUtils.ts @@ -1,3 +1,4 @@ +import { AltNode } from "types"; import { curry } from "../common/curry"; export const overrideReadonlyProperty = curry( @@ -19,3 +20,44 @@ export function isNotEmpty( ): value is TValue { return value !== null && value !== undefined; } + +export const isTypeOrGroupOfTypes = curry( + (matchTypes: NodeType[], node: SceneNode): boolean => { + if (node.visible === false || matchTypes.includes(node.type)) return true; + + if ("children" in node) { + for (let i = 0; i < node.children.length; i++) { + const childNode = node.children[i]; + const result = isTypeOrGroupOfTypes(matchTypes, childNode); + if (result) continue; + // child is false + return false; + } + // all children are true + return true; + } + + // not group or vector + return false; + }, +); + +export const renderNodeAsSVG = async (node: SceneNode) => + await node.exportAsync({ format: "SVG_STRING" }); + +export const renderAndAttachSVG = async (node: SceneNode) => { + const altNode = node as AltNode; + // const nodeName = `${node.type}:${node.id}`; + // console.log(altNode); + if (altNode.canBeFlattened) { + if (altNode.svg) { + // console.log(`SVG already rendered for ${nodeName}`); + return altNode; + } + // console.log(`${nodeName} can be flattened!`); + const svg = await renderNodeAsSVG(altNode.originalNode); + // console.log(`${svg}`); + altNode.svg = svg; + } + return altNode; +}; diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index 8a2ef42a..3017aded 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -9,7 +9,7 @@ import { clearWarnings, warnings } from "./common/commonConversionWarnings"; import { PluginSettings } from "types"; import { convertToCode } from "./common/retrieveUI/convertToCode"; -export const run = (settings: PluginSettings) => { +export const run = async (settings: PluginSettings) => { clearWarnings(); const { framework } = settings; const selection = figma.currentPage.selection; @@ -23,8 +23,12 @@ export const run = (settings: PluginSettings) => { return; } - const code = convertToCode(convertedSelection, settings); - const htmlPreview = generateHTMLPreview(convertedSelection, settings, code); + const code = await convertToCode(convertedSelection, settings); + const htmlPreview = await generateHTMLPreview( + convertedSelection, + settings, + code, + ); const colors = retrieveGenericSolidUIColors(framework); const gradients = retrieveGenericGradients(framework); diff --git a/packages/backend/src/common/commonFormatAttributes.ts b/packages/backend/src/common/commonFormatAttributes.ts index 4f92d4be..d157b7ac 100644 --- a/packages/backend/src/common/commonFormatAttributes.ts +++ b/packages/backend/src/common/commonFormatAttributes.ts @@ -17,8 +17,8 @@ export const formatStyleAttribute = ( return ` style=${isJSX ? `{{${trimmedStyles}}}` : `"${trimmedStyles}"`}`; }; -export const formatLayerNameAttribute = (name: string) => - name === "" ? "" : ` data-layer="${name}"`; +export const formatDataAttribute = (label: string, value?: string) => + ` data-${label}${value === undefined ? `` : `="${value}"`}`; export const formatClassAttribute = ( classes: string[], diff --git a/packages/backend/src/common/nodeVisibility.ts b/packages/backend/src/common/nodeVisibility.ts new file mode 100644 index 00000000..8e3959d7 --- /dev/null +++ b/packages/backend/src/common/nodeVisibility.ts @@ -0,0 +1,4 @@ +type VisibilityMixin = { visible: boolean }; +const isVisible = (node: VisibilityMixin) => node.visible; +export const getVisibleNodes = (nodes: readonly SceneNode[]) => + nodes.filter(isVisible); diff --git a/packages/backend/src/common/retrieveFill.ts b/packages/backend/src/common/retrieveFill.ts index 151c00c6..233c2edb 100644 --- a/packages/backend/src/common/retrieveFill.ts +++ b/packages/backend/src/common/retrieveFill.ts @@ -2,7 +2,7 @@ * Retrieve the first visible color that is being used by the layer, in case there are more than one. */ export const retrieveTopFill = ( - fills: ReadonlyArray | PluginAPI["mixed"], + fills: ReadonlyArray | PluginAPI["mixed"] | undefined, ): Paint | undefined => { if (fills && fills !== figma.mixed && fills.length > 0) { // on Figma, the top layer is always at the last position diff --git a/packages/backend/src/common/retrieveUI/convertToCode.ts b/packages/backend/src/common/retrieveUI/convertToCode.ts index 94ef45b6..a8298be3 100644 --- a/packages/backend/src/common/retrieveUI/convertToCode.ts +++ b/packages/backend/src/common/retrieveUI/convertToCode.ts @@ -4,16 +4,19 @@ import { htmlMain } from "../../html/htmlMain"; import { swiftuiMain } from "../../swiftui/swiftuiMain"; import { tailwindMain } from "../../tailwind/tailwindMain"; -export const convertToCode = (nodes: SceneNode[], settings: PluginSettings) => { +export const convertToCode = async ( + nodes: SceneNode[], + settings: PluginSettings, +) => { switch (settings.framework) { case "Tailwind": - return tailwindMain(nodes, settings); + return await tailwindMain(nodes, settings); case "Flutter": - return flutterMain(nodes, settings); + return await flutterMain(nodes, settings); case "SwiftUI": - return swiftuiMain(nodes, settings); + return await swiftuiMain(nodes, settings); case "HTML": default: - return htmlMain(nodes, settings); + return await htmlMain(nodes, settings); } }; diff --git a/packages/backend/src/html/builderImpl/htmlAutoLayout.ts b/packages/backend/src/html/builderImpl/htmlAutoLayout.ts index 98eee1d4..5df954ef 100644 --- a/packages/backend/src/html/builderImpl/htmlAutoLayout.ts +++ b/packages/backend/src/html/builderImpl/htmlAutoLayout.ts @@ -1,3 +1,4 @@ +import { HTMLSettings, PluginSettings } from "types"; import { formatMultipleJSXArray } from "../../common/parseJSX"; const getFlexDirection = (node: InferredAutoLayoutResult): string => @@ -47,7 +48,7 @@ const getFlex = ( export const htmlAutoLayoutProps = ( node: SceneNode, autoLayout: InferredAutoLayoutResult, - isJsx: boolean, + settings: HTMLSettings, ): string[] => formatMultipleJSXArray( { @@ -57,5 +58,5 @@ export const htmlAutoLayoutProps = ( gap: getGap(autoLayout), display: getFlex(node, autoLayout), }, - isJsx, + settings.jsx, ); diff --git a/packages/backend/src/html/builderImpl/htmlBorderRadius.ts b/packages/backend/src/html/builderImpl/htmlBorderRadius.ts index 0d70930f..f2f7fc39 100644 --- a/packages/backend/src/html/builderImpl/htmlBorderRadius.ts +++ b/packages/backend/src/html/builderImpl/htmlBorderRadius.ts @@ -40,29 +40,7 @@ export const htmlBorderRadius = (node: SceneNode, isJsx: boolean): string[] => { node.children.length > 0 && node.clipsContent === true ) { - // if ( - // node.children.some( - // (child) => - // "layoutPositioning" in child && node.layoutPositioning === "AUTO" - // ) - // ) { - // if (singleCorner) { - // comp.push( - // formatWithJSX( - // "clip-path", - // isJsx, - // `inset(0px round ${singleCorner}px)` - // ) - // ); - // } else if (cornerValues.filter((d) => d > 0).length > 0) { - // const insetValues = cornerValues.map((value) => `${value}px`).join(" "); - // comp.push( - // formatWithJSX("clip-path", isJsx, `inset(0px round ${insetValues})`) - // ); - // } - // } else { comp.push(formatWithJSX("overflow", isJsx, "hidden")); - // } } return comp; diff --git a/packages/backend/src/html/builderImpl/htmlColor.ts b/packages/backend/src/html/builderImpl/htmlColor.ts index 22730646..561d3280 100644 --- a/packages/backend/src/html/builderImpl/htmlColor.ts +++ b/packages/backend/src/html/builderImpl/htmlColor.ts @@ -3,7 +3,7 @@ import { retrieveTopFill } from "../../common/retrieveFill"; // retrieve the SOLID color on HTML export const htmlColorFromFills = ( - fills: ReadonlyArray | PluginAPI["mixed"], + fills: ReadonlyArray | PluginAPI["mixed"] | undefined, ): string => { // kind can be text, bg, border... // [when testing] fills can be undefined diff --git a/packages/backend/src/html/htmlDefaultBuilder.ts b/packages/backend/src/html/htmlDefaultBuilder.ts index 3a50d9b2..6fb998a2 100644 --- a/packages/backend/src/html/htmlDefaultBuilder.ts +++ b/packages/backend/src/html/htmlDefaultBuilder.ts @@ -21,42 +21,55 @@ import { sliceNum, stringToClassName } from "../common/numToAutoFixed"; import { commonStroke } from "../common/commonStroke"; import { formatClassAttribute, - formatLayerNameAttribute, + formatDataAttribute, formatStyleAttribute, } from "../common/commonFormatAttributes"; +import { HTMLSettings } from "types"; export class HtmlDefaultBuilder { styles: Array; - isJSX: boolean; - visible: boolean; - name: string; + data: Array; + node: SceneNode; + settings: HTMLSettings; - constructor(node: SceneNode, showLayerNames: boolean, optIsJSX: boolean) { - this.isJSX = optIsJSX; + get name() { + return this.settings.showLayerNames ? this.node.name : ""; + } + get visible() { + return this.node.visible; + } + get isJSX() { + return this.settings.jsx; + } + get optimizeLayout() { + return this.settings.optimizeLayout; + } + + constructor(node: SceneNode, settings: HTMLSettings) { + this.node = node; + this.settings = settings; this.styles = []; - this.visible = node.visible; - this.name = showLayerNames ? node.name : ""; + this.data = []; } - commonPositionStyles( - node: SceneNode & LayoutMixin & MinimalBlendMixin, - optimizeLayout: boolean, - ): this { - this.size(node, optimizeLayout); - this.autoLayoutPadding(node, optimizeLayout); - this.position(node, optimizeLayout); - this.blend(node); + commonPositionStyles(): this { + this.size(); + this.autoLayoutPadding(); + this.position(); + this.blend(); return this; } - commonShapeStyles(node: GeometryMixin & SceneNode): this { - this.applyFillsToStyle( - node.fills, - node.type === "TEXT" ? "text" : "background", - ); - this.shadow(node); - this.border(node); - this.blur(node); + commonShapeStyles(): this { + if ("fills" in this.node) { + this.applyFillsToStyle( + this.node.fills, + this.node.type === "TEXT" ? "text" : "background", + ); + } + this.shadow(); + this.border(); + this.blur(); return this; } @@ -64,17 +77,19 @@ export class HtmlDefaultBuilder { this.styles.push(...newStyles.filter((style) => style)); }; - blend(node: SceneNode & LayoutMixin & MinimalBlendMixin): this { + blend(): this { + const { node, isJSX } = this; this.addStyles( - htmlVisibility(node, this.isJSX), - ...htmlRotation(node, this.isJSX), - htmlOpacity(node, this.isJSX), - htmlBlendMode(node, this.isJSX), + htmlVisibility(node, isJSX), + ...htmlRotation(node as LayoutMixin, isJSX), + htmlOpacity(node as MinimalBlendMixin, isJSX), + htmlBlendMode(node as MinimalBlendMixin, isJSX), ); return this; } - border(node: GeometryMixin & SceneNode): this { + border(): this { + const { node } = this; this.addStyles(...htmlBorderRadius(node, this.isJSX)); const commonBorder = commonStroke(node); @@ -82,8 +97,10 @@ export class HtmlDefaultBuilder { return this; } - const color = htmlColorFromFills(node.strokes); - const borderStyle = node.dashPattern.length > 0 ? "dotted" : "solid"; + const strokes = ("strokes" in node && node.strokes) || undefined; + const color = htmlColorFromFills(strokes); + const borderStyle = + "dashPattern" in node && node.dashPattern.length > 0 ? "dotted" : "solid"; const consolidateBorders = (border: number): string => [`${sliceNum(border)}px`, color, borderStyle].filter((d) => d).join(" "); @@ -137,14 +154,15 @@ export class HtmlDefaultBuilder { return this; } - position(node: SceneNode, optimizeLayout: boolean): this { + position(): this { + const { node, optimizeLayout, isJSX } = this; if (commonIsAbsolutePosition(node, optimizeLayout)) { const { x, y } = getCommonPositionValue(node); this.addStyles( - formatWithJSX("left", this.isJSX, x), - formatWithJSX("top", this.isJSX, y), - formatWithJSX("position", this.isJSX, "absolute"), + formatWithJSX("left", isJSX, x), + formatWithJSX("top", isJSX, y), + formatWithJSX("position", isJSX, "absolute"), ); } else { if ( @@ -153,7 +171,7 @@ export class HtmlDefaultBuilder { ((optimizeLayout ? node.inferredAutoLayout : null) ?? node) ?.layoutMode === "NONE") ) { - this.addStyles(formatWithJSX("position", this.isJSX, "relative")); + this.addStyles(formatWithJSX("position", isJSX, "relative")); } } @@ -210,20 +228,24 @@ export class HtmlDefaultBuilder { return styles.filter((value) => value !== "").join(", "); } - shadow(node: SceneNode): this { + shadow(): this { + const { node, isJSX } = this; if ("effects" in node) { const shadow = htmlShadow(node); if (shadow) { - this.addStyles( - formatWithJSX("box-shadow", this.isJSX, htmlShadow(node)), - ); + this.addStyles(formatWithJSX("box-shadow", isJSX, htmlShadow(node))); } } return this; } - size(node: SceneNode, optimize: boolean): this { - const { width, height } = htmlSizePartial(node, this.isJSX, optimize); + size(): this { + const { node, settings } = this; + const { width, height } = htmlSizePartial( + node, + settings.jsx, + settings.optimizeLayout, + ); if (node.type === "TEXT") { switch (node.textAutoResize) { @@ -244,19 +266,21 @@ export class HtmlDefaultBuilder { return this; } - autoLayoutPadding(node: SceneNode, optimizeLayout: boolean): this { + autoLayoutPadding(): this { + const { node, isJSX, optimizeLayout } = this; if ("paddingLeft" in node) { this.addStyles( ...htmlPadding( (optimizeLayout ? node.inferredAutoLayout : null) ?? node, - this.isJSX, + isJSX, ), ); } return this; } - blur(node: SceneNode) { + blur() { + const { node } = this; if ("effects" in node && node.effects.length > 0) { const blur = node.effects.find( (e) => e.type === "LAYER_BLUR" && e.visible, @@ -286,17 +310,28 @@ export class HtmlDefaultBuilder { } } + addData(label: string, value?: string): this { + const attribute = formatDataAttribute(label, value); + this.data.push(attribute); + return this; + } + build(additionalStyle: Array = []): string { this.addStyles(...additionalStyle); - const layerNameAttribute = formatLayerNameAttribute(this.name); - const layerNameClass = stringToClassName(this.name); - const classAttribute = formatClassAttribute( - layerNameClass === "" ? [] : [layerNameClass], - this.isJSX, - ); + let classAttribute = ""; + if (this.name) { + this.addData("layer", this.name); + const layerNameClass = stringToClassName(this.name); + classAttribute = formatClassAttribute( + layerNameClass === "" ? [] : [layerNameClass], + this.isJSX, + ); + } + + const dataAttributes = this.data.join(""); const styleAttribute = formatStyleAttribute(this.styles, this.isJSX); - return `${layerNameAttribute}${classAttribute}${styleAttribute}`; + return `${dataAttributes}${classAttribute}${styleAttribute}`; } } diff --git a/packages/backend/src/html/htmlMain.ts b/packages/backend/src/html/htmlMain.ts index 45a59a4f..92f2c026 100644 --- a/packages/backend/src/html/htmlMain.ts +++ b/packages/backend/src/html/htmlMain.ts @@ -6,28 +6,25 @@ import { htmlAutoLayoutProps } from "./builderImpl/htmlAutoLayout"; import { formatWithJSX } from "../common/parseJSX"; import { commonSortChildrenWhenInferredAutoLayout } from "../common/commonChildrenOrder"; import { addWarning } from "../common/commonConversionWarnings"; -import { PluginSettings, HTMLPreview } from "types"; - -let showLayerNames = false; +import { PluginSettings, HTMLPreview, AltNode, HTMLSettings } from "types"; +import { renderAndAttachSVG } from "../altNodes/altNodeUtils"; +import { getVisibleNodes } from "../common/nodeVisibility"; const selfClosingTags = ["img"]; export let isPreviewGlobal = false; -let localSettings: PluginSettings; let previousExecutionCache: { style: string; text: string }[]; -export const htmlMain = ( +export const htmlMain = async ( sceneNode: Array, settings: PluginSettings, isPreview: boolean = false, -): string => { - showLayerNames = settings.showLayerNames; +): Promise => { isPreviewGlobal = isPreview; previousExecutionCache = []; - localSettings = settings; - let result = htmlWidgetGenerator(sceneNode, settings.jsx); + let result = await htmlWidgetGenerator(sceneNode, settings); // remove the initial \n that is made in Container. if (result.length > 0 && result.startsWith("\n")) { @@ -37,16 +34,16 @@ export const htmlMain = ( return result; }; -export const generateHTMLPreview = ( +export const generateHTMLPreview = async ( nodes: SceneNode[], settings: PluginSettings, code?: string, -): HTMLPreview => { +): Promise => { const htmlCodeAlreadyGenerated = settings.framework === "HTML" && settings.jsx === false && code; const htmlCode = htmlCodeAlreadyGenerated ? code - : htmlMain( + : await htmlMain( nodes, { ...settings, @@ -65,52 +62,63 @@ export const generateHTMLPreview = ( }; // todo lint idea: replace BorderRadius.only(topleft: 8, topRight: 8) with BorderRadius.horizontal(8) -const htmlWidgetGenerator = ( +const htmlWidgetGenerator = async ( sceneNode: ReadonlyArray, - isJsx: boolean, -): string => { - let comp = ""; + settings: HTMLSettings, +): Promise => { // filter non visible nodes. This is necessary at this step because conversion already happened. - const visibleSceneNode = sceneNode.filter((d) => d.visible); - visibleSceneNode.forEach((node, index) => { - // if (node.isAsset || ("isMask" in node && node.isMask === true)) { - // comp += htmlAsset(node, isJsx); - // } - - switch (node.type) { - case "RECTANGLE": - case "ELLIPSE": - comp += htmlContainer(node, "", [], isJsx); - break; - case "GROUP": - comp += htmlGroup(node, isJsx); - break; - case "FRAME": - case "COMPONENT": - case "INSTANCE": - case "COMPONENT_SET": - comp += htmlFrame(node, isJsx); - break; - case "SECTION": - comp += htmlSection(node, isJsx); - break; - case "TEXT": - comp += htmlText(node, isJsx); - break; - case "LINE": - comp += htmlLine(node, isJsx); - break; - case "VECTOR": - comp += htmlAsset(node, isJsx); - addWarning("VectorNodes are not fully supported in HTML"); - break; - } - }); + const promiseOfConvertedCode = getVisibleNodes(sceneNode).map( + convertNode(settings), + ); + const code = (await Promise.all(promiseOfConvertedCode)).join(""); + return code; +}; - return comp; +const convertNode = (settings: HTMLSettings) => async (node: SceneNode) => { + const altNode = await renderAndAttachSVG(node); + if (altNode.svg) return htmlWrapSVG(altNode, settings); + + switch (node.type) { + case "RECTANGLE": + case "ELLIPSE": + return htmlContainer(node, "", [], settings); + case "GROUP": + return htmlGroup(node, settings); + case "FRAME": + case "COMPONENT": + case "INSTANCE": + case "COMPONENT_SET": + return htmlFrame(node, settings); + case "SECTION": + return htmlSection(node, settings); + case "TEXT": + return htmlText(node, settings); + case "LINE": + return htmlLine(node, settings); + case "VECTOR": + addWarning("VectorNodes are not fully supported in HTML"); + return htmlAsset(node, settings); + default: + } + return ""; }; -const htmlGroup = (node: GroupNode, isJsx: boolean = false): string => { +const htmlWrapSVG = ( + node: AltNode, + settings: HTMLSettings, +): string => { + if (node.svg === "") return ""; + const builder = new HtmlDefaultBuilder(node, settings) + .addData("svg-wrapper") + .position(); + + return `\n\n${node.svg ?? ""}
`; +}; + +const htmlGroup = async ( + node: GroupNode, + settings: HTMLSettings, +): Promise => { // ignore the view when size is zero or less // while technically it shouldn't get less than 0, due to rounding errors, // it can get to values like: -0.000004196293048153166 @@ -119,32 +127,25 @@ const htmlGroup = (node: GroupNode, isJsx: boolean = false): string => { return ""; } - // const vectorIfExists = tailwindVector(node, isJsx); - // if (vectorIfExists) return vectorIfExists; - // this needs to be called after CustomNode because widthHeight depends on it - const builder = new HtmlDefaultBuilder( - node, - showLayerNames, - isJsx, - ).commonPositionStyles(node, localSettings.optimizeLayout); + const builder = new HtmlDefaultBuilder(node, settings).commonPositionStyles(); if (builder.styles) { const attr = builder.build(); - const generator = htmlWidgetGenerator(node.children, isJsx); + const generator = await htmlWidgetGenerator(node.children, settings); return `\n${indentString(generator)}\n
`; } - return htmlWidgetGenerator(node.children, isJsx); + return await htmlWidgetGenerator(node.children, settings); }; // this was split from htmlText to help the UI part, where the style is needed (without

). -const htmlText = (node: TextNode, isJsx: boolean): string => { - let layoutBuilder = new HtmlTextBuilder(node, showLayerNames, isJsx) - .commonPositionStyles(node, localSettings.optimizeLayout) - .textAlign(node); +const htmlText = (node: TextNode, settings: HTMLSettings): string => { + let layoutBuilder = new HtmlTextBuilder(node, settings) + .commonPositionStyles() + .textAlign(); const styledHtml = layoutBuilder.getTextSegments(node.id); previousExecutionCache.push(...styledHtml); @@ -182,45 +183,42 @@ const htmlText = (node: TextNode, isJsx: boolean): string => { return `\n${content}
`; }; -const htmlFrame = ( +const htmlFrame = async ( node: SceneNode & BaseFrameMixin, - isJsx: boolean = false, -): string => { - const childrenStr = htmlWidgetGenerator( - commonSortChildrenWhenInferredAutoLayout( - node, - localSettings.optimizeLayout, - ), - isJsx, + settings: HTMLSettings, +): Promise => { + const childrenStr = await htmlWidgetGenerator( + commonSortChildrenWhenInferredAutoLayout(node, settings.optimizeLayout), + settings, ); if (node.layoutMode !== "NONE") { - const rowColumn = htmlAutoLayoutProps(node, node, isJsx); - return htmlContainer(node, childrenStr, rowColumn, isJsx); + const rowColumn = htmlAutoLayoutProps(node, node, settings); + return htmlContainer(node, childrenStr, rowColumn, settings); } else { - if (localSettings.optimizeLayout && node.inferredAutoLayout !== null) { + if (settings.optimizeLayout && node.inferredAutoLayout !== null) { const rowColumn = htmlAutoLayoutProps( node, node.inferredAutoLayout, - isJsx, + settings, ); - return htmlContainer(node, childrenStr, rowColumn, isJsx); + return htmlContainer(node, childrenStr, rowColumn, settings); } // node.layoutMode === "NONE" && node.children.length > 1 // children needs to be absolute - return htmlContainer(node, childrenStr, [], isJsx); + return htmlContainer(node, childrenStr, [], settings); } }; -const htmlAsset = (node: SceneNode, isJsx: boolean = false): string => { +const htmlAsset = (node: SceneNode, settings: HTMLSettings): string => { if (!("opacity" in node) || !("layoutAlign" in node) || !("fills" in node)) { return ""; } - const builder = new HtmlDefaultBuilder(node, showLayerNames, isJsx) - .commonPositionStyles(node, localSettings.optimizeLayout) - .commonShapeStyles(node); + const builder = new HtmlDefaultBuilder(node, settings) + .commonPositionStyles() + .commonShapeStyles(); let tag = "div"; let src = ""; @@ -250,7 +248,7 @@ const htmlContainer = ( MinimalBlendMixin, children: string, additionalStyles: string[] = [], - isJsx: boolean, + settings: HTMLSettings, ): string => { // ignore the view when size is zero or less // while technically it shouldn't get less than 0, due to rounding errors, @@ -259,9 +257,9 @@ const htmlContainer = ( return children; } - const builder = new HtmlDefaultBuilder(node, showLayerNames, isJsx) - .commonPositionStyles(node, localSettings.optimizeLayout) - .commonShapeStyles(node); + const builder = new HtmlDefaultBuilder(node, settings) + .commonPositionStyles() + .commonShapeStyles(); if (builder.styles || additionalStyles) { let tag = "div"; @@ -277,7 +275,7 @@ const htmlContainer = ( builder.addStyles( formatWithJSX( "background-image", - isJsx, + settings.jsx, `url(https://via.placeholder.com/${node.width.toFixed( 0, )}x${node.height.toFixed(0)})`, @@ -290,7 +288,7 @@ const htmlContainer = ( if (children) { return `\n<${tag}${build}${src}>${indentString(children)}\n`; - } else if (selfClosingTags.includes(tag) || isJsx) { + } else if (selfClosingTags.includes(tag) || settings.jsx) { return `\n<${tag}${build}${src} />`; } else { return `\n<${tag}${build}${src}>`; @@ -300,11 +298,14 @@ const htmlContainer = ( return children; }; -const htmlSection = (node: SectionNode, isJsx: boolean = false): string => { - const childrenStr = htmlWidgetGenerator(node.children, isJsx); - const builder = new HtmlDefaultBuilder(node, showLayerNames, isJsx) - .size(node, localSettings.optimizeLayout) - .position(node, localSettings.optimizeLayout) +const htmlSection = async ( + node: SectionNode, + settings: HTMLSettings, +): Promise => { + const childrenStr = await htmlWidgetGenerator(node.children, settings); + const builder = new HtmlDefaultBuilder(node, settings) + .size() + .position() .applyFillsToStyle(node.fills, "background"); if (childrenStr) { @@ -314,19 +315,19 @@ const htmlSection = (node: SectionNode, isJsx: boolean = false): string => { } }; -const htmlLine = (node: LineNode, isJsx: boolean): string => { - const builder = new HtmlDefaultBuilder(node, showLayerNames, isJsx) - .commonPositionStyles(node, localSettings.optimizeLayout) - .commonShapeStyles(node); +const htmlLine = (node: LineNode, settings: HTMLSettings): string => { + const builder = new HtmlDefaultBuilder(node, settings) + .commonPositionStyles() + .commonShapeStyles(); return `\n
`; }; -export const htmlCodeGenTextStyles = (isJsx: boolean) => { +export const htmlCodeGenTextStyles = (settings: HTMLSettings) => { const result = previousExecutionCache .map( (style) => - `// ${style.text}\n${style.style.split(isJsx ? "," : ";").join(";\n")}`, + `// ${style.text}\n${style.style.split(settings.jsx ? "," : ";").join(";\n")}`, ) .join("\n---\n"); diff --git a/packages/backend/src/html/htmlTextBuilder.ts b/packages/backend/src/html/htmlTextBuilder.ts index e3e6a33d..f66d256c 100644 --- a/packages/backend/src/html/htmlTextBuilder.ts +++ b/packages/backend/src/html/htmlTextBuilder.ts @@ -6,15 +6,14 @@ import { commonLetterSpacing, commonLineHeight, } from "../common/commonTextHeightSpacing"; +import { HTMLSettings } from "types"; export class HtmlTextBuilder extends HtmlDefaultBuilder { - constructor(node: TextNode, showLayerNames: boolean, optIsJSX: boolean) { - super(node, showLayerNames, optIsJSX); + constructor(node: TextNode, settings: HTMLSettings) { + super(node, settings); } - getTextSegments( - id: string, - ): { + getTextSegments(id: string): { style: string; text: string; openTypeFeatures: { [key: string]: boolean }; @@ -117,7 +116,8 @@ export class HtmlTextBuilder extends HtmlDefaultBuilder { return ""; } - textAlign(node: TextNode): this { + textAlign(): this { + const node = this.node as TextNode; // if alignHorizontal is LEFT, don't do anything because that is native // only undefined in testing diff --git a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts index 954e0f30..9160828a 100644 --- a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts +++ b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts @@ -22,10 +22,10 @@ import { } from "../common/commonPosition"; import { pxToBlur } from "./conversionTables"; import { + formatDataAttribute, getClassLabel, - formatStyleAttribute, } from "../common/commonFormatAttributes"; -import { TailwindColorType } from "types"; +import { TailwindColorType, TailwindSettings } from "types"; const isNotEmpty = (s: string) => s !== ""; const dropEmptyStrings = (strings: string[]) => strings.filter(isNotEmpty); @@ -33,23 +33,30 @@ const dropEmptyStrings = (strings: string[]) => strings.filter(isNotEmpty); export class TailwindDefaultBuilder { attributes: string[] = []; style: string; + data: string[]; styleSeparator: string = ""; - isJSX: boolean; - visible: boolean; - name: string; + node: SceneNode; + settings: TailwindSettings; - constructor(node: SceneNode, showLayerNames: boolean, optIsJSX: boolean) { - this.isJSX = optIsJSX; + get name() { + return this.settings.showLayerNames ? this.node.name : ""; + } + get visible() { + return this.node.visible; + } + get isJSX() { + return this.settings.jsx; + } + get optimizeLayout() { + return this.settings.optimizeLayout; + } + + constructor(node: SceneNode, settings: TailwindSettings) { + this.node = node; + this.settings = settings; this.styleSeparator = this.isJSX ? "," : ";"; this.style = ""; - this.visible = node.visible; - this.name = showLayerNames ? node.name : ""; - - /* - if (showLayerNames) { - this.attributes.push(className(node.name)); - } - */ + this.data = []; } addAttributes = (...newStyles: string[]) => { @@ -59,62 +66,55 @@ export class TailwindDefaultBuilder { this.attributes.unshift(...dropEmptyStrings(newStyles)); }; - blend( - node: SceneNode & SceneNodeMixin & MinimalBlendMixin & LayoutMixin, - ): this { + blend(): this { this.addAttributes( - tailwindVisibility(node), - tailwindRotation(node), - tailwindOpacity(node), - tailwindBlendMode(node), + tailwindVisibility(this.node), + tailwindRotation(this.node as LayoutMixin), + tailwindOpacity(this.node as MinimalBlendMixin), + tailwindBlendMode(this.node as MinimalBlendMixin), ); return this; } - commonPositionStyles( - node: SceneNode & - SceneNodeMixin & - BlendMixin & - LayoutMixin & - MinimalBlendMixin, - optimizeLayout: boolean, - ): this { - this.size(node, optimizeLayout); - this.autoLayoutPadding(node, optimizeLayout); - this.position(node, optimizeLayout); - this.blend(node); + commonPositionStyles(): this { + this.size(); + this.autoLayoutPadding(); + this.position(); + this.blend(); return this; } - commonShapeStyles(node: GeometryMixin & BlendMixin & SceneNode): this { - this.customColor(node.fills, "bg"); - this.radius(node); - this.shadow(node); - this.border(node); - this.blur(node); + commonShapeStyles(): this { + this.customColor((this.node as MinimalFillsMixin).fills, "bg"); + this.radius(); + this.shadow(); + this.border(); + this.blur(); return this; } - radius(node: SceneNode): this { - if (node.type === "ELLIPSE") { + radius(): this { + if (this.node.type === "ELLIPSE") { this.addAttributes("rounded-full"); } else { - this.addAttributes(tailwindBorderRadius(node)); + this.addAttributes(tailwindBorderRadius(this.node)); } return this; } - border(node: SceneNode): this { - if ("strokes" in node) { - this.addAttributes(tailwindBorderWidth(node)); - this.customColor(node.strokes, "border"); + border(): this { + if ("strokes" in this.node) { + this.addAttributes(tailwindBorderWidth(this.node)); + this.customColor(this.node.strokes, "border"); } return this; } - position(node: SceneNode, optimizeLayout: boolean): this { + position(): this { + const { node, optimizeLayout } = this; + if (commonIsAbsolutePosition(node, optimizeLayout)) { const { x, y } = getCommonPositionValue(node); @@ -172,13 +172,14 @@ export class TailwindDefaultBuilder { * https://tailwindcss.com/docs/box-shadow/ * example: shadow */ - shadow(node: BlendMixin): this { - this.addAttributes(...tailwindShadow(node)); + shadow(): this { + this.addAttributes(...tailwindShadow(this.node as BlendMixin)); return this; } // must be called before Position, because of the hasFixedSize attribute. - size(node: SceneNode, optimizeLayout: boolean): this { + size(): this { + const { node, optimizeLayout } = this; const { width, height } = tailwindSizePartial(node, optimizeLayout); if (node.type === "TEXT") { @@ -200,18 +201,20 @@ export class TailwindDefaultBuilder { return this; } - autoLayoutPadding(node: SceneNode, optimizeLayout: boolean): this { - if ("paddingLeft" in node) { + autoLayoutPadding(): this { + if ("paddingLeft" in this.node) { this.addAttributes( ...tailwindPadding( - (optimizeLayout ? node.inferredAutoLayout : null) ?? node, + (this.optimizeLayout ? this.node.inferredAutoLayout : null) ?? + this.node, ), ); } return this; } - blur(node: SceneNode) { + blur() { + const { node } = this; if ("effects" in node && node.effects.length > 0) { const blur = node.effects.find((e) => e.type === "LAYER_BLUR"); if (blur) { @@ -239,13 +242,21 @@ export class TailwindDefaultBuilder { } } + addData(label: string, value?: string): this { + const attribute = formatDataAttribute(label, value); + this.data.push(attribute); + return this; + } + build(additionalAttr = ""): string { this.addAttributes(additionalAttr); if (this.name !== "") { this.prependAttributes(stringToClassName(this.name)); } - const layerName = this.name ? ` data-layer="${this.name}"` : ""; + if (this.name) { + this.addData("layer", this.name); + } const classLabel = getClassLabel(this.isJSX); const classNames = @@ -253,12 +264,14 @@ export class TailwindDefaultBuilder { ? ` ${classLabel}="${this.attributes.join(" ")}"` : ""; const styles = this.style.length > 0 ? ` style="${this.style}"` : ""; + const dataAttributes = this.data.join(""); - return `${layerName}${classNames}${styles}`; + return `${dataAttributes}${classNames}${styles}`; } reset(): void { this.attributes = []; + this.data = []; this.style = ""; } } diff --git a/packages/backend/src/tailwind/tailwindMain.ts b/packages/backend/src/tailwind/tailwindMain.ts index 10913d22..ff3d3441 100644 --- a/packages/backend/src/tailwind/tailwindMain.ts +++ b/packages/backend/src/tailwind/tailwindMain.ts @@ -4,8 +4,10 @@ import { TailwindTextBuilder } from "./tailwindTextBuilder"; import { TailwindDefaultBuilder } from "./tailwindDefaultBuilder"; import { tailwindAutoLayoutProps } from "./builderImpl/tailwindAutoLayout"; import { commonSortChildrenWhenInferredAutoLayout } from "../common/commonChildrenOrder"; -import { PluginSettings } from "types"; +import { AltNode, PluginSettings, TailwindSettings } from "types"; import { addWarning } from "../common/commonConversionWarnings"; +import { renderAndAttachSVG } from "../altNodes/altNodeUtils"; +import { getVisibleNodes } from "../common/nodeVisibility"; export let localTailwindSettings: PluginSettings; @@ -13,14 +15,14 @@ let previousExecutionCache: { style: string; text: string }[]; const selfClosingTags = ["img"]; -export const tailwindMain = ( +export const tailwindMain = async ( sceneNode: Array, settings: PluginSettings, -): string => { +) => { localTailwindSettings = settings; previousExecutionCache = []; - let result = tailwindWidgetGenerator(sceneNode, localTailwindSettings.jsx); + let result = await tailwindWidgetGenerator(sceneNode, settings); // remove the initial \n that is made in Container. if (result.length > 0 && result.startsWith("\n")) { @@ -30,49 +32,62 @@ export const tailwindMain = ( return result; }; -// todo lint idea: replace BorderRadius.only(topleft: 8, topRight: 8) with BorderRadius.horizontal(8) -const tailwindWidgetGenerator = ( +// TODO: lint idea: replace BorderRadius.only(topleft: 8, topRight: 8) with BorderRadius.horizontal(8) +const tailwindWidgetGenerator = async ( sceneNode: ReadonlyArray, - isJsx: boolean, -): string => { - let comp = ""; - + settings: TailwindSettings, +): Promise => { // filter non visible nodes. This is necessary at this step because conversion already happened. - const visibleSceneNode = sceneNode.filter((d) => d.visible); - visibleSceneNode.forEach((node) => { - switch (node.type) { - case "RECTANGLE": - case "ELLIPSE": - comp += tailwindContainer(node, "", "", isJsx); - break; - case "GROUP": - comp += tailwindGroup(node, isJsx); - break; - case "FRAME": - case "COMPONENT": - case "INSTANCE": - case "COMPONENT_SET": - comp += tailwindFrame(node, isJsx); - break; - case "TEXT": - comp += tailwindText(node, isJsx); - break; - case "LINE": - comp += tailwindLine(node, isJsx); - break; - case "SECTION": - comp += tailwindSection(node, isJsx); - break; - case "VECTOR": - addWarning("VectorNodes are not supported in Tailwind"); - break; - } - }); + const promiseOfConvertedCode = getVisibleNodes(sceneNode).map( + convertNode(settings), + ); + const code = (await Promise.all(promiseOfConvertedCode)).join(""); + return code; +}; + +const convertNode = (settings: TailwindSettings) => async (node: SceneNode) => { + const altNode = await renderAndAttachSVG(node); + if (altNode.svg) return tailwindWrapSVG(altNode, settings); + + switch (node.type) { + case "RECTANGLE": + case "ELLIPSE": + return tailwindContainer(node, "", "", settings); + case "GROUP": + return tailwindGroup(node, settings); + case "FRAME": + case "COMPONENT": + case "INSTANCE": + case "COMPONENT_SET": + return tailwindFrame(node, settings); + case "TEXT": + return tailwindText(node, settings); + case "LINE": + return tailwindLine(node, settings); + case "SECTION": + return tailwindSection(node, settings); + case "VECTOR": + addWarning("VectorNodes are not supported in Tailwind"); + break; + default: + addWarning(`${node.type} nodes are not supported in Tailwind`); + } + return ""; +}; + +const tailwindWrapSVG = ( + node: AltNode, + settings: TailwindSettings, +): string => { + if (node.svg === "") return ""; + const builder = new TailwindDefaultBuilder(node, settings) + .addData("svg-wrapper") + .position(); - return comp; + return `\n\n${node.svg ?? ""}
`; }; -const tailwindGroup = (node: GroupNode, isJsx: boolean = false): string => { +const tailwindGroup = async (node: GroupNode, settings: TailwindSettings) => { // ignore the view when size is zero or less // while technically it shouldn't get less than 0, due to rounding errors, // it can get to values like: -0.000004196293048153166 @@ -82,34 +97,29 @@ const tailwindGroup = (node: GroupNode, isJsx: boolean = false): string => { } // this needs to be called after CustomNode because widthHeight depends on it - const builder = new TailwindDefaultBuilder( - node, - localTailwindSettings.showLayerNames, - isJsx, - ) - .blend(node) - .size(node, localTailwindSettings.optimizeLayout) - .position(node, localTailwindSettings.optimizeLayout); + const builder = new TailwindDefaultBuilder(node, settings) + .blend() + .size() + .position(); if (builder.attributes || builder.style) { const attr = builder.build(""); - const generator = tailwindWidgetGenerator(node.children, isJsx); + const generator = await tailwindWidgetGenerator(node.children, settings); return `\n${indentString(generator)}\n
`; } - return tailwindWidgetGenerator(node.children, isJsx); + return await tailwindWidgetGenerator(node.children, settings); }; -export const tailwindText = (node: TextNode, isJsx: boolean): string => { - let layoutBuilder = new TailwindTextBuilder( - node, - localTailwindSettings.showLayerNames, - isJsx, - ) - .commonPositionStyles(node, localTailwindSettings.optimizeLayout) - .textAlign(node); +export const tailwindText = ( + node: TextNode, + settings: TailwindSettings, +): string => { + let layoutBuilder = new TailwindTextBuilder(node, settings) + .commonPositionStyles() + .textAlign(); const styledHtml = layoutBuilder.getTextSegments(node.id); previousExecutionCache.push(...styledHtml); @@ -147,16 +157,16 @@ export const tailwindText = (node: TextNode, isJsx: boolean): string => { return `\n${content}
`; }; -const tailwindFrame = ( +const tailwindFrame = async ( node: FrameNode | InstanceNode | ComponentNode | ComponentSetNode, - isJsx: boolean, -): string => { - const childrenStr = tailwindWidgetGenerator( + settings: TailwindSettings, +): Promise => { + const childrenStr = await tailwindWidgetGenerator( commonSortChildrenWhenInferredAutoLayout( node, localTailwindSettings.optimizeLayout, ), - isJsx, + settings, ); // Add overflow-hidden class if clipsContent is true @@ -168,7 +178,7 @@ const tailwindFrame = ( node, childrenStr, rowColumn + clipsContentClass, - isJsx, + settings, ); } else { if ( @@ -180,13 +190,13 @@ const tailwindFrame = ( node, childrenStr, rowColumn + clipsContentClass, - isJsx, + settings, ); } // node.layoutMode === "NONE" && node.children.length > 1 // children needs to be absolute - return tailwindContainer(node, childrenStr, clipsContentClass, isJsx); + return tailwindContainer(node, childrenStr, clipsContentClass, settings); } }; @@ -201,7 +211,7 @@ export const tailwindContainer = ( MinimalBlendMixin, children: string, additionalAttr: string, - isJsx: boolean, + settings: TailwindSettings, ): string => { // ignore the view when size is zero or less // while technically it shouldn't get less than 0, due to rounding errors, @@ -210,13 +220,9 @@ export const tailwindContainer = ( return children; } - let builder = new TailwindDefaultBuilder( - node, - localTailwindSettings.showLayerNames, - isJsx, - ) - .commonPositionStyles(node, localTailwindSettings.optimizeLayout) - .commonShapeStyles(node); + let builder = new TailwindDefaultBuilder(node, settings) + .commonPositionStyles() + .commonShapeStyles(); if (builder.attributes || additionalAttr) { const build = builder.build(additionalAttr); @@ -242,7 +248,7 @@ export const tailwindContainer = ( if (children) { return `\n<${tag}${build}${src}>${indentString(children)}\n`; - } else if (selfClosingTags.includes(tag) || isJsx) { + } else if (selfClosingTags.includes(tag) || settings.jsx) { return `\n<${tag}${build}${src} />`; } else { return `\n<${tag}${build}${src}>`; @@ -252,27 +258,25 @@ export const tailwindContainer = ( return children; }; -export const tailwindLine = (node: LineNode, isJsx: boolean): string => { - const builder = new TailwindDefaultBuilder( - node, - localTailwindSettings.showLayerNames, - isJsx, - ) - .commonPositionStyles(node, localTailwindSettings.optimizeLayout) - .commonShapeStyles(node); +export const tailwindLine = ( + node: LineNode, + settings: TailwindSettings, +): string => { + const builder = new TailwindDefaultBuilder(node, settings) + .commonPositionStyles() + .commonShapeStyles(); return `\n
`; }; -export const tailwindSection = (node: SectionNode, isJsx: boolean): string => { - const childrenStr = tailwindWidgetGenerator(node.children, isJsx); - const builder = new TailwindDefaultBuilder( - node, - localTailwindSettings.showLayerNames, - isJsx, - ) - .size(node, localTailwindSettings.optimizeLayout) - .position(node, localTailwindSettings.optimizeLayout) +export const tailwindSection = async ( + node: SectionNode, + settings: TailwindSettings, +): Promise => { + const childrenStr = await tailwindWidgetGenerator(node.children, settings); + const builder = new TailwindDefaultBuilder(node, settings) + .size() + .position() .customColor(node.fills, "bg"); if (childrenStr) { diff --git a/packages/backend/src/tailwind/tailwindTextBuilder.ts b/packages/backend/src/tailwind/tailwindTextBuilder.ts index dbfc24b2..9c23e03d 100644 --- a/packages/backend/src/tailwind/tailwindTextBuilder.ts +++ b/packages/backend/src/tailwind/tailwindTextBuilder.ts @@ -170,9 +170,9 @@ export class TailwindTextBuilder extends TailwindDefaultBuilder { * https://tailwindcss.com/docs/text-align/ * example: text-justify */ - textAlign(node: TextNode): this { + textAlign(): this { // if alignHorizontal is LEFT, don't do anything because that is native - + const node = this.node as TextNode; // only undefined in testing if (node.textAlignHorizontal && node.textAlignHorizontal !== "LEFT") { // todo when node.textAutoResize === "WIDTH_AND_HEIGHT" and there is no \n in the text, this can be ignored. diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index 82414dc1..992008d5 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -1,20 +1,30 @@ // Settings export type Framework = "Flutter" | "SwiftUI" | "HTML" | "Tailwind"; - -export interface PluginSettings { - framework: Framework; +export interface HTMLSettings { jsx: boolean; - inlineStyle: boolean; optimizeLayout: boolean; showLayerNames: boolean; - responsiveRoot: boolean; - flutterGenerationMode: string; - swiftUIGenerationMode: string; +} +export interface TailwindSettings extends HTMLSettings { roundTailwindValues: boolean; roundTailwindColors: boolean; customTailwindColors: boolean; } - +export interface FlutterSettings { + flutterGenerationMode: string; +} +export interface SwiftUISettings { + swiftUIGenerationMode: string; +} +export interface PluginSettings + extends HTMLSettings, + TailwindSettings, + FlutterSettings, + SwiftUISettings { + framework: Framework; + inlineStyle: boolean; + responsiveRoot: boolean; +} // Messaging export interface ConversionData { code: string; @@ -55,6 +65,13 @@ export type ErrorMessage = Message & { // Nodes export type ParentNode = BaseNode & ChildrenMixin; +export type AltNodeMetadata = { + originalNode: T; + canBeFlattened: boolean; + svg?: string; +}; +export type AltNode = T & AltNodeMetadata; + // Styles & Conversions export type LayoutMode = From 384e7eda4d3bd5376bca5e680052742d9ba9acf4 Mon Sep 17 00:00:00 2001 From: "Mims H. Wright" Date: Wed, 22 Jan 2025 22:23:45 +0100 Subject: [PATCH 024/168] Issue 155 rotation (#164) * made rotations better but still not aligning items right * Added a warning for using nested rotations --- packages/backend/src/common/numToAutoFixed.ts | 10 +++-- packages/backend/src/common/parseJSX.ts | 6 +-- .../src/flutter/builderImpl/flutterBlend.ts | 9 +++-- .../src/flutter/builderImpl/flutterColor.ts | 19 +++++---- .../src/flutter/builderImpl/flutterPadding.ts | 19 +++++---- .../src/flutter/builderImpl/flutterShadow.ts | 13 ++++-- .../src/flutter/builderImpl/flutterSize.ts | 6 +-- .../backend/src/flutter/flutterContainer.ts | 26 ++++++------ .../backend/src/flutter/flutterTextBuilder.ts | 10 ++--- .../backend/src/html/builderImpl/htmlBlend.ts | 40 +++++++++++++++---- .../backend/src/html/builderImpl/htmlColor.ts | 10 ++--- .../backend/src/html/htmlDefaultBuilder.ts | 13 ++++-- .../src/swiftui/builderImpl/swiftuiBlend.ts | 9 +++-- .../src/swiftui/builderImpl/swiftuiBorder.ts | 12 +++--- .../src/swiftui/builderImpl/swiftuiColor.ts | 11 ++--- .../src/swiftui/builderImpl/swiftuiEffects.ts | 20 +++++----- .../src/swiftui/builderImpl/swiftuiPadding.ts | 16 ++++---- .../src/swiftui/builderImpl/swiftuiSize.ts | 6 +-- .../src/swiftui/swiftuiDefaultBuilder.ts | 4 +- packages/backend/src/swiftui/swiftuiMain.ts | 7 +++- .../backend/src/swiftui/swiftuiTextBuilder.ts | 8 ++-- .../src/tailwind/builderImpl/tailwindBlend.ts | 4 +- .../tailwind/builderImpl/tailwindBorder.ts | 2 +- .../backend/src/tailwind/conversionTables.ts | 20 +++++----- .../src/tailwind/tailwindDefaultBuilder.ts | 9 +++-- 25 files changed, 185 insertions(+), 124 deletions(-) diff --git a/packages/backend/src/common/numToAutoFixed.ts b/packages/backend/src/common/numToAutoFixed.ts index adba91db..2f10a4c7 100644 --- a/packages/backend/src/common/numToAutoFixed.ts +++ b/packages/backend/src/common/numToAutoFixed.ts @@ -1,10 +1,14 @@ import { indentStringFlutter } from "./indentString"; // this is necessary to avoid a height of 4.999999523162842. -export const sliceNum = (num: number): string => { +export const numberToFixedString = (num: number): string => { return num.toFixed(2).replace(/\.00$/, ""); }; +export const roundToNearestDecimal = (decimal: number) => (n: number) => + Math.round(n * 10 ** decimal) / 10 ** decimal; +export const roundToNearestHundreth = roundToNearestDecimal(2); + export const printPropertyIfNotDefault = ( propertyName: string, propertyValue: any, @@ -54,14 +58,14 @@ export const generateWidgetCode = ( return `${key}: [\n${indentStringFlutter(value.join(",\n"))},\n],`; } else { return `${key}: ${ - typeof value === "number" ? sliceNum(value) : value + typeof value === "number" ? numberToFixedString(value) : value },`; } }); const positionedValuesString = (positionedValues || []) .map((value) => { - return typeof value === "number" ? sliceNum(value) : value; + return typeof value === "number" ? numberToFixedString(value) : value; }) .join(", "); diff --git a/packages/backend/src/common/parseJSX.ts b/packages/backend/src/common/parseJSX.ts index dab7adab..f18fd091 100644 --- a/packages/backend/src/common/parseJSX.ts +++ b/packages/backend/src/common/parseJSX.ts @@ -1,4 +1,4 @@ -import { sliceNum } from "./numToAutoFixed"; +import { numberToFixedString } from "./numToAutoFixed"; export const formatWithJSX = ( property: string, @@ -13,9 +13,9 @@ export const formatWithJSX = ( if (typeof value === "number") { if (isJsx) { - return `${jsx_property}: ${sliceNum(value)}`; + return `${jsx_property}: ${numberToFixedString(value)}`; } else { - return `${property}: ${sliceNum(value)}px`; + return `${property}: ${numberToFixedString(value)}px`; } } else if (isJsx) { return `${jsx_property}: '${value}'`; diff --git a/packages/backend/src/flutter/builderImpl/flutterBlend.ts b/packages/backend/src/flutter/builderImpl/flutterBlend.ts index ce5892a6..92132200 100644 --- a/packages/backend/src/flutter/builderImpl/flutterBlend.ts +++ b/packages/backend/src/flutter/builderImpl/flutterBlend.ts @@ -1,4 +1,7 @@ -import { generateWidgetCode, sliceNum } from "../../common/numToAutoFixed"; +import { + generateWidgetCode, + numberToFixedString, +} from "../../common/numToAutoFixed"; /** * https://api.flutter.dev/flutter/widgets/Opacity-class.html @@ -9,7 +12,7 @@ export const flutterOpacity = ( ): string => { if (node.opacity !== undefined && node.opacity !== 1 && child !== "") { return generateWidgetCode("Opacity", { - opacity: sliceNum(node.opacity), + opacity: numberToFixedString(node.opacity), child: child, }); } @@ -43,7 +46,7 @@ export const flutterRotation = (node: LayoutMixin, child: string): string => { Math.round(node.rotation) !== 0 ) { return generateWidgetCode("Transform", { - transform: `Matrix4.identity()..translate(0.0, 0.0)..rotateZ(${sliceNum( + transform: `Matrix4.identity()..translate(0.0, 0.0)..rotateZ(${numberToFixedString( node.rotation * (-3.14159 / 180), )})`, child: child, diff --git a/packages/backend/src/flutter/builderImpl/flutterColor.ts b/packages/backend/src/flutter/builderImpl/flutterColor.ts index bbc4c4f3..813f5191 100644 --- a/packages/backend/src/flutter/builderImpl/flutterColor.ts +++ b/packages/backend/src/flutter/builderImpl/flutterColor.ts @@ -1,6 +1,9 @@ import { rgbTo8hex, gradientAngle } from "../../common/color"; import { addWarning } from "../../common/commonConversionWarnings"; -import { generateWidgetCode, sliceNum } from "../../common/numToAutoFixed"; +import { + generateWidgetCode, + numberToFixedString, +} from "../../common/numToAutoFixed"; import { retrieveTopFill } from "../../common/retrieveFill"; import { nearestValue } from "../../tailwind/conversionTables"; @@ -101,11 +104,11 @@ const flutterRadialGradient = (fill: GradientPaint): string => { .map((d) => flutterColor(d.color, d.color.a)) .join(", "); - const x = sliceNum(fill.gradientTransform[0][2]); - const y = sliceNum(fill.gradientTransform[1][2]); + const x = numberToFixedString(fill.gradientTransform[0][2]); + const y = numberToFixedString(fill.gradientTransform[1][2]); const scaleX = fill.gradientTransform[0][0]; const scaleY = fill.gradientTransform[1][1]; - const r = sliceNum(Math.sqrt(scaleX * scaleX + scaleY * scaleY)); + const r = numberToFixedString(Math.sqrt(scaleX * scaleX + scaleY * scaleY)); return generateWidgetCode("RadialGradient", { center: `Alignment(${x}, ${y})`, @@ -119,10 +122,10 @@ const flutterAngularGradient = (fill: GradientPaint): string => { .map((d) => flutterColor(d.color, d.color.a)) .join(", "); - const x = sliceNum(fill.gradientTransform[0][2]); - const y = sliceNum(fill.gradientTransform[1][2]); - const startAngle = sliceNum(-fill.gradientTransform[0][0]); - const endAngle = sliceNum(-fill.gradientTransform[0][1]); + const x = numberToFixedString(fill.gradientTransform[0][2]); + const y = numberToFixedString(fill.gradientTransform[1][2]); + const startAngle = numberToFixedString(-fill.gradientTransform[0][0]); + const endAngle = numberToFixedString(-fill.gradientTransform[0][1]); return generateWidgetCode("SweepGradient", { center: `Alignment(${x}, ${y})`, diff --git a/packages/backend/src/flutter/builderImpl/flutterPadding.ts b/packages/backend/src/flutter/builderImpl/flutterPadding.ts index d48db14f..b440d83e 100644 --- a/packages/backend/src/flutter/builderImpl/flutterPadding.ts +++ b/packages/backend/src/flutter/builderImpl/flutterPadding.ts @@ -1,7 +1,7 @@ import { generateWidgetCode, skipDefaultProperty, - sliceNum, + numberToFixedString, } from "../../common/numToAutoFixed"; import { commonPadding } from "../../common/commonPadding"; @@ -18,22 +18,25 @@ export const flutterPadding = (node: InferredAutoLayoutResult): string => { if ("all" in padding) { return skipDefaultProperty( - `const EdgeInsets.all(${sliceNum(padding.all)})`, + `const EdgeInsets.all(${numberToFixedString(padding.all)})`, "const EdgeInsets.all(0)", ); } if ("horizontal" in padding) { return generateWidgetCode("const EdgeInsets.symmetric", { - horizontal: skipDefaultProperty(sliceNum(padding.horizontal), "0"), - vertical: skipDefaultProperty(sliceNum(padding.vertical), "0"), + horizontal: skipDefaultProperty( + numberToFixedString(padding.horizontal), + "0", + ), + vertical: skipDefaultProperty(numberToFixedString(padding.vertical), "0"), }); } return generateWidgetCode("const EdgeInsets.only", { - top: skipDefaultProperty(sliceNum(padding.top), "0"), - left: skipDefaultProperty(sliceNum(padding.left), "0"), - right: skipDefaultProperty(sliceNum(padding.right), "0"), - bottom: skipDefaultProperty(sliceNum(padding.bottom), "0"), + top: skipDefaultProperty(numberToFixedString(padding.top), "0"), + left: skipDefaultProperty(numberToFixedString(padding.left), "0"), + right: skipDefaultProperty(numberToFixedString(padding.right), "0"), + bottom: skipDefaultProperty(numberToFixedString(padding.bottom), "0"), }); }; diff --git a/packages/backend/src/flutter/builderImpl/flutterShadow.ts b/packages/backend/src/flutter/builderImpl/flutterShadow.ts index c1f99a1f..04dc8824 100644 --- a/packages/backend/src/flutter/builderImpl/flutterShadow.ts +++ b/packages/backend/src/flutter/builderImpl/flutterShadow.ts @@ -1,5 +1,8 @@ import { rgbTo8hex } from "../../common/color"; -import { generateWidgetCode, sliceNum } from "../../common/numToAutoFixed"; +import { + generateWidgetCode, + numberToFixedString, +} from "../../common/numToAutoFixed"; import { indentStringFlutter } from "../../common/indentString"; // TODO Document it can't do flutter shadows. @@ -19,11 +22,13 @@ export const flutterShadow = (node: SceneNode): string => { effect.color, effect.color.a, ).toUpperCase()})`, - blurRadius: sliceNum(effect.radius), - offset: `Offset(${sliceNum(effect.offset.x)}, ${sliceNum( + blurRadius: numberToFixedString(effect.radius), + offset: `Offset(${numberToFixedString(effect.offset.x)}, ${numberToFixedString( effect.offset.y, )})`, - spreadRadius: effect.spread ? sliceNum(effect.spread) : "0", + spreadRadius: effect.spread + ? numberToFixedString(effect.spread) + : "0", }); } }); diff --git a/packages/backend/src/flutter/builderImpl/flutterSize.ts b/packages/backend/src/flutter/builderImpl/flutterSize.ts index 685bf685..0cf193e7 100644 --- a/packages/backend/src/flutter/builderImpl/flutterSize.ts +++ b/packages/backend/src/flutter/builderImpl/flutterSize.ts @@ -1,5 +1,5 @@ import { nodeSize } from "../../common/nodeWidthHeight"; -import { sliceNum } from "../../common/numToAutoFixed"; +import { numberToFixedString } from "../../common/numToAutoFixed"; // Used in tests. export const flutterSizeWH = (node: SceneNode): string => { @@ -23,7 +23,7 @@ export const flutterSize = ( // this cast will always be true, since nodeWidthHeight was called with false to relative. let propWidth = ""; if (typeof size.width === "number") { - propWidth = sliceNum(size.width); + propWidth = numberToFixedString(size.width); } else if (size.width === "fill") { // When parent is a Row, child must be Expanded. if ( @@ -39,7 +39,7 @@ export const flutterSize = ( let propHeight = ""; if (typeof size.height === "number") { - propHeight = sliceNum(size.height); + propHeight = numberToFixedString(size.height); } else if (size.height === "fill") { // When parent is a Column, child must be Expanded. if ( diff --git a/packages/backend/src/flutter/flutterContainer.ts b/packages/backend/src/flutter/flutterContainer.ts index 7e65e5f7..50df4b0e 100644 --- a/packages/backend/src/flutter/flutterContainer.ts +++ b/packages/backend/src/flutter/flutterContainer.ts @@ -10,7 +10,7 @@ import { generateWidgetCode, skipDefaultProperty, } from "../common/numToAutoFixed"; -import { sliceNum } from "../common/numToAutoFixed"; +import { numberToFixedString } from "../common/numToAutoFixed"; import { getCommonRadius } from "../common/commonRadius"; import { commonStroke } from "../common/commonStroke"; @@ -180,12 +180,12 @@ const generateStarBorder = (node: StarNode): string => { return generateWidgetCode("StarBorder", { side: generateBorderSideCode(node), - points: sliceNum(points), - innerRadiusRatio: sliceNum(innerRadiusRatio), - pointRounding: sliceNum(pointRounding), - valleyRounding: sliceNum(valleyRounding), - rotation: sliceNum(rotation), - squash: sliceNum(squash), + points: numberToFixedString(points), + innerRadiusRatio: numberToFixedString(innerRadiusRatio), + pointRounding: numberToFixedString(pointRounding), + valleyRounding: numberToFixedString(valleyRounding), + rotation: numberToFixedString(rotation), + squash: numberToFixedString(squash), }); }; @@ -219,7 +219,7 @@ const generatePolygonBorder = (node: PolygonNode): string => { return generateWidgetCode("StarBorder.polygon", { side: generateBorderSideCode(node), - sides: sliceNum(points), + sides: numberToFixedString(points), borderRadius: generateBorderRadius(node), }); }; @@ -230,24 +230,24 @@ const generateBorderRadius = (node: SceneNode): string => { if (radius.all === 0) { return ""; } - return `BorderRadius.circular(${sliceNum(radius.all)})`; + return `BorderRadius.circular(${numberToFixedString(radius.all)})`; } return generateWidgetCode("BorderRadius.only", { topLeft: skipDefaultProperty( - `Radius.circular(${sliceNum(radius.topLeft)})`, + `Radius.circular(${numberToFixedString(radius.topLeft)})`, "Radius.circular(0)", ), topRight: skipDefaultProperty( - `Radius.circular(${sliceNum(radius.topRight)})`, + `Radius.circular(${numberToFixedString(radius.topRight)})`, "Radius.circular(0)", ), bottomLeft: skipDefaultProperty( - `Radius.circular(${sliceNum(radius.bottomLeft)})`, + `Radius.circular(${numberToFixedString(radius.bottomLeft)})`, "Radius.circular(0)", ), bottomRight: skipDefaultProperty( - `Radius.circular(${sliceNum(radius.bottomRight)})`, + `Radius.circular(${numberToFixedString(radius.bottomRight)})`, "Radius.circular(0)", ), }); diff --git a/packages/backend/src/flutter/flutterTextBuilder.ts b/packages/backend/src/flutter/flutterTextBuilder.ts index b67e7eee..74759e68 100644 --- a/packages/backend/src/flutter/flutterTextBuilder.ts +++ b/packages/backend/src/flutter/flutterTextBuilder.ts @@ -1,7 +1,7 @@ import { generateWidgetCode, skipDefaultProperty, - sliceNum, + numberToFixedString, } from "./../common/numToAutoFixed"; import { FlutterDefaultBuilder } from "./flutterDefaultBuilder"; import { flutterColorFromFills } from "./builderImpl/flutterColor"; @@ -71,7 +71,7 @@ export class FlutterTextBuilder extends FlutterDefaultBuilder { return segments.map((segment) => { const color = flutterColorFromFills(segment.fills); - const fontSize = `${sliceNum(segment.fontSize)}`; + const fontSize = `${numberToFixedString(segment.fontSize)}`; const fontStyle = this.fontStyle(segment.fontName); const fontFamily = `'${segment.fontName.family}'`; const fontWeight = `FontWeight.w${segment.fontWeight}`; @@ -139,16 +139,16 @@ export class FlutterTextBuilder extends FlutterDefaultBuilder { case "AUTO": return ""; case "PIXELS": - return sliceNum(lineHeight.value / fontSize); + return numberToFixedString(lineHeight.value / fontSize); case "PERCENT": - return sliceNum(lineHeight.value / 100); + return numberToFixedString(lineHeight.value / 100); } } letterSpacing(letterSpacing: LetterSpacing, fontSize: number): string { const value = commonLetterSpacing(letterSpacing, fontSize); if (value) { - return sliceNum(value); + return numberToFixedString(value); } return ""; } diff --git a/packages/backend/src/html/builderImpl/htmlBlend.ts b/packages/backend/src/html/builderImpl/htmlBlend.ts index 15037e38..75e3b750 100644 --- a/packages/backend/src/html/builderImpl/htmlBlend.ts +++ b/packages/backend/src/html/builderImpl/htmlBlend.ts @@ -1,4 +1,6 @@ -import { sliceNum } from "../../common/numToAutoFixed"; +import { roundToNearestHundreth } from "./../../common/numToAutoFixed"; +import { addWarning } from "../../common/commonConversionWarnings"; +import { numberToFixedString } from "../../common/numToAutoFixed"; import { formatWithJSX } from "../../common/parseJSX"; /** @@ -15,9 +17,9 @@ export const htmlOpacity = ( if (node.opacity !== undefined && node.opacity !== 1) { // formatWithJSX is not called here because opacity unit doesn't end in px. if (isJsx) { - return `opacity: ${sliceNum(node.opacity)}`; + return `opacity: ${numberToFixedString(node.opacity)}`; } else { - return `opacity: ${sliceNum(node.opacity)}`; + return `opacity: ${numberToFixedString(node.opacity)}`; } } return ""; @@ -109,16 +111,38 @@ export const htmlVisibility = ( * if rotation was changed, let it be perceived. Therefore, 1 => 45 */ export const htmlRotation = (node: LayoutMixin, isJsx: boolean): string[] => { - // that's how you convert angles to clockwise radians: angle * -pi/180 - // using 3.14159 as Pi for enough precision and to avoid importing math lib. - if (node.rotation !== undefined && Math.round(node.rotation) !== 0) { + // For some reason, a group with rotation also has rotated nodes. + // - group 1 - rotation 45° + // - child 1 - rotation 45° + // + // if the child is also rotated 45° the effect is doubled + // - group 1 - rotation 45° + // - child 1 - rotation 90° + // + // because of this, we subtract the rotation of the parent from the children. + const parent = + "parent" in node && node.parent ? (node.parent as LayoutMixin) : null; + const parentRotation: number = + parent && "rotation" in parent ? parent.rotation : 0; + const rotation: number = Math.round(parentRotation - node.rotation) ?? 0; + + if ( + roundToNearestHundreth(parentRotation) !== 0 && + roundToNearestHundreth(rotation) !== 0 + ) { + addWarning( + "Rotated elements within rotated containers are not currently supported.", + ); + } + + if (rotation !== 0) { return [ formatWithJSX( "transform", isJsx, - `rotate(${sliceNum(-node.rotation)}deg)`, + `rotate(${numberToFixedString(rotation)}deg)`, ), - formatWithJSX("transform-origin", isJsx, "0 0"), + formatWithJSX("transform-origin", isJsx, "top left"), ]; } return []; diff --git a/packages/backend/src/html/builderImpl/htmlColor.ts b/packages/backend/src/html/builderImpl/htmlColor.ts index 561d3280..2066cc64 100644 --- a/packages/backend/src/html/builderImpl/htmlColor.ts +++ b/packages/backend/src/html/builderImpl/htmlColor.ts @@ -1,4 +1,4 @@ -import { sliceNum } from "../../common/numToAutoFixed"; +import { numberToFixedString } from "../../common/numToAutoFixed"; import { retrieveTopFill } from "../../common/retrieveFill"; // retrieve the SOLID color on HTML @@ -47,10 +47,10 @@ export const htmlColor = (color: RGB, alpha: number = 1): string => { return `#${toHex(r)}${toHex(g)}${toHex(b)}`.toUpperCase(); } - const r = sliceNum(color.r * 255); - const g = sliceNum(color.g * 255); - const b = sliceNum(color.b * 255); - const a = sliceNum(alpha); + const r = numberToFixedString(color.r * 255); + const g = numberToFixedString(color.g * 255); + const b = numberToFixedString(color.b * 255); + const a = numberToFixedString(alpha); return `rgba(${r}, ${g}, ${b}, ${a})`; }; diff --git a/packages/backend/src/html/htmlDefaultBuilder.ts b/packages/backend/src/html/htmlDefaultBuilder.ts index 6fb998a2..7a48b0bd 100644 --- a/packages/backend/src/html/htmlDefaultBuilder.ts +++ b/packages/backend/src/html/htmlDefaultBuilder.ts @@ -17,7 +17,10 @@ import { commonIsAbsolutePosition, getCommonPositionValue, } from "../common/commonPosition"; -import { sliceNum, stringToClassName } from "../common/numToAutoFixed"; +import { + numberToFixedString, + stringToClassName, +} from "../common/numToAutoFixed"; import { commonStroke } from "../common/commonStroke"; import { formatClassAttribute, @@ -103,7 +106,9 @@ export class HtmlDefaultBuilder { "dashPattern" in node && node.dashPattern.length > 0 ? "dotted" : "solid"; const consolidateBorders = (border: number): string => - [`${sliceNum(border)}px`, color, borderStyle].filter((d) => d).join(" "); + [`${numberToFixedString(border)}px`, color, borderStyle] + .filter((d) => d) + .join(" "); if ("all" in commonBorder) { if (commonBorder.all === 0) { @@ -290,7 +295,7 @@ export class HtmlDefaultBuilder { formatWithJSX( "filter", this.isJSX, - `blur(${sliceNum(blur.radius)}px)`, + `blur(${numberToFixedString(blur.radius)}px)`, ), ); } @@ -303,7 +308,7 @@ export class HtmlDefaultBuilder { formatWithJSX( "backdrop-filter", this.isJSX, - `blur(${sliceNum(backgroundBlur.radius)}px)`, + `blur(${numberToFixedString(backgroundBlur.radius)}px)`, ), ); } diff --git a/packages/backend/src/swiftui/builderImpl/swiftuiBlend.ts b/packages/backend/src/swiftui/builderImpl/swiftuiBlend.ts index 2fb1b310..2a67ff0e 100644 --- a/packages/backend/src/swiftui/builderImpl/swiftuiBlend.ts +++ b/packages/backend/src/swiftui/builderImpl/swiftuiBlend.ts @@ -1,5 +1,5 @@ import { SwiftUIModifier } from "types"; -import { sliceNum } from "../../common/numToAutoFixed"; +import { numberToFixedString } from "../../common/numToAutoFixed"; /** * https://developer.apple.com/documentation/swiftui/view/opacity(_:) @@ -8,7 +8,7 @@ export const swiftuiOpacity = ( node: MinimalBlendMixin, ): SwiftUIModifier | null => { if (node.opacity !== undefined && node.opacity !== 1) { - return ["opacity", sliceNum(node.opacity)]; + return ["opacity", numberToFixedString(node.opacity)]; } return null; }; @@ -31,7 +31,10 @@ export const swiftuiVisibility = ( */ export const swiftuiRotation = (node: LayoutMixin): SwiftUIModifier | null => { if (node.rotation !== undefined && Math.round(node.rotation) !== 0) { - return ["rotationEffect", `.degrees(${sliceNum(node.rotation)})`]; + return [ + "rotationEffect", + `.degrees(${numberToFixedString(node.rotation)})`, + ]; } return null; }; diff --git a/packages/backend/src/swiftui/builderImpl/swiftuiBorder.ts b/packages/backend/src/swiftui/builderImpl/swiftuiBorder.ts index fc44deab..404f41e4 100644 --- a/packages/backend/src/swiftui/builderImpl/swiftuiBorder.ts +++ b/packages/backend/src/swiftui/builderImpl/swiftuiBorder.ts @@ -1,6 +1,6 @@ import { commonStroke } from "./../../common/commonStroke"; import { getCommonRadius } from "../../common/commonRadius"; -import { sliceNum } from "../../common/numToAutoFixed"; +import { numberToFixedString } from "../../common/numToAutoFixed"; import { swiftUISolidColor } from "./swiftuiColor"; import { SwiftUIElement } from "./swiftuiParser"; import { SwiftUIModifier } from "types"; @@ -50,7 +50,7 @@ export const swiftuiBorder = (node: SceneNode): string[] | null => { const strokeModifier: SwiftUIModifier = [ "stroke", - `${strokeColor}, lineWidth: ${sliceNum(width)}`, + `${strokeColor}, lineWidth: ${numberToFixedString(width)}`, ]; if (strokeColor) { @@ -84,9 +84,9 @@ const strokeInset = ( ): [string, string | null] => { switch (node.strokeAlign) { case "INSIDE": - return ["inset", `by: ${sliceNum(width)}`]; + return ["inset", `by: ${numberToFixedString(width)}`]; case "OUTSIDE": - return ["inset", `by: -${sliceNum(width)}`]; + return ["inset", `by: -${numberToFixedString(width)}`]; case "CENTER": return ["inset", null]; } @@ -104,7 +104,7 @@ export const swiftuiCornerRadius = (node: SceneNode): string => { const radius = getCommonRadius(node); if ("all" in radius) { if (radius.all > 0) { - return sliceNum(radius.all); + return numberToFixedString(radius.all); } else { return ""; } @@ -119,7 +119,7 @@ export const swiftuiCornerRadius = (node: SceneNode): string => { ); if (maxBorder > 0) { - return sliceNum(maxBorder); + return numberToFixedString(maxBorder); } return ""; diff --git a/packages/backend/src/swiftui/builderImpl/swiftuiColor.ts b/packages/backend/src/swiftui/builderImpl/swiftuiColor.ts index 90e49090..c7bb7b8a 100644 --- a/packages/backend/src/swiftui/builderImpl/swiftuiColor.ts +++ b/packages/backend/src/swiftui/builderImpl/swiftuiColor.ts @@ -1,7 +1,7 @@ import { retrieveTopFill } from "../../common/retrieveFill"; import { gradientAngle } from "../../common/color"; import { nearestValue } from "../../tailwind/conversionTables"; -import { sliceNum } from "../../common/numToAutoFixed"; +import { numberToFixedString } from "../../common/numToAutoFixed"; import { addWarning } from "../../common/commonConversionWarnings"; export const swiftUISolidColor = (fill: Paint): string => { @@ -116,11 +116,12 @@ export const swiftuiColor = (color: RGB, opacity: number): string => { return ".white"; } - const r = `red: ${sliceNum(color.r)}`; - const g = `green: ${sliceNum(color.g)}`; - const b = `blue: ${sliceNum(color.b)}`; + const r = `red: ${numberToFixedString(color.r)}`; + const g = `green: ${numberToFixedString(color.g)}`; + const b = `blue: ${numberToFixedString(color.b)}`; - const opacityAttr = opacity !== 1.0 ? `.opacity(${sliceNum(opacity)})` : ""; + const opacityAttr = + opacity !== 1.0 ? `.opacity(${numberToFixedString(opacity)})` : ""; return `Color(${r}, ${g}, ${b})${opacityAttr}`; }; diff --git a/packages/backend/src/swiftui/builderImpl/swiftuiEffects.ts b/packages/backend/src/swiftui/builderImpl/swiftuiEffects.ts index 536587be..4165e48f 100644 --- a/packages/backend/src/swiftui/builderImpl/swiftuiEffects.ts +++ b/packages/backend/src/swiftui/builderImpl/swiftuiEffects.ts @@ -1,4 +1,4 @@ -import { sliceNum } from "../../common/numToAutoFixed"; +import { numberToFixedString } from "../../common/numToAutoFixed"; import { SwiftUIModifier } from "types"; export const swiftuiShadow = (node: SceneNode): SwiftUIModifier | null => { @@ -20,15 +20,17 @@ export const swiftuiShadow = (node: SceneNode): SwiftUIModifier | null => { const color = shadow.color; // set color when not black with 0.25 of opacity, which is the Figma default. Round the alpha now to avoid rounding issues. - const a = sliceNum(color.a); - const r = sliceNum(color.r); - const g = sliceNum(color.g); - const b = sliceNum(color.b); + const a = numberToFixedString(color.a); + const r = numberToFixedString(color.r); + const g = numberToFixedString(color.g); + const b = numberToFixedString(color.b); comp.push(`color: Color(red: ${r}, green: ${g}, blue: ${b}, opacity: ${a})`); - comp.push(`radius: ${sliceNum(shadow.radius)}`); + comp.push(`radius: ${numberToFixedString(shadow.radius)}`); - const x = shadow.offset.x > 0 ? `x: ${sliceNum(shadow.offset.x)}` : ""; - const y = shadow.offset.y > 0 ? `y: ${sliceNum(shadow.offset.y)}` : ""; + const x = + shadow.offset.x > 0 ? `x: ${numberToFixedString(shadow.offset.x)}` : ""; + const y = + shadow.offset.y > 0 ? `y: ${numberToFixedString(shadow.offset.y)}` : ""; // add initial comma since this is an optional paramater and radius must come first. if (x && y) { @@ -59,5 +61,5 @@ export const swiftuiBlur = (node: SceneNode): SwiftUIModifier | null => { // retrieve first blur. const blur = layerBlur[0].radius; - return ["blur", `radius: ${sliceNum(blur)})`]; + return ["blur", `radius: ${numberToFixedString(blur)})`]; }; diff --git a/packages/backend/src/swiftui/builderImpl/swiftuiPadding.ts b/packages/backend/src/swiftui/builderImpl/swiftuiPadding.ts index 83bb434f..35eedc74 100644 --- a/packages/backend/src/swiftui/builderImpl/swiftuiPadding.ts +++ b/packages/backend/src/swiftui/builderImpl/swiftuiPadding.ts @@ -1,4 +1,4 @@ -import { sliceNum } from "../../common/numToAutoFixed"; +import { numberToFixedString } from "../../common/numToAutoFixed"; import { commonPadding } from "../../common/commonPadding"; import { SwiftUIModifier } from "types"; @@ -18,22 +18,22 @@ export const swiftuiPadding = ( if (padding.all === 0) { return null; } - return ["padding", sliceNum(padding.all)]; + return ["padding", numberToFixedString(padding.all)]; } if ("horizontal" in padding) { - const vertical = sliceNum(padding.vertical); - const horizontal = sliceNum(padding.horizontal); + const vertical = numberToFixedString(padding.vertical); + const horizontal = numberToFixedString(padding.horizontal); return [ "padding", `EdgeInsets(top: ${vertical}, leading: ${horizontal}, bottom: ${vertical}, trailing: ${horizontal})`, ]; } - const top = sliceNum(padding.top); - const left = sliceNum(padding.left); - const bottom = sliceNum(padding.bottom); - const right = sliceNum(padding.right); + const top = numberToFixedString(padding.top); + const left = numberToFixedString(padding.left); + const bottom = numberToFixedString(padding.bottom); + const right = numberToFixedString(padding.right); return [ "padding", `EdgeInsets(top: ${top}, leading: ${left}, bottom: ${bottom}, trailing: ${right})`, diff --git a/packages/backend/src/swiftui/builderImpl/swiftuiSize.ts b/packages/backend/src/swiftui/builderImpl/swiftuiSize.ts index febe164e..c6f1d646 100644 --- a/packages/backend/src/swiftui/builderImpl/swiftuiSize.ts +++ b/packages/backend/src/swiftui/builderImpl/swiftuiSize.ts @@ -1,5 +1,5 @@ import { nodeSize } from "../../common/nodeWidthHeight"; -import { sliceNum } from "../../common/numToAutoFixed"; +import { numberToFixedString } from "../../common/numToAutoFixed"; export const swiftuiSize = ( node: SceneNode, @@ -13,7 +13,7 @@ export const swiftuiSize = ( // this cast will always be true, since nodeWidthHeight was called with false to relative. let propWidth = ""; if (typeof size.width === "number") { - const w = sliceNum(size.width); + const w = numberToFixedString(size.width); if (shouldExtend) { propWidth = `minWidth: ${w}, maxWidth: ${w}`; @@ -26,7 +26,7 @@ export const swiftuiSize = ( let propHeight = ""; if (typeof size.height === "number") { - const h = sliceNum(size.height); + const h = numberToFixedString(size.height); if (shouldExtend) { propHeight = `minHeight: ${h}, maxHeight: ${h}`; diff --git a/packages/backend/src/swiftui/swiftuiDefaultBuilder.ts b/packages/backend/src/swiftui/swiftuiDefaultBuilder.ts index eb5eb0ae..2e629073 100644 --- a/packages/backend/src/swiftui/swiftuiDefaultBuilder.ts +++ b/packages/backend/src/swiftui/swiftuiDefaultBuilder.ts @@ -1,4 +1,4 @@ -import { sliceNum } from "./../common/numToAutoFixed"; +import { numberToFixedString } from "./../common/numToAutoFixed"; import { swiftuiBlur, swiftuiShadow } from "./builderImpl/swiftuiEffects"; import { swiftuiBorder, @@ -87,7 +87,7 @@ export class SwiftuiDefaultBuilder { this.pushModifier([ `offset`, - `x: ${sliceNum(centerX)}, y: ${sliceNum(centerY)}`, + `x: ${numberToFixedString(centerX)}, y: ${numberToFixedString(centerY)}`, ]); } return this; diff --git a/packages/backend/src/swiftui/swiftuiMain.ts b/packages/backend/src/swiftui/swiftuiMain.ts index 91db1866..3aab3d87 100644 --- a/packages/backend/src/swiftui/swiftuiMain.ts +++ b/packages/backend/src/swiftui/swiftuiMain.ts @@ -1,5 +1,8 @@ import { indentString } from "../common/indentString"; -import { stringToClassName, sliceNum } from "../common/numToAutoFixed"; +import { + stringToClassName, + numberToFixedString, +} from "../common/numToAutoFixed"; import { SwiftuiTextBuilder } from "./swiftuiTextBuilder"; import { SwiftuiDefaultBuilder } from "./swiftuiDefaultBuilder"; import { commonSortChildrenWhenInferredAutoLayout } from "../common/commonChildrenOrder"; @@ -222,7 +225,7 @@ export const generateSwiftViewCode = ( .filter(([, value]) => value !== "") .map( ([key, value]) => - `${key}: ${typeof value === "number" ? sliceNum(value) : value}`, + `${key}: ${typeof value === "number" ? numberToFixedString(value) : value}`, ); const compactPropertiesArray = propertiesArray.join(", "); diff --git a/packages/backend/src/swiftui/swiftuiTextBuilder.ts b/packages/backend/src/swiftui/swiftuiTextBuilder.ts index 38dcedb3..f5a805dc 100644 --- a/packages/backend/src/swiftui/swiftuiTextBuilder.ts +++ b/packages/backend/src/swiftui/swiftuiTextBuilder.ts @@ -1,4 +1,4 @@ -import { sliceNum } from "../common/numToAutoFixed"; +import { numberToFixedString } from "../common/numToAutoFixed"; import { commonLetterSpacing, commonLineHeight, @@ -109,7 +109,7 @@ export class SwiftuiTextBuilder extends SwiftuiDefaultBuilder { const segment = segments[0]; // return segments.map((segment) => { - const fontSize = sliceNum(segment.fontSize); + const fontSize = numberToFixedString(segment.fontSize); const fontFamily = segment.fontName.family; const fontWeight = this.fontWeight(segment.fontWeight); const lineHeight = this.lineHeight(segment.lineHeight, segment.fontSize); @@ -150,7 +150,7 @@ export class SwiftuiTextBuilder extends SwiftuiDefaultBuilder { ): string | null => { const value = commonLetterSpacing(letterSpacing, fontSize); if (value > 0) { - return sliceNum(value); + return numberToFixedString(value); } return null; }; @@ -160,7 +160,7 @@ export class SwiftuiTextBuilder extends SwiftuiDefaultBuilder { lineHeight = (lineHeight: LineHeight, fontSize: number): string | null => { const value = commonLineHeight(lineHeight, fontSize); if (value > 0) { - return sliceNum(value); + return numberToFixedString(value); } return null; }; diff --git a/packages/backend/src/tailwind/builderImpl/tailwindBlend.ts b/packages/backend/src/tailwind/builderImpl/tailwindBlend.ts index 5687a702..8ad679e8 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindBlend.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindBlend.ts @@ -1,4 +1,4 @@ -import { sliceNum } from "../../common/numToAutoFixed"; +import { numberToFixedString } from "../../common/numToAutoFixed"; import { exactValue, nearestOpacity, nearestValue } from "../conversionTables"; /** @@ -93,7 +93,7 @@ export const tailwindRotation = (node: LayoutMixin): string => { return `origin-top-left ${minusIfNegative}rotate-${nearest}`; } else { - return `origin-top-left rotate-[${sliceNum(-node.rotation)}deg]`; + return `origin-top-left rotate-[${numberToFixedString(-node.rotation)}deg]`; } } return ""; diff --git a/packages/backend/src/tailwind/builderImpl/tailwindBorder.ts b/packages/backend/src/tailwind/builderImpl/tailwindBorder.ts index 2e402d75..7cb57c6d 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindBorder.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindBorder.ts @@ -1,6 +1,6 @@ import { getCommonRadius } from "../../common/commonRadius"; import { commonStroke } from "../../common/commonStroke"; -import { sliceNum } from "../../common/numToAutoFixed"; +import { numberToFixedString } from "../../common/numToAutoFixed"; import { nearestValue, pxToBorderRadius } from "../conversionTables"; /** diff --git a/packages/backend/src/tailwind/conversionTables.ts b/packages/backend/src/tailwind/conversionTables.ts index ba814e8d..943761d5 100644 --- a/packages/backend/src/tailwind/conversionTables.ts +++ b/packages/backend/src/tailwind/conversionTables.ts @@ -1,5 +1,5 @@ import { nearestColorFrom } from "../nearest-color/nearestColor"; -import { sliceNum } from "../common/numToAutoFixed"; +import { numberToFixedString } from "../common/numToAutoFixed"; import { localTailwindSettings } from "./tailwindMain"; import { config } from "./tailwindConfig"; import { rgbTo6hex } from "../common/color"; @@ -44,7 +44,7 @@ const pxToRemToTailwind = ( return conversionMap[nearestValue(value / 16, keys)]; } - return `[${sliceNum(value)}px]`; + return `[${numberToFixedString(value)}px]`; }; const pxToTailwind = ( @@ -60,7 +60,7 @@ const pxToTailwind = ( return conversionMap[nearestValue(value, keys)]; } - return `[${sliceNum(value)}px]`; + return `[${numberToFixedString(value)}px]`; }; export const pxToLetterSpacing = (value: number): string => { @@ -89,7 +89,7 @@ export const pxToLayoutSize = (value: number): string => { return tailwindValue; } - return `[${sliceNum(value)}px]`; + return `[${numberToFixedString(value)}px]`; }; export const nearestOpacity = (nodeOpacity: number): number => { @@ -115,8 +115,10 @@ export const nearestColorFromRgb = (color: RGB) => { export const variableToColorName = (alias: VariableAlias) => { return ( - figma.variables.getVariableById(alias.id)?.name.replaceAll("/", "-").replaceAll(" ", "-") || - alias.id.toLowerCase().replaceAll(":", "-") + figma.variables + .getVariableById(alias.id) + ?.name.replaceAll("/", "-") + .replaceAll(" ", "-") || alias.id.toLowerCase().replaceAll(":", "-") ); }; @@ -138,16 +140,16 @@ export function getColorInfo(fill: SolidPaint | ColorStop) { colorType: "tailwind", colorName: "black", hex: "#000000", - meta: '' + meta: "", }; } if (fill.color.r === 1 && fill.color.g === 1 && fill.color.b === 1) { return { - colorType: "tailwind", + colorType: "tailwind", colorName: "white", hex: "#ffffff", - meta: '' + meta: "", }; } diff --git a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts index 9160828a..e271b63d 100644 --- a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts +++ b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts @@ -1,4 +1,7 @@ -import { stringToClassName, sliceNum } from "./../common/numToAutoFixed"; +import { + stringToClassName, + numberToFixedString, +} from "./../common/numToAutoFixed"; import { tailwindShadow } from "./builderImpl/tailwindShadow"; import { tailwindVisibility, @@ -118,8 +121,8 @@ export class TailwindDefaultBuilder { if (commonIsAbsolutePosition(node, optimizeLayout)) { const { x, y } = getCommonPositionValue(node); - const parsedX = sliceNum(x); - const parsedY = sliceNum(y); + const parsedX = numberToFixedString(x); + const parsedY = numberToFixedString(y); if (parsedX === "0") { this.addAttributes(`left-0`); } else { From 68e5fc06c1639de328917e1d3e2aeb0ad0b80da0 Mon Sep 17 00:00:00 2001 From: "Mims H. Wright" Date: Wed, 22 Jan 2025 22:24:08 +0100 Subject: [PATCH 025/168] adds a warning about ungrouped elements (#162) --- packages/backend/src/code.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index 3017aded..adc98ee4 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -5,7 +5,11 @@ import { } from "./common/retrieveUI/retrieveColors"; import { generateHTMLPreview, htmlMain } from "./html/htmlMain"; import { postConversionComplete, postEmptyMessage } from "./messaging"; -import { clearWarnings, warnings } from "./common/commonConversionWarnings"; +import { + addWarning, + clearWarnings, + warnings, +} from "./common/commonConversionWarnings"; import { PluginSettings } from "types"; import { convertToCode } from "./common/retrieveUI/convertToCode"; @@ -14,6 +18,12 @@ export const run = async (settings: PluginSettings) => { const { framework } = settings; const selection = figma.currentPage.selection; + if (selection.length > 1) { + addWarning( + "Ungrouped elements may have incorrect positioning. If this happens, try wrapping the selection in a Frame or Group.", + ); + } + const convertedSelection = convertNodesToAltNodes(selection, null); // ignore when nothing was selected From 6124a86da0dfcd2598bd43192bf4aed27b1d0d82 Mon Sep 17 00:00:00 2001 From: Mario Date: Fri, 7 Feb 2025 20:18:40 +0100 Subject: [PATCH 026/168] Shadow and blur effects support for text layers, Tailwind prefix support and new placeholder service (#166) * Update placeholder image URLs to use placehold.co and enhance gradient warning messages * Add support for text shadow and layer blur effects across all text builders (HTML, Tailwind, Flutter and SwiftUI) * Add custom prefix support for Tailwind classes in code generation --- apps/plugin/plugin-src/code.ts | 32 ++++----- manifest.json | 2 +- .../src/flutter/builderImpl/flutterColor.ts | 2 +- packages/backend/src/flutter/flutterMain.ts | 2 +- .../backend/src/flutter/flutterTextBuilder.ts | 66 +++++++++++++++++++ packages/backend/src/html/htmlMain.ts | 6 +- packages/backend/src/html/htmlTextBuilder.ts | 62 ++++++++++++++++- .../src/swiftui/builderImpl/swiftuiColor.ts | 2 +- .../backend/src/swiftui/swiftuiTextBuilder.ts | 51 ++++++++++++++ .../src/tailwind/builderImpl/tailwindColor.ts | 21 ++++-- packages/backend/src/tailwind/tailwindMain.ts | 4 +- .../src/tailwind/tailwindTextBuilder.ts | 55 ++++++++++++++++ .../plugin-ui/src/components/CodePanel.tsx | 55 ++++++++++++++-- packages/types/src/types.ts | 1 + 14 files changed, 325 insertions(+), 36 deletions(-) diff --git a/apps/plugin/plugin-src/code.ts b/apps/plugin/plugin-src/code.ts index 4491a152..c2b79789 100644 --- a/apps/plugin/plugin-src/code.ts +++ b/apps/plugin/plugin-src/code.ts @@ -105,15 +105,15 @@ const codegenMode = async () => { // figma.showUI(__html__, { visible: false }); await getUserSettings(); - figma.codegen.on("generate", ({ language, node }) => { + figma.codegen.on("generate", async ({ language, node }: CodegenEvent): Promise => { const convertedSelection = convertIntoNodes([node], null); switch (language) { case "html": return [ { - title: `Code`, - code: htmlMain( + title: "Code", + code: await htmlMain( convertedSelection, { ...userPluginSettings, jsx: false }, true, @@ -121,7 +121,7 @@ const codegenMode = async () => { language: "HTML", }, { - title: `Text Styles`, + title: "Text Styles", code: htmlCodeGenTextStyles(userPluginSettings), language: "HTML", }, @@ -129,8 +129,8 @@ const codegenMode = async () => { case "html_jsx": return [ { - title: `Code`, - code: htmlMain( + title: "Code", + code: await htmlMain( convertedSelection, { ...userPluginSettings, jsx: true }, true, @@ -138,7 +138,7 @@ const codegenMode = async () => { language: "HTML", }, { - title: `Text Styles`, + title: "Text Styles", code: htmlCodeGenTextStyles(userPluginSettings), language: "HTML", }, @@ -147,20 +147,20 @@ const codegenMode = async () => { case "tailwind_jsx": return [ { - title: `Code`, - code: tailwindMain(convertedSelection, { + title: "Code", + code: await tailwindMain(convertedSelection, { ...userPluginSettings, jsx: language === "tailwind_jsx", }), language: "HTML", }, // { - // title: `Style`, + // title: "Style", // code: tailwindMain(convertedSelection, defaultPluginSettings), // language: "HTML", // }, { - title: `Tailwind Colors`, + title: "Tailwind Colors", code: retrieveGenericSolidUIColors("Tailwind") .map((d) => { let str = `${d.hex};`; @@ -176,7 +176,7 @@ const codegenMode = async () => { language: "JAVASCRIPT", }, { - title: `Text Styles`, + title: "Text Styles", code: tailwindCodeGenTextStyles(), language: "HTML", }, @@ -184,7 +184,7 @@ const codegenMode = async () => { case "flutter": return [ { - title: `Code`, + title: "Code", code: flutterMain(convertedSelection, { ...userPluginSettings, flutterGenerationMode: "snippet", @@ -192,7 +192,7 @@ const codegenMode = async () => { language: "SWIFT", }, { - title: `Text Styles`, + title: "Text Styles", code: flutterCodeGenTextStyles(), language: "SWIFT", }, @@ -200,7 +200,7 @@ const codegenMode = async () => { case "swiftUI": return [ { - title: `SwiftUI`, + title: "SwiftUI", code: swiftuiMain(convertedSelection, { ...userPluginSettings, swiftUIGenerationMode: "snippet", @@ -208,7 +208,7 @@ const codegenMode = async () => { language: "SWIFT", }, { - title: `Text Styles`, + title: "Text Styles", code: swiftUICodeGenTextStyles(), language: "SWIFT", }, diff --git a/manifest.json b/manifest.json index 8fcdc7c2..b9c29cc0 100644 --- a/manifest.json +++ b/manifest.json @@ -8,7 +8,7 @@ "capabilities": ["inspect", "codegen", "vscode"], "permissions": [], "networkAccess": { - "allowedDomains": ["none"] + "allowedDomains": ["https://placehold.co"] }, "codegenLanguages": [ { "label": "HTML", "value": "html" }, diff --git a/packages/backend/src/flutter/builderImpl/flutterColor.ts b/packages/backend/src/flutter/builderImpl/flutterColor.ts index 813f5191..47227df6 100644 --- a/packages/backend/src/flutter/builderImpl/flutterColor.ts +++ b/packages/backend/src/flutter/builderImpl/flutterColor.ts @@ -56,7 +56,7 @@ export const flutterBoxDecorationColor = ( export const flutterDecorationImage = (node: SceneNode, fill: ImagePaint) => { addWarning("Image fills are replaced with placeholders"); return generateWidgetCode("DecorationImage", { - image: `NetworkImage("https://via.placeholder.com/${node.width.toFixed( + image: `NetworkImage("https://placehold.co/${node.width.toFixed( 0, )}x${node.height.toFixed(0)}")`, fit: fitToBoxFit(fill), diff --git a/packages/backend/src/flutter/flutterMain.ts b/packages/backend/src/flutter/flutterMain.ts index 0abee72f..b93da6a3 100644 --- a/packages/backend/src/flutter/flutterMain.ts +++ b/packages/backend/src/flutter/flutterMain.ts @@ -148,7 +148,7 @@ const flutterContainer = (node: SceneNode, child: string): string => { let image = ""; if ("fills" in node && retrieveTopFill(node.fills)?.type === "IMAGE") { addWarning("Image fills are replaced with placeholders"); - image = `Image.network("https://via.placeholder.com/${node.width.toFixed( + image = `Image.network("https://placehold.co/${node.width.toFixed( 0, )}x${node.height.toFixed(0)}")`; } diff --git a/packages/backend/src/flutter/flutterTextBuilder.ts b/packages/backend/src/flutter/flutterTextBuilder.ts index 74759e68..bda01b99 100644 --- a/packages/backend/src/flutter/flutterTextBuilder.ts +++ b/packages/backend/src/flutter/flutterTextBuilder.ts @@ -13,6 +13,8 @@ import { } from "../common/commonTextHeightSpacing"; export class FlutterTextBuilder extends FlutterDefaultBuilder { + node?: TextNode; + constructor(optChild: string = "") { super(optChild); } @@ -22,6 +24,7 @@ export class FlutterTextBuilder extends FlutterDefaultBuilder { } createText(node: TextNode): this { + this.node = node; let alignHorizontal = node.textAlignHorizontal?.toString()?.toLowerCase() ?? "left"; alignHorizontal = @@ -106,6 +109,12 @@ export class FlutterTextBuilder extends FlutterDefaultBuilder { styleProperties.fontFeatures = `[FontFeature.enable("sups")]`; } + // Add text-shadow if a drop shadow is applied + const shadow = this.textShadow(); + if (shadow) { + styleProperties.shadows = shadow; + } + const style = generateWidgetCode("TextStyle", styleProperties); let text = segment.characters; @@ -155,6 +164,10 @@ export class FlutterTextBuilder extends FlutterDefaultBuilder { textAutoSize(node: TextNode): this { this.child = wrapTextAutoResize(node, this.child); + // First wrap with SizedBox/Expanded as before, then apply layer blur if any. + let wrapped = wrapTextAutoResize(node, this.child); + wrapped = wrapTextWithLayerBlur(node, wrapped); + this.child = wrapped; return this; } @@ -165,6 +178,39 @@ export class FlutterTextBuilder extends FlutterDefaultBuilder { } return ""; }; + + /** + * New method to handle text shadow. + * Checks if a drop shadow effect is applied to the node and + * returns Flutter code for the TextStyle "shadows" property. + */ + textShadow(): string { + if (this.node && (this.node as TextNode).effects) { + const effects = (this.node as TextNode).effects; + const dropShadow = effects.find( + (effect) => + effect.type === "DROP_SHADOW" && effect.visible !== false, + ); + if (dropShadow) { + const ds = dropShadow as DropShadowEffect; + const offsetX = Math.round(ds.offset.x); + const offsetY = Math.round(ds.offset.y); + const blurRadius = Math.round(ds.radius); + const r = Math.round(ds.color.r * 255); + const g = Math.round(ds.color.g * 255); + const b = Math.round(ds.color.b * 255); + // Convert to hex for Flutter Color (e.g., Color(0xFF112233)) + const hex = ((1 << 24) + (r << 16) + (g << 8) + b) + .toString(16) + .slice(1) + .toUpperCase(); + return `[Shadow(offset: Offset(${offsetX}, ${offsetY}), blurRadius: ${blurRadius}, color: Color(0xFF${hex}).withOpacity(${ds.color.a.toFixed( + 2, + )}))]`; + } + } + return ""; + } } export const wrapTextAutoResize = (node: TextNode, child: string): string => { @@ -201,5 +247,25 @@ export const wrapTextAutoResize = (node: TextNode, child: string): string => { return child; }; +// New helper to wrap with layer blur using Flutter's ImageFiltered widget. +export const wrapTextWithLayerBlur = ( + node: TextNode, + child: string, +): string => { + if (node.effects) { + const blurEffect = node.effects.find( + (effect) => + effect.type === "LAYER_BLUR" && effect.visible !== false && effect.radius > 0, + ); + if (blurEffect) { + return generateWidgetCode("ImageFiltered", { + imageFilter: `ImageFilter.blur(sigmaX: ${blurEffect.radius}, sigmaY: ${blurEffect.radius})`, + child: child, + }); + } + } + return child; +}; + export const parseTextAsCode = (originalText: string) => originalText.replace(/\n/g, "\\n"); diff --git a/packages/backend/src/html/htmlMain.ts b/packages/backend/src/html/htmlMain.ts index 92f2c026..08150611 100644 --- a/packages/backend/src/html/htmlMain.ts +++ b/packages/backend/src/html/htmlMain.ts @@ -225,7 +225,7 @@ const htmlAsset = (node: SceneNode, settings: HTMLSettings): string => { if (retrieveTopFill(node.fills)?.type === "IMAGE") { addWarning("Image fills are replaced with placeholders"); tag = "img"; - src = ` src="https://via.placeholder.com/${node.width.toFixed( + src = ` src="https://placehold.co/$${node.width.toFixed( 0, )}x${node.height.toFixed(0)}"`; } @@ -268,7 +268,7 @@ const htmlContainer = ( addWarning("Image fills are replaced with placeholders"); if (!("children" in node) || node.children.length === 0) { tag = "img"; - src = ` src="https://via.placeholder.com/${node.width.toFixed( + src = ` src="https://placehold.co/${node.width.toFixed( 0, )}x${node.height.toFixed(0)}"`; } else { @@ -276,7 +276,7 @@ const htmlContainer = ( formatWithJSX( "background-image", settings.jsx, - `url(https://via.placeholder.com/${node.width.toFixed( + `url(https://placehold.co/${node.width.toFixed( 0, )}x${node.height.toFixed(0)})`, ), diff --git a/packages/backend/src/html/htmlTextBuilder.ts b/packages/backend/src/html/htmlTextBuilder.ts index f66d256c..30ab8ca5 100644 --- a/packages/backend/src/html/htmlTextBuilder.ts +++ b/packages/backend/src/html/htmlTextBuilder.ts @@ -24,6 +24,18 @@ export class HtmlTextBuilder extends HtmlDefaultBuilder { } return segments.map((segment) => { + // Prepare additional CSS properties from layer blur and drop shadow effects. + const additionalStyles: { [key: string]: string } = {}; + + const layerBlurStyle = this.getLayerBlurStyle(); + if (layerBlurStyle) { + additionalStyles.filter = layerBlurStyle; + } + const textShadowStyle = this.getTextShadowStyle(); + if (textShadowStyle) { + additionalStyles["text-shadow"] = textShadowStyle; + } + const styleAttributes = formatMultipleJSX( { color: htmlColorFromFills(segment.fills), @@ -36,12 +48,13 @@ export class HtmlTextBuilder extends HtmlDefaultBuilder { "line-height": this.lineHeight(segment.lineHeight, segment.fontSize), "letter-spacing": this.letterSpacing( segment.letterSpacing, - segment.fontSize, + segment.fontSize ), // "text-indent": segment.indentation, "word-wrap": "break-word", + ...additionalStyles, }, - this.isJSX, + this.isJSX ); const charsWithLineBreak = segment.characters.split("\n").join("
"); @@ -139,4 +152,49 @@ export class HtmlTextBuilder extends HtmlDefaultBuilder { } return this; } + + /** + * Returns a CSS filter value for layer blur. + */ + private getLayerBlurStyle(): string { + if (this.node && (this.node as TextNode).effects) { + const effects = (this.node as TextNode).effects; + const blurEffect = effects.find( + (effect) => + effect.type === "LAYER_BLUR" && + effect.visible !== false && + effect.radius > 0 + ); + if (blurEffect && blurEffect.radius) { + return `blur(${blurEffect.radius}px)`; + } + } + return ""; + } + + /** + * Returns a CSS text-shadow value if a drop shadow effect is applied. + */ + private getTextShadowStyle(): string { + if (this.node && (this.node as TextNode).effects) { + const effects = (this.node as TextNode).effects; + const dropShadow = effects.find( + (effect) => effect.type === "DROP_SHADOW" && effect.visible !== false + ); + if (dropShadow) { + const ds = dropShadow as DropShadowEffect; // Type narrow the effect. + const offsetX = Math.round(ds.offset.x); + const offsetY = Math.round(ds.offset.y); + const blurRadius = Math.round(ds.radius); + const r = Math.round(ds.color.r * 255); + const g = Math.round(ds.color.g * 255); + const b = Math.round(ds.color.b * 255); + const a = ds.color.a; + return `${offsetX}px ${offsetY}px ${blurRadius}px rgba(${r}, ${g}, ${b}, ${a.toFixed( + 2 + )})`; + } + } + return ""; + } } diff --git a/packages/backend/src/swiftui/builderImpl/swiftuiColor.ts b/packages/backend/src/swiftui/builderImpl/swiftuiColor.ts index c7bb7b8a..cea9bb6a 100644 --- a/packages/backend/src/swiftui/builderImpl/swiftuiColor.ts +++ b/packages/backend/src/swiftui/builderImpl/swiftuiColor.ts @@ -60,7 +60,7 @@ export const swiftuiBackground = ( return swiftuiGradient(fill); } else if (fill?.type === "IMAGE") { addWarning("Image fills are replaced with placeholders"); - return `AsyncImage(url: URL(string: "https://via.placeholder.com/${node.width.toFixed( + return `AsyncImage(url: URL(string: "https://placehold.co/${node.width.toFixed( 0, )}x${node.height.toFixed(0)}"))`; } diff --git a/packages/backend/src/swiftui/swiftuiTextBuilder.ts b/packages/backend/src/swiftui/swiftuiTextBuilder.ts index f5a805dc..e1438f44 100644 --- a/packages/backend/src/swiftui/swiftuiTextBuilder.ts +++ b/packages/backend/src/swiftui/swiftuiTextBuilder.ts @@ -12,6 +12,7 @@ import { parseTextAsCode } from "../flutter/flutterTextBuilder"; import { swiftuiSolidColor } from "./builderImpl/swiftuiColor"; export class SwiftuiTextBuilder extends SwiftuiDefaultBuilder { + node?: TextNode; modifiers: string[] = []; constructor(kind: string = "Text") { @@ -80,6 +81,7 @@ export class SwiftuiTextBuilder extends SwiftuiDefaultBuilder { }; createText(node: TextNode): this { + this.node = node; let alignHorizontal = node.textAlignHorizontal?.toString()?.toLowerCase() ?? "left"; alignHorizontal = @@ -140,6 +142,16 @@ export class SwiftuiTextBuilder extends SwiftuiDefaultBuilder { .addModifier([this.textStyle(segment.fontName.style), ""]) .addModifier(["foregroundColor", this.textColor(segment.fills)]); + const blurMod = this.textBlur(); + if (blurMod !== "") { + element.addModifier([blurMod, ""]); + } + + const shadowMod = this.textShadow(); + if (shadowMod !== "") { + element.addModifier([shadowMod, ""]); + } + return element; // }); } @@ -221,4 +233,43 @@ export class SwiftuiTextBuilder extends SwiftuiDefaultBuilder { // when they are centered return ""; }; + + textBlur = (): string => { + if (this.node && this.node.effects) { + const blurEffect = this.node.effects.find( + (effect) => + effect.type === "LAYER_BLUR" && + effect.visible !== false && + effect.radius > 0, + ); + if (blurEffect) { + return `.blur(radius: ${blurEffect.radius})`; + } + } + return ""; + }; + + textShadow = (): string => { + if (this.node && this.node.effects) { + const dropShadow = this.node.effects.find( + (effect) => effect.type === "DROP_SHADOW" && effect.visible !== false, + ); + if (dropShadow) { + const ds = dropShadow as DropShadowEffect; + const offsetX = Math.round(ds.offset.x); + const offsetY = Math.round(ds.offset.y); + const blurRadius = Math.round(ds.radius); + return `.shadow(color: Color(red: ${ds.color.r.toFixed( + 2, + )}, green: ${ds.color.g.toFixed( + 2, + )}, blue: ${ds.color.b.toFixed( + 2, + )}, opacity: ${ds.color.a.toFixed( + 2, + )}), radius: ${blurRadius}, x: ${offsetX}, y: ${offsetY})`; + } + } + return ""; + }; } diff --git a/packages/backend/src/tailwind/builderImpl/tailwindColor.ts b/packages/backend/src/tailwind/builderImpl/tailwindColor.ts index a5d5cb70..82f726ab 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindColor.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindColor.ts @@ -84,16 +84,27 @@ export const tailwindGradientFromFills = ( fills: ReadonlyArray | PluginAPI["mixed"], ): string => { // [when testing] node.effects can be undefined - const fill = retrieveTopFill(fills); - if (fill?.type === "GRADIENT_LINEAR") { + // Return early if no fill exists + if (!fill) { + return ""; + } + + if (fill.type === "GRADIENT_LINEAR") { return tailwindGradient(fill); } - addWarning( - "Gradients are not fully supported in Tailwind except for Linear Gradients.", - ); + // Show warning if there's a non-linear gradient + if ( + fill.type === "GRADIENT_ANGULAR" || + fill.type === "GRADIENT_RADIAL" || + fill.type === "GRADIENT_DIAMOND" + ) { + addWarning( + "Gradients are not fully supported in Tailwind except for Linear Gradients.", + ); + } return ""; }; diff --git a/packages/backend/src/tailwind/tailwindMain.ts b/packages/backend/src/tailwind/tailwindMain.ts index ff3d3441..f174ddb8 100644 --- a/packages/backend/src/tailwind/tailwindMain.ts +++ b/packages/backend/src/tailwind/tailwindMain.ts @@ -234,12 +234,12 @@ export const tailwindContainer = ( addWarning("Image fills are replaced with placeholders"); if (!("children" in node) || node.children.length === 0) { tag = "img"; - src = ` src="https://via.placeholder.com/${node.width.toFixed( + src = ` src="https://placehold.co/${node.width.toFixed( 0, )}x${node.height.toFixed(0)}"`; } else { builder.addAttributes( - `bg-[url(https://via.placeholder.com/${node.width.toFixed( + `bg-[url(https://placehold.co/${node.width.toFixed( 0, )}x${node.height.toFixed(0)})]`, ); diff --git a/packages/backend/src/tailwind/tailwindTextBuilder.ts b/packages/backend/src/tailwind/tailwindTextBuilder.ts index 9c23e03d..a20a5b76 100644 --- a/packages/backend/src/tailwind/tailwindTextBuilder.ts +++ b/packages/backend/src/tailwind/tailwindTextBuilder.ts @@ -8,6 +8,7 @@ import { pxToFontSize, pxToLetterSpacing, pxToLineHeight, + pxToBlur, } from "./conversionTables"; import { TailwindDefaultBuilder } from "./tailwindDefaultBuilder"; import { config } from "./tailwindConfig"; @@ -36,6 +37,8 @@ export class TailwindTextBuilder extends TailwindDefaultBuilder { segment.fontSize, ); // const textIndentStyle = this.indentStyle(segment.indentation); + const blurStyle = this.layerBlur(); + const shadowStyle = this.textShadow(); const styleClasses = [ color, @@ -47,6 +50,8 @@ export class TailwindTextBuilder extends TailwindDefaultBuilder { lineHeightStyle, letterSpacingStyle, // textIndentStyle, + blurStyle, + shadowStyle, ] .filter((d) => d !== "") .join(" "); @@ -229,6 +234,56 @@ export class TailwindTextBuilder extends TailwindDefaultBuilder { } } + /** + * https://v3.tailwindcss.com/docs/blur + */ + layerBlur = (): string => { + if (this.node && (this.node as TextNode).effects) { + const effects = (this.node as TextNode).effects; + const blurEffect = effects.find( + (effect) => + effect.type === "LAYER_BLUR" && effect.visible !== false, + ); + if (blurEffect && blurEffect.radius && blurEffect.radius > 0) { + const blurSuffix = pxToBlur(blurEffect.radius); + if (blurSuffix) { + return `blur-${blurSuffix}`; + } + } + } + return ""; + }; + + /** + * New method to handle text shadow. + * When a drop shadow is applied to a text element, + * this method returns an arbitrary Tailwind utility class + * in the following format: + * + * [text-shadow:_0px_4px_4px_rgb(0_0_0_/_0.50)] + */ + textShadow = (): string => { + if (this.node && (this.node as TextNode).effects) { + const effects = (this.node as TextNode).effects; + const dropShadow = effects.find( + (effect) => + effect.type === "DROP_SHADOW" && effect.visible !== false, + ); + if (dropShadow) { + const ds = dropShadow as DropShadowEffect; + const offsetX = Math.round(ds.offset.x); + const offsetY = Math.round(ds.offset.y); + const blurRadius = Math.round(ds.radius); + const r = Math.round(ds.color.r * 255); + const g = Math.round(ds.color.g * 255); + const b = Math.round(ds.color.b * 255); + const aFixed = ds.color.a.toFixed(2); + return `[text-shadow:_${offsetX}px_${offsetY}px_${blurRadius}px_rgb(${r}_${g}_${b}_/_${aFixed})]`; + } + } + return ""; + }; + reset(): void { this.attributes = []; } diff --git a/packages/plugin-ui/src/components/CodePanel.tsx b/packages/plugin-ui/src/components/CodePanel.tsx index 32c84b74..8e0cea77 100644 --- a/packages/plugin-ui/src/components/CodePanel.tsx +++ b/packages/plugin-ui/src/components/CodePanel.tsx @@ -32,11 +32,35 @@ const CodePanel = (props: CodePanelProps) => { } = props; const isEmpty = code === ""; - // Add your clipboard function here or any other actions + // State for custom prefix for Tailwind classes. + // It is initially set from settings (if available) or an empty string. + const [customPrefix, setCustomPrefix] = useState(settings?.customTailwindPrefix || ""); + + // Helper function to add the prefix before every class (or className) in the code. + // It finds every occurrence of class="..." or className="..." and, for each class, + // prepends the custom prefix. + const applyPrefixToClasses = (codeString: string, prefix: string) => { + return codeString.replace(/(class(?:Name)?)="([^"]*)"/g, (match, attr, classes) => { + const prefixedClasses = classes + .split(/\s+/) + .filter(Boolean) + .map((cls: string) => prefix + cls) + .join(" "); + return `${attr}="${prefixedClasses}"`; + }); + }; + + // If the selected framework is Tailwind and a prefix is provided then transform the code. + const prefixedCode = + selectedFramework === "Tailwind" && customPrefix.trim() !== "" + ? applyPrefixToClasses(code, customPrefix) + : code; + + // Clipboard and hover handlers. const handleButtonClick = () => { setIsPressed(true); setTimeout(() => setIsPressed(false), 250); - copy(code); + copy(prefixedCode); }; const handleButtonHover = () => setSyntaxHovered(true); @@ -82,7 +106,9 @@ const CodePanel = (props: CodePanelProps) => { title={preference.label} description={preference.description} isSelected={ - settings?.[preference.propertyName] ?? preference.isDefault + typeof settings?.[preference.propertyName] === "boolean" + ? (settings?.[preference.propertyName] as boolean) + : preference.isDefault } onSelect={(value) => { onPreferenceChanged(preference.propertyName, value); @@ -92,6 +118,27 @@ const CodePanel = (props: CodePanelProps) => { /> ))}
+ + {/* Input field for custom Tailwind prefix (only rendered when Tailwind is selected) */} + {selectedFramework === "Tailwind" && ( +
+ + { + const newVal = e.target.value; + setCustomPrefix(newVal); + onPreferenceChanged("tailwindPrefix", newVal); + }} + placeholder="e.g., tw-" + className="mt-1 p-1 px-2 border border-gray-300 rounded bg-neutral-100 dark:bg-neutral-700 text-sm" + /> +
+ )} + {selectableSettingsFiltered.length > 0 && ( <>
@@ -148,7 +195,7 @@ const CodePanel = (props: CodePanelProps) => { transitionDuration: "0.2s", }} > - {code} + {prefixedCode} )}
diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index 992008d5..076a36ad 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -9,6 +9,7 @@ export interface TailwindSettings extends HTMLSettings { roundTailwindValues: boolean; roundTailwindColors: boolean; customTailwindColors: boolean; + customTailwindPrefix?: string; } export interface FlutterSettings { flutterGenerationMode: string; From adf500d41ee3c99dc0a43cbcecfadfab42997aac Mon Sep 17 00:00:00 2001 From: "Mims H. Wright" Date: Tue, 25 Feb 2025 13:49:32 +0100 Subject: [PATCH 027/168] Issue 168 component sets (#170) * fixed issue with component sets * fix linter issue --- .../backend/src/altNodes/altConversion.ts | 29 +++++++++---------- .../backend/src/common/nodeWidthHeight.ts | 2 +- packages/types/src/types.ts | 1 + 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/backend/src/altNodes/altConversion.ts b/packages/backend/src/altNodes/altConversion.ts index a6c51d71..ca06901b 100644 --- a/packages/backend/src/altNodes/altConversion.ts +++ b/packages/backend/src/altNodes/altConversion.ts @@ -84,22 +84,21 @@ export const cloneNode = ( // Create the cloned object with the correct prototype const cloned = {} as T; // Create a new object with only the desired descriptors (excluding 'parent' and 'children') - const droppedProps = [ - "parent", - "children", - "horizontalPadding", - "verticalPadding", - "mainComponent", - "masterComponent", - "variantProperties", - "get_annotations", - "componentPropertyDefinitions", - "exposedInstances", - "componentProperties", - "componenPropertyReferences", - ]; for (const prop in node) { - if (prop in droppedProps === false) { + if ( + prop !== "parent" && + prop !== "children" && + prop !== "horizontalPadding" && + prop !== "verticalPadding" && + prop !== "mainComponent" && + prop !== "masterComponent" && + prop !== "variantProperties" && + prop !== "get_annotations" && + prop !== "componentPropertyDefinitions" && + prop !== "exposedInstances" && + prop !== "componentProperties" && + prop !== "componenPropertyReferences" + ) { cloned[prop as keyof T] = node[prop as keyof T]; } } diff --git a/packages/backend/src/common/nodeWidthHeight.ts b/packages/backend/src/common/nodeWidthHeight.ts index 5f2b583b..119e5f2b 100644 --- a/packages/backend/src/common/nodeWidthHeight.ts +++ b/packages/backend/src/common/nodeWidthHeight.ts @@ -20,7 +20,7 @@ export const nodeSize = (node: SceneNode, optimizeLayout: boolean): Size => { // const parentLayoutMode = node.parent.layoutMode; const parentLayoutMode = optimizeLayout ? node.parent.inferredAutoLayout?.layoutMode - : (null ?? node.parent.layoutMode); + : node.parent.layoutMode; const isWidthFill = (parentLayoutMode === "HORIZONTAL" && nodeAuto.layoutGrow === 1) || diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index 076a36ad..d9b85fef 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -1,3 +1,4 @@ +import "@figma/plugin-typings"; // Settings export type Framework = "Flutter" | "SwiftUI" | "HTML" | "Tailwind"; export interface HTMLSettings { From ff724766015abe58e5bc6aece30d21601ded1929 Mon Sep 17 00:00:00 2001 From: "Mims H. Wright" Date: Thu, 27 Feb 2025 07:09:38 +0100 Subject: [PATCH 028/168] replaced whitespace with underscores in font family names (#175) --- packages/backend/src/tailwind/tailwindTextBuilder.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/backend/src/tailwind/tailwindTextBuilder.ts b/packages/backend/src/tailwind/tailwindTextBuilder.ts index a20a5b76..ca3518e2 100644 --- a/packages/backend/src/tailwind/tailwindTextBuilder.ts +++ b/packages/backend/src/tailwind/tailwindTextBuilder.ts @@ -100,8 +100,9 @@ export class TailwindTextBuilder extends TailwindDefaultBuilder { if (config.fontFamily.mono.includes(fontName.family)) { return "font-mono"; } + const underscoreFontName = fontName.family.replace(/\s/g, "_"); - return "font-['" + fontName.family + "']"; + return "font-['" + underscoreFontName + "']"; }; /** @@ -241,8 +242,7 @@ export class TailwindTextBuilder extends TailwindDefaultBuilder { if (this.node && (this.node as TextNode).effects) { const effects = (this.node as TextNode).effects; const blurEffect = effects.find( - (effect) => - effect.type === "LAYER_BLUR" && effect.visible !== false, + (effect) => effect.type === "LAYER_BLUR" && effect.visible !== false, ); if (blurEffect && blurEffect.radius && blurEffect.radius > 0) { const blurSuffix = pxToBlur(blurEffect.radius); @@ -266,8 +266,7 @@ export class TailwindTextBuilder extends TailwindDefaultBuilder { if (this.node && (this.node as TextNode).effects) { const effects = (this.node as TextNode).effects; const dropShadow = effects.find( - (effect) => - effect.type === "DROP_SHADOW" && effect.visible !== false, + (effect) => effect.type === "DROP_SHADOW" && effect.visible !== false, ); if (dropShadow) { const ds = dropShadow as DropShadowEffect; From 7456e72f6bbb0e01c0b4c9c09c431abef520d5a5 Mon Sep 17 00:00:00 2001 From: "Mims H. Wright" Date: Thu, 27 Feb 2025 07:09:58 +0100 Subject: [PATCH 029/168] Extracted placeholder image url to reduce duplicated code. (#176) * Extracted placeholder image url to reduce duplicated code. * refactor to how images are built in HTML builder * linter fix --- packages/backend/src/common/images.ts | 7 ++++ .../src/flutter/builderImpl/flutterColor.ts | 5 ++- packages/backend/src/flutter/flutterMain.ts | 5 ++- packages/backend/src/html/htmlMain.ts | 33 +++++++------------ .../src/swiftui/builderImpl/swiftuiColor.ts | 5 ++- packages/backend/src/tailwind/tailwindMain.ts | 12 +++---- 6 files changed, 28 insertions(+), 39 deletions(-) create mode 100644 packages/backend/src/common/images.ts diff --git a/packages/backend/src/common/images.ts b/packages/backend/src/common/images.ts new file mode 100644 index 00000000..dad86579 --- /dev/null +++ b/packages/backend/src/common/images.ts @@ -0,0 +1,7 @@ +export const PLACEHOLDER_IMAGE_DOMAIN = "https://placehold.co"; + +export const getPlaceholderImage = (w: number, h = -1) => { + const _w = w.toFixed(0); + const _h = (h < 0 ? w : h).toFixed(0); + return `${PLACEHOLDER_IMAGE_DOMAIN}/${_w}x${_h}`; +}; diff --git a/packages/backend/src/flutter/builderImpl/flutterColor.ts b/packages/backend/src/flutter/builderImpl/flutterColor.ts index 47227df6..ff3b1b0e 100644 --- a/packages/backend/src/flutter/builderImpl/flutterColor.ts +++ b/packages/backend/src/flutter/builderImpl/flutterColor.ts @@ -6,6 +6,7 @@ import { } from "../../common/numToAutoFixed"; import { retrieveTopFill } from "../../common/retrieveFill"; import { nearestValue } from "../../tailwind/conversionTables"; +import { getPlaceholderImage } from "../../common/images"; /** * Retrieve the SOLID color for Flutter when existent, otherwise "" @@ -56,9 +57,7 @@ export const flutterBoxDecorationColor = ( export const flutterDecorationImage = (node: SceneNode, fill: ImagePaint) => { addWarning("Image fills are replaced with placeholders"); return generateWidgetCode("DecorationImage", { - image: `NetworkImage("https://placehold.co/${node.width.toFixed( - 0, - )}x${node.height.toFixed(0)}")`, + image: `NetworkImage("${getPlaceholderImage(node.width, node.height)}")`, fit: fitToBoxFit(fill), }); }; diff --git a/packages/backend/src/flutter/flutterMain.ts b/packages/backend/src/flutter/flutterMain.ts index b93da6a3..d7e8738d 100644 --- a/packages/backend/src/flutter/flutterMain.ts +++ b/packages/backend/src/flutter/flutterMain.ts @@ -14,6 +14,7 @@ import { import { commonSortChildrenWhenInferredAutoLayout } from "../common/commonChildrenOrder"; import { PluginSettings } from "types"; import { addWarning } from "../common/commonConversionWarnings"; +import { getPlaceholderImage } from "../common/images"; let localSettings: PluginSettings; let previousExecutionCache: string[]; @@ -148,9 +149,7 @@ const flutterContainer = (node: SceneNode, child: string): string => { let image = ""; if ("fills" in node && retrieveTopFill(node.fills)?.type === "IMAGE") { addWarning("Image fills are replaced with placeholders"); - image = `Image.network("https://placehold.co/${node.width.toFixed( - 0, - )}x${node.height.toFixed(0)}")`; + image = `Image.network("${getPlaceholderImage(node.width, node.height)}")`; } if (child.length > 0) { diff --git a/packages/backend/src/html/htmlMain.ts b/packages/backend/src/html/htmlMain.ts index 08150611..acf5fd38 100644 --- a/packages/backend/src/html/htmlMain.ts +++ b/packages/backend/src/html/htmlMain.ts @@ -9,6 +9,7 @@ import { addWarning } from "../common/commonConversionWarnings"; import { PluginSettings, HTMLPreview, AltNode, HTMLSettings } from "types"; import { renderAndAttachSVG } from "../altNodes/altNodeUtils"; import { getVisibleNodes } from "../common/nodeVisibility"; +import { getPlaceholderImage } from "../common/images"; const selfClosingTags = ["img"]; @@ -211,7 +212,10 @@ const htmlFrame = async ( } }; -const htmlAsset = (node: SceneNode, settings: HTMLSettings): string => { +const htmlAsset = async ( + node: SceneNode, + settings: HTMLSettings, +): Promise => { if (!("opacity" in node) || !("layoutAlign" in node) || !("fills" in node)) { return ""; } @@ -220,21 +224,13 @@ const htmlAsset = (node: SceneNode, settings: HTMLSettings): string => { .commonPositionStyles() .commonShapeStyles(); - let tag = "div"; - let src = ""; if (retrieveTopFill(node.fills)?.type === "IMAGE") { addWarning("Image fills are replaced with placeholders"); - tag = "img"; - src = ` src="https://placehold.co/$${node.width.toFixed( - 0, - )}x${node.height.toFixed(0)}"`; + const imgUrl = getPlaceholderImage(node.width, node.height); + return `\n`; } - if (tag === "div") { - return `\n
`; - } - - return `\n<${tag}${builder.build()}${src} />`; + return `\n
`; }; // properties named propSomething always take care of "," @@ -266,20 +262,13 @@ const htmlContainer = ( let src = ""; if (retrieveTopFill(node.fills)?.type === "IMAGE") { addWarning("Image fills are replaced with placeholders"); + const imageURL = getPlaceholderImage(node.width, node.height); if (!("children" in node) || node.children.length === 0) { tag = "img"; - src = ` src="https://placehold.co/${node.width.toFixed( - 0, - )}x${node.height.toFixed(0)}"`; + src = ` src="${imageURL}"`; } else { builder.addStyles( - formatWithJSX( - "background-image", - settings.jsx, - `url(https://placehold.co/${node.width.toFixed( - 0, - )}x${node.height.toFixed(0)})`, - ), + formatWithJSX("background-image", settings.jsx, `url(${imageURL})`), ); } } diff --git a/packages/backend/src/swiftui/builderImpl/swiftuiColor.ts b/packages/backend/src/swiftui/builderImpl/swiftuiColor.ts index cea9bb6a..f669aae5 100644 --- a/packages/backend/src/swiftui/builderImpl/swiftuiColor.ts +++ b/packages/backend/src/swiftui/builderImpl/swiftuiColor.ts @@ -3,6 +3,7 @@ import { gradientAngle } from "../../common/color"; import { nearestValue } from "../../tailwind/conversionTables"; import { numberToFixedString } from "../../common/numToAutoFixed"; import { addWarning } from "../../common/commonConversionWarnings"; +import { getPlaceholderImage } from "../../common/images"; export const swiftUISolidColor = (fill: Paint): string => { if (fill && fill.type === "SOLID") { @@ -60,9 +61,7 @@ export const swiftuiBackground = ( return swiftuiGradient(fill); } else if (fill?.type === "IMAGE") { addWarning("Image fills are replaced with placeholders"); - return `AsyncImage(url: URL(string: "https://placehold.co/${node.width.toFixed( - 0, - )}x${node.height.toFixed(0)}"))`; + return `AsyncImage(url: URL(string: "${getPlaceholderImage(node.width, node.height)}"))`; } return ""; diff --git a/packages/backend/src/tailwind/tailwindMain.ts b/packages/backend/src/tailwind/tailwindMain.ts index f174ddb8..335b892a 100644 --- a/packages/backend/src/tailwind/tailwindMain.ts +++ b/packages/backend/src/tailwind/tailwindMain.ts @@ -8,6 +8,7 @@ import { AltNode, PluginSettings, TailwindSettings } from "types"; import { addWarning } from "../common/commonConversionWarnings"; import { renderAndAttachSVG } from "../altNodes/altNodeUtils"; import { getVisibleNodes } from "../common/nodeVisibility"; +import { getPlaceholderImage } from "../common/images"; export let localTailwindSettings: PluginSettings; @@ -232,17 +233,12 @@ export const tailwindContainer = ( let src = ""; if (retrieveTopFill(node.fills)?.type === "IMAGE") { addWarning("Image fills are replaced with placeholders"); + const imageURL = getPlaceholderImage(node.width, node.height); if (!("children" in node) || node.children.length === 0) { tag = "img"; - src = ` src="https://placehold.co/${node.width.toFixed( - 0, - )}x${node.height.toFixed(0)}"`; + src = ` src="${imageURL}"`; } else { - builder.addAttributes( - `bg-[url(https://placehold.co/${node.width.toFixed( - 0, - )}x${node.height.toFixed(0)})]`, - ); + builder.addAttributes(`bg-[url(${imageURL})]`); } } From c159ecc7e62466cad44719df8f92aee93cee46d9 Mon Sep 17 00:00:00 2001 From: "Mims H. Wright" Date: Thu, 27 Feb 2025 07:11:14 +0100 Subject: [PATCH 030/168] Async loading (#174) * added loading message when processing large images and paths. still has issues with async because messages aren't sent synchronously * Moved the loading message out of the implementation folder and into the library. Improved the visuals and language of the loading message. * Fixed two minor linter errors in readme --- apps/debug/README.md | 2 +- apps/plugin/README.md | 2 +- apps/plugin/ui-src/App.tsx | 39 ++++++++----------- packages/backend/src/code.ts | 12 +++++- packages/backend/src/messaging.ts | 4 ++ packages/plugin-ui/src/PluginUI.tsx | 5 ++- packages/plugin-ui/src/components/Loading.tsx | 24 ++++++++++++ packages/types/src/types.ts | 1 + 8 files changed, 61 insertions(+), 28 deletions(-) create mode 100644 packages/plugin-ui/src/components/Loading.tsx diff --git a/apps/debug/README.md b/apps/debug/README.md index 4fae62af..e6d38f06 100644 --- a/apps/debug/README.md +++ b/apps/debug/README.md @@ -1,4 +1,4 @@ -## Getting Started +# Getting Started First, run the development server: diff --git a/apps/plugin/README.md b/apps/plugin/README.md index 4fae62af..e6d38f06 100644 --- a/apps/plugin/README.md +++ b/apps/plugin/README.md @@ -1,4 +1,4 @@ -## Getting Started +# Getting Started First, run the development server: diff --git a/apps/plugin/ui-src/App.tsx b/apps/plugin/ui-src/App.tsx index 7bee7558..b7e0ad8f 100644 --- a/apps/plugin/ui-src/App.tsx +++ b/apps/plugin/ui-src/App.tsx @@ -30,7 +30,7 @@ export default function App() { const [state, setState] = useState({ code: "", selectedFramework: "HTML", - isLoading: false, + isLoading: true, htmlPreview: emptyPreview, settings: null, colors: [], @@ -49,12 +49,21 @@ export default function App() { console.log("[ui] message received:", untypedMessage); switch (untypedMessage.type) { + case "conversionStart": + setState((prevState) => ({ + ...prevState, + code: "", + isLoading: true, + })); + break; + case "code": const conversionMessage = untypedMessage as ConversionMessage; setState((prevState) => ({ ...prevState, ...conversionMessage, selectedFramework: conversionMessage.settings.framework, + isLoading: false, })); break; @@ -76,6 +85,7 @@ export default function App() { warnings: [], colors: [], gradients: [], + isLoading: false, })); break; @@ -87,6 +97,7 @@ export default function App() { colors: [], gradients: [], code: `Error :(\n// ${errorMessage.error}`, + isLoading: false, })); break; default: @@ -99,26 +110,6 @@ export default function App() { }; }, []); - useEffect(() => { - if (state.selectedFramework === null) { - const timer = setTimeout( - () => setState((prevState) => ({ ...prevState, isLoading: true })), - 300, - ); - return () => clearTimeout(timer); - } else { - setState((prevState) => ({ ...prevState, isLoading: false })); - } - }, [state.selectedFramework]); - - if (state.selectedFramework === null) { - return state.isLoading ? ( -
- Loading Plugin... -
- ) : null; - } - const handleFrameworkChange = (updatedFramework: Framework) => { setState((prevState) => ({ ...prevState, @@ -129,11 +120,13 @@ export default function App() { targetOrigin: "*", }); }; - console.log("state.code", state.code.slice(0, 25)); + + const darkMode = figmaColorBgValue !== "#ffffff"; return ( -
+
{ ); } + postConversionStart(); + // force postMessage to run right now. + await new Promise((resolve) => setTimeout(resolve, 30)); + const convertedSelection = convertNodesToAltNodes(selection, null); // ignore when nothing was selected diff --git a/packages/backend/src/messaging.ts b/packages/backend/src/messaging.ts index 64adcd45..0747a435 100644 --- a/packages/backend/src/messaging.ts +++ b/packages/backend/src/messaging.ts @@ -1,5 +1,6 @@ import { ConversionMessage, + ConversionStartMessage, EmptyMessage, ErrorMessage, PluginSettings, @@ -11,6 +12,9 @@ export const postBackendMessage = figma.ui.postMessage; export const postEmptyMessage = () => postBackendMessage({ type: "empty" } as EmptyMessage); +export const postConversionStart = () => + postBackendMessage({ type: "conversionStart" } as ConversionStartMessage); + export const postConversionComplete = ( conversionData: ConversionMessage | Omit, ) => postBackendMessage({ ...conversionData, type: "code" }); diff --git a/packages/plugin-ui/src/PluginUI.tsx b/packages/plugin-ui/src/PluginUI.tsx index bd8fefe5..b446f750 100644 --- a/packages/plugin-ui/src/PluginUI.tsx +++ b/packages/plugin-ui/src/PluginUI.tsx @@ -1,4 +1,3 @@ -import { useState } from "react"; import copy from "copy-to-clipboard"; import Preview from "./components/Preview"; import GradientsPanel from "./components/GradientsPanel"; @@ -17,6 +16,7 @@ import { preferenceOptions, selectPreferenceOptions, } from "./codegenPreferenceOptions"; +import Loading from "./components/Loading"; type PluginUIProps = { code: string; @@ -28,11 +28,14 @@ type PluginUIProps = { onPreferenceChanged: (key: string, value: boolean | string) => void; colors: SolidColorConversion[]; gradients: LinearGradientConversion[]; + isLoading: boolean; }; const frameworks: Framework[] = ["HTML", "Tailwind", "Flutter", "SwiftUI"]; export const PluginUI = (props: PluginUIProps) => { + if (props.isLoading) return ; + const isEmpty = props.code === ""; const warnings = props.warnings ?? []; diff --git a/packages/plugin-ui/src/components/Loading.tsx b/packages/plugin-ui/src/components/Loading.tsx new file mode 100644 index 00000000..010977dc --- /dev/null +++ b/packages/plugin-ui/src/components/Loading.tsx @@ -0,0 +1,24 @@ +interface LoadingProps {} +const Loading = (_props: LoadingProps) => ( +
+
+

+ Converting... +

+

+ This can take a while if the selection has many images or paths +

+
+ +
+); +export default Loading; diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index d9b85fef..bc8a6a50 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -47,6 +47,7 @@ export interface UIMessage { pluginMessage: Message; } export type EmptyMessage = Message & { type: "empty" }; +export type ConversionStartMessage = Message & { type: "conversionStarted" }; export type ConversionMessage = Message & { type: "code"; } & ConversionData; From b10cfab409aaa08479fa3ac2d5df8a51cab0f637 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Thu, 27 Feb 2025 03:11:48 -0300 Subject: [PATCH 031/168] Fix color variables (come before white/black) --- .../backend/src/tailwind/conversionTables.ts | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/backend/src/tailwind/conversionTables.ts b/packages/backend/src/tailwind/conversionTables.ts index 943761d5..f8829cf1 100644 --- a/packages/backend/src/tailwind/conversionTables.ts +++ b/packages/backend/src/tailwind/conversionTables.ts @@ -134,6 +134,16 @@ export function getColorInfo(fill: SolidPaint | ColorStop) { let hex: string = "#" + rgbTo6hex(fill.color); let meta: string = ""; + // variable + if ( + localTailwindSettings.customTailwindColors && + fill.boundVariables?.color + ) { + colorName = variableToColorName(fill.boundVariables.color); + colorType = "variable"; + meta = "custom"; + } + // Check for pure black/white first if (fill.color.r === 0 && fill.color.g === 0 && fill.color.b === 0) { return { @@ -153,16 +163,6 @@ export function getColorInfo(fill: SolidPaint | ColorStop) { }; } - // variable - if ( - localTailwindSettings.customTailwindColors && - fill.boundVariables?.color - ) { - colorName = variableToColorName(fill.boundVariables.color); - colorType = "variable"; - meta = "custom"; - } - // solid color else { // get tailwind color as comparison From f9d42d698c2f3cf54342f6dfc9695820a80668d6 Mon Sep 17 00:00:00 2001 From: "Mims H. Wright" Date: Fri, 28 Feb 2025 17:12:53 +0100 Subject: [PATCH 032/168] Export images for HTML (#181) * Added simple image export using Base64 - groups with image bg not supported yet. * added a (flawed) way to export images from groups by exporting the paint objects * reworked background images in a much simpler way * store base64 in node so it's only done once * added some minor changes to messaging and re-drawing while trying to track down issues with the infinite image conversion issue * Fix missing prop in debug view * Prevent run from happening multiple times due to changing document during conversion * hopefully clarified the steps of exporting an image when there are child nodes. * Added toggle that allows you to disable exporting of images * embedImages is disabled for non-html languages. --- apps/debug/pages/index.tsx | 2 + apps/plugin/plugin-src/code.ts | 262 ++++++++++-------- apps/plugin/ui-src/App.tsx | 32 ++- packages/backend/package.json | 1 + packages/backend/src/altNodes/altNodeUtils.ts | 5 + packages/backend/src/code.ts | 1 + packages/backend/src/common/images.ts | 89 ++++++ packages/backend/src/html/htmlMain.ts | 108 ++++---- packages/plugin-ui/src/PluginUI.tsx | 5 +- .../plugin-ui/src/codegenPreferenceOptions.ts | 8 + .../plugin-ui/src/components/CodePanel.tsx | 30 +- packages/types/src/types.ts | 4 + pnpm-lock.yaml | 8 + 13 files changed, 361 insertions(+), 194 deletions(-) diff --git a/apps/debug/pages/index.tsx b/apps/debug/pages/index.tsx index 176790dd..f6038d25 100644 --- a/apps/debug/pages/index.tsx +++ b/apps/debug/pages/index.tsx @@ -19,6 +19,7 @@ export default function Web() { { safeRun(userPluginSettings); }; -const safeRun = (settings: PluginSettings) => { - try { - run(settings); - } catch (e) { - if (e && typeof e === "object" && "message" in e) { - const error = e as Error; - console.log("error: ", error.stack); - figma.ui.postMessage({ type: "error", error: error.message }); +// Used to prevent running from happening again. +let isLoading = false; +const safeRun = async (settings: PluginSettings) => { + if (isLoading === false) { + try { + isLoading = true; + await run(settings); + // hack to make it not immediately set to false when complete. (executes on next frame) + setTimeout(() => { + isLoading = false; + }, 1); + } catch (e) { + if (e && typeof e === "object" && "message" in e) { + const error = e as Error; + console.log("error: ", error.stack); + figma.ui.postMessage({ type: "error", error: error.message }); + } } } }; @@ -86,6 +97,10 @@ const standardMode = async () => { // Listen for document changes figma.on("documentchange", () => { + // Node: This was causing an infinite load when you try to export a background image from a group that contains children. + // The reason for this is that the code will temporarily hide the children of the group in order to export a clean image + // then restores the visibility of the children. This constitutes a document change so it's restarting the whole conversion. + // In order to stop this, we disable safeRun() when doing conversions (while isLoading === true). safeRun(userPluginSettings); }); @@ -105,121 +120,124 @@ const codegenMode = async () => { // figma.showUI(__html__, { visible: false }); await getUserSettings(); - figma.codegen.on("generate", async ({ language, node }: CodegenEvent): Promise => { - const convertedSelection = convertIntoNodes([node], null); - - switch (language) { - case "html": - return [ - { - title: "Code", - code: await htmlMain( - convertedSelection, - { ...userPluginSettings, jsx: false }, - true, - ), - language: "HTML", - }, - { - title: "Text Styles", - code: htmlCodeGenTextStyles(userPluginSettings), - language: "HTML", - }, - ]; - case "html_jsx": - return [ - { - title: "Code", - code: await htmlMain( - convertedSelection, - { ...userPluginSettings, jsx: true }, - true, - ), - language: "HTML", - }, - { - title: "Text Styles", - code: htmlCodeGenTextStyles(userPluginSettings), - language: "HTML", - }, - ]; - case "tailwind": - case "tailwind_jsx": - return [ - { - title: "Code", - code: await tailwindMain(convertedSelection, { - ...userPluginSettings, - jsx: language === "tailwind_jsx", - }), - language: "HTML", - }, - // { - // title: "Style", - // code: tailwindMain(convertedSelection, defaultPluginSettings), - // language: "HTML", - // }, - { - title: "Tailwind Colors", - code: retrieveGenericSolidUIColors("Tailwind") - .map((d) => { - let str = `${d.hex};`; - if (d.colorName !== d.hex) { - str += ` // ${d.colorName}`; - } - if (d.meta) { - str += ` (${d.meta})`; - } - return str; - }) - .join("\n"), - language: "JAVASCRIPT", - }, - { - title: "Text Styles", - code: tailwindCodeGenTextStyles(), - language: "HTML", - }, - ]; - case "flutter": - return [ - { - title: "Code", - code: flutterMain(convertedSelection, { - ...userPluginSettings, - flutterGenerationMode: "snippet", - }), - language: "SWIFT", - }, - { - title: "Text Styles", - code: flutterCodeGenTextStyles(), - language: "SWIFT", - }, - ]; - case "swiftUI": - return [ - { - title: "SwiftUI", - code: swiftuiMain(convertedSelection, { - ...userPluginSettings, - swiftUIGenerationMode: "snippet", - }), - language: "SWIFT", - }, - { - title: "Text Styles", - code: swiftUICodeGenTextStyles(), - language: "SWIFT", - }, - ]; - default: - break; - } + figma.codegen.on( + "generate", + async ({ language, node }: CodegenEvent): Promise => { + const convertedSelection = convertIntoNodes([node], null); + + switch (language) { + case "html": + return [ + { + title: "Code", + code: await htmlMain( + convertedSelection, + { ...userPluginSettings, jsx: false }, + true, + ), + language: "HTML", + }, + { + title: "Text Styles", + code: htmlCodeGenTextStyles(userPluginSettings), + language: "HTML", + }, + ]; + case "html_jsx": + return [ + { + title: "Code", + code: await htmlMain( + convertedSelection, + { ...userPluginSettings, jsx: true }, + true, + ), + language: "HTML", + }, + { + title: "Text Styles", + code: htmlCodeGenTextStyles(userPluginSettings), + language: "HTML", + }, + ]; + case "tailwind": + case "tailwind_jsx": + return [ + { + title: "Code", + code: await tailwindMain(convertedSelection, { + ...userPluginSettings, + jsx: language === "tailwind_jsx", + }), + language: "HTML", + }, + // { + // title: "Style", + // code: tailwindMain(convertedSelection, defaultPluginSettings), + // language: "HTML", + // }, + { + title: "Tailwind Colors", + code: retrieveGenericSolidUIColors("Tailwind") + .map((d) => { + let str = `${d.hex};`; + if (d.colorName !== d.hex) { + str += ` // ${d.colorName}`; + } + if (d.meta) { + str += ` (${d.meta})`; + } + return str; + }) + .join("\n"), + language: "JAVASCRIPT", + }, + { + title: "Text Styles", + code: tailwindCodeGenTextStyles(), + language: "HTML", + }, + ]; + case "flutter": + return [ + { + title: "Code", + code: flutterMain(convertedSelection, { + ...userPluginSettings, + flutterGenerationMode: "snippet", + }), + language: "SWIFT", + }, + { + title: "Text Styles", + code: flutterCodeGenTextStyles(), + language: "SWIFT", + }, + ]; + case "swiftUI": + return [ + { + title: "SwiftUI", + code: swiftuiMain(convertedSelection, { + ...userPluginSettings, + swiftUIGenerationMode: "snippet", + }), + language: "SWIFT", + }, + { + title: "Text Styles", + code: swiftUICodeGenTextStyles(), + language: "SWIFT", + }, + ]; + default: + break; + } - const blocks: CodegenResult[] = []; - return blocks; - }); + const blocks: CodegenResult[] = []; + return blocks; + }, + ); }; switch (figma.mode) { diff --git a/apps/plugin/ui-src/App.tsx b/apps/plugin/ui-src/App.tsx index b7e0ad8f..d7cca718 100644 --- a/apps/plugin/ui-src/App.tsx +++ b/apps/plugin/ui-src/App.tsx @@ -111,14 +111,26 @@ export default function App() { }, []); const handleFrameworkChange = (updatedFramework: Framework) => { - setState((prevState) => ({ - ...prevState, - // code: "// Loading...", - selectedFramework: updatedFramework, - })); - postUISettingsChangingMessage("framework", updatedFramework, { - targetOrigin: "*", - }); + if (updatedFramework !== state.selectedFramework) { + setState((prevState) => ({ + ...prevState, + // code: "// Loading...", + selectedFramework: updatedFramework, + })); + postUISettingsChangingMessage("framework", updatedFramework, { + targetOrigin: "*", + }); + } + }; + const handlePreferencesChange = ( + key: keyof PluginSettings, + value: boolean | string, + ) => { + if (state.settings && state.settings[key] === value) { + // do nothing + } else { + postUISettingsChangingMessage(key, value, { targetOrigin: "*" }); + } }; const darkMode = figmaColorBgValue !== "#ffffff"; @@ -131,11 +143,9 @@ export default function App() { warnings={state.warnings} selectedFramework={state.selectedFramework} setSelectedFramework={handleFrameworkChange} + onPreferenceChanged={handlePreferencesChange} htmlPreview={state.htmlPreview} settings={state.settings} - onPreferenceChanged={(key: string, value: boolean | string) => - postUISettingsChangingMessage(key, value, { targetOrigin: "*" }) - } colors={state.colors} gradients={state.gradients} /> diff --git a/packages/backend/package.json b/packages/backend/package.json index 7b2a6abf..d8aa0167 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -14,6 +14,7 @@ }, "dependencies": { "@figma/plugin-typings": "^1.105.0", + "js-base64": "^3.7.7", "react": "18.3.1", "react-dom": "18.3.1", "types": "workspace:*" diff --git a/packages/backend/src/altNodes/altNodeUtils.ts b/packages/backend/src/altNodes/altNodeUtils.ts index 53de7f44..1c1601b2 100644 --- a/packages/backend/src/altNodes/altNodeUtils.ts +++ b/packages/backend/src/altNodes/altNodeUtils.ts @@ -42,6 +42,11 @@ export const isTypeOrGroupOfTypes = curry( }, ); +export const isSVGNode = (node: SceneNode) => { + const altNode = node as AltNode; + return altNode.canBeFlattened; +}; + export const renderNodeAsSVG = async (node: SceneNode) => await node.exportAsync({ format: "SVG_STRING" }); diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index efa52472..93d1d6da 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -19,6 +19,7 @@ import { convertToCode } from "./common/retrieveUI/convertToCode"; export const run = async (settings: PluginSettings) => { clearWarnings(); + const { framework } = settings; const selection = figma.currentPage.selection; diff --git a/packages/backend/src/common/images.ts b/packages/backend/src/common/images.ts index dad86579..c1e2fe2e 100644 --- a/packages/backend/src/common/images.ts +++ b/packages/backend/src/common/images.ts @@ -1,3 +1,7 @@ +import { AltNode, ExportableNode } from "types"; +import { btoa } from "js-base64"; +import { addWarning } from "./commonConversionWarnings"; + export const PLACEHOLDER_IMAGE_DOMAIN = "https://placehold.co"; export const getPlaceholderImage = (w: number, h = -1) => { @@ -5,3 +9,88 @@ export const getPlaceholderImage = (w: number, h = -1) => { const _h = (h < 0 ? w : h).toFixed(0); return `${PLACEHOLDER_IMAGE_DOMAIN}/${_w}x${_h}`; }; + +const fillIsImage = ({ type }: Paint) => type === "IMAGE"; + +export const getImageFills = (node: MinimalFillsMixin): ImagePaint[] => { + try { + return (node.fills as ImagePaint[]).filter(fillIsImage); + } catch (e) { + return []; + } +}; + +export const nodeHasImageFill = (node: MinimalFillsMixin): Boolean => + getImageFills(node).length > 0; + +export const nodeHasMultipleFills = (node: MinimalFillsMixin) => + node.fills instanceof Array && node.fills.length > 1; + +const imageBytesToBase64 = (bytes: Uint8Array): string => { + // Convert Uint8Array to binary string + const binaryString = bytes.reduce((data, byte) => { + return data + String.fromCharCode(byte); + }, ""); + + // Encode binary string to base64 + const b64 = btoa(binaryString); + + return `data:image/png;base64,${b64}`; +}; + +export const exportNodeAsBase64PNG = async ( + node: AltNode, + excludeChildren: boolean, +) => { + // Shorcut export if the node has already been converted. + if (node.base64 !== undefined && node.base64 !== "") { + return node.base64; + } + + const n: ExportableNode = node.originalNode; + + if (n.exportAsync === undefined) { + console.log(n); + throw new TypeError( + "Something went wrong. This node doesn't have an exportAsync function. Maybe check the type before calling this function.", + ); + } + + const temporarilyHideChildren = + excludeChildren && "children" in n && n.children.length > 0; + const parent = n as ChildrenMixin; + const originalVisibility = new Map(); + + if (temporarilyHideChildren) { + // Store the original visible state of children + parent.children.map((child: SceneNode) => + originalVisibility.set(child, child.visible), + ), + // Temporarily hide all children + parent.children.forEach((child) => { + child.visible = false; + }); + } + + // export the image as bytes + const exportSettings: ExportSettingsImage = { + format: "PNG", + constraint: { type: "SCALE", value: 1 }, + }; + const bytes = await n.exportAsync(exportSettings); + + if (temporarilyHideChildren) { + // After export, restore visibility + parent.children.forEach((child) => { + child.visible = originalVisibility.get(child) ?? false; + }); + } + + addWarning("Some images exported as Base64 PNG"); + + // Encode binary string to base64 + const base64 = imageBytesToBase64(bytes); + // Save the value so it's only calculated once. + node.base64 = base64; + return base64; +}; diff --git a/packages/backend/src/html/htmlMain.ts b/packages/backend/src/html/htmlMain.ts index acf5fd38..4d1399fe 100644 --- a/packages/backend/src/html/htmlMain.ts +++ b/packages/backend/src/html/htmlMain.ts @@ -1,15 +1,24 @@ import { indentString } from "../common/indentString"; -import { retrieveTopFill } from "../common/retrieveFill"; import { HtmlTextBuilder } from "./htmlTextBuilder"; import { HtmlDefaultBuilder } from "./htmlDefaultBuilder"; import { htmlAutoLayoutProps } from "./builderImpl/htmlAutoLayout"; import { formatWithJSX } from "../common/parseJSX"; import { commonSortChildrenWhenInferredAutoLayout } from "../common/commonChildrenOrder"; -import { addWarning } from "../common/commonConversionWarnings"; -import { PluginSettings, HTMLPreview, AltNode, HTMLSettings } from "types"; -import { renderAndAttachSVG } from "../altNodes/altNodeUtils"; +import { + PluginSettings, + HTMLPreview, + AltNode, + HTMLSettings, + ExportableNode, +} from "types"; +import { isSVGNode, renderAndAttachSVG } from "../altNodes/altNodeUtils"; import { getVisibleNodes } from "../common/nodeVisibility"; -import { getPlaceholderImage } from "../common/images"; +import { + exportNodeAsBase64PNG, + getPlaceholderImage, + nodeHasImageFill, +} from "../common/images"; +import { addWarning } from "../common/commonConversionWarnings"; const selfClosingTags = ["img"]; @@ -76,32 +85,35 @@ const htmlWidgetGenerator = async ( }; const convertNode = (settings: HTMLSettings) => async (node: SceneNode) => { - const altNode = await renderAndAttachSVG(node); - if (altNode.svg) return htmlWrapSVG(altNode, settings); + if (isSVGNode(node)) { + const altNode = await renderAndAttachSVG(node); + if (altNode.svg) return htmlWrapSVG(altNode, settings); + } switch (node.type) { case "RECTANGLE": case "ELLIPSE": - return htmlContainer(node, "", [], settings); + return await htmlContainer(node, "", [], settings); case "GROUP": - return htmlGroup(node, settings); + return await htmlGroup(node, settings); case "FRAME": case "COMPONENT": case "INSTANCE": case "COMPONENT_SET": - return htmlFrame(node, settings); + return await htmlFrame(node, settings); case "SECTION": - return htmlSection(node, settings); + return await htmlSection(node, settings); case "TEXT": return htmlText(node, settings); case "LINE": return htmlLine(node, settings); case "VECTOR": - addWarning("VectorNodes are not fully supported in HTML"); - return htmlAsset(node, settings); + throw new Error( + "Normally vector type nodes are converted to SVG so this code point should be unreachable.", + ); default: + return ""; } - return ""; }; const htmlWrapSVG = ( @@ -195,7 +207,7 @@ const htmlFrame = async ( if (node.layoutMode !== "NONE") { const rowColumn = htmlAutoLayoutProps(node, node, settings); - return htmlContainer(node, childrenStr, rowColumn, settings); + return await htmlContainer(node, childrenStr, rowColumn, settings); } else { if (settings.optimizeLayout && node.inferredAutoLayout !== null) { const rowColumn = htmlAutoLayoutProps( @@ -203,39 +215,18 @@ const htmlFrame = async ( node.inferredAutoLayout, settings, ); - return htmlContainer(node, childrenStr, rowColumn, settings); + return await htmlContainer(node, childrenStr, rowColumn, settings); } // node.layoutMode === "NONE" && node.children.length > 1 // children needs to be absolute - return htmlContainer(node, childrenStr, [], settings); + return await htmlContainer(node, childrenStr, [], settings); } }; -const htmlAsset = async ( - node: SceneNode, - settings: HTMLSettings, -): Promise => { - if (!("opacity" in node) || !("layoutAlign" in node) || !("fills" in node)) { - return ""; - } - - const builder = new HtmlDefaultBuilder(node, settings) - .commonPositionStyles() - .commonShapeStyles(); - - if (retrieveTopFill(node.fills)?.type === "IMAGE") { - addWarning("Image fills are replaced with placeholders"); - const imgUrl = getPlaceholderImage(node.width, node.height); - return `\n`; - } - - return `\n
`; -}; - // properties named propSomething always take care of "," // sometimes a property might not exist, so it doesn't add "," -const htmlContainer = ( +const htmlContainer = async ( node: SceneNode & SceneNodeMixin & BlendMixin & @@ -245,11 +236,11 @@ const htmlContainer = ( children: string, additionalStyles: string[] = [], settings: HTMLSettings, -): string => { +): Promise => { // ignore the view when size is zero or less // while technically it shouldn't get less than 0, due to rounding errors, // it can get to values like: -0.000004196293048153166 - if (node.width < 0 || node.height <= 0) { + if (node.width <= 0 || node.height <= 0) { return children; } @@ -260,16 +251,36 @@ const htmlContainer = ( if (builder.styles || additionalStyles) { let tag = "div"; let src = ""; - if (retrieveTopFill(node.fills)?.type === "IMAGE") { - addWarning("Image fills are replaced with placeholders"); - const imageURL = getPlaceholderImage(node.width, node.height); - if (!("children" in node) || node.children.length === 0) { - tag = "img"; - src = ` src="${imageURL}"`; + + if (nodeHasImageFill(node)) { + const altNode = node as AltNode; + const hasChildren = "children" in node && node.children.length > 0; + let imgUrl = ""; + + // TODO: This overrides the embedImages setting to only happen when HTML is selected but + // really this should be more of a global setting that isn't tied to a specific framework. + // It's being disabled in this way so the HTML preview will only embed images when it's HTML outuput. + // The reason this is so important is that it's a costly operation an it will slow down + // the generation of code for other languages and display different results in the preview + // than what the output will look like. + if ( + settings.embedImages && + (settings as PluginSettings).framework === "HTML" + ) { + imgUrl = (await exportNodeAsBase64PNG(altNode, hasChildren)) ?? ""; } else { + addWarning("Some images were exported as placeholder URLs"); + imgUrl = getPlaceholderImage(node.width, node.height); + } + + if (hasChildren) { builder.addStyles( - formatWithJSX("background-image", settings.jsx, `url(${imageURL})`), + formatWithJSX("background-image", settings.jsx, `url(${imgUrl})`), ); + } else { + // if node has NO children + tag = "img"; + src = ` src="${imgUrl}"`; } } @@ -283,7 +294,6 @@ const htmlContainer = ( return `\n<${tag}${build}${src}>`; } } - return children; }; diff --git a/packages/plugin-ui/src/PluginUI.tsx b/packages/plugin-ui/src/PluginUI.tsx index b446f750..d3388757 100644 --- a/packages/plugin-ui/src/PluginUI.tsx +++ b/packages/plugin-ui/src/PluginUI.tsx @@ -25,7 +25,10 @@ type PluginUIProps = { selectedFramework: Framework; setSelectedFramework: (framework: Framework) => void; settings: PluginSettings | null; - onPreferenceChanged: (key: string, value: boolean | string) => void; + onPreferenceChanged: ( + key: keyof PluginSettings, + value: boolean | string, + ) => void; colors: SolidColorConversion[]; gradients: LinearGradientConversion[]; isLoading: boolean; diff --git a/packages/plugin-ui/src/codegenPreferenceOptions.ts b/packages/plugin-ui/src/codegenPreferenceOptions.ts index 9caa850a..946a96e6 100644 --- a/packages/plugin-ui/src/codegenPreferenceOptions.ts +++ b/packages/plugin-ui/src/codegenPreferenceOptions.ts @@ -1,6 +1,14 @@ import { LocalCodegenPreferenceOptions, SelectPreferenceOptions } from "types"; export const preferenceOptions: LocalCodegenPreferenceOptions[] = [ + { + itemType: "individual_select", + propertyName: "embedImages", + label: "Embed Images", + description: "Convert images to Base64 and embed them in the code.", + isDefault: false, + includedLanguages: ["HTML"], + }, { itemType: "individual_select", propertyName: "jsx", diff --git a/packages/plugin-ui/src/components/CodePanel.tsx b/packages/plugin-ui/src/components/CodePanel.tsx index 8e0cea77..2ae5413d 100644 --- a/packages/plugin-ui/src/components/CodePanel.tsx +++ b/packages/plugin-ui/src/components/CodePanel.tsx @@ -16,7 +16,10 @@ interface CodePanelProps { settings: PluginSettings | null; preferenceOptions: LocalCodegenPreferenceOptions[]; selectPreferenceOptions: SelectPreferenceOptions[]; - onPreferenceChanged: (key: string, value: boolean | string) => void; + onPreferenceChanged: ( + key: keyof PluginSettings, + value: boolean | string, + ) => void; } const CodePanel = (props: CodePanelProps) => { @@ -34,20 +37,25 @@ const CodePanel = (props: CodePanelProps) => { // State for custom prefix for Tailwind classes. // It is initially set from settings (if available) or an empty string. - const [customPrefix, setCustomPrefix] = useState(settings?.customTailwindPrefix || ""); + const [customPrefix, setCustomPrefix] = useState( + settings?.customTailwindPrefix || "", + ); // Helper function to add the prefix before every class (or className) in the code. // It finds every occurrence of class="..." or className="..." and, for each class, // prepends the custom prefix. const applyPrefixToClasses = (codeString: string, prefix: string) => { - return codeString.replace(/(class(?:Name)?)="([^"]*)"/g, (match, attr, classes) => { - const prefixedClasses = classes - .split(/\s+/) - .filter(Boolean) - .map((cls: string) => prefix + cls) - .join(" "); - return `${attr}="${prefixedClasses}"`; - }); + return codeString.replace( + /(class(?:Name)?)="([^"]*)"/g, + (match, attr, classes) => { + const prefixedClasses = classes + .split(/\s+/) + .filter(Boolean) + .map((cls: string) => prefix + cls) + .join(" "); + return `${attr}="${prefixedClasses}"`; + }, + ); }; // If the selected framework is Tailwind and a prefix is provided then transform the code. @@ -131,7 +139,7 @@ const CodePanel = (props: CodePanelProps) => { onChange={(e) => { const newVal = e.target.value; setCustomPrefix(newVal); - onPreferenceChanged("tailwindPrefix", newVal); + onPreferenceChanged("customTailwindPrefix", newVal); }} placeholder="e.g., tw-" className="mt-1 p-1 px-2 border border-gray-300 rounded bg-neutral-100 dark:bg-neutral-700 text-sm" diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index bc8a6a50..ac30d3ef 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -5,6 +5,7 @@ export interface HTMLSettings { jsx: boolean; optimizeLayout: boolean; showLayerNames: boolean; + embedImages: boolean; } export interface TailwindSettings extends HTMLSettings { roundTailwindValues: boolean; @@ -72,9 +73,12 @@ export type AltNodeMetadata = { originalNode: T; canBeFlattened: boolean; svg?: string; + base64?: string; }; export type AltNode = T & AltNodeMetadata; +export type ExportableNode = SceneNode & ExportMixin & MinimalFillsMixin; + // Styles & Conversions export type LayoutMode = diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 97fbcdae..13accd9a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -157,6 +157,9 @@ importers: '@figma/plugin-typings': specifier: ^1.105.0 version: 1.105.0 + js-base64: + specifier: ^3.7.7 + version: 3.7.7 react: specifier: 18.3.1 version: 18.3.1 @@ -2148,6 +2151,9 @@ packages: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} engines: {node: '>=10'} + js-base64@3.7.7: + resolution: {integrity: sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -4902,6 +4908,8 @@ snapshots: joycon@3.1.1: {} + js-base64@3.7.7: {} + js-tokens@4.0.0: {} js-yaml@4.1.0: From e4036d308e6e7494b1c9b33b2365747a090f4109 Mon Sep 17 00:00:00 2001 From: "Mims H. Wright" Date: Tue, 4 Mar 2025 17:36:08 +0100 Subject: [PATCH 033/168] Remeoved deprecated reference to constrainProportions (#187) --- packages/backend/src/altNodes/altConversion.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/altNodes/altConversion.ts b/packages/backend/src/altNodes/altConversion.ts index ca06901b..c74c1440 100644 --- a/packages/backend/src/altNodes/altConversion.ts +++ b/packages/backend/src/altNodes/altConversion.ts @@ -97,7 +97,8 @@ export const cloneNode = ( prop !== "componentPropertyDefinitions" && prop !== "exposedInstances" && prop !== "componentProperties" && - prop !== "componenPropertyReferences" + prop !== "componenPropertyReferences" && + prop !== "constrainProportions" ) { cloned[prop as keyof T] = node[prop as keyof T]; } From b68b74c891fde878fad7b430a589d2179b979ca1 Mon Sep 17 00:00:00 2001 From: "Mims H. Wright" Date: Tue, 4 Mar 2025 17:37:46 +0100 Subject: [PATCH 034/168] Patch flash when loading (#186) * Wrapped exportAsync in a proxy method that allows us to also message the UI when you call it so that it can display a loading message. * Added comment for clarity --- packages/backend/src/altNodes/altNodeUtils.ts | 7 ++- packages/backend/src/code.ts | 4 -- .../backend/src/common/exportAsyncProxy.ts | 43 +++++++++++++++++++ packages/backend/src/common/images.ts | 10 +---- 4 files changed, 50 insertions(+), 14 deletions(-) create mode 100644 packages/backend/src/common/exportAsyncProxy.ts diff --git a/packages/backend/src/altNodes/altNodeUtils.ts b/packages/backend/src/altNodes/altNodeUtils.ts index 1c1601b2..721d566b 100644 --- a/packages/backend/src/altNodes/altNodeUtils.ts +++ b/packages/backend/src/altNodes/altNodeUtils.ts @@ -1,5 +1,6 @@ import { AltNode } from "types"; import { curry } from "../common/curry"; +import { exportAsyncProxy } from "../common/exportAsyncProxy"; export const overrideReadonlyProperty = curry( (prop: K, value: any, obj: T): T => @@ -48,7 +49,9 @@ export const isSVGNode = (node: SceneNode) => { }; export const renderNodeAsSVG = async (node: SceneNode) => - await node.exportAsync({ format: "SVG_STRING" }); + await exportAsyncProxy(node, { + format: "SVG_STRING", + }); export const renderAndAttachSVG = async (node: SceneNode) => { const altNode = node as AltNode; @@ -60,7 +63,7 @@ export const renderAndAttachSVG = async (node: SceneNode) => { return altNode; } // console.log(`${nodeName} can be flattened!`); - const svg = await renderNodeAsSVG(altNode.originalNode); + const svg = (await renderNodeAsSVG(altNode.originalNode)) as string; // console.log(`${svg}`); altNode.svg = svg; } diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index 93d1d6da..1dd94cea 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -29,10 +29,6 @@ export const run = async (settings: PluginSettings) => { ); } - postConversionStart(); - // force postMessage to run right now. - await new Promise((resolve) => setTimeout(resolve, 30)); - const convertedSelection = convertNodesToAltNodes(selection, null); // ignore when nothing was selected diff --git a/packages/backend/src/common/exportAsyncProxy.ts b/packages/backend/src/common/exportAsyncProxy.ts new file mode 100644 index 00000000..0d8e6c16 --- /dev/null +++ b/packages/backend/src/common/exportAsyncProxy.ts @@ -0,0 +1,43 @@ +import { postConversionStart } from "../messaging"; + +let isRunning = false; + +/* + * This is a wrapper for exportAsync() This allows us to pass a message to the UI every time + * this rather costly operation gets run so that it can display a loading message. This avoids + * showing a loading message every time anything in the UI changes and only showing it when + * exportAsync() is called. + */ +export const exportAsyncProxy = async < + T extends string | Uint8Array = Uint8Array /* | Object */, +>( + node: SceneNode, + settings: ExportSettings | ExportSettingsSVGString /*| ExportSettingsREST*/, +): Promise => { + if (node.exportAsync === undefined) { + // console.log(node); + throw new TypeError( + "Something went wrong. This node doesn't have an exportAsync() function. Maybe check the type before calling this function.", + ); + } + + if (isRunning === false) { + isRunning = true; + postConversionStart(); + // force postMessage to run right now. + await new Promise((resolve) => setTimeout(resolve, 30)); + } + + // The following is necessary for typescript to not lose its mind. + let result; + if (settings.format === "SVG_STRING") { + result = await node.exportAsync(settings as ExportSettingsSVGString); + // } else if (settings.format === "JSON_REST_V1") { + // result = await node.exportAsync(settings as ExportSettingsREST); + } else { + result = await node.exportAsync(settings as ExportSettings); + } + + isRunning = false; + return result as T; +}; diff --git a/packages/backend/src/common/images.ts b/packages/backend/src/common/images.ts index c1e2fe2e..3acadbe9 100644 --- a/packages/backend/src/common/images.ts +++ b/packages/backend/src/common/images.ts @@ -1,6 +1,7 @@ import { AltNode, ExportableNode } from "types"; import { btoa } from "js-base64"; import { addWarning } from "./commonConversionWarnings"; +import { exportAsyncProxy } from "./exportAsyncProxy"; export const PLACEHOLDER_IMAGE_DOMAIN = "https://placehold.co"; @@ -49,13 +50,6 @@ export const exportNodeAsBase64PNG = async ( const n: ExportableNode = node.originalNode; - if (n.exportAsync === undefined) { - console.log(n); - throw new TypeError( - "Something went wrong. This node doesn't have an exportAsync function. Maybe check the type before calling this function.", - ); - } - const temporarilyHideChildren = excludeChildren && "children" in n && n.children.length > 0; const parent = n as ChildrenMixin; @@ -77,7 +71,7 @@ export const exportNodeAsBase64PNG = async ( format: "PNG", constraint: { type: "SCALE", value: 1 }, }; - const bytes = await n.exportAsync(exportSettings); + const bytes = await exportAsyncProxy(n, exportSettings); if (temporarilyHideChildren) { // After export, restore visibility From 30ee7edd1276e6b09f1f0faa0ee3e2dff5932800 Mon Sep 17 00:00:00 2001 From: "Mims H. Wright" Date: Sat, 15 Mar 2025 00:23:24 +0100 Subject: [PATCH 035/168] Added an issue template (#189) --- .github/ISSUE_TEMPLATE/bug_report.yaml | 64 ++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yaml diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml new file mode 100644 index 00000000..70169028 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -0,0 +1,64 @@ +name: Bug Report +description: Report a bug in the project +title: "[bug]: " +labels: ["bug", "needs triage"] +assignees: [] + +body: + - type: markdown + attributes: + value: "## 🐛 Bug Report\nPlease fill out the details below." + + - type: textarea + id: reproduction + attributes: + label: "Settings / Steps to Reproduce" + description: "Provide a step-by-step guide on how to reproduce the bug. Please include the plugin settings you were using." + placeholder: "Framework: Tailwind with JSX\n1. Selected design\n2. Selected Optimize Layers\n3. See error" + validations: + required: true + + - type: textarea + id: expected-behavior + attributes: + label: "Expected Behavior" + description: "What did you expect to happen?" + placeholder: "The button should navigate to the dashboard." + validations: + required: true + + - type: textarea + id: actual-behavior + attributes: + label: "Actual Behavior" + description: "What actually happened?" + placeholder: "The button does nothing." + validations: + required: true + + - type: input + id: design-link + attributes: + label: "Design Reference" + description: "Provide a link to the design that you were working on (if possible)." + placeholder: "Figma design link" + validations: + required: false + + - type: textarea + id: screenshots + attributes: + label: "Screenshots or Videos" + description: "If applicable, add screenshots or screen recordings to help explain the issue." + placeholder: "Drag and drop images here or paste them from your clipboard." + validations: + required: false + + - type: textarea + id: environment + attributes: + label: "Environment" + description: "Optionally provide details about your development environment." + placeholder: "OS: macOS 14.1\nBrowser: Chrome 120\nNode.js: 18.17.0" + validations: + required: true From afcc5dc90bf2b8cdd7404a1e1ed4f3a5015b26da Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Sat, 1 Mar 2025 17:24:32 -0300 Subject: [PATCH 036/168] Total refactor part 1. --- apps/debug/package.json | 10 +- apps/plugin/package.json | 16 +- apps/plugin/plugin-src/code.ts | 52 +- package.json | 8 +- packages/backend/package.json | 10 +- .../backend/src/altNodes/altConversion.ts | 87 +- packages/backend/src/code.ts | 158 +- packages/backend/src/common/color.ts | 101 + packages/backend/src/common/nodeVisibility.ts | 4 +- .../src/flutter/builderImpl/flutterBorder.ts | 2 +- .../src/flutter/builderImpl/flutterColor.ts | 24 +- .../backend/src/flutter/flutterContainer.ts | 4 +- packages/backend/src/flutter/flutterMain.ts | 3 +- .../backend/src/html/builderImpl/htmlBlend.ts | 4 +- packages/backend/src/html/htmlMain.ts | 9 +- .../src/swiftui/builderImpl/swiftuiColor.ts | 31 +- .../src/swiftui/swiftuiDefaultBuilder.ts | 2 +- packages/backend/src/swiftui/swiftuiMain.ts | 3 +- .../builderImpl/tailwindAutoLayout.ts | 21 +- .../tailwind/builderImpl/tailwindBorder.ts | 1 - .../src/tailwind/builderImpl/tailwindColor.ts | 11 +- .../src/tailwind/tailwindDefaultBuilder.ts | 3 +- packages/backend/src/tailwind/tailwindMain.ts | 272 ++- packages/eslint-config-custom/package.json | 4 +- packages/plugin-ui/package.json | 6 +- .../plugin-ui/src/codegenPreferenceOptions.ts | 24 +- .../plugin-ui/src/components/CodePanel.tsx | 8 +- packages/types/package.json | 8 +- packages/types/src/types.ts | 1 + pnpm-lock.yaml | 2057 +++++++++-------- 30 files changed, 1662 insertions(+), 1282 deletions(-) diff --git a/apps/debug/package.json b/apps/debug/package.json index 044d2f36..1efead3a 100644 --- a/apps/debug/package.json +++ b/apps/debug/package.json @@ -11,21 +11,21 @@ }, "dependencies": { "backend": "workspace:*", - "next": "^14.2.20", + "next": "^14.2.24", "plugin-ui": "workspace:*", "react": "^18.3.1", "react-dom": "^18.3.1" }, "devDependencies": { - "@types/node": "^20.17.10", - "@types/react": "^18.3.17", + "@types/node": "^20.17.21", + "@types/react": "^18.3.18", "@types/react-dom": "^18.3.5", "autoprefixer": "^10.4.20", "eslint-config-custom": "workspace:*", - "postcss": "^8.4.49", + "postcss": "^8.5.3", "tailwindcss": "3.4.6", "tsconfig": "workspace:*", "types": "workspace:*", - "typescript": "^5.7.2" + "typescript": "^5.8.2" } } diff --git a/apps/plugin/package.json b/apps/plugin/package.json index 12272dbc..4f69a1c1 100644 --- a/apps/plugin/package.json +++ b/apps/plugin/package.json @@ -10,32 +10,32 @@ "dev": "pnpm build:watch" }, "dependencies": { - "@figma/plugin-typings": "^1.105.0", + "@figma/plugin-typings": "^1.108.0", "backend": "workspace:*", "plugin-ui": "workspace:*", "react": "^18.3.1", "react-dom": "^18.3.1" }, "devDependencies": { - "@types/node": "^20.17.10", - "@types/react": "^18.3.17", + "@types/node": "^20.17.21", + "@types/react": "^18.3.18", "@types/react-dom": "^18.3.5", "@typescript-eslint/eslint-plugin": "^7.18.0", "@typescript-eslint/parser": "^7.18.0", "@vitejs/plugin-react": "^4.3.4", - "@vitejs/plugin-react-swc": "^3.7.2", + "@vitejs/plugin-react-swc": "^3.8.0", "autoprefixer": "^10.4.20", "concurrently": "^8.2.2", "esbuild": "^0.23.1", "eslint-config-custom": "workspace:*", "eslint-plugin-react-hooks": "^4.6.2", - "eslint-plugin-react-refresh": "^0.4.16", - "postcss": "^8.4.49", + "eslint-plugin-react-refresh": "^0.4.19", + "postcss": "^8.5.3", "tailwindcss": "3.4.6", "tsconfig": "workspace:*", - "typescript": "^5.7.2", "types": "workspace:*", - "vite": "^5.4.11", + "typescript": "^5.8.2", + "vite": "^5.4.14", "vite-plugin-singlefile": "^2.1.0" } } diff --git a/apps/plugin/plugin-src/code.ts b/apps/plugin/plugin-src/code.ts index 8a984412..093809bb 100644 --- a/apps/plugin/plugin-src/code.ts +++ b/apps/plugin/plugin-src/code.ts @@ -8,6 +8,7 @@ import { htmlMain, postSettingsChanged, } from "backend"; +import { nodesToJSON } from "backend/src/code"; import { retrieveGenericSolidUIColors } from "backend/src/common/retrieveUI/retrieveColors"; import { flutterCodeGenTextStyles } from "backend/src/flutter/flutterMain"; import { htmlCodeGenTextStyles } from "backend/src/html/htmlMain"; @@ -30,6 +31,7 @@ export const defaultPluginSettings: PluginSettings = { customTailwindColors: false, customTailwindPrefix: "", embedImages: false, + embedVectors: false, }; // A helper type guard to ensure the key belongs to the PluginSettings type @@ -38,8 +40,13 @@ function isKeyOfPluginSettings(key: string): key is keyof PluginSettings { } const getUserSettings = async () => { + console.log("[DEBUG] getUserSettings - Starting to fetch user settings"); const possiblePluginSrcSettings = (await figma.clientStorage.getAsync("userPluginSettings")) ?? {}; + console.log( + "[DEBUG] getUserSettings - Raw settings from storage:", + possiblePluginSrcSettings, + ); const updatedPluginSrcSettings = { ...defaultPluginSettings, @@ -57,46 +64,72 @@ const getUserSettings = async () => { }; userPluginSettings = updatedPluginSrcSettings as PluginSettings; + console.log("[DEBUG] getUserSettings - Final settings:", userPluginSettings); + return userPluginSettings; }; const initSettings = async () => { + console.log("[DEBUG] initSettings - Initializing plugin settings"); await getUserSettings(); postSettingsChanged(userPluginSettings); + console.log("[DEBUG] initSettings - Calling safeRun with settings"); safeRun(userPluginSettings); }; // Used to prevent running from happening again. let isLoading = false; const safeRun = async (settings: PluginSettings) => { + console.log( + "[DEBUG] safeRun - Called with isLoading =", + isLoading, + "selection =", + figma.currentPage.selection, + ); if (isLoading === false) { try { isLoading = true; + console.log("[DEBUG] safeRun - Starting run execution"); await run(settings); + console.log("[DEBUG] safeRun - Run execution completed"); // hack to make it not immediately set to false when complete. (executes on next frame) setTimeout(() => { + console.log("[DEBUG] safeRun - Resetting isLoading to false"); isLoading = false; }, 1); } catch (e) { + console.log("[DEBUG] safeRun - Error caught in execution"); + isLoading = false; // Make sure to reset the flag on error if (e && typeof e === "object" && "message" in e) { const error = e as Error; console.log("error: ", error.stack); figma.ui.postMessage({ type: "error", error: error.message }); } } + } else { + console.log( + "[DEBUG] safeRun - Skipping execution because isLoading =", + isLoading, + ); } }; const standardMode = async () => { + console.log("[DEBUG] standardMode - Starting standard mode initialization"); figma.showUI(__html__, { width: 450, height: 700, themeColors: true }); await initSettings(); // Listen for selection changes figma.on("selectionchange", () => { + console.log( + "[DEBUG] selectionchange event - New selection:", + figma.currentPage.selection, + ); safeRun(userPluginSettings); }); // Listen for document changes figma.on("documentchange", () => { + console.log("[DEBUG] documentchange event triggered"); // Node: This was causing an infinite load when you try to export a background image from a group that contains children. // The reason for this is that the code will temporarily hide the children of the group in order to export a clean image // then restores the visibility of the children. This constitutes a document change so it's restarting the whole conversion. @@ -105,10 +138,11 @@ const standardMode = async () => { }); figma.ui.onmessage = (msg) => { - console.log("[node] figma.ui.onmessage", msg); + console.log("[DEBUG] figma.ui.onmessage", msg); if (msg.type === "pluginSettingWillChange") { const { key, value } = msg as SettingWillChangeMessage; + console.log(`[DEBUG] Setting changed: ${key} = ${value}`); (userPluginSettings as any)[key] = value; figma.clientStorage.setAsync("userPluginSettings", userPluginSettings); safeRun(userPluginSettings); @@ -117,13 +151,24 @@ const standardMode = async () => { }; const codegenMode = async () => { + console.log("[DEBUG] codegenMode - Starting codegen mode initialization"); // figma.showUI(__html__, { visible: false }); await getUserSettings(); figma.codegen.on( "generate", async ({ language, node }: CodegenEvent): Promise => { - const convertedSelection = convertIntoNodes([node], null); + console.log( + `[DEBUG] codegen.generate - Language: ${language}, Node:`, + node, + ); + + const nodeJson = await nodesToJSON([node]); + const convertedSelection = await convertIntoNodes(nodeJson, null); + console.log( + "[DEBUG] codegen.generate - Converted selection:", + convertedSelection, + ); switch (language) { case "html": @@ -243,11 +288,14 @@ const codegenMode = async () => { switch (figma.mode) { case "default": case "inspect": + console.log("[DEBUG] Starting plugin in", figma.mode, "mode"); standardMode(); break; case "codegen": + console.log("[DEBUG] Starting plugin in codegen mode"); codegenMode(); break; default: + console.log("[DEBUG] Unknown plugin mode:", figma.mode); break; } diff --git a/package.json b/package.json index 5257cd4a..41e66f7a 100644 --- a/package.json +++ b/package.json @@ -10,10 +10,10 @@ "format": "prettier --write \"**/*.{ts,tsx,css,md}\"" }, "devDependencies": { - "eslint": "^9.17.0", + "eslint": "^9.21.0", "eslint-config-custom": "workspace:*", - "prettier": "^3.4.2", - "turbo": "^2.3.3", - "typescript": "^5.7.2" + "prettier": "^3.5.2", + "turbo": "^2.4.4", + "typescript": "^5.8.2" } } diff --git a/packages/backend/package.json b/packages/backend/package.json index d8aa0167..baab8739 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -13,19 +13,19 @@ "lint": "eslint \"src/**/*.ts*\"" }, "dependencies": { - "@figma/plugin-typings": "^1.105.0", + "@figma/plugin-typings": "^1.108.0", "js-base64": "^3.7.7", "react": "18.3.1", "react-dom": "18.3.1", "types": "workspace:*" }, "devDependencies": { - "@types/react": "^18.3.17", + "@types/react": "^18.3.18", "@types/react-dom": "^18.3.5", - "eslint": "^9.17.0", + "eslint": "^9.21.0", "eslint-config-custom": "workspace:*", "tsconfig": "workspace:*", - "tsup": "^8.3.5", - "typescript": "^5.7.2" + "tsup": "^8.4.0", + "typescript": "^5.8.2" } } diff --git a/packages/backend/src/altNodes/altConversion.ts b/packages/backend/src/altNodes/altConversion.ts index c74c1440..59b0db70 100644 --- a/packages/backend/src/altNodes/altConversion.ts +++ b/packages/backend/src/altNodes/altConversion.ts @@ -20,7 +20,9 @@ const canBeFlattened = isTypeOrGroupOfTypes([ export const convertNodeToAltNode = (parent: ParentNode | null) => - (node: SceneNode): SceneNode => { + async (node: SceneNode): Promise => { + console.log("node is", node); + (node as any).canBeFlattened = canBeFlattened(node); const type = node.type; switch (type) { // Standard nodes @@ -31,7 +33,7 @@ export const convertNodeToAltNode = case "POLYGON": case "VECTOR": case "BOOLEAN_OPERATION": - return cloneNode(node, parent); + return node; // Group nodes case "FRAME": @@ -39,31 +41,24 @@ export const convertNodeToAltNode = case "COMPONENT": case "COMPONENT_SET": // if the frame, instance etc. has no children, convert the frame to rectangle - if (node.children.length === 0) - return cloneAsRectangleNode(node, parent); - // goto SECTION - + if (node.children.length === 0) return cloneAsRectangleNode(node); case "GROUP": // if a Group is visible and has only one child, the Group should be ungrouped. if (type === "GROUP" && node.children.length === 1 && node.visible) return convertNodeToAltNode(parent)(node.children[0]); - // goto SECTION - case "SECTION": - const group = cloneNode(node, parent); - const groupChildren = convertNodesToAltNodes(node.children, group); - return assignChildren(groupChildren, group); + const groupChildren = await convertNodesToAltNodes(node.children, node); + return assignChildren(groupChildren, node); // Text Nodes case "TEXT": - globalTextStyleSegments[node.id] = extractStyledTextSegments(node); - return cloneNode(node, parent); + const textNode = (await figma.getNodeByIdAsync(node.id)) as TextNode; + globalTextStyleSegments[node.id] = extractStyledTextSegments(textNode); + return node; // Unsupported Nodes case "SLICE": - throw new Error( - `Sorry, Slices are not supported. Type:${node.type} id:${node.id}`, - ); + return null; default: throw new Error( `Sorry, an unsupported node type was selected. Type:${node.type} id:${node.id}`, @@ -71,58 +66,19 @@ export const convertNodeToAltNode = } }; -export const convertNodesToAltNodes = ( +export const convertNodesToAltNodes = async ( sceneNode: ReadonlyArray, parent: ParentNode | null, -): Array => - sceneNode.map(convertNodeToAltNode(parent)).filter(isNotEmpty); - -export const cloneNode = ( - node: T, - parent: ParentNode | null, -): T => { - // Create the cloned object with the correct prototype - const cloned = {} as T; - // Create a new object with only the desired descriptors (excluding 'parent' and 'children') - for (const prop in node) { - if ( - prop !== "parent" && - prop !== "children" && - prop !== "horizontalPadding" && - prop !== "verticalPadding" && - prop !== "mainComponent" && - prop !== "masterComponent" && - prop !== "variantProperties" && - prop !== "get_annotations" && - prop !== "componentPropertyDefinitions" && - prop !== "exposedInstances" && - prop !== "componentProperties" && - prop !== "componenPropertyReferences" && - prop !== "constrainProportions" - ) { - cloned[prop as keyof T] = node[prop as keyof T]; - } - } - assignParent(parent, cloned); - - const altNode = { - ...cloned, - originalNode: node, - canBeFlattened: canBeFlattened(node), - } as AltNode; - return altNode; -}; +): Promise> => + (await Promise.all(sceneNode.map(convertNodeToAltNode(parent)))).filter( + isNotEmpty, + ); // auto convert Frame to Rectangle when Frame has no Children -const cloneAsRectangleNode = ( - node: T, - parent: ParentNode | null, -): RectangleNode => { - const clonedNode = cloneNode(node, parent); - - assignRectangleType(clonedNode); +const cloneAsRectangleNode = (node: T): RectangleNode => { + assignRectangleType(node); - return clonedNode as unknown as RectangleNode; + return node as unknown as RectangleNode; }; const extractStyledTextSegments = (node: TextNode) => @@ -138,6 +94,11 @@ const extractStyledTextSegments = (node: TextNode) => "listOptions", "textCase", "textDecoration", + "textDecorationStyle", + "textDecorationOffset", + "textDecorationThickness", + "textDecorationColor", + "textDecorationSkipInk", "textStyleId", "fillStyleId", "openTypeFeatures", diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index 1dd94cea..32589181 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -17,10 +17,156 @@ import { import { PluginSettings } from "types"; import { convertToCode } from "./common/retrieveUI/convertToCode"; +// Helper function to add parent references to all children in the node tree +const addParentReferences = (node: any) => { + if (node.children && node.children.length > 0) { + for (const child of node.children) { + // Add parent reference to the child + child.parent = node; + // Recursively process this child's children + addParentReferences(child); + } + } +}; + +// Define all property paths that might contain gradients +const GRADIENT_PROPERTIES = ["fills", "strokes", "effects"]; + +/** + * Recursively process node and its children to update with data not available in JSON + * @param node The node to process + * @param optimizeLayout Whether to extract and include inferredAutoLayout data + */ +const processNodeData = (node: any, optimizeLayout: boolean) => { + if (node.id) { + // Check if we need to fetch the Figma node at all + const hasGradient = GRADIENT_PROPERTIES.some((propName) => { + const property = node[propName]; + return ( + property && + Array.isArray(property) && + property.length > 0 && + property.some( + (item: any) => item.type && item.type.startsWith("GRADIENT_"), + ) + ); + }); + + // Only fetch the Figma node if we have gradients or optimizeLayout is enabled + if (hasGradient || optimizeLayout) { + try { + const figmaNode = figma.getNodeById(node.id); + if (figmaNode) { + // Handle gradients if needed + if (hasGradient) { + GRADIENT_PROPERTIES.forEach((propName) => { + const property = node[propName]; + if (property && Array.isArray(property) && property.length > 0) { + // We already know there's a gradient in at least one property + if ( + property.some( + (item: any) => + item.type && item.type.startsWith("GRADIENT_"), + ) && + propName in figmaNode + ) { + // Replace with the actual property that contains proper gradient transforms + node[propName] = JSON.parse( + JSON.stringify((figmaNode as any)[propName]), + ); + } + } + }); + } + + // Extract inferredAutoLayout if optimizeLayout is enabled + if (optimizeLayout && "inferredAutoLayout" in figmaNode) { + node.inferredAutoLayout = JSON.parse( + JSON.stringify((figmaNode as any).inferredAutoLayout), + ); + } + + node.width = (figmaNode as any).width; + node.height = (figmaNode as any).height; + node.x = (figmaNode as any).x; + node.y = (figmaNode as any).y; + } + } catch (e) { + // Silently fail if there's an error accessing the Figma node + } + } else { + // Avoid calling getNodeById if we don't need to + if (node.rotation && node.rotation !== 0) { + const figmaNode = figma.getNodeById(node.id); + node.width = (figmaNode as any).width; + node.height = (figmaNode as any).height; + node.x = (figmaNode as any).x; + node.y = (figmaNode as any).y; + } else { + // Use the absoluteRenderBounds if we don't need to fetch the Figma node. + node.width = node.absoluteRenderBounds.width; + node.height = node.absoluteRenderBounds.height; + node.x = node.absoluteRenderBounds.x; + node.y = node.absoluteRenderBounds.y; + } + } + + if (!node.LayoutMode) { + node.LayoutMode = "NONE"; + } + if (!node.layoutGrow) { + node.layoutGrow = 0; + } + if (!node.layoutSizingHorizontal) { + node.layoutSizingHorizontal = "FIXED"; + } + if (!node.layoutSizingVertical) { + node.layoutSizingVertical = "FIXED"; + } + } + + // Process children recursively + if (node.children && Array.isArray(node.children)) { + node.children.forEach((child: any) => + processNodeData(child, optimizeLayout), + ); + } +}; + +/** + * Convert Figma nodes to JSON format with parent references added + * @param nodes The Figma nodes to convert to JSON + * @param optimizeLayout Whether to extract and include inferredAutoLayout data + * @returns JSON representation of the nodes with parent references + */ +export const nodesToJSON = async ( + nodes: ReadonlyArray, + optimizeLayout: boolean = false, +): Promise => { + const nodeJson = (await Promise.all( + nodes.map( + async (node) => + ( + (await node.exportAsync({ + format: "JSON_REST_V1", + })) as any + ).document, + ), + )) as SceneNode[]; + + // Process gradients and inferredAutoLayout in the JSON tree before adding parent references + nodeJson.forEach((node) => processNodeData(node, optimizeLayout)); + + // Add parent references to all children in the node tree + nodeJson.forEach((node) => addParentReferences(node)); + + return nodeJson; +}; + export const run = async (settings: PluginSettings) => { clearWarnings(); - const { framework } = settings; + const { framework, optimizeLayout } = settings; const selection = figma.currentPage.selection; if (selection.length > 1) { @@ -29,7 +175,15 @@ export const run = async (settings: PluginSettings) => { ); } - const convertedSelection = convertNodesToAltNodes(selection, null); + const nodeJson = await nodesToJSON(selection, optimizeLayout); + console.log("nodeJson", nodeJson); + + postConversionStart(); + // force postMessage to run right now. + await new Promise((resolve) => setTimeout(resolve, 30)); + + // Now we work directly with the JSON nodes + const convertedSelection = await convertNodesToAltNodes(nodeJson, null); // ignore when nothing was selected // If the selection was empty, the converted selection will also be empty. diff --git a/packages/backend/src/common/color.ts b/packages/backend/src/common/color.ts index 79f07d94..85d5e6b6 100644 --- a/packages/backend/src/common/color.ts +++ b/packages/backend/src/common/color.ts @@ -1,3 +1,7 @@ +import { retrieveTopFill } from "./retrieveFill"; +import { numberToFixedString } from "./numToAutoFixed"; + +// ---- Color Format Conversion ---- export const rgbTo6hex = (color: RGB | RGBA): string => { const hex = ((color.r * 255) | (1 << 8)).toString(16).slice(1) + @@ -19,6 +23,42 @@ export const rgbTo8hex = (color: RGB, alpha: number): string => { return hex; }; +/** + * Converts RGB values to CSS hex or rgba format + * @param color The RGB color object + * @param alpha The opacity value + * @returns A CSS color string + */ +export const rgbToCssColor = (color: RGB | RGBA, alpha: number = 1): string => { + // Special cases for common colors + if (color.r === 1 && color.g === 1 && color.b === 1 && alpha === 1) { + return "white"; + } + + if (color.r === 0 && color.g === 0 && color.b === 0 && alpha === 1) { + return "black"; + } + + // Return hex when possible (no transparency) + if (alpha === 1) { + const r = Math.round(color.r * 255); + const g = Math.round(color.g * 255); + const b = Math.round(color.b * 255); + + const toHex = (num: number): string => num.toString(16).padStart(2, "0"); + return `#${toHex(r)}${toHex(g)}${toHex(b)}`.toUpperCase(); + } + + // Use rgba for transparent colors + const r = numberToFixedString(color.r * 255); + const g = numberToFixedString(color.g * 255); + const b = numberToFixedString(color.b * 255); + const a = numberToFixedString(alpha); + + return `rgba(${r}, ${g}, ${b}, ${a})`; +}; + +// ---- Gradient Transformation ---- export const gradientAngle = (fill: GradientPaint): number => { // Thanks Gleb and Liam for helping! const decomposed = decomposeRelativeTransform( @@ -28,6 +68,36 @@ export const gradientAngle = (fill: GradientPaint): number => { return (decomposed.rotation * 180) / Math.PI; }; + +// Calculate gradient angle for CSS (different coordinate system) +export const cssGradientAngle = (angle: number): number => { + // Normalize angle: if negative, add 360 to make it positive. + return angle < 0 ? angle + 360 : angle; +}; + +// Calculate gradient coordinates for a matrix transform +export const getGradientTransformCoordinates = ( + gradientTransform: number[][], +): { centerX: string; centerY: string; radiusX: string; radiusY: string } => { + const a = gradientTransform[0][0]; + const b = gradientTransform[0][1]; + const c = gradientTransform[1][0]; + const d = gradientTransform[1][1]; + const e = gradientTransform[0][2]; + const f = gradientTransform[1][2]; + + const scaleX = Math.sqrt(a ** 2 + b ** 2); + const scaleY = Math.sqrt(c ** 2 + d ** 2); + + const centerX = ((e * scaleX * 100) / (1 - scaleX)).toFixed(2); + const centerY = (((1 - f) * scaleY * 100) / (1 - scaleY)).toFixed(2); + + const radiusX = (scaleX * 100).toFixed(2); + const radiusY = (scaleY * 100).toFixed(2); + + return { centerX, centerY, radiusX, radiusY }; +}; + // from https://math.stackexchange.com/a/2888105 export const decomposeRelativeTransform = ( t1: [number, number, number], @@ -79,3 +149,34 @@ export const decomposeRelativeTransform = ( return result; }; + +// ---- Common color check helpers ---- + +/** + * Checks if color is black + */ +export const isBlack = (color: RGB, opacity: number = 1): boolean => + color.r === 0 && color.g === 0 && color.b === 0 && opacity === 1; + +/** + * Checks if color is white + */ +export const isWhite = (color: RGB, opacity: number = 1): boolean => + color.r === 1 && color.g === 1 && color.b === 1 && opacity === 1; + +/** + * Helper for calculating gradient stops in a consistent way across frameworks + */ +export const processGradientStops = ( + stops: ReadonlyArray, + opacity: number = 1, + colorFormatter: (color: RGB | RGBA, alpha: number) => string, +): string => { + return stops + .map((stop) => { + const color = colorFormatter(stop.color, stop.color.a * opacity); + const position = `${(stop.position * 100).toFixed(0)}%`; + return `${color} ${position}`; + }) + .join(", "); +}; diff --git a/packages/backend/src/common/nodeVisibility.ts b/packages/backend/src/common/nodeVisibility.ts index 8e3959d7..7e4eb6f4 100644 --- a/packages/backend/src/common/nodeVisibility.ts +++ b/packages/backend/src/common/nodeVisibility.ts @@ -1,4 +1,2 @@ -type VisibilityMixin = { visible: boolean }; -const isVisible = (node: VisibilityMixin) => node.visible; export const getVisibleNodes = (nodes: readonly SceneNode[]) => - nodes.filter(isVisible); + nodes.filter((d) => d.visible ?? true); diff --git a/packages/backend/src/flutter/builderImpl/flutterBorder.ts b/packages/backend/src/flutter/builderImpl/flutterBorder.ts index 8073f4ec..f3e22b1d 100644 --- a/packages/backend/src/flutter/builderImpl/flutterBorder.ts +++ b/packages/backend/src/flutter/builderImpl/flutterBorder.ts @@ -17,7 +17,7 @@ export const flutterBorder = (node: SceneNode): string => { } const color = skipDefaultProperty( - flutterColorFromFills(node.strokes), + flutterColorFromFills(node, "strokes"), "Colors.black", ); diff --git a/packages/backend/src/flutter/builderImpl/flutterColor.ts b/packages/backend/src/flutter/builderImpl/flutterColor.ts index ff3b1b0e..4fcf7dd2 100644 --- a/packages/backend/src/flutter/builderImpl/flutterColor.ts +++ b/packages/backend/src/flutter/builderImpl/flutterColor.ts @@ -10,10 +10,18 @@ import { getPlaceholderImage } from "../../common/images"; /** * Retrieve the SOLID color for Flutter when existent, otherwise "" + * @param node SceneNode containing the property to examine + * @param propertyPath Property path to extract fills from (e.g., 'fills', 'strokes') or direct fills array */ export const flutterColorFromFills = ( - fills: ReadonlyArray | PluginAPI["mixed"], + node: SceneNode, + propertyPath: string, ): string => { + let fills: ReadonlyArray | PluginAPI["mixed"]; + fills = node[propertyPath as keyof SceneNode] as + | ReadonlyArray + | PluginAPI["mixed"]; + const fill = retrieveTopFill(fills); if (fill && fill.type === "SOLID") { @@ -32,10 +40,18 @@ export const flutterColorFromFills = ( return ""; }; +/** + * Get box decoration properties for a Flutter node + */ export const flutterBoxDecorationColor = ( node: SceneNode, - fills: ReadonlyArray | PluginAPI["mixed"], + propertyPath: string, ): Record => { + let fills: ReadonlyArray | PluginAPI["mixed"]; + fills = node[propertyPath as keyof SceneNode] as + | ReadonlyArray + | PluginAPI["mixed"]; + const fill = retrieveTopFill(fills); if (fill && fill.type === "SOLID") { @@ -178,13 +194,13 @@ export const flutterColor = (color: RGB, opacity: number): string => { if (sum === 0) { return opacity === 1 ? "Colors.black" - : `Colors.black.withOpacity(${opacity})`; + : `Colors.black.withOpacity(${numberToFixedString(opacity)})`; } if (sum === 3) { return opacity === 1 ? "Colors.white" - : `Colors.white.withOpacity(${opacity})`; + : `Colors.white.withOpacity(${numberToFixedString(opacity)})`; } return `Color(0x${rgbTo8hex(color, opacity).toUpperCase()})`; diff --git a/packages/backend/src/flutter/flutterContainer.ts b/packages/backend/src/flutter/flutterContainer.ts index 50df4b0e..a82e3419 100644 --- a/packages/backend/src/flutter/flutterContainer.ts +++ b/packages/backend/src/flutter/flutterContainer.ts @@ -85,7 +85,7 @@ const getDecoration = (node: SceneNode): string => { } const propBoxShadow = flutterShadow(node); - const decorationBackground = flutterBoxDecorationColor(node, node.fills); + const decorationBackground = flutterBoxDecorationColor(node, "fills"); let shapeDecorationBorder = ""; if (node.type === "STAR") { @@ -139,7 +139,7 @@ const generateBorderSideCode = ( "BorderSide.strokeAlignInside", ), color: skipDefaultProperty( - flutterColorFromFills(node.strokes), + flutterColorFromFills(node, "strokes"), "Colors.black", ), }), diff --git a/packages/backend/src/flutter/flutterMain.ts b/packages/backend/src/flutter/flutterMain.ts index d7e8738d..a2bda1be 100644 --- a/packages/backend/src/flutter/flutterMain.ts +++ b/packages/backend/src/flutter/flutterMain.ts @@ -15,6 +15,7 @@ import { commonSortChildrenWhenInferredAutoLayout } from "../common/commonChildr import { PluginSettings } from "types"; import { addWarning } from "../common/commonConversionWarnings"; import { getPlaceholderImage } from "../common/images"; +import { getVisibleNodes } from "../common/nodeVisibility"; let localSettings: PluginSettings; let previousExecutionCache: string[]; @@ -88,7 +89,7 @@ const flutterWidgetGenerator = ( let comp: string[] = []; // filter non visible nodes. This is necessary at this step because conversion already happened. - const visibleSceneNode = sceneNode.filter((d) => d.visible); + const visibleSceneNode = getVisibleNodes(sceneNode); const sceneLen = visibleSceneNode.length; visibleSceneNode.forEach((node, index) => { diff --git a/packages/backend/src/html/builderImpl/htmlBlend.ts b/packages/backend/src/html/builderImpl/htmlBlend.ts index 75e3b750..1a68039a 100644 --- a/packages/backend/src/html/builderImpl/htmlBlend.ts +++ b/packages/backend/src/html/builderImpl/htmlBlend.ts @@ -124,7 +124,9 @@ export const htmlRotation = (node: LayoutMixin, isJsx: boolean): string[] => { "parent" in node && node.parent ? (node.parent as LayoutMixin) : null; const parentRotation: number = parent && "rotation" in parent ? parent.rotation : 0; - const rotation: number = Math.round(parentRotation - node.rotation) ?? 0; + + const nodeRotation = node.rotation || 0; + const rotation = Math.round(parentRotation - nodeRotation) ?? 0; if ( roundToNearestHundreth(parentRotation) !== 0 && diff --git a/packages/backend/src/html/htmlMain.ts b/packages/backend/src/html/htmlMain.ts index 4d1399fe..99f4989e 100644 --- a/packages/backend/src/html/htmlMain.ts +++ b/packages/backend/src/html/htmlMain.ts @@ -11,7 +11,7 @@ import { HTMLSettings, ExportableNode, } from "types"; -import { isSVGNode, renderAndAttachSVG } from "../altNodes/altNodeUtils"; +import { renderAndAttachSVG } from "../altNodes/altNodeUtils"; import { getVisibleNodes } from "../common/nodeVisibility"; import { exportNodeAsBase64PNG, @@ -71,11 +71,12 @@ export const generateHTMLPreview = async ( }; }; -// todo lint idea: replace BorderRadius.only(topleft: 8, topRight: 8) with BorderRadius.horizontal(8) const htmlWidgetGenerator = async ( sceneNode: ReadonlyArray, settings: HTMLSettings, ): Promise => { + console.log("htmlWidgetGenerator", sceneNode); + // filter non visible nodes. This is necessary at this step because conversion already happened. const promiseOfConvertedCode = getVisibleNodes(sceneNode).map( convertNode(settings), @@ -85,7 +86,9 @@ const htmlWidgetGenerator = async ( }; const convertNode = (settings: HTMLSettings) => async (node: SceneNode) => { - if (isSVGNode(node)) { + console.log("converting", node); + + if ((node as any).canBeFlattened) { const altNode = await renderAndAttachSVG(node); if (altNode.svg) return htmlWrapSVG(altNode, settings); } diff --git a/packages/backend/src/swiftui/builderImpl/swiftuiColor.ts b/packages/backend/src/swiftui/builderImpl/swiftuiColor.ts index f669aae5..9ecb7a19 100644 --- a/packages/backend/src/swiftui/builderImpl/swiftuiColor.ts +++ b/packages/backend/src/swiftui/builderImpl/swiftuiColor.ts @@ -5,6 +5,10 @@ import { numberToFixedString } from "../../common/numToAutoFixed"; import { addWarning } from "../../common/commonConversionWarnings"; import { getPlaceholderImage } from "../../common/images"; +/** + * Retrieve the SwiftUI color for a Paint object + * @param fill The paint object to extract color from + */ export const swiftUISolidColor = (fill: Paint): string => { if (fill && fill.type === "SOLID") { return swiftuiColor(fill.color, fill.opacity ?? 1.0); @@ -22,9 +26,22 @@ export const swiftUISolidColor = (fill: Paint): string => { return ""; }; +/** + * Retrieve the SwiftUI solid color when existent, otherwise "" + * @param node SceneNode containing the property to examine + * @param propertyPath Property path to extract fills from (e.g., 'fills', 'strokes') or direct fills array + */ export const swiftuiSolidColor = ( - fills: ReadonlyArray | PluginAPI["mixed"], + node: SceneNode, + propertyPath: string | keyof SceneNode, ): string => { + let fills: ReadonlyArray | PluginAPI["mixed"]; + + // Property path string provided + fills = node[propertyPath as keyof SceneNode] as + | ReadonlyArray + | PluginAPI["mixed"]; + const fill = retrieveTopFill(fills); if (fill && fill.type === "SOLID") { @@ -47,14 +64,22 @@ export const swiftuiSolidColor = ( return ""; }; +/** + * Get SwiftUI background for a node + * @param node SceneNode containing the property to examine + * @param propertyPath Property path to extract fills from (e.g., 'fills', 'strokes') or direct fills array + */ export const swiftuiBackground = ( node: SceneNode, - fills: ReadonlyArray | PluginAPI["mixed"], + propertyPath: string, ): string => { + const fills = node[propertyPath as keyof SceneNode] as + | ReadonlyArray + | PluginAPI["mixed"]; + const fill = retrieveTopFill(fills); if (fill && fill.type === "SOLID") { - // opacity should only be null on set, not on get. But better be prevented. const opacity = fill.opacity ?? 1.0; return swiftuiColor(fill.color, opacity); } else if (fill?.type === "GRADIENT_LINEAR") { diff --git a/packages/backend/src/swiftui/swiftuiDefaultBuilder.ts b/packages/backend/src/swiftui/swiftuiDefaultBuilder.ts index 2e629073..7ae8d73e 100644 --- a/packages/backend/src/swiftui/swiftuiDefaultBuilder.ts +++ b/packages/backend/src/swiftui/swiftuiDefaultBuilder.ts @@ -105,7 +105,7 @@ export class SwiftuiDefaultBuilder { shapeBackground(node: SceneNode): this { if ("fills" in node) { - const background = swiftuiBackground(node, node.fills); + const background = swiftuiBackground(node, "fills"); if (background) { this.pushModifier([`background`, background]); } diff --git a/packages/backend/src/swiftui/swiftuiMain.ts b/packages/backend/src/swiftui/swiftuiMain.ts index 3aab3d87..bf068794 100644 --- a/packages/backend/src/swiftui/swiftuiMain.ts +++ b/packages/backend/src/swiftui/swiftuiMain.ts @@ -8,6 +8,7 @@ import { SwiftuiDefaultBuilder } from "./swiftuiDefaultBuilder"; import { commonSortChildrenWhenInferredAutoLayout } from "../common/commonChildrenOrder"; import { PluginSettings } from "types"; import { addWarning } from "../common/commonConversionWarnings"; +import { getVisibleNodes } from "../common/nodeVisibility"; let localSettings: PluginSettings; let previousExecutionCache: string[]; @@ -66,7 +67,7 @@ const swiftuiWidgetGenerator = ( indentLevel: number, ): string => { // filter non visible nodes. This is necessary at this step because conversion already happened. - const visibleSceneNode = sceneNode.filter((d) => d.visible); + const visibleSceneNode = getVisibleNodes(sceneNode); let comp: string[] = []; visibleSceneNode.forEach((node, index) => { diff --git a/packages/backend/src/tailwind/builderImpl/tailwindAutoLayout.ts b/packages/backend/src/tailwind/builderImpl/tailwindAutoLayout.ts index 8078968f..19cfdfa0 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindAutoLayout.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindAutoLayout.ts @@ -47,13 +47,14 @@ const getFlex = ( export const tailwindAutoLayoutProps = ( node: SceneNode, autoLayout: InferredAutoLayoutResult, -): string => - Object.values({ - flexDirection: getFlexDirection(autoLayout), - justifyContent: getJustifyContent(autoLayout), - alignItems: getAlignItems(autoLayout), - gap: getGap(autoLayout), - flex: getFlex(node, autoLayout), - }) - .filter((value) => value !== "") - .join(" "); +): string => { + const classes = [ + getFlex(node, autoLayout), + getFlexDirection(autoLayout), + getJustifyContent(autoLayout), + getAlignItems(autoLayout), + getGap(autoLayout), + ].filter(Boolean); + + return classes.join(" "); +}; diff --git a/packages/backend/src/tailwind/builderImpl/tailwindBorder.ts b/packages/backend/src/tailwind/builderImpl/tailwindBorder.ts index 7cb57c6d..8639702f 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindBorder.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindBorder.ts @@ -1,6 +1,5 @@ import { getCommonRadius } from "../../common/commonRadius"; import { commonStroke } from "../../common/commonStroke"; -import { numberToFixedString } from "../../common/numToAutoFixed"; import { nearestValue, pxToBorderRadius } from "../conversionTables"; /** diff --git a/packages/backend/src/tailwind/builderImpl/tailwindColor.ts b/packages/backend/src/tailwind/builderImpl/tailwindColor.ts index 82f726ab..0954cfe6 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindColor.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindColor.ts @@ -1,4 +1,3 @@ -import { retrieveTopFill } from "../../common/retrieveFill"; import { gradientAngle } from "../../common/color"; import { getColorInfo, @@ -7,6 +6,7 @@ import { } from "../conversionTables"; import { TailwindColorType } from "types"; import { addWarning } from "../../common/commonConversionWarnings"; +import { retrieveTopFill } from "../../common/retrieveFill"; /** * Get a tailwind color value object @@ -76,10 +76,6 @@ export const tailwindColorFromFills = ( return ""; }; -/** - * https://tailwindcss.com/docs/box-shadow/ - * example: shadow - */ export const tailwindGradientFromFills = ( fills: ReadonlyArray | PluginAPI["mixed"], ): string => { @@ -114,24 +110,19 @@ export const tailwindGradient = (fill: GradientPaint): string => { if (fill.gradientStops.length === 1) { const fromColor = tailwindSolidColor(fill.gradientStops[0]); - return `${direction} from-${fromColor}`; } else if (fill.gradientStops.length === 2) { const fromColor = tailwindSolidColor(fill.gradientStops[0]); const toColor = tailwindSolidColor(fill.gradientStops[1]); - return `${direction} from-${fromColor} to-${toColor}`; } else { const fromColor = tailwindSolidColor(fill.gradientStops[0]); - // middle (second color) const viaColor = tailwindSolidColor(fill.gradientStops[1]); - // last const toColor = tailwindSolidColor( fill.gradientStops[fill.gradientStops.length - 1], ); - return `${direction} from-${fromColor} via-${viaColor} to-${toColor}`; } }; diff --git a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts index e271b63d..93c7e8f1 100644 --- a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts +++ b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts @@ -45,7 +45,7 @@ export class TailwindDefaultBuilder { return this.settings.showLayerNames ? this.node.name : ""; } get visible() { - return this.node.visible; + return this.node.visible ?? true; } get isJSX() { return this.settings.jsx; @@ -156,7 +156,6 @@ export class TailwindDefaultBuilder { paint: ReadonlyArray | PluginAPI["mixed"], kind: TailwindColorType, ): this { - // visible is true or undefinied (tests) if (this.visible) { let gradient = ""; if (kind === "bg") { diff --git a/packages/backend/src/tailwind/tailwindMain.ts b/packages/backend/src/tailwind/tailwindMain.ts index 335b892a..b39e6225 100644 --- a/packages/backend/src/tailwind/tailwindMain.ts +++ b/packages/backend/src/tailwind/tailwindMain.ts @@ -1,103 +1,107 @@ import { retrieveTopFill } from "../common/retrieveFill"; import { indentString } from "../common/indentString"; +import { addWarning } from "../common/commonConversionWarnings"; +import { commonSortChildrenWhenInferredAutoLayout } from "../common/commonChildrenOrder"; +import { getVisibleNodes } from "../common/nodeVisibility"; +import { getPlaceholderImage } from "../common/images"; import { TailwindTextBuilder } from "./tailwindTextBuilder"; import { TailwindDefaultBuilder } from "./tailwindDefaultBuilder"; import { tailwindAutoLayoutProps } from "./builderImpl/tailwindAutoLayout"; -import { commonSortChildrenWhenInferredAutoLayout } from "../common/commonChildrenOrder"; -import { AltNode, PluginSettings, TailwindSettings } from "types"; -import { addWarning } from "../common/commonConversionWarnings"; import { renderAndAttachSVG } from "../altNodes/altNodeUtils"; -import { getVisibleNodes } from "../common/nodeVisibility"; -import { getPlaceholderImage } from "../common/images"; +import { AltNode, PluginSettings, TailwindSettings } from "types"; export let localTailwindSettings: PluginSettings; - -let previousExecutionCache: { style: string; text: string }[]; - -const selfClosingTags = ["img"]; +let previousExecutionCache: { + style: string; + text: string; + openTypeFeatures: Record; +}[] = []; +const SELF_CLOSING_TAGS = ["img"]; export const tailwindMain = async ( sceneNode: Array, settings: PluginSettings, -) => { +): Promise => { localTailwindSettings = settings; previousExecutionCache = []; let result = await tailwindWidgetGenerator(sceneNode, settings); - // remove the initial \n that is made in Container. - if (result.length > 0 && result.startsWith("\n")) { - result = result.slice(1, result.length); + // Remove the initial newline that is made in Container + if (result.startsWith("\n")) { + result = result.slice(1); } return result; }; -// TODO: lint idea: replace BorderRadius.only(topleft: 8, topRight: 8) with BorderRadius.horizontal(8) const tailwindWidgetGenerator = async ( sceneNode: ReadonlyArray, settings: TailwindSettings, ): Promise => { - // filter non visible nodes. This is necessary at this step because conversion already happened. - const promiseOfConvertedCode = getVisibleNodes(sceneNode).map( - convertNode(settings), - ); + const visibleNodes = getVisibleNodes(sceneNode); + const promiseOfConvertedCode = visibleNodes.map(convertNode(settings)); const code = (await Promise.all(promiseOfConvertedCode)).join(""); return code; }; -const convertNode = (settings: TailwindSettings) => async (node: SceneNode) => { - const altNode = await renderAndAttachSVG(node); - if (altNode.svg) return tailwindWrapSVG(altNode, settings); - - switch (node.type) { - case "RECTANGLE": - case "ELLIPSE": - return tailwindContainer(node, "", "", settings); - case "GROUP": - return tailwindGroup(node, settings); - case "FRAME": - case "COMPONENT": - case "INSTANCE": - case "COMPONENT_SET": - return tailwindFrame(node, settings); - case "TEXT": - return tailwindText(node, settings); - case "LINE": - return tailwindLine(node, settings); - case "SECTION": - return tailwindSection(node, settings); - case "VECTOR": - addWarning("VectorNodes are not supported in Tailwind"); - break; - default: - addWarning(`${node.type} nodes are not supported in Tailwind`); - } - return ""; -}; +const convertNode = + (settings: TailwindSettings) => + async (node: SceneNode): Promise => { + console.log("altNode", node); + const altNode = await renderAndAttachSVG(node); + if (altNode.svg) { + return tailwindWrapSVG(altNode, settings); + } + + switch (node.type) { + case "RECTANGLE": + case "ELLIPSE": + return tailwindContainer(node, "", "", settings); + case "GROUP": + return tailwindGroup(node, settings); + case "FRAME": + case "COMPONENT": + case "INSTANCE": + case "COMPONENT_SET": + return tailwindFrame(node, settings); + case "TEXT": + return tailwindText(node, settings); + case "LINE": + return tailwindLine(node, settings); + case "SECTION": + return tailwindSection(node, settings); + case "VECTOR": + addWarning("VectorNodes are not supported in Tailwind"); + break; + default: + addWarning(`${node.type} nodes are not supported in Tailwind`); + } + return ""; + }; const tailwindWrapSVG = ( node: AltNode, settings: TailwindSettings, ): string => { - if (node.svg === "") return ""; + if (!node.svg) return ""; + const builder = new TailwindDefaultBuilder(node, settings) .addData("svg-wrapper") .position(); - return `\n\n${node.svg ?? ""}
`; + return `\n\n${node.svg}
`; }; -const tailwindGroup = async (node: GroupNode, settings: TailwindSettings) => { - // ignore the view when size is zero or less - // while technically it shouldn't get less than 0, due to rounding errors, - // it can get to values like: -0.000004196293048153166 - // also ignore if there are no children inside, which makes no sense +const tailwindGroup = async ( + node: GroupNode, + settings: TailwindSettings, +): Promise => { + // Ignore the view when size is zero or less or if there are no children if (node.width < 0 || node.height <= 0 || node.children.length === 0) { return ""; } - // this needs to be called after CustomNode because widthHeight depends on it const builder = new TailwindDefaultBuilder(node, settings) .blend() .size() @@ -105,9 +109,7 @@ const tailwindGroup = async (node: GroupNode, settings: TailwindSettings) => { if (builder.attributes || builder.style) { const attr = builder.build(""); - const generator = await tailwindWidgetGenerator(node.children, settings); - return `\n${indentString(generator)}\n
`; } @@ -118,7 +120,7 @@ export const tailwindText = ( node: TextNode, settings: TailwindSettings, ): string => { - let layoutBuilder = new TailwindTextBuilder(node, settings) + const layoutBuilder = new TailwindTextBuilder(node, settings) .commonPositionStyles() .textAlign(); @@ -127,19 +129,19 @@ export const tailwindText = ( let content = ""; if (styledHtml.length === 1) { - layoutBuilder.addAttributes(styledHtml[0].style); - content = styledHtml[0].text; - - const additionalTag = - styledHtml[0].openTypeFeatures.SUBS === true - ? "sub" - : styledHtml[0].openTypeFeatures.SUPS === true - ? "sup" - : ""; - - if (additionalTag) { - content = `<${additionalTag}>${content}`; - } + const segment = styledHtml[0]; + layoutBuilder.addAttributes(segment.style); + + const getFeatureTag = (features: Record): string => { + if (features.SUBS === true) return "sub"; + if (features.SUPS === true) return "sup"; + return ""; + }; + + const additionalTag = getFeatureTag(segment.openTypeFeatures); + content = additionalTag + ? `<${additionalTag}>${segment.text}` + : segment.text; } else { content = styledHtml .map((style) => { @@ -162,47 +164,34 @@ const tailwindFrame = async ( node: FrameNode | InstanceNode | ComponentNode | ComponentSetNode, settings: TailwindSettings, ): Promise => { - const childrenStr = await tailwindWidgetGenerator( - commonSortChildrenWhenInferredAutoLayout( - node, - localTailwindSettings.optimizeLayout, - ), - settings, + const sortedChildren = commonSortChildrenWhenInferredAutoLayout( + node, + localTailwindSettings.optimizeLayout, ); + const childrenStr = await tailwindWidgetGenerator(sortedChildren, settings); - // Add overflow-hidden class if clipsContent is true const clipsContentClass = node.clipsContent ? " overflow-hidden" : ""; + let layoutProps = ""; if (node.layoutMode !== "NONE") { - const rowColumn = tailwindAutoLayoutProps(node, node); - return tailwindContainer( - node, - childrenStr, - rowColumn + clipsContentClass, - settings, - ); - } else { - if ( - localTailwindSettings.optimizeLayout && - node.inferredAutoLayout !== null - ) { - const rowColumn = tailwindAutoLayoutProps(node, node.inferredAutoLayout); - return tailwindContainer( - node, - childrenStr, - rowColumn + clipsContentClass, - settings, - ); - } - - // node.layoutMode === "NONE" && node.children.length > 1 - // children needs to be absolute - return tailwindContainer(node, childrenStr, clipsContentClass, settings); + layoutProps = tailwindAutoLayoutProps(node, node); + } else if ( + localTailwindSettings.optimizeLayout && + node.inferredAutoLayout !== null + ) { + layoutProps = tailwindAutoLayoutProps(node, node.inferredAutoLayout); } + + return tailwindContainer( + node, + childrenStr, + layoutProps + clipsContentClass, + settings, + ); }; -// properties named propSomething always take care of "," -// sometimes a property might not exist, so it doesn't add "," +// Properties named propSomething always take care of "," +// Sometimes a property might not exist, so it doesn't add "," export const tailwindContainer = ( node: SceneNode & SceneNodeMixin & @@ -214,44 +203,46 @@ export const tailwindContainer = ( additionalAttr: string, settings: TailwindSettings, ): string => { - // ignore the view when size is zero or less - // while technically it shouldn't get less than 0, due to rounding errors, - // it can get to values like: -0.000004196293048153166 + // Ignore the view when size is zero or less if (node.width < 0 || node.height < 0) { return children; } - let builder = new TailwindDefaultBuilder(node, settings) + const builder = new TailwindDefaultBuilder(node, settings) .commonPositionStyles() .commonShapeStyles(); - if (builder.attributes || additionalAttr) { - const build = builder.build(additionalAttr); - - // image fill and no children -- let's emit an - let tag = "div"; - let src = ""; - if (retrieveTopFill(node.fills)?.type === "IMAGE") { - addWarning("Image fills are replaced with placeholders"); - const imageURL = getPlaceholderImage(node.width, node.height); - if (!("children" in node) || node.children.length === 0) { - tag = "img"; - src = ` src="${imageURL}"`; - } else { - builder.addAttributes(`bg-[url(${imageURL})]`); - } - } + if (!builder.attributes && !additionalAttr) { + return children; + } + + const build = builder.build(additionalAttr); - if (children) { - return `\n<${tag}${build}${src}>${indentString(children)}\n`; - } else if (selfClosingTags.includes(tag) || settings.jsx) { - return `\n<${tag}${build}${src} />`; + // Determine if we should use img tag + let tag = "div"; + let src = ""; + const topFill = retrieveTopFill(node.fills); + + if (topFill?.type === "IMAGE") { + addWarning("Image fills are replaced with placeholders"); + const imageURL = getPlaceholderImage(node.width, node.height); + + if (!("children" in node) || node.children.length === 0) { + tag = "img"; + src = ` src="${imageURL}"`; } else { - return `\n<${tag}${build}${src}>`; + builder.addAttributes(`bg-[url(${imageURL})]`); } } - return children; + // Generate appropriate HTML + if (children) { + return `\n<${tag}${build}${src}>${indentString(children)}\n`; + } else if (SELF_CLOSING_TAGS.includes(tag) || settings.jsx) { + return `\n<${tag}${build}${src} />`; + } else { + return `\n<${tag}${build}${src}>`; + } }; export const tailwindLine = ( @@ -275,21 +266,18 @@ export const tailwindSection = async ( .position() .customColor(node.fills, "bg"); - if (childrenStr) { - return `\n${indentString(childrenStr)}\n
`; - } else { - return `\n
`; - } + const build = builder.build(); + return childrenStr + ? `\n${indentString(childrenStr)}\n
` + : `\n
`; }; -export const tailwindCodeGenTextStyles = () => { - const result = previousExecutionCache - .map((style) => `// ${style.text}\n${style.style.split(" ").join("\n")}`) - .join("\n---\n"); - - if (!result) { +export const tailwindCodeGenTextStyles = (): string => { + if (previousExecutionCache.length === 0) { return "// No text styles in this selection"; } - return result; + return previousExecutionCache + .map((style) => `// ${style.text}\n${style.style.split(" ").join("\n")}`) + .join("\n---\n"); }; diff --git a/packages/eslint-config-custom/package.json b/packages/eslint-config-custom/package.json index 91fbbced..513f6bd8 100644 --- a/packages/eslint-config-custom/package.json +++ b/packages/eslint-config-custom/package.json @@ -4,9 +4,9 @@ "main": "index.js", "license": "MIT", "dependencies": { - "eslint-config-next": "^14.2.20", + "eslint-config-next": "^14.2.24", "eslint-config-prettier": "^9.1.0", - "eslint-config-turbo": "^2.3.3", + "eslint-config-turbo": "^2.4.4", "eslint-plugin-react": "7.35.0" }, "publishConfig": { diff --git a/packages/plugin-ui/package.json b/packages/plugin-ui/package.json index c3ebc43b..765ac372 100644 --- a/packages/plugin-ui/package.json +++ b/packages/plugin-ui/package.json @@ -10,7 +10,7 @@ "lint": "eslint \"src/**/*.ts*\"" }, "dependencies": { - "@types/react": "^18.3.17", + "@types/react": "^18.3.18", "@types/react-dom": "^18.3.5", "@types/react-syntax-highlighter": "15.5.13", "copy-to-clipboard": "^3.3.3", @@ -19,10 +19,10 @@ "tailwindcss": "3.4.6" }, "devDependencies": { - "eslint": "^9.17.0", + "eslint": "^9.21.0", "eslint-config-custom": "workspace:*", "tsconfig": "workspace:*", "types": "workspace:*", - "typescript": "^5.7.2" + "typescript": "^5.8.2" } } diff --git a/packages/plugin-ui/src/codegenPreferenceOptions.ts b/packages/plugin-ui/src/codegenPreferenceOptions.ts index 946a96e6..24386f41 100644 --- a/packages/plugin-ui/src/codegenPreferenceOptions.ts +++ b/packages/plugin-ui/src/codegenPreferenceOptions.ts @@ -1,14 +1,6 @@ import { LocalCodegenPreferenceOptions, SelectPreferenceOptions } from "types"; export const preferenceOptions: LocalCodegenPreferenceOptions[] = [ - { - itemType: "individual_select", - propertyName: "embedImages", - label: "Embed Images", - description: "Convert images to Base64 and embed them in the code.", - isDefault: false, - includedLanguages: ["HTML"], - }, { itemType: "individual_select", propertyName: "jsx", @@ -57,6 +49,22 @@ export const preferenceOptions: LocalCodegenPreferenceOptions[] = [ isDefault: false, includedLanguages: ["Tailwind"], }, + { + itemType: "individual_select", + propertyName: "embedImages", + label: "Embed Images", + description: "Convert images to Base64 and embed them in the code.", + isDefault: false, + includedLanguages: ["HTML"], + }, + { + itemType: "individual_select", + propertyName: "embedVectors", + label: "Embed Vectors", + description: "Convert vectors in the code.", + isDefault: false, + includedLanguages: ["HTML"], + }, // Add your preferences data here ]; diff --git a/packages/plugin-ui/src/components/CodePanel.tsx b/packages/plugin-ui/src/components/CodePanel.tsx index 2ae5413d..9d6eecc5 100644 --- a/packages/plugin-ui/src/components/CodePanel.tsx +++ b/packages/plugin-ui/src/components/CodePanel.tsx @@ -33,7 +33,7 @@ const CodePanel = (props: CodePanelProps) => { settings, onPreferenceChanged, } = props; - const isEmpty = code === ""; + const isCodeEmpty = code === ""; // State for custom prefix for Tailwind classes. // It is initially set from settings (if available) or an empty string. @@ -85,7 +85,7 @@ const CodePanel = (props: CodePanelProps) => {

Code

- {isEmpty === false && ( + {isCodeEmpty === false && (
- {isEmpty === false && ( + {isCodeEmpty === false && (
{preferenceOptions @@ -186,7 +186,7 @@ const CodePanel = (props: CodePanelProps) => { syntaxHovered ? "ring-2" : "ring-0" }`} > - {isEmpty ? ( + {isCodeEmpty ? (

No layer is selected. Please select a layer.

) : ( =6.9.0'} - '@babel/compat-data@7.26.3': - resolution: {integrity: sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==} + '@babel/compat-data@7.26.8': + resolution: {integrity: sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==} engines: {node: '>=6.9.0'} - '@babel/core@7.26.0': - resolution: {integrity: sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==} + '@babel/core@7.26.9': + resolution: {integrity: sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==} engines: {node: '>=6.9.0'} - '@babel/generator@7.26.3': - resolution: {integrity: sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==} + '@babel/generator@7.26.9': + resolution: {integrity: sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.25.9': - resolution: {integrity: sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==} + '@babel/helper-compilation-targets@7.26.5': + resolution: {integrity: sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==} engines: {node: '>=6.9.0'} '@babel/helper-module-imports@7.25.9': @@ -314,8 +314,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-plugin-utils@7.25.9': - resolution: {integrity: sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==} + '@babel/helper-plugin-utils@7.26.5': + resolution: {integrity: sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==} engines: {node: '>=6.9.0'} '@babel/helper-string-parser@7.25.9': @@ -330,12 +330,12 @@ packages: resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.26.0': - resolution: {integrity: sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==} + '@babel/helpers@7.26.9': + resolution: {integrity: sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==} engines: {node: '>=6.9.0'} - '@babel/parser@7.26.3': - resolution: {integrity: sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==} + '@babel/parser@7.26.9': + resolution: {integrity: sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==} engines: {node: '>=6.0.0'} hasBin: true @@ -351,20 +351,20 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/runtime@7.26.0': - resolution: {integrity: sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==} + '@babel/runtime@7.26.9': + resolution: {integrity: sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==} engines: {node: '>=6.9.0'} - '@babel/template@7.25.9': - resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==} + '@babel/template@7.26.9': + resolution: {integrity: sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.26.4': - resolution: {integrity: sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==} + '@babel/traverse@7.26.9': + resolution: {integrity: sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==} engines: {node: '>=6.9.0'} - '@babel/types@7.26.3': - resolution: {integrity: sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==} + '@babel/types@7.26.9': + resolution: {integrity: sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==} engines: {node: '>=6.9.0'} '@esbuild/aix-ppc64@0.21.5': @@ -379,8 +379,8 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.24.0': - resolution: {integrity: sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==} + '@esbuild/aix-ppc64@0.25.0': + resolution: {integrity: sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] @@ -397,8 +397,8 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.24.0': - resolution: {integrity: sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==} + '@esbuild/android-arm64@0.25.0': + resolution: {integrity: sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==} engines: {node: '>=18'} cpu: [arm64] os: [android] @@ -415,8 +415,8 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.24.0': - resolution: {integrity: sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==} + '@esbuild/android-arm@0.25.0': + resolution: {integrity: sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==} engines: {node: '>=18'} cpu: [arm] os: [android] @@ -433,8 +433,8 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.24.0': - resolution: {integrity: sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==} + '@esbuild/android-x64@0.25.0': + resolution: {integrity: sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==} engines: {node: '>=18'} cpu: [x64] os: [android] @@ -451,8 +451,8 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.24.0': - resolution: {integrity: sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==} + '@esbuild/darwin-arm64@0.25.0': + resolution: {integrity: sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] @@ -469,8 +469,8 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.24.0': - resolution: {integrity: sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==} + '@esbuild/darwin-x64@0.25.0': + resolution: {integrity: sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==} engines: {node: '>=18'} cpu: [x64] os: [darwin] @@ -487,8 +487,8 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.24.0': - resolution: {integrity: sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==} + '@esbuild/freebsd-arm64@0.25.0': + resolution: {integrity: sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] @@ -505,8 +505,8 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.24.0': - resolution: {integrity: sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==} + '@esbuild/freebsd-x64@0.25.0': + resolution: {integrity: sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] @@ -523,8 +523,8 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.24.0': - resolution: {integrity: sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==} + '@esbuild/linux-arm64@0.25.0': + resolution: {integrity: sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==} engines: {node: '>=18'} cpu: [arm64] os: [linux] @@ -541,8 +541,8 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.24.0': - resolution: {integrity: sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==} + '@esbuild/linux-arm@0.25.0': + resolution: {integrity: sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==} engines: {node: '>=18'} cpu: [arm] os: [linux] @@ -559,8 +559,8 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.24.0': - resolution: {integrity: sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==} + '@esbuild/linux-ia32@0.25.0': + resolution: {integrity: sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==} engines: {node: '>=18'} cpu: [ia32] os: [linux] @@ -577,8 +577,8 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.24.0': - resolution: {integrity: sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==} + '@esbuild/linux-loong64@0.25.0': + resolution: {integrity: sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==} engines: {node: '>=18'} cpu: [loong64] os: [linux] @@ -595,8 +595,8 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.24.0': - resolution: {integrity: sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==} + '@esbuild/linux-mips64el@0.25.0': + resolution: {integrity: sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] @@ -613,8 +613,8 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.24.0': - resolution: {integrity: sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==} + '@esbuild/linux-ppc64@0.25.0': + resolution: {integrity: sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] @@ -631,8 +631,8 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.24.0': - resolution: {integrity: sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==} + '@esbuild/linux-riscv64@0.25.0': + resolution: {integrity: sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] @@ -649,8 +649,8 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.24.0': - resolution: {integrity: sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==} + '@esbuild/linux-s390x@0.25.0': + resolution: {integrity: sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==} engines: {node: '>=18'} cpu: [s390x] os: [linux] @@ -667,12 +667,18 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.24.0': - resolution: {integrity: sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==} + '@esbuild/linux-x64@0.25.0': + resolution: {integrity: sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==} engines: {node: '>=18'} cpu: [x64] os: [linux] + '@esbuild/netbsd-arm64@0.25.0': + resolution: {integrity: sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-x64@0.21.5': resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} engines: {node: '>=12'} @@ -685,8 +691,8 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.24.0': - resolution: {integrity: sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==} + '@esbuild/netbsd-x64@0.25.0': + resolution: {integrity: sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] @@ -697,8 +703,8 @@ packages: cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-arm64@0.24.0': - resolution: {integrity: sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==} + '@esbuild/openbsd-arm64@0.25.0': + resolution: {integrity: sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] @@ -715,8 +721,8 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.24.0': - resolution: {integrity: sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==} + '@esbuild/openbsd-x64@0.25.0': + resolution: {integrity: sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] @@ -733,8 +739,8 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.24.0': - resolution: {integrity: sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==} + '@esbuild/sunos-x64@0.25.0': + resolution: {integrity: sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==} engines: {node: '>=18'} cpu: [x64] os: [sunos] @@ -751,8 +757,8 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.24.0': - resolution: {integrity: sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==} + '@esbuild/win32-arm64@0.25.0': + resolution: {integrity: sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==} engines: {node: '>=18'} cpu: [arm64] os: [win32] @@ -769,8 +775,8 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.24.0': - resolution: {integrity: sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==} + '@esbuild/win32-ia32@0.25.0': + resolution: {integrity: sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==} engines: {node: '>=18'} cpu: [ia32] os: [win32] @@ -787,8 +793,8 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.24.0': - resolution: {integrity: sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==} + '@esbuild/win32-x64@0.25.0': + resolution: {integrity: sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -803,32 +809,32 @@ packages: resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/config-array@0.19.1': - resolution: {integrity: sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==} + '@eslint/config-array@0.19.2': + resolution: {integrity: sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/core@0.9.1': - resolution: {integrity: sha512-GuUdqkyyzQI5RMIWkHhvTWLCyLo1jNK3vzkSyaExH5kHPDHcuL2VOpHjmMY+y3+NC69qAKToBqldTBgYeLSr9Q==} + '@eslint/core@0.12.0': + resolution: {integrity: sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/eslintrc@3.2.0': - resolution: {integrity: sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==} + '@eslint/eslintrc@3.3.0': + resolution: {integrity: sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.17.0': - resolution: {integrity: sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==} + '@eslint/js@9.21.0': + resolution: {integrity: sha512-BqStZ3HX8Yz6LvsF5ByXYrtigrV5AXADWLAGc7PH/1SxOb7/FIYYMszZZWiUou/GB9P2lXWk2SV4d+Z8h0nknw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/object-schema@2.1.5': - resolution: {integrity: sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==} + '@eslint/object-schema@2.1.6': + resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/plugin-kit@0.2.4': - resolution: {integrity: sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg==} + '@eslint/plugin-kit@0.2.7': + resolution: {integrity: sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@figma/plugin-typings@1.105.0': - resolution: {integrity: sha512-UpeS7TUvc4Mv9OP+RMI6GtBTKpD9EMTwsa3H1M6GPE5zTicr/PnBUrrkU52PFXqb9FeVZhMopCdPm7AXQeDa7Q==} + '@figma/plugin-typings@1.108.0': + resolution: {integrity: sha512-hZocQwkMUfQZSa/6H9qkSkIKMDC70OOcV8U7hBocAsD/bWEZh13uj+aP6IHHmStscoavo9Ir5pU7NWEUUcXLug==} '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} @@ -846,8 +852,8 @@ packages: resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} engines: {node: '>=18.18'} - '@humanwhocodes/retry@0.4.1': - resolution: {integrity: sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==} + '@humanwhocodes/retry@0.4.2': + resolution: {integrity: sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==} engines: {node: '>=18.18'} '@isaacs/cliui@8.0.2': @@ -872,62 +878,62 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} - '@next/env@14.2.20': - resolution: {integrity: sha512-JfDpuOCB0UBKlEgEy/H6qcBSzHimn/YWjUHzKl1jMeUO+QVRdzmTTl8gFJaNO87c8DXmVKhFCtwxQ9acqB3+Pw==} + '@next/env@14.2.24': + resolution: {integrity: sha512-LAm0Is2KHTNT6IT16lxT+suD0u+VVfYNQqM+EJTKuFRRuY2z+zj01kueWXPCxbMBDt0B5vONYzabHGUNbZYAhA==} - '@next/eslint-plugin-next@14.2.20': - resolution: {integrity: sha512-T0JRi706KLbvR1Uc46t56VtawbhR/igdBagzOrA7G+vv4rvjwnlu/Y4/Iq6X9TDVj5UZjyot4lUdkNd3V2kLhw==} + '@next/eslint-plugin-next@14.2.24': + resolution: {integrity: sha512-FDL3qs+5DML0AJz56DCVr+KnFYivxeAX73En8QbPw9GjJZ6zbfvqDy+HrarHFzbsIASn7y8y5ySJ/lllSruNVQ==} - '@next/swc-darwin-arm64@14.2.20': - resolution: {integrity: sha512-WDfq7bmROa5cIlk6ZNonNdVhKmbCv38XteVFYsxea1vDJt3SnYGgxLGMTXQNfs5OkFvAhmfKKrwe7Y0Hs+rWOg==} + '@next/swc-darwin-arm64@14.2.24': + resolution: {integrity: sha512-7Tdi13aojnAZGpapVU6meVSpNzgrFwZ8joDcNS8cJVNuP3zqqrLqeory9Xec5TJZR/stsGJdfwo8KeyloT3+rQ==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@14.2.20': - resolution: {integrity: sha512-XIQlC+NAmJPfa2hruLvr1H1QJJeqOTDV+v7tl/jIdoFvqhoihvSNykLU/G6NMgoeo+e/H7p/VeWSOvMUHKtTIg==} + '@next/swc-darwin-x64@14.2.24': + resolution: {integrity: sha512-lXR2WQqUtu69l5JMdTwSvQUkdqAhEWOqJEYUQ21QczQsAlNOW2kWZCucA6b3EXmPbcvmHB1kSZDua/713d52xg==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@14.2.20': - resolution: {integrity: sha512-pnzBrHTPXIMm5QX3QC8XeMkpVuoAYOmyfsO4VlPn+0NrHraNuWjdhe+3xLq01xR++iCvX+uoeZmJDKcOxI201Q==} + '@next/swc-linux-arm64-gnu@14.2.24': + resolution: {integrity: sha512-nxvJgWOpSNmzidYvvGDfXwxkijb6hL9+cjZx1PVG6urr2h2jUqBALkKjT7kpfurRWicK6hFOvarmaWsINT1hnA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-arm64-musl@14.2.20': - resolution: {integrity: sha512-WhJJAFpi6yqmUx1momewSdcm/iRXFQS0HU2qlUGlGE/+98eu7JWLD5AAaP/tkK1mudS/rH2f9E3WCEF2iYDydQ==} + '@next/swc-linux-arm64-musl@14.2.24': + resolution: {integrity: sha512-PaBgOPhqa4Abxa3y/P92F3kklNPsiFjcjldQGT7kFmiY5nuFn8ClBEoX8GIpqU1ODP2y8P6hio6vTomx2Vy0UQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-x64-gnu@14.2.20': - resolution: {integrity: sha512-ao5HCbw9+iG1Kxm8XsGa3X174Ahn17mSYBQlY6VGsdsYDAbz/ZP13wSLfvlYoIDn1Ger6uYA+yt/3Y9KTIupRg==} + '@next/swc-linux-x64-gnu@14.2.24': + resolution: {integrity: sha512-vEbyadiRI7GOr94hd2AB15LFVgcJZQWu7Cdi9cWjCMeCiUsHWA0U5BkGPuoYRnTxTn0HacuMb9NeAmStfBCLoQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-linux-x64-musl@14.2.20': - resolution: {integrity: sha512-CXm/kpnltKTT7945np6Td3w7shj/92TMRPyI/VvveFe8+YE+/YOJ5hyAWK5rpx711XO1jBCgXl211TWaxOtkaA==} + '@next/swc-linux-x64-musl@14.2.24': + resolution: {integrity: sha512-df0FC9ptaYsd8nQCINCzFtDWtko8PNRTAU0/+d7hy47E0oC17tI54U/0NdGk7l/76jz1J377dvRjmt6IUdkpzQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-win32-arm64-msvc@14.2.20': - resolution: {integrity: sha512-upJn2HGQgKNDbXVfIgmqT2BN8f3z/mX8ddoyi1I565FHbfowVK5pnMEwauvLvaJf4iijvuKq3kw/b6E9oIVRWA==} + '@next/swc-win32-arm64-msvc@14.2.24': + resolution: {integrity: sha512-ZEntbLjeYAJ286eAqbxpZHhDFYpYjArotQ+/TW9j7UROh0DUmX7wYDGtsTPpfCV8V+UoqHBPU7q9D4nDNH014Q==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-ia32-msvc@14.2.20': - resolution: {integrity: sha512-igQW/JWciTGJwj3G1ipalD2V20Xfx3ywQy17IV0ciOUBbFhNfyU1DILWsTi32c8KmqgIDviUEulW/yPb2FF90w==} + '@next/swc-win32-ia32-msvc@14.2.24': + resolution: {integrity: sha512-9KuS+XUXM3T6v7leeWU0erpJ6NsFIwiTFD5nzNg8J5uo/DMIPvCp3L1Ao5HjbHX0gkWPB1VrKoo/Il4F0cGK2Q==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] - '@next/swc-win32-x64-msvc@14.2.20': - resolution: {integrity: sha512-AFmqeLW6LtxeFTuoB+MXFeM5fm5052i3MU6xD0WzJDOwku6SkZaxb1bxjBaRC8uNqTRTSPl0yMFtjNowIVI67w==} + '@next/swc-win32-x64-msvc@14.2.24': + resolution: {integrity: sha512-cXcJ2+x0fXQ2CntaE00d7uUH+u1Bfp/E0HsNQH79YiLaZE5Rbm7dZzyAYccn3uICM7mw+DxoMqEfGXZtF4Fgaw==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -952,169 +958,169 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@rollup/rollup-android-arm-eabi@4.28.1': - resolution: {integrity: sha512-2aZp8AES04KI2dy3Ss6/MDjXbwBzj+i0GqKtWXgw2/Ma6E4jJvujryO6gJAghIRVz7Vwr9Gtl/8na3nDUKpraQ==} + '@rollup/rollup-android-arm-eabi@4.34.8': + resolution: {integrity: sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.28.1': - resolution: {integrity: sha512-EbkK285O+1YMrg57xVA+Dp0tDBRB93/BZKph9XhMjezf6F4TpYjaUSuPt5J0fZXlSag0LmZAsTmdGGqPp4pQFA==} + '@rollup/rollup-android-arm64@4.34.8': + resolution: {integrity: sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.28.1': - resolution: {integrity: sha512-prduvrMKU6NzMq6nxzQw445zXgaDBbMQvmKSJaxpaZ5R1QDM8w+eGxo6Y/jhT/cLoCvnZI42oEqf9KQNYz1fqQ==} + '@rollup/rollup-darwin-arm64@4.34.8': + resolution: {integrity: sha512-02rVdZ5tgdUNRxIUrFdcMBZQoaPMrxtwSb+/hOfBdqkatYHR3lZ2A2EGyHq2sGOd0Owk80oV3snlDASC24He3Q==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.28.1': - resolution: {integrity: sha512-WsvbOunsUk0wccO/TV4o7IKgloJ942hVFK1CLatwv6TJspcCZb9umQkPdvB7FihmdxgaKR5JyxDjWpCOp4uZlQ==} + '@rollup/rollup-darwin-x64@4.34.8': + resolution: {integrity: sha512-qIP/elwR/tq/dYRx3lgwK31jkZvMiD6qUtOycLhTzCvrjbZ3LjQnEM9rNhSGpbLXVJYQ3rq39A6Re0h9tU2ynw==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.28.1': - resolution: {integrity: sha512-HTDPdY1caUcU4qK23FeeGxCdJF64cKkqajU0iBnTVxS8F7H/7BewvYoG+va1KPSL63kQ1PGNyiwKOfReavzvNA==} + '@rollup/rollup-freebsd-arm64@4.34.8': + resolution: {integrity: sha512-IQNVXL9iY6NniYbTaOKdrlVP3XIqazBgJOVkddzJlqnCpRi/yAeSOa8PLcECFSQochzqApIOE1GHNu3pCz+BDA==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.28.1': - resolution: {integrity: sha512-m/uYasxkUevcFTeRSM9TeLyPe2QDuqtjkeoTpP9SW0XxUWfcYrGDMkO/m2tTw+4NMAF9P2fU3Mw4ahNvo7QmsQ==} + '@rollup/rollup-freebsd-x64@4.34.8': + resolution: {integrity: sha512-TYXcHghgnCqYFiE3FT5QwXtOZqDj5GmaFNTNt3jNC+vh22dc/ukG2cG+pi75QO4kACohZzidsq7yKTKwq/Jq7Q==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.28.1': - resolution: {integrity: sha512-QAg11ZIt6mcmzpNE6JZBpKfJaKkqTm1A9+y9O+frdZJEuhQxiugM05gnCWiANHj4RmbgeVJpTdmKRmH/a+0QbA==} + '@rollup/rollup-linux-arm-gnueabihf@4.34.8': + resolution: {integrity: sha512-A4iphFGNkWRd+5m3VIGuqHnG3MVnqKe7Al57u9mwgbyZ2/xF9Jio72MaY7xxh+Y87VAHmGQr73qoKL9HPbXj1g==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.28.1': - resolution: {integrity: sha512-dRP9PEBfolq1dmMcFqbEPSd9VlRuVWEGSmbxVEfiq2cs2jlZAl0YNxFzAQS2OrQmsLBLAATDMb3Z6MFv5vOcXg==} + '@rollup/rollup-linux-arm-musleabihf@4.34.8': + resolution: {integrity: sha512-S0lqKLfTm5u+QTxlFiAnb2J/2dgQqRy/XvziPtDd1rKZFXHTyYLoVL58M/XFwDI01AQCDIevGLbQrMAtdyanpA==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.28.1': - resolution: {integrity: sha512-uGr8khxO+CKT4XU8ZUH1TTEUtlktK6Kgtv0+6bIFSeiSlnGJHG1tSFSjm41uQ9sAO/5ULx9mWOz70jYLyv1QkA==} + '@rollup/rollup-linux-arm64-gnu@4.34.8': + resolution: {integrity: sha512-jpz9YOuPiSkL4G4pqKrus0pn9aYwpImGkosRKwNi+sJSkz+WU3anZe6hi73StLOQdfXYXC7hUfsQlTnjMd3s1A==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.28.1': - resolution: {integrity: sha512-QF54q8MYGAqMLrX2t7tNpi01nvq5RI59UBNx+3+37zoKX5KViPo/gk2QLhsuqok05sSCRluj0D00LzCwBikb0A==} + '@rollup/rollup-linux-arm64-musl@4.34.8': + resolution: {integrity: sha512-KdSfaROOUJXgTVxJNAZ3KwkRc5nggDk+06P6lgi1HLv1hskgvxHUKZ4xtwHkVYJ1Rep4GNo+uEfycCRRxht7+Q==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.28.1': - resolution: {integrity: sha512-vPul4uodvWvLhRco2w0GcyZcdyBfpfDRgNKU+p35AWEbJ/HPs1tOUrkSueVbBS0RQHAf/A+nNtDpvw95PeVKOA==} + '@rollup/rollup-linux-loongarch64-gnu@4.34.8': + resolution: {integrity: sha512-NyF4gcxwkMFRjgXBM6g2lkT58OWztZvw5KkV2K0qqSnUEqCVcqdh2jN4gQrTn/YUpAcNKyFHfoOZEer9nwo6uQ==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.28.1': - resolution: {integrity: sha512-pTnTdBuC2+pt1Rmm2SV7JWRqzhYpEILML4PKODqLz+C7Ou2apEV52h19CR7es+u04KlqplggmN9sqZlekg3R1A==} + '@rollup/rollup-linux-powerpc64le-gnu@4.34.8': + resolution: {integrity: sha512-LMJc999GkhGvktHU85zNTDImZVUCJ1z/MbAJTnviiWmmjyckP5aQsHtcujMjpNdMZPT2rQEDBlJfubhs3jsMfw==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.28.1': - resolution: {integrity: sha512-vWXy1Nfg7TPBSuAncfInmAI/WZDd5vOklyLJDdIRKABcZWojNDY0NJwruY2AcnCLnRJKSaBgf/GiJfauu8cQZA==} + '@rollup/rollup-linux-riscv64-gnu@4.34.8': + resolution: {integrity: sha512-xAQCAHPj8nJq1PI3z8CIZzXuXCstquz7cIOL73HHdXiRcKk8Ywwqtx2wrIy23EcTn4aZ2fLJNBB8d0tQENPCmw==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.28.1': - resolution: {integrity: sha512-/yqC2Y53oZjb0yz8PVuGOQQNOTwxcizudunl/tFs1aLvObTclTwZ0JhXF2XcPT/zuaymemCDSuuUPXJJyqeDOg==} + '@rollup/rollup-linux-s390x-gnu@4.34.8': + resolution: {integrity: sha512-DdePVk1NDEuc3fOe3dPPTb+rjMtuFw89gw6gVWxQFAuEqqSdDKnrwzZHrUYdac7A7dXl9Q2Vflxpme15gUWQFA==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.28.1': - resolution: {integrity: sha512-fzgeABz7rrAlKYB0y2kSEiURrI0691CSL0+KXwKwhxvj92VULEDQLpBYLHpF49MSiPG4sq5CK3qHMnb9tlCjBw==} + '@rollup/rollup-linux-x64-gnu@4.34.8': + resolution: {integrity: sha512-8y7ED8gjxITUltTUEJLQdgpbPh1sUQ0kMTmufRF/Ns5tI9TNMNlhWtmPKKHCU0SilX+3MJkZ0zERYYGIVBYHIA==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.28.1': - resolution: {integrity: sha512-xQTDVzSGiMlSshpJCtudbWyRfLaNiVPXt1WgdWTwWz9n0U12cI2ZVtWe/Jgwyv/6wjL7b66uu61Vg0POWVfz4g==} + '@rollup/rollup-linux-x64-musl@4.34.8': + resolution: {integrity: sha512-SCXcP0ZpGFIe7Ge+McxY5zKxiEI5ra+GT3QRxL0pMMtxPfpyLAKleZODi1zdRHkz5/BhueUrYtYVgubqe9JBNQ==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.28.1': - resolution: {integrity: sha512-wSXmDRVupJstFP7elGMgv+2HqXelQhuNf+IS4V+nUpNVi/GUiBgDmfwD0UGN3pcAnWsgKG3I52wMOBnk1VHr/A==} + '@rollup/rollup-win32-arm64-msvc@4.34.8': + resolution: {integrity: sha512-YHYsgzZgFJzTRbth4h7Or0m5O74Yda+hLin0irAIobkLQFRQd1qWmnoVfwmKm9TXIZVAD0nZ+GEb2ICicLyCnQ==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.28.1': - resolution: {integrity: sha512-ZkyTJ/9vkgrE/Rk9vhMXhf8l9D+eAhbAVbsGsXKy2ohmJaWg0LPQLnIxRdRp/bKyr8tXuPlXhIoGlEB5XpJnGA==} + '@rollup/rollup-win32-ia32-msvc@4.34.8': + resolution: {integrity: sha512-r3NRQrXkHr4uWy5TOjTpTYojR9XmF0j/RYgKCef+Ag46FWUTltm5ziticv8LdNsDMehjJ543x/+TJAek/xBA2w==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.28.1': - resolution: {integrity: sha512-ZvK2jBafvttJjoIdKm/Q/Bh7IJ1Ose9IBOwpOXcOvW3ikGTQGmKDgxTC6oCAzW6PynbkKP8+um1du81XJHZ0JA==} + '@rollup/rollup-win32-x64-msvc@4.34.8': + resolution: {integrity: sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g==} cpu: [x64] os: [win32] '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} - '@rushstack/eslint-patch@1.10.4': - resolution: {integrity: sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==} + '@rushstack/eslint-patch@1.10.5': + resolution: {integrity: sha512-kkKUDVlII2DQiKy7UstOR1ErJP8kUKAQ4oa+SQtM0K+lPdmmjj0YnnxBgtTVYH7mUKtbsxeFC9y0AmK7Yb78/A==} - '@swc/core-darwin-arm64@1.10.1': - resolution: {integrity: sha512-NyELPp8EsVZtxH/mEqvzSyWpfPJ1lugpTQcSlMduZLj1EASLO4sC8wt8hmL1aizRlsbjCX+r0PyL+l0xQ64/6Q==} + '@swc/core-darwin-arm64@1.11.5': + resolution: {integrity: sha512-GEd1hzEx0mSGkJYMFMGLnrGgjL2rOsOsuYWyjyiA3WLmhD7o+n/EWBDo6mzD/9aeF8dzSPC0TnW216gJbvrNzA==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] - '@swc/core-darwin-x64@1.10.1': - resolution: {integrity: sha512-L4BNt1fdQ5ZZhAk5qoDfUnXRabDOXKnXBxMDJ+PWLSxOGBbWE6aJTnu4zbGjJvtot0KM46m2LPAPY8ttknqaZA==} + '@swc/core-darwin-x64@1.11.5': + resolution: {integrity: sha512-toz04z9wAClVvQSEY3xzrgyyeWBAfMWcKG4K0ugNvO56h/wczi2ZHRlnAXZW1tghKBk3z6MXqa/srfXgNhffKw==} engines: {node: '>=10'} cpu: [x64] os: [darwin] - '@swc/core-linux-arm-gnueabihf@1.10.1': - resolution: {integrity: sha512-Y1u9OqCHgvVp2tYQAJ7hcU9qO5brDMIrA5R31rwWQIAKDkJKtv3IlTHF0hrbWk1wPR0ZdngkQSJZple7G+Grvw==} + '@swc/core-linux-arm-gnueabihf@1.11.5': + resolution: {integrity: sha512-5SjmKxXdwbBpsYGTpgeXOXMIjS563/ntRGn8Zc12H/c4VfPrRLGhgbJ/48z2XVFyBLcw7BCHZyFuVX1+ZI3W0Q==} engines: {node: '>=10'} cpu: [arm] os: [linux] - '@swc/core-linux-arm64-gnu@1.10.1': - resolution: {integrity: sha512-tNQHO/UKdtnqjc7o04iRXng1wTUXPgVd8Y6LI4qIbHVoVPwksZydISjMcilKNLKIwOoUQAkxyJ16SlOAeADzhQ==} + '@swc/core-linux-arm64-gnu@1.11.5': + resolution: {integrity: sha512-pydIlInHRzRIwB0NHblz3Dx58H/bsi0I5F2deLf9iOmwPNuOGcEEZF1Qatc7YIjP5DFbXK+Dcz+pMUZb2cc2MQ==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-arm64-musl@1.10.1': - resolution: {integrity: sha512-x0L2Pd9weQ6n8dI1z1Isq00VHFvpBClwQJvrt3NHzmR+1wCT/gcYl1tp9P5xHh3ldM8Cn4UjWCw+7PaUgg8FcQ==} + '@swc/core-linux-arm64-musl@1.11.5': + resolution: {integrity: sha512-LhBHKjkZq5tJF1Lh0NJFpx7ROnCWLckrlIAIdSt9XfOV+zuEXJQOj+NFcM1eNk17GFfFyUMOZyGZxzYq5dveEQ==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-x64-gnu@1.10.1': - resolution: {integrity: sha512-yyYEwQcObV3AUsC79rSzN9z6kiWxKAVJ6Ntwq2N9YoZqSPYph+4/Am5fM1xEQYf/kb99csj0FgOelomJSobxQA==} + '@swc/core-linux-x64-gnu@1.11.5': + resolution: {integrity: sha512-dCi4xkxXlsk5sQYb3i413Cfh7+wMJeBYTvBZTD5xh+/DgRtIcIJLYJ2tNjWC4/C2i5fj+Ze9bKNSdd8weRWZ3A==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-linux-x64-musl@1.10.1': - resolution: {integrity: sha512-tcaS43Ydd7Fk7sW5ROpaf2Kq1zR+sI5K0RM+0qYLYYurvsJruj3GhBCaiN3gkzd8m/8wkqNqtVklWaQYSDsyqA==} + '@swc/core-linux-x64-musl@1.11.5': + resolution: {integrity: sha512-K0AC4TreM5Oo/tXNXnE/Gf5+5y/HwUdd7xvUjOpZddcX/RlsbYOKWLgOtA3fdFIuta7XC+vrGKmIhm5l70DSVQ==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-win32-arm64-msvc@1.10.1': - resolution: {integrity: sha512-D3Qo1voA7AkbOzQ2UGuKNHfYGKL6eejN8VWOoQYtGHHQi1p5KK/Q7V1ku55oxXBsj79Ny5FRMqiRJpVGad7bjQ==} + '@swc/core-win32-arm64-msvc@1.11.5': + resolution: {integrity: sha512-wzum8sYUsvPY7kgUfuqVYTgIPYmBC8KPksoNM1fz5UfhudU0ciQuYvUBD47GIGOevaoxhLkjPH4CB95vh1mJ9w==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - '@swc/core-win32-ia32-msvc@1.10.1': - resolution: {integrity: sha512-WalYdFoU3454Og+sDKHM1MrjvxUGwA2oralknXkXL8S0I/8RkWZOB++p3pLaGbTvOO++T+6znFbQdR8KRaa7DA==} + '@swc/core-win32-ia32-msvc@1.11.5': + resolution: {integrity: sha512-lco7mw0TPRTpVPR6NwggJpjdUkAboGRkLrDHjIsUaR+Y5+0m5FMMkHOMxWXAbrBS5c4ph7QErp4Lma4r9Mn5og==} engines: {node: '>=10'} cpu: [ia32] os: [win32] - '@swc/core-win32-x64-msvc@1.10.1': - resolution: {integrity: sha512-JWobfQDbTnoqaIwPKQ3DVSywihVXlQMbDuwik/dDWlj33A8oEHcjPOGs4OqcA3RHv24i+lfCQpM3Mn4FAMfacA==} + '@swc/core-win32-x64-msvc@1.11.5': + resolution: {integrity: sha512-E+DApLSC6JRK8VkDa4bNsBdD7Qoomx1HvKVZpOXl9v94hUZI5GMExl4vU5isvb+hPWL7rZ0NeI7ITnVLgLJRbA==} engines: {node: '>=10'} cpu: [x64] os: [win32] - '@swc/core@1.10.1': - resolution: {integrity: sha512-rQ4dS6GAdmtzKiCRt3LFVxl37FaY1cgL9kSUTnhQ2xc3fmHOd7jdJK/V4pSZMG1ruGTd0bsi34O2R0Olg9Zo/w==} + '@swc/core@1.11.5': + resolution: {integrity: sha512-EVY7zfpehxhTZXOfy508gb3D78ihoGGmvyiTWtlBPjgIaidP1Xw0naHMD78CWiFlZmeDjKXJufGtsEGOnZdmNA==} engines: {node: '>=10'} peerDependencies: '@swc/helpers': '*' @@ -1131,8 +1137,8 @@ packages: '@swc/helpers@0.5.5': resolution: {integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==} - '@swc/types@0.1.17': - resolution: {integrity: sha512-V5gRru+aD8YVyCOMAjMpWR1Ui577DD5KSJsHP8RAxopAH22jFz6GZd/qxqjO6MJHQhcsjvjOFXyDhyLQUnMveQ==} + '@swc/types@0.1.19': + resolution: {integrity: sha512-WkAZaAfj44kh/UFdAQcrMP1I0nwRqpt27u+08LMBYMqmQfwwMofYoMh/48NGkMMRfC4ynpfwRbJuu8ErfNloeA==} '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -1158,8 +1164,8 @@ packages: '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} - '@types/node@20.17.10': - resolution: {integrity: sha512-/jrvh5h6NXhEauFFexRin69nA0uHJ5gwk4iDivp/DeoEua3uwCUto6PC86IpRITBOs4+6i2I56K5x5b6WYGXHA==} + '@types/node@20.17.21': + resolution: {integrity: sha512-yw1WZ94lZpdZbpnaF+WRvlN/Sx2EZWe/YZVdK4mC4u02/ql6Ozen8qbRJhOtltOxCg97/kpijhGs5X6STwkvbg==} '@types/prop-types@15.7.14': resolution: {integrity: sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==} @@ -1172,8 +1178,8 @@ packages: '@types/react-syntax-highlighter@15.5.13': resolution: {integrity: sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA==} - '@types/react@18.3.17': - resolution: {integrity: sha512-opAQ5no6LqJNo9TqnxBKsgnkIYHozW9KSTlFVoSUJYh1Fl/sswkEoqIugRSm7tbh6pABtYjGAjW+GOS23j8qbw==} + '@types/react@18.3.18': + resolution: {integrity: sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==} '@types/unist@2.0.11': resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} @@ -1189,8 +1195,8 @@ packages: typescript: optional: true - '@typescript-eslint/eslint-plugin@8.18.1': - resolution: {integrity: sha512-Ncvsq5CT3Gvh+uJG0Lwlho6suwDfUXH0HztslDf5I+F2wAFAZMRwYLEorumpKLzmO2suAXZ/td1tBg4NZIi9CQ==} + '@typescript-eslint/eslint-plugin@8.25.0': + resolution: {integrity: sha512-VM7bpzAe7JO/BFf40pIT1lJqS/z1F8OaSsUB3rpFJucQA4cOSuH2RVVVkFULN+En0Djgr29/jb4EQnedUo95KA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 @@ -1207,8 +1213,8 @@ packages: typescript: optional: true - '@typescript-eslint/parser@8.18.1': - resolution: {integrity: sha512-rBnTWHCdbYM2lh7hjyXqxk70wvon3p2FyaniZuey5TrcGBpfhVp0OxOa6gxr9Q9YhZFKyfbEnxc24ZnVbbUkCA==} + '@typescript-eslint/parser@8.25.0': + resolution: {integrity: sha512-4gbs64bnbSzu4FpgMiQ1A+D+urxkoJk/kqlDJ2W//5SygaEiAP2B4GoS7TEdxgwol2el03gckFV9lJ4QOMiiHg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -1218,8 +1224,8 @@ packages: resolution: {integrity: sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==} engines: {node: ^18.18.0 || >=20.0.0} - '@typescript-eslint/scope-manager@8.18.1': - resolution: {integrity: sha512-HxfHo2b090M5s2+/9Z3gkBhI6xBH8OJCFjH9MhQ+nnoZqxU3wNxkLT+VWXWSFWc3UF3Z+CfPAyqdCTdoXtDPCQ==} + '@typescript-eslint/scope-manager@8.25.0': + resolution: {integrity: sha512-6PPeiKIGbgStEyt4NNXa2ru5pMzQ8OYKO1hX1z53HMomrmiSB+R5FmChgQAP1ro8jMtNawz+TRQo/cSXrauTpg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/type-utils@7.18.0': @@ -1232,8 +1238,8 @@ packages: typescript: optional: true - '@typescript-eslint/type-utils@8.18.1': - resolution: {integrity: sha512-jAhTdK/Qx2NJPNOTxXpMwlOiSymtR2j283TtPqXkKBdH8OAMmhiUfP0kJjc/qSE51Xrq02Gj9NY7MwK+UxVwHQ==} + '@typescript-eslint/type-utils@8.25.0': + resolution: {integrity: sha512-d77dHgHWnxmXOPJuDWO4FDWADmGQkN5+tt6SFRZz/RtCWl4pHgFl3+WdYCn16+3teG09DY6XtEpf3gGD0a186g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -1243,8 +1249,8 @@ packages: resolution: {integrity: sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==} engines: {node: ^18.18.0 || >=20.0.0} - '@typescript-eslint/types@8.18.1': - resolution: {integrity: sha512-7uoAUsCj66qdNQNpH2G8MyTFlgerum8ubf21s3TSM3XmKXuIn+H2Sifh/ES2nPOPiYSRJWAk0fDkW0APBWcpfw==} + '@typescript-eslint/types@8.25.0': + resolution: {integrity: sha512-+vUe0Zb4tkNgznQwicsvLUJgZIRs6ITeWSCclX1q85pR1iOiaj+4uZJIUp//Z27QWu5Cseiw3O3AR8hVpax7Aw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/typescript-estree@7.18.0': @@ -1256,8 +1262,8 @@ packages: typescript: optional: true - '@typescript-eslint/typescript-estree@8.18.1': - resolution: {integrity: sha512-z8U21WI5txzl2XYOW7i9hJhxoKKNG1kcU4RzyNvKrdZDmbjkmLBo8bgeiOJmA06kizLI76/CCBAAGlTlEeUfyg==} + '@typescript-eslint/typescript-estree@8.25.0': + resolution: {integrity: sha512-ZPaiAKEZ6Blt/TPAx5Ot0EIB/yGtLI2EsGoY6F7XKklfMxYQyvtL+gT/UCqkMzO0BVFHLDlzvFqQzurYahxv9Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.8.0' @@ -1268,8 +1274,8 @@ packages: peerDependencies: eslint: ^8.56.0 - '@typescript-eslint/utils@8.18.1': - resolution: {integrity: sha512-8vikiIj2ebrC4WRdcAdDcmnu9Q/MXXwg+STf40BVfT8exDqBCUPdypvzcUPxEqRGKg9ALagZ0UWcYCtn+4W2iQ==} + '@typescript-eslint/utils@8.25.0': + resolution: {integrity: sha512-syqRbrEv0J1wywiLsK60XzHnQe/kRViI3zwFALrNEgnntn1l24Ra2KvOAWwWbWZ1lBZxZljPDGOq967dsl6fkA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -1279,12 +1285,12 @@ packages: resolution: {integrity: sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==} engines: {node: ^18.18.0 || >=20.0.0} - '@typescript-eslint/visitor-keys@8.18.1': - resolution: {integrity: sha512-Vj0WLm5/ZsD013YeUKn+K0y8p1M0jPpxOkKdbD1wB0ns53a5piVY02zjf072TblEweAbcYiFiPoSMF3kp+VhhQ==} + '@typescript-eslint/visitor-keys@8.25.0': + resolution: {integrity: sha512-kCYXKAum9CecGVHGij7muybDfTS2sD3t0L4bJsEZLkyrXUImiCTq1M3LG2SRtOhiHFwMR9wAFplpT6XHYjTkwQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@vitejs/plugin-react-swc@3.7.2': - resolution: {integrity: sha512-y0byko2b2tSVVf5Gpng1eEhX1OvPC7x8yns1Fx8jDzlJp4LS6CMkCPfLw47cjyoMrshQDoQw4qcgjsU9VvlCew==} + '@vitejs/plugin-react-swc@3.8.0': + resolution: {integrity: sha512-T4sHPvS+DIqDP51ifPqa9XIRAz/kIvIi8oXcnOZZgHmMotgmmdxe/DD5tMFlt5nuIRzT0/QuiwmKlH0503Aapw==} peerDependencies: vite: ^4 || ^5 || ^6 @@ -1340,8 +1346,8 @@ packages: resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} engines: {node: '>= 0.4'} - array-buffer-byte-length@1.0.1: - resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} engines: {node: '>= 0.4'} array-includes@3.1.8: @@ -1379,6 +1385,10 @@ packages: ast-types-flow@0.0.8: resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + autoprefixer@10.4.20: resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==} engines: {node: ^10 || ^12 || >=14} @@ -1415,13 +1425,13 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.24.3: - resolution: {integrity: sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==} + browserslist@4.24.4: + resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - bundle-require@5.0.0: - resolution: {integrity: sha512-GuziW3fSSmopcx4KRymQEJVbZUfqlCqcq7dvs6TYwKRZiegK/2buMxQTPs6MGlNv50wms1699qYO54R8XfRX4w==} + bundle-require@5.1.0: + resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} peerDependencies: esbuild: '>=0.18' @@ -1434,8 +1444,8 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} - call-bind-apply-helpers@1.0.1: - resolution: {integrity: sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==} + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} call-bind@1.0.8: @@ -1454,8 +1464,8 @@ packages: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} - caniuse-lite@1.0.30001689: - resolution: {integrity: sha512-CmeR2VBycfa+5/jOfnp/NpWPGd06nf1XYiefUvhXFfZE4GkRc9jv+eGPS4nT558WS/8lYCzV8SlANCIPvbWP1g==} + caniuse-lite@1.0.30001701: + resolution: {integrity: sha512-faRs/AW3jA9nTwmJBSO1PQ6L/EOgsB5HMQQq4iCu5zhPgVVgO/pZRHlmatwijZKetFw8/Pr4q6dEN8sJuq8qTw==} chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} @@ -1474,8 +1484,8 @@ packages: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} - chokidar@4.0.2: - resolution: {integrity: sha512-/b57FK+bblSU+dfewfFe0rT1YjVDfOmeLQwCAuC+vwvgLkXboATqqmy+Ipux6JrF6L5joe5CBnFOw+gLWH6yKg==} + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} client-only@0.0.1: @@ -1507,8 +1517,8 @@ packages: engines: {node: ^14.13.0 || >=16.0.0} hasBin: true - consola@3.2.3: - resolution: {integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==} + consola@3.4.0: + resolution: {integrity: sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA==} engines: {node: ^14.18.0 || >=16.10.0} convert-source-map@2.0.0: @@ -1532,16 +1542,16 @@ packages: damerau-levenshtein@1.0.8: resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} - data-view-buffer@1.0.1: - resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} engines: {node: '>= 0.4'} - data-view-byte-length@1.0.1: - resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} engines: {node: '>= 0.4'} - data-view-byte-offset@1.0.0: - resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} engines: {node: '>= 0.4'} date-fns@2.30.0: @@ -1601,8 +1611,8 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - electron-to-chromium@1.5.74: - resolution: {integrity: sha512-ck3//9RC+6oss/1Bh9tiAVFy5vfSKbRHAFh7Z3/eTRkEqJeWgymloShB17Vg3Z4nmDNp35vAd1BZ6CMW4Wt6Iw==} + electron-to-chromium@1.5.109: + resolution: {integrity: sha512-AidaH9JETVRr9DIPGfp1kAarm/W6hRJTPuCnkF+2MqhF4KaAgRIcBc8nvjk+YMXZhwfISof/7WG29eS4iGxQLQ==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -1610,12 +1620,12 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - enhanced-resolve@5.17.1: - resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==} + enhanced-resolve@5.18.1: + resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} engines: {node: '>=10.13.0'} - es-abstract@1.23.6: - resolution: {integrity: sha512-Ifco6n3yj2tMZDWNLyloZrytt9lqqlwvS83P3HtaETR0NUOYnIULGGHpktqYGObGy+8wc1okO25p8TjemhImvA==} + es-abstract@1.23.9: + resolution: {integrity: sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==} engines: {node: '>= 0.4'} es-define-property@1.0.1: @@ -1626,20 +1636,21 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-iterator-helpers@1.2.0: - resolution: {integrity: sha512-tpxqxncxnpw3c93u8n3VOzACmRFoVmWJqbWXvX/JfKbkhBw1oslgPrUfeSt2psuqyEJFD6N/9lg5i7bsKpoq+Q==} + es-iterator-helpers@1.2.1: + resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==} engines: {node: '>= 0.4'} - es-object-atoms@1.0.0: - resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} - es-set-tostringtag@2.0.3: - resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} - es-shim-unscopables@1.0.2: - resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} + es-shim-unscopables@1.1.0: + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} + engines: {node: '>= 0.4'} es-to-primitive@1.3.0: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} @@ -1655,8 +1666,8 @@ packages: engines: {node: '>=18'} hasBin: true - esbuild@0.24.0: - resolution: {integrity: sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==} + esbuild@0.25.0: + resolution: {integrity: sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==} engines: {node: '>=18'} hasBin: true @@ -1668,8 +1679,8 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - eslint-config-next@14.2.20: - resolution: {integrity: sha512-gHBvp4RDd51DAaDco7KiWFy731EwcItkDtGUaZH1EUXEnHCzsVRjMceT+b8aThjMLjOScz6Q27MGlePASvK4Aw==} + eslint-config-next@14.2.24: + resolution: {integrity: sha512-9r1ujK++Pgpfixr5+DQ6rXDIQmSzuDbBlAQYMkJRMz9KWqovX7ESUTC0EAyBfOCl3ubkoeplw+aoXDuih3A8fw==} peerDependencies: eslint: ^7.23.0 || ^8.0.0 typescript: '>=3.3.1' @@ -1683,16 +1694,17 @@ packages: peerDependencies: eslint: '>=7.0.0' - eslint-config-turbo@2.3.3: - resolution: {integrity: sha512-cM9wSBYowQIrjx2MPCzFE6jTnG4vpTPJKZ/O+Ps3CqrmGK/wtNOsY6WHGMwLtKY/nNbgRahAJH6jGVF6k2coOg==} + eslint-config-turbo@2.4.4: + resolution: {integrity: sha512-4w/heWywWkFw09a5MY5lCvb9suJlhBSkzNtGTwM5+zRif4rksubaMYy1pD0++5rqoDVcQax25jCrtii9ptsNDw==} peerDependencies: eslint: '>6.6.0' + turbo: '>2.0.0' eslint-import-resolver-node@0.3.9: resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} - eslint-import-resolver-typescript@3.7.0: - resolution: {integrity: sha512-Vrwyi8HHxY97K5ebydMtffsWAn1SCR9eol49eCd5fJS4O1WV7PaAjbcjmbfJJSMz/t4Mal212Uz/fQZrOB8mow==} + eslint-import-resolver-typescript@3.8.3: + resolution: {integrity: sha512-A0bu4Ks2QqDWNpeEgTQMPTngaMhuDu4yv6xpftBMAf+1ziXnpx+eSR1WRfoPTe2BAiAjHFZ7kSNx1fvr5g5pmQ==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: eslint: '*' @@ -1753,8 +1765,8 @@ packages: peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 - eslint-plugin-react-refresh@0.4.16: - resolution: {integrity: sha512-slterMlxAhov/DZO8NScf6mEeMBBXodFUolijDvrtTxyezyLoTQaa73FyYus/VbTdftd8wBgBxPMRk3poleXNQ==} + eslint-plugin-react-refresh@0.4.19: + resolution: {integrity: sha512-eyy8pcr/YxSYjBoqIFSrlbn9i/xvxUFa8CjzAYo9cFjgGXqq1hyjihcpZvxRLalpaWmueWR81xn7vuKmAFijDQ==} peerDependencies: eslint: '>=8.40' @@ -1764,10 +1776,11 @@ packages: peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 - eslint-plugin-turbo@2.3.3: - resolution: {integrity: sha512-j8UEA0Z+NNCsjZep9G5u5soDQHcXq/x4amrwulk6eHF1U91H2qAjp5I4jQcvJewmccCJbVp734PkHHTRnosjpg==} + eslint-plugin-turbo@2.4.4: + resolution: {integrity: sha512-myEnQTjr3FkI0j1Fu0Mqnv1z8n0JW5iFTOUNzHaEevjzl+1uzMSsFwks/x8i3rGmI3EYtC1BY8K2B2pS0Vfx6w==} peerDependencies: eslint: '>6.6.0' + turbo: '>2.0.0' eslint-scope@8.2.0: resolution: {integrity: sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==} @@ -1781,8 +1794,8 @@ packages: resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.17.0: - resolution: {integrity: sha512-evtlNcpJg+cZLcnVKwsai8fExnqjGPicK7gnUtlNuzu+Fv9bI0aLpND5T44VLQtoMEnI57LoXO9XAkIXwohKrA==} + eslint@9.21.0: + resolution: {integrity: sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -1814,8 +1827,8 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - fast-glob@3.3.2: - resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} fast-json-stable-stringify@2.1.0: @@ -1824,14 +1837,14 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fastq@1.17.1: - resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} fault@1.0.4: resolution: {integrity: sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==} - fdir@6.4.2: - resolution: {integrity: sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==} + fdir@6.4.3: + resolution: {integrity: sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==} peerDependencies: picomatch: ^3 || ^4 peerDependenciesMeta: @@ -1854,14 +1867,15 @@ packages: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} - flatted@3.3.2: - resolution: {integrity: sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==} + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} - for-each@0.3.3: - resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} - foreground-child@3.3.0: - resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} format@0.2.2: @@ -1879,8 +1893,8 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - function.prototype.name@1.1.7: - resolution: {integrity: sha512-2g4x+HqTJKM9zcJqBSpjoRmdcPFtJM60J3xJisTQSXBWka5XqyBN/2tNUgma1mztTXyDuUsEtYe5qcs7xYzYQA==} + function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} engines: {node: '>= 0.4'} functions-have-names@1.2.3: @@ -1894,16 +1908,20 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - get-intrinsic@1.2.6: - resolution: {integrity: sha512-qxsEs+9A+u85HhllWJJFicJfPDhRmjzoYdl64aMWW9yRIJmSyxdn8IEkuIM530/7T+lv0TIHd8L6Q/ra0tEoeA==} + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} get-symbol-description@1.1.0: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} - get-tsconfig@4.8.1: - resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} + get-tsconfig@4.10.0: + resolution: {integrity: sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==} glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} @@ -1991,8 +2009,8 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} - import-fresh@3.3.0: - resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} imurmurhash@0.1.4: @@ -2013,8 +2031,8 @@ packages: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} - is-async-function@2.0.0: - resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} engines: {node: '>= 0.4'} is-bigint@1.1.0: @@ -2025,8 +2043,8 @@ packages: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} - is-boolean-object@1.2.1: - resolution: {integrity: sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==} + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} engines: {node: '>= 0.4'} is-bun-module@1.3.0: @@ -2036,8 +2054,8 @@ packages: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} - is-core-module@2.16.0: - resolution: {integrity: sha512-urTSINYfAYgcbLb0yDQ6egFm6h3Mo1DcF9EkyXSRjjzdHbsulg01qhwWuXdOoUBuTkbQ80KDboXa0vFJ+BDH+g==} + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} engines: {node: '>= 0.4'} is-data-view@1.0.2: @@ -2063,8 +2081,8 @@ packages: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - is-generator-function@1.0.10: - resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} + is-generator-function@1.1.0: + resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==} engines: {node: '>= 0.4'} is-glob@4.0.3: @@ -2078,10 +2096,6 @@ packages: resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} engines: {node: '>= 0.4'} - is-negative-zero@2.0.3: - resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} - engines: {node: '>= 0.4'} - is-number-object@1.1.1: resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} engines: {node: '>= 0.4'} @@ -2118,8 +2132,8 @@ packages: resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} engines: {node: '>= 0.4'} - is-weakref@1.1.0: - resolution: {integrity: sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==} + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} engines: {node: '>= 0.4'} is-weakset@2.0.4: @@ -2132,8 +2146,8 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - iterator.prototype@1.1.4: - resolution: {integrity: sha512-x4WH0BWmrMmg4oHHl+duwubhrvczGlyuGAZu3nvrf0UXOfPu8IhZObFEr7DE/iv01YgVZrsOiRcqw2srkKEDIA==} + iterator.prototype@1.1.5: + resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} engines: {node: '>= 0.4'} jackspeak@2.3.6: @@ -2243,8 +2257,8 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - math-intrinsics@1.0.0: - resolution: {integrity: sha512-4MqMiKP90ybymYvsut0CH2g4XWbfLtmlCkXmtmdcDCxNB+mQcu1w/1+L/VD7vi/PSv7X2JYV7SCcR+jiPXnQtA==} + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} merge2@1.4.1: @@ -2283,8 +2297,8 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - next@14.2.20: - resolution: {integrity: sha512-yPvIiWsiyVYqJlSQxwmzMIReXn5HxFNq4+tlVQ812N1FbvhmE+fDpIAD7bcS2mGYQwPJ5vAsQouyme2eKsxaug==} + next@14.2.24: + resolution: {integrity: sha512-En8VEexSJ0Py2FfVnRRh8gtERwDRaJGNvsvad47ShkC2Yi8AXQPXEA2vKoDJlGFSj5WE5SyF21zNi4M5gyi+SQ==} engines: {node: '>=18.17.0'} hasBin: true peerDependencies: @@ -2320,16 +2334,16 @@ packages: resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} engines: {node: '>= 6'} - object-inspect@1.13.3: - resolution: {integrity: sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==} + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} object-keys@1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} - object.assign@4.1.6: - resolution: {integrity: sha512-yVh6qdqwYsX9rUYzK6VGV4QT16okE8zNvcX0uRPg9MmBNcvPdTJi7cErXnUw/IeQ7Cg4tYMM6BTL3JEcEA0YTQ==} + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} engines: {node: '>= 0.4'} object.entries@1.1.8: @@ -2344,14 +2358,18 @@ packages: resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} engines: {node: '>= 0.4'} - object.values@1.2.0: - resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} + object.values@1.2.1: + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -2408,8 +2426,8 @@ packages: resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} engines: {node: '>= 6'} - possible-typed-array-names@1.0.0: - resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} postcss-import@15.1.0: @@ -2471,16 +2489,16 @@ packages: resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} engines: {node: ^10 || ^12 || >=14} - postcss@8.4.49: - resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==} + postcss@8.5.3: + resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} engines: {node: ^10 || ^12 || >=14} prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - prettier@3.4.2: - resolution: {integrity: sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==} + prettier@3.5.2: + resolution: {integrity: sha512-lc6npv5PH7hVqozBR7lkBNOGXV9vMwROAPlumdBkX0wTbbzPu/U1hk5yL8p2pt4Xoc+2mkT8t/sow2YrV/M5qg==} engines: {node: '>=14'} hasBin: true @@ -2533,12 +2551,12 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} - readdirp@4.0.2: - resolution: {integrity: sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==} - engines: {node: '>= 14.16.0'} + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} - reflect.getprototypeof@1.0.9: - resolution: {integrity: sha512-r0Ay04Snci87djAsI4U+WNRcSw5S4pOH7qFjd/veA5gC7TbqESR3tcj28ia95L/fYUDw11JKP7uqUKUAfVvV5Q==} + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} engines: {node: '>= 0.4'} refractor@3.6.0: @@ -2547,8 +2565,8 @@ packages: regenerator-runtime@0.14.1: resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} - regexp.prototype.flags@1.5.3: - resolution: {integrity: sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==} + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} engines: {node: '>= 0.4'} require-directory@2.1.1: @@ -2566,33 +2584,38 @@ packages: resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - resolve@1.22.9: - resolution: {integrity: sha512-QxrmX1DzraFIi9PxdG5VkRfRwIgjwyud+z/iBwfRRrVmHc+P9Q7u2lSSpQ6bjr2gy5lrqIiU9vb6iAeGf2400A==} + resolve@1.22.10: + resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + engines: {node: '>= 0.4'} hasBin: true resolve@2.0.0-next.5: resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} hasBin: true - reusify@1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rollup@4.28.1: - resolution: {integrity: sha512-61fXYl/qNVinKmGSTHAZ6Yy8I3YIJC/r2m9feHo6SwVAVcLT5MPwOUFe7EuURA/4m0NR8lXG4BBXuo/IZEsjMg==} + rollup@4.34.8: + resolution: {integrity: sha512-489gTVMzAYdiZHFVA/ig/iYFllCcWFHMvUHI1rpFmkoUtRlQxqh6/yiNqnYibjMZ2b/+FUQwldG+aLsEt6bglQ==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - rxjs@7.8.1: - resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + rxjs@7.8.2: + resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} safe-array-concat@1.1.3: resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} engines: {node: '>=0.4'} + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + safe-regex-test@1.1.0: resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} engines: {node: '>= 0.4'} @@ -2604,8 +2627,8 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.6.3: - resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + semver@7.7.1: + resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} engines: {node: '>=10'} hasBin: true @@ -2617,6 +2640,10 @@ packages: resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} engines: {node: '>= 0.4'} + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -2686,8 +2713,8 @@ packages: resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==} engines: {node: '>= 0.4'} - string.prototype.matchall@4.0.11: - resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==} + string.prototype.matchall@4.0.12: + resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} engines: {node: '>= 0.4'} string.prototype.repeat@1.0.0: @@ -2767,11 +2794,11 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} - tinyexec@0.3.1: - resolution: {integrity: sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==} + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} - tinyglobby@0.2.10: - resolution: {integrity: sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==} + tinyglobby@0.2.12: + resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==} engines: {node: '>=12.0.0'} to-regex-range@5.0.1: @@ -2794,6 +2821,12 @@ packages: peerDependencies: typescript: '>=4.2.0' + ts-api-utils@2.0.1: + resolution: {integrity: sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} @@ -2803,8 +2836,8 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tsup@8.3.5: - resolution: {integrity: sha512-Tunf6r6m6tnZsG9GYWndg0z8dEV7fD733VBFzFJ5Vcm1FtlXB8xBD/rtrBi2a3YKEV7hHtxiZtW5EAVADoe1pA==} + tsup@8.4.0: + resolution: {integrity: sha512-b+eZbPCjz10fRryaAA7C8xlIHnf8VnsaRqydheLIqwG/Mcpfk8Z5zp3HayX7GaTygkigHl5cBUs+IhcySiIexQ==} engines: {node: '>=18'} hasBin: true peerDependencies: @@ -2822,38 +2855,38 @@ packages: typescript: optional: true - turbo-darwin-64@2.3.3: - resolution: {integrity: sha512-bxX82xe6du/3rPmm4aCC5RdEilIN99VUld4HkFQuw+mvFg6darNBuQxyWSHZTtc25XgYjQrjsV05888w1grpaA==} + turbo-darwin-64@2.4.4: + resolution: {integrity: sha512-5kPvRkLAfmWI0MH96D+/THnDMGXlFNmjeqNRj5grLKiry+M9pKj3pRuScddAXPdlxjO5Ptz06UNaOQrrYGTx1g==} cpu: [x64] os: [darwin] - turbo-darwin-arm64@2.3.3: - resolution: {integrity: sha512-DYbQwa3NsAuWkCUYVzfOUBbSUBVQzH5HWUFy2Kgi3fGjIWVZOFk86ss+xsWu//rlEAfYwEmopigsPYSmW4X15A==} + turbo-darwin-arm64@2.4.4: + resolution: {integrity: sha512-/gtHPqbGQXDFhrmy+Q/MFW2HUTUlThJ97WLLSe4bxkDrKHecDYhAjbZ4rN3MM93RV9STQb3Tqy4pZBtsd4DfCw==} cpu: [arm64] os: [darwin] - turbo-linux-64@2.3.3: - resolution: {integrity: sha512-eHj9OIB0dFaP6BxB88jSuaCLsOQSYWBgmhy2ErCu6D2GG6xW3b6e2UWHl/1Ho9FsTg4uVgo4DB9wGsKa5erjUA==} + turbo-linux-64@2.4.4: + resolution: {integrity: sha512-SR0gri4k0bda56hw5u9VgDXLKb1Q+jrw4lM7WAhnNdXvVoep4d6LmnzgMHQQR12Wxl3KyWPbkz9d1whL6NTm2Q==} cpu: [x64] os: [linux] - turbo-linux-arm64@2.3.3: - resolution: {integrity: sha512-NmDE/NjZoDj1UWBhMtOPmqFLEBKhzGS61KObfrDEbXvU3lekwHeoPvAMfcovzswzch+kN2DrtbNIlz+/rp8OCg==} + turbo-linux-arm64@2.4.4: + resolution: {integrity: sha512-COXXwzRd3vslQIfJhXUklgEqlwq35uFUZ7hnN+AUyXx7hUOLIiD5NblL+ETrHnhY4TzWszrbwUMfe2BYWtaPQg==} cpu: [arm64] os: [linux] - turbo-windows-64@2.3.3: - resolution: {integrity: sha512-O2+BS4QqjK3dOERscXqv7N2GXNcqHr9hXumkMxDj/oGx9oCatIwnnwx34UmzodloSnJpgSqjl8iRWiY65SmYoQ==} + turbo-windows-64@2.4.4: + resolution: {integrity: sha512-PV9rYNouGz4Ff3fd6sIfQy5L7HT9a4fcZoEv8PKRavU9O75G7PoDtm8scpHU10QnK0QQNLbE9qNxOAeRvF0fJg==} cpu: [x64] os: [win32] - turbo-windows-arm64@2.3.3: - resolution: {integrity: sha512-dW4ZK1r6XLPNYLIKjC4o87HxYidtRRcBeo/hZ9Wng2XM/MqqYkAyzJXJGgRMsc0MMEN9z4+ZIfnSNBrA0b08ag==} + turbo-windows-arm64@2.4.4: + resolution: {integrity: sha512-403sqp9t5sx6YGEC32IfZTVWkRAixOQomGYB8kEc6ZD+//LirSxzeCHCnM8EmSXw7l57U1G+Fb0kxgTcKPU/Lg==} cpu: [arm64] os: [win32] - turbo@2.3.3: - resolution: {integrity: sha512-DUHWQAcC8BTiUZDRzAYGvpSpGLiaOQPfYXlCieQbwUvmml/LRGIe3raKdrOPOoiX0DYlzxs2nH6BoWJoZrj8hA==} + turbo@2.4.4: + resolution: {integrity: sha512-N9FDOVaY3yz0YCOhYIgOGYad7+m2ptvinXygw27WPLQvcZDl3+0Sa77KGVlLSiuPDChOUEnTKE9VJwLSi9BPGQ==} hasBin: true type-check@0.4.0: @@ -2868,16 +2901,16 @@ packages: resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} engines: {node: '>= 0.4'} - typed-array-byte-offset@1.0.3: - resolution: {integrity: sha512-GsvTyUHTriq6o/bHcTd0vM7OQ9JEdlvluu9YISaA7+KzDzPaIzEeDFNkTfhdE3MYcNhNi0vq/LlegYgIs5yPAw==} + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} engines: {node: '>= 0.4'} typed-array-length@1.0.7: resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} engines: {node: '>= 0.4'} - typescript@5.7.2: - resolution: {integrity: sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==} + typescript@5.8.2: + resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==} engines: {node: '>=14.17'} hasBin: true @@ -2888,8 +2921,8 @@ packages: undici-types@6.19.8: resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} - update-browserslist-db@1.1.1: - resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==} + update-browserslist-db@1.1.3: + resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -2907,8 +2940,8 @@ packages: rollup: ^4.28.1 vite: ^5.4.11 || ^6.0.0 - vite@5.4.11: - resolution: {integrity: sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==} + vite@5.4.14: + resolution: {integrity: sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -2988,8 +3021,8 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yaml@2.6.1: - resolution: {integrity: sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==} + yaml@2.7.0: + resolution: {integrity: sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==} engines: {node: '>= 14'} hasBin: true @@ -3020,20 +3053,20 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.26.3': {} + '@babel/compat-data@7.26.8': {} - '@babel/core@7.26.0': + '@babel/core@7.26.9': dependencies: '@ampproject/remapping': 2.3.0 '@babel/code-frame': 7.26.2 - '@babel/generator': 7.26.3 - '@babel/helper-compilation-targets': 7.25.9 - '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) - '@babel/helpers': 7.26.0 - '@babel/parser': 7.26.3 - '@babel/template': 7.25.9 - '@babel/traverse': 7.26.4 - '@babel/types': 7.26.3 + '@babel/generator': 7.26.9 + '@babel/helper-compilation-targets': 7.26.5 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.9) + '@babel/helpers': 7.26.9 + '@babel/parser': 7.26.9 + '@babel/template': 7.26.9 + '@babel/traverse': 7.26.9 + '@babel/types': 7.26.9 convert-source-map: 2.0.0 debug: 4.4.0 gensync: 1.0.0-beta.2 @@ -3042,39 +3075,39 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/generator@7.26.3': + '@babel/generator@7.26.9': dependencies: - '@babel/parser': 7.26.3 - '@babel/types': 7.26.3 + '@babel/parser': 7.26.9 + '@babel/types': 7.26.9 '@jridgewell/gen-mapping': 0.3.8 '@jridgewell/trace-mapping': 0.3.25 jsesc: 3.1.0 - '@babel/helper-compilation-targets@7.25.9': + '@babel/helper-compilation-targets@7.26.5': dependencies: - '@babel/compat-data': 7.26.3 + '@babel/compat-data': 7.26.8 '@babel/helper-validator-option': 7.25.9 - browserslist: 4.24.3 + browserslist: 4.24.4 lru-cache: 5.1.1 semver: 6.3.1 '@babel/helper-module-imports@7.25.9': dependencies: - '@babel/traverse': 7.26.4 - '@babel/types': 7.26.3 + '@babel/traverse': 7.26.9 + '@babel/types': 7.26.9 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.0)': + '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.9)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.9 '@babel/helper-module-imports': 7.25.9 '@babel/helper-validator-identifier': 7.25.9 - '@babel/traverse': 7.26.4 + '@babel/traverse': 7.26.9 transitivePeerDependencies: - supports-color - '@babel/helper-plugin-utils@7.25.9': {} + '@babel/helper-plugin-utils@7.26.5': {} '@babel/helper-string-parser@7.25.9': {} @@ -3082,48 +3115,48 @@ snapshots: '@babel/helper-validator-option@7.25.9': {} - '@babel/helpers@7.26.0': + '@babel/helpers@7.26.9': dependencies: - '@babel/template': 7.25.9 - '@babel/types': 7.26.3 + '@babel/template': 7.26.9 + '@babel/types': 7.26.9 - '@babel/parser@7.26.3': + '@babel/parser@7.26.9': dependencies: - '@babel/types': 7.26.3 + '@babel/types': 7.26.9 - '@babel/plugin-transform-react-jsx-self@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-react-jsx-self@7.25.9(@babel/core@7.26.9)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/core': 7.26.9 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-react-jsx-source@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-react-jsx-source@7.25.9(@babel/core@7.26.9)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/core': 7.26.9 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/runtime@7.26.0': + '@babel/runtime@7.26.9': dependencies: regenerator-runtime: 0.14.1 - '@babel/template@7.25.9': + '@babel/template@7.26.9': dependencies: '@babel/code-frame': 7.26.2 - '@babel/parser': 7.26.3 - '@babel/types': 7.26.3 + '@babel/parser': 7.26.9 + '@babel/types': 7.26.9 - '@babel/traverse@7.26.4': + '@babel/traverse@7.26.9': dependencies: '@babel/code-frame': 7.26.2 - '@babel/generator': 7.26.3 - '@babel/parser': 7.26.3 - '@babel/template': 7.25.9 - '@babel/types': 7.26.3 + '@babel/generator': 7.26.9 + '@babel/parser': 7.26.9 + '@babel/template': 7.26.9 + '@babel/types': 7.26.9 debug: 4.4.0 globals: 11.12.0 transitivePeerDependencies: - supports-color - '@babel/types@7.26.3': + '@babel/types@7.26.9': dependencies: '@babel/helper-string-parser': 7.25.9 '@babel/helper-validator-identifier': 7.25.9 @@ -3134,7 +3167,7 @@ snapshots: '@esbuild/aix-ppc64@0.23.1': optional: true - '@esbuild/aix-ppc64@0.24.0': + '@esbuild/aix-ppc64@0.25.0': optional: true '@esbuild/android-arm64@0.21.5': @@ -3143,7 +3176,7 @@ snapshots: '@esbuild/android-arm64@0.23.1': optional: true - '@esbuild/android-arm64@0.24.0': + '@esbuild/android-arm64@0.25.0': optional: true '@esbuild/android-arm@0.21.5': @@ -3152,7 +3185,7 @@ snapshots: '@esbuild/android-arm@0.23.1': optional: true - '@esbuild/android-arm@0.24.0': + '@esbuild/android-arm@0.25.0': optional: true '@esbuild/android-x64@0.21.5': @@ -3161,7 +3194,7 @@ snapshots: '@esbuild/android-x64@0.23.1': optional: true - '@esbuild/android-x64@0.24.0': + '@esbuild/android-x64@0.25.0': optional: true '@esbuild/darwin-arm64@0.21.5': @@ -3170,7 +3203,7 @@ snapshots: '@esbuild/darwin-arm64@0.23.1': optional: true - '@esbuild/darwin-arm64@0.24.0': + '@esbuild/darwin-arm64@0.25.0': optional: true '@esbuild/darwin-x64@0.21.5': @@ -3179,7 +3212,7 @@ snapshots: '@esbuild/darwin-x64@0.23.1': optional: true - '@esbuild/darwin-x64@0.24.0': + '@esbuild/darwin-x64@0.25.0': optional: true '@esbuild/freebsd-arm64@0.21.5': @@ -3188,7 +3221,7 @@ snapshots: '@esbuild/freebsd-arm64@0.23.1': optional: true - '@esbuild/freebsd-arm64@0.24.0': + '@esbuild/freebsd-arm64@0.25.0': optional: true '@esbuild/freebsd-x64@0.21.5': @@ -3197,7 +3230,7 @@ snapshots: '@esbuild/freebsd-x64@0.23.1': optional: true - '@esbuild/freebsd-x64@0.24.0': + '@esbuild/freebsd-x64@0.25.0': optional: true '@esbuild/linux-arm64@0.21.5': @@ -3206,7 +3239,7 @@ snapshots: '@esbuild/linux-arm64@0.23.1': optional: true - '@esbuild/linux-arm64@0.24.0': + '@esbuild/linux-arm64@0.25.0': optional: true '@esbuild/linux-arm@0.21.5': @@ -3215,7 +3248,7 @@ snapshots: '@esbuild/linux-arm@0.23.1': optional: true - '@esbuild/linux-arm@0.24.0': + '@esbuild/linux-arm@0.25.0': optional: true '@esbuild/linux-ia32@0.21.5': @@ -3224,7 +3257,7 @@ snapshots: '@esbuild/linux-ia32@0.23.1': optional: true - '@esbuild/linux-ia32@0.24.0': + '@esbuild/linux-ia32@0.25.0': optional: true '@esbuild/linux-loong64@0.21.5': @@ -3233,7 +3266,7 @@ snapshots: '@esbuild/linux-loong64@0.23.1': optional: true - '@esbuild/linux-loong64@0.24.0': + '@esbuild/linux-loong64@0.25.0': optional: true '@esbuild/linux-mips64el@0.21.5': @@ -3242,7 +3275,7 @@ snapshots: '@esbuild/linux-mips64el@0.23.1': optional: true - '@esbuild/linux-mips64el@0.24.0': + '@esbuild/linux-mips64el@0.25.0': optional: true '@esbuild/linux-ppc64@0.21.5': @@ -3251,7 +3284,7 @@ snapshots: '@esbuild/linux-ppc64@0.23.1': optional: true - '@esbuild/linux-ppc64@0.24.0': + '@esbuild/linux-ppc64@0.25.0': optional: true '@esbuild/linux-riscv64@0.21.5': @@ -3260,7 +3293,7 @@ snapshots: '@esbuild/linux-riscv64@0.23.1': optional: true - '@esbuild/linux-riscv64@0.24.0': + '@esbuild/linux-riscv64@0.25.0': optional: true '@esbuild/linux-s390x@0.21.5': @@ -3269,7 +3302,7 @@ snapshots: '@esbuild/linux-s390x@0.23.1': optional: true - '@esbuild/linux-s390x@0.24.0': + '@esbuild/linux-s390x@0.25.0': optional: true '@esbuild/linux-x64@0.21.5': @@ -3278,7 +3311,10 @@ snapshots: '@esbuild/linux-x64@0.23.1': optional: true - '@esbuild/linux-x64@0.24.0': + '@esbuild/linux-x64@0.25.0': + optional: true + + '@esbuild/netbsd-arm64@0.25.0': optional: true '@esbuild/netbsd-x64@0.21.5': @@ -3287,13 +3323,13 @@ snapshots: '@esbuild/netbsd-x64@0.23.1': optional: true - '@esbuild/netbsd-x64@0.24.0': + '@esbuild/netbsd-x64@0.25.0': optional: true '@esbuild/openbsd-arm64@0.23.1': optional: true - '@esbuild/openbsd-arm64@0.24.0': + '@esbuild/openbsd-arm64@0.25.0': optional: true '@esbuild/openbsd-x64@0.21.5': @@ -3302,7 +3338,7 @@ snapshots: '@esbuild/openbsd-x64@0.23.1': optional: true - '@esbuild/openbsd-x64@0.24.0': + '@esbuild/openbsd-x64@0.25.0': optional: true '@esbuild/sunos-x64@0.21.5': @@ -3311,7 +3347,7 @@ snapshots: '@esbuild/sunos-x64@0.23.1': optional: true - '@esbuild/sunos-x64@0.24.0': + '@esbuild/sunos-x64@0.25.0': optional: true '@esbuild/win32-arm64@0.21.5': @@ -3320,7 +3356,7 @@ snapshots: '@esbuild/win32-arm64@0.23.1': optional: true - '@esbuild/win32-arm64@0.24.0': + '@esbuild/win32-arm64@0.25.0': optional: true '@esbuild/win32-ia32@0.21.5': @@ -3329,7 +3365,7 @@ snapshots: '@esbuild/win32-ia32@0.23.1': optional: true - '@esbuild/win32-ia32@0.24.0': + '@esbuild/win32-ia32@0.25.0': optional: true '@esbuild/win32-x64@0.21.5': @@ -3338,51 +3374,52 @@ snapshots: '@esbuild/win32-x64@0.23.1': optional: true - '@esbuild/win32-x64@0.24.0': + '@esbuild/win32-x64@0.25.0': optional: true - '@eslint-community/eslint-utils@4.4.1(eslint@9.17.0(jiti@1.21.7))': + '@eslint-community/eslint-utils@4.4.1(eslint@9.21.0(jiti@1.21.7))': dependencies: - eslint: 9.17.0(jiti@1.21.7) + eslint: 9.21.0(jiti@1.21.7) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} - '@eslint/config-array@0.19.1': + '@eslint/config-array@0.19.2': dependencies: - '@eslint/object-schema': 2.1.5 + '@eslint/object-schema': 2.1.6 debug: 4.4.0 minimatch: 3.1.2 transitivePeerDependencies: - supports-color - '@eslint/core@0.9.1': + '@eslint/core@0.12.0': dependencies: '@types/json-schema': 7.0.15 - '@eslint/eslintrc@3.2.0': + '@eslint/eslintrc@3.3.0': dependencies: ajv: 6.12.6 debug: 4.4.0 espree: 10.3.0 globals: 14.0.0 ignore: 5.3.2 - import-fresh: 3.3.0 + import-fresh: 3.3.1 js-yaml: 4.1.0 minimatch: 3.1.2 strip-json-comments: 3.1.1 transitivePeerDependencies: - supports-color - '@eslint/js@9.17.0': {} + '@eslint/js@9.21.0': {} - '@eslint/object-schema@2.1.5': {} + '@eslint/object-schema@2.1.6': {} - '@eslint/plugin-kit@0.2.4': + '@eslint/plugin-kit@0.2.7': dependencies: + '@eslint/core': 0.12.0 levn: 0.4.1 - '@figma/plugin-typings@1.105.0': {} + '@figma/plugin-typings@1.108.0': {} '@humanfs/core@0.19.1': {} @@ -3395,7 +3432,7 @@ snapshots: '@humanwhocodes/retry@0.3.1': {} - '@humanwhocodes/retry@0.4.1': {} + '@humanwhocodes/retry@0.4.2': {} '@isaacs/cliui@8.0.2': dependencies: @@ -3423,37 +3460,37 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 - '@next/env@14.2.20': {} + '@next/env@14.2.24': {} - '@next/eslint-plugin-next@14.2.20': + '@next/eslint-plugin-next@14.2.24': dependencies: glob: 10.3.10 - '@next/swc-darwin-arm64@14.2.20': + '@next/swc-darwin-arm64@14.2.24': optional: true - '@next/swc-darwin-x64@14.2.20': + '@next/swc-darwin-x64@14.2.24': optional: true - '@next/swc-linux-arm64-gnu@14.2.20': + '@next/swc-linux-arm64-gnu@14.2.24': optional: true - '@next/swc-linux-arm64-musl@14.2.20': + '@next/swc-linux-arm64-musl@14.2.24': optional: true - '@next/swc-linux-x64-gnu@14.2.20': + '@next/swc-linux-x64-gnu@14.2.24': optional: true - '@next/swc-linux-x64-musl@14.2.20': + '@next/swc-linux-x64-musl@14.2.24': optional: true - '@next/swc-win32-arm64-msvc@14.2.20': + '@next/swc-win32-arm64-msvc@14.2.24': optional: true - '@next/swc-win32-ia32-msvc@14.2.20': + '@next/swc-win32-ia32-msvc@14.2.24': optional: true - '@next/swc-win32-x64-msvc@14.2.20': + '@next/swc-win32-x64-msvc@14.2.24': optional: true '@nodelib/fs.scandir@2.1.5': @@ -3466,119 +3503,119 @@ snapshots: '@nodelib/fs.walk@1.2.8': dependencies: '@nodelib/fs.scandir': 2.1.5 - fastq: 1.17.1 + fastq: 1.19.1 '@nolyfill/is-core-module@1.0.39': {} '@pkgjs/parseargs@0.11.0': optional: true - '@rollup/rollup-android-arm-eabi@4.28.1': + '@rollup/rollup-android-arm-eabi@4.34.8': optional: true - '@rollup/rollup-android-arm64@4.28.1': + '@rollup/rollup-android-arm64@4.34.8': optional: true - '@rollup/rollup-darwin-arm64@4.28.1': + '@rollup/rollup-darwin-arm64@4.34.8': optional: true - '@rollup/rollup-darwin-x64@4.28.1': + '@rollup/rollup-darwin-x64@4.34.8': optional: true - '@rollup/rollup-freebsd-arm64@4.28.1': + '@rollup/rollup-freebsd-arm64@4.34.8': optional: true - '@rollup/rollup-freebsd-x64@4.28.1': + '@rollup/rollup-freebsd-x64@4.34.8': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.28.1': + '@rollup/rollup-linux-arm-gnueabihf@4.34.8': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.28.1': + '@rollup/rollup-linux-arm-musleabihf@4.34.8': optional: true - '@rollup/rollup-linux-arm64-gnu@4.28.1': + '@rollup/rollup-linux-arm64-gnu@4.34.8': optional: true - '@rollup/rollup-linux-arm64-musl@4.28.1': + '@rollup/rollup-linux-arm64-musl@4.34.8': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.28.1': + '@rollup/rollup-linux-loongarch64-gnu@4.34.8': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.28.1': + '@rollup/rollup-linux-powerpc64le-gnu@4.34.8': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.28.1': + '@rollup/rollup-linux-riscv64-gnu@4.34.8': optional: true - '@rollup/rollup-linux-s390x-gnu@4.28.1': + '@rollup/rollup-linux-s390x-gnu@4.34.8': optional: true - '@rollup/rollup-linux-x64-gnu@4.28.1': + '@rollup/rollup-linux-x64-gnu@4.34.8': optional: true - '@rollup/rollup-linux-x64-musl@4.28.1': + '@rollup/rollup-linux-x64-musl@4.34.8': optional: true - '@rollup/rollup-win32-arm64-msvc@4.28.1': + '@rollup/rollup-win32-arm64-msvc@4.34.8': optional: true - '@rollup/rollup-win32-ia32-msvc@4.28.1': + '@rollup/rollup-win32-ia32-msvc@4.34.8': optional: true - '@rollup/rollup-win32-x64-msvc@4.28.1': + '@rollup/rollup-win32-x64-msvc@4.34.8': optional: true '@rtsao/scc@1.1.0': {} - '@rushstack/eslint-patch@1.10.4': {} + '@rushstack/eslint-patch@1.10.5': {} - '@swc/core-darwin-arm64@1.10.1': + '@swc/core-darwin-arm64@1.11.5': optional: true - '@swc/core-darwin-x64@1.10.1': + '@swc/core-darwin-x64@1.11.5': optional: true - '@swc/core-linux-arm-gnueabihf@1.10.1': + '@swc/core-linux-arm-gnueabihf@1.11.5': optional: true - '@swc/core-linux-arm64-gnu@1.10.1': + '@swc/core-linux-arm64-gnu@1.11.5': optional: true - '@swc/core-linux-arm64-musl@1.10.1': + '@swc/core-linux-arm64-musl@1.11.5': optional: true - '@swc/core-linux-x64-gnu@1.10.1': + '@swc/core-linux-x64-gnu@1.11.5': optional: true - '@swc/core-linux-x64-musl@1.10.1': + '@swc/core-linux-x64-musl@1.11.5': optional: true - '@swc/core-win32-arm64-msvc@1.10.1': + '@swc/core-win32-arm64-msvc@1.11.5': optional: true - '@swc/core-win32-ia32-msvc@1.10.1': + '@swc/core-win32-ia32-msvc@1.11.5': optional: true - '@swc/core-win32-x64-msvc@1.10.1': + '@swc/core-win32-x64-msvc@1.11.5': optional: true - '@swc/core@1.10.1(@swc/helpers@0.5.12)': + '@swc/core@1.11.5(@swc/helpers@0.5.12)': dependencies: '@swc/counter': 0.1.3 - '@swc/types': 0.1.17 + '@swc/types': 0.1.19 optionalDependencies: - '@swc/core-darwin-arm64': 1.10.1 - '@swc/core-darwin-x64': 1.10.1 - '@swc/core-linux-arm-gnueabihf': 1.10.1 - '@swc/core-linux-arm64-gnu': 1.10.1 - '@swc/core-linux-arm64-musl': 1.10.1 - '@swc/core-linux-x64-gnu': 1.10.1 - '@swc/core-linux-x64-musl': 1.10.1 - '@swc/core-win32-arm64-msvc': 1.10.1 - '@swc/core-win32-ia32-msvc': 1.10.1 - '@swc/core-win32-x64-msvc': 1.10.1 + '@swc/core-darwin-arm64': 1.11.5 + '@swc/core-darwin-x64': 1.11.5 + '@swc/core-linux-arm-gnueabihf': 1.11.5 + '@swc/core-linux-arm64-gnu': 1.11.5 + '@swc/core-linux-arm64-musl': 1.11.5 + '@swc/core-linux-x64-gnu': 1.11.5 + '@swc/core-linux-x64-musl': 1.11.5 + '@swc/core-win32-arm64-msvc': 1.11.5 + '@swc/core-win32-ia32-msvc': 1.11.5 + '@swc/core-win32-x64-msvc': 1.11.5 '@swc/helpers': 0.5.12 '@swc/counter@0.1.3': {} @@ -3593,30 +3630,30 @@ snapshots: '@swc/counter': 0.1.3 tslib: 2.8.1 - '@swc/types@0.1.17': + '@swc/types@0.1.19': dependencies: '@swc/counter': 0.1.3 '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.26.3 - '@babel/types': 7.26.3 + '@babel/parser': 7.26.9 + '@babel/types': 7.26.9 '@types/babel__generator': 7.6.8 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.20.6 '@types/babel__generator@7.6.8': dependencies: - '@babel/types': 7.26.3 + '@babel/types': 7.26.9 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.26.3 - '@babel/types': 7.26.3 + '@babel/parser': 7.26.9 + '@babel/types': 7.26.9 '@types/babel__traverse@7.20.6': dependencies: - '@babel/types': 7.26.3 + '@babel/types': 7.26.9 '@types/estree@1.0.6': {} @@ -3628,84 +3665,84 @@ snapshots: '@types/json5@0.0.29': {} - '@types/node@20.17.10': + '@types/node@20.17.21': dependencies: undici-types: 6.19.8 '@types/prop-types@15.7.14': {} - '@types/react-dom@18.3.5(@types/react@18.3.17)': + '@types/react-dom@18.3.5(@types/react@18.3.18)': dependencies: - '@types/react': 18.3.17 + '@types/react': 18.3.18 '@types/react-syntax-highlighter@15.5.13': dependencies: - '@types/react': 18.3.17 + '@types/react': 18.3.18 - '@types/react@18.3.17': + '@types/react@18.3.18': dependencies: '@types/prop-types': 15.7.14 csstype: 3.1.3 '@types/unist@2.0.11': {} - '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2)': + '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2))(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 7.18.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2) + '@typescript-eslint/parser': 7.18.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2) '@typescript-eslint/scope-manager': 7.18.0 - '@typescript-eslint/type-utils': 7.18.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2) - '@typescript-eslint/utils': 7.18.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2) + '@typescript-eslint/type-utils': 7.18.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2) + '@typescript-eslint/utils': 7.18.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2) '@typescript-eslint/visitor-keys': 7.18.0 - eslint: 9.17.0(jiti@1.21.7) + eslint: 9.21.0(jiti@1.21.7) graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 - ts-api-utils: 1.4.3(typescript@5.7.2) + ts-api-utils: 1.4.3(typescript@5.8.2) optionalDependencies: - typescript: 5.7.2 + typescript: 5.8.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@8.18.1(@typescript-eslint/parser@8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2)': + '@typescript-eslint/eslint-plugin@8.25.0(@typescript-eslint/parser@8.25.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2))(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2) - '@typescript-eslint/scope-manager': 8.18.1 - '@typescript-eslint/type-utils': 8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2) - '@typescript-eslint/utils': 8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2) - '@typescript-eslint/visitor-keys': 8.18.1 - eslint: 9.17.0(jiti@1.21.7) + '@typescript-eslint/parser': 8.25.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2) + '@typescript-eslint/scope-manager': 8.25.0 + '@typescript-eslint/type-utils': 8.25.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2) + '@typescript-eslint/utils': 8.25.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2) + '@typescript-eslint/visitor-keys': 8.25.0 + eslint: 9.21.0(jiti@1.21.7) graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 - ts-api-utils: 1.4.3(typescript@5.7.2) - typescript: 5.7.2 + ts-api-utils: 2.0.1(typescript@5.8.2) + typescript: 5.8.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@7.18.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2)': + '@typescript-eslint/parser@7.18.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2)': dependencies: '@typescript-eslint/scope-manager': 7.18.0 '@typescript-eslint/types': 7.18.0 - '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.7.2) + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.8.2) '@typescript-eslint/visitor-keys': 7.18.0 debug: 4.4.0 - eslint: 9.17.0(jiti@1.21.7) + eslint: 9.21.0(jiti@1.21.7) optionalDependencies: - typescript: 5.7.2 + typescript: 5.8.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2)': + '@typescript-eslint/parser@8.25.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2)': dependencies: - '@typescript-eslint/scope-manager': 8.18.1 - '@typescript-eslint/types': 8.18.1 - '@typescript-eslint/typescript-estree': 8.18.1(typescript@5.7.2) - '@typescript-eslint/visitor-keys': 8.18.1 + '@typescript-eslint/scope-manager': 8.25.0 + '@typescript-eslint/types': 8.25.0 + '@typescript-eslint/typescript-estree': 8.25.0(typescript@5.8.2) + '@typescript-eslint/visitor-keys': 8.25.0 debug: 4.4.0 - eslint: 9.17.0(jiti@1.21.7) - typescript: 5.7.2 + eslint: 9.21.0(jiti@1.21.7) + typescript: 5.8.2 transitivePeerDependencies: - supports-color @@ -3714,39 +3751,39 @@ snapshots: '@typescript-eslint/types': 7.18.0 '@typescript-eslint/visitor-keys': 7.18.0 - '@typescript-eslint/scope-manager@8.18.1': + '@typescript-eslint/scope-manager@8.25.0': dependencies: - '@typescript-eslint/types': 8.18.1 - '@typescript-eslint/visitor-keys': 8.18.1 + '@typescript-eslint/types': 8.25.0 + '@typescript-eslint/visitor-keys': 8.25.0 - '@typescript-eslint/type-utils@7.18.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2)': + '@typescript-eslint/type-utils@7.18.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2)': dependencies: - '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.7.2) - '@typescript-eslint/utils': 7.18.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2) + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.8.2) + '@typescript-eslint/utils': 7.18.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2) debug: 4.4.0 - eslint: 9.17.0(jiti@1.21.7) - ts-api-utils: 1.4.3(typescript@5.7.2) + eslint: 9.21.0(jiti@1.21.7) + ts-api-utils: 1.4.3(typescript@5.8.2) optionalDependencies: - typescript: 5.7.2 + typescript: 5.8.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2)': + '@typescript-eslint/type-utils@8.25.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2)': dependencies: - '@typescript-eslint/typescript-estree': 8.18.1(typescript@5.7.2) - '@typescript-eslint/utils': 8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2) + '@typescript-eslint/typescript-estree': 8.25.0(typescript@5.8.2) + '@typescript-eslint/utils': 8.25.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2) debug: 4.4.0 - eslint: 9.17.0(jiti@1.21.7) - ts-api-utils: 1.4.3(typescript@5.7.2) - typescript: 5.7.2 + eslint: 9.21.0(jiti@1.21.7) + ts-api-utils: 2.0.1(typescript@5.8.2) + typescript: 5.8.2 transitivePeerDependencies: - supports-color '@typescript-eslint/types@7.18.0': {} - '@typescript-eslint/types@8.18.1': {} + '@typescript-eslint/types@8.25.0': {} - '@typescript-eslint/typescript-estree@7.18.0(typescript@5.7.2)': + '@typescript-eslint/typescript-estree@7.18.0(typescript@5.8.2)': dependencies: '@typescript-eslint/types': 7.18.0 '@typescript-eslint/visitor-keys': 7.18.0 @@ -3754,46 +3791,46 @@ snapshots: globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.5 - semver: 7.6.3 - ts-api-utils: 1.4.3(typescript@5.7.2) + semver: 7.7.1 + ts-api-utils: 1.4.3(typescript@5.8.2) optionalDependencies: - typescript: 5.7.2 + typescript: 5.8.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.18.1(typescript@5.7.2)': + '@typescript-eslint/typescript-estree@8.25.0(typescript@5.8.2)': dependencies: - '@typescript-eslint/types': 8.18.1 - '@typescript-eslint/visitor-keys': 8.18.1 + '@typescript-eslint/types': 8.25.0 + '@typescript-eslint/visitor-keys': 8.25.0 debug: 4.4.0 - fast-glob: 3.3.2 + fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.5 - semver: 7.6.3 - ts-api-utils: 1.4.3(typescript@5.7.2) - typescript: 5.7.2 + semver: 7.7.1 + ts-api-utils: 2.0.1(typescript@5.8.2) + typescript: 5.8.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@7.18.0(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2)': + '@typescript-eslint/utils@7.18.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2)': dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.17.0(jiti@1.21.7)) + '@eslint-community/eslint-utils': 4.4.1(eslint@9.21.0(jiti@1.21.7)) '@typescript-eslint/scope-manager': 7.18.0 '@typescript-eslint/types': 7.18.0 - '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.7.2) - eslint: 9.17.0(jiti@1.21.7) + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.8.2) + eslint: 9.21.0(jiti@1.21.7) transitivePeerDependencies: - supports-color - typescript - '@typescript-eslint/utils@8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2)': + '@typescript-eslint/utils@8.25.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2)': dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.17.0(jiti@1.21.7)) - '@typescript-eslint/scope-manager': 8.18.1 - '@typescript-eslint/types': 8.18.1 - '@typescript-eslint/typescript-estree': 8.18.1(typescript@5.7.2) - eslint: 9.17.0(jiti@1.21.7) - typescript: 5.7.2 + '@eslint-community/eslint-utils': 4.4.1(eslint@9.21.0(jiti@1.21.7)) + '@typescript-eslint/scope-manager': 8.25.0 + '@typescript-eslint/types': 8.25.0 + '@typescript-eslint/typescript-estree': 8.25.0(typescript@5.8.2) + eslint: 9.21.0(jiti@1.21.7) + typescript: 5.8.2 transitivePeerDependencies: - supports-color @@ -3802,26 +3839,26 @@ snapshots: '@typescript-eslint/types': 7.18.0 eslint-visitor-keys: 3.4.3 - '@typescript-eslint/visitor-keys@8.18.1': + '@typescript-eslint/visitor-keys@8.25.0': dependencies: - '@typescript-eslint/types': 8.18.1 + '@typescript-eslint/types': 8.25.0 eslint-visitor-keys: 4.2.0 - '@vitejs/plugin-react-swc@3.7.2(@swc/helpers@0.5.12)(vite@5.4.11(@types/node@20.17.10))': + '@vitejs/plugin-react-swc@3.8.0(@swc/helpers@0.5.12)(vite@5.4.14(@types/node@20.17.21))': dependencies: - '@swc/core': 1.10.1(@swc/helpers@0.5.12) - vite: 5.4.11(@types/node@20.17.10) + '@swc/core': 1.11.5(@swc/helpers@0.5.12) + vite: 5.4.14(@types/node@20.17.21) transitivePeerDependencies: - '@swc/helpers' - '@vitejs/plugin-react@4.3.4(vite@5.4.11(@types/node@20.17.10))': + '@vitejs/plugin-react@4.3.4(vite@5.4.14(@types/node@20.17.21))': dependencies: - '@babel/core': 7.26.0 - '@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.0) + '@babel/core': 7.26.9 + '@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.9) + '@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.9) '@types/babel__core': 7.20.5 react-refresh: 0.14.2 - vite: 5.4.11(@types/node@20.17.10) + vite: 5.4.14(@types/node@20.17.21) transitivePeerDependencies: - supports-color @@ -3861,18 +3898,18 @@ snapshots: aria-query@5.3.2: {} - array-buffer-byte-length@1.0.1: + array-buffer-byte-length@1.0.2: dependencies: - call-bind: 1.0.8 + call-bound: 1.0.3 is-array-buffer: 3.0.5 array-includes@3.1.8: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.6 - es-object-atoms: 1.0.0 - get-intrinsic: 1.2.6 + es-abstract: 1.23.9 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 is-string: 1.1.1 array-union@2.1.0: {} @@ -3881,67 +3918,69 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.6 + es-abstract: 1.23.9 es-errors: 1.3.0 - es-object-atoms: 1.0.0 - es-shim-unscopables: 1.0.2 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 array.prototype.findlastindex@1.2.5: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.6 + es-abstract: 1.23.9 es-errors: 1.3.0 - es-object-atoms: 1.0.0 - es-shim-unscopables: 1.0.2 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 array.prototype.flat@1.3.3: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.6 - es-shim-unscopables: 1.0.2 + es-abstract: 1.23.9 + es-shim-unscopables: 1.1.0 array.prototype.flatmap@1.3.3: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.6 - es-shim-unscopables: 1.0.2 + es-abstract: 1.23.9 + es-shim-unscopables: 1.1.0 array.prototype.tosorted@1.1.4: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.6 + es-abstract: 1.23.9 es-errors: 1.3.0 - es-shim-unscopables: 1.0.2 + es-shim-unscopables: 1.1.0 arraybuffer.prototype.slice@1.0.4: dependencies: - array-buffer-byte-length: 1.0.1 + array-buffer-byte-length: 1.0.2 call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.6 + es-abstract: 1.23.9 es-errors: 1.3.0 - get-intrinsic: 1.2.6 + get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 ast-types-flow@0.0.8: {} - autoprefixer@10.4.20(postcss@8.4.49): + async-function@1.0.0: {} + + autoprefixer@10.4.20(postcss@8.5.3): dependencies: - browserslist: 4.24.3 - caniuse-lite: 1.0.30001689 + browserslist: 4.24.4 + caniuse-lite: 1.0.30001701 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 - postcss: 8.4.49 + postcss: 8.5.3 postcss-value-parser: 4.2.0 available-typed-arrays@1.0.7: dependencies: - possible-typed-array-names: 1.0.0 + possible-typed-array-names: 1.1.0 axe-core@4.10.2: {} @@ -3964,16 +4003,16 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.24.3: + browserslist@4.24.4: dependencies: - caniuse-lite: 1.0.30001689 - electron-to-chromium: 1.5.74 + caniuse-lite: 1.0.30001701 + electron-to-chromium: 1.5.109 node-releases: 2.0.19 - update-browserslist-db: 1.1.1(browserslist@4.24.3) + update-browserslist-db: 1.1.3(browserslist@4.24.4) - bundle-require@5.0.0(esbuild@0.24.0): + bundle-require@5.1.0(esbuild@0.25.0): dependencies: - esbuild: 0.24.0 + esbuild: 0.25.0 load-tsconfig: 0.2.5 busboy@1.6.0: @@ -3982,28 +4021,28 @@ snapshots: cac@6.7.14: {} - call-bind-apply-helpers@1.0.1: + call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 function-bind: 1.1.2 call-bind@1.0.8: dependencies: - call-bind-apply-helpers: 1.0.1 + call-bind-apply-helpers: 1.0.2 es-define-property: 1.0.1 - get-intrinsic: 1.2.6 + get-intrinsic: 1.3.0 set-function-length: 1.2.2 call-bound@1.0.3: dependencies: - call-bind-apply-helpers: 1.0.1 - get-intrinsic: 1.2.6 + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 callsites@3.1.0: {} camelcase-css@2.0.1: {} - caniuse-lite@1.0.30001689: {} + caniuse-lite@1.0.30001701: {} chalk@4.1.2: dependencies: @@ -4028,9 +4067,9 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - chokidar@4.0.2: + chokidar@4.0.3: dependencies: - readdirp: 4.0.2 + readdirp: 4.1.2 client-only@0.0.1: {} @@ -4057,14 +4096,14 @@ snapshots: chalk: 4.1.2 date-fns: 2.30.0 lodash: 4.17.21 - rxjs: 7.8.1 + rxjs: 7.8.2 shell-quote: 1.8.2 spawn-command: 0.0.2 supports-color: 8.1.1 tree-kill: 1.2.2 yargs: 17.7.2 - consola@3.2.3: {} + consola@3.4.0: {} convert-source-map@2.0.0: {} @@ -4084,27 +4123,27 @@ snapshots: damerau-levenshtein@1.0.8: {} - data-view-buffer@1.0.1: + data-view-buffer@1.0.2: dependencies: - call-bind: 1.0.8 + call-bound: 1.0.3 es-errors: 1.3.0 is-data-view: 1.0.2 - data-view-byte-length@1.0.1: + data-view-byte-length@1.0.2: dependencies: - call-bind: 1.0.8 + call-bound: 1.0.3 es-errors: 1.3.0 is-data-view: 1.0.2 - data-view-byte-offset@1.0.0: + data-view-byte-offset@1.0.1: dependencies: - call-bind: 1.0.8 + call-bound: 1.0.3 es-errors: 1.3.0 is-data-view: 1.0.2 date-fns@2.30.0: dependencies: - '@babel/runtime': 7.26.0 + '@babel/runtime': 7.26.9 debug@3.2.7: dependencies: @@ -4144,40 +4183,41 @@ snapshots: dunder-proto@1.0.1: dependencies: - call-bind-apply-helpers: 1.0.1 + call-bind-apply-helpers: 1.0.2 es-errors: 1.3.0 gopd: 1.2.0 eastasianwidth@0.2.0: {} - electron-to-chromium@1.5.74: {} + electron-to-chromium@1.5.109: {} emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} - enhanced-resolve@5.17.1: + enhanced-resolve@5.18.1: dependencies: graceful-fs: 4.2.11 tapable: 2.2.1 - es-abstract@1.23.6: + es-abstract@1.23.9: dependencies: - array-buffer-byte-length: 1.0.1 + array-buffer-byte-length: 1.0.2 arraybuffer.prototype.slice: 1.0.4 available-typed-arrays: 1.0.7 call-bind: 1.0.8 call-bound: 1.0.3 - data-view-buffer: 1.0.1 - data-view-byte-length: 1.0.1 - data-view-byte-offset: 1.0.0 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 es-define-property: 1.0.1 es-errors: 1.3.0 - es-object-atoms: 1.0.0 - es-set-tostringtag: 2.0.3 + es-object-atoms: 1.1.1 + es-set-tostringtag: 2.1.0 es-to-primitive: 1.3.0 - function.prototype.name: 1.1.7 - get-intrinsic: 1.2.6 + function.prototype.name: 1.1.8 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 get-symbol-description: 1.1.0 globalthis: 1.0.4 gopd: 1.2.0 @@ -4189,25 +4229,27 @@ snapshots: is-array-buffer: 3.0.5 is-callable: 1.2.7 is-data-view: 1.0.2 - is-negative-zero: 2.0.3 is-regex: 1.2.1 is-shared-array-buffer: 1.0.4 is-string: 1.1.1 is-typed-array: 1.1.15 - is-weakref: 1.1.0 - math-intrinsics: 1.0.0 - object-inspect: 1.13.3 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 object-keys: 1.1.1 - object.assign: 4.1.6 - regexp.prototype.flags: 1.5.3 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 safe-array-concat: 1.1.3 + safe-push-apply: 1.0.0 safe-regex-test: 1.1.0 + set-proto: 1.0.0 string.prototype.trim: 1.2.10 string.prototype.trimend: 1.0.9 string.prototype.trimstart: 1.0.8 typed-array-buffer: 1.0.3 typed-array-byte-length: 1.0.3 - typed-array-byte-offset: 1.0.3 + typed-array-byte-offset: 1.0.4 typed-array-length: 1.0.7 unbox-primitive: 1.1.0 which-typed-array: 1.1.18 @@ -4216,35 +4258,37 @@ snapshots: es-errors@1.3.0: {} - es-iterator-helpers@1.2.0: + es-iterator-helpers@1.2.1: dependencies: call-bind: 1.0.8 + call-bound: 1.0.3 define-properties: 1.2.1 - es-abstract: 1.23.6 + es-abstract: 1.23.9 es-errors: 1.3.0 - es-set-tostringtag: 2.0.3 + es-set-tostringtag: 2.1.0 function-bind: 1.1.2 - get-intrinsic: 1.2.6 + get-intrinsic: 1.3.0 globalthis: 1.0.4 gopd: 1.2.0 has-property-descriptors: 1.0.2 has-proto: 1.2.0 has-symbols: 1.1.0 internal-slot: 1.1.0 - iterator.prototype: 1.1.4 + iterator.prototype: 1.1.5 safe-array-concat: 1.1.3 - es-object-atoms@1.0.0: + es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 - es-set-tostringtag@2.0.3: + es-set-tostringtag@2.1.0: dependencies: - get-intrinsic: 1.2.6 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 has-tostringtag: 1.0.2 hasown: 2.0.2 - es-shim-unscopables@1.0.2: + es-shim-unscopables@1.1.0: dependencies: hasown: 2.0.2 @@ -4307,102 +4351,103 @@ snapshots: '@esbuild/win32-ia32': 0.23.1 '@esbuild/win32-x64': 0.23.1 - esbuild@0.24.0: + esbuild@0.25.0: optionalDependencies: - '@esbuild/aix-ppc64': 0.24.0 - '@esbuild/android-arm': 0.24.0 - '@esbuild/android-arm64': 0.24.0 - '@esbuild/android-x64': 0.24.0 - '@esbuild/darwin-arm64': 0.24.0 - '@esbuild/darwin-x64': 0.24.0 - '@esbuild/freebsd-arm64': 0.24.0 - '@esbuild/freebsd-x64': 0.24.0 - '@esbuild/linux-arm': 0.24.0 - '@esbuild/linux-arm64': 0.24.0 - '@esbuild/linux-ia32': 0.24.0 - '@esbuild/linux-loong64': 0.24.0 - '@esbuild/linux-mips64el': 0.24.0 - '@esbuild/linux-ppc64': 0.24.0 - '@esbuild/linux-riscv64': 0.24.0 - '@esbuild/linux-s390x': 0.24.0 - '@esbuild/linux-x64': 0.24.0 - '@esbuild/netbsd-x64': 0.24.0 - '@esbuild/openbsd-arm64': 0.24.0 - '@esbuild/openbsd-x64': 0.24.0 - '@esbuild/sunos-x64': 0.24.0 - '@esbuild/win32-arm64': 0.24.0 - '@esbuild/win32-ia32': 0.24.0 - '@esbuild/win32-x64': 0.24.0 + '@esbuild/aix-ppc64': 0.25.0 + '@esbuild/android-arm': 0.25.0 + '@esbuild/android-arm64': 0.25.0 + '@esbuild/android-x64': 0.25.0 + '@esbuild/darwin-arm64': 0.25.0 + '@esbuild/darwin-x64': 0.25.0 + '@esbuild/freebsd-arm64': 0.25.0 + '@esbuild/freebsd-x64': 0.25.0 + '@esbuild/linux-arm': 0.25.0 + '@esbuild/linux-arm64': 0.25.0 + '@esbuild/linux-ia32': 0.25.0 + '@esbuild/linux-loong64': 0.25.0 + '@esbuild/linux-mips64el': 0.25.0 + '@esbuild/linux-ppc64': 0.25.0 + '@esbuild/linux-riscv64': 0.25.0 + '@esbuild/linux-s390x': 0.25.0 + '@esbuild/linux-x64': 0.25.0 + '@esbuild/netbsd-arm64': 0.25.0 + '@esbuild/netbsd-x64': 0.25.0 + '@esbuild/openbsd-arm64': 0.25.0 + '@esbuild/openbsd-x64': 0.25.0 + '@esbuild/sunos-x64': 0.25.0 + '@esbuild/win32-arm64': 0.25.0 + '@esbuild/win32-ia32': 0.25.0 + '@esbuild/win32-x64': 0.25.0 escalade@3.2.0: {} escape-string-regexp@4.0.0: {} - eslint-config-next@14.2.20(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2): + eslint-config-next@14.2.24(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2): dependencies: - '@next/eslint-plugin-next': 14.2.20 - '@rushstack/eslint-patch': 1.10.4 - '@typescript-eslint/eslint-plugin': 8.18.1(@typescript-eslint/parser@8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2) - '@typescript-eslint/parser': 8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2) - eslint: 9.17.0(jiti@1.21.7) + '@next/eslint-plugin-next': 14.2.24 + '@rushstack/eslint-patch': 1.10.5 + '@typescript-eslint/eslint-plugin': 8.25.0(@typescript-eslint/parser@8.25.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2))(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2) + '@typescript-eslint/parser': 8.25.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2) + eslint: 9.21.0(jiti@1.21.7) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@9.17.0(jiti@1.21.7)) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint-import-resolver-typescript@3.7.0)(eslint@9.17.0(jiti@1.21.7)) - eslint-plugin-jsx-a11y: 6.10.2(eslint@9.17.0(jiti@1.21.7)) - eslint-plugin-react: 7.35.0(eslint@9.17.0(jiti@1.21.7)) - eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@9.17.0(jiti@1.21.7)) + eslint-import-resolver-typescript: 3.8.3(eslint-plugin-import@2.31.0)(eslint@9.21.0(jiti@1.21.7)) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.25.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2))(eslint-import-resolver-typescript@3.8.3)(eslint@9.21.0(jiti@1.21.7)) + eslint-plugin-jsx-a11y: 6.10.2(eslint@9.21.0(jiti@1.21.7)) + eslint-plugin-react: 7.35.0(eslint@9.21.0(jiti@1.21.7)) + eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@9.21.0(jiti@1.21.7)) optionalDependencies: - typescript: 5.7.2 + typescript: 5.8.2 transitivePeerDependencies: - eslint-import-resolver-webpack - eslint-plugin-import-x - supports-color - eslint-config-prettier@9.1.0(eslint@9.17.0(jiti@1.21.7)): + eslint-config-prettier@9.1.0(eslint@9.21.0(jiti@1.21.7)): dependencies: - eslint: 9.17.0(jiti@1.21.7) + eslint: 9.21.0(jiti@1.21.7) - eslint-config-turbo@2.3.3(eslint@9.17.0(jiti@1.21.7)): + eslint-config-turbo@2.4.4(eslint@9.21.0(jiti@1.21.7))(turbo@2.4.4): dependencies: - eslint: 9.17.0(jiti@1.21.7) - eslint-plugin-turbo: 2.3.3(eslint@9.17.0(jiti@1.21.7)) + eslint: 9.21.0(jiti@1.21.7) + eslint-plugin-turbo: 2.4.4(eslint@9.21.0(jiti@1.21.7))(turbo@2.4.4) + turbo: 2.4.4 eslint-import-resolver-node@0.3.9: dependencies: debug: 3.2.7 - is-core-module: 2.16.0 - resolve: 1.22.9 + is-core-module: 2.16.1 + resolve: 1.22.10 transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0)(eslint@9.17.0(jiti@1.21.7)): + eslint-import-resolver-typescript@3.8.3(eslint-plugin-import@2.31.0)(eslint@9.21.0(jiti@1.21.7)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.0 - enhanced-resolve: 5.17.1 - eslint: 9.17.0(jiti@1.21.7) - fast-glob: 3.3.2 - get-tsconfig: 4.8.1 + enhanced-resolve: 5.18.1 + eslint: 9.21.0(jiti@1.21.7) + get-tsconfig: 4.10.0 is-bun-module: 1.3.0 - is-glob: 4.0.3 stable-hash: 0.0.4 + tinyglobby: 0.2.12 optionalDependencies: - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint-import-resolver-typescript@3.7.0)(eslint@9.17.0(jiti@1.21.7)) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.25.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2))(eslint-import-resolver-typescript@3.8.3)(eslint@9.21.0(jiti@1.21.7)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@9.17.0(jiti@1.21.7)): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.25.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.8.3)(eslint@9.21.0(jiti@1.21.7)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2) - eslint: 9.17.0(jiti@1.21.7) + '@typescript-eslint/parser': 8.25.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2) + eslint: 9.21.0(jiti@1.21.7) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@9.17.0(jiti@1.21.7)) + eslint-import-resolver-typescript: 3.8.3(eslint-plugin-import@2.31.0)(eslint@9.21.0(jiti@1.21.7)) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint-import-resolver-typescript@3.7.0)(eslint@9.17.0(jiti@1.21.7)): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.25.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2))(eslint-import-resolver-typescript@3.8.3)(eslint@9.21.0(jiti@1.21.7)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.8 @@ -4411,27 +4456,27 @@ snapshots: array.prototype.flatmap: 1.3.3 debug: 3.2.7 doctrine: 2.1.0 - eslint: 9.17.0(jiti@1.21.7) + eslint: 9.21.0(jiti@1.21.7) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@9.17.0(jiti@1.21.7)) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.25.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.8.3)(eslint@9.21.0(jiti@1.21.7)) hasown: 2.0.2 - is-core-module: 2.16.0 + is-core-module: 2.16.1 is-glob: 4.0.3 minimatch: 3.1.2 object.fromentries: 2.0.8 object.groupby: 1.0.3 - object.values: 1.2.0 + object.values: 1.2.1 semver: 6.3.1 string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2) + '@typescript-eslint/parser': 8.25.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-jsx-a11y@6.10.2(eslint@9.17.0(jiti@1.21.7)): + eslint-plugin-jsx-a11y@6.10.2(eslint@9.21.0(jiti@1.21.7)): dependencies: aria-query: 5.3.2 array-includes: 3.1.8 @@ -4441,7 +4486,7 @@ snapshots: axobject-query: 4.1.0 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 - eslint: 9.17.0(jiti@1.21.7) + eslint: 9.21.0(jiti@1.21.7) hasown: 2.0.2 jsx-ast-utils: 3.3.5 language-tags: 1.0.9 @@ -4450,44 +4495,45 @@ snapshots: safe-regex-test: 1.1.0 string.prototype.includes: 2.0.1 - eslint-plugin-react-hooks@4.6.2(eslint@9.17.0(jiti@1.21.7)): + eslint-plugin-react-hooks@4.6.2(eslint@9.21.0(jiti@1.21.7)): dependencies: - eslint: 9.17.0(jiti@1.21.7) + eslint: 9.21.0(jiti@1.21.7) - eslint-plugin-react-hooks@5.0.0-canary-7118f5dd7-20230705(eslint@9.17.0(jiti@1.21.7)): + eslint-plugin-react-hooks@5.0.0-canary-7118f5dd7-20230705(eslint@9.21.0(jiti@1.21.7)): dependencies: - eslint: 9.17.0(jiti@1.21.7) + eslint: 9.21.0(jiti@1.21.7) - eslint-plugin-react-refresh@0.4.16(eslint@9.17.0(jiti@1.21.7)): + eslint-plugin-react-refresh@0.4.19(eslint@9.21.0(jiti@1.21.7)): dependencies: - eslint: 9.17.0(jiti@1.21.7) + eslint: 9.21.0(jiti@1.21.7) - eslint-plugin-react@7.35.0(eslint@9.17.0(jiti@1.21.7)): + eslint-plugin-react@7.35.0(eslint@9.21.0(jiti@1.21.7)): dependencies: array-includes: 3.1.8 array.prototype.findlast: 1.2.5 array.prototype.flatmap: 1.3.3 array.prototype.tosorted: 1.1.4 doctrine: 2.1.0 - es-iterator-helpers: 1.2.0 - eslint: 9.17.0(jiti@1.21.7) + es-iterator-helpers: 1.2.1 + eslint: 9.21.0(jiti@1.21.7) estraverse: 5.3.0 hasown: 2.0.2 jsx-ast-utils: 3.3.5 minimatch: 3.1.2 object.entries: 1.1.8 object.fromentries: 2.0.8 - object.values: 1.2.0 + object.values: 1.2.1 prop-types: 15.8.1 resolve: 2.0.0-next.5 semver: 6.3.1 - string.prototype.matchall: 4.0.11 + string.prototype.matchall: 4.0.12 string.prototype.repeat: 1.0.0 - eslint-plugin-turbo@2.3.3(eslint@9.17.0(jiti@1.21.7)): + eslint-plugin-turbo@2.4.4(eslint@9.21.0(jiti@1.21.7))(turbo@2.4.4): dependencies: dotenv: 16.0.3 - eslint: 9.17.0(jiti@1.21.7) + eslint: 9.21.0(jiti@1.21.7) + turbo: 2.4.4 eslint-scope@8.2.0: dependencies: @@ -4498,18 +4544,18 @@ snapshots: eslint-visitor-keys@4.2.0: {} - eslint@9.17.0(jiti@1.21.7): + eslint@9.21.0(jiti@1.21.7): dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.17.0(jiti@1.21.7)) + '@eslint-community/eslint-utils': 4.4.1(eslint@9.21.0(jiti@1.21.7)) '@eslint-community/regexpp': 4.12.1 - '@eslint/config-array': 0.19.1 - '@eslint/core': 0.9.1 - '@eslint/eslintrc': 3.2.0 - '@eslint/js': 9.17.0 - '@eslint/plugin-kit': 0.2.4 + '@eslint/config-array': 0.19.2 + '@eslint/core': 0.12.0 + '@eslint/eslintrc': 3.3.0 + '@eslint/js': 9.21.0 + '@eslint/plugin-kit': 0.2.7 '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 - '@humanwhocodes/retry': 0.4.1 + '@humanwhocodes/retry': 0.4.2 '@types/estree': 1.0.6 '@types/json-schema': 7.0.15 ajv: 6.12.6 @@ -4559,7 +4605,7 @@ snapshots: fast-deep-equal@3.1.3: {} - fast-glob@3.3.2: + fast-glob@3.3.3: dependencies: '@nodelib/fs.stat': 2.0.5 '@nodelib/fs.walk': 1.2.8 @@ -4571,15 +4617,15 @@ snapshots: fast-levenshtein@2.0.6: {} - fastq@1.17.1: + fastq@1.19.1: dependencies: - reusify: 1.0.4 + reusify: 1.1.0 fault@1.0.4: dependencies: format: 0.2.2 - fdir@6.4.2(picomatch@4.0.2): + fdir@6.4.3(picomatch@4.0.2): optionalDependencies: picomatch: 4.0.2 @@ -4598,16 +4644,16 @@ snapshots: flat-cache@4.0.1: dependencies: - flatted: 3.3.2 + flatted: 3.3.3 keyv: 4.5.4 - flatted@3.3.2: {} + flatted@3.3.3: {} - for-each@0.3.3: + for-each@0.3.5: dependencies: is-callable: 1.2.7 - foreground-child@3.3.0: + foreground-child@3.3.1: dependencies: cross-spawn: 7.0.6 signal-exit: 4.1.0 @@ -4621,9 +4667,10 @@ snapshots: function-bind@1.1.2: {} - function.prototype.name@1.1.7: + function.prototype.name@1.1.8: dependencies: call-bind: 1.0.8 + call-bound: 1.0.3 define-properties: 1.2.1 functions-have-names: 1.2.3 hasown: 2.0.2 @@ -4635,26 +4682,31 @@ snapshots: get-caller-file@2.0.5: {} - get-intrinsic@1.2.6: + get-intrinsic@1.3.0: dependencies: - call-bind-apply-helpers: 1.0.1 - dunder-proto: 1.0.1 + call-bind-apply-helpers: 1.0.2 es-define-property: 1.0.1 es-errors: 1.3.0 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 function-bind: 1.1.2 + get-proto: 1.0.1 gopd: 1.2.0 has-symbols: 1.1.0 hasown: 2.0.2 - math-intrinsics: 1.0.0 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 get-symbol-description@1.1.0: dependencies: call-bound: 1.0.3 es-errors: 1.3.0 - get-intrinsic: 1.2.6 + get-intrinsic: 1.3.0 - get-tsconfig@4.8.1: + get-tsconfig@4.10.0: dependencies: resolve-pkg-maps: 1.0.0 @@ -4668,7 +4720,7 @@ snapshots: glob@10.3.10: dependencies: - foreground-child: 3.3.0 + foreground-child: 3.3.1 jackspeak: 2.3.6 minimatch: 9.0.5 minipass: 7.1.2 @@ -4676,7 +4728,7 @@ snapshots: glob@10.4.5: dependencies: - foreground-child: 3.3.0 + foreground-child: 3.3.1 jackspeak: 3.4.3 minimatch: 9.0.5 minipass: 7.1.2 @@ -4696,7 +4748,7 @@ snapshots: dependencies: array-union: 2.1.0 dir-glob: 3.0.1 - fast-glob: 3.3.2 + fast-glob: 3.3.3 ignore: 5.3.2 merge2: 1.4.1 slash: 3.0.0 @@ -4745,7 +4797,7 @@ snapshots: ignore@5.3.2: {} - import-fresh@3.3.0: + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 resolve-from: 4.0.0 @@ -4769,11 +4821,15 @@ snapshots: dependencies: call-bind: 1.0.8 call-bound: 1.0.3 - get-intrinsic: 1.2.6 + get-intrinsic: 1.3.0 - is-async-function@2.0.0: + is-async-function@2.1.1: dependencies: + async-function: 1.0.0 + call-bound: 1.0.3 + get-proto: 1.0.1 has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 is-bigint@1.1.0: dependencies: @@ -4783,25 +4839,25 @@ snapshots: dependencies: binary-extensions: 2.3.0 - is-boolean-object@1.2.1: + is-boolean-object@1.2.2: dependencies: call-bound: 1.0.3 has-tostringtag: 1.0.2 is-bun-module@1.3.0: dependencies: - semver: 7.6.3 + semver: 7.7.1 is-callable@1.2.7: {} - is-core-module@2.16.0: + is-core-module@2.16.1: dependencies: hasown: 2.0.2 is-data-view@1.0.2: dependencies: call-bound: 1.0.3 - get-intrinsic: 1.2.6 + get-intrinsic: 1.3.0 is-typed-array: 1.1.15 is-date-object@1.1.0: @@ -4819,9 +4875,12 @@ snapshots: is-fullwidth-code-point@3.0.0: {} - is-generator-function@1.0.10: + is-generator-function@1.1.0: dependencies: + call-bound: 1.0.3 + get-proto: 1.0.1 has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 is-glob@4.0.3: dependencies: @@ -4831,8 +4890,6 @@ snapshots: is-map@2.0.3: {} - is-negative-zero@2.0.3: {} - is-number-object@1.1.1: dependencies: call-bound: 1.0.3 @@ -4870,26 +4927,26 @@ snapshots: is-weakmap@2.0.2: {} - is-weakref@1.1.0: + is-weakref@1.1.1: dependencies: call-bound: 1.0.3 is-weakset@2.0.4: dependencies: call-bound: 1.0.3 - get-intrinsic: 1.2.6 + get-intrinsic: 1.3.0 isarray@2.0.5: {} isexe@2.0.0: {} - iterator.prototype@1.1.4: + iterator.prototype@1.1.5: dependencies: define-data-property: 1.1.4 - es-object-atoms: 1.0.0 - get-intrinsic: 1.2.6 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 has-symbols: 1.1.0 - reflect.getprototypeof: 1.0.9 set-function-name: 2.0.2 jackspeak@2.3.6: @@ -4934,8 +4991,8 @@ snapshots: dependencies: array-includes: 3.1.8 array.prototype.flat: 1.3.3 - object.assign: 4.1.6 - object.values: 1.2.0 + object.assign: 4.1.7 + object.values: 1.2.1 keyv@4.5.4: dependencies: @@ -4985,7 +5042,7 @@ snapshots: dependencies: yallist: 3.1.1 - math-intrinsics@1.0.0: {} + math-intrinsics@1.1.0: {} merge2@1.4.1: {} @@ -5018,27 +5075,27 @@ snapshots: natural-compare@1.4.0: {} - next@14.2.20(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + next@14.2.24(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@next/env': 14.2.20 + '@next/env': 14.2.24 '@swc/helpers': 0.5.5 busboy: 1.6.0 - caniuse-lite: 1.0.30001689 + caniuse-lite: 1.0.30001701 graceful-fs: 4.2.11 postcss: 8.4.31 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) styled-jsx: 5.1.1(react@18.3.1) optionalDependencies: - '@next/swc-darwin-arm64': 14.2.20 - '@next/swc-darwin-x64': 14.2.20 - '@next/swc-linux-arm64-gnu': 14.2.20 - '@next/swc-linux-arm64-musl': 14.2.20 - '@next/swc-linux-x64-gnu': 14.2.20 - '@next/swc-linux-x64-musl': 14.2.20 - '@next/swc-win32-arm64-msvc': 14.2.20 - '@next/swc-win32-ia32-msvc': 14.2.20 - '@next/swc-win32-x64-msvc': 14.2.20 + '@next/swc-darwin-arm64': 14.2.24 + '@next/swc-darwin-x64': 14.2.24 + '@next/swc-linux-arm64-gnu': 14.2.24 + '@next/swc-linux-arm64-musl': 14.2.24 + '@next/swc-linux-x64-gnu': 14.2.24 + '@next/swc-linux-x64-musl': 14.2.24 + '@next/swc-win32-arm64-msvc': 14.2.24 + '@next/swc-win32-ia32-msvc': 14.2.24 + '@next/swc-win32-x64-msvc': 14.2.24 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros @@ -5053,15 +5110,16 @@ snapshots: object-hash@3.0.0: {} - object-inspect@1.13.3: {} + object-inspect@1.13.4: {} object-keys@1.1.1: {} - object.assign@4.1.6: + object.assign@4.1.7: dependencies: call-bind: 1.0.8 call-bound: 1.0.3 define-properties: 1.2.1 + es-object-atoms: 1.1.1 has-symbols: 1.1.0 object-keys: 1.1.1 @@ -5069,26 +5127,27 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 object.fromentries@2.0.8: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.6 - es-object-atoms: 1.0.0 + es-abstract: 1.23.9 + es-object-atoms: 1.1.1 object.groupby@1.0.3: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.6 + es-abstract: 1.23.9 - object.values@1.2.0: + object.values@1.2.1: dependencies: call-bind: 1.0.8 + call-bound: 1.0.3 define-properties: 1.2.1 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 optionator@0.9.4: dependencies: @@ -5099,6 +5158,12 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 @@ -5145,38 +5210,38 @@ snapshots: pirates@4.0.6: {} - possible-typed-array-names@1.0.0: {} + possible-typed-array-names@1.1.0: {} - postcss-import@15.1.0(postcss@8.4.49): + postcss-import@15.1.0(postcss@8.5.3): dependencies: - postcss: 8.4.49 + postcss: 8.5.3 postcss-value-parser: 4.2.0 read-cache: 1.0.0 - resolve: 1.22.9 + resolve: 1.22.10 - postcss-js@4.0.1(postcss@8.4.49): + postcss-js@4.0.1(postcss@8.5.3): dependencies: camelcase-css: 2.0.1 - postcss: 8.4.49 + postcss: 8.5.3 - postcss-load-config@4.0.2(postcss@8.4.49): + postcss-load-config@4.0.2(postcss@8.5.3): dependencies: lilconfig: 3.1.3 - yaml: 2.6.1 + yaml: 2.7.0 optionalDependencies: - postcss: 8.4.49 + postcss: 8.5.3 - postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.4.49)(yaml@2.6.1): + postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.3)(yaml@2.7.0): dependencies: lilconfig: 3.1.3 optionalDependencies: jiti: 1.21.7 - postcss: 8.4.49 - yaml: 2.6.1 + postcss: 8.5.3 + yaml: 2.7.0 - postcss-nested@6.2.0(postcss@8.4.49): + postcss-nested@6.2.0(postcss@8.5.3): dependencies: - postcss: 8.4.49 + postcss: 8.5.3 postcss-selector-parser: 6.1.2 postcss-selector-parser@6.1.2: @@ -5192,7 +5257,7 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - postcss@8.4.49: + postcss@8.5.3: dependencies: nanoid: 3.3.8 picocolors: 1.1.1 @@ -5200,7 +5265,7 @@ snapshots: prelude-ls@1.2.1: {} - prettier@3.4.2: {} + prettier@3.5.2: {} prismjs@1.27.0: {} @@ -5232,7 +5297,7 @@ snapshots: react-syntax-highlighter@15.6.1(react@18.3.1): dependencies: - '@babel/runtime': 7.26.0 + '@babel/runtime': 7.26.9 highlight.js: 10.7.3 highlightjs-vue: 1.0.0 lowlight: 1.20.0 @@ -5252,17 +5317,17 @@ snapshots: dependencies: picomatch: 2.3.1 - readdirp@4.0.2: {} + readdirp@4.1.2: {} - reflect.getprototypeof@1.0.9: + reflect.getprototypeof@1.0.10: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - dunder-proto: 1.0.1 - es-abstract: 1.23.6 + es-abstract: 1.23.9 es-errors: 1.3.0 - get-intrinsic: 1.2.6 - gopd: 1.2.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 which-builtin-type: 1.2.1 refractor@3.6.0: @@ -5273,11 +5338,13 @@ snapshots: regenerator-runtime@0.14.1: {} - regexp.prototype.flags@1.5.3: + regexp.prototype.flags@1.5.4: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 set-function-name: 2.0.2 require-directory@2.1.1: {} @@ -5288,50 +5355,50 @@ snapshots: resolve-pkg-maps@1.0.0: {} - resolve@1.22.9: + resolve@1.22.10: dependencies: - is-core-module: 2.16.0 + is-core-module: 2.16.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 resolve@2.0.0-next.5: dependencies: - is-core-module: 2.16.0 + is-core-module: 2.16.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - reusify@1.0.4: {} + reusify@1.1.0: {} - rollup@4.28.1: + rollup@4.34.8: dependencies: '@types/estree': 1.0.6 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.28.1 - '@rollup/rollup-android-arm64': 4.28.1 - '@rollup/rollup-darwin-arm64': 4.28.1 - '@rollup/rollup-darwin-x64': 4.28.1 - '@rollup/rollup-freebsd-arm64': 4.28.1 - '@rollup/rollup-freebsd-x64': 4.28.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.28.1 - '@rollup/rollup-linux-arm-musleabihf': 4.28.1 - '@rollup/rollup-linux-arm64-gnu': 4.28.1 - '@rollup/rollup-linux-arm64-musl': 4.28.1 - '@rollup/rollup-linux-loongarch64-gnu': 4.28.1 - '@rollup/rollup-linux-powerpc64le-gnu': 4.28.1 - '@rollup/rollup-linux-riscv64-gnu': 4.28.1 - '@rollup/rollup-linux-s390x-gnu': 4.28.1 - '@rollup/rollup-linux-x64-gnu': 4.28.1 - '@rollup/rollup-linux-x64-musl': 4.28.1 - '@rollup/rollup-win32-arm64-msvc': 4.28.1 - '@rollup/rollup-win32-ia32-msvc': 4.28.1 - '@rollup/rollup-win32-x64-msvc': 4.28.1 + '@rollup/rollup-android-arm-eabi': 4.34.8 + '@rollup/rollup-android-arm64': 4.34.8 + '@rollup/rollup-darwin-arm64': 4.34.8 + '@rollup/rollup-darwin-x64': 4.34.8 + '@rollup/rollup-freebsd-arm64': 4.34.8 + '@rollup/rollup-freebsd-x64': 4.34.8 + '@rollup/rollup-linux-arm-gnueabihf': 4.34.8 + '@rollup/rollup-linux-arm-musleabihf': 4.34.8 + '@rollup/rollup-linux-arm64-gnu': 4.34.8 + '@rollup/rollup-linux-arm64-musl': 4.34.8 + '@rollup/rollup-linux-loongarch64-gnu': 4.34.8 + '@rollup/rollup-linux-powerpc64le-gnu': 4.34.8 + '@rollup/rollup-linux-riscv64-gnu': 4.34.8 + '@rollup/rollup-linux-s390x-gnu': 4.34.8 + '@rollup/rollup-linux-x64-gnu': 4.34.8 + '@rollup/rollup-linux-x64-musl': 4.34.8 + '@rollup/rollup-win32-arm64-msvc': 4.34.8 + '@rollup/rollup-win32-ia32-msvc': 4.34.8 + '@rollup/rollup-win32-x64-msvc': 4.34.8 fsevents: 2.3.3 run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 - rxjs@7.8.1: + rxjs@7.8.2: dependencies: tslib: 2.8.1 @@ -5339,10 +5406,15 @@ snapshots: dependencies: call-bind: 1.0.8 call-bound: 1.0.3 - get-intrinsic: 1.2.6 + get-intrinsic: 1.3.0 has-symbols: 1.1.0 isarray: 2.0.5 + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + safe-regex-test@1.1.0: dependencies: call-bound: 1.0.3 @@ -5355,14 +5427,14 @@ snapshots: semver@6.3.1: {} - semver@7.6.3: {} + semver@7.7.1: {} set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 es-errors: 1.3.0 function-bind: 1.1.2 - get-intrinsic: 1.2.6 + get-intrinsic: 1.3.0 gopd: 1.2.0 has-property-descriptors: 1.0.2 @@ -5373,6 +5445,12 @@ snapshots: functions-have-names: 1.2.3 has-property-descriptors: 1.0.2 + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -5384,27 +5462,27 @@ snapshots: side-channel-list@1.0.0: dependencies: es-errors: 1.3.0 - object-inspect: 1.13.3 + object-inspect: 1.13.4 side-channel-map@1.0.1: dependencies: call-bound: 1.0.3 es-errors: 1.3.0 - get-intrinsic: 1.2.6 - object-inspect: 1.13.3 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 side-channel-weakmap@1.0.2: dependencies: call-bound: 1.0.3 es-errors: 1.3.0 - get-intrinsic: 1.2.6 - object-inspect: 1.13.3 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 side-channel-map: 1.0.1 side-channel@1.1.0: dependencies: es-errors: 1.3.0 - object-inspect: 1.13.3 + object-inspect: 1.13.4 side-channel-list: 1.0.0 side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 @@ -5443,27 +5521,28 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.6 + es-abstract: 1.23.9 - string.prototype.matchall@4.0.11: + string.prototype.matchall@4.0.12: dependencies: call-bind: 1.0.8 + call-bound: 1.0.3 define-properties: 1.2.1 - es-abstract: 1.23.6 + es-abstract: 1.23.9 es-errors: 1.3.0 - es-object-atoms: 1.0.0 - get-intrinsic: 1.2.6 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 gopd: 1.2.0 has-symbols: 1.1.0 internal-slot: 1.1.0 - regexp.prototype.flags: 1.5.3 + regexp.prototype.flags: 1.5.4 set-function-name: 2.0.2 side-channel: 1.1.0 string.prototype.repeat@1.0.0: dependencies: define-properties: 1.2.1 - es-abstract: 1.23.6 + es-abstract: 1.23.9 string.prototype.trim@1.2.10: dependencies: @@ -5471,8 +5550,8 @@ snapshots: call-bound: 1.0.3 define-data-property: 1.1.4 define-properties: 1.2.1 - es-abstract: 1.23.6 - es-object-atoms: 1.0.0 + es-abstract: 1.23.9 + es-object-atoms: 1.1.1 has-property-descriptors: 1.0.2 string.prototype.trimend@1.0.9: @@ -5480,13 +5559,13 @@ snapshots: call-bind: 1.0.8 call-bound: 1.0.3 define-properties: 1.2.1 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 string.prototype.trimstart@1.0.8: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 strip-ansi@6.0.1: dependencies: @@ -5532,7 +5611,7 @@ snapshots: chokidar: 3.6.0 didyoumean: 1.2.2 dlv: 1.1.3 - fast-glob: 3.3.2 + fast-glob: 3.3.3 glob-parent: 6.0.2 is-glob: 4.0.3 jiti: 1.21.7 @@ -5541,13 +5620,13 @@ snapshots: normalize-path: 3.0.0 object-hash: 3.0.0 picocolors: 1.1.1 - postcss: 8.4.49 - postcss-import: 15.1.0(postcss@8.4.49) - postcss-js: 4.0.1(postcss@8.4.49) - postcss-load-config: 4.0.2(postcss@8.4.49) - postcss-nested: 6.2.0(postcss@8.4.49) + postcss: 8.5.3 + postcss-import: 15.1.0(postcss@8.5.3) + postcss-js: 4.0.1(postcss@8.5.3) + postcss-load-config: 4.0.2(postcss@8.5.3) + postcss-nested: 6.2.0(postcss@8.5.3) postcss-selector-parser: 6.1.2 - resolve: 1.22.9 + resolve: 1.22.10 sucrase: 3.35.0 transitivePeerDependencies: - ts-node @@ -5562,11 +5641,11 @@ snapshots: dependencies: any-promise: 1.3.0 - tinyexec@0.3.1: {} + tinyexec@0.3.2: {} - tinyglobby@0.2.10: + tinyglobby@0.2.12: dependencies: - fdir: 6.4.2(picomatch@4.0.2) + fdir: 6.4.3(picomatch@4.0.2) picomatch: 4.0.2 to-regex-range@5.0.1: @@ -5581,9 +5660,13 @@ snapshots: tree-kill@1.2.2: {} - ts-api-utils@1.4.3(typescript@5.7.2): + ts-api-utils@1.4.3(typescript@5.8.2): + dependencies: + typescript: 5.8.2 + + ts-api-utils@2.0.1(typescript@5.8.2): dependencies: - typescript: 5.7.2 + typescript: 5.8.2 ts-interface-checker@0.1.13: {} @@ -5596,60 +5679,60 @@ snapshots: tslib@2.8.1: {} - tsup@8.3.5(@swc/core@1.10.1(@swc/helpers@0.5.12))(jiti@1.21.7)(postcss@8.4.49)(typescript@5.7.2)(yaml@2.6.1): + tsup@8.4.0(@swc/core@1.11.5(@swc/helpers@0.5.12))(jiti@1.21.7)(postcss@8.5.3)(typescript@5.8.2)(yaml@2.7.0): dependencies: - bundle-require: 5.0.0(esbuild@0.24.0) + bundle-require: 5.1.0(esbuild@0.25.0) cac: 6.7.14 - chokidar: 4.0.2 - consola: 3.2.3 + chokidar: 4.0.3 + consola: 3.4.0 debug: 4.4.0 - esbuild: 0.24.0 + esbuild: 0.25.0 joycon: 3.1.1 picocolors: 1.1.1 - postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.4.49)(yaml@2.6.1) + postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.3)(yaml@2.7.0) resolve-from: 5.0.0 - rollup: 4.28.1 + rollup: 4.34.8 source-map: 0.8.0-beta.0 sucrase: 3.35.0 - tinyexec: 0.3.1 - tinyglobby: 0.2.10 + tinyexec: 0.3.2 + tinyglobby: 0.2.12 tree-kill: 1.2.2 optionalDependencies: - '@swc/core': 1.10.1(@swc/helpers@0.5.12) - postcss: 8.4.49 - typescript: 5.7.2 + '@swc/core': 1.11.5(@swc/helpers@0.5.12) + postcss: 8.5.3 + typescript: 5.8.2 transitivePeerDependencies: - jiti - supports-color - tsx - yaml - turbo-darwin-64@2.3.3: + turbo-darwin-64@2.4.4: optional: true - turbo-darwin-arm64@2.3.3: + turbo-darwin-arm64@2.4.4: optional: true - turbo-linux-64@2.3.3: + turbo-linux-64@2.4.4: optional: true - turbo-linux-arm64@2.3.3: + turbo-linux-arm64@2.4.4: optional: true - turbo-windows-64@2.3.3: + turbo-windows-64@2.4.4: optional: true - turbo-windows-arm64@2.3.3: + turbo-windows-arm64@2.4.4: optional: true - turbo@2.3.3: + turbo@2.4.4: optionalDependencies: - turbo-darwin-64: 2.3.3 - turbo-darwin-arm64: 2.3.3 - turbo-linux-64: 2.3.3 - turbo-linux-arm64: 2.3.3 - turbo-windows-64: 2.3.3 - turbo-windows-arm64: 2.3.3 + turbo-darwin-64: 2.4.4 + turbo-darwin-arm64: 2.4.4 + turbo-linux-64: 2.4.4 + turbo-linux-arm64: 2.4.4 + turbo-windows-64: 2.4.4 + turbo-windows-arm64: 2.4.4 type-check@0.4.0: dependencies: @@ -5664,31 +5747,31 @@ snapshots: typed-array-byte-length@1.0.3: dependencies: call-bind: 1.0.8 - for-each: 0.3.3 + for-each: 0.3.5 gopd: 1.2.0 has-proto: 1.2.0 is-typed-array: 1.1.15 - typed-array-byte-offset@1.0.3: + typed-array-byte-offset@1.0.4: dependencies: available-typed-arrays: 1.0.7 call-bind: 1.0.8 - for-each: 0.3.3 + for-each: 0.3.5 gopd: 1.2.0 has-proto: 1.2.0 is-typed-array: 1.1.15 - reflect.getprototypeof: 1.0.9 + reflect.getprototypeof: 1.0.10 typed-array-length@1.0.7: dependencies: call-bind: 1.0.8 - for-each: 0.3.3 + for-each: 0.3.5 gopd: 1.2.0 is-typed-array: 1.1.15 - possible-typed-array-names: 1.0.0 - reflect.getprototypeof: 1.0.9 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 - typescript@5.7.2: {} + typescript@5.8.2: {} unbox-primitive@1.1.0: dependencies: @@ -5699,9 +5782,9 @@ snapshots: undici-types@6.19.8: {} - update-browserslist-db@1.1.1(browserslist@4.24.3): + update-browserslist-db@1.1.3(browserslist@4.24.4): dependencies: - browserslist: 4.24.3 + browserslist: 4.24.4 escalade: 3.2.0 picocolors: 1.1.1 @@ -5711,19 +5794,19 @@ snapshots: util-deprecate@1.0.2: {} - vite-plugin-singlefile@2.1.0(rollup@4.28.1)(vite@5.4.11(@types/node@20.17.10)): + vite-plugin-singlefile@2.1.0(rollup@4.34.8)(vite@5.4.14(@types/node@20.17.21)): dependencies: micromatch: 4.0.8 - rollup: 4.28.1 - vite: 5.4.11(@types/node@20.17.10) + rollup: 4.34.8 + vite: 5.4.14(@types/node@20.17.21) - vite@5.4.11(@types/node@20.17.10): + vite@5.4.14(@types/node@20.17.21): dependencies: esbuild: 0.21.5 - postcss: 8.4.49 - rollup: 4.28.1 + postcss: 8.5.3 + rollup: 4.34.8 optionalDependencies: - '@types/node': 20.17.10 + '@types/node': 20.17.21 fsevents: 2.3.3 webidl-conversions@4.0.2: {} @@ -5737,7 +5820,7 @@ snapshots: which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0 - is-boolean-object: 1.2.1 + is-boolean-object: 1.2.2 is-number-object: 1.1.1 is-string: 1.1.1 is-symbol: 1.1.1 @@ -5745,14 +5828,14 @@ snapshots: which-builtin-type@1.2.1: dependencies: call-bound: 1.0.3 - function.prototype.name: 1.1.7 + function.prototype.name: 1.1.8 has-tostringtag: 1.0.2 - is-async-function: 2.0.0 + is-async-function: 2.1.1 is-date-object: 1.1.0 is-finalizationregistry: 1.1.1 - is-generator-function: 1.0.10 + is-generator-function: 1.1.0 is-regex: 1.2.1 - is-weakref: 1.1.0 + is-weakref: 1.1.1 isarray: 2.0.5 which-boxed-primitive: 1.1.1 which-collection: 1.0.2 @@ -5770,7 +5853,7 @@ snapshots: available-typed-arrays: 1.0.7 call-bind: 1.0.8 call-bound: 1.0.3 - for-each: 0.3.3 + for-each: 0.3.5 gopd: 1.2.0 has-tostringtag: 1.0.2 @@ -5798,7 +5881,7 @@ snapshots: yallist@3.1.1: {} - yaml@2.6.1: {} + yaml@2.7.0: {} yargs-parser@21.1.1: {} From 6b2441a6c7574ffee9c2724cd28320dbd1e95203 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Mon, 3 Mar 2025 11:57:35 -0300 Subject: [PATCH 037/168] Add variants properties --- packages/backend/src/code.ts | 14 ++++++++++++-- .../src/flutter/builderImpl/flutterAutoLayout.ts | 2 ++ .../backend/src/html/builderImpl/htmlAutoLayout.ts | 2 ++ packages/backend/src/html/htmlDefaultBuilder.ts | 8 ++++++++ .../src/tailwind/builderImpl/tailwindAutoLayout.ts | 2 ++ .../backend/src/tailwind/tailwindDefaultBuilder.ts | 9 +++++++++ 6 files changed, 35 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index 32589181..861c901d 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -52,8 +52,11 @@ const processNodeData = (node: any, optimizeLayout: boolean) => { ); }); - // Only fetch the Figma node if we have gradients or optimizeLayout is enabled - if (hasGradient || optimizeLayout) { + // Check if node is an instance to extract component metadata + const isInstance = node.type === "INSTANCE"; + + // Only fetch the Figma node if we have gradients, optimizeLayout is enabled, or it's an instance + if (hasGradient || optimizeLayout || isInstance) { try { const figmaNode = figma.getNodeById(node.id); if (figmaNode) { @@ -86,6 +89,13 @@ const processNodeData = (node: any, optimizeLayout: boolean) => { ); } + // Extract component metadata from instances + if (isInstance && figmaNode.type === "INSTANCE") { + if (figmaNode.variantProperties) { + node.variantProperties = figmaNode.variantProperties; + } + } + node.width = (figmaNode as any).width; node.height = (figmaNode as any).height; node.x = (figmaNode as any).x; diff --git a/packages/backend/src/flutter/builderImpl/flutterAutoLayout.ts b/packages/backend/src/flutter/builderImpl/flutterAutoLayout.ts index da72ae5e..c585008e 100644 --- a/packages/backend/src/flutter/builderImpl/flutterAutoLayout.ts +++ b/packages/backend/src/flutter/builderImpl/flutterAutoLayout.ts @@ -2,6 +2,7 @@ export const getMainAxisAlignment = ( node: InferredAutoLayoutResult, ): string => { switch (node.primaryAxisAlignItems) { + case undefined: case "MIN": return "MainAxisAlignment.start"; case "CENTER": @@ -17,6 +18,7 @@ export const getCrossAxisAlignment = ( node: InferredAutoLayoutResult, ): string => { switch (node.counterAxisAlignItems) { + case undefined: case "MIN": return "CrossAxisAlignment.start"; case "CENTER": diff --git a/packages/backend/src/html/builderImpl/htmlAutoLayout.ts b/packages/backend/src/html/builderImpl/htmlAutoLayout.ts index 5df954ef..8ffe58cf 100644 --- a/packages/backend/src/html/builderImpl/htmlAutoLayout.ts +++ b/packages/backend/src/html/builderImpl/htmlAutoLayout.ts @@ -6,6 +6,7 @@ const getFlexDirection = (node: InferredAutoLayoutResult): string => const getJustifyContent = (node: InferredAutoLayoutResult): string => { switch (node.primaryAxisAlignItems) { + case undefined: case "MIN": return "flex-start"; case "CENTER": @@ -19,6 +20,7 @@ const getJustifyContent = (node: InferredAutoLayoutResult): string => { const getAlignItems = (node: InferredAutoLayoutResult): string => { switch (node.counterAxisAlignItems) { + case undefined: case "MIN": return "flex-start"; case "CENTER": diff --git a/packages/backend/src/html/htmlDefaultBuilder.ts b/packages/backend/src/html/htmlDefaultBuilder.ts index 7a48b0bd..1e5eabe1 100644 --- a/packages/backend/src/html/htmlDefaultBuilder.ts +++ b/packages/backend/src/html/htmlDefaultBuilder.ts @@ -334,6 +334,14 @@ export class HtmlDefaultBuilder { ); } + if ("variantProperties" in this.node && this.node.variantProperties) { + Object.entries(this.node.variantProperties) + ?.map((prop) => { + this.addData(prop[0], prop[1]); + }) + .sort(); + } + const dataAttributes = this.data.join(""); const styleAttribute = formatStyleAttribute(this.styles, this.isJSX); diff --git a/packages/backend/src/tailwind/builderImpl/tailwindAutoLayout.ts b/packages/backend/src/tailwind/builderImpl/tailwindAutoLayout.ts index 19cfdfa0..84b0ca82 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindAutoLayout.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindAutoLayout.ts @@ -5,6 +5,7 @@ const getFlexDirection = (node: InferredAutoLayoutResult): string => const getJustifyContent = (node: InferredAutoLayoutResult): string => { switch (node.primaryAxisAlignItems) { + case undefined: case "MIN": return "justify-start"; case "CENTER": @@ -18,6 +19,7 @@ const getJustifyContent = (node: InferredAutoLayoutResult): string => { const getAlignItems = (node: InferredAutoLayoutResult): string => { switch (node.counterAxisAlignItems) { + case undefined: case "MIN": return "items-start"; case "CENTER": diff --git a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts index 93c7e8f1..9c7947d0 100644 --- a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts +++ b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts @@ -260,6 +260,15 @@ export class TailwindDefaultBuilder { this.addData("layer", this.name); } + if ("variantProperties" in this.node && this.node.variantProperties) { + console.log("entries", this.node.variantProperties); + Object.entries(this.node.variantProperties) + ?.map((prop) => { + this.addData(prop[0], prop[1]); + }) + .sort(); + } + const classLabel = getClassLabel(this.isJSX); const classNames = this.attributes.length > 0 From 5040affd8f89c49b5cef824851668c3f559e2ba6 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Mon, 3 Mar 2025 12:05:52 -0300 Subject: [PATCH 038/168] Add about --- apps/plugin/package.json | 1 + packages/plugin-ui/src/PluginUI.tsx | 142 +++++++++++--------- packages/plugin-ui/src/components/About.tsx | 67 +++++++++ packages/plugin-ui/tsconfig.json | 2 +- pnpm-lock.yaml | 12 ++ 5 files changed, 163 insertions(+), 61 deletions(-) create mode 100644 packages/plugin-ui/src/components/About.tsx diff --git a/apps/plugin/package.json b/apps/plugin/package.json index 4f69a1c1..45b6602c 100644 --- a/apps/plugin/package.json +++ b/apps/plugin/package.json @@ -12,6 +12,7 @@ "dependencies": { "@figma/plugin-typings": "^1.108.0", "backend": "workspace:*", + "lucide-react": "^0.477.0", "plugin-ui": "workspace:*", "react": "^18.3.1", "react-dom": "^18.3.1" diff --git a/packages/plugin-ui/src/PluginUI.tsx b/packages/plugin-ui/src/PluginUI.tsx index d3388757..1a6db986 100644 --- a/packages/plugin-ui/src/PluginUI.tsx +++ b/packages/plugin-ui/src/PluginUI.tsx @@ -4,6 +4,7 @@ import GradientsPanel from "./components/GradientsPanel"; import ColorsPanel from "./components/ColorsPanel"; import CodePanel from "./components/CodePanel"; import WarningIcon from "./components/WarningIcon"; +import About from "./components/About"; import { Framework, HTMLPreview, @@ -17,6 +18,7 @@ import { selectPreferenceOptions, } from "./codegenPreferenceOptions"; import Loading from "./components/Loading"; +import { useState } from "react"; type PluginUIProps = { code: string; @@ -37,6 +39,8 @@ type PluginUIProps = { const frameworks: Framework[] = ["HTML", "Tailwind", "Flutter", "SwiftUI"]; export const PluginUI = (props: PluginUIProps) => { + const [showAbout, setShowAbout] = useState(false); + if (props.isLoading) return ; const isEmpty = props.code === ""; @@ -45,22 +49,36 @@ export const PluginUI = (props: PluginUIProps) => { return (
-
- {frameworks.map((tab) => ( - - ))} +
+
+ {frameworks.map((tab) => ( + + ))} +
+
{ }} >
-
- {isEmpty === false && props.htmlPreview && ( - - )} - {warnings.length > 0 && ( -
-
-
- + {showAbout ? ( + + ) : ( +
+ {isEmpty === false && props.htmlPreview && ( + + )} + {warnings.length > 0 && ( +
+
+
+ +
+

Warnings:

-

Warnings:

+
    + {warnings.map((message: string, index) => ( +
  • + {message} +
  • + ))} +
-
    - {warnings.map((message: string) => ( -
  • - {message} -
  • - ))} -
-
- )} - - - {props.colors.length > 0 && ( - { - copy(value); - }} + )} + - )} - {props.gradients.length > 0 && ( - { - copy(value); - }} - /> - )} -
+ {props.colors.length > 0 && ( + { + copy(value); + }} + /> + )} + + {props.gradients.length > 0 && ( + { + copy(value); + }} + /> + )} +
+ )}
); diff --git a/packages/plugin-ui/src/components/About.tsx b/packages/plugin-ui/src/components/About.tsx new file mode 100644 index 00000000..6785bded --- /dev/null +++ b/packages/plugin-ui/src/components/About.tsx @@ -0,0 +1,67 @@ +import React from "react"; + +const About = () => { + return ( +
+
+

Figma to Code

+

+ Created by Bernardo Ferrari +

+
+ +
+

Privacy Policy

+

+ This plugin is completely private. It processes your design locally and + does not collect or transmit any of your data. +

+
+ +
+

Open Source

+

+ Figma to Code is an open-source project. You can view the source code + and contribute on GitHub. +

+ + View on GitHub + +
+ +
+

Contact

+

+ If you have any issues, feedback, or questions, please contact me: +

+ +
+
+ ); +}; + +export default About; diff --git a/packages/plugin-ui/tsconfig.json b/packages/plugin-ui/tsconfig.json index c1403a7e..e66b2206 100644 --- a/packages/plugin-ui/tsconfig.json +++ b/packages/plugin-ui/tsconfig.json @@ -1,5 +1,5 @@ { "extends": "tsconfig/react-library.json", - "include": ["src"], + "include": [".turbo/src"], "exclude": ["dist", "build", "node_modules"] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0dc7c386..b1c9b179 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -81,6 +81,9 @@ importers: backend: specifier: workspace:* version: link:../../packages/backend + lucide-react: + specifier: ^0.477.0 + version: 0.477.0(react@18.3.1) plugin-ui: specifier: workspace:* version: link:../../packages/plugin-ui @@ -2257,6 +2260,11 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lucide-react@0.477.0: + resolution: {integrity: sha512-yCf7aYxerFZAbd8jHJxjwe1j7jEMPptjnaOqdYeirFnEy85cNR3/L+o0I875CYFYya+eEVzZSbNuRk8BZPDpVw==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} @@ -5042,6 +5050,10 @@ snapshots: dependencies: yallist: 3.1.1 + lucide-react@0.477.0(react@18.3.1): + dependencies: + react: 18.3.1 + math-intrinsics@1.1.0: {} merge2@1.4.1: {} From be0c87ed6d8e059fd53943c1a79d0455c273cef2 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Mon, 3 Mar 2025 12:37:33 -0300 Subject: [PATCH 039/168] Add info --- apps/debug/package.json | 8 +- apps/plugin/package.json | 8 +- apps/plugin/ui-src/main.tsx | 4 +- packages/backend/package.json | 8 +- packages/plugin-ui/package.json | 7 +- packages/plugin-ui/src/PluginUI.tsx | 9 +- packages/plugin-ui/src/components/About.tsx | 218 ++++++++++++++---- .../plugin-ui/src/components/CodePanel.tsx | 1 + .../plugin-ui/src/components/ColorsPanel.tsx | 1 + .../plugin-ui/src/components/ExpandIcon.tsx | 2 + .../src/components/GradientsPanel.tsx | 1 + packages/plugin-ui/src/components/Loading.tsx | 2 + packages/plugin-ui/src/components/Preview.tsx | 1 + .../src/components/SelectableToggle.tsx | 2 + .../plugin-ui/src/components/WarningIcon.tsx | 19 -- packages/types/package.json | 4 +- pnpm-lock.yaml | 150 ++++++------ 17 files changed, 276 insertions(+), 169 deletions(-) delete mode 100644 packages/plugin-ui/src/components/WarningIcon.tsx diff --git a/apps/debug/package.json b/apps/debug/package.json index 1efead3a..366db3d7 100644 --- a/apps/debug/package.json +++ b/apps/debug/package.json @@ -13,13 +13,13 @@ "backend": "workspace:*", "next": "^14.2.24", "plugin-ui": "workspace:*", - "react": "^18.3.1", - "react-dom": "^18.3.1" + "react": "^19.0.0", + "react-dom": "^19.0.0" }, "devDependencies": { "@types/node": "^20.17.21", - "@types/react": "^18.3.18", - "@types/react-dom": "^18.3.5", + "@types/react": "^19.0.10", + "@types/react-dom": "^19.0.4", "autoprefixer": "^10.4.20", "eslint-config-custom": "workspace:*", "postcss": "^8.5.3", diff --git a/apps/plugin/package.json b/apps/plugin/package.json index 45b6602c..77e9f614 100644 --- a/apps/plugin/package.json +++ b/apps/plugin/package.json @@ -14,13 +14,13 @@ "backend": "workspace:*", "lucide-react": "^0.477.0", "plugin-ui": "workspace:*", - "react": "^18.3.1", - "react-dom": "^18.3.1" + "react": "^19.0.0", + "react-dom": "^19.0.0" }, "devDependencies": { "@types/node": "^20.17.21", - "@types/react": "^18.3.18", - "@types/react-dom": "^18.3.5", + "@types/react": "^19.0.10", + "@types/react-dom": "^19.0.4", "@typescript-eslint/eslint-plugin": "^7.18.0", "@typescript-eslint/parser": "^7.18.0", "@vitejs/plugin-react": "^4.3.4", diff --git a/apps/plugin/ui-src/main.tsx b/apps/plugin/ui-src/main.tsx index faff9775..2a882473 100644 --- a/apps/plugin/ui-src/main.tsx +++ b/apps/plugin/ui-src/main.tsx @@ -1,5 +1,5 @@ -import ReactDOM from "react-dom"; +import ReactDOM from "react-dom/client"; import App from "./App"; import "./index.css"; -ReactDOM.render(, document.getElementById("root")); +ReactDOM.createRoot(document.getElementById("root")!).render(); diff --git a/packages/backend/package.json b/packages/backend/package.json index baab8739..3bd44a16 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -15,13 +15,13 @@ "dependencies": { "@figma/plugin-typings": "^1.108.0", "js-base64": "^3.7.7", - "react": "18.3.1", - "react-dom": "18.3.1", + "react": "19.0.0", + "react-dom": "19.0.0", "types": "workspace:*" }, "devDependencies": { - "@types/react": "^18.3.18", - "@types/react-dom": "^18.3.5", + "@types/react": "^19.0.10", + "@types/react-dom": "^19.0.4", "eslint": "^9.21.0", "eslint-config-custom": "workspace:*", "tsconfig": "workspace:*", diff --git a/packages/plugin-ui/package.json b/packages/plugin-ui/package.json index 765ac372..a8253394 100644 --- a/packages/plugin-ui/package.json +++ b/packages/plugin-ui/package.json @@ -10,11 +10,12 @@ "lint": "eslint \"src/**/*.ts*\"" }, "dependencies": { - "@types/react": "^18.3.18", - "@types/react-dom": "^18.3.5", + "@types/react": "^19.0.10", + "@types/react-dom": "^19.0.4", "@types/react-syntax-highlighter": "15.5.13", "copy-to-clipboard": "^3.3.3", - "react": "^18.3.1", + "lucide-react": "^0.477.0", + "react": "^19.0.0", "react-syntax-highlighter": "^15.6.1", "tailwindcss": "3.4.6" }, diff --git a/packages/plugin-ui/src/PluginUI.tsx b/packages/plugin-ui/src/PluginUI.tsx index 1a6db986..71a45f46 100644 --- a/packages/plugin-ui/src/PluginUI.tsx +++ b/packages/plugin-ui/src/PluginUI.tsx @@ -3,7 +3,6 @@ import Preview from "./components/Preview"; import GradientsPanel from "./components/GradientsPanel"; import ColorsPanel from "./components/ColorsPanel"; import CodePanel from "./components/CodePanel"; -import WarningIcon from "./components/WarningIcon"; import About from "./components/About"; import { Framework, @@ -19,6 +18,8 @@ import { } from "./codegenPreferenceOptions"; import Loading from "./components/Loading"; import { useState } from "react"; +import { InfoIcon, TriangleAlertIcon } from "lucide-react"; +import React from "react"; type PluginUIProps = { code: string; @@ -69,7 +70,7 @@ export const PluginUI = (props: PluginUIProps) => { ))}
{
- +

Warnings:

diff --git a/packages/plugin-ui/src/components/About.tsx b/packages/plugin-ui/src/components/About.tsx index 6785bded..0de731de 100644 --- a/packages/plugin-ui/src/components/About.tsx +++ b/packages/plugin-ui/src/components/About.tsx @@ -1,67 +1,189 @@ import React from "react"; +import { + Code, + Github, + Heart, + Lock, + Mail, + MessageCircle, + Star, + Zap, +} from "lucide-react"; const About = () => { return ( -
-
-

Figma to Code

-

- Created by Bernardo Ferrari -

+
+ {/* Header Section with Logo and Title */} +
+
+ +
+

Figma to Code

+
+ Created with + + by Bernardo Ferrari +
+
-
-

Privacy Policy

-

- This plugin is completely private. It processes your design locally and - does not collect or transmit any of your data. -

-
+ {/* Cards Section */} +
+ {/* Privacy Policy Card */} +
+
+
+ +
+

Privacy Policy

+
+

+ This plugin is completely private. All of your design data is + processed locally in your browser and never leaves your computer. No + analytics, no data collection, no tracking. +

+
-
-

Open Source

-

- Figma to Code is an open-source project. You can view the source code - and contribute on GitHub. -

- - View on GitHub - -
+ {/* Open Source Card */} +
+
+
+ +
+

Open Source

+
+

+ Figma to Code is completely open-source. Contributions, bug reports, + and feature requests are welcome! +

+ + + View on GitHub + +
- + + {/* Contact Card */} + +
+ + {/* Footer */} +
+

+ © {new Date().getFullYear()} Bernardo Ferrari. All rights reserved. +

); }; +function XLogo() { + return ( + + + + ); +} + export default About; diff --git a/packages/plugin-ui/src/components/CodePanel.tsx b/packages/plugin-ui/src/components/CodePanel.tsx index 9d6eecc5..e0a4fa1f 100644 --- a/packages/plugin-ui/src/components/CodePanel.tsx +++ b/packages/plugin-ui/src/components/CodePanel.tsx @@ -9,6 +9,7 @@ import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; import { coldarkDark as theme } from "react-syntax-highlighter/dist/esm/styles/prism"; import copy from "copy-to-clipboard"; import SelectableToggle from "./SelectableToggle"; +import React from "react"; interface CodePanelProps { code: string; diff --git a/packages/plugin-ui/src/components/ColorsPanel.tsx b/packages/plugin-ui/src/components/ColorsPanel.tsx index ed6463b3..dfd19d32 100644 --- a/packages/plugin-ui/src/components/ColorsPanel.tsx +++ b/packages/plugin-ui/src/components/ColorsPanel.tsx @@ -1,3 +1,4 @@ +import React from "react"; import { useState } from "react"; import { SolidColorConversion } from "types"; diff --git a/packages/plugin-ui/src/components/ExpandIcon.tsx b/packages/plugin-ui/src/components/ExpandIcon.tsx index ae9f397d..0f7edc1a 100644 --- a/packages/plugin-ui/src/components/ExpandIcon.tsx +++ b/packages/plugin-ui/src/components/ExpandIcon.tsx @@ -1,3 +1,5 @@ +import React from "react"; + const ExpandIcon = (props: { size: number }) => ( (
void; isSelected?: boolean; diff --git a/packages/plugin-ui/src/components/WarningIcon.tsx b/packages/plugin-ui/src/components/WarningIcon.tsx deleted file mode 100644 index f6a5c1eb..00000000 --- a/packages/plugin-ui/src/components/WarningIcon.tsx +++ /dev/null @@ -1,19 +0,0 @@ -const WarningIcon = () => ( - - - - - -); -export default WarningIcon; diff --git a/packages/types/package.json b/packages/types/package.json index ccc54883..7bffd087 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -14,8 +14,8 @@ }, "dependencies": { "@figma/plugin-typings": "^1.108.0", - "@types/react": "^18.3.18", - "@types/react-dom": "^18.3.5", + "@types/react": "^19.0.10", + "@types/react-dom": "^19.0.4", "tsconfig": "workspace:*" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b1c9b179..7f9836b8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -31,26 +31,26 @@ importers: version: link:../../packages/backend next: specifier: ^14.2.24 - version: 14.2.24(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 14.2.24(react-dom@19.0.0(react@19.0.0))(react@19.0.0) plugin-ui: specifier: workspace:* version: link:../../packages/plugin-ui react: - specifier: ^18.3.1 - version: 18.3.1 + specifier: ^19.0.0 + version: 19.0.0 react-dom: - specifier: ^18.3.1 - version: 18.3.1(react@18.3.1) + specifier: ^19.0.0 + version: 19.0.0(react@19.0.0) devDependencies: '@types/node': specifier: ^20.17.21 version: 20.17.21 '@types/react': - specifier: ^18.3.18 - version: 18.3.18 + specifier: ^19.0.10 + version: 19.0.10 '@types/react-dom': - specifier: ^18.3.5 - version: 18.3.5(@types/react@18.3.18) + specifier: ^19.0.4 + version: 19.0.4(@types/react@19.0.10) autoprefixer: specifier: ^10.4.20 version: 10.4.20(postcss@8.5.3) @@ -83,26 +83,26 @@ importers: version: link:../../packages/backend lucide-react: specifier: ^0.477.0 - version: 0.477.0(react@18.3.1) + version: 0.477.0(react@19.0.0) plugin-ui: specifier: workspace:* version: link:../../packages/plugin-ui react: - specifier: ^18.3.1 - version: 18.3.1 + specifier: ^19.0.0 + version: 19.0.0 react-dom: - specifier: ^18.3.1 - version: 18.3.1(react@18.3.1) + specifier: ^19.0.0 + version: 19.0.0(react@19.0.0) devDependencies: '@types/node': specifier: ^20.17.21 version: 20.17.21 '@types/react': - specifier: ^18.3.18 - version: 18.3.18 + specifier: ^19.0.10 + version: 19.0.10 '@types/react-dom': - specifier: ^18.3.5 - version: 18.3.5(@types/react@18.3.18) + specifier: ^19.0.4 + version: 19.0.4(@types/react@19.0.10) '@typescript-eslint/eslint-plugin': specifier: ^7.18.0 version: 7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2))(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2) @@ -164,21 +164,21 @@ importers: specifier: ^3.7.7 version: 3.7.7 react: - specifier: 18.3.1 - version: 18.3.1 + specifier: 19.0.0 + version: 19.0.0 react-dom: - specifier: 18.3.1 - version: 18.3.1(react@18.3.1) + specifier: 19.0.0 + version: 19.0.0(react@19.0.0) types: specifier: workspace:* version: link:../types devDependencies: '@types/react': - specifier: ^18.3.18 - version: 18.3.18 + specifier: ^19.0.10 + version: 19.0.10 '@types/react-dom': - specifier: ^18.3.5 - version: 18.3.5(@types/react@18.3.18) + specifier: ^19.0.4 + version: 19.0.4(@types/react@19.0.10) eslint: specifier: ^9.21.0 version: 9.21.0(jiti@1.21.7) @@ -213,23 +213,26 @@ importers: packages/plugin-ui: dependencies: '@types/react': - specifier: ^18.3.18 - version: 18.3.18 + specifier: ^19.0.10 + version: 19.0.10 '@types/react-dom': - specifier: ^18.3.5 - version: 18.3.5(@types/react@18.3.18) + specifier: ^19.0.4 + version: 19.0.4(@types/react@19.0.10) '@types/react-syntax-highlighter': specifier: 15.5.13 version: 15.5.13 copy-to-clipboard: specifier: ^3.3.3 version: 3.3.3 + lucide-react: + specifier: ^0.477.0 + version: 0.477.0(react@19.0.0) react: - specifier: ^18.3.1 - version: 18.3.1 + specifier: ^19.0.0 + version: 19.0.0 react-syntax-highlighter: specifier: ^15.6.1 - version: 15.6.1(react@18.3.1) + version: 15.6.1(react@19.0.0) tailwindcss: specifier: 3.4.6 version: 3.4.6 @@ -258,11 +261,11 @@ importers: specifier: ^1.108.0 version: 1.108.0 '@types/react': - specifier: ^18.3.18 - version: 18.3.18 + specifier: ^19.0.10 + version: 19.0.10 '@types/react-dom': - specifier: ^18.3.5 - version: 18.3.5(@types/react@18.3.18) + specifier: ^19.0.4 + version: 19.0.4(@types/react@19.0.10) tsconfig: specifier: workspace:* version: link:../tsconfig @@ -1170,19 +1173,16 @@ packages: '@types/node@20.17.21': resolution: {integrity: sha512-yw1WZ94lZpdZbpnaF+WRvlN/Sx2EZWe/YZVdK4mC4u02/ql6Ozen8qbRJhOtltOxCg97/kpijhGs5X6STwkvbg==} - '@types/prop-types@15.7.14': - resolution: {integrity: sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==} - - '@types/react-dom@18.3.5': - resolution: {integrity: sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==} + '@types/react-dom@19.0.4': + resolution: {integrity: sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg==} peerDependencies: - '@types/react': ^18.0.0 + '@types/react': ^19.0.0 '@types/react-syntax-highlighter@15.5.13': resolution: {integrity: sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA==} - '@types/react@18.3.18': - resolution: {integrity: sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==} + '@types/react@19.0.10': + resolution: {integrity: sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==} '@types/unist@2.0.11': resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} @@ -2531,10 +2531,10 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - react-dom@18.3.1: - resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + react-dom@19.0.0: + resolution: {integrity: sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==} peerDependencies: - react: ^18.3.1 + react: ^19.0.0 react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -2548,8 +2548,8 @@ packages: peerDependencies: react: '>= 0.14.0' - react@18.3.1: - resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + react@19.0.0: + resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==} engines: {node: '>=0.10.0'} read-cache@1.0.0: @@ -2628,8 +2628,8 @@ packages: resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} engines: {node: '>= 0.4'} - scheduler@0.23.2: - resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + scheduler@0.25.0: + resolution: {integrity: sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==} semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} @@ -3677,19 +3677,16 @@ snapshots: dependencies: undici-types: 6.19.8 - '@types/prop-types@15.7.14': {} - - '@types/react-dom@18.3.5(@types/react@18.3.18)': + '@types/react-dom@19.0.4(@types/react@19.0.10)': dependencies: - '@types/react': 18.3.18 + '@types/react': 19.0.10 '@types/react-syntax-highlighter@15.5.13': dependencies: - '@types/react': 18.3.18 + '@types/react': 19.0.10 - '@types/react@18.3.18': + '@types/react@19.0.10': dependencies: - '@types/prop-types': 15.7.14 csstype: 3.1.3 '@types/unist@2.0.11': {} @@ -5050,9 +5047,9 @@ snapshots: dependencies: yallist: 3.1.1 - lucide-react@0.477.0(react@18.3.1): + lucide-react@0.477.0(react@19.0.0): dependencies: - react: 18.3.1 + react: 19.0.0 math-intrinsics@1.1.0: {} @@ -5087,7 +5084,7 @@ snapshots: natural-compare@1.4.0: {} - next@14.2.24(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + next@14.2.24(react-dom@19.0.0(react@19.0.0))(react@19.0.0): dependencies: '@next/env': 14.2.24 '@swc/helpers': 0.5.5 @@ -5095,9 +5092,9 @@ snapshots: caniuse-lite: 1.0.30001701 graceful-fs: 4.2.11 postcss: 8.4.31 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - styled-jsx: 5.1.1(react@18.3.1) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + styled-jsx: 5.1.1(react@19.0.0) optionalDependencies: '@next/swc-darwin-arm64': 14.2.24 '@next/swc-darwin-x64': 14.2.24 @@ -5297,29 +5294,26 @@ snapshots: queue-microtask@1.2.3: {} - react-dom@18.3.1(react@18.3.1): + react-dom@19.0.0(react@19.0.0): dependencies: - loose-envify: 1.4.0 - react: 18.3.1 - scheduler: 0.23.2 + react: 19.0.0 + scheduler: 0.25.0 react-is@16.13.1: {} react-refresh@0.14.2: {} - react-syntax-highlighter@15.6.1(react@18.3.1): + react-syntax-highlighter@15.6.1(react@19.0.0): dependencies: '@babel/runtime': 7.26.9 highlight.js: 10.7.3 highlightjs-vue: 1.0.0 lowlight: 1.20.0 prismjs: 1.29.0 - react: 18.3.1 + react: 19.0.0 refractor: 3.6.0 - react@18.3.1: - dependencies: - loose-envify: 1.4.0 + react@19.0.0: {} read-cache@1.0.0: dependencies: @@ -5433,9 +5427,7 @@ snapshots: es-errors: 1.3.0 is-regex: 1.2.1 - scheduler@0.23.2: - dependencies: - loose-envify: 1.4.0 + scheduler@0.25.0: {} semver@6.3.1: {} @@ -5591,10 +5583,10 @@ snapshots: strip-json-comments@3.1.1: {} - styled-jsx@5.1.1(react@18.3.1): + styled-jsx@5.1.1(react@19.0.0): dependencies: client-only: 0.0.1 - react: 18.3.1 + react: 19.0.0 sucrase@3.35.0: dependencies: From e1f863addc26f30c73c39517aaa5607780fc1ddf Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Mon, 3 Mar 2025 13:23:39 -0300 Subject: [PATCH 040/168] Improve loading --- packages/plugin-ui/src/components/Loading.tsx | 62 ++++++++++++++----- 1 file changed, 45 insertions(+), 17 deletions(-) diff --git a/packages/plugin-ui/src/components/Loading.tsx b/packages/plugin-ui/src/components/Loading.tsx index e9d27553..c1ec5f1b 100644 --- a/packages/plugin-ui/src/components/Loading.tsx +++ b/packages/plugin-ui/src/components/Loading.tsx @@ -1,26 +1,54 @@ import React from "react"; +import { Code } from "lucide-react"; interface LoadingProps {} + const Loading = (_props: LoadingProps) => ( -
-
-

- Converting... -

-

- This can take a while if the selection has many images or paths +

+
+ {/* Logo animation */} +
+
+
+ +
+ {/* Loading spinner */} + + + + +
+ + {/* Text */} +

+ Converting Design +

+

+ Please wait while your design is being converted to code. This may take a moment for complex designs.

+ + {/* Progress bar */} +
+
+
-
); + export default Loading; From f42ef6fe676baa9d55a687e1740c6d151489c928 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Mon, 3 Mar 2025 14:16:59 -0300 Subject: [PATCH 041/168] Improve preview and warning --- apps/plugin/package.json | 6 +- apps/plugin/tailwind.config.js | 6 +- .../src/common/commonFormatAttributes.ts | 4 +- .../src/common/lowercaseFirstLetter.ts | 7 + packages/plugin-ui/package.json | 5 +- packages/plugin-ui/src/PluginUI.tsx | 23 +- .../plugin-ui/src/components/CodePanel.tsx | 14 +- .../plugin-ui/src/components/CopyButton.tsx | 104 ++++++++ packages/plugin-ui/src/components/Preview.tsx | 181 +++++++++++--- .../src/components/SelectableToggle.tsx | 72 ++++-- .../src/components/WarningsPanel.tsx | 227 ++++++++++++++++++ packages/plugin-ui/src/lib/utils.ts | 6 + packages/plugin-ui/tailwind.config.js | 14 +- packages/plugin-ui/tsconfig.json | 2 +- pnpm-lock.yaml | 98 ++++++++ 15 files changed, 670 insertions(+), 99 deletions(-) create mode 100644 packages/backend/src/common/lowercaseFirstLetter.ts create mode 100644 packages/plugin-ui/src/components/CopyButton.tsx create mode 100644 packages/plugin-ui/src/components/WarningsPanel.tsx create mode 100644 packages/plugin-ui/src/lib/utils.ts diff --git a/apps/plugin/package.json b/apps/plugin/package.json index 77e9f614..641d287a 100644 --- a/apps/plugin/package.json +++ b/apps/plugin/package.json @@ -12,10 +12,14 @@ "dependencies": { "@figma/plugin-typings": "^1.108.0", "backend": "workspace:*", + "clsx": "^2.1.1", "lucide-react": "^0.477.0", + "motion": "^12.4.9", "plugin-ui": "workspace:*", "react": "^19.0.0", - "react-dom": "^19.0.0" + "react-dom": "^19.0.0", + "tailwind-merge": "^3.0.2", + "tailwindcss-animate": "^1.0.7" }, "devDependencies": { "@types/node": "^20.17.21", diff --git a/apps/plugin/tailwind.config.js b/apps/plugin/tailwind.config.js index 77687bd7..0c74c637 100644 --- a/apps/plugin/tailwind.config.js +++ b/apps/plugin/tailwind.config.js @@ -6,9 +6,5 @@ module.exports = { "../../packages/plugin-ui/**/*.{js,ts,jsx,tsx}", ], darkMode: "class", - theme: { - extend: {}, - }, - variants: {}, - plugins: [], + }; diff --git a/packages/backend/src/common/commonFormatAttributes.ts b/packages/backend/src/common/commonFormatAttributes.ts index d157b7ac..8adb6392 100644 --- a/packages/backend/src/common/commonFormatAttributes.ts +++ b/packages/backend/src/common/commonFormatAttributes.ts @@ -1,4 +1,4 @@ -import { stringToClassName as stringToClassName } from "./numToAutoFixed"; +import { lowercaseFirstLetter } from "./lowercaseFirstLetter"; export const getClassLabel = (isJSX: boolean = false) => isJSX ? "className" : "class"; @@ -18,7 +18,7 @@ export const formatStyleAttribute = ( }; export const formatDataAttribute = (label: string, value?: string) => - ` data-${label}${value === undefined ? `` : `="${value}"`}`; + ` data-${lowercaseFirstLetter(label)}${value === undefined ? `` : `="${value}"`}`; export const formatClassAttribute = ( classes: string[], diff --git a/packages/backend/src/common/lowercaseFirstLetter.ts b/packages/backend/src/common/lowercaseFirstLetter.ts new file mode 100644 index 00000000..b7215dc3 --- /dev/null +++ b/packages/backend/src/common/lowercaseFirstLetter.ts @@ -0,0 +1,7 @@ +export function lowercaseFirstLetter(str: string): string { + if (!str || str.length === 0) { + return str; + } + + return str.charAt(0).toLowerCase() + str.slice(1); +} diff --git a/packages/plugin-ui/package.json b/packages/plugin-ui/package.json index a8253394..7c61b749 100644 --- a/packages/plugin-ui/package.json +++ b/packages/plugin-ui/package.json @@ -13,11 +13,14 @@ "@types/react": "^19.0.10", "@types/react-dom": "^19.0.4", "@types/react-syntax-highlighter": "15.5.13", + "clsx": "^2.1.1", "copy-to-clipboard": "^3.3.3", "lucide-react": "^0.477.0", "react": "^19.0.0", "react-syntax-highlighter": "^15.6.1", - "tailwindcss": "3.4.6" + "tailwind-merge": "^3.0.2", + "tailwindcss": "3.4.6", + "tailwindcss-animate": "^1.0.7" }, "devDependencies": { "eslint": "^9.21.0", diff --git a/packages/plugin-ui/src/PluginUI.tsx b/packages/plugin-ui/src/PluginUI.tsx index 71a45f46..fede84a3 100644 --- a/packages/plugin-ui/src/PluginUI.tsx +++ b/packages/plugin-ui/src/PluginUI.tsx @@ -4,6 +4,7 @@ import GradientsPanel from "./components/GradientsPanel"; import ColorsPanel from "./components/ColorsPanel"; import CodePanel from "./components/CodePanel"; import About from "./components/About"; +import WarningsPanel from "./components/WarningsPanel"; import { Framework, HTMLPreview, @@ -18,7 +19,7 @@ import { } from "./codegenPreferenceOptions"; import Loading from "./components/Loading"; import { useState } from "react"; -import { InfoIcon, TriangleAlertIcon } from "lucide-react"; +import { InfoIcon } from "lucide-react"; import React from "react"; type PluginUIProps = { @@ -96,23 +97,9 @@ export const PluginUI = (props: PluginUIProps) => { {isEmpty === false && props.htmlPreview && ( )} - {warnings.length > 0 && ( -
-
-
- -
-

Warnings:

-
-
    - {warnings.map((message: string, index) => ( -
  • - {message} -
  • - ))} -
-
- )} + + {warnings.length > 0 && } + { Code

{isCodeEmpty === false && ( - + /> )}
diff --git a/packages/plugin-ui/src/components/CopyButton.tsx b/packages/plugin-ui/src/components/CopyButton.tsx new file mode 100644 index 00000000..7e2f0e9f --- /dev/null +++ b/packages/plugin-ui/src/components/CopyButton.tsx @@ -0,0 +1,104 @@ +"use client"; + +import { useState, useEffect } from "react"; +import { Copy, CheckCircle, Check } from "lucide-react"; +import copy from "copy-to-clipboard"; + +interface CopyButtonProps { + value: string; + className?: string; + showLabel?: boolean; + successDuration?: number; + onMouseEnter?: () => void; + onMouseLeave?: () => void; +} + +export function CopyButton({ + value, + className, + showLabel = true, + successDuration = 750, + onMouseEnter, + onMouseLeave, +}: CopyButtonProps) { + const [isCopied, setIsCopied] = useState(false); + + useEffect(() => { + if (isCopied) { + const timer = setTimeout(() => { + setIsCopied(false); + console.log("iscopied false"); + }, successDuration); + + return () => clearTimeout(timer); + } + }, [isCopied, successDuration]); + + const handleCopy = async () => { + try { + copy(value); + setIsCopied(true); + console.log("iscopied true"); + } catch (error) { + console.error("Failed to copy text: ", error); + } + }; + + return ( + + ); +} diff --git a/packages/plugin-ui/src/components/Preview.tsx b/packages/plugin-ui/src/components/Preview.tsx index 0b933235..b06bfb3c 100644 --- a/packages/plugin-ui/src/components/Preview.tsx +++ b/packages/plugin-ui/src/components/Preview.tsx @@ -1,52 +1,177 @@ -import React from "react"; +import React, { useState, useEffect } from "react"; import { HTMLPreview } from "types"; +import { + Maximize2, + Minimize2, + MonitorSmartphone, + Smartphone, +} from "lucide-react"; const Preview: React.FC<{ htmlPreview: HTMLPreview; }> = (props) => { - const targetWidth = 240; - const targetHeight = 120; - const scaleFactor = Math.min( - targetWidth / props.htmlPreview.size.width, - targetHeight / props.htmlPreview.size.height, - ); + const [expanded, setExpanded] = useState(false); + const [viewMode, setViewMode] = useState<"desktop" | "mobile">("desktop"); + const [animationClass, setAnimationClass] = useState(""); + + // Define consistent dimensions regardless of mode + const containerWidth = expanded ? 320 : 240; + const containerHeight = expanded ? 180 : 120; + + // Calculate content dimensions based on view mode + const contentWidth = + viewMode === "desktop" ? containerWidth : Math.floor(containerWidth * 0.4); // Narrower for mobile + + // Adjust scale factor based on view mode + const scaleFactor = + viewMode === "desktop" + ? Math.min( + containerWidth / props.htmlPreview.size.width, + containerHeight / props.htmlPreview.size.height, + ) + : Math.min( + contentWidth / props.htmlPreview.size.width, + containerHeight / props.htmlPreview.size.height, + ); + + // Add animation when changing view mode + useEffect(() => { + setAnimationClass( + viewMode === "desktop" + ? "animate-slide-in-left" + : "animate-slide-in-right", + ); + const timer = setTimeout(() => setAnimationClass(""), 300); // Remove animation class after it completes + return () => clearTimeout(timer); + }, [viewMode]); + + // Add animation when changing size + useEffect(() => { + const timer = setTimeout(() => setAnimationClass("animate-scale-in"), 50); + return () => { + clearTimeout(timer); + setAnimationClass(""); + }; + }, [expanded]); return ( -
-
- Responsive Preview +
+ {/* Header with view mode controls */} +
+

+ + Preview +

+
+ {/* View Mode Toggle */} +
+ + +
+ + {/* Expand/Collapse Button */} + +
-
+ + {/* Preview container */} +
+ {/* Outer container with fixed dimensions */}
+ {/* Inner content positioned based on view mode */}
+ {/* Device frame - just a border for mobile, no status bar or home indicator */}
+ className={`w-full h-full flex justify-center items-center overflow-hidden bg-white dark:bg-black ${ + viewMode === "desktop" + ? "border border-neutral-300 dark:border-neutral-600 rounded shadow-sm" + : "border-2 border-neutral-400 dark:border-neutral-500 rounded-xl shadow-sm" + } transition-all duration-300 ease-in-out`} + > + {/* Content - no padding needed anymore */} +
+
+
+
+ + {/* Footer with size info */} +
+ + {props.htmlPreview.size.width}×{props.htmlPreview.size.height}px + +
+ {viewMode === "mobile" ? ( + + + Mobile view + + ) : ( + + + Desktop view + + )} +
+
); }; + export default Preview; diff --git a/packages/plugin-ui/src/components/SelectableToggle.tsx b/packages/plugin-ui/src/components/SelectableToggle.tsx index 40a35754..c9cba2fd 100644 --- a/packages/plugin-ui/src/components/SelectableToggle.tsx +++ b/packages/plugin-ui/src/components/SelectableToggle.tsx @@ -1,4 +1,5 @@ -import React from "react"; +import React, { useState } from "react"; +import { Check, HelpCircle } from "lucide-react"; type SelectableToggleProps = { onSelect: (isSelected: boolean) => void; @@ -17,34 +18,61 @@ const SelectableToggle = ({ buttonClass, checkClass, }: SelectableToggleProps) => { + const [showTooltip, setShowTooltip] = useState(false); + const handleClick = () => { onSelect(!isSelected); }; return ( - + > +
+ {/* Checkbox circle with check mark for selected state */} +
+ {isSelected && ( + + )} +
+ + {title} + + {/* Help icon for description */} + {description && ( +
setShowTooltip(true)} + onMouseLeave={() => setShowTooltip(false)} + > + +
+ )} +
+ + + {/* Tooltip */} + {showTooltip && description && ( +
+ {description} +
+
+ )} +
); }; + export default SelectableToggle; diff --git a/packages/plugin-ui/src/components/WarningsPanel.tsx b/packages/plugin-ui/src/components/WarningsPanel.tsx new file mode 100644 index 00000000..5b7c4683 --- /dev/null +++ b/packages/plugin-ui/src/components/WarningsPanel.tsx @@ -0,0 +1,227 @@ +import React, { useState } from "react"; +import { + AlertTriangle, + ChevronDown, + ChevronUp, + XCircle, + AlertOctagon, + ExternalLink, + Info, +} from "lucide-react"; +import { Warning } from "types"; + +interface WarningsPanelProps { + warnings: Warning[]; +} + +// Helper function to categorize warnings by severity +const categorizeWarnings = (warnings: Warning[]) => { + const critical = warnings.filter( + (w) => + w.toString().toLowerCase().includes("error") || + w.toString().toLowerCase().includes("critical") || + w.toString().toLowerCase().includes("missing"), + ); + const standard = warnings.filter((w) => !critical.includes(w)); + + return { critical, standard }; +}; + +const WarningsPanel: React.FC = ({ warnings }) => { + const [isCollapsed, setIsCollapsed] = useState(false); + const [activeTab, setActiveTab] = useState<"all" | "critical" | "standard">( + "all", + ); + const { critical, standard } = categorizeWarnings(warnings); + + if (warnings.length === 0) return null; + + const displayedWarnings = + activeTab === "all" + ? warnings + : activeTab === "critical" + ? critical + : standard; + + return ( +
+ {/* Header - medium size */} +
setIsCollapsed(!isCollapsed)} + > +
+
+ +
+
+

+ {warnings.length} {warnings.length === 1 ? "Warning" : "Warnings"} +

+ {critical.length > 0 && ( + + {critical.length} critical + + )} +
+
+ +
+ + {/* Warning content - balanced size */} + {!isCollapsed && ( +
+ {/* Tabs - medium size */} + {critical.length > 0 && standard.length > 0 && ( +
+ + + +
+ )} + + {/* Warning list - balanced size */} +
+ {displayedWarnings.map((message, index) => { + const isCritical = critical.includes(message); + return ( +
+
+
+ {isCritical ? ( + + ) : ( + + )} +
+
+

+ {message.toString()} +

+ + {/* Suggested fix - balanced size */} + {isCritical && ( +
+ Tip: + {suggestFixForWarning(message.toString())} +
+ )} +
+ + {/* Action link - balanced size */} + {shouldShowActionButtons(message.toString()) && ( + + Info + + + )} +
+
+ ); + })} +
+ + {/* Help text - balanced size */} + {displayedWarnings.length > 0 && ( +
+ + + Addressing warnings can improve the quality of the generated + code. + +
+ )} +
+ )} +
+ ); +}; + +// Helper functions (these would be expanded with actual logic in your implementation) +const suggestFixForWarning = (warning: string): string => { + if (warning.toLowerCase().includes("missing")) { + return "Add the required properties to your component or select a parent element that includes all necessary children."; + } + if (warning.toLowerCase().includes("unsupported")) { + return "Consider using a different element type or simplifying the design for better conversion results."; + } + return "Check your design elements and ensure they follow the recommended structure for code conversion."; +}; + +const shouldShowActionButtons = (warning: string): boolean => { + // Example condition - you would customize this based on your specific warnings + return ( + warning.toLowerCase().includes("unsupported") || + warning.toLowerCase().includes("missing") + ); +}; + +const getDocsLinkForWarning = (warning: string): string => { + // Example URLs - in reality you would point to specific documentation pages + if (warning.toLowerCase().includes("unsupported")) { + return "https://github.com/bernaferrari/figma-to-code/wiki/Supported-Elements"; + } + return "https://github.com/bernaferrari/figma-to-code/wiki"; +}; + +export default WarningsPanel; diff --git a/packages/plugin-ui/src/lib/utils.ts b/packages/plugin-ui/src/lib/utils.ts new file mode 100644 index 00000000..365058ce --- /dev/null +++ b/packages/plugin-ui/src/lib/utils.ts @@ -0,0 +1,6 @@ +import { type ClassValue, clsx } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/packages/plugin-ui/tailwind.config.js b/packages/plugin-ui/tailwind.config.js index 4006c8d3..1d108713 100644 --- a/packages/plugin-ui/tailwind.config.js +++ b/packages/plugin-ui/tailwind.config.js @@ -1,13 +1,5 @@ +/** @type {import('tailwindcss').Config} */ module.exports = { - content: [ - "./pages/**/*.{js,ts,jsx,tsx}", - "./components/**/*.{js,ts,jsx,tsx}", - "../../packages/plugin-ui/**/*.{js,ts,jsx,tsx}", - ], - darkMode: "class", - theme: { - extend: {}, - }, - variants: {}, - plugins: [], + content: ['./src/**/*.{js,jsx,ts,tsx}'], + darkMode: 'media', }; diff --git a/packages/plugin-ui/tsconfig.json b/packages/plugin-ui/tsconfig.json index e66b2206..c1403a7e 100644 --- a/packages/plugin-ui/tsconfig.json +++ b/packages/plugin-ui/tsconfig.json @@ -1,5 +1,5 @@ { "extends": "tsconfig/react-library.json", - "include": [".turbo/src"], + "include": ["src"], "exclude": ["dist", "build", "node_modules"] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7f9836b8..68c0431f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -81,9 +81,15 @@ importers: backend: specifier: workspace:* version: link:../../packages/backend + clsx: + specifier: ^2.1.1 + version: 2.1.1 lucide-react: specifier: ^0.477.0 version: 0.477.0(react@19.0.0) + motion: + specifier: ^12.4.9 + version: 12.4.9(react-dom@19.0.0(react@19.0.0))(react@19.0.0) plugin-ui: specifier: workspace:* version: link:../../packages/plugin-ui @@ -93,6 +99,12 @@ importers: react-dom: specifier: ^19.0.0 version: 19.0.0(react@19.0.0) + tailwind-merge: + specifier: ^3.0.2 + version: 3.0.2 + tailwindcss-animate: + specifier: ^1.0.7 + version: 1.0.7(tailwindcss@3.4.6) devDependencies: '@types/node': specifier: ^20.17.21 @@ -221,6 +233,9 @@ importers: '@types/react-syntax-highlighter': specifier: 15.5.13 version: 15.5.13 + clsx: + specifier: ^2.1.1 + version: 2.1.1 copy-to-clipboard: specifier: ^3.3.3 version: 3.3.3 @@ -233,9 +248,15 @@ importers: react-syntax-highlighter: specifier: ^15.6.1 version: 15.6.1(react@19.0.0) + tailwind-merge: + specifier: ^3.0.2 + version: 3.0.2 tailwindcss: specifier: 3.4.6 version: 3.4.6 + tailwindcss-animate: + specifier: ^1.0.7 + version: 1.0.7(tailwindcss@3.4.6) devDependencies: eslint: specifier: ^9.21.0 @@ -1498,6 +1519,10 @@ packages: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -1888,6 +1913,20 @@ packages: fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + framer-motion@12.4.9: + resolution: {integrity: sha512-c+nDhfiNUwi8G4BrhrP2hjPsDHzIKRbUhDlcK7oC5kXY4QK1IrT/kuhY4BgK6h2ujDrZ8ocvFrG2X8+b1m/MkQ==} + peerDependencies: + '@emotion/is-prop-valid': '*' + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/is-prop-valid': + optional: true + react: + optional: true + react-dom: + optional: true + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -2291,6 +2330,26 @@ packages: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} + motion-dom@12.4.5: + resolution: {integrity: sha512-Q2xmhuyYug1CGTo0jdsL05EQ4RhIYXlggFS/yPhQQRNzbrhjKQ1tbjThx5Plv68aX31LsUQRq4uIkuDxdO5vRQ==} + + motion-utils@12.0.0: + resolution: {integrity: sha512-MNFiBKbbqnmvOjkPyOKgHUp3Q6oiokLkI1bEwm5QA28cxMZrv0CbbBGDNmhF6DIXsi1pCQBSs0dX8xjeER1tmA==} + + motion@12.4.9: + resolution: {integrity: sha512-lOT2+X9b3yvDEC+pAClTzLSW/D5T/zZweO+UN1lMe86WtGFQIbHU/VjEhwGREW0QryG9KECB1uK3QJo8G3NGag==} + peerDependencies: + '@emotion/is-prop-valid': '*' + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/is-prop-valid': + optional: true + react: + optional: true + react-dom: + optional: true + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -2786,6 +2845,14 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + tailwind-merge@3.0.2: + resolution: {integrity: sha512-l7z+OYZ7mu3DTqrL88RiKrKIqO3NcpEO8V/Od04bNpvk0kiIFndGEoqfuzvj4yuhRkHKjRkII2z+KS2HfPcSxw==} + + tailwindcss-animate@1.0.7: + resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders' + tailwindcss@3.4.6: resolution: {integrity: sha512-1uRHzPB+Vzu57ocybfZ4jh5Q3SdlH7XW23J5sQoM9LhE9eIOlzxer/3XPSsycvih3rboRsvt0QCmzSrqyOYUIA==} engines: {node: '>=14.0.0'} @@ -4084,6 +4151,8 @@ snapshots: strip-ansi: 6.0.1 wrap-ansi: 7.0.0 + clsx@2.1.1: {} + color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -4667,6 +4736,15 @@ snapshots: fraction.js@4.3.7: {} + framer-motion@12.4.9(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + dependencies: + motion-dom: 12.4.5 + motion-utils: 12.0.0 + tslib: 2.8.1 + optionalDependencies: + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + fsevents@2.3.3: optional: true @@ -5072,6 +5150,20 @@ snapshots: minipass@7.1.2: {} + motion-dom@12.4.5: + dependencies: + motion-utils: 12.0.0 + + motion-utils@12.0.0: {} + + motion@12.4.9(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + dependencies: + framer-motion: 12.4.9(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + tslib: 2.8.1 + optionalDependencies: + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + ms@2.1.3: {} mz@2.7.0: @@ -5608,6 +5700,12 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + tailwind-merge@3.0.2: {} + + tailwindcss-animate@1.0.7(tailwindcss@3.4.6): + dependencies: + tailwindcss: 3.4.6 + tailwindcss@3.4.6: dependencies: '@alloc/quick-lru': 5.2.0 From 70c1d6946319c70350781fcfdf32e59ee5cab3a9 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Mon, 3 Mar 2025 14:24:35 -0300 Subject: [PATCH 042/168] Fix variants --- packages/backend/src/html/htmlDefaultBuilder.ts | 7 +++---- packages/backend/src/tailwind/tailwindDefaultBuilder.ts | 8 +++----- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/packages/backend/src/html/htmlDefaultBuilder.ts b/packages/backend/src/html/htmlDefaultBuilder.ts index 1e5eabe1..f8120c8e 100644 --- a/packages/backend/src/html/htmlDefaultBuilder.ts +++ b/packages/backend/src/html/htmlDefaultBuilder.ts @@ -336,10 +336,9 @@ export class HtmlDefaultBuilder { if ("variantProperties" in this.node && this.node.variantProperties) { Object.entries(this.node.variantProperties) - ?.map((prop) => { - this.addData(prop[0], prop[1]); - }) - .sort(); + ?.map((prop) => formatDataAttribute(prop[0], prop[1])) + .sort() + .forEach((d) => this.data.push(d)); } const dataAttributes = this.data.join(""); diff --git a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts index 9c7947d0..9946621f 100644 --- a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts +++ b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts @@ -261,12 +261,10 @@ export class TailwindDefaultBuilder { } if ("variantProperties" in this.node && this.node.variantProperties) { - console.log("entries", this.node.variantProperties); Object.entries(this.node.variantProperties) - ?.map((prop) => { - this.addData(prop[0], prop[1]); - }) - .sort(); + ?.map((prop) => formatDataAttribute(prop[0], prop[1])) + .sort() + .forEach((d) => this.data.push(d)); } const classLabel = getClassLabel(this.isJSX); From 268fb118c8fbac7aa6d20757aa6f67b187db8d38 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Mon, 3 Mar 2025 14:57:45 -0300 Subject: [PATCH 043/168] Better empty state --- .../plugin-ui/src/components/CodePanel.tsx | 23 +++-- .../plugin-ui/src/components/EmptyState.tsx | 98 +++++++++++++++++++ 2 files changed, 109 insertions(+), 12 deletions(-) create mode 100644 packages/plugin-ui/src/components/EmptyState.tsx diff --git a/packages/plugin-ui/src/components/CodePanel.tsx b/packages/plugin-ui/src/components/CodePanel.tsx index 0af0aefc..e07dc8ad 100644 --- a/packages/plugin-ui/src/components/CodePanel.tsx +++ b/packages/plugin-ui/src/components/CodePanel.tsx @@ -7,10 +7,10 @@ import { import { useState } from "react"; import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; import { coldarkDark as theme } from "react-syntax-highlighter/dist/esm/styles/prism"; -import copy from "copy-to-clipboard"; import SelectableToggle from "./SelectableToggle"; import React from "react"; import { CopyButton } from "./CopyButton"; +import EmptyState from "./EmptyState"; interface CodePanelProps { code: string; @@ -25,7 +25,6 @@ interface CodePanelProps { } const CodePanel = (props: CodePanelProps) => { - const [isPressed, setIsPressed] = useState(false); const [syntaxHovered, setSyntaxHovered] = useState(false); const { code, @@ -66,13 +65,6 @@ const CodePanel = (props: CodePanelProps) => { ? applyPrefixToClasses(code, customPrefix) : code; - // Clipboard and hover handlers. - const handleButtonClick = () => { - setIsPressed(true); - setTimeout(() => setIsPressed(false), 250); - copy(prefixedCode); - }; - const handleButtonHover = () => setSyntaxHovered(true); const handleButtonLeave = () => setSyntaxHovered(false); @@ -177,15 +169,21 @@ const CodePanel = (props: CodePanelProps) => { )}
{isCodeEmpty ? ( -

No layer is selected. Please select a layer.

+ ) : ( {
); }; + export default CodePanel; diff --git a/packages/plugin-ui/src/components/EmptyState.tsx b/packages/plugin-ui/src/components/EmptyState.tsx new file mode 100644 index 00000000..586ead86 --- /dev/null +++ b/packages/plugin-ui/src/components/EmptyState.tsx @@ -0,0 +1,98 @@ +import React from "react"; +import { Code, MousePointer, Eye, Copy } from "lucide-react"; + +const EmptyState = () => { + return ( +
+ {/* Icon with "no code" symbol */} +
+
+ + + + + +
+
+ + {/* Title and hint */} +

+ No Layer Selected +

+

+ Select a layer from your Figma design to view the generated code. +

+ + {/* Completely redesigned steps section */} +
+
+ {/* Progress bar */} +
+ + {/* Steps with connecting line */} +
    + {/* Step 1 - Current */} +
  1. +
    +
    +
    + +
    +
    +
    +
    + Select +
    +

    + Choose a layer +

    +
    +
  2. + + {/* Step 2 */} +
  3. +
    + +
    +
    +
    + View +
    +

    + See the code +

    +
    +
  4. + + {/* Step 3 */} +
  5. +
    + +
    +
    +
    + Copy +
    +

    + Use anywhere +

    +
    +
  6. +
+
+
+
+ ); +}; + +export default EmptyState; From f313613e45117434cbea6debf6bfec76baec4b1e Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Mon, 3 Mar 2025 15:32:09 -0300 Subject: [PATCH 044/168] Improve --- apps/debug/package.json | 2 +- packages/backend/src/code.ts | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/apps/debug/package.json b/apps/debug/package.json index 366db3d7..2a589dec 100644 --- a/apps/debug/package.json +++ b/apps/debug/package.json @@ -17,7 +17,7 @@ "react-dom": "^19.0.0" }, "devDependencies": { - "@types/node": "^20.17.21", + "@types/node": "^22.13.9", "@types/react": "^19.0.10", "@types/react-dom": "^19.0.4", "autoprefixer": "^10.4.20", diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index 861c901d..8f971537 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -96,23 +96,29 @@ const processNodeData = (node: any, optimizeLayout: boolean) => { } } - node.width = (figmaNode as any).width; - node.height = (figmaNode as any).height; - node.x = (figmaNode as any).x; - node.y = (figmaNode as any).y; + if ("width" in figmaNode) { + node.width = (figmaNode as any).width; + node.height = (figmaNode as any).height; + node.x = (figmaNode as any).x; + node.y = (figmaNode as any).y; + } } } catch (e) { // Silently fail if there's an error accessing the Figma node } } else { // Avoid calling getNodeById if we don't need to - if (node.rotation && node.rotation !== 0) { + if ( + "rotation" in node && + node.rotation !== undefined && + node.rotation !== 0 + ) { const figmaNode = figma.getNodeById(node.id); node.width = (figmaNode as any).width; node.height = (figmaNode as any).height; node.x = (figmaNode as any).x; node.y = (figmaNode as any).y; - } else { + } else if (node.absoluteRenderBounds) { // Use the absoluteRenderBounds if we don't need to fetch the Figma node. node.width = node.absoluteRenderBounds.width; node.height = node.absoluteRenderBounds.height; From 8384584fc9b41ad925ab3c4f99fb342093129277 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Mon, 3 Mar 2025 16:10:56 -0300 Subject: [PATCH 045/168] Minor fixes --- packages/backend/src/code.ts | 4 +-- packages/backend/src/common/commonPosition.ts | 27 +++++++++---------- packages/backend/src/tailwind/tailwindMain.ts | 16 +++++------ 3 files changed, 22 insertions(+), 25 deletions(-) diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index 8f971537..5bf72b65 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -127,8 +127,8 @@ const processNodeData = (node: any, optimizeLayout: boolean) => { } } - if (!node.LayoutMode) { - node.LayoutMode = "NONE"; + if (!node.layoutMode) { + node.layoutMode = "NONE"; } if (!node.layoutGrow) { node.layoutGrow = 0; diff --git a/packages/backend/src/common/commonPosition.ts b/packages/backend/src/common/commonPosition.ts index acdb9007..ce97d203 100644 --- a/packages/backend/src/common/commonPosition.ts +++ b/packages/backend/src/common/commonPosition.ts @@ -122,22 +122,21 @@ export const commonIsAbsolutePosition = ( return false; } - if ("layoutAlign" in node) { - if (!node.parent || node.parent === undefined) { - return false; - } + if (!node.parent || node.parent === undefined) { + return false; + } - const parentLayoutIsNone = - "layoutMode" in node.parent && node.parent.layoutMode === "NONE"; - const hasNoLayoutMode = !("layoutMode" in node.parent); + const parentLayoutIsNone = + "layoutMode" in node.parent && node.parent.layoutMode === "NONE"; + const hasNoLayoutMode = !("layoutMode" in node.parent); - if ( - node.layoutPositioning === "ABSOLUTE" || - parentLayoutIsNone || - hasNoLayoutMode - ) { - return true; - } + if ( + ("layoutPositioning" in node && node.layoutPositioning === "ABSOLUTE") || + parentLayoutIsNone || + hasNoLayoutMode + ) { + return true; } + return false; }; diff --git a/packages/backend/src/tailwind/tailwindMain.ts b/packages/backend/src/tailwind/tailwindMain.ts index b39e6225..aa1613c8 100644 --- a/packages/backend/src/tailwind/tailwindMain.ts +++ b/packages/backend/src/tailwind/tailwindMain.ts @@ -170,7 +170,7 @@ const tailwindFrame = async ( ); const childrenStr = await tailwindWidgetGenerator(sortedChildren, settings); - const clipsContentClass = node.clipsContent ? " overflow-hidden" : ""; + const clipsContentClass = node.clipsContent ? "overflow-hidden" : ""; let layoutProps = ""; if (node.layoutMode !== "NONE") { @@ -182,16 +182,14 @@ const tailwindFrame = async ( layoutProps = tailwindAutoLayoutProps(node, node.inferredAutoLayout); } - return tailwindContainer( - node, - childrenStr, - layoutProps + clipsContentClass, - settings, - ); + // Combine classes properly, ensuring no extra spaces + const combinedProps = [layoutProps, clipsContentClass] + .filter(Boolean) + .join(" "); + + return tailwindContainer(node, childrenStr, combinedProps, settings); }; -// Properties named propSomething always take care of "," -// Sometimes a property might not exist, so it doesn't add "," export const tailwindContainer = ( node: SceneNode & SceneNodeMixin & From 9078e4f6240a6e8203727d91409523446b9c8b96 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Tue, 4 Mar 2025 12:07:25 -0300 Subject: [PATCH 046/168] Improve about --- packages/backend/src/api_types.ts | 6899 +++++++++++++++++ .../src/tailwind/tailwindDefaultBuilder.ts | 47 +- packages/plugin-ui/src/components/About.tsx | 13 +- 3 files changed, 6936 insertions(+), 23 deletions(-) create mode 100644 packages/backend/src/api_types.ts diff --git a/packages/backend/src/api_types.ts b/packages/backend/src/api_types.ts new file mode 100644 index 00000000..b5657707 --- /dev/null +++ b/packages/backend/src/api_types.ts @@ -0,0 +1,6899 @@ +// Copied from https://github.com/figma/rest-api-spec/blob/main/dist/api_types.ts + +export type IsLayerTrait = { + /** + * A string uniquely identifying this node within the document. + */ + id: string + + /** + * The name given to the node by the user in the tool. + */ + name: string + + /** + * The type of the node + */ + type: string + + /** + * Whether or not the node is visible on the canvas. + */ + visible?: boolean + + /** + * If true, layer is locked and cannot be edited + */ + locked?: boolean + + /** + * Whether the layer is fixed while the parent is scrolling + * + * @deprecated + */ + isFixed?: boolean + + /** + * How layer should be treated when the frame is resized + */ + scrollBehavior: 'SCROLLS' | 'FIXED' | 'STICKY_SCROLLS' + + /** + * The rotation of the node, if not 0. + */ + rotation?: number + + /** + * A mapping of a layer's property to component property name of component properties attached to + * this node. The component property name can be used to look up more information on the + * corresponding component's or component set's componentPropertyDefinitions. + */ + componentPropertyReferences?: { [key: string]: string } + + /** + * Data written by plugins that is visible only to the plugin that wrote it. Requires the + * `pluginData` to include the ID of the plugin. + */ + pluginData?: unknown + + /** + * Data written by plugins that is visible to all plugins. Requires the `pluginData` parameter to + * include the string "shared". + */ + sharedPluginData?: unknown + + /** + * A mapping of field to the variables applied to this field. Most fields will only map to a single + * `VariableAlias`. However, for properties like `fills`, `strokes`, `size`, `componentProperties`, + * and `textRangeFills`, it is possible to have multiple variables bound to the field. + */ + boundVariables?: { + size?: { + x?: VariableAlias + + y?: VariableAlias + } + + individualStrokeWeights?: { + top?: VariableAlias + + bottom?: VariableAlias + + left?: VariableAlias + + right?: VariableAlias + } + + characters?: VariableAlias + + itemSpacing?: VariableAlias + + paddingLeft?: VariableAlias + + paddingRight?: VariableAlias + + paddingTop?: VariableAlias + + paddingBottom?: VariableAlias + + visible?: VariableAlias + + topLeftRadius?: VariableAlias + + topRightRadius?: VariableAlias + + bottomLeftRadius?: VariableAlias + + bottomRightRadius?: VariableAlias + + minWidth?: VariableAlias + + maxWidth?: VariableAlias + + minHeight?: VariableAlias + + maxHeight?: VariableAlias + + counterAxisSpacing?: VariableAlias + + opacity?: VariableAlias + + fontFamily?: VariableAlias[] + + fontSize?: VariableAlias[] + + fontStyle?: VariableAlias[] + + fontWeight?: VariableAlias[] + + letterSpacing?: VariableAlias[] + + lineHeight?: VariableAlias[] + + paragraphSpacing?: VariableAlias[] + + paragraphIndent?: VariableAlias[] + + fills?: VariableAlias[] + + strokes?: VariableAlias[] + + componentProperties?: { [key: string]: VariableAlias } + + textRangeFills?: VariableAlias[] + + effects?: VariableAlias[] + + layoutGrids?: VariableAlias[] + } + + /** + * A mapping of variable collection ID to mode ID representing the explicitly set modes for this + * node. + */ + explicitVariableModes?: { [key: string]: string } + } + + export type HasChildrenTrait = { + /** + * An array of nodes that are direct children of this node + */ + children: SubcanvasNode[] + } + + export type HasLayoutTrait = { + /** + * Bounding box of the node in absolute space coordinates. + */ + absoluteBoundingBox: Rectangle | null + + /** + * The actual bounds of a node accounting for drop shadows, thick strokes, and anything else that + * may fall outside the node's regular bounding box defined in `x`, `y`, `width`, and `height`. The + * `x` and `y` inside this property represent the absolute position of the node on the page. This + * value will be `null` if the node is invisible. + */ + absoluteRenderBounds: Rectangle | null + + /** + * Keep height and width constrained to same ratio. + */ + preserveRatio?: boolean + + /** + * Horizontal and vertical layout constraints for node. + */ + constraints?: LayoutConstraint + + /** + * The top two rows of a matrix that represents the 2D transform of this node relative to its + * parent. The bottom row of the matrix is implicitly always (0, 0, 1). Use to transform coordinates + * in geometry. Only present if `geometry=paths` is passed. + */ + relativeTransform?: Transform + + /** + * Width and height of element. This is different from the width and height of the bounding box in + * that the absolute bounding box represents the element after scaling and rotation. Only present if + * `geometry=paths` is passed. + */ + size?: Vector + + /** + * Determines if the layer should stretch along the parent's counter axis. This property is only + * provided for direct children of auto-layout frames. + * + * - `INHERIT` + * - `STRETCH` + * + * In previous versions of auto layout, determined how the layer is aligned inside an auto-layout + * frame. This property is only provided for direct children of auto-layout frames. + * + * - `MIN` + * - `CENTER` + * - `MAX` + * - `STRETCH` + * + * In horizontal auto-layout frames, "MIN" and "MAX" correspond to "TOP" and "BOTTOM". In vertical + * auto-layout frames, "MIN" and "MAX" correspond to "LEFT" and "RIGHT". + */ + layoutAlign?: 'INHERIT' | 'STRETCH' | 'MIN' | 'CENTER' | 'MAX' + + /** + * This property is applicable only for direct children of auto-layout frames, ignored otherwise. + * Determines whether a layer should stretch along the parent's primary axis. A `0` corresponds to a + * fixed size and `1` corresponds to stretch. + */ + layoutGrow?: 0 | 1 + + /** + * Determines whether a layer's size and position should be determined by auto-layout settings or + * manually adjustable. + */ + layoutPositioning?: 'AUTO' | 'ABSOLUTE' + + /** + * The minimum width of the frame. This property is only applicable for auto-layout frames or direct + * children of auto-layout frames. + */ + minWidth?: number + + /** + * The maximum width of the frame. This property is only applicable for auto-layout frames or direct + * children of auto-layout frames. + */ + maxWidth?: number + + /** + * The minimum height of the frame. This property is only applicable for auto-layout frames or + * direct children of auto-layout frames. + */ + minHeight?: number + + /** + * The maximum height of the frame. This property is only applicable for auto-layout frames or + * direct children of auto-layout frames. + */ + maxHeight?: number + + /** + * The horizontal sizing setting on this auto-layout frame or frame child. + * + * - `FIXED` + * - `HUG`: only valid on auto-layout frames and text nodes + * - `FILL`: only valid on auto-layout frame children + */ + layoutSizingHorizontal?: 'FIXED' | 'HUG' | 'FILL' + + /** + * The vertical sizing setting on this auto-layout frame or frame child. + * + * - `FIXED` + * - `HUG`: only valid on auto-layout frames and text nodes + * - `FILL`: only valid on auto-layout frame children + */ + layoutSizingVertical?: 'FIXED' | 'HUG' | 'FILL' + } + + export type HasFramePropertiesTrait = { + /** + * Whether or not this node clip content outside of its bounds + */ + clipsContent: boolean + + /** + * Background of the node. This is deprecated, as backgrounds for frames are now in the `fills` + * field. + * + * @deprecated + */ + background?: Paint[] + + /** + * Background color of the node. This is deprecated, as frames now support more than a solid color + * as a background. Please use the `fills` field instead. + * + * @deprecated + */ + backgroundColor?: RGBA + + /** + * An array of layout grids attached to this node (see layout grids section for more details). GROUP + * nodes do not have this attribute + */ + layoutGrids?: LayoutGrid[] + + /** + * Whether a node has primary axis scrolling, horizontal or vertical. + */ + overflowDirection?: + | 'HORIZONTAL_SCROLLING' + | 'VERTICAL_SCROLLING' + | 'HORIZONTAL_AND_VERTICAL_SCROLLING' + | 'NONE' + + /** + * Whether this layer uses auto-layout to position its children. + */ + layoutMode?: 'NONE' | 'HORIZONTAL' | 'VERTICAL' + + /** + * Whether the primary axis has a fixed length (determined by the user) or an automatic length + * (determined by the layout engine). This property is only applicable for auto-layout frames. + */ + primaryAxisSizingMode?: 'FIXED' | 'AUTO' + + /** + * Whether the counter axis has a fixed length (determined by the user) or an automatic length + * (determined by the layout engine). This property is only applicable for auto-layout frames. + */ + counterAxisSizingMode?: 'FIXED' | 'AUTO' + + /** + * Determines how the auto-layout frame's children should be aligned in the primary axis direction. + * This property is only applicable for auto-layout frames. + */ + primaryAxisAlignItems?: 'MIN' | 'CENTER' | 'MAX' | 'SPACE_BETWEEN' + + /** + * Determines how the auto-layout frame's children should be aligned in the counter axis direction. + * This property is only applicable for auto-layout frames. + */ + counterAxisAlignItems?: 'MIN' | 'CENTER' | 'MAX' | 'BASELINE' + + /** + * The padding between the left border of the frame and its children. This property is only + * applicable for auto-layout frames. + */ + paddingLeft?: number + + /** + * The padding between the right border of the frame and its children. This property is only + * applicable for auto-layout frames. + */ + paddingRight?: number + + /** + * The padding between the top border of the frame and its children. This property is only + * applicable for auto-layout frames. + */ + paddingTop?: number + + /** + * The padding between the bottom border of the frame and its children. This property is only + * applicable for auto-layout frames. + */ + paddingBottom?: number + + /** + * The distance between children of the frame. Can be negative. This property is only applicable for + * auto-layout frames. + */ + itemSpacing?: number + + /** + * Determines the canvas stacking order of layers in this frame. When true, the first layer will be + * draw on top. This property is only applicable for auto-layout frames. + */ + itemReverseZIndex?: boolean + + /** + * Determines whether strokes are included in layout calculations. When true, auto-layout frames + * behave like css "box-sizing: border-box". This property is only applicable for auto-layout + * frames. + */ + strokesIncludedInLayout?: boolean + + /** + * Whether this auto-layout frame has wrapping enabled. + */ + layoutWrap?: 'NO_WRAP' | 'WRAP' + + /** + * The distance between wrapped tracks of an auto-layout frame. This property is only applicable for + * auto-layout frames with `layoutWrap: "WRAP"` + */ + counterAxisSpacing?: number + + /** + * Determines how the auto-layout frame’s wrapped tracks should be aligned in the counter axis + * direction. This property is only applicable for auto-layout frames with `layoutWrap: "WRAP"`. + */ + counterAxisAlignContent?: 'AUTO' | 'SPACE_BETWEEN' + } + + export type HasBlendModeAndOpacityTrait = { + /** + * How this node blends with nodes behind it in the scene (see blend mode section for more details) + */ + blendMode: BlendMode + + /** + * Opacity of the node + */ + opacity?: number + } + + export type HasExportSettingsTrait = { + /** + * An array of export settings representing images to export from the node. + */ + exportSettings?: ExportSetting[] + } + + export type HasGeometryTrait = MinimalFillsTrait & + MinimalStrokesTrait & { + /** + * Map from ID to PaintOverride for looking up fill overrides. To see which regions are overriden, + * you must use the `geometry=paths` option. Each path returned may have an `overrideID` which maps + * to this table. + */ + fillOverrideTable?: { [key: string]: PaintOverride | null } + + /** + * Only specified if parameter `geometry=paths` is used. An array of paths representing the object + * fill. + */ + fillGeometry?: Path[] + + /** + * Only specified if parameter `geometry=paths` is used. An array of paths representing the object + * stroke. + */ + strokeGeometry?: Path[] + + /** + * A string enum describing the end caps of vector paths. + */ + strokeCap?: + | 'NONE' + | 'ROUND' + | 'SQUARE' + | 'LINE_ARROW' + | 'TRIANGLE_ARROW' + | 'DIAMOND_FILLED' + | 'CIRCLE_FILLED' + | 'TRIANGLE_FILLED' + | 'WASHI_TAPE_1' + | 'WASHI_TAPE_2' + | 'WASHI_TAPE_3' + | 'WASHI_TAPE_4' + | 'WASHI_TAPE_5' + | 'WASHI_TAPE_6' + + /** + * Only valid if `strokeJoin` is "MITER". The corner angle, in degrees, below which `strokeJoin` + * will be set to "BEVEL" to avoid super sharp corners. By default this is 28.96 degrees. + */ + strokeMiterAngle?: number + } + + export type MinimalFillsTrait = { + /** + * An array of fill paints applied to the node. + */ + fills: Paint[] + + /** + * A mapping of a StyleType to style ID (see Style) of styles present on this node. The style ID can + * be used to look up more information about the style in the top-level styles field. + */ + styles?: { [key: string]: string } + } + + export type MinimalStrokesTrait = { + /** + * An array of stroke paints applied to the node. + */ + strokes?: Paint[] + + /** + * The weight of strokes on the node. + */ + strokeWeight?: number + + /** + * Position of stroke relative to vector outline, as a string enum + * + * - `INSIDE`: stroke drawn inside the shape boundary + * - `OUTSIDE`: stroke drawn outside the shape boundary + * - `CENTER`: stroke drawn centered along the shape boundary + */ + strokeAlign?: 'INSIDE' | 'OUTSIDE' | 'CENTER' + + /** + * A string enum with value of "MITER", "BEVEL", or "ROUND", describing how corners in vector paths + * are rendered. + */ + strokeJoin?: 'MITER' | 'BEVEL' | 'ROUND' + + /** + * An array of floating point numbers describing the pattern of dash length and gap lengths that the + * vector stroke will use when drawn. + * + * For example a value of [1, 2] indicates that the stroke will be drawn with a dash of length 1 + * followed by a gap of length 2, repeated. + */ + strokeDashes?: number[] + } + + export type IndividualStrokesTrait = { + /** + * An object including the top, bottom, left, and right stroke weights. Only returned if individual + * stroke weights are used. + */ + individualStrokeWeights?: StrokeWeights + } + + export type CornerTrait = { + /** + * Radius of each corner if a single radius is set for all corners + */ + cornerRadius?: number + + /** + * A value that lets you control how "smooth" the corners are. Ranges from 0 to 1. 0 is the default + * and means that the corner is perfectly circular. A value of 0.6 means the corner matches the iOS + * 7 "squircle" icon shape. Other values produce various other curves. + */ + cornerSmoothing?: number + + /** + * Array of length 4 of the radius of each corner of the frame, starting in the top left and + * proceeding clockwise. + * + * Values are given in the order top-left, top-right, bottom-right, bottom-left. + */ + rectangleCornerRadii?: number[] + } + + export type HasEffectsTrait = { + /** + * An array of effects attached to this node (see effects section for more details) + */ + effects: Effect[] + } + + export type HasMaskTrait = { + /** + * Does this node mask sibling nodes in front of it? + */ + isMask?: boolean + + /** + * If this layer is a mask, this property describes the operation used to mask the layer's siblings. + * The value may be one of the following: + * + * - ALPHA: the mask node's alpha channel will be used to determine the opacity of each pixel in the + * masked result. + * - VECTOR: if the mask node has visible fill paints, every pixel inside the node's fill regions will + * be fully visible in the masked result. If the mask has visible stroke paints, every pixel + * inside the node's stroke regions will be fully visible in the masked result. + * - LUMINANCE: the luminance value of each pixel of the mask node will be used to determine the + * opacity of that pixel in the masked result. + */ + maskType?: 'ALPHA' | 'VECTOR' | 'LUMINANCE' + + /** + * True if maskType is VECTOR. This field is deprecated; use maskType instead. + * + * @deprecated + */ + isMaskOutline?: boolean + } + + export type ComponentPropertiesTrait = { + /** + * A mapping of name to `ComponentPropertyDefinition` for every component property on this + * component. Each property has a type, defaultValue, and other optional values. + */ + componentPropertyDefinitions?: { [key: string]: ComponentPropertyDefinition } + } + + export type TypePropertiesTrait = { + /** + * The raw characters in the text node. + */ + characters: string + + /** + * Style of text including font family and weight. + */ + style: TypeStyle + + /** + * The array corresponds to characters in the text box, where each element references the + * 'styleOverrideTable' to apply specific styles to each character. The array's length can be less + * than or equal to the number of characters due to the removal of trailing zeros. Elements with a + * value of 0 indicate characters that use the default type style. If the array is shorter than the + * total number of characters, the characters beyond the array's length also use the default style. + */ + characterStyleOverrides: number[] + + /** + * Internal property, preserved for backward compatibility. Avoid using this value. + */ + layoutVersion?: number + + /** + * Map from ID to TypeStyle for looking up style overrides. + */ + styleOverrideTable: { [key: string]: TypeStyle } + + /** + * An array with the same number of elements as lines in the text node, where lines are delimited by + * newline or paragraph separator characters. Each element in the array corresponds to the list type + * of a specific line. List types are represented as string enums with one of these possible + * values: + * + * - `NONE`: Not a list item. + * - `ORDERED`: Text is an ordered list (numbered). + * - `UNORDERED`: Text is an unordered list (bulleted). + */ + lineTypes: ('NONE' | 'ORDERED' | 'UNORDERED')[] + + /** + * An array with the same number of elements as lines in the text node, where lines are delimited by + * newline or paragraph separator characters. Each element in the array corresponds to the + * indentation level of a specific line. + */ + lineIndentations: number[] + } + + export type HasTextSublayerTrait = { + /** + * Text contained within a text box. + */ + characters: string + } + + export type TransitionSourceTrait = { + /** + * Node ID of node to transition to in prototyping + */ + transitionNodeID?: string + + /** + * The duration of the prototyping transition on this node (in milliseconds). This will override the + * default transition duration on the prototype, for this node. + */ + transitionDuration?: number + + /** + * The easing curve used in the prototyping transition on this node. + */ + transitionEasing?: EasingType + + interactions?: Interaction[] + } + + export type DevStatusTrait = { + /** + * Represents whether or not a node has a particular handoff (or dev) status applied to it. + */ + devStatus?: { + type: 'NONE' | 'READY_FOR_DEV' | 'COMPLETED' + + /** + * An optional field where the designer can add more information about the design and what has + * changed. + */ + description?: string + } + } + + export type AnnotationsTrait = object + + export type FrameTraits = IsLayerTrait & + HasBlendModeAndOpacityTrait & + HasChildrenTrait & + HasLayoutTrait & + HasFramePropertiesTrait & + CornerTrait & + HasGeometryTrait & + HasExportSettingsTrait & + HasEffectsTrait & + HasMaskTrait & + TransitionSourceTrait & + IndividualStrokesTrait & + DevStatusTrait & + AnnotationsTrait + + export type DefaultShapeTraits = IsLayerTrait & + HasBlendModeAndOpacityTrait & + HasLayoutTrait & + HasGeometryTrait & + HasExportSettingsTrait & + HasEffectsTrait & + HasMaskTrait & + TransitionSourceTrait + + export type CornerRadiusShapeTraits = DefaultShapeTraits & CornerTrait + + export type RectangularShapeTraits = DefaultShapeTraits & + CornerTrait & + IndividualStrokesTrait & + AnnotationsTrait + + export type Node = + | BooleanOperationNode + | ComponentNode + | ComponentSetNode + | ConnectorNode + | EllipseNode + | EmbedNode + | FrameNode + | GroupNode + | InstanceNode + | LineNode + | LinkUnfurlNode + | RectangleNode + | RegularPolygonNode + | SectionNode + | ShapeWithTextNode + | SliceNode + | StarNode + | StickyNode + | TableNode + | TableCellNode + | TextNode + | VectorNode + | WashiTapeNode + | WidgetNode + | DocumentNode + | CanvasNode + + export type DocumentNode = { + type: 'DOCUMENT' + + children: CanvasNode[] + } & IsLayerTrait + + export type CanvasNode = { + type: 'CANVAS' + + children: SubcanvasNode[] + + /** + * Background color of the canvas. + */ + backgroundColor: RGBA + + /** + * Node ID that corresponds to the start frame for prototypes. This is deprecated with the + * introduction of multiple flows. Please use the `flowStartingPoints` field. + * + * @deprecated + */ + prototypeStartNodeID: string | null + + /** + * An array of flow starting points sorted by its position in the prototype settings panel. + */ + flowStartingPoints: FlowStartingPoint[] + + /** + * The device used to view a prototype. + */ + prototypeDevice: PrototypeDevice + + measurements?: Measurement[] + } & IsLayerTrait & + HasExportSettingsTrait + + export type SubcanvasNode = + | BooleanOperationNode + | ComponentNode + | ComponentSetNode + | ConnectorNode + | EllipseNode + | EmbedNode + | FrameNode + | GroupNode + | InstanceNode + | LineNode + | LinkUnfurlNode + | RectangleNode + | RegularPolygonNode + | SectionNode + | ShapeWithTextNode + | SliceNode + | StarNode + | StickyNode + | TableNode + | TableCellNode + | TextNode + | VectorNode + | WashiTapeNode + | WidgetNode + + export type BooleanOperationNode = { + /** + * The type of this node, represented by the string literal "BOOLEAN_OPERATION" + */ + type: 'BOOLEAN_OPERATION' + + /** + * A string enum indicating the type of boolean operation applied. + */ + booleanOperation: 'UNION' | 'INTERSECT' | 'SUBTRACT' | 'EXCLUDE' + } & IsLayerTrait & + HasBlendModeAndOpacityTrait & + HasChildrenTrait & + HasLayoutTrait & + HasGeometryTrait & + HasExportSettingsTrait & + HasEffectsTrait & + HasMaskTrait & + TransitionSourceTrait + + export type SectionNode = { + /** + * The type of this node, represented by the string literal "SECTION" + */ + type: 'SECTION' + + /** + * Whether the contents of the section are visible + */ + sectionContentsHidden: boolean + } & IsLayerTrait & + HasGeometryTrait & + HasChildrenTrait & + HasLayoutTrait & + DevStatusTrait + + export type FrameNode = { + /** + * The type of this node, represented by the string literal "FRAME" + */ + type: 'FRAME' + } & FrameTraits + + export type GroupNode = { + /** + * The type of this node, represented by the string literal "GROUP" + */ + type: 'GROUP' + } & FrameTraits + + export type ComponentNode = { + /** + * The type of this node, represented by the string literal "COMPONENT" + */ + type: 'COMPONENT' + } & FrameTraits & + ComponentPropertiesTrait + + export type ComponentSetNode = { + /** + * The type of this node, represented by the string literal "COMPONENT_SET" + */ + type: 'COMPONENT_SET' + } & FrameTraits & + ComponentPropertiesTrait + + export type VectorNode = { + /** + * The type of this node, represented by the string literal "VECTOR" + */ + type: 'VECTOR' + } & CornerRadiusShapeTraits & + AnnotationsTrait + + export type StarNode = { + /** + * The type of this node, represented by the string literal "STAR" + */ + type: 'STAR' + } & CornerRadiusShapeTraits & + AnnotationsTrait + + export type LineNode = { + /** + * The type of this node, represented by the string literal "LINE" + */ + type: 'LINE' + } & DefaultShapeTraits & + AnnotationsTrait + + export type EllipseNode = { + /** + * The type of this node, represented by the string literal "ELLIPSE" + */ + type: 'ELLIPSE' + + arcData: ArcData + } & DefaultShapeTraits & + AnnotationsTrait + + export type RegularPolygonNode = { + /** + * The type of this node, represented by the string literal "REGULAR_POLYGON" + */ + type: 'REGULAR_POLYGON' + } & CornerRadiusShapeTraits & + AnnotationsTrait + + export type RectangleNode = { + /** + * The type of this node, represented by the string literal "RECTANGLE" + */ + type: 'RECTANGLE' + } & RectangularShapeTraits + + export type TextNode = { + /** + * The type of this node, represented by the string literal "TEXT" + */ + type: 'TEXT' + } & DefaultShapeTraits & + TypePropertiesTrait & + AnnotationsTrait + + export type TableNode = { + /** + * The type of this node, represented by the string literal "TABLE" + */ + type: 'TABLE' + } & IsLayerTrait & + HasChildrenTrait & + HasLayoutTrait & + MinimalStrokesTrait & + HasEffectsTrait & + HasBlendModeAndOpacityTrait & + HasExportSettingsTrait + + export type TableCellNode = { + /** + * The type of this node, represented by the string literal "TABLE_CELL" + */ + type: 'TABLE_CELL' + } & IsLayerTrait & + MinimalFillsTrait & + HasLayoutTrait & + HasTextSublayerTrait + + export type SliceNode = { + /** + * The type of this node, represented by the string literal "SLICE" + */ + type: 'SLICE' + } & IsLayerTrait + + export type InstanceNode = { + /** + * The type of this node, represented by the string literal "INSTANCE" + */ + type: 'INSTANCE' + + /** + * ID of component that this instance came from. + */ + componentId: string + + /** + * If true, this node has been marked as exposed to its containing component or component set. + */ + isExposedInstance?: boolean + + /** + * IDs of instances that have been exposed to this node's level. + */ + exposedInstances?: string[] + + /** + * A mapping of name to `ComponentProperty` for all component properties on this instance. Each + * property has a type, value, and other optional values. + */ + componentProperties?: { [key: string]: ComponentProperty } + + /** + * An array of all of the fields directly overridden on this instance. Inherited overrides are not + * included. + */ + overrides: Overrides[] + } & FrameTraits + + export type EmbedNode = { + /** + * The type of this node, represented by the string literal "EMBED" + */ + type: 'EMBED' + } & IsLayerTrait & + HasExportSettingsTrait + + export type LinkUnfurlNode = { + /** + * The type of this node, represented by the string literal "LINK_UNFURL" + */ + type: 'LINK_UNFURL' + } & IsLayerTrait & + HasExportSettingsTrait + + export type StickyNode = { + /** + * The type of this node, represented by the string literal "STICKY" + */ + type: 'STICKY' + + /** + * If true, author name is visible. + */ + authorVisible?: boolean + } & IsLayerTrait & + HasLayoutTrait & + HasBlendModeAndOpacityTrait & + MinimalFillsTrait & + HasMaskTrait & + HasEffectsTrait & + HasExportSettingsTrait & + HasTextSublayerTrait + + export type ShapeWithTextNode = { + /** + * The type of this node, represented by the string literal "SHAPE_WITH_TEXT" + */ + type: 'SHAPE_WITH_TEXT' + + /** + * Geometric shape type. Most shape types have the same name as their tooltip but there are a few + * exceptions. ENG_DATABASE: Cylinder, ENG_QUEUE: Horizontal cylinder, ENG_FILE: File, ENG_FOLDER: + * Folder. + */ + shapeType: ShapeType + } & IsLayerTrait & + HasLayoutTrait & + HasBlendModeAndOpacityTrait & + MinimalFillsTrait & + HasMaskTrait & + HasEffectsTrait & + HasExportSettingsTrait & + HasTextSublayerTrait & + CornerTrait & + MinimalStrokesTrait + + export type ConnectorNode = { + /** + * The type of this node, represented by the string literal "CONNECTOR" + */ + type: 'CONNECTOR' + + /** + * The starting point of the connector. + */ + connectorStart: ConnectorEndpoint + + /** + * The ending point of the connector. + */ + connectorEnd: ConnectorEndpoint + + /** + * A string enum describing the end cap of the start of the connector. + */ + connectorStartStrokeCap: + | 'NONE' + | 'LINE_ARROW' + | 'TRIANGLE_ARROW' + | 'DIAMOND_FILLED' + | 'CIRCLE_FILLED' + | 'TRIANGLE_FILLED' + + /** + * A string enum describing the end cap of the end of the connector. + */ + connectorEndStrokeCap: + | 'NONE' + | 'LINE_ARROW' + | 'TRIANGLE_ARROW' + | 'DIAMOND_FILLED' + | 'CIRCLE_FILLED' + | 'TRIANGLE_FILLED' + + /** + * Connector line type. + */ + connectorLineType: ConnectorLineType + + /** + * Connector text background. + */ + textBackground?: ConnectorTextBackground + } & IsLayerTrait & + HasLayoutTrait & + HasBlendModeAndOpacityTrait & + HasEffectsTrait & + HasExportSettingsTrait & + HasTextSublayerTrait & + MinimalStrokesTrait + + export type WashiTapeNode = { + /** + * The type of this node, represented by the string literal "WASHI_TAPE" + */ + type: 'WASHI_TAPE' + } & DefaultShapeTraits + + export type WidgetNode = { + /** + * The type of this node, represented by the string literal "WIDGET" + */ + type: 'WIDGET' + } & IsLayerTrait & + HasExportSettingsTrait & + HasChildrenTrait + + /** + * An RGB color + */ + export type RGB = { + /** + * Red channel value, between 0 and 1. + */ + r: number + + /** + * Green channel value, between 0 and 1. + */ + g: number + + /** + * Blue channel value, between 0 and 1. + */ + b: number + } + + /** + * An RGBA color + */ + export type RGBA = { + /** + * Red channel value, between 0 and 1. + */ + r: number + + /** + * Green channel value, between 0 and 1. + */ + g: number + + /** + * Blue channel value, between 0 and 1. + */ + b: number + + /** + * Alpha channel value, between 0 and 1. + */ + a: number + } + + /** + * A flow starting point used when launching a prototype to enter Presentation view. + */ + export type FlowStartingPoint = { + /** + * Unique identifier specifying the frame. + */ + nodeId: string + + /** + * Name of flow. + */ + name: string + } + + /** + * A width and a height. + */ + export type Size = { + /** + * The width of a size. + */ + width: number + + /** + * The height of a size. + */ + height: number + } + + /** + * The device used to view a prototype. + */ + export type PrototypeDevice = { + type: 'NONE' | 'PRESET' | 'CUSTOM' | 'PRESENTATION' + + size?: Size + + presetIdentifier?: string + + rotation: 'NONE' | 'CCW_90' + } + + /** + * Sizing constraint for exports. + */ + export type Constraint = { + /** + * Type of constraint to apply: + * + * - `SCALE`: Scale by `value`. + * - `WIDTH`: Scale proportionally and set width to `value`. + * - `HEIGHT`: Scale proportionally and set height to `value`. + */ + type: 'SCALE' | 'WIDTH' | 'HEIGHT' + + /** + * See type property for effect of this field. + */ + value: number + } + + /** + * An export setting. + */ + export type ExportSetting = { + suffix: string + + format: 'JPG' | 'PNG' | 'SVG' | 'PDF' + + constraint: Constraint + } + + /** + * This type is a string enum with the following possible values + * + * Normal blends: + * + * - `PASS_THROUGH` (only applicable to objects with children) + * - `NORMAL` + * + * Darken: + * + * - `DARKEN` + * - `MULTIPLY` + * - `LINEAR_BURN` + * - `COLOR_BURN` + * + * Lighten: + * + * - `LIGHTEN` + * - `SCREEN` + * - `LINEAR_DODGE` + * - `COLOR_DODGE` + * + * Contrast: + * + * - `OVERLAY` + * - `SOFT_LIGHT` + * - `HARD_LIGHT` + * + * Inversion: + * + * - `DIFFERENCE` + * - `EXCLUSION` + * + * Component: + * + * - `HUE` + * - `SATURATION` + * - `COLOR` + * - `LUMINOSITY` + */ + export type BlendMode = + | 'PASS_THROUGH' + | 'NORMAL' + | 'DARKEN' + | 'MULTIPLY' + | 'LINEAR_BURN' + | 'COLOR_BURN' + | 'LIGHTEN' + | 'SCREEN' + | 'LINEAR_DODGE' + | 'COLOR_DODGE' + | 'OVERLAY' + | 'SOFT_LIGHT' + | 'HARD_LIGHT' + | 'DIFFERENCE' + | 'EXCLUSION' + | 'HUE' + | 'SATURATION' + | 'COLOR' + | 'LUMINOSITY' + + /** + * A 2d vector. + */ + export type Vector = { + /** + * X coordinate of the vector. + */ + x: number + + /** + * Y coordinate of the vector. + */ + y: number + } + + /** + * A single color stop with its position along the gradient axis, color, and bound variables if any + */ + export type ColorStop = { + /** + * Value between 0 and 1 representing position along gradient axis. + */ + position: number + + /** + * Color attached to corresponding position. + */ + color: RGBA + + /** + * The variables bound to a particular gradient stop + */ + boundVariables?: { color?: VariableAlias } + } + + /** + * A transformation matrix is standard way in computer graphics to represent translation and + * rotation. These are the top two rows of a 3x3 matrix. The bottom row of the matrix is assumed to + * be [0, 0, 1]. This is known as an affine transform and is enough to represent translation, + * rotation, and skew. + * + * The identity transform is [[1, 0, 0], [0, 1, 0]]. + * + * A translation matrix will typically look like: + * + * ;[ + * [1, 0, tx], + * [0, 1, ty], + * ] + * + * And a rotation matrix will typically look like: + * + * ;[ + * [cos(angle), sin(angle), 0], + * [-sin(angle), cos(angle), 0], + * ] + * + * Another way to think about this transform is as three vectors: + * + * - The x axis (t[0][0], t[1][0]) + * - The y axis (t[0][1], t[1][1]) + * - The translation offset (t[0][2], t[1][2]) + * + * The most common usage of the Transform matrix is the `relativeTransform property`. This + * particular usage of the matrix has a few additional restrictions. The translation offset can take + * on any value but we do enforce that the axis vectors are unit vectors (i.e. have length 1). The + * axes are not required to be at 90° angles to each other. + */ + export type Transform = number[][] + + /** + * Image filters to apply to the node. + */ + export type ImageFilters = { + exposure?: number + + contrast?: number + + saturation?: number + + temperature?: number + + tint?: number + + highlights?: number + + shadows?: number + } + + export type BasePaint = { + /** + * Is the paint enabled? + */ + visible?: boolean + + /** + * Overall opacity of paint (colors within the paint can also have opacity values which would blend + * with this) + */ + opacity?: number + + /** + * How this node blends with nodes behind it in the scene + */ + blendMode: BlendMode + } + + export type SolidPaint = { + /** + * The string literal "SOLID" representing the paint's type. Always check the `type` before reading + * other properties. + */ + type: 'SOLID' + + /** + * Solid color of the paint + */ + color: RGBA + + /** + * The variables bound to a particular field on this paint + */ + boundVariables?: { color?: VariableAlias } + } & BasePaint + + export type GradientPaint = { + /** + * The string literal representing the paint's type. Always check the `type` before reading other + * properties. + */ + type: 'GRADIENT_LINEAR' | 'GRADIENT_RADIAL' | 'GRADIENT_ANGULAR' | 'GRADIENT_DIAMOND' + + /** + * This field contains three vectors, each of which are a position in normalized object space + * (normalized object space is if the top left corner of the bounding box of the object is (0, 0) + * and the bottom right is (1,1)). The first position corresponds to the start of the gradient + * (value 0 for the purposes of calculating gradient stops), the second position is the end of the + * gradient (value 1), and the third handle position determines the width of the gradient. + */ + gradientHandlePositions: Vector[] + + /** + * Positions of key points along the gradient axis with the colors anchored there. Colors along the + * gradient are interpolated smoothly between neighboring gradient stops. + */ + gradientStops: ColorStop[] + } & BasePaint + + export type ImagePaint = { + /** + * The string literal "IMAGE" representing the paint's type. Always check the `type` before reading + * other properties. + */ + type: 'IMAGE' + + /** + * Image scaling mode. + */ + scaleMode: 'FILL' | 'FIT' | 'TILE' | 'STRETCH' + + /** + * A reference to an image embedded in this node. To download the image using this reference, use + * the `GET file images` endpoint to retrieve the mapping from image references to image URLs. + */ + imageRef: string + + /** + * Affine transform applied to the image, only present if `scaleMode` is `STRETCH` + */ + imageTransform?: Transform + + /** + * Amount image is scaled by in tiling, only present if scaleMode is `TILE`. + */ + scalingFactor?: number + + /** + * Defines what image filters have been applied to this paint, if any. If this property is not + * defined, no filters have been applied. + */ + filters?: ImageFilters + + /** + * Image rotation, in degrees. + */ + rotation?: number + + /** + * A reference to an animated GIF embedded in this node. To download the image using this reference, + * use the `GET file images` endpoint to retrieve the mapping from image references to image URLs. + */ + gifRef?: string + } & BasePaint + + export type Paint = SolidPaint | GradientPaint | ImagePaint + + /** + * Layout constraint relative to containing Frame + */ + export type LayoutConstraint = { + /** + * Vertical constraint (relative to containing frame) as an enum: + * + * - `TOP`: Node is laid out relative to top of the containing frame + * - `BOTTOM`: Node is laid out relative to bottom of the containing frame + * - `CENTER`: Node is vertically centered relative to containing frame + * - `TOP_BOTTOM`: Both top and bottom of node are constrained relative to containing frame (node + * stretches with frame) + * - `SCALE`: Node scales vertically with containing frame + */ + vertical: 'TOP' | 'BOTTOM' | 'CENTER' | 'TOP_BOTTOM' | 'SCALE' + + /** + * Horizontal constraint (relative to containing frame) as an enum: + * + * - `LEFT`: Node is laid out relative to left of the containing frame + * - `RIGHT`: Node is laid out relative to right of the containing frame + * - `CENTER`: Node is horizontally centered relative to containing frame + * - `LEFT_RIGHT`: Both left and right of node are constrained relative to containing frame (node + * stretches with frame) + * - `SCALE`: Node scales horizontally with containing frame + */ + horizontal: 'LEFT' | 'RIGHT' | 'CENTER' | 'LEFT_RIGHT' | 'SCALE' + } + + /** + * A rectangle that expresses a bounding box in absolute coordinates. + */ + export type Rectangle = { + /** + * X coordinate of top left corner of the rectangle. + */ + x: number + + /** + * Y coordinate of top left corner of the rectangle. + */ + y: number + + /** + * Width of the rectangle. + */ + width: number + + /** + * Height of the rectangle. + */ + height: number + } + + /** + * Guides to align and place objects within a frames. + */ + export type LayoutGrid = { + /** + * Orientation of the grid as a string enum + * + * - `COLUMNS`: Vertical grid + * - `ROWS`: Horizontal grid + * - `GRID`: Square grid + */ + pattern: 'COLUMNS' | 'ROWS' | 'GRID' + + /** + * Width of column grid or height of row grid or square grid spacing. + */ + sectionSize: number + + /** + * Is the grid currently visible? + */ + visible: boolean + + /** + * Color of the grid + */ + color: RGBA + + /** + * Positioning of grid as a string enum + * + * - `MIN`: Grid starts at the left or top of the frame + * - `MAX`: Grid starts at the right or bottom of the frame + * - `STRETCH`: Grid is stretched to fit the frame + * - `CENTER`: Grid is center aligned + */ + alignment: 'MIN' | 'MAX' | 'STRETCH' | 'CENTER' + + /** + * Spacing in between columns and rows + */ + gutterSize: number + + /** + * Spacing before the first column or row + */ + offset: number + + /** + * Number of columns or rows + */ + count: number + + /** + * The variables bound to a particular field on this layout grid + */ + boundVariables?: { + gutterSize?: VariableAlias + + numSections?: VariableAlias + + sectionSize?: VariableAlias + + offset?: VariableAlias + } + } + + /** + * Base properties shared by all shadow effects + */ + export type BaseShadowEffect = { + /** + * The color of the shadow + */ + color: RGBA + + /** + * Blend mode of the shadow + */ + blendMode: BlendMode + + /** + * How far the shadow is projected in the x and y directions + */ + offset: Vector + + /** + * Radius of the blur effect (applies to shadows as well) + */ + radius: number + + /** + * The distance by which to expand (or contract) the shadow. + * + * For drop shadows, a positive `spread` value creates a shadow larger than the node, whereas a + * negative value creates a shadow smaller than the node. + * + * For inner shadows, a positive `spread` value contracts the shadow. Spread values are only + * accepted on rectangles and ellipses, or on frames, components, and instances with visible fill + * paints and `clipsContent` enabled. When left unspecified, the default value is 0. + */ + spread?: number + + /** + * Whether this shadow is visible. + */ + visible: boolean + + /** + * The variables bound to a particular field on this shadow effect + */ + boundVariables?: { + radius?: VariableAlias + + spread?: VariableAlias + + color?: VariableAlias + + offsetX?: VariableAlias + + offsetY?: VariableAlias + } + } + + export type DropShadowEffect = { + /** + * A string literal representing the effect's type. Always check the type before reading other + * properties. + */ + type: 'DROP_SHADOW' + + /** + * Whether to show the shadow behind translucent or transparent pixels + */ + showShadowBehindNode: boolean + } & BaseShadowEffect + + export type InnerShadowEffect = { + /** + * A string literal representing the effect's type. Always check the type before reading other + * properties. + */ + type?: 'INNER_SHADOW' + } & BaseShadowEffect + + /** + * A blur effect + */ + export type BlurEffect = { + /** + * A string literal representing the effect's type. Always check the type before reading other + * properties. + */ + type: 'LAYER_BLUR' | 'BACKGROUND_BLUR' + + /** + * Whether this blur is active. + */ + visible: boolean + + /** + * Radius of the blur effect + */ + radius: number + + /** + * The variables bound to a particular field on this blur effect + */ + boundVariables?: { radius?: VariableAlias } + } + + export type Effect = DropShadowEffect | InnerShadowEffect | BlurEffect + + /** + * A set of properties that can be applied to nodes and published. Styles for a property can be + * created in the corresponding property's panel while editing a file. + */ + export type Style = { + /** + * The key of the style + */ + key: string + + /** + * Name of the style + */ + name: string + + /** + * Description of the style + */ + description: string + + /** + * Whether this style is a remote style that doesn't live in this file + */ + remote: boolean + + styleType: StyleType + } + + /** + * This type is a string enum with the following possible values: + * + * - `EASE_IN`: Ease in with an animation curve similar to CSS ease-in. + * - `EASE_OUT`: Ease out with an animation curve similar to CSS ease-out. + * - `EASE_IN_AND_OUT`: Ease in and then out with an animation curve similar to CSS ease-in-out. + * - `LINEAR`: No easing, similar to CSS linear. + * - `EASE_IN_BACK`: Ease in with an animation curve that moves past the initial keyframe's value and + * then accelerates as it reaches the end. + * - `EASE_OUT_BACK`: Ease out with an animation curve that starts fast, then slows and goes past the + * ending keyframe's value. + * - `EASE_IN_AND_OUT_BACK`: Ease in and then out with an animation curve that overshoots the initial + * keyframe's value, then accelerates quickly before it slows and overshoots the ending keyframes + * value. + * - `CUSTOM_CUBIC_BEZIER`: User-defined cubic bezier curve. + * - `GENTLE`: Gentle animation similar to react-spring. + * - `QUICK`: Quick spring animation, great for toasts and notifications. + * - `BOUNCY`: Bouncy spring, for delightful animations like a heart bounce. + * - `SLOW`: Slow spring, useful as a steady, natural way to scale up fullscreen content. + * - `CUSTOM_SPRING`: User-defined spring animation. + */ + export type EasingType = + | 'EASE_IN' + | 'EASE_OUT' + | 'EASE_IN_AND_OUT' + | 'LINEAR' + | 'EASE_IN_BACK' + | 'EASE_OUT_BACK' + | 'EASE_IN_AND_OUT_BACK' + | 'CUSTOM_CUBIC_BEZIER' + | 'GENTLE' + | 'QUICK' + | 'BOUNCY' + | 'SLOW' + | 'CUSTOM_SPRING' + + /** + * Individual stroke weights + */ + export type StrokeWeights = { + /** + * The top stroke weight. + */ + top: number + + /** + * The right stroke weight. + */ + right: number + + /** + * The bottom stroke weight. + */ + bottom: number + + /** + * The left stroke weight. + */ + left: number + } + + /** + * Paint metadata to override default paints. + */ + export type PaintOverride = { + /** + * Paints applied to characters. + */ + fills?: Paint[] + + /** + * ID of style node, if any, that this inherits fill data from. + */ + inheritFillStyleId?: string + } + + /** + * Defines a single path + */ + export type Path = { + /** + * A series of path commands that encodes how to draw the path. + */ + path: string + + /** + * The winding rule for the path (same as in SVGs). This determines whether a given point in space + * is inside or outside the path. + */ + windingRule: 'NONZERO' | 'EVENODD' + + /** + * If there is a per-region fill, this refers to an ID in the `fillOverrideTable`. + */ + overrideID?: number + } + + /** + * Information about the arc properties of an ellipse. 0° is the x axis and increasing angles rotate + * clockwise. + */ + export type ArcData = { + /** + * Start of the sweep in radians. + */ + startingAngle: number + + /** + * End of the sweep in radians. + */ + endingAngle: number + + /** + * Inner radius value between 0 and 1 + */ + innerRadius: number + } + + /** + * A link to either a URL or another frame (node) in the document. + */ + export type Hyperlink = { + /** + * The type of hyperlink. Can be either `URL` or `NODE`. + */ + type: 'URL' | 'NODE' + + /** + * The URL that the hyperlink points to, if `type` is `URL`. + */ + url?: string + + /** + * The ID of the node that the hyperlink points to, if `type` is `NODE`. + */ + nodeID?: string + } + + /** + * Metadata for character formatting. + */ + export type TypeStyle = { + /** + * Font family of text (standard name). + */ + fontFamily?: string + + /** + * PostScript font name. + */ + fontPostScriptName?: string | null + + /** + * Describes visual weight or emphasis, such as Bold or Italic. + */ + fontStyle?: string + + /** + * Space between paragraphs in px, 0 if not present. + */ + paragraphSpacing?: number + + /** + * Paragraph indentation in px, 0 if not present. + */ + paragraphIndent?: number + + /** + * Space between list items in px, 0 if not present. + */ + listSpacing?: number + + /** + * Whether or not text is italicized. + */ + italic?: boolean + + /** + * Numeric font weight. + */ + fontWeight?: number + + /** + * Font size in px. + */ + fontSize?: number + + /** + * Text casing applied to the node, default is the original casing. + */ + textCase?: 'UPPER' | 'LOWER' | 'TITLE' | 'SMALL_CAPS' | 'SMALL_CAPS_FORCED' + + /** + * Text decoration applied to the node, default is none. + */ + textDecoration?: 'NONE' | 'STRIKETHROUGH' | 'UNDERLINE' + + /** + * Dimensions along which text will auto resize, default is that the text does not auto-resize. + * TRUNCATE means that the text will be shortened and trailing text will be replaced with "…" if the + * text contents is larger than the bounds. `TRUNCATE` as a return value is deprecated and will be + * removed in a future version. Read from `textTruncation` instead. + */ + textAutoResize?: 'NONE' | 'WIDTH_AND_HEIGHT' | 'HEIGHT' | 'TRUNCATE' + + /** + * Whether this text node will truncate with an ellipsis when the text contents is larger than the + * text node. + */ + textTruncation?: 'DISABLED' | 'ENDING' + + /** + * When `textTruncation: "ENDING"` is set, `maxLines` determines how many lines a text node can grow + * to before it truncates. + */ + maxLines?: number + + /** + * Horizontal text alignment as string enum. + */ + textAlignHorizontal?: 'LEFT' | 'RIGHT' | 'CENTER' | 'JUSTIFIED' + + /** + * Vertical text alignment as string enum. + */ + textAlignVertical?: 'TOP' | 'CENTER' | 'BOTTOM' + + /** + * Space between characters in px. + */ + letterSpacing?: number + + /** + * An array of fill paints applied to the characters. + */ + fills?: Paint[] + + /** + * Link to a URL or frame. + */ + hyperlink?: Hyperlink + + /** + * A map of OpenType feature flags to 1 or 0, 1 if it is enabled and 0 if it is disabled. Note that + * some flags aren't reflected here. For example, SMCP (small caps) is still represented by the + * `textCase` field. + */ + opentypeFlags?: { [key: string]: number } + + /** + * Line height in px. + */ + lineHeightPx?: number + + /** + * Line height as a percentage of normal line height. This is deprecated; in a future version of the + * API only lineHeightPx and lineHeightPercentFontSize will be returned. + */ + lineHeightPercent?: number + + /** + * Line height as a percentage of the font size. Only returned when `lineHeightPercent` (deprecated) + * is not 100. + */ + lineHeightPercentFontSize?: number + + /** + * The unit of the line height value specified by the user. + */ + lineHeightUnit?: 'PIXELS' | 'FONT_SIZE_%' | 'INTRINSIC_%' + + /** + * The variables bound to a particular field on this style + */ + boundVariables?: { + fontFamily?: VariableAlias + + fontSize?: VariableAlias + + fontStyle?: VariableAlias + + fontWeight?: VariableAlias + + letterSpacing?: VariableAlias + + lineHeight?: VariableAlias + + paragraphSpacing?: VariableAlias + + paragraphIndent?: VariableAlias + } + + /** + * Whether or not this style has overrides over a text style. The possible fields to override are + * semanticWeight, semanticItalic, hyperlink, and textDecoration. If this is true, then those fields + * are overrides if present. + */ + isOverrideOverTextStyle?: boolean + + /** + * Indicates how the font weight was overridden when there is a text style override. + */ + semanticWeight?: 'BOLD' | 'NORMAL' + + /** + * Indicates how the font style was overridden when there is a text style override. + */ + semanticItalic?: 'ITALIC' | 'NORMAL' + } + + /** + * Component property type. + */ + export type ComponentPropertyType = 'BOOLEAN' | 'INSTANCE_SWAP' | 'TEXT' | 'VARIANT' + + /** + * Instance swap preferred value. + */ + export type InstanceSwapPreferredValue = { + /** + * Type of node for this preferred value. + */ + type: 'COMPONENT' | 'COMPONENT_SET' + + /** + * Key of this component or component set. + */ + key: string + } + + /** + * A property of a component. + */ + export type ComponentPropertyDefinition = { + /** + * Type of this component property. + */ + type: ComponentPropertyType + + /** + * Initial value of this property for instances. + */ + defaultValue: boolean | string + + /** + * All possible values for this property. Only exists on VARIANT properties. + */ + variantOptions?: string[] + + /** + * Preferred values for this property. Only applicable if type is `INSTANCE_SWAP`. + */ + preferredValues?: InstanceSwapPreferredValue[] + } + + /** + * A property of a component. + */ + export type ComponentProperty = { + /** + * Type of this component property. + */ + type: ComponentPropertyType + + /** + * Value of the property for this component instance. + */ + value: boolean | string + + /** + * Preferred values for this property. Only applicable if type is `INSTANCE_SWAP`. + */ + preferredValues?: InstanceSwapPreferredValue[] + + /** + * The variables bound to a particular field on this component property + */ + boundVariables?: { value?: VariableAlias } + } + + /** + * Fields directly overridden on an instance. Inherited overrides are not included. + */ + export type Overrides = { + /** + * A unique ID for a node. + */ + id: string + + /** + * An array of properties. + */ + overriddenFields: string[] + } + + /** + * Geometric shape type. + */ + export type ShapeType = + | 'SQUARE' + | 'ELLIPSE' + | 'ROUNDED_RECTANGLE' + | 'DIAMOND' + | 'TRIANGLE_UP' + | 'TRIANGLE_DOWN' + | 'PARALLELOGRAM_RIGHT' + | 'PARALLELOGRAM_LEFT' + | 'ENG_DATABASE' + | 'ENG_QUEUE' + | 'ENG_FILE' + | 'ENG_FOLDER' + | 'TRAPEZOID' + | 'PREDEFINED_PROCESS' + | 'SHIELD' + | 'DOCUMENT_SINGLE' + | 'DOCUMENT_MULTIPLE' + | 'MANUAL_INPUT' + | 'HEXAGON' + | 'CHEVRON' + | 'PENTAGON' + | 'OCTAGON' + | 'STAR' + | 'PLUS' + | 'ARROW_LEFT' + | 'ARROW_RIGHT' + | 'SUMMING_JUNCTION' + | 'OR' + | 'SPEECH_BUBBLE' + | 'INTERNAL_STORAGE' + + /** + * Stores canvas location for a connector start/end point. + */ + export type ConnectorEndpoint = + | { + /** + * Node ID that this endpoint attaches to. + */ + endpointNodeId?: string + + /** + * The position of the endpoint relative to the node. + */ + position?: Vector + } + | { + /** + * Node ID that this endpoint attaches to. + */ + endpointNodeId?: string + + /** + * The magnet type is a string enum. + */ + magnet?: 'AUTO' | 'TOP' | 'BOTTOM' | 'LEFT' | 'RIGHT' | 'CENTER' + } + + /** + * Connector line type. + */ + export type ConnectorLineType = 'STRAIGHT' | 'ELBOWED' + + export type ConnectorTextBackground = CornerTrait & MinimalFillsTrait + + /** + * A description of a main component. Helps you identify which component instances are attached to. + */ + export type Component = { + /** + * The key of the component + */ + key: string + + /** + * Name of the component + */ + name: string + + /** + * The description of the component as entered in the editor + */ + description: string + + /** + * The ID of the component set if the component belongs to one + */ + componentSetId?: string + + /** + * An array of documentation links attached to this component + */ + documentationLinks: DocumentationLink[] + + /** + * Whether this component is a remote component that doesn't live in this file + */ + remote: boolean + } + + /** + * A description of a component set, which is a node containing a set of variants of a component. + */ + export type ComponentSet = { + /** + * The key of the component set + */ + key: string + + /** + * Name of the component set + */ + name: string + + /** + * The description of the component set as entered in the editor + */ + description: string + + /** + * An array of documentation links attached to this component set + */ + documentationLinks?: DocumentationLink[] + + /** + * Whether this component set is a remote component set that doesn't live in this file + */ + remote?: boolean + } + + /** + * Represents a link to documentation for a component or component set. + */ + export type DocumentationLink = { + /** + * Should be a valid URI (e.g. https://www.figma.com). + */ + uri: string + } + + /** + * Contains a variable alias + */ + export type VariableAlias = { + type: 'VARIABLE_ALIAS' + + /** + * The id of the variable that the current variable is aliased to. This variable can be a local or + * remote variable, and both can be retrieved via the GET /v1/files/:file_key/variables/local + * endpoint. + */ + id: string + } + + /** + * An interaction in the Figma viewer, containing a trigger and one or more actions. + */ + export type Interaction = { + /** + * The user event that initiates the interaction. + */ + trigger: Trigger | null + + /** + * The actions that are performed when the trigger is activated. + */ + actions?: Action[] + } + + /** + * The `"ON_HOVER"` and `"ON_PRESS"` trigger types revert the navigation when the trigger is + * finished (the result is temporary). `"MOUSE_ENTER"`, `"MOUSE_LEAVE"`, `"MOUSE_UP"` and + * `"MOUSE_DOWN"` are permanent, one-way navigation. The `delay` parameter requires the trigger to + * be held for a certain duration of time before the action occurs. Both `timeout` and `delay` + * values are in milliseconds. The `"ON_MEDIA_HIT"` and `"ON_MEDIA_END"` trigger types can only + * trigger from a video. They fire when a video reaches a certain time or ends. The `timestamp` + * value is in seconds. + */ + export type Trigger = + | { type: 'ON_CLICK' | 'ON_HOVER' | 'ON_PRESS' | 'ON_DRAG' } + | AfterTimeoutTrigger + | { + type: 'MOUSE_ENTER' | 'MOUSE_LEAVE' | 'MOUSE_UP' | 'MOUSE_DOWN' + + delay: number + + /** + * Whether this is a [deprecated + * version](https://help.figma.com/hc/en-us/articles/360040035834-Prototype-triggers#h_01HHN04REHJNP168R26P1CMP0A) + * of the trigger that was left unchanged for backwards compatibility. If not present, the trigger + * is the latest version. + */ + deprecatedVersion?: boolean + } + | OnKeyDownTrigger + | OnMediaHitTrigger + | { type: 'ON_MEDIA_END' } + + export type AfterTimeoutTrigger = { + type: 'AFTER_TIMEOUT' + + timeout: number + } + + export type OnKeyDownTrigger = { + type: 'ON_KEY_DOWN' + + device: 'KEYBOARD' | 'XBOX_ONE' | 'PS4' | 'SWITCH_PRO' | 'UNKNOWN_CONTROLLER' + + keyCodes: number[] + } + + export type OnMediaHitTrigger = { + type: 'ON_MEDIA_HIT' + + mediaHitTime: number + } + + /** + * An action that is performed when a trigger is activated. + */ + export type Action = + | { type: 'BACK' | 'CLOSE' } + | OpenURLAction + | UpdateMediaRuntimeAction + | SetVariableAction + | SetVariableModeAction + | ConditionalAction + | NodeAction + + /** + * An action that opens a URL. + */ + export type OpenURLAction = { + type: 'URL' + + url: string + } + + /** + * An action that affects a video node in the Figma viewer. For example, to play, pause, or skip. + */ + export type UpdateMediaRuntimeAction = + | { + type: 'UPDATE_MEDIA_RUNTIME' + + destinationId: string | null + + mediaAction: 'PLAY' | 'PAUSE' | 'TOGGLE_PLAY_PAUSE' | 'MUTE' | 'UNMUTE' | 'TOGGLE_MUTE_UNMUTE' + } + | { + type: 'UPDATE_MEDIA_RUNTIME' + + destinationId?: string | null + + mediaAction: 'SKIP_FORWARD' | 'SKIP_BACKWARD' + + amountToSkip: number + } + | { + type: 'UPDATE_MEDIA_RUNTIME' + + destinationId?: string | null + + mediaAction: 'SKIP_TO' + + newTimestamp: number + } + + /** + * An action that navigates to a specific node in the Figma viewer. + */ + export type NodeAction = { + type: 'NODE' + + destinationId: string | null + + navigation: Navigation + + transition: Transition | null + + /** + * Whether the scroll offsets of any scrollable elements in the current screen or overlay are + * preserved when navigating to the destination. This is applicable only if the layout of both the + * current frame and its destination are the same. + */ + preserveScrollPosition?: boolean + + /** + * Applicable only when `navigation` is `"OVERLAY"` and the destination is a frame with + * `overlayPosition` equal to `"MANUAL"`. This value represents the offset by which the overlay is + * opened relative to this node. + */ + overlayRelativePosition?: Vector + + /** + * When true, all videos within the destination frame will reset their memorized playback position + * to 00:00 before starting to play. + */ + resetVideoPosition?: boolean + + /** + * Whether the scroll offsets of any scrollable elements in the current screen or overlay reset when + * navigating to the destination. This is applicable only if the layout of both the current frame + * and its destination are the same. + */ + resetScrollPosition?: boolean + + /** + * Whether the state of any interactive components in the current screen or overlay reset when + * navigating to the destination. This is applicable if there are interactive components in the + * destination frame. + */ + resetInteractiveComponents?: boolean + } + + /** + * The method of navigation. The possible values are: + * + * - `"NAVIGATE"`: Replaces the current screen with the destination, also closing all overlays. + * - `"OVERLAY"`: Opens the destination as an overlay on the current screen. + * - `"SWAP"`: On an overlay, replaces the current (topmost) overlay with the destination. On a + * top-level frame, behaves the same as `"NAVIGATE"` except that no entry is added to the + * navigation history. + * - `"SCROLL_TO"`: Scrolls to the destination on the current screen. + * - `"CHANGE_TO"`: Changes the closest ancestor instance of source node to the specified variant. + */ + export type Navigation = 'NAVIGATE' | 'SWAP' | 'OVERLAY' | 'SCROLL_TO' | 'CHANGE_TO' + + export type Transition = SimpleTransition | DirectionalTransition + + /** + * Describes an animation used when navigating in a prototype. + */ + export type SimpleTransition = { + type: 'DISSOLVE' | 'SMART_ANIMATE' | 'SCROLL_ANIMATE' + + /** + * The duration of the transition in milliseconds. + */ + duration: number + + /** + * The easing curve of the transition. + */ + easing: Easing + } + + /** + * Describes an animation used when navigating in a prototype. + */ + export type DirectionalTransition = { + type: 'MOVE_IN' | 'MOVE_OUT' | 'PUSH' | 'SLIDE_IN' | 'SLIDE_OUT' + + direction: 'LEFT' | 'RIGHT' | 'TOP' | 'BOTTOM' + + /** + * The duration of the transition in milliseconds. + */ + duration: number + + /** + * The easing curve of the transition. + */ + easing: Easing + + /** + * When the transition `type` is `"SMART_ANIMATE"` or when `matchLayers` is `true`, then the + * transition will be performed using smart animate, which attempts to match corresponding layers an + * interpolate other properties during the animation. + */ + matchLayers?: boolean + } + + /** + * Describes an easing curve. + */ + export type Easing = { + /** + * The type of easing curve. + */ + type: EasingType + + /** + * A cubic bezier curve that defines the easing. + */ + easingFunctionCubicBezier?: { + /** + * The x component of the first control point. + */ + x1: number + + /** + * The y component of the first control point. + */ + y1: number + + /** + * The x component of the second control point. + */ + x2: number + + /** + * The y component of the second control point. + */ + y2: number + } + + /** + * A spring function that defines the easing. + */ + easingFunctionSpring?: { + mass: number + + stiffness: number + + damping: number + } + } + + /** + * Sets a variable to a specific value. + */ + export type SetVariableAction = { + type: 'SET_VARIABLE' + + variableId: string | null + + variableValue?: VariableData + } + + /** + * Sets a variable to a specific mode. + */ + export type SetVariableModeAction = { + type: 'SET_VARIABLE_MODE' + + variableCollectionId?: string | null + + variableModeId?: string | null + } + + /** + * Checks if a condition is met before performing certain actions by using an if/else conditional + * statement. + */ + export type ConditionalAction = { + type: 'CONDITIONAL' + + conditionalBlocks: ConditionalBlock[] + } + + /** + * A value to set a variable to during prototyping. + */ + export type VariableData = { + type?: VariableDataType + + resolvedType?: VariableResolvedDataType + + value?: boolean | number | string | RGB | RGBA | VariableAlias | Expression + } + + /** + * Defines the types of data a VariableData object can hold + */ + export type VariableDataType = + | 'BOOLEAN' + | 'FLOAT' + | 'STRING' + | 'COLOR' + | 'VARIABLE_ALIAS' + | 'EXPRESSION' + + /** + * Defines the types of data a VariableData object can eventually equal + */ + export type VariableResolvedDataType = 'BOOLEAN' | 'FLOAT' | 'STRING' | 'COLOR' + + /** + * Defines the [Expression](https://help.figma.com/hc/en-us/articles/15253194385943) object, which + * contains a list of `VariableData` objects strung together by operators (`ExpressionFunction`). + */ + export type Expression = { + expressionFunction: ExpressionFunction + + expressionArguments: VariableData[] + } + + /** + * Defines the list of operators available to use in an Expression. + */ + export type ExpressionFunction = + | 'ADDITION' + | 'SUBTRACTION' + | 'MULTIPLICATION' + | 'DIVISION' + | 'EQUALS' + | 'NOT_EQUAL' + | 'LESS_THAN' + | 'LESS_THAN_OR_EQUAL' + | 'GREATER_THAN' + | 'GREATER_THAN_OR_EQUAL' + | 'AND' + | 'OR' + | 'VAR_MODE_LOOKUP' + | 'NEGATE' + | 'NOT' + + /** + * Either the if or else conditional blocks. The if block contains a condition to check. If that + * condition is met then it will run those list of actions, else it will run the actions in the else + * block. + */ + export type ConditionalBlock = { + condition?: VariableData + + actions: Action[] + } + + /** + * A pinned distance between two nodes in Dev Mode + */ + export type Measurement = { + id: string + + start: MeasurementStartEnd + + end: MeasurementStartEnd + + offset: MeasurementOffsetInner | MeasurementOffsetOuter + + /** + * When manually overridden, the displayed value of the measurement + */ + freeText?: string + } + + /** + * The node and side a measurement is pinned to + */ + export type MeasurementStartEnd = { + nodeId: string + + side: 'TOP' | 'RIGHT' | 'BOTTOM' | 'LEFT' + } + + /** + * Measurement offset relative to the inside of the start node + */ + export type MeasurementOffsetInner = { + type: 'INNER' + + relative: number + } + + /** + * Measurement offset relative to the outside of the start node + */ + export type MeasurementOffsetOuter = { + type: 'OUTER' + + fixed: number + } + + /** + * Position of a comment relative to the frame to which it is attached. + */ + export type FrameOffset = { + /** + * Unique id specifying the frame. + */ + node_id: string + + /** + * 2D vector offset within the frame from the top-left corner. + */ + node_offset: Vector + } + + /** + * Position of a region comment on the canvas. + */ + export type Region = { + /** + * X coordinate of the position. + */ + x: number + + /** + * Y coordinate of the position. + */ + y: number + + /** + * The height of the comment region. Must be greater than 0. + */ + region_height: number + + /** + * The width of the comment region. Must be greater than 0. + */ + region_width: number + + /** + * The corner of the comment region to pin to the node's corner as a string enum. + */ + comment_pin_corner?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' + } + + /** + * Position of a region comment relative to the frame to which it is attached. + */ + export type FrameOffsetRegion = { + /** + * Unique id specifying the frame. + */ + node_id: string + + /** + * 2D vector offset within the frame from the top-left corner. + */ + node_offset: Vector + + /** + * The height of the comment region. Must be greater than 0. + */ + region_height: number + + /** + * The width of the comment region. Must be greater than 0. + */ + region_width: number + + /** + * The corner of the comment region to pin to the node's corner as a string enum. + */ + comment_pin_corner?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' + } + + /** + * A comment or reply left by a user. + */ + export type Comment = { + /** + * Unique identifier for comment. + */ + id: string + + /** + * Positioning information of the comment. Includes information on the location of the comment pin, + * which is either the absolute coordinates on the canvas or a relative offset within a frame. If + * the comment is a region, it will also contain the region height, width, and position of the + * anchor in regards to the region. + */ + client_meta: Vector | FrameOffset | Region | FrameOffsetRegion + + /** + * The file in which the comment lives + */ + file_key: string + + /** + * If present, the id of the comment to which this is the reply + */ + parent_id?: string + + /** + * The user who left the comment + */ + user: User + + /** + * The UTC ISO 8601 time at which the comment was left + */ + created_at: string + + /** + * If set, the UTC ISO 8601 time the comment was resolved + */ + resolved_at?: string | null + + /** + * The content of the comment + */ + message: string + + /** + * Only set for top level comments. The number displayed with the comment in the UI + */ + order_id: string | null + + /** + * An array of reactions to the comment + */ + reactions: Reaction[] + } + + /** + * A reaction left by a user. + */ + export type Reaction = { + /** + * The user who left the reaction. + */ + user: User + + emoji: Emoji + + /** + * The UTC ISO 8601 time at which the reaction was left. + */ + created_at: string + } + + /** + * The emoji type of reaction as shortcode (e.g. `:heart:`, `:+1::skin-tone-2:`). The list of + * accepted emoji shortcodes can be found in [this + * file](https://raw.githubusercontent.com/missive/emoji-mart/main/packages/emoji-mart-data/sets/14/native.json) + * under the top-level emojis and aliases fields, with optional skin tone modifiers when + * applicable. + */ + export type Emoji = string + + /** + * A description of a user. + */ + export type User = { + /** + * Unique stable id of the user. + */ + id: string + + /** + * Name of the user. + */ + handle: string + + /** + * URL link to the user's profile image. + */ + img_url: string + } + + /** + * Data on the frame a component resides in. + */ + export type FrameInfo = { + /** + * The ID of the frame node within the file. + */ + nodeId?: string + + /** + * The name of the frame node. + */ + name?: string + + /** + * The background color of the frame node. + */ + backgroundColor?: string + + /** + * The ID of the page containing the frame node. + */ + pageId: string + + /** + * The name of the page containing the frame node. + */ + pageName: string + } + + /** + * An arrangement of published UI elements that can be instantiated across figma files. + */ + export type PublishedComponent = { + /** + * The unique identifier for the component. + */ + key: string + + /** + * The unique identifier of the Figma file that contains the component. + */ + file_key: string + + /** + * The unique identifier of the component node within the Figma file. + */ + node_id: string + + /** + * A URL to a thumbnail image of the component. + */ + thumbnail_url?: string + + /** + * The name of the component. + */ + name: string + + /** + * The description of the component as entered by the publisher. + */ + description: string + + /** + * The UTC ISO 8601 time when the component was created. + */ + created_at: string + + /** + * The UTC ISO 8601 time when the component was last updated. + */ + updated_at: string + + /** + * The user who last updated the component. + */ + user: User + + /** + * The containing frame of the component. + */ + containing_frame?: FrameInfo + } + + /** + * A node containing a set of variants of a component. + */ + export type PublishedComponentSet = { + /** + * The unique identifier for the component set. + */ + key: string + + /** + * The unique identifier of the Figma file that contains the component set. + */ + file_key: string + + /** + * The unique identifier of the component set node within the Figma file. + */ + node_id: string + + /** + * A URL to a thumbnail image of the component set. + */ + thumbnail_url?: string + + /** + * The name of the component set. + */ + name: string + + /** + * The description of the component set as entered by the publisher. + */ + description: string + + /** + * The UTC ISO 8601 time when the component set was created. + */ + created_at: string + + /** + * The UTC ISO 8601 time when the component set was last updated. + */ + updated_at: string + + /** + * The user who last updated the component set. + */ + user: User + + /** + * The containing frame of the component set. + */ + containing_frame?: FrameInfo + } + + /** + * The type of style + */ + export type StyleType = 'FILL' | 'TEXT' | 'EFFECT' | 'GRID' + + /** + * A set of published properties that can be applied to nodes. + */ + export type PublishedStyle = { + /** + * The unique identifier for the style + */ + key: string + + /** + * The unique identifier of the Figma file that contains the style. + */ + file_key: string + + /** + * ID of the style node within the figma file + */ + node_id: string + + style_type: StyleType + + /** + * A URL to a thumbnail image of the style. + */ + thumbnail_url?: string + + /** + * The name of the style. + */ + name: string + + /** + * The description of the style as entered by the publisher. + */ + description: string + + /** + * The UTC ISO 8601 time when the style was created. + */ + created_at: string + + /** + * The UTC ISO 8601 time when the style was last updated. + */ + updated_at: string + + /** + * The user who last updated the style. + */ + user: User + + /** + * A user specified order number by which the style can be sorted. + */ + sort_position: string + } + + /** + * A Project can be identified by both the Project name, and the Project ID. + */ + export type Project = { + /** + * The ID of the project. + */ + id: string + + /** + * The name of the project. + */ + name: string + } + + /** + * A version of a file + */ + export type Version = { + /** + * Unique identifier for version + */ + id: string + + /** + * The UTC ISO 8601 time at which the version was created + */ + created_at: string + + /** + * The label given to the version in the editor + */ + label: string | null + + /** + * The description of the version as entered in the editor + */ + description: string | null + + /** + * The user that created the version + */ + user: User + + /** + * A URL to a thumbnail image of the file version. + */ + thumbnail_url?: string + } + + /** + * A description of an HTTP webhook (from Figma back to your application) + */ + export type WebhookV2 = { + /** + * The ID of the webhook + */ + id: string + + /** + * The event this webhook triggers on + */ + event_type: WebhookV2Event + + /** + * The team id you are subscribed to for updates + */ + team_id: string + + /** + * The current status of the webhook + */ + status: WebhookV2Status + + /** + * The client ID of the OAuth application that registered this webhook, if any + */ + client_id: string | null + + /** + * The passcode that will be passed back to the webhook endpoint + */ + passcode: string + + /** + * The endpoint that will be hit when the webhook is triggered + */ + endpoint: string + + /** + * Optional user-provided description or name for the webhook. This is provided to help make + * maintaining a number of webhooks more convenient. Max length 140 characters. + */ + description: string | null + } + + /** + * An enum representing the possible events that a webhook can subscribe to + */ + export type WebhookV2Event = + | 'PING' + | 'FILE_UPDATE' + | 'FILE_VERSION_UPDATE' + | 'FILE_DELETE' + | 'LIBRARY_PUBLISH' + | 'FILE_COMMENT' + + /** + * An enum representing the possible statuses you can set a webhook to: + * + * - `ACTIVE`: The webhook is healthy and receive all events + * - `PAUSED`: The webhook is paused and will not receive any events + */ + export type WebhookV2Status = 'ACTIVE' | 'PAUSED' + + /** + * Information regarding the most recent interactions sent to a webhook endpoint + */ + export type WebhookV2Request = { + /** + * The ID of the webhook the requests were sent to + */ + webhook_id: string + + request_info: WebhookV2RequestInfo + + response_info: WebhookV2ResponseInfo + + /** + * Error message for this request. NULL if no error occurred + */ + error_msg: string | null + } + + /** + * Information regarding the request sent to a webhook endpoint + */ + export type WebhookV2RequestInfo = { + /** + * The ID of the webhook + */ + id: string + + /** + * The actual endpoint the request was sent to + */ + endpoint: string + + /** + * The contents of the request that was sent to the endpoint + */ + payload: object + + /** + * UTC ISO 8601 timestamp of when the request was sent + */ + sent_at: string + } + + /** + * Information regarding the reply sent back from a webhook endpoint + */ + export type WebhookV2ResponseInfo = object | null + + /** + * An object representing the library item information in the payload of the `LIBRARY_PUBLISH` event + */ + export type LibraryItemData = { + /** + * Unique identifier for the library item + */ + key: string + + /** + * Name of the library item + */ + name: string + } + + /** + * An object representing a fragment of a comment left by a user, used in the payload of the + * `FILE_COMMENT` event. Note only ONE of the fields below will be set + */ + export type CommentFragment = { + /** + * Comment text that is set if a fragment is text based + */ + text?: string + + /** + * User id that is set if a fragment refers to a user mention + */ + mention?: string + } + + export type WebhookBasePayload = { + /** + * The passcode specified when the webhook was created, should match what was initially provided + */ + passcode: string + + /** + * UTC ISO 8601 timestamp of when the event was triggered. + */ + timestamp: string + + /** + * The id of the webhook that caused the callback + */ + webhook_id: string + } + + export type WebhookPingPayload = WebhookBasePayload & { event_type: 'PING' } + + export type WebhookFileUpdatePayload = WebhookBasePayload & { + event_type: 'FILE_UPDATE' + + /** + * The key of the file that was updated + */ + file_key: string + + /** + * The name of the file that was updated + */ + file_name: string + } + + export type WebhookFileDeletePayload = WebhookBasePayload & { + event_type: 'FILE_DELETE' + + /** + * The key of the file that was deleted + */ + file_key: string + + /** + * The name of the file that was deleted + */ + file_name: string + + /** + * The user that deleted the file and triggered this event + */ + triggered_by: User + } + + export type WebhookFileVersionUpdatePayload = WebhookBasePayload & { + event_type: 'FILE_VERSION_UPDATE' + + /** + * UTC ISO 8601 timestamp of when the version was created + */ + created_at: string + + /** + * Description of the version in the version history + */ + description?: string + + /** + * The key of the file that was updated + */ + file_key: string + + /** + * The name of the file that was updated + */ + file_name: string + + /** + * The user that created the named version and triggered this event + */ + triggered_by: User + + /** + * ID of the published version + */ + version_id: string + } + + export type WebhookLibraryPublishPayload = WebhookBasePayload & { + event_type: 'LIBRARY_PUBLISH' + + /** + * Components that were created by the library publish + */ + created_components: LibraryItemData[] + + /** + * Styles that were created by the library publish + */ + created_styles: LibraryItemData[] + + /** + * Variables that were created by the library publish + */ + created_variables: LibraryItemData[] + + /** + * Components that were modified by the library publish + */ + modified_components: LibraryItemData[] + + /** + * Styles that were modified by the library publish + */ + modified_styles: LibraryItemData[] + + /** + * Variables that were modified by the library publish + */ + modified_variables: LibraryItemData[] + + /** + * Components that were deleted by the library publish + */ + deleted_components: LibraryItemData[] + + /** + * Styles that were deleted by the library publish + */ + deleted_styles: LibraryItemData[] + + /** + * Variables that were deleted by the library publish + */ + deleted_variables: LibraryItemData[] + + /** + * Description of the library publish + */ + description?: string + + /** + * The key of the file that was published + */ + file_key: string + + /** + * The name of the file that was published + */ + file_name: string + + /** + * The library item that was published + */ + library_item: LibraryItemData + + /** + * The user that published the library and triggered this event + */ + triggered_by: User + } + + export type WebhookFileCommentPayload = WebhookBasePayload & { + event_type: 'FILE_COMMENT' + + /** + * Contents of the comment itself + */ + comment: CommentFragment[] + + /** + * Unique identifier for comment + */ + comment_id: string + + /** + * The UTC ISO 8601 time at which the comment was left + */ + created_at: string + + /** + * The key of the file that was commented on + */ + file_key: string + + /** + * The name of the file that was commented on + */ + file_name: string + + /** + * Users that were mentioned in the comment + */ + mentions?: User[] + + /** + * The user that made the comment and triggered this event + */ + triggered_by: User + } + + /** + * A Figma user + */ + export type ActivityLogUserEntity = { + /** + * The type of entity. + */ + type: 'user' + + /** + * Unique stable id of the user. + */ + id: string + + /** + * Name of the user. + */ + name: string + + /** + * Email associated with the user's account. + */ + email: string + } + + /** + * A Figma Design or FigJam file + */ + export type ActivityLogFileEntity = { + /** + * The type of entity. + */ + type: 'file' + + /** + * Unique identifier of the file. + */ + key: string + + /** + * Name of the file. + */ + name: string + + /** + * Indicates if the object is a file on Figma Design or FigJam. + */ + editor_type: 'figma' | 'figjam' + + /** + * Access policy for users who have the link to the file. + */ + link_access: 'view' | 'edit' | 'org_view' | 'org_edit' | 'inherit' + + /** + * Access policy for users who have the link to the file's prototype. + */ + proto_link_access: 'view' | 'org_view' | 'inherit' + } + + /** + * A file branch that diverges from and can be merged back into the main file + */ + export type ActivityLogFileRepoEntity = { + /** + * The type of entity. + */ + type: 'file_repo' + + /** + * Unique identifier of the file branch. + */ + id: string + + /** + * Name of the file. + */ + name: string + + /** + * Key of the main file. + */ + main_file_key: string + } + + /** + * A project that a collection of Figma files are grouped under + */ + export type ActivityLogProjectEntity = { + /** + * The type of entity. + */ + type: 'project' + + /** + * Unique identifier of the project. + */ + id: string + + /** + * Name of the project. + */ + name: string + } + + /** + * A Figma team that contains multiple users and projects + */ + export type ActivityLogTeamEntity = { + /** + * The type of entity. + */ + type: 'team' + + /** + * Unique identifier of the team. + */ + id: string + + /** + * Name of the team. + */ + name: string + } + + /** + * Part of the organizational hierarchy of managing files and users within Figma, only available on + * the Enterprise Plan + */ + export type ActivityLogWorkspaceEntity = { + /** + * The type of entity. + */ + type: 'workspace' + + /** + * Unique identifier of the workspace. + */ + id: string + + /** + * Name of the workspace. + */ + name: string + } + + /** + * A Figma organization + */ + export type ActivityLogOrgEntity = { + /** + * The type of entity. + */ + type: 'org' + + /** + * Unique identifier of the organization. + */ + id: string + + /** + * Name of the organization. + */ + name: string + } + + /** + * A Figma plugin + */ + export type ActivityLogPluginEntity = { + /** + * The type of entity. + */ + type: 'plugin' + + /** + * Unique identifier of the plugin. + */ + id: string + + /** + * Name of the plugin. + */ + name: string + + /** + * Indicates if the object is a plugin is available on Figma Design or FigJam. + */ + editor_type: 'figma' | 'figjam' + } + + /** + * A Figma widget + */ + export type ActivityLogWidgetEntity = { + /** + * The type of entity. + */ + type: 'widget' + + /** + * Unique identifier of the widget. + */ + id: string + + /** + * Name of the widget. + */ + name: string + + /** + * Indicates if the object is a widget available on Figma Design or FigJam. + */ + editor_type: 'figma' | 'figjam' + } + + /** + * An event returned by the Activity Logs API. + */ + export type ActivityLog = { + /** + * The ID of the event. + */ + id: string + + /** + * The timestamp of the event in seconds since the Unix epoch. + */ + timestamp: number + + /** + * The user who performed the action. + */ + actor: object | null + + /** + * The task or activity the actor performed. + */ + action: { + /** + * The type of the action. + */ + type: string + + /** + * Metadata of the action. Each action type supports its own metadata attributes. + */ + details: object | null + } + + /** + * The resource the actor took the action on. It can be a user, file, project or other resource + * types. + */ + entity: + | ActivityLogUserEntity + | ActivityLogFileEntity + | ActivityLogFileRepoEntity + | ActivityLogProjectEntity + | ActivityLogTeamEntity + | ActivityLogWorkspaceEntity + | ActivityLogOrgEntity + | ActivityLogPluginEntity + | ActivityLogWidgetEntity + + /** + * Contextual information about the event. + */ + context: { + /** + * The third-party application that triggered the event, if applicable. + */ + client_name: string | null + + /** + * The IP address from of the client that sent the event request. + */ + ip_address: string + + /** + * If Figma's Support team triggered the event. This is either true or false. + */ + is_figma_support_team_action: boolean + + /** + * The id of the organization where the event took place. + */ + org_id: string + + /** + * The id of the team where the event took place -- if this took place in a specific team. + */ + team_id: string | null + } + } + + /** + * An object describing the user's payment status. + */ + export type PaymentStatus = { + /** + * The current payment status of the user on the resource, as a string enum: + * + * - `UNPAID`: user has not paid for the resource + * - `PAID`: user has an active purchase on the resource + * - `TRIAL`: user is in the trial period for a subscription resource + */ + type?: 'UNPAID' | 'PAID' | 'TRIAL' + } + + /** + * An object describing a user's payment information for a plugin, widget, or Community file. + */ + export type PaymentInformation = { + /** + * The ID of the user whose payment information was queried. Can be used to verify the validity of a + * response. + */ + user_id: string + + /** + * The ID of the plugin, widget, or Community file that was queried. Can be used to verify the + * validity of a response. + */ + resource_id: string + + /** + * The type of the resource. + */ + resource_type: 'PLUGIN' | 'WIDGET' | 'COMMUNITY_FILE' + + payment_status: PaymentStatus + + /** + * The UTC ISO 8601 timestamp indicating when the user purchased the resource. No value is given if + * the user has never purchased the resource. + * + * Note that a value will still be returned if the user had purchased the resource, but no longer + * has active access to it (e.g. purchase refunded, subscription ended). + */ + date_of_purchase?: string + } + + /** + * Scopes allow a variable to be shown or hidden in the variable picker for various fields. This + * declutters the Figma UI if you have a large number of variables. Variable scopes are currently + * supported on `FLOAT`, `STRING`, and `COLOR` variables. + * + * `ALL_SCOPES` is a special scope that means that the variable will be shown in the variable picker + * for all variable fields. If `ALL_SCOPES` is set, no additional scopes can be set. + * + * `ALL_FILLS` is a special scope that means that the variable will be shown in the variable picker + * for all fill fields. If `ALL_FILLS` is set, no additional fill scopes can be set. + * + * Valid scopes for `FLOAT` variables: + * + * - `ALL_SCOPES` + * - `TEXT_CONTENT` + * - `WIDTH_HEIGHT` + * - `GAP` + * - `STROKE_FLOAT` + * - `EFFECT_FLOAT` + * - `OPACITY` + * - `FONT_WEIGHT` + * - `FONT_SIZE` + * - `LINE_HEIGHT` + * - `LETTER_SPACING` + * - `PARAGRAPH_SPACING` + * - `PARAGRAPH_INDENT` + * + * Valid scopes for `STRING` variables: + * + * - `ALL_SCOPES` + * - `TEXT_CONTENT` + * - `FONT_FAMILY` + * - `FONT_STYLE` + * + * Valid scopes for `COLOR` variables: + * + * - `ALL_SCOPES` + * - `ALL_FILLS` + * - `FRAME_FILL` + * - `SHAPE_FILL` + * - `TEXT_FILL` + * - `STROKE_COLOR` + * - `EFFECT_COLOR` + */ + export type VariableScope = + | 'ALL_SCOPES' + | 'TEXT_CONTENT' + | 'CORNER_RADIUS' + | 'WIDTH_HEIGHT' + | 'GAP' + | 'ALL_FILLS' + | 'FRAME_FILL' + | 'SHAPE_FILL' + | 'TEXT_FILL' + | 'STROKE_COLOR' + | 'STROKE_FLOAT' + | 'EFFECT_FLOAT' + | 'EFFECT_COLOR' + | 'OPACITY' + | 'FONT_FAMILY' + | 'FONT_STYLE' + | 'FONT_WEIGHT' + | 'FONT_SIZE' + | 'LINE_HEIGHT' + | 'LETTER_SPACING' + | 'PARAGRAPH_SPACING' + | 'PARAGRAPH_INDENT' + + /** + * An object containing platform-specific code syntax definitions for a variable. All platforms are + * optional. + */ + export type VariableCodeSyntax = { + WEB?: string + + ANDROID?: string + + iOS?: string + } + + /** + * A grouping of related Variable objects each with the same modes. + */ + export type LocalVariableCollection = { + /** + * The unique identifier of this variable collection. + */ + id: string + + /** + * The name of this variable collection. + */ + name: string + + /** + * The key of this variable collection. + */ + key: string + + /** + * The modes of this variable collection. + */ + modes: { + /** + * The unique identifier of this mode. + */ + modeId: string + + /** + * The name of this mode. + */ + name: string + }[] + + /** + * The id of the default mode. + */ + defaultModeId: string + + /** + * Whether this variable collection is remote. + */ + remote: boolean + + /** + * Whether this variable collection is hidden when publishing the current file as a library. + */ + hiddenFromPublishing: boolean + + /** + * The ids of the variables in the collection. Note that the order of these variables is roughly the + * same as what is shown in Figma Design, however it does not account for groups. As a result, the + * order of these variables may not exactly reflect the exact ordering and grouping shown in the + * authoring UI. + */ + variableIds: string[] + } + + /** + * A Variable is a single design token that defines values for each of the modes in its + * VariableCollection. These values can be applied to various kinds of design properties. + */ + export type LocalVariable = { + /** + * The unique identifier of this variable. + */ + id: string + + /** + * The name of this variable. + */ + name: string + + /** + * The key of this variable. + */ + key: string + + /** + * The id of the variable collection that contains this variable. + */ + variableCollectionId: string + + /** + * The resolved type of the variable. + */ + resolvedType: 'BOOLEAN' | 'FLOAT' | 'STRING' | 'COLOR' + + /** + * The values for each mode of this variable. + */ + valuesByMode: { [key: string]: boolean | number | string | RGBA | VariableAlias } + + /** + * Whether this variable is remote. + */ + remote: boolean + + /** + * The description of this variable. + */ + description: string + + /** + * Whether this variable is hidden when publishing the current file as a library. + * + * If the parent `VariableCollection` is marked as `hiddenFromPublishing`, then this variable will + * also be hidden from publishing via the UI. `hiddenFromPublishing` is independently toggled for a + * variable and collection. However, both must be true for a given variable to be publishable. + */ + hiddenFromPublishing: boolean + + /** + * An array of scopes in the UI where this variable is shown. Setting this property will show/hide + * this variable in the variable picker UI for different fields. + * + * Setting scopes for a variable does not prevent that variable from being bound in other scopes + * (for example, via the Plugin API). This only limits the variables that are shown in pickers + * within the Figma UI. + */ + scopes: VariableScope[] + + codeSyntax: VariableCodeSyntax + + /** + * Indicates that the variable was deleted in the editor, but the document may still contain + * references to the variable. References to the variable may exist through bound values or variable + * aliases. + */ + deletedButReferenced?: boolean + } + + /** + * A grouping of related Variable objects each with the same modes. + */ + export type PublishedVariableCollection = { + /** + * The unique identifier of this variable collection. + */ + id: string + + /** + * The ID of the variable collection that is used by subscribing files. This ID changes every time + * the variable collection is modified and published. + */ + subscribed_id: string + + /** + * The name of this variable collection. + */ + name: string + + /** + * The key of this variable collection. + */ + key: string + + /** + * The UTC ISO 8601 time at which the variable collection was last updated. + * + * This timestamp will change any time a variable in the collection is changed. + */ + updatedAt: string + } + + /** + * A Variable is a single design token that defines values for each of the modes in its + * VariableCollection. These values can be applied to various kinds of design properties. + */ + export type PublishedVariable = { + /** + * The unique identifier of this variable. + */ + id: string + + /** + * The ID of the variable that is used by subscribing files. This ID changes every time the variable + * is modified and published. + */ + subscribed_id: string + + /** + * The name of this variable. + */ + name: string + + /** + * The key of this variable. + */ + key: string + + /** + * The id of the variable collection that contains this variable. + */ + variableCollectionId: string + + /** + * The resolved type of the variable. + */ + resolvedDataType: 'BOOLEAN' | 'FLOAT' | 'STRING' | 'COLOR' + + /** + * The UTC ISO 8601 time at which the variable was last updated. + */ + updatedAt: string + } + + /** + * An object that contains details about creating a `VariableCollection`. + */ + export type VariableCollectionCreate = { + /** + * The action to perform for the variable collection. + */ + action: 'CREATE' + + /** + * A temporary id for this variable collection. + */ + id?: string + + /** + * The name of this variable collection. + */ + name: string + + /** + * The initial mode refers to the mode that is created by default. You can set a temporary id here, + * in order to reference this mode later in this request. + */ + initialModeId?: string + + /** + * Whether this variable collection is hidden when publishing the current file as a library. + */ + hiddenFromPublishing?: boolean + } + + /** + * An object that contains details about updating a `VariableCollection`. + */ + export type VariableCollectionUpdate = { + /** + * The action to perform for the variable collection. + */ + action: 'UPDATE' + + /** + * The id of the variable collection to update. + */ + id: string + + /** + * The name of this variable collection. + */ + name?: string + + /** + * Whether this variable collection is hidden when publishing the current file as a library. + */ + hiddenFromPublishing?: boolean + } + + /** + * An object that contains details about deleting a `VariableCollection`. + */ + export type VariableCollectionDelete = { + /** + * The action to perform for the variable collection. + */ + action: 'DELETE' + + /** + * The id of the variable collection to delete. + */ + id: string + } + + export type VariableCollectionChange = + | VariableCollectionCreate + | VariableCollectionUpdate + | VariableCollectionDelete + + /** + * An object that contains details about creating a `VariableMode`. + */ + export type VariableModeCreate = { + /** + * The action to perform for the variable mode. + */ + action: 'CREATE' + + /** + * A temporary id for this variable mode. + */ + id?: string + + /** + * The name of this variable mode. + */ + name: string + + /** + * The variable collection that will contain the mode. You can use the temporary id of a variable + * collection. + */ + variableCollectionId: string + } + + /** + * An object that contains details about updating a `VariableMode`. + */ + export type VariableModeUpdate = { + /** + * The action to perform for the variable mode. + */ + action: 'UPDATE' + + /** + * The id of the variable mode to update. + */ + id: string + + /** + * The name of this variable mode. + */ + name?: string + + /** + * The variable collection that contains the mode. + */ + variableCollectionId: string + } + + /** + * An object that contains details about deleting a `VariableMode`. + */ + export type VariableModeDelete = { + /** + * The action to perform for the variable mode. + */ + action: 'DELETE' + + /** + * The id of the variable mode to delete. + */ + id: string + } + + export type VariableModeChange = VariableModeCreate | VariableModeUpdate | VariableModeDelete + + /** + * An object that contains details about creating a `Variable`. + */ + export type VariableCreate = { + /** + * The action to perform for the variable. + */ + action: 'CREATE' + + /** + * A temporary id for this variable. + */ + id?: string + + /** + * The name of this variable. + */ + name: string + + /** + * The variable collection that will contain the variable. You can use the temporary id of a + * variable collection. + */ + variableCollectionId: string + + /** + * The resolved type of the variable. + */ + resolvedType: 'BOOLEAN' | 'FLOAT' | 'STRING' | 'COLOR' + + /** + * The description of this variable. + */ + description?: string + + /** + * Whether this variable is hidden when publishing the current file as a library. + */ + hiddenFromPublishing?: boolean + + /** + * An array of scopes in the UI where this variable is shown. Setting this property will show/hide + * this variable in the variable picker UI for different fields. + */ + scopes?: VariableScope[] + + codeSyntax?: VariableCodeSyntax + } + + /** + * An object that contains details about updating a `Variable`. + */ + export type VariableUpdate = { + /** + * The action to perform for the variable. + */ + action: 'UPDATE' + + /** + * The id of the variable to update. + */ + id: string + + /** + * The name of this variable. + */ + name?: string + + /** + * The description of this variable. + */ + description?: string + + /** + * Whether this variable is hidden when publishing the current file as a library. + */ + hiddenFromPublishing?: boolean + + /** + * An array of scopes in the UI where this variable is shown. Setting this property will show/hide + * this variable in the variable picker UI for different fields. + */ + scopes?: VariableScope[] + + codeSyntax?: VariableCodeSyntax + } + + /** + * An object that contains details about deleting a `Variable`. + */ + export type VariableDelete = { + /** + * The action to perform for the variable. + */ + action: 'DELETE' + + /** + * The id of the variable to delete. + */ + id: string + } + + export type VariableChange = VariableCreate | VariableUpdate | VariableDelete + + /** + * An object that represents a value for a given mode of a variable. All properties are required. + */ + export type VariableModeValue = { + /** + * The target variable. You can use the temporary id of a variable. + */ + variableId: string + + /** + * Must correspond to a mode in the variable collection that contains the target variable. + */ + modeId: string + + value: VariableValue + } + + /** + * The value for the variable. The value must match the variable's type. If setting to a variable + * alias, the alias must resolve to this type. + */ + export type VariableValue = boolean | number | string | RGB | RGBA | VariableAlias + + /** + * A dev resource in a file + */ + export type DevResource = { + /** + * Unique identifier of the dev resource + */ + id: string + + /** + * The name of the dev resource. + */ + name: string + + /** + * The URL of the dev resource. + */ + url: string + + /** + * The file key where the dev resource belongs. + */ + file_key: string + + /** + * The target node to attach the dev resource to. + */ + node_id: string + } + + /** + * Library analytics component actions data broken down by asset. + */ + export type LibraryAnalyticsComponentActionsByAsset = { + /** + * The date in ISO 8601 format. e.g. 2023-12-13 + */ + week: string + + /** + * Unique, stable id of the component. + */ + component_key: string + + /** + * Name of the component. + */ + component_name: string + + /** + * Unique, stable id of the component set that this component belongs to. + */ + component_set_key?: string + + /** + * Name of the component set that this component belongs to. + */ + component_set_name?: string + + /** + * The number of detach events for this period. + */ + detachments: number + + /** + * The number of insertion events for this period. + */ + insertions: number + } + + /** + * Library analytics action data broken down by team. + */ + export type LibraryAnalyticsComponentActionsByTeam = { + /** + * The date in ISO 8601 format. e.g. 2023-12-13 + */ + week: string + + /** + * The name of the team using the library. + */ + team_name: string + + /** + * The name of the workspace that the team belongs to. + */ + workspace_name?: string + + /** + * The number of detach events for this period. + */ + detachments: number + + /** + * The number of insertion events for this period. + */ + insertions: number + } + + /** + * Library analytics component usage data broken down by component. + */ + export type LibraryAnalyticsComponentUsagesByAsset = { + /** + * Unique, stable id of the component. + */ + component_key: string + + /** + * Name of the component. + */ + component_name: string + + /** + * Unique, stable id of the component set that this component belongs to. + */ + component_set_key?: string + + /** + * Name of the component set that this component belongs to. + */ + component_set_name?: string + + /** + * The number of instances of the component within the organization. + */ + usages: number + + /** + * The number of teams using the component within the organization. + */ + teams_using: number + + /** + * The number of files using the component within the organization. + */ + files_using: number + } + + /** + * Library analytics component usage data broken down by file. + */ + export type LibraryAnalyticsComponentUsagesByFile = { + /** + * The name of the file using the library. + */ + file_name: string + + /** + * The name of the team the file belongs to. + */ + team_name: string + + /** + * The name of the workspace that the file belongs to. + */ + workspace_name?: string + + /** + * The number of component instances from the library used within the file. + */ + usages: number + } + + /** + * Library analytics style actions data broken down by asset. + */ + export type LibraryAnalyticsStyleActionsByAsset = { + /** + * The date in ISO 8601 format. e.g. 2023-12-13 + */ + week: string + + /** + * Unique, stable id of the style. + */ + style_key: string + + /** + * The name of the style. + */ + style_name: string + + /** + * The type of the style. + */ + style_type: string + + /** + * The number of detach events for this period. + */ + detachments: number + + /** + * The number of insertion events for this period. + */ + insertions: number + } + + /** + * Library analytics style action data broken down by team. + */ + export type LibraryAnalyticsStyleActionsByTeam = { + /** + * The date in ISO 8601 format. e.g. 2023-12-13 + */ + week: string + + /** + * The name of the team using the library. + */ + team_name: string + + /** + * The name of the workspace that the team belongs to. + */ + workspace_name?: string + + /** + * The number of detach events for this period. + */ + detachments: number + + /** + * The number of insertion events for this period. + */ + insertions: number + } + + /** + * Library analytics style usage data broken down by component. + */ + export type LibraryAnalyticsStyleUsagesByAsset = { + /** + * Unique, stable id of the style. + */ + style_key: string + + /** + * The name of the style. + */ + style_name: string + + /** + * The type of the style. + */ + style_type: string + + /** + * The number of usages of the style within the organization. + */ + usages: number + + /** + * The number of teams using the style within the organization. + */ + teams_using: number + + /** + * The number of files using the style within the organization. + */ + files_using: number + } + + /** + * Library analytics style usage data broken down by file. + */ + export type LibraryAnalyticsStyleUsagesByFile = { + /** + * The name of the file using the library. + */ + file_name: string + + /** + * The name of the team the file belongs to. + */ + team_name: string + + /** + * The name of the workspace that the file belongs to. + */ + workspace_name?: string + + /** + * The number of times styles from this library are used within the file. + */ + usages: number + } + + /** + * Library analytics variable actions data broken down by asset. + */ + export type LibraryAnalyticsVariableActionsByAsset = { + /** + * The date in ISO 8601 format. e.g. 2023-12-13 + */ + week: string + + /** + * Unique, stable id of the variable. + */ + variable_key: string + + /** + * The name of the variable. + */ + variable_name: string + + /** + * The type of the variable. + */ + variable_type: string + + /** + * Unique, stable id of the collection the variable belongs to. + */ + collection_key: string + + /** + * The name of the collection the variable belongs to. + */ + collection_name: string + + /** + * The number of detach events for this period. + */ + detachments: number + + /** + * The number of insertion events for this period. + */ + insertions: number + } + + /** + * Library analytics variable action data broken down by team. + */ + export type LibraryAnalyticsVariableActionsByTeam = { + /** + * The date in ISO 8601 format. e.g. 2023-12-13 + */ + week: string + + /** + * The name of the team using the library. + */ + team_name: string + + /** + * The name of the workspace that the team belongs to. + */ + workspace_name?: string + + /** + * The number of detach events for this period. + */ + detachments: number + + /** + * The number of insertion events for this period. + */ + insertions: number + } + + /** + * Library analytics variable usage data broken down by component. + */ + export type LibraryAnalyticsVariableUsagesByAsset = { + /** + * Unique, stable id of the variable. + */ + variable_key: string + + /** + * The name of the variable. + */ + variable_name: string + + /** + * The type of the variable. + */ + variable_type: string + + /** + * Unique, stable id of the collection the variable belongs to. + */ + collection_key: string + + /** + * The name of the collection the variable belongs to. + */ + collection_name: string + + /** + * The number of usages of the variable within the organization. + */ + usages: number + + /** + * The number of teams using the variable within the organization. + */ + teams_using: number + + /** + * The number of files using the variable within the organization. + */ + files_using: number + } + + /** + * Library analytics variable usage data broken down by file. + */ + export type LibraryAnalyticsVariableUsagesByFile = { + /** + * The name of the file using the library. + */ + file_name: string + + /** + * The name of the team the file belongs to. + */ + team_name: string + + /** + * The name of the workspace that the file belongs to. + */ + workspace_name?: string + + /** + * The number of times variables from this library are used within the file. + */ + usages: number + } + + /** + * If pagination is needed due to the length of the response, identifies the next and previous + * pages. + */ + export type ResponsePagination = { + /** + * A URL that calls the previous page of the response. + */ + prev_page?: string + + /** + * A URL that calls the next page of the response. + */ + next_page?: string + } + + /** + * Pagination cursor + */ + export type ResponseCursor = { + before?: number + + after?: number + } + + /** + * A response indicating an error occurred. + */ + export type ErrorResponsePayloadWithErrMessage = { + /** + * Status code + */ + status: number + + /** + * A string describing the error + */ + err: string + } + + /** + * A response indicating an error occurred. + */ + export type ErrorResponsePayloadWithErrorBoolean = { + /** + * For erroneous requests, this value is always `true`. + */ + error: true + + /** + * Status code + */ + status: number + + /** + * A string describing the error + */ + message: string + } + + /** + * Response from the GET /v1/files/{file_key} endpoint. + */ + export type GetFileResponse = { + /** + * The name of the file as it appears in the editor. + */ + name: string + + /** + * The role of the user making the API request in relation to the file. + */ + role: 'owner' | 'editor' | 'viewer' + + /** + * The UTC ISO 8601 time at which the file was last modified. + */ + lastModified: string + + /** + * The type of editor associated with this file. + */ + editorType: 'figma' | 'figjam' + + /** + * A URL to a thumbnail image of the file. + */ + thumbnailUrl?: string + + /** + * The version number of the file. This number is incremented when a file is modified and can be + * used to check if the file has changed between requests. + */ + version: string + + document: DocumentNode + + /** + * A mapping from component IDs to component metadata. + */ + components: { [key: string]: Component } + + /** + * A mapping from component set IDs to component set metadata. + */ + componentSets: { [key: string]: ComponentSet } + + /** + * The version of the file schema that this file uses. + */ + schemaVersion: number + + /** + * A mapping from style IDs to style metadata. + */ + styles: { [key: string]: Style } + + /** + * The share permission level of the file link. + */ + linkAccess?: string + + /** + * The key of the main file for this file. If present, this file is a component or component set. + */ + mainFileKey?: string + + /** + * A list of branches for this file. + */ + branches?: { + /** + * The key of the branch. + */ + key: string + + /** + * The name of the branch. + */ + name: string + + /** + * A URL to a thumbnail image of the branch. + */ + thumbnail_url: string + + /** + * The UTC ISO 8601 time at which the branch was last modified. + */ + last_modified: string + }[] + } + + /** + * Response from the GET /v1/files/{file_key}/nodes endpoint. + */ + export type GetFileNodesResponse = { + /** + * The name of the file as it appears in the editor. + */ + name: string + + /** + * The role of the user making the API request in relation to the file. + */ + role: 'owner' | 'editor' | 'viewer' + + /** + * The UTC ISO 8601 time at which the file was last modified. + */ + lastModified: string + + /** + * The type of editor associated with this file. + */ + editorType: 'figma' | 'figjam' + + /** + * A URL to a thumbnail image of the file. + */ + thumbnailUrl: string + + /** + * The version number of the file. This number is incremented when a file is modified and can be + * used to check if the file has changed between requests. + */ + version: string + + /** + * A mapping from node IDs to node metadata. + */ + nodes: { + [key: string]: { + document: Node + + /** + * A mapping from component IDs to component metadata. + */ + components: { [key: string]: Component } + + /** + * A mapping from component set IDs to component set metadata. + */ + componentSets: { [key: string]: ComponentSet } + + /** + * The version of the file schema that this file uses. + */ + schemaVersion: number + + /** + * A mapping from style IDs to style metadata. + */ + styles: { [key: string]: Style } + } + } + } + + /** + * Response from the GET /v1/images/{file_key} endpoint. + */ + export type GetImagesResponse = { + /** + * For successful requests, this value is always `null`. + */ + err: null + + /** + * A map from node IDs to URLs of the rendered images. + */ + images: { [key: string]: string | null } + } + + /** + * Response from the GET /v1/files/{file_key}/images endpoint. + */ + export type GetImageFillsResponse = { + /** + * For successful requests, this value is always `false`. + */ + error: false + + /** + * Status code + */ + status: 200 + + meta: { + /** + * A map of image references to URLs of the image fills. + */ + images: { [key: string]: string } + } + } + + /** + * Response from the GET /v1/teams/{team_id}/projects endpoint. + */ + export type GetTeamProjectsResponse = { + /** + * The team's name. + */ + name: string + + /** + * An array of projects. + */ + projects: Project[] + } + + /** + * Response from the GET /v1/projects/{project_id}/files endpoint. + */ + export type GetProjectFilesResponse = { + /** + * The project's name. + */ + name: string + + /** + * An array of files. + */ + files: { + /** + * The file's key. + */ + key: string + + /** + * The file's name. + */ + name: string + + /** + * The file's thumbnail URL. + */ + thumbnail_url?: string + + /** + * The UTC ISO 8601 time at which the file was last modified. + */ + last_modified: string + }[] + } + + /** + * Response from the GET /v1/files/{file_key}/versions endpoint. + */ + export type GetFileVersionsResponse = { + /** + * An array of versions. + */ + versions: Version[] + + pagination: ResponsePagination + } + + /** + * Response from the GET /v1/files/{file_key}/comments endpoint. + */ + export type GetCommentsResponse = { + /** + * An array of comments. + */ + comments: Comment[] + } + + /** + * Response from the POST /v1/files/{file_key}/comments endpoint. + */ + export type PostCommentResponse = Comment + + /** + * Response from the DELETE /v1/files/{file_key}/comments/{comment_id} endpoint. + */ + export type DeleteCommentResponse = { + /** + * The status of the request. + */ + status: 200 + + /** + * For successful requests, this value is always `false`. + */ + error: false + } + + /** + * Response from the GET /v1/files/{file_key}/comments/{comment_id}/reactions endpoint. + */ + export type GetCommentReactionsResponse = { + /** + * An array of reactions. + */ + reactions: Reaction[] + + pagination: ResponsePagination + } + + /** + * Response from the POST /v1/files/{file_key}/comments/{comment_id}/reactions endpoint. + */ + export type PostCommentReactionResponse = { + /** + * The status of the request. + */ + status: 200 + + /** + * For successful requests, this value is always `false`. + */ + error: false + } + + /** + * Response from the DELETE /v1/files/{file_key}/comments/{comment_id}/reactions endpoint. + */ + export type DeleteCommentReactionResponse = { + /** + * The status of the request. + */ + status: 200 + + /** + * For successful requests, this value is always `false`. + */ + error: false + } + + /** + * Response from the GET /v1/me endpoint. + */ + export type GetMeResponse = User & { + /** + * Email associated with the user's account. This property is only present on the /v1/me endpoint. + */ + email: string + } + + /** + * Response from the GET /v1/teams/{team_id}/components endpoint. + */ + export type GetTeamComponentsResponse = { + /** + * The status of the request. + */ + status: 200 + + /** + * For successful requests, this value is always `false`. + */ + error: false + + meta: { + components: PublishedComponent[] + + cursor?: ResponseCursor + } + } + + /** + * Response from the GET /v1/files/{file_key}/components endpoint. + */ + export type GetFileComponentsResponse = { + /** + * The status of the request. + */ + status: 200 + + /** + * For successful requests, this value is always `false`. + */ + error: false + + meta: { components: PublishedComponent[] } + } + + /** + * Response from the GET /v1/components/{key} endpoint. + */ + export type GetComponentResponse = { + /** + * The status of the request. + */ + status: 200 + + /** + * For successful requests, this value is always `false`. + */ + error: false + + meta: PublishedComponent + } + + /** + * Response from the GET /v1/teams/{team_id}/component_sets endpoint. + */ + export type GetTeamComponentSetsResponse = { + /** + * The status of the request. + */ + status: 200 + + /** + * For successful requests, this value is always `false`. + */ + error: false + + meta: { + component_sets: PublishedComponentSet[] + + cursor?: ResponseCursor + } + } + + /** + * Response from the GET /v1/files/{file_key}/component_sets endpoint. + */ + export type GetFileComponentSetsResponse = { + /** + * The status of the request. + */ + status: 200 + + /** + * For successful requests, this value is always `false`. + */ + error: false + + meta: { component_sets: PublishedComponentSet[] } + } + + /** + * Response from the GET /v1/component_sets/{key} endpoint. + */ + export type GetComponentSetResponse = { + /** + * The status of the request. + */ + status: 200 + + /** + * For successful requests, this value is always `false`. + */ + error: false + + meta: PublishedComponentSet + } + + /** + * Response from the GET /v1/teams/{team_id}/styles endpoint. + */ + export type GetTeamStylesResponse = { + /** + * The status of the request. + */ + status: 200 + + /** + * For successful requests, this value is always `false`. + */ + error: false + + meta: { + styles: PublishedStyle[] + + cursor?: ResponseCursor + } + } + + /** + * Response from the GET /v1/files/{file_key}/styles endpoint. + */ + export type GetFileStylesResponse = { + /** + * The status of the request. + */ + status: 200 + + /** + * For successful requests, this value is always `false`. + */ + error: false + + meta: { styles: PublishedStyle[] } + } + + /** + * Response from the GET /v1/styles/{key} endpoint. + */ + export type GetStyleResponse = { + /** + * The status of the request. + */ + status: 200 + + /** + * For successful requests, this value is always `false`. + */ + error: false + + meta: PublishedStyle + } + + /** + * Response from the POST /v2/webhooks endpoint. + */ + export type PostWebhookResponse = WebhookV2 + + /** + * Response from the GET /v2/webhooks/{webhook_id} endpoint. + */ + export type GetWebhookResponse = WebhookV2 + + /** + * Response from the PUT /v2/webhooks/{webhook_id} endpoint. + */ + export type PutWebhookResponse = WebhookV2 + + /** + * Response from the DELETE /v2/webhooks/{webhook_id} endpoint. + */ + export type DeleteWebhookResponse = WebhookV2 + + /** + * Response from the GET /v2/teams/{team_id}/webhooks endpoint. + */ + export type GetTeamWebhooksResponse = { + /** + * An array of webhooks. + */ + webhooks: WebhookV2[] + } + + /** + * Response from the GET /v2/webhooks/{webhook_id}/requests endpoint. + */ + export type GetWebhookRequestsResponse = { + /** + * An array of webhook requests. + */ + requests: WebhookV2Request[] + } + + /** + * Response from the GET /v1/activity_logs endpoint. + */ + export type GetActivityLogsResponse = { + /** + * The response status code. + */ + status?: 200 + + /** + * For successful requests, this value is always `false`. + */ + error?: false + + meta?: { + /** + * An array of activity logs sorted by timestamp in ascending order by default. + */ + activity_logs?: ActivityLog[] + + /** + * Encodes the last event (the most recent event) + */ + cursor?: string + + /** + * Whether there is a next page of events + */ + next_page?: boolean + } + } + + /** + * Response from the GET /v1/payments endpoint. + */ + export type GetPaymentsResponse = { + /** + * The response status code. + */ + status: 200 + + /** + * For successful requests, this value is always `false`. + */ + error: false + + meta: PaymentInformation + } + + /** + * Response from the GET /v1/files/{file_key}/variables/local endpoint. + */ + export type GetLocalVariablesResponse = { + /** + * The response status code. + */ + status: 200 + + /** + * For successful requests, this value is always `false`. + */ + error: false + + meta: { + /** + * A map of variable ids to variables + */ + variables: { [key: string]: LocalVariable } + + /** + * A map of variable collection ids to variable collections + */ + variableCollections: { [key: string]: LocalVariableCollection } + } + } + + /** + * Response from the GET /v1/files/{file_key}/variables/published endpoint. + */ + export type GetPublishedVariablesResponse = { + /** + * The response status code. + */ + status: 200 + + /** + * For successful requests, this value is always `false`. + */ + error: false + + meta: { + /** + * A map of variable ids to variables + */ + variables: { [key: string]: PublishedVariable } + + /** + * A map of variable collection ids to variable collections + */ + variableCollections: { [key: string]: PublishedVariableCollection } + } + } + + /** + * Response from the POST /v1/files/{file_key}/variables endpoint. + */ + export type PostVariablesResponse = { + /** + * The response status code. + */ + status: 200 + + /** + * For successful requests, this value is always `false`. + */ + error: false + + meta: { + /** + * A map of temporary ids in the request to the real ids of the newly created objects + */ + tempIdToRealId: { [key: string]: string } + } + } + + /** + * Response from the GET /v1/files/{file_key}/dev_resources endpoint. + */ + export type GetDevResourcesResponse = { + /** + * An array of dev resources. + */ + dev_resources: DevResource[] + } + + /** + * Response from the POST /v1/dev_resources endpoint. + */ + export type PostDevResourcesResponse = { + /** + * An array of links created. + */ + links_created: DevResource[] + + /** + * An array of errors. + */ + errors?: { + /** + * The file key. + */ + file_key?: string | null + + /** + * The node id. + */ + node_id?: string | null + + /** + * The error message. + */ + error: string + }[] + } + + /** + * Response from the PUT /v1/dev_resources endpoint. + */ + export type PutDevResourcesResponse = { + /** + * An array of links updated. + */ + links_updated?: DevResource[] + + /** + * An array of errors. + */ + errors?: { + /** + * The id of the dev resource. + */ + id?: string + + /** + * The error message. + */ + error: string + }[] + } + + /** + * Response from the DELETE /v1/files/{file_key}/dev_resources/{dev_resource_id} endpoint. + */ + export type DeleteDevResourceResponse = void + + /** + * Response from the GET /v1/analytics/libraries/{file_key}/component/actions. + */ + export type GetLibraryAnalyticsComponentActionsResponse = { + /** + * An array of analytics data. + */ + rows: LibraryAnalyticsComponentActionsByAsset[] | LibraryAnalyticsComponentActionsByTeam[] + + /** + * Whether there is a next page of data that can be fetched. + */ + next_page: boolean + + /** + * The cursor to use to fetch the next page of data. Not present if next_page is false. + */ + cursor?: string + } + + /** + * Response from the PUT /v1/analytics/libraries/{file_key}/component/usages. + */ + export type GetLibraryAnalyticsComponentUsagesResponse = { + /** + * An array of analytics data. + */ + rows: LibraryAnalyticsComponentUsagesByAsset[] | LibraryAnalyticsComponentUsagesByFile[] + + /** + * Whether there is a next page of data that can be fetched. + */ + next_page: boolean + + /** + * The cursor to use to fetch the next page of data. Not present if next_page is false. + */ + cursor?: string + } + + /** + * Response from the GET /v1/analytics/libraries/{file_key}/style/actions. + */ + export type GetLibraryAnalyticsStyleActionsResponse = { + /** + * An array of analytics data. + */ + rows: LibraryAnalyticsStyleActionsByAsset[] | LibraryAnalyticsStyleActionsByTeam[] + + /** + * Whether there is a next page of data that can be fetched. + */ + next_page: boolean + + /** + * The cursor to use to fetch the next page of data. Not present if next_page is false. + */ + cursor?: string + } + + /** + * Response from the PUT /v1/analytics/libraries/{file_key}/style/usages. + */ + export type GetLibraryAnalyticsStyleUsagesResponse = { + /** + * An array of analytics data. + */ + rows: LibraryAnalyticsStyleUsagesByAsset[] | LibraryAnalyticsStyleUsagesByFile[] + + /** + * Whether there is a next page of data that can be fetched. + */ + next_page: boolean + + /** + * The cursor to use to fetch the next page of data. Not present if next_page is false. + */ + cursor?: string + } + + /** + * Response from the GET /v1/analytics/libraries/{file_key}/variable/actions. + */ + export type GetLibraryAnalyticsVariableActionsResponse = { + /** + * An array of analytics data. + */ + rows: LibraryAnalyticsVariableActionsByAsset[] | LibraryAnalyticsVariableActionsByTeam[] + + /** + * Whether there is a next page of data that can be fetched. + */ + next_page: boolean + + /** + * The cursor to use to fetch the next page of data. Not present if next_page is false. + */ + cursor?: string + } + + /** + * Response from the PUT /v1/analytics/libraries/{file_key}/variable/usages. + */ + export type GetLibraryAnalyticsVariableUsagesResponse = { + /** + * An array of analytics data. + */ + rows: LibraryAnalyticsVariableUsagesByAsset[] | LibraryAnalyticsVariableUsagesByFile[] + + /** + * Whether there is a next page of data that can be fetched. + */ + next_page: boolean + + /** + * The cursor to use to fetch the next page of data. Not present if next_page is false. + */ + cursor?: string + } + + /** + * Bad request. Parameters are invalid or malformed. Please check the input formats. This error can + * also happen if the requested resources are too large to complete the request, which results in a + * timeout. Please reduce the number and size of objects requested. + */ + export type BadRequestErrorResponseWithErrMessage = ErrorResponsePayloadWithErrMessage & { + /** + * Status code + */ + status: 400 + } + + /** + * Bad request. Parameters are invalid or malformed. Please check the input formats. This error can + * also happen if the requested resources are too large to complete the request, which results in a + * timeout. Please reduce the number and size of objects requested. + */ + export type BadRequestErrorResponseWithErrorBoolean = ErrorResponsePayloadWithErrorBoolean & { + /** + * Status code + */ + status: 400 + } + + /** + * Token is missing or incorrect. + */ + export type UnauthorizedErrorResponseWithErrorBoolean = ErrorResponsePayloadWithErrorBoolean & { + /** + * Status code + */ + status: 401 + } + + /** + * The request was valid, but the server is refusing action. The user might not have the necessary + * permissions for a resource, or may need an account of some sort. + */ + export type ForbiddenErrorResponseWithErrMessage = ErrorResponsePayloadWithErrMessage & { + /** + * Status code + */ + status: 403 + } + + /** + * The request was valid, but the server is refusing action. The user might not have the necessary + * permissions for a resource, or may need an account of some sort. + */ + export type ForbiddenErrorResponseWithErrorBoolean = ErrorResponsePayloadWithErrorBoolean & { + /** + * Status code + */ + status: 403 + } + + /** + * The requested file or resource was not found. + */ + export type NotFoundErrorResponseWithErrMessage = ErrorResponsePayloadWithErrMessage & { + /** + * Status code + */ + status: 404 + } + + /** + * The requested file or resource was not found. + */ + export type NotFoundErrorResponseWithErrorBoolean = ErrorResponsePayloadWithErrorBoolean & { + /** + * Status code + */ + status: 404 + } + + /** + * In some cases API requests may be throttled or rate limited. Please wait a while before + * attempting the request again (typically a minute). + */ + export type TooManyRequestsErrorResponseWithErrMessage = ErrorResponsePayloadWithErrMessage & { + /** + * Status code + */ + status: 429 + } + + /** + * In some cases API requests may be throttled or rate limited. Please wait a while before + * attempting the request again (typically a minute). + */ + export type TooManyRequestsErrorResponseWithErrorBoolean = ErrorResponsePayloadWithErrorBoolean & { + /** + * Status code + */ + status: 429 + } + + /** + * An internal server error occurred. + */ + export type InternalServerErrorResponseWithErrMessage = ErrorResponsePayloadWithErrMessage & { + /** + * Status code + */ + status: 500 + } + + /** + * An internal server error occurred. + */ + export type InternalServerErrorResponseWithErrorBoolean = ErrorResponsePayloadWithErrorBoolean & { + /** + * Status code + */ + status: 500 + } + + /** + * Path parameters for GET /v1/files/{file_key} + */ + export type GetFilePathParams = { + /** + * File to export JSON from. This can be a file key or branch key. Use `GET /v1/files/:key` with + * the `branch_data` query param to get the branch key. + */ + file_key: string + } + + /** + * Query parameters for GET /v1/files/{file_key} + */ + export type GetFileQueryParams = { + /** + * A specific version ID to get. Omitting this will get the current version of the file. + */ + version?: string + /** + * Comma separated list of nodes that you care about in the document. If specified, only a subset of + * the document will be returned corresponding to the nodes listed, their children, and everything + * between the root node and the listed nodes. + * + * Note: There may be other nodes included in the returned JSON that are outside the ancestor chains + * of the desired nodes. The response may also include dependencies of anything in the nodes' + * subtrees. For example, if a node subtree contains an instance of a local component that lives + * elsewhere in that file, that component and its ancestor chain will also be included. + * + * For historical reasons, top-level canvas nodes are always returned, regardless of whether they + * are listed in the `ids` parameter. This quirk may be removed in a future version of the API. + */ + ids?: string + /** + * Positive integer representing how deep into the document tree to traverse. For example, setting + * this to 1 returns only Pages, setting it to 2 returns Pages and all top level objects on each + * page. Not setting this parameter returns all nodes. + */ + depth?: number + /** + * Set to "paths" to export vector data. + */ + geometry?: string + /** + * A comma separated list of plugin IDs and/or the string "shared". Any data present in the document + * written by those plugins will be included in the result in the `pluginData` and + * `sharedPluginData` properties. + */ + plugin_data?: string + /** + * Returns branch metadata for the requested file. If the file is a branch, the main file's key will + * be included in the returned response. If the file has branches, their metadata will be included + * in the returned response. Default: false. + */ + branch_data?: boolean + } + + /** + * Path parameters for GET /v1/files/{file_key}/nodes + */ + export type GetFileNodesPathParams = { + /** + * File to export JSON from. This can be a file key or branch key. Use `GET /v1/files/:key` with + * the `branch_data` query param to get the branch key. + */ + file_key: string + } + + /** + * Query parameters for GET /v1/files/{file_key}/nodes + */ + export type GetFileNodesQueryParams = { + /** + * A comma separated list of node IDs to retrieve and convert. + */ + ids: string + /** + * A specific version ID to get. Omitting this will get the current version of the file. + */ + version?: string + /** + * Positive integer representing how deep into the node tree to traverse. For example, setting this + * to 1 will return only the children directly underneath the desired nodes. Not setting this + * parameter returns all nodes. + * + * Note: this parameter behaves differently from the same parameter in the `GET /v1/files/:key` + * endpoint. In this endpoint, the depth will be counted starting from the desired node rather than + * the document root node. + */ + depth?: number + /** + * Set to "paths" to export vector data. + */ + geometry?: string + /** + * A comma separated list of plugin IDs and/or the string "shared". Any data present in the document + * written by those plugins will be included in the result in the `pluginData` and + * `sharedPluginData` properties. + */ + plugin_data?: string + } + + /** + * Path parameters for GET /v1/images/{file_key} + */ + export type GetImagesPathParams = { + /** + * File to export images from. This can be a file key or branch key. Use `GET /v1/files/:key` with + * the `branch_data` query param to get the branch key. + */ + file_key: string + } + + /** + * Query parameters for GET /v1/images/{file_key} + */ + export type GetImagesQueryParams = { + /** + * A comma separated list of node IDs to render. + */ + ids: string + /** + * A specific version ID to get. Omitting this will get the current version of the file. + */ + version?: string + /** + * A number between 0.01 and 4, the image scaling factor. + */ + scale?: number + /** + * A string enum for the image output format. + */ + format?: 'jpg' | 'png' | 'svg' | 'pdf' + /** + * Whether text elements are rendered as outlines (vector paths) or as `` elements in SVGs. + * + * Rendering text elements as outlines guarantees that the text looks exactly the same in the SVG as + * it does in the browser/inside Figma. + * + * Exporting as `` allows text to be selectable inside SVGs and generally makes the SVG easier + * to read. However, this relies on the browser's rendering engine which can vary between browsers + * and/or operating systems. As such, visual accuracy is not guaranteed as the result could look + * different than in Figma. + */ + svg_outline_text?: boolean + /** + * Whether to include id attributes for all SVG elements. Adds the layer name to the `id` attribute + * of an svg element. + */ + svg_include_id?: boolean + /** + * Whether to include node id attributes for all SVG elements. Adds the node id to a `data-node-id` + * attribute of an svg element. + */ + svg_include_node_id?: boolean + /** + * Whether to simplify inside/outside strokes and use stroke attribute if possible instead of + * ``. + */ + svg_simplify_stroke?: boolean + /** + * Whether content that overlaps the node should be excluded from rendering. Passing false (i.e., + * rendering overlaps) may increase processing time, since more of the document must be included in + * rendering. + */ + contents_only?: boolean + /** + * Use the full dimensions of the node regardless of whether or not it is cropped or the space + * around it is empty. Use this to export text nodes without cropping. + */ + use_absolute_bounds?: boolean + } + + /** + * Path parameters for GET /v1/files/{file_key}/images + */ + export type GetImageFillsPathParams = { + /** + * File to get image URLs from. This can be a file key or branch key. Use `GET /v1/files/:key` with + * the `branch_data` query param to get the branch key. + */ + file_key: string + } + + /** + * Path parameters for GET /v1/teams/{team_id}/projects + */ + export type GetTeamProjectsPathParams = { + /** + * ID of the team to list projects from + */ + team_id: string + } + + /** + * Path parameters for GET /v1/projects/{project_id}/files + */ + export type GetProjectFilesPathParams = { + /** + * ID of the project to list files from + */ + project_id: string + } + + /** + * Query parameters for GET /v1/projects/{project_id}/files + */ + export type GetProjectFilesQueryParams = { + /** + * Returns branch metadata in the response for each main file with a branch inside the project. + */ + branch_data?: boolean + } + + /** + * Path parameters for GET /v1/files/{file_key}/versions + */ + export type GetFileVersionsPathParams = { + /** + * File to get version history from. This can be a file key or branch key. Use `GET /v1/files/:key` + * with the `branch_data` query param to get the branch key. + */ + file_key: string + } + + /** + * Query parameters for GET /v1/files/{file_key}/versions + */ + export type GetFileVersionsQueryParams = { + /** + * The number of items returned in a page of the response. If not included, `page_size` is `30`. + */ + page_size?: number + /** + * A version ID for one of the versions in the history. Gets versions before this ID. Used for + * paginating. If the response is not paginated, this link returns the same data in the current + * response. + */ + before?: number + /** + * A version ID for one of the versions in the history. Gets versions after this ID. Used for + * paginating. If the response is not paginated, this property is not included. + */ + after?: number + } + + /** + * Path parameters for GET /v1/files/{file_key}/comments + */ + export type GetCommentsPathParams = { + /** + * File to get comments from. This can be a file key or branch key. Use `GET /v1/files/:key` with + * the `branch_data` query param to get the branch key. + */ + file_key: string + } + + /** + * Query parameters for GET /v1/files/{file_key}/comments + */ + export type GetCommentsQueryParams = { + /** + * If enabled, will return comments as their markdown equivalents when applicable. + */ + as_md?: boolean + } + + /** + * Path parameters for POST /v1/files/{file_key}/comments + */ + export type PostCommentPathParams = { + /** + * File to add comments in. This can be a file key or branch key. Use `GET /v1/files/:key` with the + * `branch_data` query param to get the branch key. + */ + file_key: string + } + + /** + * Request body parameters for POST /v1/files/{file_key}/comments + */ + export type PostCommentRequestBody = { + /** + * The text contents of the comment to post. + */ + message: string + + /** + * The ID of the comment to reply to, if any. This must be a root comment. You cannot reply to other + * replies (a comment that has a parent_id). + */ + comment_id?: string + + /** + * The position where to place the comment. + */ + client_meta?: Vector | FrameOffset | Region | FrameOffsetRegion + } + + /** + * Path parameters for DELETE /v1/files/{file_key}/comments/{comment_id} + */ + export type DeleteCommentPathParams = { + /** + * File to delete comment from. This can be a file key or branch key. Use `GET /v1/files/:key` with + * the `branch_data` query param to get the branch key. + */ + file_key: string + /** + * Comment id of comment to delete + */ + comment_id: string + } + + /** + * Path parameters for DELETE /v1/files/{file_key}/comments/{comment_id}/reactions + */ + export type DeleteCommentReactionPathParams = { + /** + * File to delete comment reaction from. This can be a file key or branch key. Use `GET + * /v1/files/:key` with the `branch_data` query param to get the branch key. + */ + file_key: string + /** + * ID of comment to delete reaction from. + */ + comment_id: string + } + + /** + * Query parameters for DELETE /v1/files/{file_key}/comments/{comment_id}/reactions + */ + export type DeleteCommentReactionQueryParams = { emoji: Emoji } + + /** + * Path parameters for GET /v1/files/{file_key}/comments/{comment_id}/reactions + */ + export type GetCommentReactionsPathParams = { + /** + * File to get comment containing reactions from. This can be a file key or branch key. Use `GET + * /v1/files/:key` with the `branch_data` query param to get the branch key. + */ + file_key: string + /** + * ID of comment to get reactions from. + */ + comment_id: string + } + + /** + * Query parameters for GET /v1/files/{file_key}/comments/{comment_id}/reactions + */ + export type GetCommentReactionsQueryParams = { + /** + * Cursor for pagination, retrieved from the response of the previous call. + */ + cursor?: string + } + + /** + * Path parameters for POST /v1/files/{file_key}/comments/{comment_id}/reactions + */ + export type PostCommentReactionPathParams = { + /** + * File to post comment reactions to. This can be a file key or branch key. Use `GET + * /v1/files/:key` with the `branch_data` query param to get the branch key. + */ + file_key: string + /** + * ID of comment to react to. + */ + comment_id: string + } + + /** + * Request body parameters for POST /v1/files/{file_key}/comments/{comment_id}/reactions + */ + export type PostCommentReactionRequestBody = { emoji: Emoji } + + /** + * Path parameters for GET /v1/teams/{team_id}/components + */ + export type GetTeamComponentsPathParams = { + /** + * Id of the team to list components from. + */ + team_id: string + } + + /** + * Query parameters for GET /v1/teams/{team_id}/components + */ + export type GetTeamComponentsQueryParams = { + /** + * Number of items to return in a paged list of results. Defaults to 30. + */ + page_size?: number + /** + * Cursor indicating which id after which to start retrieving components for. Exclusive with before. + * The cursor value is an internally tracked integer that doesn't correspond to any Ids. + */ + after?: number + /** + * Cursor indicating which id before which to start retrieving components for. Exclusive with after. + * The cursor value is an internally tracked integer that doesn't correspond to any Ids. + */ + before?: number + } + + /** + * Path parameters for GET /v1/files/{file_key}/components + */ + export type GetFileComponentsPathParams = { + /** + * File to list components from. This must be a main file key, not a branch key, as it is not + * possible to publish from branches. + */ + file_key: string + } + + /** + * Path parameters for GET /v1/components/{key} + */ + export type GetComponentPathParams = { + /** + * The unique identifier of the component. + */ + key: string + } + + /** + * Path parameters for GET /v1/teams/{team_id}/component_sets + */ + export type GetTeamComponentSetsPathParams = { + /** + * Id of the team to list component sets from. + */ + team_id: string + } + + /** + * Query parameters for GET /v1/teams/{team_id}/component_sets + */ + export type GetTeamComponentSetsQueryParams = { + /** + * Number of items to return in a paged list of results. Defaults to 30. + */ + page_size?: number + /** + * Cursor indicating which id after which to start retrieving component sets for. Exclusive with + * before. The cursor value is an internally tracked integer that doesn't correspond to any Ids. + */ + after?: number + /** + * Cursor indicating which id before which to start retrieving component sets for. Exclusive with + * after. The cursor value is an internally tracked integer that doesn't correspond to any Ids. + */ + before?: number + } + + /** + * Path parameters for GET /v1/files/{file_key}/component_sets + */ + export type GetFileComponentSetsPathParams = { + /** + * File to list component sets from. This must be a main file key, not a branch key, as it is not + * possible to publish from branches. + */ + file_key: string + } + + /** + * Path parameters for GET /v1/component_sets/{key} + */ + export type GetComponentSetPathParams = { + /** + * The unique identifier of the component set. + */ + key: string + } + + /** + * Path parameters for GET /v1/teams/{team_id}/styles + */ + export type GetTeamStylesPathParams = { + /** + * Id of the team to list styles from. + */ + team_id: string + } + + /** + * Query parameters for GET /v1/teams/{team_id}/styles + */ + export type GetTeamStylesQueryParams = { + /** + * Number of items to return in a paged list of results. Defaults to 30. + */ + page_size?: number + /** + * Cursor indicating which id after which to start retrieving styles for. Exclusive with before. The + * cursor value is an internally tracked integer that doesn't correspond to any Ids. + */ + after?: number + /** + * Cursor indicating which id before which to start retrieving styles for. Exclusive with after. The + * cursor value is an internally tracked integer that doesn't correspond to any Ids. + */ + before?: number + } + + /** + * Path parameters for GET /v1/files/{file_key}/styles + */ + export type GetFileStylesPathParams = { + /** + * File to list styles from. This must be a main file key, not a branch key, as it is not possible + * to publish from branches. + */ + file_key: string + } + + /** + * Path parameters for GET /v1/styles/{key} + */ + export type GetStylePathParams = { + /** + * The unique identifier of the style. + */ + key: string + } + + /** + * Request body parameters for POST /v2/webhooks + */ + export type PostWebhookRequestBody = { + event_type: WebhookV2Event + + /** + * Team id to receive updates about + */ + team_id: string + + /** + * The HTTP endpoint that will receive a POST request when the event triggers. Max length 2048 + * characters. + */ + endpoint: string + + /** + * String that will be passed back to your webhook endpoint to verify that it is being called by + * Figma. Max length 100 characters. + */ + passcode: string + + /** + * State of the webhook, including any error state it may be in + */ + status?: WebhookV2Status + + /** + * User provided description or name for the webhook. Max length 150 characters. + */ + description?: string + } + + /** + * Path parameters for DELETE /v2/webhooks/{webhook_id} + */ + export type DeleteWebhookPathParams = { + /** + * ID of webhook to delete + */ + webhook_id: string + } + + /** + * Path parameters for GET /v2/webhooks/{webhook_id} + */ + export type GetWebhookPathParams = { + /** + * ID of webhook to get + */ + webhook_id: string + } + + /** + * Path parameters for PUT /v2/webhooks/{webhook_id} + */ + export type PutWebhookPathParams = { + /** + * ID of webhook to update + */ + webhook_id: string + } + + /** + * Request body parameters for PUT /v2/webhooks/{webhook_id} + */ + export type PutWebhookRequestBody = { + event_type: WebhookV2Event + + /** + * The HTTP endpoint that will receive a POST request when the event triggers. Max length 2048 + * characters. + */ + endpoint: string + + /** + * String that will be passed back to your webhook endpoint to verify that it is being called by + * Figma. Max length 100 characters. + */ + passcode: string + + /** + * State of the webhook, including any error state it may be in + */ + status?: WebhookV2Status + + /** + * User provided description or name for the webhook. Max length 150 characters. + */ + description?: string + } + + /** + * Path parameters for GET /v2/teams/{team_id}/webhooks + */ + export type GetTeamWebhooksPathParams = { + /** + * ID of team to get webhooks for + */ + team_id: string + } + + /** + * Path parameters for GET /v2/webhooks/{webhook_id}/requests + */ + export type GetWebhookRequestsPathParams = { + /** + * The id of the webhook subscription you want to see events from + */ + webhook_id: string + } + + /** + * Query parameters for GET /v1/activity_logs + */ + export type GetActivityLogsQueryParams = { + /** + * Event type(s) to include in the response. Can have multiple values separated by comma. All + * events are returned by default. + */ + events?: string + /** + * Unix timestamp of the least recent event to include. This param defaults to one year ago if + * unspecified. Events prior to one year ago are not available. + */ + start_time?: number + /** + * Unix timestamp of the most recent event to include. This param defaults to the current timestamp + * if unspecified. + */ + end_time?: number + /** + * Maximum number of events to return. This param defaults to 1000 if unspecified. + */ + limit?: number + /** + * Event order by timestamp. This param can be either "asc" (default) or "desc". + */ + order?: 'asc' | 'desc' + } + + /** + * Query parameters for GET /v1/payments + */ + export type GetPaymentsQueryParams = { + /** + * Short-lived token returned from "getPluginPaymentTokenAsync" in the plugin payments API and used + * to authenticate to this endpoint. Read more about generating this token through "Calling the + * Payments REST API from a plugin or widget" below. + */ + plugin_payment_token?: string + /** + * The ID of the user to query payment information about. You can get the user ID by having the user + * OAuth2 to the Figma REST API. + */ + user_id?: number + /** + * The ID of the Community file to query a user's payment information on. You can get the Community + * file ID from the file's Community page (look for the number after "file/" in the URL). Provide + * exactly one of "community_file_id", "plugin_id", or "widget_id". + */ + community_file_id?: number + /** + * The ID of the plugin to query a user's payment information on. You can get the plugin ID from the + * plugin's manifest, or from the plugin's Community page (look for the number after "plugin/" in + * the URL). Provide exactly one of "community_file_id", "plugin_id", or "widget_id". + */ + plugin_id?: number + /** + * The ID of the widget to query a user's payment information on. You can get the widget ID from the + * widget's manifest, or from the widget's Community page (look for the number after "widget/" in + * the URL). Provide exactly one of "community_file_id", "plugin_id", or "widget_id". + */ + widget_id?: number + } + + /** + * Path parameters for GET /v1/files/{file_key}/variables/local + */ + export type GetLocalVariablesPathParams = { + /** + * File to get variables from. This can be a file key or branch key. Use `GET /v1/files/:key` with + * the `branch_data` query param to get the branch key. + */ + file_key: string + } + + /** + * Path parameters for GET /v1/files/{file_key}/variables/published + */ + export type GetPublishedVariablesPathParams = { + /** + * File to get variables from. This must be a main file key, not a branch key, as it is not + * possible to publish from branches. + */ + file_key: string + } + + /** + * Path parameters for POST /v1/files/{file_key}/variables + */ + export type PostVariablesPathParams = { + /** + * File to modify variables in. This can be a file key or branch key. Use `GET /v1/files/:key` with + * the `branch_data` query param to get the branch key. + */ + file_key: string + } + + /** + * Request body parameters for POST /v1/files/{file_key}/variables + */ + export type PostVariablesRequestBody = { + /** + * For creating, updating, and deleting variable collections. + */ + variableCollections?: VariableCollectionChange[] + + /** + * For creating, updating, and deleting modes within variable collections. + */ + variableModes?: VariableModeChange[] + + /** + * For creating, updating, and deleting variables. + */ + variables?: VariableChange[] + + /** + * For setting a specific value, given a variable and a mode. + */ + variableModeValues?: VariableModeValue[] + } + + /** + * Path parameters for GET /v1/files/{file_key}/dev_resources + */ + export type GetDevResourcesPathParams = { + /** + * The file to get the dev resources from. This must be a main file key, not a branch key. + */ + file_key: string + } + + /** + * Query parameters for GET /v1/files/{file_key}/dev_resources + */ + export type GetDevResourcesQueryParams = { + /** + * Comma separated list of nodes that you care about in the document. If specified, only dev + * resources attached to these nodes will be returned. If not specified, all dev resources in the + * file will be returned. + */ + node_ids?: string + } + + /** + * Request body parameters for POST /v1/dev_resources + */ + export type PostDevResourcesRequestBody = { + /** + * An array of dev resources. + */ + dev_resources: { + /** + * The name of the dev resource. + */ + name: string + + /** + * The URL of the dev resource. + */ + url: string + + /** + * The file key where the dev resource belongs. + */ + file_key: string + + /** + * The target node to attach the dev resource to. + */ + node_id: string + }[] + } + + /** + * Request body parameters for PUT /v1/dev_resources + */ + export type PutDevResourcesRequestBody = { + /** + * An array of dev resources. + */ + dev_resources: { + /** + * Unique identifier of the dev resource + */ + id: string + + /** + * The name of the dev resource. + */ + name?: string + + /** + * The URL of the dev resource. + */ + url?: string + }[] + } + + /** + * Path parameters for DELETE /v1/files/{file_key}/dev_resources/{dev_resource_id} + */ + export type DeleteDevResourcePathParams = { + /** + * The file to delete the dev resource from. This must be a main file key, not a branch key. + */ + file_key: string + /** + * The id of the dev resource to delete. + */ + dev_resource_id: string + } + + /** + * Path parameters for GET /v1/analytics/libraries/{file_key}/component/actions + */ + export type GetLibraryAnalyticsComponentActionsPathParams = { + /** + * File key of the library to fetch analytics data for. + */ + file_key: string + } + + /** + * Query parameters for GET /v1/analytics/libraries/{file_key}/component/actions + */ + export type GetLibraryAnalyticsComponentActionsQueryParams = { + /** + * Cursor indicating what page of data to fetch. Obtained from prior API call. + */ + cursor?: string + /** + * A dimension to group returned analytics data by. + */ + group_by: 'component' | 'team' + /** + * ISO 8601 date string (YYYY-MM-DD) of the earliest week to include. Dates are rounded back to the + * nearest start of a week. Defaults to one year prior. + */ + start_date?: string + /** + * ISO 8601 date string (YYYY-MM-DD) of the latest week to include. Dates are rounded forward to the + * nearest end of a week. Defaults to the latest computed week. + */ + end_date?: string + } + + /** + * Path parameters for GET /v1/analytics/libraries/{file_key}/component/usages + */ + export type GetLibraryAnalyticsComponentUsagesPathParams = { + /** + * File key of the library to fetch analytics data for. + */ + file_key: string + } + + /** + * Query parameters for GET /v1/analytics/libraries/{file_key}/component/usages + */ + export type GetLibraryAnalyticsComponentUsagesQueryParams = { + /** + * Cursor indicating what page of data to fetch. Obtained from prior API call. + */ + cursor?: string + /** + * A dimension to group returned analytics data by. + */ + group_by: 'component' | 'file' + } + + /** + * Path parameters for GET /v1/analytics/libraries/{file_key}/style/actions + */ + export type GetLibraryAnalyticsStyleActionsPathParams = { + /** + * File key of the library to fetch analytics data for. + */ + file_key: string + } + + /** + * Query parameters for GET /v1/analytics/libraries/{file_key}/style/actions + */ + export type GetLibraryAnalyticsStyleActionsQueryParams = { + /** + * Cursor indicating what page of data to fetch. Obtained from prior API call. + */ + cursor?: string + /** + * A dimension to group returned analytics data by. + */ + group_by: 'style' | 'team' + /** + * ISO 8601 date string (YYYY-MM-DD) of the earliest week to include. Dates are rounded back to the + * nearest start of a week. Defaults to one year prior. + */ + start_date?: string + /** + * ISO 8601 date string (YYYY-MM-DD) of the latest week to include. Dates are rounded forward to the + * nearest end of a week. Defaults to the latest computed week. + */ + end_date?: string + } + + /** + * Path parameters for GET /v1/analytics/libraries/{file_key}/style/usages + */ + export type GetLibraryAnalyticsStyleUsagesPathParams = { + /** + * File key of the library to fetch analytics data for. + */ + file_key: string + } + + /** + * Query parameters for GET /v1/analytics/libraries/{file_key}/style/usages + */ + export type GetLibraryAnalyticsStyleUsagesQueryParams = { + /** + * Cursor indicating what page of data to fetch. Obtained from prior API call. + */ + cursor?: string + /** + * A dimension to group returned analytics data by. + */ + group_by: 'style' | 'file' + } + + /** + * Path parameters for GET /v1/analytics/libraries/{file_key}/variable/actions + */ + export type GetLibraryAnalyticsVariableActionsPathParams = { + /** + * File key of the library to fetch analytics data for. + */ + file_key: string + } + + /** + * Query parameters for GET /v1/analytics/libraries/{file_key}/variable/actions + */ + export type GetLibraryAnalyticsVariableActionsQueryParams = { + /** + * Cursor indicating what page of data to fetch. Obtained from prior API call. + */ + cursor?: string + /** + * A dimension to group returned analytics data by. + */ + group_by: 'variable' | 'team' + /** + * ISO 8601 date string (YYYY-MM-DD) of the earliest week to include. Dates are rounded back to the + * nearest start of a week. Defaults to one year prior. + */ + start_date?: string + /** + * ISO 8601 date string (YYYY-MM-DD) of the latest week to include. Dates are rounded forward to the + * nearest end of a week. Defaults to the latest computed week. + */ + end_date?: string + } + + /** + * Path parameters for GET /v1/analytics/libraries/{file_key}/variable/usages + */ + export type GetLibraryAnalyticsVariableUsagesPathParams = { + /** + * File key of the library to fetch analytics data for. + */ + file_key: string + } + + /** + * Query parameters for GET /v1/analytics/libraries/{file_key}/variable/usages + */ + export type GetLibraryAnalyticsVariableUsagesQueryParams = { + /** + * Cursor indicating what page of data to fetch. Obtained from prior API call. + */ + cursor?: string + /** + * A dimension to group returned analytics data by. + */ + group_by: 'variable' | 'file' + } \ No newline at end of file diff --git a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts index 9946621f..f1b616ad 100644 --- a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts +++ b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts @@ -30,7 +30,7 @@ import { } from "../common/commonFormatAttributes"; import { TailwindColorType, TailwindSettings } from "types"; -const isNotEmpty = (s: string) => s !== ""; +const isNotEmpty = (s: string) => s !== "" && s !== null && s !== undefined; const dropEmptyStrings = (strings: string[]) => strings.filter(isNotEmpty); export class TailwindDefaultBuilder { @@ -63,10 +63,15 @@ export class TailwindDefaultBuilder { } addAttributes = (...newStyles: string[]) => { - this.attributes.push(...dropEmptyStrings(newStyles)); + // Filter out empty strings and trim any extra spaces + const cleanedStyles = dropEmptyStrings(newStyles).map((s) => s.trim()); + this.attributes.push(...cleanedStyles); }; + prependAttributes = (...newStyles: string[]) => { - this.attributes.unshift(...dropEmptyStrings(newStyles)); + // Filter out empty strings and trim any extra spaces + const cleanedStyles = dropEmptyStrings(newStyles).map((s) => s.trim()); + this.attributes.unshift(...cleanedStyles); }; blend(): this { @@ -184,21 +189,21 @@ export class TailwindDefaultBuilder { const { node, optimizeLayout } = this; const { width, height } = tailwindSizePartial(node, optimizeLayout); - if (node.type === "TEXT") { - switch (node.textAutoResize) { - case "WIDTH_AND_HEIGHT": - break; - case "HEIGHT": - this.addAttributes(width); - break; - case "NONE": - case "TRUNCATE": - this.addAttributes(width, height); - break; - } - } else { - this.addAttributes(width, height); - } + // if (node.type === "TEXT") { + // switch (node.textAutoResize) { + // case "WIDTH_AND_HEIGHT": + // break; + // case "HEIGHT": + // this.addAttributes(width); + // break; + // case "NONE": + // case "TRUNCATE": + // this.addAttributes(width, height); + // break; + // } + // } else { + this.addAttributes(width, height); + // } return this; } @@ -251,7 +256,9 @@ export class TailwindDefaultBuilder { } build(additionalAttr = ""): string { - this.addAttributes(additionalAttr); + if (additionalAttr) { + this.addAttributes(additionalAttr); + } if (this.name !== "") { this.prependAttributes(stringToClassName(this.name)); @@ -270,7 +277,7 @@ export class TailwindDefaultBuilder { const classLabel = getClassLabel(this.isJSX); const classNames = this.attributes.length > 0 - ? ` ${classLabel}="${this.attributes.join(" ")}"` + ? ` ${classLabel}="${this.attributes.filter(Boolean).join(" ")}"` : ""; const styles = this.style.length > 0 ? ` style="${this.style}"` : ""; const dataAttributes = this.data.join(""); diff --git a/packages/plugin-ui/src/components/About.tsx b/packages/plugin-ui/src/components/About.tsx index 0de731de..1a3f01f3 100644 --- a/packages/plugin-ui/src/components/About.tsx +++ b/packages/plugin-ui/src/components/About.tsx @@ -1,5 +1,6 @@ import React from "react"; import { + ArrowRightIcon, Code, Github, Heart, @@ -108,17 +109,23 @@ const About = () => {
  • -
    +
    + +
    Convert Figma designs to HTML, Tailwind, Flutter, and SwiftUI
  • -
    +
    + +
    Extract colors and gradients from your designs
  • -
    +
    + +
    Get responsive code that matches your design
From 0aedab5a39d1e5b13a9b5aec077eb8bd784e85fe Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Tue, 4 Mar 2025 12:37:45 -0300 Subject: [PATCH 047/168] All-new text segments --- .../backend/src/altNodes/altConversion.ts | 33 +--------- packages/backend/src/code.ts | 60 ++++++++++++------- .../src/flutter/builderImpl/flutterColor.ts | 10 ++++ .../backend/src/flutter/flutterTextBuilder.ts | 23 ++++--- packages/backend/src/html/htmlMain.ts | 2 +- packages/backend/src/html/htmlTextBuilder.ts | 18 +++--- .../src/swiftui/builderImpl/swiftuiColor.ts | 38 ++++-------- .../src/swiftui/swiftuiDefaultBuilder.ts | 4 +- .../backend/src/swiftui/swiftuiTextBuilder.ts | 17 +++--- .../src/tailwind/tailwindDefaultBuilder.ts | 31 +++++----- packages/backend/src/tailwind/tailwindMain.ts | 2 +- .../src/tailwind/tailwindTextBuilder.ts | 12 ++-- 12 files changed, 117 insertions(+), 133 deletions(-) diff --git a/packages/backend/src/altNodes/altConversion.ts b/packages/backend/src/altNodes/altConversion.ts index 59b0db70..226a16d8 100644 --- a/packages/backend/src/altNodes/altConversion.ts +++ b/packages/backend/src/altNodes/altConversion.ts @@ -1,15 +1,11 @@ -import { StyledTextSegmentSubset, ParentNode, AltNode } from "types"; +import { ParentNode } from "types"; import { - assignParent, isNotEmpty, assignRectangleType, assignChildren, isTypeOrGroupOfTypes, } from "./altNodeUtils"; -export let globalTextStyleSegments: Record = - {}; - // List of types that can be flattened into SVG const canBeFlattened = isTypeOrGroupOfTypes([ "VECTOR", @@ -49,13 +45,9 @@ export const convertNodeToAltNode = case "SECTION": const groupChildren = await convertNodesToAltNodes(node.children, node); return assignChildren(groupChildren, node); - // Text Nodes case "TEXT": - const textNode = (await figma.getNodeByIdAsync(node.id)) as TextNode; - globalTextStyleSegments[node.id] = extractStyledTextSegments(textNode); return node; - // Unsupported Nodes case "SLICE": return null; @@ -80,26 +72,3 @@ const cloneAsRectangleNode = (node: T): RectangleNode => { return node as unknown as RectangleNode; }; - -const extractStyledTextSegments = (node: TextNode) => - node.getStyledTextSegments([ - "fontName", - "fills", - "fontSize", - "fontWeight", - "hyperlink", - "indentation", - "letterSpacing", - "lineHeight", - "listOptions", - "textCase", - "textDecoration", - "textDecorationStyle", - "textDecorationOffset", - "textDecorationThickness", - "textDecorationColor", - "textDecorationSkipInk", - "textStyleId", - "fillStyleId", - "openTypeFeatures", - ]); diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index 5bf72b65..93ad03c2 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -52,11 +52,13 @@ const processNodeData = (node: any, optimizeLayout: boolean) => { ); }); - // Check if node is an instance to extract component metadata - const isInstance = node.type === "INSTANCE"; - // Only fetch the Figma node if we have gradients, optimizeLayout is enabled, or it's an instance - if (hasGradient || optimizeLayout || isInstance) { + if ( + hasGradient || + optimizeLayout || + node.type === "INSTANCE" || + node.type === "TEXT" + ) { try { const figmaNode = figma.getNodeById(node.id); if (figmaNode) { @@ -82,6 +84,31 @@ const processNodeData = (node: any, optimizeLayout: boolean) => { }); } + if (figmaNode.type === "TEXT") { + node.styledTextSegments = figmaNode.getStyledTextSegments([ + "fontName", + "fills", + "fontSize", + "fontWeight", + "hyperlink", + "indentation", + "letterSpacing", + "lineHeight", + "listOptions", + "textCase", + "textDecoration", + "textDecorationStyle", + "textDecorationOffset", + "textDecorationThickness", + "textDecorationColor", + "textDecorationSkipInk", + "textStyleId", + "fillStyleId", + "openTypeFeatures", + ]); + Object.assign(node, node.style); + } + // Extract inferredAutoLayout if optimizeLayout is enabled if (optimizeLayout && "inferredAutoLayout" in figmaNode) { node.inferredAutoLayout = JSON.parse( @@ -90,7 +117,7 @@ const processNodeData = (node: any, optimizeLayout: boolean) => { } // Extract component metadata from instances - if (isInstance && figmaNode.type === "INSTANCE") { + if (figmaNode.type === "INSTANCE") { if (figmaNode.variantProperties) { node.variantProperties = figmaNode.variantProperties; } @@ -107,24 +134,11 @@ const processNodeData = (node: any, optimizeLayout: boolean) => { // Silently fail if there's an error accessing the Figma node } } else { - // Avoid calling getNodeById if we don't need to - if ( - "rotation" in node && - node.rotation !== undefined && - node.rotation !== 0 - ) { - const figmaNode = figma.getNodeById(node.id); - node.width = (figmaNode as any).width; - node.height = (figmaNode as any).height; - node.x = (figmaNode as any).x; - node.y = (figmaNode as any).y; - } else if (node.absoluteRenderBounds) { - // Use the absoluteRenderBounds if we don't need to fetch the Figma node. - node.width = node.absoluteRenderBounds.width; - node.height = node.absoluteRenderBounds.height; - node.x = node.absoluteRenderBounds.x; - node.y = node.absoluteRenderBounds.y; - } + const figmaNode = figma.getNodeById(node.id); + node.width = (figmaNode as any).width; + node.height = (figmaNode as any).height; + node.x = (figmaNode as any).x; + node.y = (figmaNode as any).y; } if (!node.layoutMode) { diff --git a/packages/backend/src/flutter/builderImpl/flutterColor.ts b/packages/backend/src/flutter/builderImpl/flutterColor.ts index 4fcf7dd2..c04b2078 100644 --- a/packages/backend/src/flutter/builderImpl/flutterColor.ts +++ b/packages/backend/src/flutter/builderImpl/flutterColor.ts @@ -22,6 +22,16 @@ export const flutterColorFromFills = ( | ReadonlyArray | PluginAPI["mixed"]; + return flutterColorFromDirectFills(fills); +}; + +/** + * Retrieve the SOLID color for Flutter directly from fills when existent, otherwise "" + * @param fills The fills array to process + */ +export const flutterColorFromDirectFills = ( + fills: ReadonlyArray | PluginAPI["mixed"], +): string => { const fill = retrieveTopFill(fills); if (fill && fill.type === "SOLID") { diff --git a/packages/backend/src/flutter/flutterTextBuilder.ts b/packages/backend/src/flutter/flutterTextBuilder.ts index bda01b99..1239e3b8 100644 --- a/packages/backend/src/flutter/flutterTextBuilder.ts +++ b/packages/backend/src/flutter/flutterTextBuilder.ts @@ -4,13 +4,16 @@ import { numberToFixedString, } from "./../common/numToAutoFixed"; import { FlutterDefaultBuilder } from "./flutterDefaultBuilder"; -import { flutterColorFromFills } from "./builderImpl/flutterColor"; +import { + flutterColorFromDirectFills, + flutterColorFromFills, +} from "./builderImpl/flutterColor"; import { flutterSize } from "./builderImpl/flutterSize"; -import { globalTextStyleSegments } from "../altNodes/altConversion"; import { commonLetterSpacing, commonLineHeight, } from "../common/commonTextHeightSpacing"; +import { StyledTextSegmentSubset } from "types/src/types"; export class FlutterTextBuilder extends FlutterDefaultBuilder { node?: TextNode; @@ -35,7 +38,7 @@ export class FlutterTextBuilder extends FlutterDefaultBuilder { alignHorizontal !== "left" ? `TextAlign.${alignHorizontal}` : "", }; - const segments = this.getTextSegments(node.id); + const segments = this.getTextSegments(node); if (segments.length === 1) { this.child = generateWidgetCode( "Text", @@ -61,18 +64,19 @@ export class FlutterTextBuilder extends FlutterDefaultBuilder { return this; } - getTextSegments(id: string): { + getTextSegments(node: TextNode): { style: string; text: string; openTypeFeatures: { [key: string]: boolean }; }[] { - const segments = globalTextStyleSegments[id]; + const segments = (node as any) + .styledTextSegments as StyledTextSegmentSubset[]; if (!segments) { return []; } return segments.map((segment) => { - const color = flutterColorFromFills(segment.fills); + const color = flutterColorFromDirectFills(segment.fills); const fontSize = `${numberToFixedString(segment.fontSize)}`; const fontStyle = this.fontStyle(segment.fontName); @@ -188,8 +192,7 @@ export class FlutterTextBuilder extends FlutterDefaultBuilder { if (this.node && (this.node as TextNode).effects) { const effects = (this.node as TextNode).effects; const dropShadow = effects.find( - (effect) => - effect.type === "DROP_SHADOW" && effect.visible !== false, + (effect) => effect.type === "DROP_SHADOW" && effect.visible !== false, ); if (dropShadow) { const ds = dropShadow as DropShadowEffect; @@ -255,7 +258,9 @@ export const wrapTextWithLayerBlur = ( if (node.effects) { const blurEffect = node.effects.find( (effect) => - effect.type === "LAYER_BLUR" && effect.visible !== false && effect.radius > 0, + effect.type === "LAYER_BLUR" && + effect.visible !== false && + effect.radius > 0, ); if (blurEffect) { return generateWidgetCode("ImageFiltered", { diff --git a/packages/backend/src/html/htmlMain.ts b/packages/backend/src/html/htmlMain.ts index 99f4989e..d12bd73e 100644 --- a/packages/backend/src/html/htmlMain.ts +++ b/packages/backend/src/html/htmlMain.ts @@ -163,7 +163,7 @@ const htmlText = (node: TextNode, settings: HTMLSettings): string => { .commonPositionStyles() .textAlign(); - const styledHtml = layoutBuilder.getTextSegments(node.id); + const styledHtml = layoutBuilder.getTextSegments(node); previousExecutionCache.push(...styledHtml); let content = ""; diff --git a/packages/backend/src/html/htmlTextBuilder.ts b/packages/backend/src/html/htmlTextBuilder.ts index 30ab8ca5..d18e08fe 100644 --- a/packages/backend/src/html/htmlTextBuilder.ts +++ b/packages/backend/src/html/htmlTextBuilder.ts @@ -1,24 +1,24 @@ import { formatMultipleJSX, formatWithJSX } from "../common/parseJSX"; import { HtmlDefaultBuilder } from "./htmlDefaultBuilder"; -import { globalTextStyleSegments } from "../altNodes/altConversion"; import { htmlColorFromFills } from "./builderImpl/htmlColor"; import { commonLetterSpacing, commonLineHeight, } from "../common/commonTextHeightSpacing"; -import { HTMLSettings } from "types"; +import { HTMLSettings, StyledTextSegmentSubset } from "types"; export class HtmlTextBuilder extends HtmlDefaultBuilder { constructor(node: TextNode, settings: HTMLSettings) { super(node, settings); } - getTextSegments(id: string): { + getTextSegments(node: TextNode): { style: string; text: string; openTypeFeatures: { [key: string]: boolean }; }[] { - const segments = globalTextStyleSegments[id]; + const segments = (node as any) + .styledTextSegments as StyledTextSegmentSubset[]; if (!segments) { return []; } @@ -48,13 +48,13 @@ export class HtmlTextBuilder extends HtmlDefaultBuilder { "line-height": this.lineHeight(segment.lineHeight, segment.fontSize), "letter-spacing": this.letterSpacing( segment.letterSpacing, - segment.fontSize + segment.fontSize, ), // "text-indent": segment.indentation, "word-wrap": "break-word", ...additionalStyles, }, - this.isJSX + this.isJSX, ); const charsWithLineBreak = segment.characters.split("\n").join("
"); @@ -163,7 +163,7 @@ export class HtmlTextBuilder extends HtmlDefaultBuilder { (effect) => effect.type === "LAYER_BLUR" && effect.visible !== false && - effect.radius > 0 + effect.radius > 0, ); if (blurEffect && blurEffect.radius) { return `blur(${blurEffect.radius}px)`; @@ -179,7 +179,7 @@ export class HtmlTextBuilder extends HtmlDefaultBuilder { if (this.node && (this.node as TextNode).effects) { const effects = (this.node as TextNode).effects; const dropShadow = effects.find( - (effect) => effect.type === "DROP_SHADOW" && effect.visible !== false + (effect) => effect.type === "DROP_SHADOW" && effect.visible !== false, ); if (dropShadow) { const ds = dropShadow as DropShadowEffect; // Type narrow the effect. @@ -191,7 +191,7 @@ export class HtmlTextBuilder extends HtmlDefaultBuilder { const b = Math.round(ds.color.b * 255); const a = ds.color.a; return `${offsetX}px ${offsetY}px ${blurRadius}px rgba(${r}, ${g}, ${b}, ${a.toFixed( - 2 + 2, )})`; } } diff --git a/packages/backend/src/swiftui/builderImpl/swiftuiColor.ts b/packages/backend/src/swiftui/builderImpl/swiftuiColor.ts index 9ecb7a19..39b6c851 100644 --- a/packages/backend/src/swiftui/builderImpl/swiftuiColor.ts +++ b/packages/backend/src/swiftui/builderImpl/swiftuiColor.ts @@ -42,6 +42,16 @@ export const swiftuiSolidColor = ( | ReadonlyArray | PluginAPI["mixed"]; + return swiftuiSolidColorFromDirectFills(fills); +}; + +/** + * Retrieve the SwiftUI solid color directly from fills when existent, otherwise "" + * @param fills The fills array to process + */ +export const swiftuiSolidColorFromDirectFills = ( + fills: ReadonlyArray | PluginAPI["mixed"], +): string => { const fill = retrieveTopFill(fills); if (fill && fill.type === "SOLID") { @@ -64,34 +74,6 @@ export const swiftuiSolidColor = ( return ""; }; -/** - * Get SwiftUI background for a node - * @param node SceneNode containing the property to examine - * @param propertyPath Property path to extract fills from (e.g., 'fills', 'strokes') or direct fills array - */ -export const swiftuiBackground = ( - node: SceneNode, - propertyPath: string, -): string => { - const fills = node[propertyPath as keyof SceneNode] as - | ReadonlyArray - | PluginAPI["mixed"]; - - const fill = retrieveTopFill(fills); - - if (fill && fill.type === "SOLID") { - const opacity = fill.opacity ?? 1.0; - return swiftuiColor(fill.color, opacity); - } else if (fill?.type === "GRADIENT_LINEAR") { - return swiftuiGradient(fill); - } else if (fill?.type === "IMAGE") { - addWarning("Image fills are replaced with placeholders"); - return `AsyncImage(url: URL(string: "${getPlaceholderImage(node.width, node.height)}"))`; - } - - return ""; -}; - export const swiftuiGradient = (fill: GradientPaint): string => { const direction = gradientDirection(gradientAngle(fill)); diff --git a/packages/backend/src/swiftui/swiftuiDefaultBuilder.ts b/packages/backend/src/swiftui/swiftuiDefaultBuilder.ts index 7ae8d73e..3648877f 100644 --- a/packages/backend/src/swiftui/swiftuiDefaultBuilder.ts +++ b/packages/backend/src/swiftui/swiftuiDefaultBuilder.ts @@ -4,7 +4,6 @@ import { swiftuiBorder, swiftuiCornerRadius, } from "./builderImpl/swiftuiBorder"; -import { swiftuiBackground } from "./builderImpl/swiftuiColor"; import { swiftuiPadding } from "./builderImpl/swiftuiPadding"; import { swiftuiSize } from "./builderImpl/swiftuiSize"; @@ -20,6 +19,7 @@ import { } from "../common/commonPosition"; import { SwiftUIElement } from "./builderImpl/swiftuiParser"; import { SwiftUIModifier } from "types"; +import { swiftuiSolidColor } from "./builderImpl/swiftuiColor"; export class SwiftuiDefaultBuilder { element: SwiftUIElement; @@ -105,7 +105,7 @@ export class SwiftuiDefaultBuilder { shapeBackground(node: SceneNode): this { if ("fills" in node) { - const background = swiftuiBackground(node, "fills"); + const background = swiftuiSolidColor(node, "fills"); if (background) { this.pushModifier([`background`, background]); } diff --git a/packages/backend/src/swiftui/swiftuiTextBuilder.ts b/packages/backend/src/swiftui/swiftuiTextBuilder.ts index e1438f44..56307bd3 100644 --- a/packages/backend/src/swiftui/swiftuiTextBuilder.ts +++ b/packages/backend/src/swiftui/swiftuiTextBuilder.ts @@ -6,10 +6,10 @@ import { import { SwiftuiDefaultBuilder } from "./swiftuiDefaultBuilder"; import { swiftuiWeightMatcher } from "./builderImpl/swiftuiTextWeight"; import { swiftuiSize } from "./builderImpl/swiftuiSize"; -import { globalTextStyleSegments } from "../altNodes/altConversion"; import { SwiftUIElement } from "./builderImpl/swiftuiParser"; import { parseTextAsCode } from "../flutter/flutterTextBuilder"; -import { swiftuiSolidColor } from "./builderImpl/swiftuiColor"; +import { swiftuiSolidColorFromDirectFills } from "./builderImpl/swiftuiColor"; +import { StyledTextSegmentSubset } from "types"; export class SwiftuiTextBuilder extends SwiftuiDefaultBuilder { node?: TextNode; @@ -42,7 +42,7 @@ export class SwiftuiTextBuilder extends SwiftuiDefaultBuilder { } textColor(fills: Paint[]): string { - const fillColor = swiftuiSolidColor(fills); + const fillColor = swiftuiSolidColorFromDirectFills(fills); if (fillColor) { return fillColor; } @@ -92,7 +92,7 @@ export class SwiftuiTextBuilder extends SwiftuiDefaultBuilder { // alignHorizontal !== "left" ? `TextAlign.${alignHorizontal}` : "", // }; - const segments = this.getTextSegments(node.id, node.characters); + const segments = this.getTextSegments(node, node.characters); if (segments) { this.element = segments; } else { @@ -102,8 +102,9 @@ export class SwiftuiTextBuilder extends SwiftuiDefaultBuilder { return this; } - getTextSegments(id: string, characters: string): SwiftUIElement | null { - const segments = globalTextStyleSegments[id]; + getTextSegments(node: TextNode, characters: string): SwiftUIElement | null { + const segments = (node as any) + .styledTextSegments as StyledTextSegmentSubset[]; if (!segments) { return null; } @@ -261,9 +262,7 @@ export class SwiftuiTextBuilder extends SwiftuiDefaultBuilder { const blurRadius = Math.round(ds.radius); return `.shadow(color: Color(red: ${ds.color.r.toFixed( 2, - )}, green: ${ds.color.g.toFixed( - 2, - )}, blue: ${ds.color.b.toFixed( + )}, green: ${ds.color.g.toFixed(2)}, blue: ${ds.color.b.toFixed( 2, )}, opacity: ${ds.color.a.toFixed( 2, diff --git a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts index f1b616ad..d40c9e6d 100644 --- a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts +++ b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts @@ -125,6 +125,7 @@ export class TailwindDefaultBuilder { if (commonIsAbsolutePosition(node, optimizeLayout)) { const { x, y } = getCommonPositionValue(node); + console.log("x", x, y); const parsedX = numberToFixedString(x); const parsedY = numberToFixedString(y); @@ -189,21 +190,21 @@ export class TailwindDefaultBuilder { const { node, optimizeLayout } = this; const { width, height } = tailwindSizePartial(node, optimizeLayout); - // if (node.type === "TEXT") { - // switch (node.textAutoResize) { - // case "WIDTH_AND_HEIGHT": - // break; - // case "HEIGHT": - // this.addAttributes(width); - // break; - // case "NONE": - // case "TRUNCATE": - // this.addAttributes(width, height); - // break; - // } - // } else { - this.addAttributes(width, height); - // } + if (node.type === "TEXT") { + switch (node.textAutoResize) { + case "WIDTH_AND_HEIGHT": + break; + case "HEIGHT": + this.addAttributes(width); + break; + case "NONE": + case "TRUNCATE": + this.addAttributes(width, height); + break; + } + } else { + this.addAttributes(width, height); + } return this; } diff --git a/packages/backend/src/tailwind/tailwindMain.ts b/packages/backend/src/tailwind/tailwindMain.ts index aa1613c8..6074aef9 100644 --- a/packages/backend/src/tailwind/tailwindMain.ts +++ b/packages/backend/src/tailwind/tailwindMain.ts @@ -124,7 +124,7 @@ export const tailwindText = ( .commonPositionStyles() .textAlign(); - const styledHtml = layoutBuilder.getTextSegments(node.id); + const styledHtml = layoutBuilder.getTextSegments(node); previousExecutionCache.push(...styledHtml); let content = ""; diff --git a/packages/backend/src/tailwind/tailwindTextBuilder.ts b/packages/backend/src/tailwind/tailwindTextBuilder.ts index ca3518e2..de5e7e4c 100644 --- a/packages/backend/src/tailwind/tailwindTextBuilder.ts +++ b/packages/backend/src/tailwind/tailwindTextBuilder.ts @@ -1,4 +1,3 @@ -import { globalTextStyleSegments } from "../altNodes/altConversion"; import { commonLetterSpacing, commonLineHeight, @@ -12,14 +11,17 @@ import { } from "./conversionTables"; import { TailwindDefaultBuilder } from "./tailwindDefaultBuilder"; import { config } from "./tailwindConfig"; +import { StyledTextSegmentSubset } from "types"; export class TailwindTextBuilder extends TailwindDefaultBuilder { - getTextSegments(id: string): { + getTextSegments(node: TextNode): { style: string; text: string; openTypeFeatures: { [key: string]: boolean }; }[] { - const segments = globalTextStyleSegments[id]; + const segments = (node as any) + .styledTextSegments as StyledTextSegmentSubset[]; + if (!segments) { return []; } @@ -53,9 +55,11 @@ export class TailwindTextBuilder extends TailwindDefaultBuilder { blurStyle, shadowStyle, ] - .filter((d) => d !== "") + .filter(Boolean) .join(" "); + console.log("styleClasses", styleClasses, segment); + const charsWithLineBreak = segment.characters.split("\n").join("
"); return { style: styleClasses, From 496566832224f0c6d53407d0f9ac207e545d117a Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Tue, 4 Mar 2025 13:03:56 -0300 Subject: [PATCH 048/168] try to improve color --- .../plugin-ui/src/components/ColorsPanel.tsx | 18 +++++++++++++----- .../src/components/GradientsPanel.tsx | 18 ++++++++++++++---- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/packages/plugin-ui/src/components/ColorsPanel.tsx b/packages/plugin-ui/src/components/ColorsPanel.tsx index dfd19d32..32ed866c 100644 --- a/packages/plugin-ui/src/components/ColorsPanel.tsx +++ b/packages/plugin-ui/src/components/ColorsPanel.tsx @@ -1,4 +1,3 @@ -import React from "react"; import { useState } from "react"; import { SolidColorConversion } from "types"; @@ -15,10 +14,19 @@ const ColorsPanel = (props: { }; return ( -
-

- Colors -

+
+
+
+

+ {/*
*/} + Color Palette +

+ + {props.colors.length} color{props.colors.length > 1 ? "s" : ""} + +
+
+
{props.colors.map((color, idx) => ( + )} + + {(expanded || alwaysExpanded) && ( +
+ {/* Render preference toggles if any */} + {settings.length > 0 && ( +
+ {settings.map((preference) => ( + { + onPreferenceChanged?.(preference.propertyName, value); + }} + buttonClass="bg-green-100 dark:bg-black dark:ring-green-800 ring-green-500" + checkClass="bg-green-400 dark:bg-black dark:bg-green-500 dark:border-green-500 ring-green-300 border-green-400" + /> + ))} +
+ )} + {children} +
+ )} +
+ ); +}; + +export default SettingsGroup; diff --git a/packages/plugin-ui/src/components/WarningsPanel.tsx b/packages/plugin-ui/src/components/WarningsPanel.tsx index 5b7c4683..0ca36ff5 100644 --- a/packages/plugin-ui/src/components/WarningsPanel.tsx +++ b/packages/plugin-ui/src/components/WarningsPanel.tsx @@ -183,8 +183,8 @@ const WarningsPanel: React.FC = ({ warnings }) => { {/* Help text - balanced size */} {displayedWarnings.length > 0 && ( -
- +
+ {/* */} Addressing warnings can improve the quality of the generated code. From f3d178e9e9bd2a70536a829a94e9fe73d57c01c8 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Wed, 5 Mar 2025 03:14:26 -0300 Subject: [PATCH 058/168] All new settings part 2 --- .../plugin-ui/src/components/CodePanel.tsx | 49 ++--- .../src/components/CustomPrefixInput.tsx | 177 ++++++++++++++++++ .../src/components/SettingsGroup.tsx | 21 ++- 3 files changed, 209 insertions(+), 38 deletions(-) create mode 100644 packages/plugin-ui/src/components/CustomPrefixInput.tsx diff --git a/packages/plugin-ui/src/components/CodePanel.tsx b/packages/plugin-ui/src/components/CodePanel.tsx index 298efa8a..26f139b3 100644 --- a/packages/plugin-ui/src/components/CodePanel.tsx +++ b/packages/plugin-ui/src/components/CodePanel.tsx @@ -4,13 +4,14 @@ import { PluginSettings, SelectPreferenceOptions, } from "types"; -import { useMemo, useState } from "react"; +import { useMemo, useState, useEffect } from "react"; import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; import { coldarkDark as theme } from "react-syntax-highlighter/dist/esm/styles/prism"; import SelectableToggle from "./SelectableToggle"; import { CopyButton } from "./CopyButton"; import EmptyState from "./EmptyState"; import SettingsGroup from "./SettingsGroup"; +import CustomPrefixInput from "./CustomPrefixInput"; interface CodePanelProps { code: string; @@ -36,12 +37,6 @@ const CodePanel = (props: CodePanelProps) => { } = props; const isCodeEmpty = code === ""; - // State for custom prefix for Tailwind classes. - // It is initially set from settings (if available) or an empty string. - const [customPrefix, setCustomPrefix] = useState( - settings?.customTailwindPrefix || "", - ); - // Helper function to add the prefix before every class (or className) in the code. // It finds every occurrence of class="..." or className="..." and, for each class, // prepends the custom prefix. @@ -61,8 +56,9 @@ const CodePanel = (props: CodePanelProps) => { // If the selected framework is Tailwind and a prefix is provided then transform the code. const prefixedCode = - selectedFramework === "Tailwind" && customPrefix.trim() !== "" - ? applyPrefixToClasses(code, customPrefix) + selectedFramework === "Tailwind" && + settings?.customTailwindPrefix?.trim() !== "" + ? applyPrefixToClasses(code, settings.customTailwindPrefix) : code; const handleButtonHover = () => setSyntaxHovered(true); @@ -107,25 +103,10 @@ const CodePanel = (props: CodePanelProps) => { }; }, [preferenceOptions, selectPreferenceOptions, selectedFramework]); - // Create the custom prefix input component - const CustomPrefixInput = () => ( -
- - { - const newVal = e.target.value; - setCustomPrefix(newVal); - onPreferenceChanged("customTailwindPrefix", newVal); - }} - placeholder="e.g., tw-" - className="p-1.5 px-2 border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-neutral-800 text-sm w-full max-w-xs" - /> -
- ); + // Handle custom prefix change + const handleCustomPrefixChange = (newValue: string) => { + onPreferenceChanged("customTailwindPrefix", newValue); + }; return (
@@ -146,7 +127,7 @@ const CodePanel = (props: CodePanelProps) => {
{/* Essential settings always shown */} { /> {/* Styling preferences with custom prefix for Tailwind */} - {stylingPreferences.length > 0 && ( + {(stylingPreferences.length > 0 || + selectedFramework === "Tailwind") && ( - {selectedFramework === "Tailwind" && } + {selectedFramework === "Tailwind" && ( + + )} )} diff --git a/packages/plugin-ui/src/components/CustomPrefixInput.tsx b/packages/plugin-ui/src/components/CustomPrefixInput.tsx new file mode 100644 index 00000000..2d6bd669 --- /dev/null +++ b/packages/plugin-ui/src/components/CustomPrefixInput.tsx @@ -0,0 +1,177 @@ +import React, { useState, useRef, useEffect } from "react"; +import { HelpCircle, Check } from "lucide-react"; + +interface CustomPrefixInputProps { + initialValue: string; + onValueChange: (value: string) => void; +} + +const CustomPrefixInput = React.memo(({ initialValue, onValueChange }: CustomPrefixInputProps) => { + // Use internal state to manage the input value + const [inputValue, setInputValue] = useState(initialValue); + const [isFocused, setIsFocused] = useState(false); + const [hasChanges, setHasChanges] = useState(false); + const [showSuccess, setShowSuccess] = useState(false); + const inputRef = useRef(null); + + // Update internal state when initialValue changes (from parent) + useEffect(() => { + setInputValue(initialValue); + setHasChanges(false); + }, [initialValue]); + + const examples = ["flex"]; + const hasInvalidChars = /\s/.test(inputValue); + + const handleChange = (e: React.ChangeEvent) => { + const newValue = e.target.value; + setInputValue(newValue); + setHasChanges(newValue !== initialValue); + }; + + const applyChanges = () => { + if (hasInvalidChars) return; + + onValueChange(inputValue); + setHasChanges(false); + + // Show success indicator briefly + setShowSuccess(true); + setTimeout(() => setShowSuccess(false), 1500); + }; + + const handleBlur = () => { + setIsFocused(false); + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + e.preventDefault(); + applyChanges(); + inputRef.current?.blur(); + } + }; + + return ( +
+
+ + +
+ +
+ Add a prefix to all generated Tailwind classes. +
+ Useful for avoiding conflicts with existing CSS. +
+
+
+ + {showSuccess && ( + + Applied + + )} +
+ +
+
+ setIsFocused(true)} + onBlur={handleBlur} + onKeyDown={handleKeyDown} + placeholder="e.g., tw-" + className={`p-1.5 px-2.5 border rounded-md text-sm w-full transition-all focus:outline-none ${ + hasInvalidChars + ? "border-red-300 dark:border-red-700 bg-red-50 dark:bg-red-900/20" + : isFocused + ? "border-green-400 dark:border-green-600 ring-1 ring-green-300 dark:ring-green-800 bg-white dark:bg-neutral-800" + : "border-gray-300 dark:border-gray-600 bg-white dark:bg-neutral-800 hover:border-gray-400 dark:hover:border-gray-500" + }`} + /> + + {hasInvalidChars && ( +

+ Prefix cannot contain spaces +

+ )} +
+ + {hasChanges && ( + + )} +
+ + {inputValue && !hasInvalidChars && ( +
+

+ Preview{hasChanges ? " (not applied yet)" : ""}: +

+
+ {examples.map((example) => ( +
+
+ + {inputValue} + + + {example} + +
+ + → + +
+ {example} +
+
+ ))} +
+ + {hasChanges && ( +

+ Press Enter or click Done to apply changes +

+ )} +
+ )} +
+ ); +}); + +CustomPrefixInput.displayName = "CustomPrefixInput"; + +// Add a keyframe for fade-in-out animation +if (typeof document !== 'undefined') { + const style = document.createElement('style'); + style.innerHTML = ` + @keyframes fadeInOut { + 0% { opacity: 0; } + 20% { opacity: 1; } + 80% { opacity: 1; } + 100% { opacity: 0; } + } + .animate-fade-in-out { + animation: fadeInOut 1.5s ease-in-out; + } + `; + document.head.appendChild(style); +} + +export default CustomPrefixInput; diff --git a/packages/plugin-ui/src/components/SettingsGroup.tsx b/packages/plugin-ui/src/components/SettingsGroup.tsx index 69854309..8115d31d 100644 --- a/packages/plugin-ui/src/components/SettingsGroup.tsx +++ b/packages/plugin-ui/src/components/SettingsGroup.tsx @@ -32,24 +32,31 @@ const SettingsGroup: React.FC = ({ } return ( -
- {!alwaysExpanded && ( +
+ {alwaysExpanded ? ( +
+ + {title} + +
+ ) : ( )} {(expanded || alwaysExpanded) && ( -
+
{/* Render preference toggles if any */} {settings.length > 0 && (
From 83420b8a13ba080b4750190c1f81d160621588aa Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Wed, 5 Mar 2025 03:16:59 -0300 Subject: [PATCH 059/168] Fix lint --- packages/plugin-ui/src/components/CodePanel.tsx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/plugin-ui/src/components/CodePanel.tsx b/packages/plugin-ui/src/components/CodePanel.tsx index 26f139b3..6b2db900 100644 --- a/packages/plugin-ui/src/components/CodePanel.tsx +++ b/packages/plugin-ui/src/components/CodePanel.tsx @@ -40,7 +40,14 @@ const CodePanel = (props: CodePanelProps) => { // Helper function to add the prefix before every class (or className) in the code. // It finds every occurrence of class="..." or className="..." and, for each class, // prepends the custom prefix. - const applyPrefixToClasses = (codeString: string, prefix: string) => { + const applyPrefixToClasses = ( + codeString: string, + prefix: string | undefined, + ) => { + if (!prefix) { + return codeString; + } + return codeString.replace( /(class(?:Name)?)="([^"]*)"/g, (match, attr, classes) => { @@ -58,7 +65,7 @@ const CodePanel = (props: CodePanelProps) => { const prefixedCode = selectedFramework === "Tailwind" && settings?.customTailwindPrefix?.trim() !== "" - ? applyPrefixToClasses(code, settings.customTailwindPrefix) + ? applyPrefixToClasses(code, settings?.customTailwindPrefix) : code; const handleButtonHover = () => setSyntaxHovered(true); From 456feb941cd38a47a843fb4c0d06a81f11d80100 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Wed, 5 Mar 2025 15:07:53 -0300 Subject: [PATCH 060/168] Improve rounding mechanism --- packages/backend/src/altNodes/altNodeUtils.ts | 1 + packages/backend/src/html/htmlMain.ts | 12 +++++-- .../backend/src/tailwind/conversionTables.ts | 33 +++++++++++++++++-- packages/backend/src/tailwind/tailwindMain.ts | 14 ++++++-- .../plugin-ui/src/codegenPreferenceOptions.ts | 5 +-- .../plugin-ui/src/components/CodePanel.tsx | 18 ++-------- 6 files changed, 57 insertions(+), 26 deletions(-) diff --git a/packages/backend/src/altNodes/altNodeUtils.ts b/packages/backend/src/altNodes/altNodeUtils.ts index 738db589..5ede70ca 100644 --- a/packages/backend/src/altNodes/altNodeUtils.ts +++ b/packages/backend/src/altNodes/altNodeUtils.ts @@ -61,6 +61,7 @@ export const renderAndAttachSVG = async (node: any) => { // console.log(altNode); if (node.canBeFlattened) { console.log("altNode is", node); + if (node.svg) { // console.log(`SVG already rendered for ${nodeName}`); return node; diff --git a/packages/backend/src/html/htmlMain.ts b/packages/backend/src/html/htmlMain.ts index 9bbb3168..40788221 100644 --- a/packages/backend/src/html/htmlMain.ts +++ b/packages/backend/src/html/htmlMain.ts @@ -88,9 +88,11 @@ const htmlWidgetGenerator = async ( const convertNode = (settings: HTMLSettings) => async (node: SceneNode) => { console.log("converting", node); - if ((node as any).canBeFlattened) { + if (settings.embedVectors && (node as any).canBeFlattened) { const altNode = await renderAndAttachSVG(node); - if (altNode.svg) return htmlWrapSVG(altNode, settings); + if (altNode.svg) { + return htmlWrapSVG(altNode, settings); + } } switch (node.type) { @@ -111,6 +113,10 @@ const convertNode = (settings: HTMLSettings) => async (node: SceneNode) => { case "LINE": return htmlLine(node, settings); case "VECTOR": + addWarning( + "VectorNodes are not supported in HTML. They can be converted via Embed Vector setting", + ); + return node; throw new Error( "Normally vector type nodes are converted to SVG so this code point should be unreachable.", ); @@ -273,7 +279,7 @@ const htmlContainer = async ( ) { imgUrl = (await exportNodeAsBase64PNG(altNode, hasChildren)) ?? ""; } else { - addWarning("Some images were exported as placeholder URLs"); + // addWarning("Some images were exported as placeholder URLs"); imgUrl = getPlaceholderImage(node.width, node.height); } diff --git a/packages/backend/src/tailwind/conversionTables.ts b/packages/backend/src/tailwind/conversionTables.ts index 457e073c..884fd4e0 100644 --- a/packages/backend/src/tailwind/conversionTables.ts +++ b/packages/backend/src/tailwind/conversionTables.ts @@ -10,6 +10,22 @@ export const nearestValue = (goal: number, array: Array): number => { }); }; +// New function to get nearest value only if it's within acceptable threshold +export const nearestValueWithThreshold = ( + goal: number, + array: Array, + thresholdPercent: number = 15, +): number | null => { + const nearest = nearestValue(goal, array); + const diff = Math.abs(nearest - goal); + const percentDiff = (diff / goal) * 100; + + if (percentDiff <= thresholdPercent) { + return nearest; + } + return null; +}; + export const exactValue = ( goal: number, array: Array, @@ -36,12 +52,18 @@ const pxToRemToTailwind = ( conversionMap: Record, ): string => { const keys = Object.keys(conversionMap).map((d) => +d); - const convertedValue = exactValue(value / 16, keys); + const remValue = value / 16; + const convertedValue = exactValue(remValue, keys); if (convertedValue) { return conversionMap[convertedValue]; } else if (localTailwindSettings.roundTailwindValues) { - return conversionMap[nearestValue(value / 16, keys)]; + // Only round if the nearest value is within acceptable threshold + const thresholdValue = nearestValueWithThreshold(remValue, keys, 15); + + if (thresholdValue !== null) { + return conversionMap[thresholdValue]; + } } return `[${numberToFixedString(value)}px]`; @@ -57,7 +79,12 @@ const pxToTailwind = ( if (convertedValue) { return conversionMap[convertedValue]; } else if (localTailwindSettings.roundTailwindValues) { - return conversionMap[nearestValue(value, keys)]; + // Only round if the nearest value is within acceptable threshold + const thresholdValue = nearestValueWithThreshold(value, keys, 15); + + if (thresholdValue !== null) { + return conversionMap[thresholdValue]; + } } return `[${numberToFixedString(value)}px]`; diff --git a/packages/backend/src/tailwind/tailwindMain.ts b/packages/backend/src/tailwind/tailwindMain.ts index c0138e06..57d763ab 100644 --- a/packages/backend/src/tailwind/tailwindMain.ts +++ b/packages/backend/src/tailwind/tailwindMain.ts @@ -11,6 +11,12 @@ import { renderAndAttachSVG } from "../altNodes/altNodeUtils"; import { AltNode, PluginSettings, TailwindSettings } from "types"; export let localTailwindSettings: PluginSettings; +localTailwindSettings = { + // ...existing settings... + roundTailwindValues: true, + roundingThreshold: 15, // Maximum % difference allowed for rounding (e.g., 15%) + // ...existing settings... +}; let previousExecutionCache: { style: string; text: string; @@ -49,9 +55,11 @@ const convertNode = (settings: TailwindSettings) => async (node: SceneNode): Promise => { console.log("altNode", node); - const altNode = await renderAndAttachSVG(node); - if (altNode.svg) { - return tailwindWrapSVG(altNode, settings); + if (settings.embedVectors && (node as any).canBeFlattened) { + const altNode = await renderAndAttachSVG(node); + if (altNode.svg) { + return tailwindWrapSVG(altNode, settings); + } } switch (node.type) { diff --git a/packages/plugin-ui/src/codegenPreferenceOptions.ts b/packages/plugin-ui/src/codegenPreferenceOptions.ts index 77619c3e..25972488 100644 --- a/packages/plugin-ui/src/codegenPreferenceOptions.ts +++ b/packages/plugin-ui/src/codegenPreferenceOptions.ts @@ -29,7 +29,8 @@ export const preferenceOptions: LocalCodegenPreferenceOptions[] = [ itemType: "individual_select", propertyName: "roundTailwindValues", label: "Round values", - description: "Round pixel values to nearest Tailwind sizes", + description: + "Round pixel values to nearest Tailwind sizes (within a 15% range)", isDefault: false, includedLanguages: ["Tailwind"], }, @@ -63,7 +64,7 @@ export const preferenceOptions: LocalCodegenPreferenceOptions[] = [ label: "Embed Vectors", description: "Convert vectors in the code.", isDefault: false, - includedLanguages: ["HTML"], + includedLanguages: ["HTML", "Tailwind"], }, // Add your preferences data here ]; diff --git a/packages/plugin-ui/src/components/CodePanel.tsx b/packages/plugin-ui/src/components/CodePanel.tsx index 6b2db900..99bad9e7 100644 --- a/packages/plugin-ui/src/components/CodePanel.tsx +++ b/packages/plugin-ui/src/components/CodePanel.tsx @@ -75,7 +75,6 @@ const CodePanel = (props: CodePanelProps) => { const { essentialPreferences, stylingPreferences, - advancedPreferences, selectableSettingsFiltered, } = useMemo(() => { // Get preferences for the current framework @@ -90,8 +89,9 @@ const CodePanel = (props: CodePanelProps) => { "roundTailwindColors", "customTailwindColors", "showLayerNames", + "embedImages", + "embedVectors", ]; - const advancedPropertyNames = ["embedImages", "embedVectors"]; // Group preferences by category return { @@ -101,9 +101,7 @@ const CodePanel = (props: CodePanelProps) => { stylingPreferences: frameworkPreferences.filter((p) => stylingPropertyNames.includes(p.propertyName), ), - advancedPreferences: frameworkPreferences.filter((p) => - advancedPropertyNames.includes(p.propertyName), - ), + selectableSettingsFiltered: selectPreferenceOptions.filter((p) => p.includedLanguages?.includes(selectedFramework), ), @@ -159,16 +157,6 @@ const CodePanel = (props: CodePanelProps) => { )} - {/* Advanced settings */} - {advancedPreferences.length > 0 && ( - - )} - {/* Framework-specific options */} {selectableSettingsFiltered.length > 0 && (
From 1b2a41d7585ac683bd78accf914dd1508164d158 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Wed, 5 Mar 2025 20:49:05 -0300 Subject: [PATCH 061/168] Trim layer, improve warning, improve vector --- apps/plugin/plugin-src/code.ts | 2 +- .../backend/src/html/builderImpl/htmlColor.ts | 4 +- .../backend/src/html/htmlDefaultBuilder.ts | 4 +- packages/backend/src/html/htmlMain.ts | 38 ++++++++++--------- .../src/tailwind/builderImpl/tailwindColor.ts | 2 +- .../backend/src/tailwind/conversionTables.ts | 2 +- .../src/tailwind/tailwindDefaultBuilder.ts | 2 +- packages/backend/src/tailwind/tailwindMain.ts | 17 ++++----- .../plugin-ui/src/codegenPreferenceOptions.ts | 23 +++++------ .../plugin-ui/src/components/CodePanel.tsx | 2 +- packages/types/src/types.ts | 4 +- 11 files changed, 51 insertions(+), 49 deletions(-) diff --git a/apps/plugin/plugin-src/code.ts b/apps/plugin/plugin-src/code.ts index 093809bb..07978a0a 100644 --- a/apps/plugin/plugin-src/code.ts +++ b/apps/plugin/plugin-src/code.ts @@ -28,7 +28,7 @@ export const defaultPluginSettings: PluginSettings = { swiftUIGenerationMode: "snippet", roundTailwindValues: false, roundTailwindColors: false, - customTailwindColors: false, + useColorVariables: false, customTailwindPrefix: "", embedImages: false, embedVectors: false, diff --git a/packages/backend/src/html/builderImpl/htmlColor.ts b/packages/backend/src/html/builderImpl/htmlColor.ts index 336c2b27..3defdc16 100644 --- a/packages/backend/src/html/builderImpl/htmlColor.ts +++ b/packages/backend/src/html/builderImpl/htmlColor.ts @@ -55,7 +55,7 @@ export const htmlColorFromFills = ( fills: ReadonlyArray | PluginAPI["mixed"] | undefined, settings: HTMLSettings, ): string => { - const useCustomColors = settings.customTailwindColors === true; + const useCustomColors = settings.useColorVariables === true; const fill = retrieveTopFill(fills); if (fill) { const { color, opacity, boundVariable } = getColorAndVariable(fill); @@ -133,7 +133,7 @@ export const htmlGradientFromFills = ( fills: ReadonlyArray | PluginAPI["mixed"], settings: HTMLSettings, ): string => { - const useCustomColors = settings.customTailwindColors === true; + const useCustomColors = settings.useColorVariables === true; const fill = retrieveTopFill(fills); if (!fill) return ""; switch (fill.type) { diff --git a/packages/backend/src/html/htmlDefaultBuilder.ts b/packages/backend/src/html/htmlDefaultBuilder.ts index 40204be5..d5cfda84 100644 --- a/packages/backend/src/html/htmlDefaultBuilder.ts +++ b/packages/backend/src/html/htmlDefaultBuilder.ts @@ -331,8 +331,8 @@ export class HtmlDefaultBuilder { let classAttribute = ""; if (this.name) { - this.addData("layer", this.name); - const layerNameClass = stringToClassName(this.name); + this.addData("layer", this.name.trim()); + const layerNameClass = stringToClassName(this.name.trim()); classAttribute = formatClassAttribute( layerNameClass === "" ? [] : [layerNameClass], this.isJSX, diff --git a/packages/backend/src/html/htmlMain.ts b/packages/backend/src/html/htmlMain.ts index 40788221..b1d6389f 100644 --- a/packages/backend/src/html/htmlMain.ts +++ b/packages/backend/src/html/htmlMain.ts @@ -49,18 +49,19 @@ export const generateHTMLPreview = async ( settings: PluginSettings, code?: string, ): Promise => { - const htmlCodeAlreadyGenerated = - settings.framework === "HTML" && settings.jsx === false && code; - const htmlCode = htmlCodeAlreadyGenerated - ? code - : await htmlMain( - nodes, - { - ...settings, - jsx: false, - }, - true, - ); + // const htmlCodeAlreadyGenerated = + // settings.framework === "HTML" && settings.jsx === false && code; + const htmlCode = + // htmlCodeAlreadyGenerated + // ? code : + await htmlMain( + nodes, + { + ...settings, + jsx: false, + }, + true, + ); return { size: { @@ -113,14 +114,15 @@ const convertNode = (settings: HTMLSettings) => async (node: SceneNode) => { case "LINE": return htmlLine(node, settings); case "VECTOR": - addWarning( - "VectorNodes are not supported in HTML. They can be converted via Embed Vector setting", - ); - return node; - throw new Error( - "Normally vector type nodes are converted to SVG so this code point should be unreachable.", + addWarning("Vector is not supported"); + return await htmlContainer( + { ...node, type: "RECTANGLE" } as any, + "", + [], + settings, ); default: + addWarning(`${node.type} node is not supported`); return ""; } }; diff --git a/packages/backend/src/tailwind/builderImpl/tailwindColor.ts b/packages/backend/src/tailwind/builderImpl/tailwindColor.ts index a969b933..1e0df04a 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindColor.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindColor.ts @@ -14,7 +14,7 @@ import { retrieveTopFill } from "../../common/retrieveFill"; */ export function tailwindColor(fill: SolidPaint) { const { hex, colorType, colorName, meta } = getColorInfo(fill); - const exportValue = tailwindSolidColor(fill, "solid"); + const exportValue = tailwindSolidColor(fill, "bg"); return { exportValue, colorName, diff --git a/packages/backend/src/tailwind/conversionTables.ts b/packages/backend/src/tailwind/conversionTables.ts index 884fd4e0..3b52756d 100644 --- a/packages/backend/src/tailwind/conversionTables.ts +++ b/packages/backend/src/tailwind/conversionTables.ts @@ -163,7 +163,7 @@ export function getColorInfo(fill: SolidPaint | ColorStop) { // variable if ( - localTailwindSettings.customTailwindColors && + localTailwindSettings.useColorVariables && fill.boundVariables?.color ) { colorName = variableToColorName(fill.boundVariables.color); diff --git a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts index d40c9e6d..2d5df811 100644 --- a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts +++ b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts @@ -265,7 +265,7 @@ export class TailwindDefaultBuilder { this.prependAttributes(stringToClassName(this.name)); } if (this.name) { - this.addData("layer", this.name); + this.addData("layer", this.name.trim()); } if ("variantProperties" in this.node && this.node.variantProperties) { diff --git a/packages/backend/src/tailwind/tailwindMain.ts b/packages/backend/src/tailwind/tailwindMain.ts index 57d763ab..561e8be8 100644 --- a/packages/backend/src/tailwind/tailwindMain.ts +++ b/packages/backend/src/tailwind/tailwindMain.ts @@ -11,12 +11,6 @@ import { renderAndAttachSVG } from "../altNodes/altNodeUtils"; import { AltNode, PluginSettings, TailwindSettings } from "types"; export let localTailwindSettings: PluginSettings; -localTailwindSettings = { - // ...existing settings... - roundTailwindValues: true, - roundingThreshold: 15, // Maximum % difference allowed for rounding (e.g., 15%) - // ...existing settings... -}; let previousExecutionCache: { style: string; text: string; @@ -80,10 +74,15 @@ const convertNode = case "SECTION": return tailwindSection(node, settings); case "VECTOR": - addWarning("VectorNodes are not supported in Tailwind"); - break; + addWarning("Vector is not supported"); + return tailwindContainer( + { ...node, type: "RECTANGLE" } as any, + "", + "", + settings, + ); default: - addWarning(`${node.type} nodes are not supported in Tailwind`); + addWarning(`${node.type} node is not supported`); } return ""; }; diff --git a/packages/plugin-ui/src/codegenPreferenceOptions.ts b/packages/plugin-ui/src/codegenPreferenceOptions.ts index 25972488..7516b261 100644 --- a/packages/plugin-ui/src/codegenPreferenceOptions.ts +++ b/packages/plugin-ui/src/codegenPreferenceOptions.ts @@ -5,7 +5,7 @@ export const preferenceOptions: LocalCodegenPreferenceOptions[] = [ itemType: "individual_select", propertyName: "jsx", label: "React (JSX)", - description: 'Render "class" attributes as "className"', + description: "", isDefault: false, includedLanguages: ["HTML", "Tailwind"], }, @@ -13,7 +13,8 @@ export const preferenceOptions: LocalCodegenPreferenceOptions[] = [ itemType: "individual_select", propertyName: "optimizeLayout", label: "Optimize layout", - description: "Attempt to auto-layout suitable element groups", + description: + "Attempt to auto-layout suitable element groups. This may increase code quality, but may not always work as expected.", isDefault: true, includedLanguages: ["HTML", "Tailwind", "Flutter", "SwiftUI"], }, @@ -21,7 +22,7 @@ export const preferenceOptions: LocalCodegenPreferenceOptions[] = [ itemType: "individual_select", propertyName: "showLayerNames", label: "Layer names", - description: "Include layer names in classes", + description: "Include Figma layer names in classes.", isDefault: false, includedLanguages: ["HTML", "Tailwind"], }, @@ -30,7 +31,7 @@ export const preferenceOptions: LocalCodegenPreferenceOptions[] = [ propertyName: "roundTailwindValues", label: "Round values", description: - "Round pixel values to nearest Tailwind sizes (within a 15% range)", + "Round pixel values to nearest Tailwind sizes (within a 15% range).", isDefault: false, includedLanguages: ["Tailwind"], }, @@ -38,15 +39,16 @@ export const preferenceOptions: LocalCodegenPreferenceOptions[] = [ itemType: "individual_select", propertyName: "roundTailwindColors", label: "Round colors", - description: "Round color values to nearest Tailwind colors", + description: "Round Figma color values to nearest Tailwind colors.", isDefault: false, includedLanguages: ["Tailwind"], }, { itemType: "individual_select", - propertyName: "customTailwindColors", - label: "Color variables", - description: "Use color variable names as custom color names", + propertyName: "useColorVariables", + label: "Color Variables", + description: + "Export code using Figma variables as colors. Example: 'bg-background' instead of 'bg-white'.", isDefault: false, includedLanguages: ["HTML", "Tailwind"], }, @@ -54,7 +56,7 @@ export const preferenceOptions: LocalCodegenPreferenceOptions[] = [ itemType: "individual_select", propertyName: "embedImages", label: "Embed Images", - description: "Convert images to Base64 and embed them in the code.", + description: "Convert Figma images to Base64 and embed them in the code.", isDefault: false, includedLanguages: ["HTML"], }, @@ -62,11 +64,10 @@ export const preferenceOptions: LocalCodegenPreferenceOptions[] = [ itemType: "individual_select", propertyName: "embedVectors", label: "Embed Vectors", - description: "Convert vectors in the code.", + description: "Convert Figma vectors to code.", isDefault: false, includedLanguages: ["HTML", "Tailwind"], }, - // Add your preferences data here ]; export const selectPreferenceOptions: SelectPreferenceOptions[] = [ diff --git a/packages/plugin-ui/src/components/CodePanel.tsx b/packages/plugin-ui/src/components/CodePanel.tsx index 99bad9e7..96801f5e 100644 --- a/packages/plugin-ui/src/components/CodePanel.tsx +++ b/packages/plugin-ui/src/components/CodePanel.tsx @@ -87,7 +87,7 @@ const CodePanel = (props: CodePanelProps) => { const stylingPropertyNames = [ "roundTailwindValues", "roundTailwindColors", - "customTailwindColors", + "useColorVariables", "showLayerNames", "embedImages", "embedVectors", diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index 9755c221..bc626fdb 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -7,12 +7,12 @@ export interface HTMLSettings { showLayerNames: boolean; embedImages: boolean; embedVectors: boolean; - customTailwindColors: boolean; + useColorVariables: boolean; } export interface TailwindSettings extends HTMLSettings { roundTailwindValues: boolean; roundTailwindColors: boolean; - customTailwindColors: boolean; + useColorVariables: boolean; customTailwindPrefix?: string; } export interface FlutterSettings { From a6653ebb6f777c4836a927249a79f989b725ff07 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Wed, 5 Mar 2025 23:43:05 -0300 Subject: [PATCH 062/168] Fix vector export --- apps/plugin/plugin-src/code.ts | 11 ++++++++++ packages/backend/src/altNodes/altNodeUtils.ts | 22 +++++++++++-------- packages/backend/src/html/htmlMain.ts | 4 +++- packages/backend/src/tailwind/tailwindMain.ts | 4 +++- packages/types/src/types.ts | 1 + 5 files changed, 31 insertions(+), 11 deletions(-) diff --git a/apps/plugin/plugin-src/code.ts b/apps/plugin/plugin-src/code.ts index 07978a0a..27e46678 100644 --- a/apps/plugin/plugin-src/code.ts +++ b/apps/plugin/plugin-src/code.ts @@ -103,7 +103,18 @@ const safeRun = async (settings: PluginSettings) => { const error = e as Error; console.log("error: ", error.stack); figma.ui.postMessage({ type: "error", error: error.message }); + } else { + // Handle non-standard errors or unknown error types + const errorMessage = String(e); + console.log("Unknown error: ", errorMessage); + figma.ui.postMessage({ + type: "error", + error: errorMessage || "Unknown error occurred" + }); } + + // Send a message to reset the UI state + figma.ui.postMessage({ type: "conversion-complete", success: false }); } } else { console.log( diff --git a/packages/backend/src/altNodes/altNodeUtils.ts b/packages/backend/src/altNodes/altNodeUtils.ts index 5ede70ca..50f05cc8 100644 --- a/packages/backend/src/altNodes/altNodeUtils.ts +++ b/packages/backend/src/altNodes/altNodeUtils.ts @@ -1,6 +1,7 @@ import { AltNode } from "types"; import { curry } from "../common/curry"; import { exportAsyncProxy } from "../common/exportAsyncProxy"; +import { addWarning } from "../common/commonConversionWarnings"; export const overrideReadonlyProperty = curry( (prop: K, value: any, obj: T): T => @@ -51,11 +52,6 @@ export const isSVGNode = (node: SceneNode) => { return altNode.canBeFlattened; }; -export const renderNodeAsSVG = async (node: SceneNode) => - await exportAsyncProxy(node, { - format: "SVG_STRING", - }); - export const renderAndAttachSVG = async (node: any) => { // const nodeName = `${node.type}:${node.id}`; // console.log(altNode); @@ -66,10 +62,18 @@ export const renderAndAttachSVG = async (node: any) => { // console.log(`SVG already rendered for ${nodeName}`); return node; } - // console.log(`${nodeName} can be flattened!`); - const svg = (await renderNodeAsSVG(node)) as string; - // console.log(`${svg}`); - node.svg = svg; + + try { + // console.log(`${nodeName} can be flattened!`); + const svg = (await exportAsyncProxy(node, { + format: "SVG_STRING", + })) as string; + node.svg = svg; + } catch (error) { + addWarning(`Failed rendering SVG for ${node.name}`); + console.error(`Error rendering SVG for ${node.type}:${node.id}`); + console.error(error); + } } return node; }; diff --git a/packages/backend/src/html/htmlMain.ts b/packages/backend/src/html/htmlMain.ts index b1d6389f..ffa053f7 100644 --- a/packages/backend/src/html/htmlMain.ts +++ b/packages/backend/src/html/htmlMain.ts @@ -114,7 +114,9 @@ const convertNode = (settings: HTMLSettings) => async (node: SceneNode) => { case "LINE": return htmlLine(node, settings); case "VECTOR": - addWarning("Vector is not supported"); + if (!settings.embedVectors) { + addWarning("Vector is not supported"); + } return await htmlContainer( { ...node, type: "RECTANGLE" } as any, "", diff --git a/packages/backend/src/tailwind/tailwindMain.ts b/packages/backend/src/tailwind/tailwindMain.ts index 561e8be8..f92683d8 100644 --- a/packages/backend/src/tailwind/tailwindMain.ts +++ b/packages/backend/src/tailwind/tailwindMain.ts @@ -74,7 +74,9 @@ const convertNode = case "SECTION": return tailwindSection(node, settings); case "VECTOR": - addWarning("Vector is not supported"); + if (!settings.embedVectors) { + addWarning("Vector is not supported"); + } return tailwindContainer( { ...node, type: "RECTANGLE" } as any, "", diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index bc626fdb..c5622198 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -14,6 +14,7 @@ export interface TailwindSettings extends HTMLSettings { roundTailwindColors: boolean; useColorVariables: boolean; customTailwindPrefix?: string; + embedVectors: boolean; } export interface FlutterSettings { flutterGenerationMode: string; From 1ad15be5ce803a0538abefe781ac9765c396e53f Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Thu, 6 Mar 2025 01:15:06 -0300 Subject: [PATCH 063/168] Experiment with styled components --- apps/plugin/package.json | 1 + apps/plugin/plugin-src/code.ts | 34 +- packages/backend/package.json | 1 + packages/backend/src/code.ts | 133 +++--- .../src/common/retrieveUI/convertToCode.ts | 2 +- .../backend/src/html/htmlDefaultBuilder.ts | 142 +++++- packages/backend/src/html/htmlMain.ts | 426 ++++++++++++++++-- packages/backend/src/html/htmlTextBuilder.ts | 64 ++- .../plugin-ui/src/codegenPreferenceOptions.ts | 14 +- .../plugin-ui/src/components/CodePanel.tsx | 40 +- packages/types/src/types.ts | 1 + pnpm-lock.yaml | 13 + 12 files changed, 731 insertions(+), 140 deletions(-) diff --git a/apps/plugin/package.json b/apps/plugin/package.json index 641d287a..9a3d3f6f 100644 --- a/apps/plugin/package.json +++ b/apps/plugin/package.json @@ -15,6 +15,7 @@ "clsx": "^2.1.1", "lucide-react": "^0.477.0", "motion": "^12.4.9", + "nanoid": "^5.1.2", "plugin-ui": "workspace:*", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/apps/plugin/plugin-src/code.ts b/apps/plugin/plugin-src/code.ts index 27e46678..33611d9a 100644 --- a/apps/plugin/plugin-src/code.ts +++ b/apps/plugin/plugin-src/code.ts @@ -32,6 +32,8 @@ export const defaultPluginSettings: PluginSettings = { customTailwindPrefix: "", embedImages: false, embedVectors: false, + exportCSS: false, + styledComponents: false, }; // A helper type guard to ensure the key belongs to the PluginSettings type @@ -107,12 +109,12 @@ const safeRun = async (settings: PluginSettings) => { // Handle non-standard errors or unknown error types const errorMessage = String(e); console.log("Unknown error: ", errorMessage); - figma.ui.postMessage({ - type: "error", - error: errorMessage || "Unknown error occurred" + figma.ui.postMessage({ + type: "error", + error: errorMessage || "Unknown error occurred", }); } - + // Send a message to reset the UI state figma.ui.postMessage({ type: "conversion-complete", success: false }); } @@ -186,11 +188,13 @@ const codegenMode = async () => { return [ { title: "Code", - code: await htmlMain( - convertedSelection, - { ...userPluginSettings, jsx: false }, - true, - ), + code: ( + await htmlMain( + convertedSelection, + { ...userPluginSettings, jsx: false }, + true, + ) + ).html, language: "HTML", }, { @@ -203,11 +207,13 @@ const codegenMode = async () => { return [ { title: "Code", - code: await htmlMain( - convertedSelection, - { ...userPluginSettings, jsx: true }, - true, - ), + code: ( + await htmlMain( + convertedSelection, + { ...userPluginSettings, jsx: true }, + true, + ) + ).html, language: "HTML", }, { diff --git a/packages/backend/package.json b/packages/backend/package.json index 3bd44a16..9669b663 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -15,6 +15,7 @@ "dependencies": { "@figma/plugin-typings": "^1.108.0", "js-base64": "^3.7.7", + "nanoid": "^5.1.2", "react": "19.0.0", "react-dom": "19.0.0", "types": "workspace:*" diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index 50c213cd..2c682e93 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -8,10 +8,19 @@ import { clearWarnings, warnings, } from "./common/commonConversionWarnings"; -import { generateHTMLPreview } from "./html/htmlMain"; import { postConversionComplete, postEmptyMessage } from "./messaging"; import { PluginSettings } from "types"; import { convertToCode } from "./common/retrieveUI/convertToCode"; +import { generateHTMLPreview } from "./html/htmlMain"; + +export const generateId = (length: number = 4): string => { + const chars = "1234567890abcdefghijklmnopqrstuvwxyz"; + return Array.from({ length }, () => + chars.charAt(Math.floor(Math.random() * chars.length)) + ).join(''); +}; +// Keep track of node names to identify duplicates +const nodeNameRegistry: Map = new Map(); // Helper function to add parent references to all children in the node tree const addParentReferences = (node: any) => { @@ -48,38 +57,52 @@ const processNodeData = (node: any, optimizeLayout: boolean) => { ); }); - // Only fetch the Figma node if we have gradients, optimizeLayout is enabled, or it's an instance - if ( - hasGradient || - optimizeLayout || - node.type === "INSTANCE" || - node.type === "TEXT" - ) { - try { - const figmaNode = figma.getNodeById(node.id); - if (figmaNode) { - // Handle gradients if needed + try { + const figmaNode = figma.getNodeById(node.id); + if (figmaNode) { + // Ensure node has a unique name - store directly on node + if (figmaNode.name) { + const cleanName = figmaNode.name.trim(); + + // Track names for uniqueness + const count = nodeNameRegistry.get(cleanName) || 0; + nodeNameRegistry.set(cleanName, count + 1); + + // For first occurrence, use original name; for duplicates, add suffix + node.uniqueName = + count === 0 + ? cleanName + : `${cleanName}_${generateId()}`; + } + + // Handle additional node properties + if ( + hasGradient || + optimizeLayout || + node.type === "INSTANCE" || + node.type === "TEXT" + ) { + // Handle gradients if (hasGradient) { GRADIENT_PROPERTIES.forEach((propName) => { const property = node[propName]; - if (property && Array.isArray(property) && property.length > 0) { - // We already know there's a gradient in at least one property - if ( - property.some( - (item: any) => - item.type && item.type.startsWith("GRADIENT_"), - ) && - propName in figmaNode - ) { - // Replace with the actual property that contains proper gradient transforms - node[propName] = JSON.parse( - JSON.stringify((figmaNode as any)[propName]), - ); - } + if ( + property && + Array.isArray(property) && + property.length > 0 && + property.some( + (item) => item.type && item.type.startsWith("GRADIENT_"), + ) && + propName in figmaNode + ) { + node[propName] = JSON.parse( + JSON.stringify((figmaNode as any)[propName]), + ); } }); } + // Handle text-specific properties if (figmaNode.type === "TEXT") { node.styledTextSegments = figmaNode.getStyledTextSegments([ "fontName", @@ -111,45 +134,36 @@ const processNodeData = (node: any, optimizeLayout: boolean) => { } // Extract component metadata from instances - if (figmaNode.type === "INSTANCE") { - if (figmaNode.variantProperties) { - node.variantProperties = figmaNode.variantProperties; - } + if ( + node.type === "INSTANCE" && + "variantProperties" in figmaNode && + figmaNode.variantProperties + ) { + node.variantProperties = figmaNode.variantProperties; } + } - if ("width" in figmaNode) { - node.width = (figmaNode as any).width; - node.height = (figmaNode as any).height; - node.x = (figmaNode as any).x; - node.y = (figmaNode as any).y; - } + // Always copy size and position + if ("width" in figmaNode) { + node.width = (figmaNode as any).width; + node.height = (figmaNode as any).height; + node.x = (figmaNode as any).x; + node.y = (figmaNode as any).y; } - } catch (e) { - // Silently fail if there's an error accessing the Figma node } - } else { - const figmaNode = figma.getNodeById(node.id); - node.width = (figmaNode as any).width; - node.height = (figmaNode as any).height; - node.x = (figmaNode as any).x; - node.y = (figmaNode as any).y; + } catch (e) { + // Silently fail if there's an error accessing the Figma node } - if (!node.layoutMode) { - node.layoutMode = "NONE"; - } - if (!node.layoutGrow) { - node.layoutGrow = 0; - } - if (!node.layoutSizingHorizontal) { - node.layoutSizingHorizontal = "FIXED"; - } - if (!node.layoutSizingVertical) { - node.layoutSizingVertical = "FIXED"; - } - + // Set default layout properties if missing + if (!node.layoutMode) node.layoutMode = "NONE"; + if (!node.layoutGrow) node.layoutGrow = 0; + if (!node.layoutSizingHorizontal) node.layoutSizingHorizontal = "FIXED"; + if (!node.layoutSizingVertical) node.layoutSizingVertical = "FIXED"; + // If layout sizing is HUG but there are no children, set it to FIXED - const hasChildren = node.children && Array.isArray(node.children) && node.children.length > 0; + const hasChildren = + node.children && Array.isArray(node.children) && node.children.length > 0; if (node.layoutSizingHorizontal === "HUG" && !hasChildren) { node.layoutSizingHorizontal = "FIXED"; } @@ -176,6 +190,9 @@ export const nodesToJSON = async ( nodes: ReadonlyArray, optimizeLayout: boolean = false, ): Promise => { + // Reset name registry for each conversion + nodeNameRegistry.clear(); + const nodeJson = (await Promise.all( nodes.map( async (node) => diff --git a/packages/backend/src/common/retrieveUI/convertToCode.ts b/packages/backend/src/common/retrieveUI/convertToCode.ts index a8298be3..fc96319e 100644 --- a/packages/backend/src/common/retrieveUI/convertToCode.ts +++ b/packages/backend/src/common/retrieveUI/convertToCode.ts @@ -17,6 +17,6 @@ export const convertToCode = async ( return await swiftuiMain(nodes, settings); case "HTML": default: - return await htmlMain(nodes, settings); + return (await htmlMain(nodes, settings)).html; } }; diff --git a/packages/backend/src/html/htmlDefaultBuilder.ts b/packages/backend/src/html/htmlDefaultBuilder.ts index d5cfda84..892c7782 100644 --- a/packages/backend/src/html/htmlDefaultBuilder.ts +++ b/packages/backend/src/html/htmlDefaultBuilder.ts @@ -28,31 +28,91 @@ import { formatStyleAttribute, } from "../common/commonFormatAttributes"; import { HTMLSettings } from "types"; +import { + cssCollection, + generateUniqueClassName, + getSvelteClassName, + stylesToCSS, +} from "./htmlMain"; export class HtmlDefaultBuilder { styles: Array; data: Array; node: SceneNode; settings: HTMLSettings; + cssClassName: string | null = null; get name() { + if (this.settings.htmlGenerationMode === "styled-components") { + return this.settings.showLayerNames + ? (this.node as any).uniqueName || this.node.name + : ""; + } return this.settings.showLayerNames ? this.node.name : ""; } + get visible() { return this.node.visible; } + get isJSX() { - return this.settings.jsx; + return this.settings.htmlGenerationMode === "jsx"; } + get optimizeLayout() { return this.settings.optimizeLayout; } + get exportCSS() { + return this.settings.htmlGenerationMode === "svelte"; + } + + get useStyledComponents() { + return this.settings.htmlGenerationMode === "styled-components"; + } + + get useInlineStyles() { + return ( + this.settings.htmlGenerationMode === "html" || + this.settings.htmlGenerationMode === "jsx" + ); + } + + // Get the appropriate HTML element based on node type + get htmlElement(): string { + if (this.node.type === "TEXT") return "p"; + return "div"; + } + constructor(node: SceneNode, settings: HTMLSettings) { this.node = node; this.settings = settings; this.styles = []; this.data = []; + + // For both Svelte and styled-components, use similar naming pattern + if ( + this.settings.htmlGenerationMode === "svelte" || + this.settings.htmlGenerationMode === "styled-components" + ) { + // Always generate a unique classname that relates to the node name + const nodeName = (this.node as any).uniqueName || this.node.name; + + // Clean the name and create a valid CSS class name + let baseClassName = nodeName + ? nodeName.replace(/[^a-zA-Z0-9\s_-]/g, "") + .replace(/\s+/g, "-") + .toLowerCase() + : this.node.type.toLowerCase(); + + // Make sure it's valid + if (!/^[a-z]/i.test(baseClassName)) { + baseClassName = `${this.node.type.toLowerCase()}-${baseClassName}`; + } + + // For Svelte, use the same prefix style as styled-components for consistency + this.cssClassName = generateUniqueClassName(baseClassName); + } } commonPositionStyles(): this { @@ -329,14 +389,27 @@ export class HtmlDefaultBuilder { build(additionalStyle: Array = []): string { this.addStyles(...additionalStyle); - let classAttribute = ""; + // Different handling based on generation mode + const mode = this.settings.htmlGenerationMode || "html"; + + // Early return for styled-components with no other attributes + if ( + mode === "styled-components" && + !this.data.length && + this.styles.length > 0 && + this.cssClassName + ) { + this.storeStyles(); + return ""; // Return empty string as we're using the component directly + } + + let classNames: string[] = []; if (this.name) { this.addData("layer", this.name.trim()); const layerNameClass = stringToClassName(this.name.trim()); - classAttribute = formatClassAttribute( - layerNameClass === "" ? [] : [layerNameClass], - this.isJSX, - ); + if (layerNameClass !== "") { + classNames.push(layerNameClass); + } } if ("variantProperties" in this.node && this.node.variantProperties) { @@ -346,9 +419,66 @@ export class HtmlDefaultBuilder { .forEach((d) => this.data.push(d)); } + // For Svelte mode, we use classes + if (mode === "svelte" && this.styles.length > 0 && this.cssClassName) { + classNames.push(this.cssClassName); + this.storeStyles(); + this.styles = []; // Clear inline styles for Svelte + } + // For styled-components, we need the class but keep styles for the component + else if ( + mode === "styled-components" && + this.styles.length > 0 && + this.cssClassName + ) { + classNames.push(this.cssClassName); + this.storeStyles(); + // Keep styles for styled-components + } + const dataAttributes = this.data.join(""); + + // Class attributes + const classAttribute = + mode === "styled-components" + ? formatClassAttribute( + classNames.filter((c) => c !== this.cssClassName), + this.isJSX, + ) + : formatClassAttribute(classNames, this.isJSX); + + // Style attribute const styleAttribute = formatStyleAttribute(this.styles, this.isJSX); return `${dataAttributes}${classAttribute}${styleAttribute}`; } + + // Extract style storage into a method to avoid duplication + private storeStyles(): void { + if (!this.cssClassName || this.styles.length === 0) return; + + // Convert to CSS format if needed + const cssStyles = stylesToCSS(this.styles, this.isJSX); + + // Both modes use the standard div/span elements, no need for semantic HTML inference + // which causes conflicts with duplicate tag selectors + let element = this.node.type === "TEXT" ? "p" : "div"; + + // Only override for really obvious cases + if ((this.node as any).name?.toLowerCase().includes("button")) { + element = "button"; + } else if ((this.node as any).name?.toLowerCase().includes("img") || + (this.node as any).name?.toLowerCase().includes("image")) { + element = "img"; + } + + cssCollection[this.cssClassName] = { + styles: cssStyles, + nodeName: (this.node as any).uniqueName || + this.node.name?.replace(/[^a-zA-Z0-9]/g, "") || + undefined, + nodeType: this.node.type, + element: element, + }; + } } diff --git a/packages/backend/src/html/htmlMain.ts b/packages/backend/src/html/htmlMain.ts index ffa053f7..f6bc014b 100644 --- a/packages/backend/src/html/htmlMain.ts +++ b/packages/backend/src/html/htmlMain.ts @@ -19,6 +19,8 @@ import { nodeHasImageFill, } from "../common/images"; import { addWarning } from "../common/commonConversionWarnings"; +import { customAlphabet } from "nanoid"; +import { generateId } from "../code"; const selfClosingTags = ["img"]; @@ -26,22 +28,328 @@ export let isPreviewGlobal = false; let previousExecutionCache: { style: string; text: string }[]; +// Define better type for the output +export interface HtmlOutput { + html: string; + css?: string; +} + +// Define HTML generation modes for better type safety +export type HtmlGenerationMode = + | "html" + | "jsx" + | "styled-components" + | "svelte"; + +// CSS Collection for external stylesheet or styled-components +interface CSSCollection { + [className: string]: { + styles: string[]; + nodeName?: string; + nodeType?: string; + element?: string; // Base HTML element to use + }; +} + +export let cssCollection: CSSCollection = {}; + +// Generate a unique class name with a prefix +export function generateUniqueClassName(prefix = "figma"): string { + // Sanitize the prefix to ensure valid CSS class + const sanitizedPrefix = + prefix.replace(/[^a-zA-Z0-9_-]/g, "").replace(/^[0-9_-]/, "f") || // Ensure it doesn't start with a number or special char + "figma"; + + return `${sanitizedPrefix}-${generateId()}`; +} + +// Convert styles to CSS format +export function stylesToCSS(styles: string[], isJSX: boolean): string[] { + return styles + .map((style) => { + // Skip empty styles + if (!style.trim()) return ""; + + // Handle JSX format if needed + if (isJSX) { + return style.replace(/^([a-zA-Z0-9]+):/, (match, prop) => { + // Convert camelCase to kebab-case for CSS + return ( + prop + .replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, "$1-$2") + .toLowerCase() + ":" + ); + }); + } + return style; + }) + .filter(Boolean); // Remove empty entries +} + +// Get proper component name from node info +export function getComponentName( + node: any, + className?: string, + nodeType = "div", +): string { + // Start with Styled prefix + let name = "Styled"; + + // Use uniqueName if available, otherwise use name + const nodeName: string = node.uniqueName || node.name; + + // Try to use node name first + if (nodeName && nodeName.length > 0) { + // Clean up the node name and capitalize first letter + const cleanName = nodeName + .replace(/[^a-zA-Z0-9]/g, "") + .replace(/^[a-z]/, (match) => match.toUpperCase()); + + name += cleanName || nodeType.charAt(0).toUpperCase() + nodeType.slice(1); + } + // Fall back to className if provided + else if (className) { + const parts = className.split("-"); + if (parts.length > 0 && parts[0]) { + name += parts[0].charAt(0).toUpperCase() + parts[0].slice(1); + } else { + name += nodeType.charAt(0).toUpperCase() + nodeType.slice(1); + } + } + // Last resort + else { + name += nodeType.charAt(0).toUpperCase() + nodeType.slice(1); + } + + return name; +} + +// Get the collected CSS as a string with improved formatting +export function getCollectedCSS(): string { + if (Object.keys(cssCollection).length === 0) { + return ""; + } + + return Object.entries(cssCollection) + .map(([className, { styles }]) => { + if (!styles.length) return ""; + return `.${className} {\n ${styles.join(";\n ")}${styles.length ? ";" : ""}\n}`; + }) + .filter(Boolean) + .join("\n\n"); +} + +// Generate styled-components with improved naming and formatting +export function generateStyledComponents(): string { + const components: string[] = []; + + Object.entries(cssCollection).forEach( + ([className, { styles, nodeName, nodeType, element }]) => { + // Skip if no styles + if (!styles.length) return; + + // Determine base HTML element - defaults to div + const baseElement = element || (nodeType === "TEXT" ? "p" : "div"); + const componentName = getComponentName( + { name: nodeName }, + className, + baseElement, + ); + + const styledComponent = `const ${componentName} = styled.${baseElement}\` + ${styles.join(";\n ")}${styles.length ? ";" : ""} +\`;`; + + components.push(styledComponent); + }, + ); + + if (components.length === 0) { + return ""; + } + + return `${components.join("\n\n")}`; +} + +// Get a valid React component name from a layer name +export function getReactComponentName(node: any): string { + // Use uniqueName if available, otherwise use name + const name: string = node?.uniqueName || node?.name; + + // Default name if nothing valid is provided + if (!name || name.trim() === "") { + return "App"; + } + + // Convert to PascalCase + let componentName = name + .replace(/[^a-zA-Z0-9_]/g, " ") // Replace non-alphanumeric chars with spaces + .split(/\s+/) // Split by spaces + .map((part) => + part ? part.charAt(0).toUpperCase() + part.slice(1).toLowerCase() : "", + ) + .join(""); + + // Ensure it starts with uppercase letter (React component convention) + componentName = + componentName.charAt(0).toUpperCase() + componentName.slice(1); + + // Ensure it's a valid identifier - if it starts with a number, prefix with 'Component' + if (/^[0-9]/.test(componentName)) { + componentName = "Component" + componentName; + } + + // If we ended up with nothing valid, use the default + return componentName || "App"; +} + +// Get a Svelte-friendly component name +export function getSvelteElementName( + elementType: string, + nodeName?: string, +): string { + // For Svelte, use semantic element names where possible + if (elementType === "TEXT" || elementType === "p") { + return "p"; + } else if (elementType === "img" || elementType === "IMAGE") { + return "img"; + } else if ( + nodeName && + (nodeName.toLowerCase().includes("button") || + nodeName.toLowerCase().includes("btn")) + ) { + return "button"; + } else if (nodeName && nodeName.toLowerCase().includes("link")) { + return "a"; + } else { + return "div"; // Default element + } +} + +// Generate semantic class names for Svelte +export function getSvelteClassName(prefix?: string, nodeType?: string): string { + if (!prefix) { + return nodeType?.toLowerCase() || "element"; + } + + // Clean and format the prefix + return prefix + .replace(/[^a-zA-Z0-9_-]/g, "-") + .replace(/-{2,}/g, "-") // Replace multiple hyphens with a single one + .replace(/^-+|-+$/g, "") // Remove leading/trailing hyphens + .toLowerCase(); +} + +// Generate component code based on the specified mode +function generateComponentCode( + html: string, + sceneNode: Array, + mode: HtmlGenerationMode, +): string { + switch (mode) { + case "styled-components": + return generateReactComponent(html, sceneNode, true); + case "svelte": + return generateSvelteComponent(html, sceneNode); + case "html": + case "jsx": + default: + return html; + } +} + +// Generate React component from HTML, with optional styled-components +function generateReactComponent( + html: string, + sceneNode: Array, + useStyledComponents: boolean = false, +): string { + const styledComponentsCode = useStyledComponents + ? generateStyledComponents() + : ""; + const componentName = getReactComponentName(sceneNode[0]); + + const imports = ['import React from "react";']; + + if (useStyledComponents) { + imports.push('import styled from "styled-components";'); + } + + return `${imports.join("\n")} +${styledComponentsCode ? `\n${styledComponentsCode}` : ""} + +const ${componentName} = () => { + return ( +${indentString(html, 4)} + ); +}; + +export default ${componentName}; +`; +} + +// Generate Svelte component from the collected styles and HTML +function generateSvelteComponent( + html: string, + sceneNode: Array, +): string { + const componentName = getReactComponentName(sceneNode[0]); + + // Build CSS classes similar to styled-components but for Svelte + const cssRules: string[] = []; + + Object.entries(cssCollection).forEach(([className, { styles }]) => { + if (!styles.length) return; + + // Always use class selector to avoid conflicts + cssRules.push( + `.${className} {\n ${styles.join(";\n ")}${styles.length ? ";" : ""}\n}`, + ); + }); + + return `${html} + +`; +} + export const htmlMain = async ( sceneNode: Array, settings: PluginSettings, isPreview: boolean = false, -): Promise => { +): Promise => { isPreviewGlobal = isPreview; previousExecutionCache = []; + cssCollection = {}; - let result = await htmlWidgetGenerator(sceneNode, settings); + let htmlContent = await htmlWidgetGenerator(sceneNode, settings); // remove the initial \n that is made in Container. - if (result.length > 0 && result.startsWith("\n")) { - result = result.slice(1, result.length); + if (htmlContent.length > 0 && htmlContent.startsWith("\n")) { + htmlContent = htmlContent.slice(1, htmlContent.length); } - return result; + // Always return an object with html property + const output: HtmlOutput = { html: htmlContent }; + + // Handle different HTML generation modes + const mode = settings.htmlGenerationMode || "html"; + + if (mode !== "html") { + // Generate component code for non-html modes + output.html = generateComponentCode(htmlContent, sceneNode, mode); + + // For svelte mode, we don't need separate CSS as it's included in the component + if (mode === "svelte" && Object.keys(cssCollection).length > 0) { + // CSS is already included in the Svelte component + } + } else if (Object.keys(cssCollection).length > 0) { + // For plain HTML with CSS, include CSS separately + output.css = getCollectedCSS(); + } + + return output; }; export const generateHTMLPreview = async ( @@ -49,26 +357,22 @@ export const generateHTMLPreview = async ( settings: PluginSettings, code?: string, ): Promise => { - // const htmlCodeAlreadyGenerated = - // settings.framework === "HTML" && settings.jsx === false && code; - const htmlCode = - // htmlCodeAlreadyGenerated - // ? code : - await htmlMain( - nodes, - { - ...settings, - jsx: false, - }, - true, - ); + const result = await htmlMain( + nodes, + { + ...settings, + htmlGenerationMode: "html", + jsx: false, + }, + true, + ); return { size: { width: nodes[0].width, height: nodes[0].height, }, - content: htmlCode, + content: result.html, }; }; @@ -77,7 +381,6 @@ const htmlWidgetGenerator = async ( settings: HTMLSettings, ): Promise => { console.log("htmlWidgetGenerator", sceneNode); - // filter non visible nodes. This is necessary at this step because conversion already happened. const promiseOfConvertedCode = getVisibleNodes(sceneNode).map( convertNode(settings), @@ -87,8 +390,6 @@ const htmlWidgetGenerator = async ( }; const convertNode = (settings: HTMLSettings) => async (node: SceneNode) => { - console.log("converting", node); - if (settings.embedVectors && (node as any).canBeFlattened) { const altNode = await renderAndAttachSVG(node); if (altNode.svg) { @@ -137,7 +438,6 @@ const htmlWrapSVG = ( const builder = new HtmlDefaultBuilder(node, settings) .addData("svg-wrapper") .position(); - return `\n\n${node.svg ?? ""}
`; }; @@ -158,16 +458,13 @@ const htmlGroup = async ( if (builder.styles) { const attr = builder.build(); - const generator = await htmlWidgetGenerator(node.children, settings); - return `\n${indentString(generator)}\n
`; } - return await htmlWidgetGenerator(node.children, settings); }; -// this was split from htmlText to help the UI part, where the style is needed (without

). +// For htmlText and htmlContainer, use the htmlGenerationMode to determine styling approach const htmlText = (node: TextNode, settings: HTMLSettings): string => { let layoutBuilder = new HtmlTextBuilder(node, settings) .commonPositionStyles() @@ -177,9 +474,45 @@ const htmlText = (node: TextNode, settings: HTMLSettings): string => { const styledHtml = layoutBuilder.getTextSegments(node); previousExecutionCache.push(...styledHtml); + const mode = settings.htmlGenerationMode || "html"; + + // For styled-components mode + if (mode === "styled-components") { + const componentName = layoutBuilder.cssClassName + ? getComponentName(node, layoutBuilder.cssClassName, "p") + : getComponentName(node, undefined, "p"); + + if (styledHtml.length === 1) { + return `\n<${componentName}>${styledHtml[0].text}`; + } else { + const content = styledHtml + .map((style) => { + const tag = + style.openTypeFeatures.SUBS === true + ? "sub" + : style.openTypeFeatures.SUPS === true + ? "sup" + : "span"; + + if (style.componentName) { + return `<${style.componentName}>${style.text}`; + } + return `<${tag}>${style.text}`; + }) + .join(""); + + return `\n<${componentName}>${content}`; + } + } + + // Standard HTML/CSS approach for HTML, React or Svelte let content = ""; if (styledHtml.length === 1) { - layoutBuilder.addStyles(styledHtml[0].style); + // For HTML and React modes, we use inline styles + if (mode === "html" || mode === "jsx") { + layoutBuilder.addStyles(styledHtml[0].style); + } + content = styledHtml[0].text; const additionalTag = @@ -191,10 +524,14 @@ const htmlText = (node: TextNode, settings: HTMLSettings): string => { if (additionalTag) { content = `<${additionalTag}>${content}`; + } else if (mode === "svelte" && styledHtml[0].className) { + // Use span just like styled-components for consistency + content = `${content}`; } } else { content = styledHtml .map((style) => { + // Always use span for multi-segment text in Svelte mode const tag = style.openTypeFeatures.SUBS === true ? "sub" @@ -202,11 +539,17 @@ const htmlText = (node: TextNode, settings: HTMLSettings): string => { ? "sup" : "span"; + // Use class name for Svelte with same approach as styled-components + if (mode === "svelte" && style.className) { + return `${style.text}`; + } + return `<${tag} style="${style.style}">${style.text}`; }) .join(""); } + // Always use div as container to be consistent with styled-components return `\n${content}
`; }; @@ -252,8 +595,6 @@ const htmlContainer = async ( settings: HTMLSettings, ): Promise => { // ignore the view when size is zero or less - // while technically it shouldn't get less than 0, due to rounding errors, - // it can get to values like: -0.000004196293048153166 if (node.width <= 0 || node.height <= 0) { return children; } @@ -267,23 +608,17 @@ const htmlContainer = async ( let src = ""; if (nodeHasImageFill(node)) { + // ...existing image handling code... const altNode = node as AltNode; const hasChildren = "children" in node && node.children.length > 0; let imgUrl = ""; - // TODO: This overrides the embedImages setting to only happen when HTML is selected but - // really this should be more of a global setting that isn't tied to a specific framework. - // It's being disabled in this way so the HTML preview will only embed images when it's HTML outuput. - // The reason this is so important is that it's a costly operation an it will slow down - // the generation of code for other languages and display different results in the preview - // than what the output will look like. if ( settings.embedImages && (settings as PluginSettings).framework === "HTML" ) { imgUrl = (await exportNodeAsBase64PNG(altNode, hasChildren)) ?? ""; } else { - // addWarning("Some images were exported as placeholder URLs"); imgUrl = getPlaceholderImage(node.width, node.height); } @@ -292,14 +627,26 @@ const htmlContainer = async ( formatWithJSX("background-image", settings.jsx, `url(${imgUrl})`), ); } else { - // if node has NO children tag = "img"; src = ` src="${imgUrl}"`; } } const build = builder.build(additionalStyles); + const mode = settings.htmlGenerationMode || "html"; + + // For styled-components mode + if (mode === "styled-components" && builder.cssClassName) { + const componentName = getComponentName(node, builder.cssClassName); + if (children) { + return `\n<${componentName}>${indentString(children)}\n`; + } else { + return `\n<${componentName} ${src}/>`; + } + } + + // Standard HTML approach for HTML, React, or Svelte if (children) { return `\n<${tag}${build}${src}>${indentString(children)}\n`; } else if (selfClosingTags.includes(tag) || settings.jsx) { @@ -308,6 +655,7 @@ const htmlContainer = async ( return `\n<${tag}${build}${src}>`; } } + return children; }; @@ -340,7 +688,7 @@ export const htmlCodeGenTextStyles = (settings: HTMLSettings) => { const result = previousExecutionCache .map( (style) => - `// ${style.text}\n${style.style.split(settings.jsx ? "," : ";").join(";\n")}`, + `// ${style.text}\n${style.style.split(settings.htmlGenerationMode === "jsx" ? "," : ";").join(";\n")}`, ) .join("\n---\n"); diff --git a/packages/backend/src/html/htmlTextBuilder.ts b/packages/backend/src/html/htmlTextBuilder.ts index 5e0d60f8..cf101766 100644 --- a/packages/backend/src/html/htmlTextBuilder.ts +++ b/packages/backend/src/html/htmlTextBuilder.ts @@ -6,16 +6,30 @@ import { commonLineHeight, } from "../common/commonTextHeightSpacing"; import { HTMLSettings, StyledTextSegmentSubset } from "types"; +import { + cssCollection, + generateUniqueClassName, + stylesToCSS, + getComponentName, + getSvelteClassName, +} from "./htmlMain"; export class HtmlTextBuilder extends HtmlDefaultBuilder { constructor(node: TextNode, settings: HTMLSettings) { super(node, settings); } + // Override htmlElement to ensure text nodes use paragraph elements + get htmlElement(): string { + return "p"; + } + getTextSegments(node: TextNode): { style: string; text: string; openTypeFeatures: { [key: string]: boolean }; + className?: string; + componentName?: string; }[] { const segments = (node as any) .styledTextSegments as StyledTextSegmentSubset[]; @@ -23,7 +37,7 @@ export class HtmlTextBuilder extends HtmlDefaultBuilder { return []; } - return segments.map((segment) => { + return segments.map((segment, index) => { // Prepare additional CSS properties from layer blur and drop shadow effects. const additionalStyles: { [key: string]: string } = {}; @@ -58,11 +72,57 @@ export class HtmlTextBuilder extends HtmlDefaultBuilder { ); const charsWithLineBreak = segment.characters.split("\n").join("
"); - return { + const result: any = { style: styleAttributes, text: charsWithLineBreak, openTypeFeatures: segment.openTypeFeatures, }; + + // Add class name and component name for Svelte or styled-components modes + const mode = this.settings.htmlGenerationMode; + if ((mode === "svelte" || mode === "styled-components") && styleAttributes) { + // Create a consistent naming scheme for both modes + const uniqueName = (node as any).uniqueName || node.name || "text"; + + // Create segment name with index for uniqueness + const segmentPrefix = index > 0 + ? `${uniqueName.replace(/[^a-zA-Z0-9]/g, "")}-${index}` + : uniqueName.replace(/[^a-zA-Z0-9]/g, ""); + + // Always generate a unique className for consistent styling + const className = generateUniqueClassName(segmentPrefix); + result.className = className; + + // Convert styles to CSS format + const cssStyles = stylesToCSS( + styleAttributes + .split(this.isJSX ? "," : ";") + .map((style) => style.trim()) + .filter((style) => style), + this.isJSX + ); + + // In both modes, use span for text segments to avoid selector conflicts + const elementTag = "span"; + + // Store in cssCollection with consistent metadata + cssCollection[className] = { + styles: cssStyles, + nodeName: segmentPrefix, + nodeType: "TEXT", + element: elementTag, + }; + + if (mode === "styled-components") { + result.componentName = getComponentName( + { name: segmentPrefix }, + className, + elementTag + ); + } + } + + return result; }); } diff --git a/packages/plugin-ui/src/codegenPreferenceOptions.ts b/packages/plugin-ui/src/codegenPreferenceOptions.ts index 7516b261..0347dfac 100644 --- a/packages/plugin-ui/src/codegenPreferenceOptions.ts +++ b/packages/plugin-ui/src/codegenPreferenceOptions.ts @@ -7,7 +7,7 @@ export const preferenceOptions: LocalCodegenPreferenceOptions[] = [ label: "React (JSX)", description: "", isDefault: false, - includedLanguages: ["HTML", "Tailwind"], + includedLanguages: ["Tailwind"], }, { itemType: "individual_select", @@ -71,6 +71,18 @@ export const preferenceOptions: LocalCodegenPreferenceOptions[] = [ ]; export const selectPreferenceOptions: SelectPreferenceOptions[] = [ + { + itemType: "select", + propertyName: "htmlGenerationMode", + label: "Mode", + options: [ + { label: "HTML", value: "html" }, + { label: "React (JSX)", value: "jsx" }, + { label: "Svelte", value: "svelte" }, + { label: "styled-components", value: "styled-components" }, + ], + includedLanguages: ["HTML"], + }, { itemType: "select", propertyName: "flutterGenerationMode", diff --git a/packages/plugin-ui/src/components/CodePanel.tsx b/packages/plugin-ui/src/components/CodePanel.tsx index 96801f5e..2f60c731 100644 --- a/packages/plugin-ui/src/components/CodePanel.tsx +++ b/packages/plugin-ui/src/components/CodePanel.tsx @@ -85,6 +85,8 @@ const CodePanel = (props: CodePanelProps) => { // Define preference grouping based on property names const essentialPropertyNames = ["jsx", "optimizeLayout"]; const stylingPropertyNames = [ + "styledComponents", + "exportCSS", "roundTailwindValues", "roundTailwindColors", "useColorVariables", @@ -139,27 +141,9 @@ const CodePanel = (props: CodePanelProps) => { onPreferenceChanged={onPreferenceChanged} /> - {/* Styling preferences with custom prefix for Tailwind */} - {(stylingPreferences.length > 0 || - selectedFramework === "Tailwind") && ( - - {selectedFramework === "Tailwind" && ( - - )} - - )} - {/* Framework-specific options */} {selectableSettingsFiltered.length > 0 && ( -
+

{selectedFramework} Options

@@ -193,6 +177,24 @@ const CodePanel = (props: CodePanelProps) => {
)} + + {/* Styling preferences with custom prefix for Tailwind */} + {(stylingPreferences.length > 0 || + selectedFramework === "Tailwind") && ( + + {selectedFramework === "Tailwind" && ( + + )} + + )}
)} diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index c5622198..0bfc11da 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -8,6 +8,7 @@ export interface HTMLSettings { embedImages: boolean; embedVectors: boolean; useColorVariables: boolean; + htmlGenerationMode: "html" | "jsx" | "styled-components" | "svelte"; } export interface TailwindSettings extends HTMLSettings { roundTailwindValues: boolean; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5ac4f6fe..756ec514 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -90,6 +90,9 @@ importers: motion: specifier: ^12.4.9 version: 12.4.9(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + nanoid: + specifier: ^5.1.2 + version: 5.1.2 plugin-ui: specifier: workspace:* version: link:../../packages/plugin-ui @@ -175,6 +178,9 @@ importers: js-base64: specifier: ^3.7.7 version: 3.7.7 + nanoid: + specifier: ^5.1.2 + version: 5.1.2 react: specifier: 19.0.0 version: 19.0.0 @@ -2364,6 +2370,11 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + nanoid@5.1.2: + resolution: {integrity: sha512-b+CiXQCNMUGe0Ri64S9SXFcP9hogjAJ2Rd6GdVxhPLRm7mhGaM7VgOvCAJ1ZshfHbqVDI3uqTI5C8/GaKuLI7g==} + engines: {node: ^18 || >=20} + hasBin: true + natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -5184,6 +5195,8 @@ snapshots: nanoid@3.3.8: {} + nanoid@5.1.2: {} + natural-compare@1.4.0: {} next@14.2.24(react-dom@19.0.0(react@19.0.0))(react@19.0.0): From c8ce626e19024811370c6d866059d6302f0ca113 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Thu, 6 Mar 2025 01:56:08 -0300 Subject: [PATCH 064/168] Experiment 2 --- packages/backend/src/code.ts | 218 ++++++++++-------- .../backend/src/html/htmlDefaultBuilder.ts | 48 ++-- packages/backend/src/html/htmlMain.ts | 21 +- packages/backend/src/html/htmlTextBuilder.ts | 29 ++- .../src/tailwind/tailwindDefaultBuilder.ts | 2 +- 5 files changed, 183 insertions(+), 135 deletions(-) diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index 2c682e93..9ec40fc9 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -1,3 +1,4 @@ +import { btoa } from "js-base64"; import { convertNodesToAltNodes } from "./altNodes/altConversion"; import { retrieveGenericSolidUIColors, @@ -13,14 +14,8 @@ import { PluginSettings } from "types"; import { convertToCode } from "./common/retrieveUI/convertToCode"; import { generateHTMLPreview } from "./html/htmlMain"; -export const generateId = (length: number = 4): string => { - const chars = "1234567890abcdefghijklmnopqrstuvwxyz"; - return Array.from({ length }, () => - chars.charAt(Math.floor(Math.random() * chars.length)) - ).join(''); -}; -// Keep track of node names to identify duplicates -const nodeNameRegistry: Map = new Map(); +// Keep track of node names for sequential numbering +const nodeNameCounters: Map = new Map(); // Helper function to add parent references to all children in the node tree const addParentReferences = (node: any) => { @@ -57,102 +52,135 @@ const processNodeData = (node: any, optimizeLayout: boolean) => { ); }); - try { - const figmaNode = figma.getNodeById(node.id); - if (figmaNode) { - // Ensure node has a unique name - store directly on node - if (figmaNode.name) { - const cleanName = figmaNode.name.trim(); - - // Track names for uniqueness - const count = nodeNameRegistry.get(cleanName) || 0; - nodeNameRegistry.set(cleanName, count + 1); - - // For first occurrence, use original name; for duplicates, add suffix - node.uniqueName = - count === 0 - ? cleanName - : `${cleanName}_${generateId()}`; - } + // Ensure node has a unique name with simple numbering + const cleanName = node.name.trim(); - // Handle additional node properties - if ( - hasGradient || - optimizeLayout || - node.type === "INSTANCE" || - node.type === "TEXT" - ) { - // Handle gradients - if (hasGradient) { - GRADIENT_PROPERTIES.forEach((propName) => { - const property = node[propName]; - if ( - property && - Array.isArray(property) && - property.length > 0 && - property.some( - (item) => item.type && item.type.startsWith("GRADIENT_"), - ) && - propName in figmaNode - ) { - node[propName] = JSON.parse( - JSON.stringify((figmaNode as any)[propName]), - ); - } - }); - } + // Track names with simple counter + const count = nodeNameCounters.get(cleanName) || 0; + nodeNameCounters.set(cleanName, count + 1); - // Handle text-specific properties - if (figmaNode.type === "TEXT") { - node.styledTextSegments = figmaNode.getStyledTextSegments([ - "fontName", - "fills", - "fontSize", - "fontWeight", - "hyperlink", - "indentation", - "letterSpacing", - "lineHeight", - "listOptions", - "textCase", - "textDecoration", - "textStyleId", - "fillStyleId", - "openTypeFeatures", - ]); - Object.assign(node, node.style); - if (!node.textAutoResize) { - node.textAutoResize = "NONE"; - } - } + // For first occurrence, use original name; for duplicates, add sequential suffix + node.uniqueName = + count === 0 + ? cleanName + : `${cleanName}_${count.toString().padStart(2, "0")}`; - // Extract inferredAutoLayout if optimizeLayout is enabled - if (optimizeLayout && "inferredAutoLayout" in figmaNode) { - node.inferredAutoLayout = JSON.parse( - JSON.stringify((figmaNode as any).inferredAutoLayout), - ); - } + console.log("going inside", node); + // Handle additional node properties + if ( + hasGradient || + optimizeLayout || + node.type === "INSTANCE" || + node.type === "TEXT" + ) { + const figmaNode = figma.getNodeById(node.id); - // Extract component metadata from instances + if (!figmaNode) { + return; + } + + // Handle gradients + if (hasGradient) { + GRADIENT_PROPERTIES.forEach((propName) => { + const property = node[propName]; if ( - node.type === "INSTANCE" && - "variantProperties" in figmaNode && - figmaNode.variantProperties + property && + Array.isArray(property) && + property.length > 0 && + property.some( + (item) => item.type && item.type.startsWith("GRADIENT_"), + ) && + propName in figmaNode ) { - node.variantProperties = figmaNode.variantProperties; + node[propName] = JSON.parse( + JSON.stringify((figmaNode as any)[propName]), + ); } + }); + } + console.log("eee"); + + // Handle text-specific properties + if (figmaNode.type === "TEXT") { + // Get the text segments + const styledTextSegments = figmaNode.getStyledTextSegments([ + "fontName", + "fills", + "fontSize", + "fontWeight", + "hyperlink", + "indentation", + "letterSpacing", + "lineHeight", + "listOptions", + "textCase", + "textDecoration", + "textStyleId", + "fillStyleId", + "openTypeFeatures", + ]); + + // Assign unique IDs to each segment + if (styledTextSegments.length > 0) { + const baseSegmentName = (node.uniqueName || node.name) + .replace(/[^a-zA-Z0-9_-]/g, "") + .toLowerCase(); + // Add a uniqueId to each segment + node.styledTextSegments = styledTextSegments.map( + (segment: any, index) => { + const mutableSegment = Object.assign({}, segment); + // For single segments, don't add index suffix + if (styledTextSegments.length === 1) { + mutableSegment.uniqueId = `${baseSegmentName}_span`; + } else { + // For multiple segments, add index suffix + mutableSegment.uniqueId = `${baseSegmentName}_span_${(index + 1).toString().padStart(2, "0")}`; + } + console.log("after"); + return mutableSegment; + }, + ); } - // Always copy size and position - if ("width" in figmaNode) { - node.width = (figmaNode as any).width; - node.height = (figmaNode as any).height; - node.x = (figmaNode as any).x; - node.y = (figmaNode as any).y; + Object.assign(node, node.style); + if (!node.textAutoResize) { + node.textAutoResize = "NONE"; } } - } catch (e) { - // Silently fail if there's an error accessing the Figma node + + // Extract inferredAutoLayout if optimizeLayout is enabled + if (optimizeLayout && "inferredAutoLayout" in figmaNode) { + node.inferredAutoLayout = JSON.parse( + JSON.stringify((figmaNode as any).inferredAutoLayout), + ); + } + + // Extract component metadata from instances + if ( + node.type === "INSTANCE" && + "variantProperties" in figmaNode && + figmaNode.variantProperties + ) { + node.variantProperties = figmaNode.variantProperties; + } + + console.log("figmaNode", figmaNode); + // Always copy size and position + if ("width" in figmaNode) { + node.width = (figmaNode as any).width; + node.height = (figmaNode as any).height; + node.x = (figmaNode as any).x; + node.y = (figmaNode as any).y; + } + } else { + // Hopefully one day this won't be needed anymore. + const figmaNode = figma.getNodeById(node.id); + if (figmaNode && "width" in figmaNode) { + node.width = (figmaNode as any).width; + node.height = (figmaNode as any).height; + node.x = (figmaNode as any).x; + node.y = (figmaNode as any).y; + } } // Set default layout properties if missing @@ -190,8 +218,8 @@ export const nodesToJSON = async ( nodes: ReadonlyArray, optimizeLayout: boolean = false, ): Promise => { - // Reset name registry for each conversion - nodeNameRegistry.clear(); + // Reset name counters for each conversion + nodeNameCounters.clear(); const nodeJson = (await Promise.all( nodes.map( diff --git a/packages/backend/src/html/htmlDefaultBuilder.ts b/packages/backend/src/html/htmlDefaultBuilder.ts index 892c7782..da6cc673 100644 --- a/packages/backend/src/html/htmlDefaultBuilder.ts +++ b/packages/backend/src/html/htmlDefaultBuilder.ts @@ -90,27 +90,29 @@ export class HtmlDefaultBuilder { this.styles = []; this.data = []; - // For both Svelte and styled-components, use similar naming pattern + // For both Svelte and styled-components, use sequential class names if ( this.settings.htmlGenerationMode === "svelte" || this.settings.htmlGenerationMode === "styled-components" ) { - // Always generate a unique classname that relates to the node name - const nodeName = (this.node as any).uniqueName || this.node.name; - + // Use uniqueName (which already has _01, _02 suffixes) if available + let baseClassName = + (this.node as any).uniqueName || + this.node.name || + this.node.type.toLowerCase(); + // Clean the name and create a valid CSS class name - let baseClassName = nodeName - ? nodeName.replace(/[^a-zA-Z0-9\s_-]/g, "") - .replace(/\s+/g, "-") - .toLowerCase() - : this.node.type.toLowerCase(); - + baseClassName = baseClassName + .replace(/[^a-zA-Z0-9\s_-]/g, "") + .replace(/\s+/g, "-") + .toLowerCase(); + // Make sure it's valid if (!/^[a-z]/i.test(baseClassName)) { baseClassName = `${this.node.type.toLowerCase()}-${baseClassName}`; } - // For Svelte, use the same prefix style as styled-components for consistency + // Generate unique class name with simple counter suffix this.cssClassName = generateUniqueClassName(baseClassName); } } @@ -406,9 +408,12 @@ export class HtmlDefaultBuilder { let classNames: string[] = []; if (this.name) { this.addData("layer", this.name.trim()); - const layerNameClass = stringToClassName(this.name.trim()); - if (layerNameClass !== "") { - classNames.push(layerNameClass); + + if (mode !== "svelte" && mode !== "styled-components") { + const layerNameClass = stringToClassName(this.name.trim()); + if (layerNameClass !== "") { + classNames.push(layerNameClass); + } } } @@ -467,16 +472,19 @@ export class HtmlDefaultBuilder { // Only override for really obvious cases if ((this.node as any).name?.toLowerCase().includes("button")) { element = "button"; - } else if ((this.node as any).name?.toLowerCase().includes("img") || - (this.node as any).name?.toLowerCase().includes("image")) { - element = "img"; + } else if ( + (this.node as any).name?.toLowerCase().includes("img") || + (this.node as any).name?.toLowerCase().includes("image") + ) { + element = "img"; } cssCollection[this.cssClassName] = { styles: cssStyles, - nodeName: (this.node as any).uniqueName || - this.node.name?.replace(/[^a-zA-Z0-9]/g, "") || - undefined, + nodeName: + (this.node as any).uniqueName || + this.node.name?.replace(/[^a-zA-Z0-9]/g, "") || + undefined, nodeType: this.node.type, element: element, }; diff --git a/packages/backend/src/html/htmlMain.ts b/packages/backend/src/html/htmlMain.ts index f6bc014b..abfbb67d 100644 --- a/packages/backend/src/html/htmlMain.ts +++ b/packages/backend/src/html/htmlMain.ts @@ -19,8 +19,6 @@ import { nodeHasImageFill, } from "../common/images"; import { addWarning } from "../common/commonConversionWarnings"; -import { customAlphabet } from "nanoid"; -import { generateId } from "../code"; const selfClosingTags = ["img"]; @@ -53,14 +51,28 @@ interface CSSCollection { export let cssCollection: CSSCollection = {}; -// Generate a unique class name with a prefix +// Instance counters for class name generation - we keep this but primarily as a fallback +const classNameCounters: Map = new Map(); + +// Generate a class name - prefer direct uniqueId, but fall back to counter-based if needed export function generateUniqueClassName(prefix = "figma"): string { // Sanitize the prefix to ensure valid CSS class const sanitizedPrefix = prefix.replace(/[^a-zA-Z0-9_-]/g, "").replace(/^[0-9_-]/, "f") || // Ensure it doesn't start with a number or special char "figma"; - return `${sanitizedPrefix}-${generateId()}`; + // Most of the time, we'll just use the prefix directly as it's pre-generated to be unique + // But keep the counter logic as a fallback + const count = classNameCounters.get(sanitizedPrefix) || 0; + classNameCounters.set(sanitizedPrefix, count + 1); + + // Only add suffix if this isn't the first instance + return count === 0 ? sanitizedPrefix : `${sanitizedPrefix}_${count.toString().padStart(2, "0")}`; +} + +// Reset all class name counters - call this at the start of processing +export function resetClassNameCounters(): void { + classNameCounters.clear(); } // Convert styles to CSS format @@ -322,6 +334,7 @@ export const htmlMain = async ( isPreviewGlobal = isPreview; previousExecutionCache = []; cssCollection = {}; + resetClassNameCounters(); // Reset counters for each new generation let htmlContent = await htmlWidgetGenerator(sceneNode, settings); diff --git a/packages/backend/src/html/htmlTextBuilder.ts b/packages/backend/src/html/htmlTextBuilder.ts index cf101766..d870d562 100644 --- a/packages/backend/src/html/htmlTextBuilder.ts +++ b/packages/backend/src/html/htmlTextBuilder.ts @@ -80,17 +80,16 @@ export class HtmlTextBuilder extends HtmlDefaultBuilder { // Add class name and component name for Svelte or styled-components modes const mode = this.settings.htmlGenerationMode; - if ((mode === "svelte" || mode === "styled-components") && styleAttributes) { - // Create a consistent naming scheme for both modes - const uniqueName = (node as any).uniqueName || node.name || "text"; + if ( + (mode === "svelte" || mode === "styled-components") && + styleAttributes + ) { + // Use the pre-assigned uniqueId from the segment if available, + // or generate one if not (as a fallback) + const segmentName = (segment as any).uniqueId || + `${((node as any).uniqueName || node.name || "text").replace(/[^a-zA-Z0-9_-]/g, "").toLowerCase()}_text_${(index + 1).toString().padStart(2, "0")}`; - // Create segment name with index for uniqueness - const segmentPrefix = index > 0 - ? `${uniqueName.replace(/[^a-zA-Z0-9]/g, "")}-${index}` - : uniqueName.replace(/[^a-zA-Z0-9]/g, ""); - - // Always generate a unique className for consistent styling - const className = generateUniqueClassName(segmentPrefix); + const className = generateUniqueClassName(segmentName); result.className = className; // Convert styles to CSS format @@ -99,7 +98,7 @@ export class HtmlTextBuilder extends HtmlDefaultBuilder { .split(this.isJSX ? "," : ";") .map((style) => style.trim()) .filter((style) => style), - this.isJSX + this.isJSX, ); // In both modes, use span for text segments to avoid selector conflicts @@ -108,16 +107,16 @@ export class HtmlTextBuilder extends HtmlDefaultBuilder { // Store in cssCollection with consistent metadata cssCollection[className] = { styles: cssStyles, - nodeName: segmentPrefix, + nodeName: segmentName, nodeType: "TEXT", - element: elementTag, + element: elementTag, }; if (mode === "styled-components") { result.componentName = getComponentName( - { name: segmentPrefix }, + { name: segmentName }, className, - elementTag + elementTag, ); } } diff --git a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts index 2d5df811..79e3d79c 100644 --- a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts +++ b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts @@ -125,7 +125,7 @@ export class TailwindDefaultBuilder { if (commonIsAbsolutePosition(node, optimizeLayout)) { const { x, y } = getCommonPositionValue(node); - console.log("x", x, y); + console.log("x", x, y, "node", node); const parsedX = numberToFixedString(x); const parsedY = numberToFixedString(y); From 9bf600729cf1953d0b99b2224135f36f42eedd12 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Thu, 6 Mar 2025 02:25:42 -0300 Subject: [PATCH 065/168] Improve even more --- apps/plugin/plugin-src/code.ts | 56 +++++++++++++++++-- manifest.json | 2 + packages/backend/src/code.ts | 12 +--- packages/backend/src/html/htmlMain.ts | 33 +++++------ .../src/tailwind/tailwindDefaultBuilder.ts | 2 +- packages/backend/src/tailwind/tailwindMain.ts | 5 +- .../plugin-ui/src/codegenPreferenceOptions.ts | 32 ++++++----- .../plugin-ui/src/components/CodePanel.tsx | 53 ++++++++---------- .../src/components/FrameworkTabs.tsx | 43 ++++++++++++++ .../src/components/SelectableToggle.tsx | 30 +++++----- packages/types/src/types.ts | 2 +- 11 files changed, 171 insertions(+), 99 deletions(-) create mode 100644 packages/plugin-ui/src/components/FrameworkTabs.tsx diff --git a/apps/plugin/plugin-src/code.ts b/apps/plugin/plugin-src/code.ts index 33611d9a..d466258f 100644 --- a/apps/plugin/plugin-src/code.ts +++ b/apps/plugin/plugin-src/code.ts @@ -19,7 +19,6 @@ let userPluginSettings: PluginSettings; export const defaultPluginSettings: PluginSettings = { framework: "HTML", - jsx: false, optimizeLayout: true, showLayerNames: false, inlineStyle: true, @@ -32,8 +31,8 @@ export const defaultPluginSettings: PluginSettings = { customTailwindPrefix: "", embedImages: false, embedVectors: false, - exportCSS: false, - styledComponents: false, + htmlGenerationMode: "html", + tailwindGenerationMode: "jsx", }; // A helper type guard to ensure the key belongs to the PluginSettings type @@ -191,7 +190,7 @@ const codegenMode = async () => { code: ( await htmlMain( convertedSelection, - { ...userPluginSettings, jsx: false }, + { ...userPluginSettings, htmlGenerationMode: "html" }, true, ) ).html, @@ -210,7 +209,7 @@ const codegenMode = async () => { code: ( await htmlMain( convertedSelection, - { ...userPluginSettings, jsx: true }, + { ...userPluginSettings, htmlGenerationMode: "jsx" }, true, ) ).html, @@ -222,6 +221,50 @@ const codegenMode = async () => { language: "HTML", }, ]; + + case "html_svelte": + return [ + { + title: "Code", + code: ( + await htmlMain( + convertedSelection, + { ...userPluginSettings, htmlGenerationMode: "svelte" }, + true, + ) + ).html, + language: "HTML", + }, + { + title: "Text Styles", + code: htmlCodeGenTextStyles(userPluginSettings), + language: "HTML", + }, + ]; + + case "html_styled_components": + return [ + { + title: "Code", + code: ( + await htmlMain( + convertedSelection, + { + ...userPluginSettings, + htmlGenerationMode: "styled-components", + }, + true, + ) + ).html, + language: "HTML", + }, + { + title: "Text Styles", + code: htmlCodeGenTextStyles(userPluginSettings), + language: "HTML", + }, + ]; + case "tailwind": case "tailwind_jsx": return [ @@ -229,7 +272,8 @@ const codegenMode = async () => { title: "Code", code: await tailwindMain(convertedSelection, { ...userPluginSettings, - jsx: language === "tailwind_jsx", + tailwindGenerationMode: + language === "tailwind_jsx" ? "jsx" : "html", }), language: "HTML", }, diff --git a/manifest.json b/manifest.json index b9c29cc0..888938bf 100644 --- a/manifest.json +++ b/manifest.json @@ -13,6 +13,8 @@ "codegenLanguages": [ { "label": "HTML", "value": "html" }, { "label": "React (JSX)", "value": "html_jsx" }, + { "label": "Svelte", "value": "html_svelte" }, + { "label": "Styled Components", "value": "html_styled_components" }, { "label": "Tailwind", "value": "tailwind" }, { "label": "Tailwind (JSX)", "value": "tailwind_jsx" }, { "label": "Flutter", "value": "flutter" }, diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index 9ec40fc9..2486e702 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -1,4 +1,3 @@ -import { btoa } from "js-base64"; import { convertNodesToAltNodes } from "./altNodes/altConversion"; import { retrieveGenericSolidUIColors, @@ -65,7 +64,6 @@ const processNodeData = (node: any, optimizeLayout: boolean) => { ? cleanName : `${cleanName}_${count.toString().padStart(2, "0")}`; - console.log("going inside", node); // Handle additional node properties if ( hasGradient || @@ -98,7 +96,6 @@ const processNodeData = (node: any, optimizeLayout: boolean) => { } }); } - console.log("eee"); // Handle text-specific properties if (figmaNode.type === "TEXT") { @@ -136,7 +133,6 @@ const processNodeData = (node: any, optimizeLayout: boolean) => { // For multiple segments, add index suffix mutableSegment.uniqueId = `${baseSegmentName}_span_${(index + 1).toString().padStart(2, "0")}`; } - console.log("after"); return mutableSegment; }, ); @@ -163,8 +159,6 @@ const processNodeData = (node: any, optimizeLayout: boolean) => { ) { node.variantProperties = figmaNode.variantProperties; } - - console.log("figmaNode", figmaNode); // Always copy size and position if ("width" in figmaNode) { node.width = (figmaNode as any).width; @@ -267,11 +261,7 @@ export const run = async (settings: PluginSettings) => { } const code = await convertToCode(convertedSelection, settings); - const htmlPreview = await generateHTMLPreview( - convertedSelection, - settings, - code, - ); + const htmlPreview = await generateHTMLPreview(convertedSelection, settings); const colors = retrieveGenericSolidUIColors(framework); const gradients = retrieveGenericGradients(framework, settings); diff --git a/packages/backend/src/html/htmlMain.ts b/packages/backend/src/html/htmlMain.ts index abfbb67d..16a1aeb2 100644 --- a/packages/backend/src/html/htmlMain.ts +++ b/packages/backend/src/html/htmlMain.ts @@ -65,9 +65,11 @@ export function generateUniqueClassName(prefix = "figma"): string { // But keep the counter logic as a fallback const count = classNameCounters.get(sanitizedPrefix) || 0; classNameCounters.set(sanitizedPrefix, count + 1); - + // Only add suffix if this isn't the first instance - return count === 0 ? sanitizedPrefix : `${sanitizedPrefix}_${count.toString().padStart(2, "0")}`; + return count === 0 + ? sanitizedPrefix + : `${sanitizedPrefix}_${count.toString().padStart(2, "0")}`; } // Reset all class name counters - call this at the start of processing @@ -260,9 +262,9 @@ function generateComponentCode( ): string { switch (mode) { case "styled-components": - return generateReactComponent(html, sceneNode, true); + return generateReactComponent(html, sceneNode); case "svelte": - return generateSvelteComponent(html, sceneNode); + return generateSvelteComponent(html); case "html": case "jsx": default: @@ -274,18 +276,15 @@ function generateComponentCode( function generateReactComponent( html: string, sceneNode: Array, - useStyledComponents: boolean = false, ): string { - const styledComponentsCode = useStyledComponents - ? generateStyledComponents() - : ""; + const styledComponentsCode = generateStyledComponents(); + const componentName = getReactComponentName(sceneNode[0]); - const imports = ['import React from "react";']; - - if (useStyledComponents) { - imports.push('import styled from "styled-components";'); - } + const imports = [ + 'import React from "react";', + 'import styled from "styled-components";', + ]; return `${imports.join("\n")} ${styledComponentsCode ? `\n${styledComponentsCode}` : ""} @@ -301,12 +300,7 @@ export default ${componentName}; } // Generate Svelte component from the collected styles and HTML -function generateSvelteComponent( - html: string, - sceneNode: Array, -): string { - const componentName = getReactComponentName(sceneNode[0]); - +function generateSvelteComponent(html: string): string { // Build CSS classes similar to styled-components but for Svelte const cssRules: string[] = []; @@ -368,7 +362,6 @@ export const htmlMain = async ( export const generateHTMLPreview = async ( nodes: SceneNode[], settings: PluginSettings, - code?: string, ): Promise => { const result = await htmlMain( nodes, diff --git a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts index 79e3d79c..0e72f947 100644 --- a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts +++ b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts @@ -48,7 +48,7 @@ export class TailwindDefaultBuilder { return this.node.visible ?? true; } get isJSX() { - return this.settings.jsx; + return this.settings.tailwindGenerationMode === "jsx"; } get optimizeLayout() { return this.settings.optimizeLayout; diff --git a/packages/backend/src/tailwind/tailwindMain.ts b/packages/backend/src/tailwind/tailwindMain.ts index f92683d8..3d95f7b4 100644 --- a/packages/backend/src/tailwind/tailwindMain.ts +++ b/packages/backend/src/tailwind/tailwindMain.ts @@ -246,7 +246,10 @@ export const tailwindContainer = ( // Generate appropriate HTML if (children) { return `\n<${tag}${build}${src}>${indentString(children)}\n`; - } else if (SELF_CLOSING_TAGS.includes(tag) || settings.jsx) { + } else if ( + SELF_CLOSING_TAGS.includes(tag) || + settings.tailwindGenerationMode === "jsx" + ) { return `\n<${tag}${build}${src} />`; } else { return `\n<${tag}${build}${src}>`; diff --git a/packages/plugin-ui/src/codegenPreferenceOptions.ts b/packages/plugin-ui/src/codegenPreferenceOptions.ts index 0347dfac..41acc3c8 100644 --- a/packages/plugin-ui/src/codegenPreferenceOptions.ts +++ b/packages/plugin-ui/src/codegenPreferenceOptions.ts @@ -3,11 +3,11 @@ import { LocalCodegenPreferenceOptions, SelectPreferenceOptions } from "types"; export const preferenceOptions: LocalCodegenPreferenceOptions[] = [ { itemType: "individual_select", - propertyName: "jsx", - label: "React (JSX)", - description: "", + propertyName: "showLayerNames", + label: "Layer names", + description: "Include Figma layer names in classes.", isDefault: false, - includedLanguages: ["Tailwind"], + includedLanguages: ["HTML", "Tailwind"], }, { itemType: "individual_select", @@ -18,14 +18,6 @@ export const preferenceOptions: LocalCodegenPreferenceOptions[] = [ isDefault: true, includedLanguages: ["HTML", "Tailwind", "Flutter", "SwiftUI"], }, - { - itemType: "individual_select", - propertyName: "showLayerNames", - label: "Layer names", - description: "Include Figma layer names in classes.", - isDefault: false, - includedLanguages: ["HTML", "Tailwind"], - }, { itemType: "individual_select", propertyName: "roundTailwindValues", @@ -56,7 +48,8 @@ export const preferenceOptions: LocalCodegenPreferenceOptions[] = [ itemType: "individual_select", propertyName: "embedImages", label: "Embed Images", - description: "Convert Figma images to Base64 and embed them in the code.", + description: + "Convert Figma images to Base64 and embed them in the code. This may be slow. If there are too many images, it could freeze Figma.", isDefault: false, includedLanguages: ["HTML"], }, @@ -64,7 +57,8 @@ export const preferenceOptions: LocalCodegenPreferenceOptions[] = [ itemType: "individual_select", propertyName: "embedVectors", label: "Embed Vectors", - description: "Convert Figma vectors to code.", + description: + "Convert Figma vectors to code. This is faster than embedding images, but still slower than not embedding.", isDefault: false, includedLanguages: ["HTML", "Tailwind"], }, @@ -83,6 +77,16 @@ export const selectPreferenceOptions: SelectPreferenceOptions[] = [ ], includedLanguages: ["HTML"], }, + { + itemType: "select", + propertyName: "tailwindGenerationMode", + label: "Mode", + options: [ + { label: "HTML", value: "html" }, + { label: "React (JSX)", value: "jsx" }, + ], + includedLanguages: ["Tailwind"], + }, { itemType: "select", propertyName: "flutterGenerationMode", diff --git a/packages/plugin-ui/src/components/CodePanel.tsx b/packages/plugin-ui/src/components/CodePanel.tsx index 2f60c731..5d8013dc 100644 --- a/packages/plugin-ui/src/components/CodePanel.tsx +++ b/packages/plugin-ui/src/components/CodePanel.tsx @@ -12,6 +12,7 @@ import { CopyButton } from "./CopyButton"; import EmptyState from "./EmptyState"; import SettingsGroup from "./SettingsGroup"; import CustomPrefixInput from "./CustomPrefixInput"; +import FrameworkTabs from "./FrameworkTabs"; interface CodePanelProps { code: string; @@ -83,7 +84,7 @@ const CodePanel = (props: CodePanelProps) => { ); // Define preference grouping based on property names - const essentialPropertyNames = ["jsx", "optimizeLayout"]; + const essentialPropertyNames = ["jsx"]; const stylingPropertyNames = [ "styledComponents", "exportCSS", @@ -91,6 +92,7 @@ const CodePanel = (props: CodePanelProps) => { "roundTailwindColors", "useColorVariables", "showLayerNames", + "optimizeLayout", "embedImages", "embedVectors", ]; @@ -143,38 +145,27 @@ const CodePanel = (props: CodePanelProps) => { {/* Framework-specific options */} {selectableSettingsFiltered.length > 0 && ( -
-

+

+

{selectedFramework} Options

-
- {selectableSettingsFiltered.map((preference) => ( -
- {preference.options.map((option) => ( - { - onPreferenceChanged( - preference.propertyName, - option.value, - ); - }} - buttonClass="bg-blue-100 dark:bg-black dark:ring-blue-800" - checkClass="bg-blue-400 dark:bg-black dark:bg-blue-500 dark:border-blue-500 ring-blue-300 border-blue-400" - /> - ))} -
- ))} -
+ {selectableSettingsFiltered.map((preference) => { + // Regular toggle buttons for other options + return ( + option.isDefault) + ?.value ?? + "") as string + } + onChange={(value) => { + onPreferenceChanged(preference.propertyName, value); + }} + /> + ); + })}
)} diff --git a/packages/plugin-ui/src/components/FrameworkTabs.tsx b/packages/plugin-ui/src/components/FrameworkTabs.tsx new file mode 100644 index 00000000..e39b9de4 --- /dev/null +++ b/packages/plugin-ui/src/components/FrameworkTabs.tsx @@ -0,0 +1,43 @@ +import React from "react"; + +type Option = { + value: string; + label: string; +}; + +interface FrameworkTabsProps { + options: Option[]; + selectedValue: string; + onChange: (value: string) => void; +} + +const FrameworkTabs: React.FC = ({ + options, + selectedValue, + onChange, +}) => { + return ( +
+
+ {options.map((option) => { + const isSelected = option.value === selectedValue; + return ( + + ); + })} +
+
+ ); +}; + +export default FrameworkTabs; diff --git a/packages/plugin-ui/src/components/SelectableToggle.tsx b/packages/plugin-ui/src/components/SelectableToggle.tsx index faacea84..bcc55c19 100644 --- a/packages/plugin-ui/src/components/SelectableToggle.tsx +++ b/packages/plugin-ui/src/components/SelectableToggle.tsx @@ -1,5 +1,6 @@ import { useState } from "react"; import { Check, HelpCircle } from "lucide-react"; +import { cn } from "../lib/utils"; type SelectableToggleProps = { onSelect: (isSelected: boolean) => void; @@ -28,20 +29,19 @@ const SelectableToggle = ({
- {/* Tooltip */} + {/* Enhanced tooltip */} {showTooltip && description && ( -
- {description} +
+

+ {description} +

)} diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index 0bfc11da..a4fccee8 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -2,7 +2,6 @@ import "@figma/plugin-typings"; // Settings export type Framework = "Flutter" | "SwiftUI" | "HTML" | "Tailwind"; export interface HTMLSettings { - jsx: boolean; optimizeLayout: boolean; showLayerNames: boolean; embedImages: boolean; @@ -11,6 +10,7 @@ export interface HTMLSettings { htmlGenerationMode: "html" | "jsx" | "styled-components" | "svelte"; } export interface TailwindSettings extends HTMLSettings { + tailwindGenerationMode: "html" | "jsx"; roundTailwindValues: boolean; roundTailwindColors: boolean; useColorVariables: boolean; From 7c82b17cf6957737fd2455c63b5a2cda2445e9ff Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Thu, 6 Mar 2025 03:06:34 -0300 Subject: [PATCH 066/168] Small tweaks --- apps/plugin/plugin-src/code.ts | 4 ++-- manifest.json | 1 + packages/backend/src/code.ts | 16 +++++++++------- packages/backend/src/html/htmlMain.ts | 13 +++++++++---- .../src/tailwind/tailwindDefaultBuilder.ts | 2 -- packages/backend/src/tailwind/tailwindMain.ts | 1 - 6 files changed, 21 insertions(+), 16 deletions(-) diff --git a/apps/plugin/plugin-src/code.ts b/apps/plugin/plugin-src/code.ts index d466258f..08831db8 100644 --- a/apps/plugin/plugin-src/code.ts +++ b/apps/plugin/plugin-src/code.ts @@ -139,8 +139,8 @@ const standardMode = async () => { safeRun(userPluginSettings); }); - // Listen for document changes - figma.on("documentchange", () => { + // Listen for page changes + figma.on("currentpagechange", () => { console.log("[DEBUG] documentchange event triggered"); // Node: This was causing an infinite load when you try to export a background image from a group that contains children. // The reason for this is that the code will temporarily hide the children of the group in order to export a clean image diff --git a/manifest.json b/manifest.json index 888938bf..083d8ee0 100644 --- a/manifest.json +++ b/manifest.json @@ -7,6 +7,7 @@ "editorType": ["figma", "dev"], "capabilities": ["inspect", "codegen", "vscode"], "permissions": [], + "documentAccess": "dynamic-page", "networkAccess": { "allowedDomains": ["https://placehold.co"] }, diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index 2486e702..2e58bae7 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -36,7 +36,7 @@ const GRADIENT_PROPERTIES = ["fills", "strokes", "effects"]; * @param node The node to process * @param optimizeLayout Whether to extract and include inferredAutoLayout data */ -const processNodeData = (node: any, optimizeLayout: boolean) => { +const processNodeData = async (node: any, optimizeLayout: boolean) => { if (node.id) { // Check if we need to fetch the Figma node at all const hasGradient = GRADIENT_PROPERTIES.some((propName) => { @@ -71,7 +71,7 @@ const processNodeData = (node: any, optimizeLayout: boolean) => { node.type === "INSTANCE" || node.type === "TEXT" ) { - const figmaNode = figma.getNodeById(node.id); + const figmaNode = await figma.getNodeByIdAsync(node.id); if (!figmaNode) { return; @@ -168,7 +168,7 @@ const processNodeData = (node: any, optimizeLayout: boolean) => { } } else { // Hopefully one day this won't be needed anymore. - const figmaNode = figma.getNodeById(node.id); + const figmaNode = await figma.getNodeByIdAsync(node.id); if (figmaNode && "width" in figmaNode) { node.width = (figmaNode as any).width; node.height = (figmaNode as any).height; @@ -196,9 +196,9 @@ const processNodeData = (node: any, optimizeLayout: boolean) => { // Process children recursively if (node.children && Array.isArray(node.children)) { - node.children.forEach((child: any) => - processNodeData(child, optimizeLayout), - ); + for (const child of node.children) { + await processNodeData(child, optimizeLayout); + } } }; @@ -227,7 +227,9 @@ export const nodesToJSON = async ( )) as SceneNode[]; // Process gradients and inferredAutoLayout in the JSON tree before adding parent references - nodeJson.forEach((node) => processNodeData(node, optimizeLayout)); + for (const node of nodeJson) { + await processNodeData(node, optimizeLayout); + } // Add parent references to all children in the node tree nodeJson.forEach((node) => addParentReferences(node)); diff --git a/packages/backend/src/html/htmlMain.ts b/packages/backend/src/html/htmlMain.ts index 16a1aeb2..c2f85881 100644 --- a/packages/backend/src/html/htmlMain.ts +++ b/packages/backend/src/html/htmlMain.ts @@ -368,7 +368,6 @@ export const generateHTMLPreview = async ( { ...settings, htmlGenerationMode: "html", - jsx: false, }, true, ); @@ -386,7 +385,6 @@ const htmlWidgetGenerator = async ( sceneNode: ReadonlyArray, settings: HTMLSettings, ): Promise => { - console.log("htmlWidgetGenerator", sceneNode); // filter non visible nodes. This is necessary at this step because conversion already happened. const promiseOfConvertedCode = getVisibleNodes(sceneNode).map( convertNode(settings), @@ -630,7 +628,11 @@ const htmlContainer = async ( if (hasChildren) { builder.addStyles( - formatWithJSX("background-image", settings.jsx, `url(${imgUrl})`), + formatWithJSX( + "background-image", + settings.htmlGenerationMode === "jsx", + `url(${imgUrl})`, + ), ); } else { tag = "img"; @@ -655,7 +657,10 @@ const htmlContainer = async ( // Standard HTML approach for HTML, React, or Svelte if (children) { return `\n<${tag}${build}${src}>${indentString(children)}\n`; - } else if (selfClosingTags.includes(tag) || settings.jsx) { + } else if ( + selfClosingTags.includes(tag) || + settings.htmlGenerationMode === "jsx" + ) { return `\n<${tag}${build}${src} />`; } else { return `\n<${tag}${build}${src}>`; diff --git a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts index 0e72f947..e0d0731b 100644 --- a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts +++ b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts @@ -122,10 +122,8 @@ export class TailwindDefaultBuilder { position(): this { const { node, optimizeLayout } = this; - if (commonIsAbsolutePosition(node, optimizeLayout)) { const { x, y } = getCommonPositionValue(node); - console.log("x", x, y, "node", node); const parsedX = numberToFixedString(x); const parsedY = numberToFixedString(y); diff --git a/packages/backend/src/tailwind/tailwindMain.ts b/packages/backend/src/tailwind/tailwindMain.ts index 3d95f7b4..8bee9378 100644 --- a/packages/backend/src/tailwind/tailwindMain.ts +++ b/packages/backend/src/tailwind/tailwindMain.ts @@ -48,7 +48,6 @@ const tailwindWidgetGenerator = async ( const convertNode = (settings: TailwindSettings) => async (node: SceneNode): Promise => { - console.log("altNode", node); if (settings.embedVectors && (node as any).canBeFlattened) { const altNode = await renderAndAttachSVG(node); if (altNode.svg) { From 791b04aa4757669f278396499d7f9d174a1f3570 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Thu, 6 Mar 2025 13:09:40 -0300 Subject: [PATCH 067/168] Fix variables --- packages/backend/src/code.ts | 95 ++++++++++++++++--- .../backend/src/html/htmlDefaultBuilder.ts | 2 +- .../src/tailwind/builderImpl/tailwindColor.ts | 1 + .../backend/src/tailwind/conversionTables.ts | 20 ++-- .../src/tailwind/tailwindDefaultBuilder.ts | 1 + 5 files changed, 95 insertions(+), 24 deletions(-) diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index 2e58bae7..6a975ea8 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -12,6 +12,7 @@ import { postConversionComplete, postEmptyMessage } from "./messaging"; import { PluginSettings } from "types"; import { convertToCode } from "./common/retrieveUI/convertToCode"; import { generateHTMLPreview } from "./html/htmlMain"; +import { variableToColorName } from "./tailwind/conversionTables"; // Keep track of node names for sequential numbering const nodeNameCounters: Map = new Map(); @@ -31,12 +32,63 @@ const addParentReferences = (node: any) => { // Define all property paths that might contain gradients const GRADIENT_PROPERTIES = ["fills", "strokes", "effects"]; +/** + * Process color variables in a paint style and add pre-computed variable names + * @param paint The paint style to process (fill or stroke) + */ +const processColorVariables = async (paint: Paint) => { + if ( + paint.type === "GRADIENT_ANGULAR" || + paint.type === "GRADIENT_DIAMOND" || + paint.type === "GRADIENT_LINEAR" || + paint.type === "GRADIENT_RADIAL" + ) { + for (const stop of paint.gradientStops) { + if (stop.boundVariables?.color) { + (stop as any).variableColorName = await variableToColorName( + stop.boundVariables.color, + ); + } + } + } else if (paint.type === "SOLID" && paint.boundVariables?.color) { + // Pre-compute and store the variable name + (paint as any).variableColorName = await variableToColorName( + paint.boundVariables.color, + ); + } +}; + +const getColorVariables = async (node: any, settings: PluginSettings) => { + if (settings.useColorVariables) { + // Process color variables in fills and strokes + if (node.fills && Array.isArray(node.fills)) { + for (const fill of node.fills) { + await processColorVariables(fill); + } + } + + if (node.strokes && Array.isArray(node.strokes)) { + for (const stroke of node.strokes) { + await processColorVariables(stroke); + } + } + // Process color variables in effects if they exist + if (node.effects && Array.isArray(node.effects)) { + for (const effect of node.effects) { + if (effect.color) { + await processColorVariables(effect); + } + } + } + } +}; + /** * Recursively process node and its children to update with data not available in JSON * @param node The node to process * @param optimizeLayout Whether to extract and include inferredAutoLayout data */ -const processNodeData = async (node: any, optimizeLayout: boolean) => { +const processNodeData = async (node: any, settings: PluginSettings) => { if (node.id) { // Check if we need to fetch the Figma node at all const hasGradient = GRADIENT_PROPERTIES.some((propName) => { @@ -67,7 +119,7 @@ const processNodeData = async (node: any, optimizeLayout: boolean) => { // Handle additional node properties if ( hasGradient || - optimizeLayout || + settings.optimizeLayout || node.type === "INSTANCE" || node.type === "TEXT" ) { @@ -100,7 +152,7 @@ const processNodeData = async (node: any, optimizeLayout: boolean) => { // Handle text-specific properties if (figmaNode.type === "TEXT") { // Get the text segments - const styledTextSegments = figmaNode.getStyledTextSegments([ + let styledTextSegments = figmaNode.getStyledTextSegments([ "fontName", "fills", "fontSize", @@ -122,20 +174,32 @@ const processNodeData = async (node: any, optimizeLayout: boolean) => { const baseSegmentName = (node.uniqueName || node.name) .replace(/[^a-zA-Z0-9_-]/g, "") .toLowerCase(); + // Add a uniqueId to each segment - node.styledTextSegments = styledTextSegments.map( - (segment: any, index) => { + styledTextSegments = await Promise.all( + styledTextSegments.map(async (segment, index) => { const mutableSegment = Object.assign({}, segment); + + if (settings.useColorVariables && segment.fills) { + mutableSegment.fills = segment.fills.map((d) => ({ ...d })); + for (const fill of mutableSegment.fills) { + await processColorVariables(fill); + } + } + // For single segments, don't add index suffix if (styledTextSegments.length === 1) { - mutableSegment.uniqueId = `${baseSegmentName}_span`; + (mutableSegment as any).uniqueId = `${baseSegmentName}_span`; } else { // For multiple segments, add index suffix - mutableSegment.uniqueId = `${baseSegmentName}_span_${(index + 1).toString().padStart(2, "0")}`; + (mutableSegment as any).uniqueId = + `${baseSegmentName}_span_${(index + 1).toString().padStart(2, "0")}`; } return mutableSegment; - }, + }), ); + + node.styledTextSegments = styledTextSegments; } Object.assign(node, node.style); @@ -145,7 +209,7 @@ const processNodeData = async (node: any, optimizeLayout: boolean) => { } // Extract inferredAutoLayout if optimizeLayout is enabled - if (optimizeLayout && "inferredAutoLayout" in figmaNode) { + if (settings.optimizeLayout && "inferredAutoLayout" in figmaNode) { node.inferredAutoLayout = JSON.parse( JSON.stringify((figmaNode as any).inferredAutoLayout), ); @@ -159,6 +223,7 @@ const processNodeData = async (node: any, optimizeLayout: boolean) => { ) { node.variantProperties = figmaNode.variantProperties; } + // Always copy size and position if ("width" in figmaNode) { node.width = (figmaNode as any).width; @@ -177,6 +242,8 @@ const processNodeData = async (node: any, optimizeLayout: boolean) => { } } + await getColorVariables(node, settings); + // Set default layout properties if missing if (!node.layoutMode) node.layoutMode = "NONE"; if (!node.layoutGrow) node.layoutGrow = 0; @@ -197,7 +264,7 @@ const processNodeData = async (node: any, optimizeLayout: boolean) => { // Process children recursively if (node.children && Array.isArray(node.children)) { for (const child of node.children) { - await processNodeData(child, optimizeLayout); + await processNodeData(child, settings); } } }; @@ -210,7 +277,7 @@ const processNodeData = async (node: any, optimizeLayout: boolean) => { */ export const nodesToJSON = async ( nodes: ReadonlyArray, - optimizeLayout: boolean = false, + settings: PluginSettings, ): Promise => { // Reset name counters for each conversion nodeNameCounters.clear(); @@ -228,7 +295,7 @@ export const nodesToJSON = async ( // Process gradients and inferredAutoLayout in the JSON tree before adding parent references for (const node of nodeJson) { - await processNodeData(node, optimizeLayout); + await processNodeData(node, settings); } // Add parent references to all children in the node tree @@ -240,7 +307,7 @@ export const nodesToJSON = async ( export const run = async (settings: PluginSettings) => { clearWarnings(); - const { framework, optimizeLayout } = settings; + const { framework } = settings; const selection = figma.currentPage.selection; if (selection.length > 1) { @@ -249,7 +316,7 @@ export const run = async (settings: PluginSettings) => { ); } - const nodeJson = await nodesToJSON(selection, optimizeLayout); + const nodeJson = await nodesToJSON(selection, settings); console.log("nodeJson", nodeJson); // Now we work directly with the JSON nodes diff --git a/packages/backend/src/html/htmlDefaultBuilder.ts b/packages/backend/src/html/htmlDefaultBuilder.ts index da6cc673..eb0d0e9c 100644 --- a/packages/backend/src/html/htmlDefaultBuilder.ts +++ b/packages/backend/src/html/htmlDefaultBuilder.ts @@ -315,7 +315,7 @@ export class HtmlDefaultBuilder { const { node, settings } = this; const { width, height } = htmlSizePartial( node, - settings.jsx, + settings.htmlGenerationMode === "jsx", settings.optimizeLayout, ); diff --git a/packages/backend/src/tailwind/builderImpl/tailwindColor.ts b/packages/backend/src/tailwind/builderImpl/tailwindColor.ts index 1e0df04a..bbfd6202 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindColor.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindColor.ts @@ -61,6 +61,7 @@ export const tailwindSolidColor = ( fill: SolidPaint | ColorStop, kind: TailwindColorType, ): string => { + console.log("fill is", fill); const { colorName } = getColorInfo(fill); const effectiveOpacity = calculateEffectiveOpacity(fill); diff --git a/packages/backend/src/tailwind/conversionTables.ts b/packages/backend/src/tailwind/conversionTables.ts index 3b52756d..296c3c68 100644 --- a/packages/backend/src/tailwind/conversionTables.ts +++ b/packages/backend/src/tailwind/conversionTables.ts @@ -140,11 +140,10 @@ export const nearestColorFromRgb = (color: RGB) => { return { name, value }; }; -export const variableToColorName = (alias: VariableAlias) => { +export const variableToColorName = async (alias: VariableAlias) => { return ( - figma.variables - .getVariableById(alias.id) - ?.name.replaceAll("/", "-") + (await figma.variables.getVariableByIdAsync(alias.id))?.name + .replaceAll("/", "-") .replaceAll(" ", "-") || alias.id.toLowerCase().replaceAll(":", "-") ); }; @@ -161,12 +160,15 @@ export function getColorInfo(fill: SolidPaint | ColorStop) { let hex: string = "#" + rgbTo6hex(fill.color); let meta: string = ""; + console.log( + "(fill as any).variableColorName", + fill, + (fill as any).variableColorName, + ); // variable - if ( - localTailwindSettings.useColorVariables && - fill.boundVariables?.color - ) { - colorName = variableToColorName(fill.boundVariables.color); + if ((fill as any).variableColorName) { + // Use pre-computed variable name if available + colorName = (fill as any).variableColorName; // || variableToColorName(fill.boundVariables.color); colorType = "variable"; meta = "custom"; diff --git a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts index e0d0731b..9b740062 100644 --- a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts +++ b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts @@ -94,6 +94,7 @@ export class TailwindDefaultBuilder { } commonShapeStyles(): this { + console.log("this.node is", this.node); this.customColor((this.node as MinimalFillsMixin).fills, "bg"); this.radius(); this.shadow(); From e53287a11d34b51282da59d0fcdd0cc8ed4d7fd7 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Thu, 6 Mar 2025 13:29:21 -0300 Subject: [PATCH 068/168] Fix color --- .../backend/src/html/builderImpl/htmlColor.ts | 73 ++++++++++--------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/packages/backend/src/html/builderImpl/htmlColor.ts b/packages/backend/src/html/builderImpl/htmlColor.ts index 3defdc16..e23bb0e0 100644 --- a/packages/backend/src/html/builderImpl/htmlColor.ts +++ b/packages/backend/src/html/builderImpl/htmlColor.ts @@ -1,24 +1,25 @@ import { HTMLSettings } from "types"; import { numberToFixedString } from "../../common/numToAutoFixed"; import { retrieveTopFill } from "../../common/retrieveFill"; -import { variableToColorName } from "../../tailwind/conversionTables"; import { getGradientTransformCoordinates } from "../../common/color"; /** * Helper to process a color with variable binding if present */ -const processColorWithVariable = ( - color: RGB, - opacity: number = 1, - boundVariable?: VariableAlias, - useCustomColors: boolean = false, -): string => { - if (useCustomColors && boundVariable) { - const varName = variableToColorName(boundVariable); - const fallbackColor = htmlColor(color, opacity); +const processColorWithVariable = (fill: { + color: RGB; + opacity?: number; + variableColorName?: string; +}): string => { + const opacity = fill.opacity ?? 1; + + if (fill.variableColorName) { + const varName = fill.variableColorName; + console.log("varName is", varName); + const fallbackColor = htmlColor(fill.color, opacity); return `var(--${varName}, ${fallbackColor})`; } - return htmlColor(color, opacity); + return htmlColor(fill.color, opacity); }; /** @@ -26,12 +27,16 @@ const processColorWithVariable = ( */ const getColorAndVariable = ( fill: Paint, -): { color: RGB; opacity: number; boundVariable?: VariableAlias } => { +): { + color: RGB; + opacity: number; + variableColorName?: string; +} => { if (fill.type === "SOLID") { return { color: fill.color, opacity: fill.opacity ?? 1, - boundVariable: fill.boundVariables?.color, + variableColorName: (fill as any).variableColorName, }; } else if ( (fill.type === "GRADIENT_LINEAR" || @@ -44,7 +49,7 @@ const getColorAndVariable = ( return { color: firstStop.color, opacity: fill.opacity ?? 1, - boundVariable: firstStop.boundVariables?.color, + variableColorName: (firstStop as any).variableColorName, }; } return { color: { r: 0, g: 0, b: 0 }, opacity: 0 }; @@ -55,16 +60,10 @@ export const htmlColorFromFills = ( fills: ReadonlyArray | PluginAPI["mixed"] | undefined, settings: HTMLSettings, ): string => { - const useCustomColors = settings.useColorVariables === true; const fill = retrieveTopFill(fills); if (fill) { - const { color, opacity, boundVariable } = getColorAndVariable(fill); - return processColorWithVariable( - color, - opacity, - boundVariable, - useCustomColors, - ); + const colorInfo = getColorAndVariable(fill); + return processColorWithVariable(colorInfo); } return ""; }; @@ -91,19 +90,21 @@ export const htmlColor = (color: RGB, alpha: number = 1): string => { }; // Process a single gradient stop with proper color and position -const processGradientStop = ( +const processGradientStop = async ( stop: ColorStop, useCustomColors: boolean, fillOpacity: number = 1, positionMultiplier: number = 100, unit: string = "%", -): string => { - const color = processColorWithVariable( - stop.color, - stop.color.a * fillOpacity, - stop.boundVariables?.color, - useCustomColors, - ); +): Promise => { + const fillInfo = { + color: stop.color, + opacity: stop.color.a * fillOpacity, + boundVariables: stop.boundVariables, + variableColorName: (stop as any).variableColorName, + }; + + const color = processColorWithVariable(fillInfo); const position = `${(stop.position * positionMultiplier).toFixed(0)}${unit}`; return `${color} ${position}`; }; @@ -129,10 +130,10 @@ const processGradientStops = ( .join(", "); }; -export const htmlGradientFromFills = ( +export const htmlGradientFromFills = async ( fills: ReadonlyArray | PluginAPI["mixed"], settings: HTMLSettings, -): string => { +): Promise => { const useCustomColors = settings.useColorVariables === true; const fill = retrieveTopFill(fills); if (!fill) return ""; @@ -140,9 +141,9 @@ export const htmlGradientFromFills = ( case "GRADIENT_LINEAR": return htmlLinearGradient(fill, useCustomColors); case "GRADIENT_ANGULAR": - return htmlAngularGradient(fill, useCustomColors); + return await htmlAngularGradient(fill, useCustomColors); case "GRADIENT_RADIAL": - return htmlRadialGradient(fill, useCustomColors); + return await htmlRadialGradient(fill, useCustomColors); case "GRADIENT_DIAMOND": return htmlDiamondGradient(fill, useCustomColors); // Added diamond gradient case default: @@ -168,10 +169,10 @@ export const cssGradientAngle = (angle: number): number => { return cssAngle < 0 ? cssAngle + 360 : cssAngle; }; -export const htmlLinearGradient = ( +export const htmlLinearGradient = async ( fill: GradientPaint, useCustomColors: boolean, -): string => { +): Promise => { const figmaAngle = gradientAngle2(fill); const angle = cssGradientAngle(figmaAngle).toFixed(0); const mappedFill = processGradientStops( From 95aff6b277d417edd09e914e17620f64139510df Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Thu, 6 Mar 2025 15:34:15 -0300 Subject: [PATCH 069/168] Fix styled higlighting --- packages/backend/src/html/htmlMain.ts | 7 ++----- packages/plugin-ui/src/components/CodePanel.tsx | 13 ++++++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/backend/src/html/htmlMain.ts b/packages/backend/src/html/htmlMain.ts index c2f85881..749b7217 100644 --- a/packages/backend/src/html/htmlMain.ts +++ b/packages/backend/src/html/htmlMain.ts @@ -289,14 +289,11 @@ function generateReactComponent( return `${imports.join("\n")} ${styledComponentsCode ? `\n${styledComponentsCode}` : ""} -const ${componentName} = () => { +export const ${componentName} = () => { return ( ${indentString(html, 4)} ); -}; - -export default ${componentName}; -`; +};`; } // Generate Svelte component from the collected styles and HTML diff --git a/packages/plugin-ui/src/components/CodePanel.tsx b/packages/plugin-ui/src/components/CodePanel.tsx index 5d8013dc..1c36cdce 100644 --- a/packages/plugin-ui/src/components/CodePanel.tsx +++ b/packages/plugin-ui/src/components/CodePanel.tsx @@ -199,11 +199,14 @@ const CodePanel = (props: CodePanelProps) => { ) : ( Date: Thu, 6 Mar 2025 16:51:01 -0300 Subject: [PATCH 070/168] Fiz z positioning --- packages/backend/src/code.ts | 28 +++++++++++++++++++ .../backend/src/html/htmlDefaultBuilder.ts | 6 ++++ 2 files changed, 34 insertions(+) diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index 6a975ea8..e12020c1 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -266,7 +266,35 @@ const processNodeData = async (node: any, settings: PluginSettings) => { for (const child of node.children) { await processNodeData(child, settings); } + + // Handle itemReverseZIndex for absolute-positioned children + if ("itemReverseZIndex" in node && node.itemReverseZIndex) { + const absoluteChildren = node.children.filter( + (child: SceneNode) => + "layoutPositioning" in child && + child.layoutPositioning === "ABSOLUTE", + ); + const reversedAbsolute = [...absoluteChildren].reverse(); + let index = 0; + node.children = node.children.map((child: SceneNode) => { + if ( + "layoutPositioning" in child && + child.layoutPositioning === "ABSOLUTE" + ) { + return reversedAbsolute[index++]; + } else { + return child; + } + }); + } } + + // Process children recursively + // if (node.children && Array.isArray(node.children)) { + // for (const child of node.children) { + // await processNodeData(child, settings); + // } + // } }; /** diff --git a/packages/backend/src/html/htmlDefaultBuilder.ts b/packages/backend/src/html/htmlDefaultBuilder.ts index eb0d0e9c..f97990ff 100644 --- a/packages/backend/src/html/htmlDefaultBuilder.ts +++ b/packages/backend/src/html/htmlDefaultBuilder.ts @@ -122,6 +122,12 @@ export class HtmlDefaultBuilder { this.autoLayoutPadding(); this.position(); this.blend(); + + // Add z-index if we have a custom value from the itemReverseZIndex handling + if ((this.node as any).customZIndex !== undefined) { + this.addStyles(`z-index: ${(this.node as any).customZIndex}`); + } + return this; } From 6a2b5191af5d7d818e1c06199f86106a545c8162 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Thu, 6 Mar 2025 16:52:01 -0300 Subject: [PATCH 071/168] Fix text --- packages/backend/src/code.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index e12020c1..c4e87975 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -267,8 +267,12 @@ const processNodeData = async (node: any, settings: PluginSettings) => { await processNodeData(child, settings); } - // Handle itemReverseZIndex for absolute-positioned children - if ("itemReverseZIndex" in node && node.itemReverseZIndex) { + // Handle itemReverseZIndex for absolute-positioned children on AutoLayout + if ( + "children" in node && + "itemReverseZIndex" in node && + node.itemReverseZIndex + ) { const absoluteChildren = node.children.filter( (child: SceneNode) => "layoutPositioning" in child && From 0218ee24f52ec458a2230ae82e42939ec90ea198 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Thu, 6 Mar 2025 18:01:22 -0300 Subject: [PATCH 072/168] Fiz z-align --- packages/backend/src/code.ts | 48 ++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index c4e87975..292ec0bd 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -83,6 +83,29 @@ const getColorVariables = async (node: any, settings: PluginSettings) => { } }; +function adjustChildrenOrder(node: any) { + if (!node.itemReverseZIndex || !node.children || node.layoutMode === "NONE") { + return; + } + + const children = node.children; + const absoluteChildren = []; + const fixedChildren = []; + + // Single pass to separate absolute and fixed children + for (let i = children.length - 1; i >= 0; i--) { + const child = children[i]; + if (child.layoutPositioning === "ABSOLUTE") { + absoluteChildren.push(child); + } else { + fixedChildren.unshift(child); // Add to beginning to maintain original order + } + } + + // Combine the arrays (reversed absolute children + original order fixed children) + node.children = [...absoluteChildren, ...fixedChildren]; +} + /** * Recursively process node and its children to update with data not available in JSON * @param node The node to process @@ -267,30 +290,7 @@ const processNodeData = async (node: any, settings: PluginSettings) => { await processNodeData(child, settings); } - // Handle itemReverseZIndex for absolute-positioned children on AutoLayout - if ( - "children" in node && - "itemReverseZIndex" in node && - node.itemReverseZIndex - ) { - const absoluteChildren = node.children.filter( - (child: SceneNode) => - "layoutPositioning" in child && - child.layoutPositioning === "ABSOLUTE", - ); - const reversedAbsolute = [...absoluteChildren].reverse(); - let index = 0; - node.children = node.children.map((child: SceneNode) => { - if ( - "layoutPositioning" in child && - child.layoutPositioning === "ABSOLUTE" - ) { - return reversedAbsolute[index++]; - } else { - return child; - } - }); - } + adjustChildrenOrder(node); } // Process children recursively From 5da861cf4066ac9e7003baacc9b89c1fa91a8ce4 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Thu, 6 Mar 2025 18:26:36 -0300 Subject: [PATCH 073/168] Fix absolute positioning --- packages/backend/src/code.ts | 18 +++-- .../backend/src/common/nodeWidthHeight.ts | 75 ++++++++++++------- .../backend/src/html/htmlDefaultBuilder.ts | 9 ++- .../src/tailwind/tailwindDefaultBuilder.ts | 3 +- 4 files changed, 70 insertions(+), 35 deletions(-) diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index 292ec0bd..4c1b9c48 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -144,6 +144,7 @@ const processNodeData = async (node: any, settings: PluginSettings) => { hasGradient || settings.optimizeLayout || node.type === "INSTANCE" || + node.type === "COMPONENT" || node.type === "TEXT" ) { const figmaNode = await figma.getNodeByIdAsync(node.id); @@ -239,11 +240,7 @@ const processNodeData = async (node: any, settings: PluginSettings) => { } // Extract component metadata from instances - if ( - node.type === "INSTANCE" && - "variantProperties" in figmaNode && - figmaNode.variantProperties - ) { + if ("variantProperties" in figmaNode && figmaNode.variantProperties) { node.variantProperties = figmaNode.variantProperties; } @@ -290,6 +287,17 @@ const processNodeData = async (node: any, settings: PluginSettings) => { await processNodeData(child, settings); } + if ( + node.layoutMode !== "NONE" && + "children" in node && + node.children.some( + (d: any) => + "layoutPositioning" in d && d.layoutPositioning === "ABSOLUTE", + ) + ) { + (node as any).isRelative = true; + } + adjustChildrenOrder(node); } diff --git a/packages/backend/src/common/nodeWidthHeight.ts b/packages/backend/src/common/nodeWidthHeight.ts index 119e5f2b..bd471ed4 100644 --- a/packages/backend/src/common/nodeWidthHeight.ts +++ b/packages/backend/src/common/nodeWidthHeight.ts @@ -22,30 +22,53 @@ export const nodeSize = (node: SceneNode, optimizeLayout: boolean): Size => { ? node.parent.inferredAutoLayout?.layoutMode : node.parent.layoutMode; - const isWidthFill = - (parentLayoutMode === "HORIZONTAL" && nodeAuto.layoutGrow === 1) || - (parentLayoutMode === "VERTICAL" && nodeAuto.layoutAlign === "STRETCH"); - const isHeightFill = - (parentLayoutMode === "HORIZONTAL" && nodeAuto.layoutAlign === "STRETCH") || - (parentLayoutMode === "VERTICAL" && nodeAuto.layoutGrow === 1); - const modesSwapped = parentLayoutMode === "HORIZONTAL"; - const primaryAxisMode = modesSwapped - ? "counterAxisSizingMode" - : "primaryAxisSizingMode"; - const counterAxisMode = modesSwapped - ? "primaryAxisSizingMode" - : "counterAxisSizingMode"; - - return { - width: isWidthFill - ? "fill" - : "layoutMode" in nodeAuto && nodeAuto[primaryAxisMode] === "AUTO" - ? null - : node.width, - height: isHeightFill - ? "fill" - : "layoutMode" in nodeAuto && nodeAuto[counterAxisMode] === "AUTO" - ? null - : node.height, - }; + // Check for explicit layout sizing properties + if ( + "layoutSizingHorizontal" in nodeAuto && + "layoutSizingVertical" in nodeAuto + ) { + const width = + nodeAuto.layoutSizingHorizontal === "FILL" + ? "fill" + : nodeAuto.layoutSizingHorizontal === "HUG" + ? null + : node.width; + + const height = + nodeAuto.layoutSizingVertical === "FILL" + ? "fill" + : nodeAuto.layoutSizingVertical === "HUG" + ? null + : node.height; + + return { width, height }; + } + + return { width: node.width, height: node.height }; + + // const isWidthFill = + // (parentLayoutMode === "HORIZONTAL" && nodeAuto.layoutGrow === 1) || + // (parentLayoutMode === "VERTICAL" && nodeAuto.layoutAlign === "STRETCH"); + // const isHeightFill = + // (parentLayoutMode === "HORIZONTAL" && nodeAuto.layoutAlign === "STRETCH") || + // (parentLayoutMode === "VERTICAL" && nodeAuto.layoutGrow === 1); + // const modesSwapped = parentLayoutMode === "HORIZONTAL"; + // const primaryAxisMode = modesSwapped + // ? "counterAxisSizingMode" + // : "primaryAxisSizingMode"; + // const counterAxisMode = modesSwapped + // ? "primaryAxisSizingMode" + // : "counterAxisSizingMode"; + + // return { + // width: isWidthFill + // ? "fill" + // : "layoutMode" in nodeAuto && nodeAuto[primaryAxisMode] === "AUTO" + // ? null + // : node.width, + // height: isHeightFill + // ? "fill" + // : "layoutMode" in nodeAuto && nodeAuto[counterAxisMode] === "AUTO" + // ? null + // : node.height, }; diff --git a/packages/backend/src/html/htmlDefaultBuilder.ts b/packages/backend/src/html/htmlDefaultBuilder.ts index f97990ff..e0b6dc0f 100644 --- a/packages/backend/src/html/htmlDefaultBuilder.ts +++ b/packages/backend/src/html/htmlDefaultBuilder.ts @@ -122,12 +122,12 @@ export class HtmlDefaultBuilder { this.autoLayoutPadding(); this.position(); this.blend(); - + // Add z-index if we have a custom value from the itemReverseZIndex handling if ((this.node as any).customZIndex !== undefined) { this.addStyles(`z-index: ${(this.node as any).customZIndex}`); } - + return this; } @@ -242,7 +242,8 @@ export class HtmlDefaultBuilder { node.type === "GROUP" || ("layoutMode" in node && ((optimizeLayout ? node.inferredAutoLayout : null) ?? node) - ?.layoutMode === "NONE") + ?.layoutMode === "NONE") || + (node as any).isRelative ) { this.addStyles(formatWithJSX("position", isJSX, "relative")); } @@ -424,6 +425,8 @@ export class HtmlDefaultBuilder { } if ("variantProperties" in this.node && this.node.variantProperties) { + console.log("this.node.variantProperties", this.node.variantProperties); + Object.entries(this.node.variantProperties) ?.map((prop) => formatDataAttribute(prop[0], prop[1])) .sort() diff --git a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts index 9b740062..d34b4bae 100644 --- a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts +++ b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts @@ -144,7 +144,8 @@ export class TailwindDefaultBuilder { node.type === "GROUP" || ("layoutMode" in node && ((optimizeLayout ? node.inferredAutoLayout : null) ?? node) - ?.layoutMode === "NONE") + ?.layoutMode === "NONE") || + (node as any).isRelative ) { this.addAttributes("relative"); } From 705bf39c0478324f0cd69fee6a8b86bdb3e8934b Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Thu, 6 Mar 2025 21:27:09 -0300 Subject: [PATCH 074/168] Add gradient to tailwind --- .../backend/src/html/builderImpl/htmlColor.ts | 105 +++++++++--------- .../src/tailwind/builderImpl/tailwindColor.ts | 38 +++++-- 2 files changed, 83 insertions(+), 60 deletions(-) diff --git a/packages/backend/src/html/builderImpl/htmlColor.ts b/packages/backend/src/html/builderImpl/htmlColor.ts index e23bb0e0..2b1cde2e 100644 --- a/packages/backend/src/html/builderImpl/htmlColor.ts +++ b/packages/backend/src/html/builderImpl/htmlColor.ts @@ -1,7 +1,6 @@ import { HTMLSettings } from "types"; import { numberToFixedString } from "../../common/numToAutoFixed"; import { retrieveTopFill } from "../../common/retrieveFill"; -import { getGradientTransformCoordinates } from "../../common/color"; /** * Helper to process a color with variable binding if present @@ -90,13 +89,12 @@ export const htmlColor = (color: RGB, alpha: number = 1): string => { }; // Process a single gradient stop with proper color and position -const processGradientStop = async ( +const processGradientStop = ( stop: ColorStop, - useCustomColors: boolean, fillOpacity: number = 1, positionMultiplier: number = 100, unit: string = "%", -): Promise => { +): string => { const fillInfo = { color: stop.color, opacity: stop.color.a * fillOpacity, @@ -112,40 +110,32 @@ const processGradientStop = async ( // Process all gradient stops for any gradient type const processGradientStops = ( stops: ReadonlyArray, - useCustomColors: boolean, fillOpacity: number = 1, positionMultiplier: number = 100, unit: string = "%", ): string => { return stops .map((stop) => - processGradientStop( - stop, - useCustomColors, - fillOpacity, - positionMultiplier, - unit, - ), + processGradientStop(stop, fillOpacity, positionMultiplier, unit), ) .join(", "); }; -export const htmlGradientFromFills = async ( +export const htmlGradientFromFills = ( fills: ReadonlyArray | PluginAPI["mixed"], settings: HTMLSettings, -): Promise => { - const useCustomColors = settings.useColorVariables === true; +): string => { const fill = retrieveTopFill(fills); if (!fill) return ""; switch (fill.type) { case "GRADIENT_LINEAR": - return htmlLinearGradient(fill, useCustomColors); + return htmlLinearGradient(fill); case "GRADIENT_ANGULAR": - return await htmlAngularGradient(fill, useCustomColors); + return htmlAngularGradient(fill); case "GRADIENT_RADIAL": - return await htmlRadialGradient(fill, useCustomColors); + return htmlRadialGradient(fill); // Updated to use radial gradient function case "GRADIENT_DIAMOND": - return htmlDiamondGradient(fill, useCustomColors); // Added diamond gradient case + return htmlDiamondGradient(fill); default: return ""; } @@ -169,15 +159,11 @@ export const cssGradientAngle = (angle: number): number => { return cssAngle < 0 ? cssAngle + 360 : cssAngle; }; -export const htmlLinearGradient = async ( - fill: GradientPaint, - useCustomColors: boolean, -): Promise => { +export const htmlLinearGradient = (fill: GradientPaint): string => { const figmaAngle = gradientAngle2(fill); const angle = cssGradientAngle(figmaAngle).toFixed(0); const mappedFill = processGradientStops( fill.gradientStops, - useCustomColors, fill.opacity ?? 1, 100, "%", @@ -187,47 +173,64 @@ export const htmlLinearGradient = async ( export const invertYCoordinate = (y: number): number => 1 - y; -export const htmlRadialGradient = ( - fill: GradientPaint, - useCustomColors: boolean, -): string => { +export const htmlAngularGradient = (fill: GradientPaint): string => { + const angle = gradientAngle2(fill).toFixed(0); + // Extract matrix components + const a = fill.gradientTransform[0][0]; + const b = fill.gradientTransform[0][1]; + const tx = fill.gradientTransform[0][2]; + const c = fill.gradientTransform[1][0]; + const d = fill.gradientTransform[1][1]; + const ty = fill.gradientTransform[1][2]; + // Compute center by transforming (0.5, 0.5) + const centerX = (a * 0.5 + b * 0.5 + tx) * 100; + const centerY = (c * 0.5 + d * 0.5 + ty) * 100; + const centerXPercent = centerX.toFixed(2); + const centerYPercent = centerY.toFixed(2); const mappedFill = processGradientStops( fill.gradientStops, - useCustomColors, fill.opacity ?? 1, - 100, - "%", + 360, + "deg", ); - const { centerX, centerY, radiusX, radiusY } = - getGradientTransformCoordinates(fill.gradientTransform); - return `radial-gradient(${radiusX}% ${radiusY}% at ${centerX}% ${centerY}%, ${mappedFill})`; + return `conic-gradient(from ${angle}deg at ${centerXPercent}% ${centerYPercent}%, ${mappedFill})`; }; -export const htmlAngularGradient = ( - fill: GradientPaint, - useCustomColors: boolean, -): string => { - const angle = gradientAngle2(fill).toFixed(0); - const centerX = (fill.gradientTransform[0][2] * 100).toFixed(2); - const centerY = (fill.gradientTransform[1][2] * 100).toFixed(2); - const mappedFill = processGradientStops( +export const htmlRadialGradient = (fill: GradientPaint): string => { + const [[a, b, tx], [c, d, ty]] = fill.gradientTransform; + + // Calculate inverse of the linear part of the gradientTransform matrix + const det = a * d - b * c; + if (Math.abs(det) < 1e-6) return ""; // Avoid division by zero + + const invDet = 1 / det; + const invA = d * invDet; + const invB = -b * invDet; + const invC = -c * invDet; + const invD = a * invDet; + + // Calculate center by solving inverse transform for (0.5, 0.5) + const cx = (invA * (0.5 - tx) + invB * (0.5 - ty)) * 100; + const cy = (invC * (0.5 - tx) + invD * (0.5 - ty)) * 100; + + // Calculate column vectors of inverse matrix + const col1Length = Math.sqrt(invA ** 2 + invC ** 2) * 100; + const col2Length = Math.sqrt(invB ** 2 + invD ** 2) * 100; + + // Get radii as half lengths of column vectors (sorted) + const radii = [col1Length / 2, col2Length / 2].sort((a, b) => b - a); + + const mappedStops = processGradientStops( fill.gradientStops, - useCustomColors, fill.opacity ?? 1, - 360, - "deg", ); - return `conic-gradient(from ${angle}deg at ${centerX}% ${centerY}%, ${mappedFill})`; + return `radial-gradient(ellipse ${radii[0].toFixed(2)}% ${radii[1].toFixed(2)}% at ${cx.toFixed(2)}% ${cy.toFixed(2)}%, ${mappedStops})`; }; // Added function for diamond gradient -export const htmlDiamondGradient = ( - fill: GradientPaint, - useCustomColors: boolean, -): string => { +export const htmlDiamondGradient = (fill: GradientPaint): string => { const stops = processGradientStops( fill.gradientStops, - useCustomColors, fill.opacity ?? 1, 50, // Adjusted multiplier for diamond gradient "%", diff --git a/packages/backend/src/tailwind/builderImpl/tailwindColor.ts b/packages/backend/src/tailwind/builderImpl/tailwindColor.ts index bbfd6202..15de851d 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindColor.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindColor.ts @@ -8,6 +8,13 @@ import { TailwindColorType } from "types"; import { addWarning } from "../../common/commonConversionWarnings"; import { retrieveTopFill } from "../../common/retrieveFill"; +// Import the HTML gradient functions +import { + htmlAngularGradient, + htmlRadialGradient, + htmlDiamondGradient, +} from "../../html/builderImpl/htmlColor"; + /** * Get a tailwind color value object * @param fill @@ -132,20 +139,33 @@ export const tailwindGradientFromFills = ( return tailwindGradient(fill); } - // Show warning if there's a non-linear gradient - if ( - fill.type === "GRADIENT_ANGULAR" || - fill.type === "GRADIENT_RADIAL" || - fill.type === "GRADIENT_DIAMOND" - ) { - addWarning( - "Gradients are not fully supported in Tailwind except for Linear Gradients.", - ); + // Use arbitrary values with HTML-based gradient syntax for other gradient types + if (fill.type === "GRADIENT_ANGULAR") { + return tailwindArbitraryGradient(htmlAngularGradient(fill)); + } + + if (fill.type === "GRADIENT_RADIAL") { + return tailwindArbitraryGradient(htmlRadialGradient(fill)); + } + + if (fill.type === "GRADIENT_DIAMOND") { + return tailwindArbitraryGradient(htmlDiamondGradient(fill)); } return ""; }; +/** + * Converts CSS gradient syntax to Tailwind arbitrary value syntax + * @param cssGradient The CSS gradient string (e.g., "radial-gradient(...)") + * @returns Tailwind class with arbitrary value (e.g., "bg-[radial-gradient(...)]") + */ +const tailwindArbitraryGradient = (cssGradient: string): string => { + // Replace spaces with underscores for Tailwind compatibility + const tailwindValue = cssGradient.replace(/\s+/g, "_"); + return `bg-[${tailwindValue}]`; +}; + export const tailwindGradient = (fill: GradientPaint): string => { const direction = gradientDirection(gradientAngle(fill)); From 4ffdb1add69f3e023c20c02daa873b3b06dc273c Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Thu, 6 Mar 2025 21:36:58 -0300 Subject: [PATCH 075/168] Fix gradient --- packages/backend/src/tailwind/builderImpl/tailwindColor.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/tailwind/builderImpl/tailwindColor.ts b/packages/backend/src/tailwind/builderImpl/tailwindColor.ts index 15de851d..00b323c4 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindColor.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindColor.ts @@ -149,7 +149,8 @@ export const tailwindGradientFromFills = ( } if (fill.type === "GRADIENT_DIAMOND") { - return tailwindArbitraryGradient(htmlDiamondGradient(fill)); + // Diamond is too complex, it is going to create 3 linear gradients, which gets too weird in Tailwind. + return ""; } return ""; From db60ab93c56887d05054ec33bdf502fd88507325 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Thu, 6 Mar 2025 21:41:01 -0300 Subject: [PATCH 076/168] Clean up --- packages/backend/src/code.ts | 2 +- packages/backend/src/common/retrieveUI/retrieveColors.ts | 5 ++--- packages/backend/src/html/builderImpl/htmlColor.ts | 6 +----- packages/backend/src/tailwind/builderImpl/tailwindColor.ts | 2 -- 4 files changed, 4 insertions(+), 11 deletions(-) diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index 4c1b9c48..2d59fab1 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -372,7 +372,7 @@ export const run = async (settings: PluginSettings) => { const code = await convertToCode(convertedSelection, settings); const htmlPreview = await generateHTMLPreview(convertedSelection, settings); const colors = retrieveGenericSolidUIColors(framework); - const gradients = retrieveGenericGradients(framework, settings); + const gradients = retrieveGenericGradients(framework); postConversionComplete({ code, diff --git a/packages/backend/src/common/retrieveUI/retrieveColors.ts b/packages/backend/src/common/retrieveUI/retrieveColors.ts index 4ce01345..b50a14ac 100644 --- a/packages/backend/src/common/retrieveUI/retrieveColors.ts +++ b/packages/backend/src/common/retrieveUI/retrieveColors.ts @@ -76,7 +76,6 @@ const convertSolidColor = ( export const retrieveGenericLinearGradients = ( framework: Framework, - settings: HTMLSettings, ): Array => { const selectionColors = figma.getSelectionColors(); const colorStr: Array = []; @@ -89,7 +88,7 @@ export const retrieveGenericLinearGradients = ( exportValue = flutterGradient(paint); break; case "HTML": - exportValue = htmlGradientFromFills([paint], settings); + exportValue = htmlGradientFromFills(paint); break; case "Tailwind": exportValue = tailwindGradient(paint); @@ -99,7 +98,7 @@ export const retrieveGenericLinearGradients = ( break; } colorStr.push({ - cssPreview: htmlGradientFromFills([paint], settings), + cssPreview: htmlGradientFromFills(paint), exportValue, }); } diff --git a/packages/backend/src/html/builderImpl/htmlColor.ts b/packages/backend/src/html/builderImpl/htmlColor.ts index 2b1cde2e..6eec999a 100644 --- a/packages/backend/src/html/builderImpl/htmlColor.ts +++ b/packages/backend/src/html/builderImpl/htmlColor.ts @@ -121,11 +121,7 @@ const processGradientStops = ( .join(", "); }; -export const htmlGradientFromFills = ( - fills: ReadonlyArray | PluginAPI["mixed"], - settings: HTMLSettings, -): string => { - const fill = retrieveTopFill(fills); +export const htmlGradientFromFills = (fill: Paint): string => { if (!fill) return ""; switch (fill.type) { case "GRADIENT_LINEAR": diff --git a/packages/backend/src/tailwind/builderImpl/tailwindColor.ts b/packages/backend/src/tailwind/builderImpl/tailwindColor.ts index 00b323c4..83d13733 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindColor.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindColor.ts @@ -5,14 +5,12 @@ import { nearestValue, } from "../conversionTables"; import { TailwindColorType } from "types"; -import { addWarning } from "../../common/commonConversionWarnings"; import { retrieveTopFill } from "../../common/retrieveFill"; // Import the HTML gradient functions import { htmlAngularGradient, htmlRadialGradient, - htmlDiamondGradient, } from "../../html/builderImpl/htmlColor"; /** From d9c92f4fa1dee071b80ce715cee855ae60df3002 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Thu, 6 Mar 2025 22:07:13 -0300 Subject: [PATCH 077/168] Fix padding and border --- packages/backend/src/code.ts | 29 ++++++++++++------- .../src/html/builderImpl/htmlAutoLayout.ts | 4 +-- .../backend/src/html/builderImpl/htmlColor.ts | 1 - .../backend/src/html/htmlDefaultBuilder.ts | 11 +++++-- 4 files changed, 29 insertions(+), 16 deletions(-) diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index 2d59fab1..69f9e692 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -235,7 +235,7 @@ const processNodeData = async (node: any, settings: PluginSettings) => { // Extract inferredAutoLayout if optimizeLayout is enabled if (settings.optimizeLayout && "inferredAutoLayout" in figmaNode) { node.inferredAutoLayout = JSON.parse( - JSON.stringify((figmaNode as any).inferredAutoLayout), + JSON.stringify(figmaNode.inferredAutoLayout), ); } @@ -246,22 +246,31 @@ const processNodeData = async (node: any, settings: PluginSettings) => { // Always copy size and position if ("width" in figmaNode) { - node.width = (figmaNode as any).width; - node.height = (figmaNode as any).height; - node.x = (figmaNode as any).x; - node.y = (figmaNode as any).y; + node.width = figmaNode.width; + node.height = figmaNode.height; + node.x = figmaNode.x; + node.y = figmaNode.y; } } else { // Hopefully one day this won't be needed anymore. const figmaNode = await figma.getNodeByIdAsync(node.id); - if (figmaNode && "width" in figmaNode) { - node.width = (figmaNode as any).width; - node.height = (figmaNode as any).height; - node.x = (figmaNode as any).x; - node.y = (figmaNode as any).y; + if (figmaNode) { + if ("width" in figmaNode) { + node.width = figmaNode.width; + node.height = figmaNode.height; + node.x = figmaNode.x; + node.y = figmaNode.y; + } } } + if ("individualStrokeWeights" in node) { + node.strokeTopWeight = node.individualStrokeWeights.top; + node.strokeBottomWeight = node.individualStrokeWeights.bottom; + node.strokeLeftWeight = node.individualStrokeWeights.left; + node.strokeRightWeight = node.individualStrokeWeights.right; + } + await getColorVariables(node, settings); // Set default layout properties if missing diff --git a/packages/backend/src/html/builderImpl/htmlAutoLayout.ts b/packages/backend/src/html/builderImpl/htmlAutoLayout.ts index 8ffe58cf..8bbcad5f 100644 --- a/packages/backend/src/html/builderImpl/htmlAutoLayout.ts +++ b/packages/backend/src/html/builderImpl/htmlAutoLayout.ts @@ -1,4 +1,4 @@ -import { HTMLSettings, PluginSettings } from "types"; +import { HTMLSettings } from "types"; import { formatMultipleJSXArray } from "../../common/parseJSX"; const getFlexDirection = (node: InferredAutoLayoutResult): string => @@ -60,5 +60,5 @@ export const htmlAutoLayoutProps = ( gap: getGap(autoLayout), display: getFlex(node, autoLayout), }, - settings.jsx, + settings.htmlGenerationMode === "jsx", ); diff --git a/packages/backend/src/html/builderImpl/htmlColor.ts b/packages/backend/src/html/builderImpl/htmlColor.ts index 6eec999a..2d2e7fb5 100644 --- a/packages/backend/src/html/builderImpl/htmlColor.ts +++ b/packages/backend/src/html/builderImpl/htmlColor.ts @@ -14,7 +14,6 @@ const processColorWithVariable = (fill: { if (fill.variableColorName) { const varName = fill.variableColorName; - console.log("varName is", varName); const fallbackColor = htmlColor(fill.color, opacity); return `var(--${varName}, ${fallbackColor})`; } diff --git a/packages/backend/src/html/htmlDefaultBuilder.ts b/packages/backend/src/html/htmlDefaultBuilder.ts index e0b6dc0f..4b443491 100644 --- a/packages/backend/src/html/htmlDefaultBuilder.ts +++ b/packages/backend/src/html/htmlDefaultBuilder.ts @@ -298,7 +298,7 @@ export class HtmlDefaultBuilder { paint.type === "GRADIENT_ANGULAR" || paint.type === "GRADIENT_DIAMOND" ) { - return htmlGradientFromFills([paint], this.settings); + return htmlGradientFromFills(paint); } return ""; // Handle other paint types safely @@ -347,10 +347,15 @@ export class HtmlDefaultBuilder { autoLayoutPadding(): this { const { node, isJSX, optimizeLayout } = this; - if ("paddingLeft" in node) { + if ( + "paddingLeft" in node || + "paddingRight" in node || + "paddingTop" in node || + "paddingBottom" in node + ) { this.addStyles( ...htmlPadding( - (optimizeLayout ? node.inferredAutoLayout : null) ?? node, + (optimizeLayout ? (node as any).inferredAutoLayout : null) ?? node, isJSX, ), ); From 50cedca10dbd49ae6af58f03132de72b361ff3b4 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Thu, 6 Mar 2025 22:18:03 -0300 Subject: [PATCH 078/168] Allow changing background --- packages/plugin-ui/src/components/Preview.tsx | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/plugin-ui/src/components/Preview.tsx b/packages/plugin-ui/src/components/Preview.tsx index 46b4b9f6..9cc7461b 100644 --- a/packages/plugin-ui/src/components/Preview.tsx +++ b/packages/plugin-ui/src/components/Preview.tsx @@ -5,6 +5,7 @@ import { Minimize2, MonitorSmartphone, Smartphone, + Circle, } from "lucide-react"; const Preview: React.FC<{ @@ -13,6 +14,7 @@ const Preview: React.FC<{ const [expanded, setExpanded] = useState(false); const [viewMode, setViewMode] = useState<"desktop" | "mobile">("desktop"); const [animationClass, setAnimationClass] = useState(""); + const [bgColor, setBgColor] = useState<"white" | "black">("white"); // Define consistent dimensions regardless of mode const containerWidth = expanded ? 320 : 240; @@ -63,6 +65,16 @@ const Preview: React.FC<{ Preview
+ {/* Background Color Toggle */} + + {/* View Mode Toggle */}
+
+ )} + )}
From 3cc5b2d4eaf30bdf168788855363055d729c4fcc Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Fri, 7 Mar 2025 14:10:48 -0300 Subject: [PATCH 081/168] Even faster memoization --- packages/backend/src/code.ts | 6 +++--- packages/plugin-ui/src/components/CodePanel.tsx | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index 6525b164..519fc442 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -38,14 +38,14 @@ const addParentReferences = (node: any) => { } }; -const variableCache = new Map>(); +const variableCache = new Map(); const memoizedVariableToColorName = async ( variableId: string, ): Promise => { if (!variableCache.has(variableId)) { - const promise = variableToColorName(variableId); - variableCache.set(variableId, promise); + const colorName = await variableToColorName(variableId); + variableCache.set(variableId, colorName); } return variableCache.get(variableId)!; }; diff --git a/packages/plugin-ui/src/components/CodePanel.tsx b/packages/plugin-ui/src/components/CodePanel.tsx index be41219d..35dd5150 100644 --- a/packages/plugin-ui/src/components/CodePanel.tsx +++ b/packages/plugin-ui/src/components/CodePanel.tsx @@ -4,10 +4,9 @@ import { PluginSettings, SelectPreferenceOptions, } from "types"; -import { useMemo, useState, useEffect } from "react"; +import { useMemo, useState } from "react"; import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; import { coldarkDark as theme } from "react-syntax-highlighter/dist/esm/styles/prism"; -import SelectableToggle from "./SelectableToggle"; import { CopyButton } from "./CopyButton"; import EmptyState from "./EmptyState"; import SettingsGroup from "./SettingsGroup"; From 782dcaaf4c1ce6a8374e60eb86b723151d233705 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Fri, 7 Mar 2025 20:36:47 -0300 Subject: [PATCH 082/168] Add min/max width/height. --- packages/backend/src/code.ts | 6 ++ .../backend/src/common/nodeWidthHeight.ts | 27 +++--- .../src/flutter/builderImpl/flutterSize.ts | 23 ++++- .../backend/src/flutter/flutterContainer.ts | 15 ++- packages/backend/src/flutter/flutterMain.ts | 3 +- .../backend/src/flutter/flutterTextBuilder.ts | 32 ++++++- .../backend/src/html/builderImpl/htmlSize.ts | 29 +++++- .../backend/src/html/htmlDefaultBuilder.ts | 7 +- .../src/swiftui/builderImpl/swiftuiSize.ts | 54 +++++------ .../src/swiftui/swiftuiDefaultBuilder.ts | 12 ++- packages/backend/src/swiftui/swiftuiMain.ts | 1 + .../backend/src/swiftui/swiftuiTextBuilder.ts | 4 +- .../src/tailwind/builderImpl/tailwindSize.ts | 91 ++++++++++++++----- .../backend/src/tailwind/conversionTables.ts | 24 ++++- .../src/tailwind/tailwindDefaultBuilder.ts | 9 +- 15 files changed, 249 insertions(+), 88 deletions(-) diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index 519fc442..10a18203 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -333,6 +333,12 @@ const processNodePair = async ( if (!jsonNode.layoutSizingHorizontal) jsonNode.layoutSizingHorizontal = "FIXED"; if (!jsonNode.layoutSizingVertical) jsonNode.layoutSizingVertical = "FIXED"; + if (!jsonNode.primaryAxisAlignItems) { + jsonNode.primaryAxisAlignItems = "MIN"; + } + if (!jsonNode.counterAxisAlignItems) { + jsonNode.counterAxisAlignItems = "MIN"; + } // If layout sizing is HUG but there are no children, set it to FIXED const hasChildren = diff --git a/packages/backend/src/common/nodeWidthHeight.ts b/packages/backend/src/common/nodeWidthHeight.ts index bd471ed4..dff8fb77 100644 --- a/packages/backend/src/common/nodeWidthHeight.ts +++ b/packages/backend/src/common/nodeWidthHeight.ts @@ -1,27 +1,11 @@ import { Size } from "types"; export const nodeSize = (node: SceneNode, optimizeLayout: boolean): Size => { - const hasLayout = - "layoutAlign" in node && node.parent && "layoutMode" in node.parent; - - if (!hasLayout) { - return { width: node.width, height: node.height }; - } - const nodeAuto = (optimizeLayout && "inferredAutoLayout" in node ? node.inferredAutoLayout : null) ?? node; - if ("layoutMode" in nodeAuto && nodeAuto.layoutMode === "NONE") { - return { width: node.width, height: node.height }; - } - - // const parentLayoutMode = node.parent.layoutMode; - const parentLayoutMode = optimizeLayout - ? node.parent.inferredAutoLayout?.layoutMode - : node.parent.layoutMode; - // Check for explicit layout sizing properties if ( "layoutSizingHorizontal" in nodeAuto && @@ -44,6 +28,17 @@ export const nodeSize = (node: SceneNode, optimizeLayout: boolean): Size => { return { width, height }; } + if ("layoutMode" in nodeAuto && nodeAuto.layoutMode === "NONE") { + return { width: node.width, height: node.height }; + } + + const hasLayout = + "layoutAlign" in node && node.parent && "layoutMode" in node.parent; + + if (!hasLayout) { + return { width: node.width, height: node.height }; + } + return { width: node.width, height: node.height }; // const isWidthFill = diff --git a/packages/backend/src/flutter/builderImpl/flutterSize.ts b/packages/backend/src/flutter/builderImpl/flutterSize.ts index 0cf193e7..5877d2bb 100644 --- a/packages/backend/src/flutter/builderImpl/flutterSize.ts +++ b/packages/backend/src/flutter/builderImpl/flutterSize.ts @@ -11,7 +11,7 @@ export const flutterSizeWH = (node: SceneNode): string => { export const flutterSize = ( node: SceneNode, optimizeLayout: boolean, -): { width: string; height: string; isExpanded: boolean } => { +): { width: string; height: string; isExpanded: boolean; constraints: Record } => { const size = nodeSize(node, optimizeLayout); let isExpanded: boolean = false; @@ -53,5 +53,24 @@ export const flutterSize = ( } } - return { width: propWidth, height: propHeight, isExpanded }; + // Handle min/max constraints + const constraints: Record = {}; + + if (node.minWidth !== undefined && node.minWidth !== null) { + constraints.minWidth = numberToFixedString(node.minWidth); + } + + if (node.maxWidth !== undefined && node.maxWidth !== null) { + constraints.maxWidth = numberToFixedString(node.maxWidth); + } + + if (node.minHeight !== undefined && node.minHeight !== null) { + constraints.minHeight = numberToFixedString(node.minHeight); + } + + if (node.maxHeight !== undefined && node.maxHeight !== null) { + constraints.maxHeight = numberToFixedString(node.maxHeight); + } + + return { width: propWidth, height: propHeight, isExpanded, constraints }; }; diff --git a/packages/backend/src/flutter/flutterContainer.ts b/packages/backend/src/flutter/flutterContainer.ts index a82e3419..aac9f96e 100644 --- a/packages/backend/src/flutter/flutterContainer.ts +++ b/packages/backend/src/flutter/flutterContainer.ts @@ -28,7 +28,10 @@ export const flutterContainer = ( // ignore for Groups const propBoxDecoration = getDecoration(node); - const { width, height, isExpanded } = flutterSize(node, optimizeLayout); + const { width, height, isExpanded, constraints } = flutterSize( + node, + optimizeLayout, + ); const clipBehavior = "clipsContent" in node && node.clipsContent === true @@ -46,6 +49,8 @@ export const flutterContainer = ( } let result: string; + const hasConstraints = constraints && Object.keys(constraints).length > 0; + if (width || height || propBoxDecoration || clipBehavior) { const parsedDecoration = skipDefaultProperty( propBoxDecoration, @@ -69,6 +74,14 @@ export const flutterContainer = ( result = child; } + // Apply constraints if any exist + if (hasConstraints) { + result = generateWidgetCode("ConstrainedBox", { + constraints: generateWidgetCode("BoxConstraints", constraints), + child: result, + }); + } + // Add Expanded() when parent is a Row/Column and width is full. if (isExpanded) { result = generateWidgetCode("Expanded", { diff --git a/packages/backend/src/flutter/flutterMain.ts b/packages/backend/src/flutter/flutterMain.ts index 9433b958..8dffb577 100644 --- a/packages/backend/src/flutter/flutterMain.ts +++ b/packages/backend/src/flutter/flutterMain.ts @@ -239,7 +239,6 @@ const makeRowColumn = ( // mainAxisSize: getFlex(node, autoLayout), mainAxisAlignment: getMainAxisAlignment(autoLayout), crossAxisAlignment: getCrossAxisAlignment(autoLayout), - children: [children], }; // Add spacing parameter if itemSpacing is set @@ -249,6 +248,8 @@ const makeRowColumn = ( addWarning("Flutter doesn't support negative itemSpacing"); } + widgetProps.children = [children]; + return generateWidgetCode(rowOrColumn, widgetProps); }; diff --git a/packages/backend/src/flutter/flutterTextBuilder.ts b/packages/backend/src/flutter/flutterTextBuilder.ts index b70f8d4e..40c334e3 100644 --- a/packages/backend/src/flutter/flutterTextBuilder.ts +++ b/packages/backend/src/flutter/flutterTextBuilder.ts @@ -168,7 +168,34 @@ export class FlutterTextBuilder extends FlutterDefaultBuilder { textAutoSize(node: TextNode): this { let result = this.child; - + + // Get constraints for the node + const constraints: Record = {}; + + if (node.minWidth !== undefined && node.minWidth !== null) { + constraints.minWidth = numberToFixedString(node.minWidth); + } + + if (node.maxWidth !== undefined && node.maxWidth !== null) { + constraints.maxWidth = numberToFixedString(node.maxWidth); + } + + if (node.minHeight !== undefined && node.minHeight !== null) { + constraints.minHeight = numberToFixedString(node.minHeight); + } + + if (node.maxHeight !== undefined && node.maxHeight !== null) { + constraints.maxHeight = numberToFixedString(node.maxHeight); + } + + const hasConstraints = Object.keys(constraints).length > 0; + if (hasConstraints) { + result = generateWidgetCode("ConstrainedBox", { + constraints: generateWidgetCode("BoxConstraints", constraints), + child: result, + }); + } + switch (node.textAutoResize) { case "WIDTH_AND_HEIGHT": break; @@ -187,9 +214,8 @@ export class FlutterTextBuilder extends FlutterDefaultBuilder { }); break; } - + result = wrapTextWithLayerBlur(node, result); - this.child = result; return this; } diff --git a/packages/backend/src/html/builderImpl/htmlSize.ts b/packages/backend/src/html/builderImpl/htmlSize.ts index 97e9151d..d6a3a7c1 100644 --- a/packages/backend/src/html/builderImpl/htmlSize.ts +++ b/packages/backend/src/html/builderImpl/htmlSize.ts @@ -6,11 +6,12 @@ export const htmlSizePartial = ( node: SceneNode, isJsx: boolean, optimizeLayout: boolean, -): { width: string; height: string } => { +): { width: string; height: string; constraints: string[] } => { if (isPreviewGlobal && node.parent === undefined) { return { width: formatWithJSX("width", isJsx, "100%"), height: formatWithJSX("height", isJsx, "100%"), + constraints: [], }; } @@ -50,5 +51,29 @@ export const htmlSizePartial = ( } } - return { width: w, height: h }; + // Handle min/max width/height constraints + const constraints = []; + + if (node.maxWidth !== undefined && node.maxWidth !== null) { + constraints.push(formatWithJSX("max-width", isJsx, node.maxWidth)); + } + + if (node.minWidth !== undefined && node.minWidth !== null) { + constraints.push(formatWithJSX("min-width", isJsx, node.minWidth)); + } + + if (node.maxHeight !== undefined && node.maxHeight !== null) { + constraints.push(formatWithJSX("max-height", isJsx, node.maxHeight)); + } + + if (node.minHeight !== undefined && node.minHeight !== null) { + constraints.push(formatWithJSX("min-height", isJsx, node.minHeight)); + } + + // Return constraints separately instead of appending to width/height + return { + width: w, + height: h, + constraints: constraints, + }; }; diff --git a/packages/backend/src/html/htmlDefaultBuilder.ts b/packages/backend/src/html/htmlDefaultBuilder.ts index 364dcefd..7febce2e 100644 --- a/packages/backend/src/html/htmlDefaultBuilder.ts +++ b/packages/backend/src/html/htmlDefaultBuilder.ts @@ -320,7 +320,7 @@ export class HtmlDefaultBuilder { size(): this { const { node, settings } = this; - const { width, height } = htmlSizePartial( + const { width, height, constraints } = htmlSizePartial( node, settings.htmlGenerationMode === "jsx", settings.optimizeLayout, @@ -342,6 +342,11 @@ export class HtmlDefaultBuilder { this.addStyles(width, height); } + // Add constraints as separate styles + if (constraints.length > 0) { + this.addStyles(...constraints); + } + return this; } diff --git a/packages/backend/src/swiftui/builderImpl/swiftuiSize.ts b/packages/backend/src/swiftui/builderImpl/swiftuiSize.ts index c6f1d646..4b7fc13b 100644 --- a/packages/backend/src/swiftui/builderImpl/swiftuiSize.ts +++ b/packages/backend/src/swiftui/builderImpl/swiftuiSize.ts @@ -3,39 +3,39 @@ import { numberToFixedString } from "../../common/numToAutoFixed"; export const swiftuiSize = ( node: SceneNode, - optimizeLayout: boolean = false, -): { width: string; height: string } => { - const size = nodeSize(node, optimizeLayout); + optimize: boolean = false, +): { width: string; height: string; constraints: string[] } => { + const size = nodeSize(node, optimize); - // if width is set as maxWidth, height must also be set as maxHeight (not height) - const shouldExtend = size.height === "fill" || size.width === "fill"; + const constraintProps: string[] = []; + let width = ""; + let height = ""; - // this cast will always be true, since nodeWidthHeight was called with false to relative. - let propWidth = ""; + // Handle width and height if (typeof size.width === "number") { - const w = numberToFixedString(size.width); - - if (shouldExtend) { - propWidth = `minWidth: ${w}, maxWidth: ${w}`; - } else { - propWidth = `width: ${w}`; - } - } else if (size.width === "fill") { - propWidth = `maxWidth: .infinity`; + width = `width: ${numberToFixedString(size.width)}`; } - - let propHeight = ""; if (typeof size.height === "number") { - const h = numberToFixedString(size.height); + height = `height: ${numberToFixedString(size.height)}`; + } - if (shouldExtend) { - propHeight = `minHeight: ${h}, maxHeight: ${h}`; - } else { - propHeight = `height: ${h}`; - } - } else if (size.height === "fill") { - propHeight = `maxHeight: .infinity`; + // Handle min/max constraints + if (node.minWidth !== undefined && node.minWidth !== null) { + constraintProps.push(`minWidth: ${numberToFixedString(node.minWidth)}`); + } + if (node.maxWidth !== undefined && node.maxWidth !== null) { + constraintProps.push(`maxWidth: ${numberToFixedString(node.maxWidth)}`); + } + if (node.minHeight !== undefined && node.minHeight !== null) { + constraintProps.push(`minHeight: ${numberToFixedString(node.minHeight)}`); + } + if (node.maxHeight !== undefined && node.maxHeight !== null) { + constraintProps.push(`maxHeight: ${numberToFixedString(node.maxHeight)}`); } - return { width: propWidth, height: propHeight }; + return { + width, + height, + constraints: constraintProps, + }; }; diff --git a/packages/backend/src/swiftui/swiftuiDefaultBuilder.ts b/packages/backend/src/swiftui/swiftuiDefaultBuilder.ts index 3648877f..a990adc4 100644 --- a/packages/backend/src/swiftui/swiftuiDefaultBuilder.ts +++ b/packages/backend/src/swiftui/swiftuiDefaultBuilder.ts @@ -139,10 +139,14 @@ export class SwiftuiDefaultBuilder { } size(node: SceneNode, optimize: boolean): this { - const { width, height } = swiftuiSize(node, optimize); - const sizes = [width, height].filter((d) => d); - if (sizes.length > 0) { - this.pushModifier([`frame`, sizes.join(", ")]); + const { width, height, constraints } = swiftuiSize(node, optimize); + if (width || height) { + this.pushModifier([`frame`, [width, height].filter(Boolean).join(", ")]); + } + + // Add constraints if any exist + if (constraints.length > 0) { + this.pushModifier([`frame`, constraints.join(", ")]); } return this; diff --git a/packages/backend/src/swiftui/swiftuiMain.ts b/packages/backend/src/swiftui/swiftuiMain.ts index ebf331cf..94a6610f 100644 --- a/packages/backend/src/swiftui/swiftuiMain.ts +++ b/packages/backend/src/swiftui/swiftuiMain.ts @@ -197,6 +197,7 @@ const createDirectionalStack = ( const getLayoutAlignment = ( inferredAutoLayout: InferredAutoLayoutResult, ): string => { + console.log("inferred is", inferredAutoLayout); switch (inferredAutoLayout.counterAxisAlignItems) { case "MIN": return inferredAutoLayout.layoutMode === "VERTICAL" ? ".leading" : ".top"; diff --git a/packages/backend/src/swiftui/swiftuiTextBuilder.ts b/packages/backend/src/swiftui/swiftuiTextBuilder.ts index 56307bd3..549e50d8 100644 --- a/packages/backend/src/swiftui/swiftuiTextBuilder.ts +++ b/packages/backend/src/swiftui/swiftuiTextBuilder.ts @@ -179,7 +179,7 @@ export class SwiftuiTextBuilder extends SwiftuiDefaultBuilder { }; wrapTextAutoResize = (node: TextNode): string => { - const { width, height } = swiftuiSize(node); + const { width, height, constraints } = swiftuiSize(node); let comp: string[] = []; switch (node.textAutoResize) { @@ -194,6 +194,8 @@ export class SwiftuiTextBuilder extends SwiftuiDefaultBuilder { break; } + comp.push(...constraints); + if (comp.length > 0) { const align = this.textAlignment(node); return `.frame(${comp.join(", ")}${align})`; diff --git a/packages/backend/src/tailwind/builderImpl/tailwindSize.ts b/packages/backend/src/tailwind/builderImpl/tailwindSize.ts index af7a183a..c3fef8c1 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindSize.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindSize.ts @@ -1,62 +1,103 @@ import { pxToLayoutSize } from "../conversionTables"; import { nodeSize } from "../../common/nodeWidthHeight"; -import { formatWithJSX } from "../../common/parseJSX"; +import { numberToFixedString } from "../../common/numToAutoFixed"; +import { TailwindSettings } from "types"; + +/** + * Formats a size value into a Tailwind class + * Uses Tailwind's standard classes if there's a good match, otherwise uses arbitrary values + */ +const formatTailwindSizeValue = ( + size: number, + prefix: string, + settings?: TailwindSettings, +): string => { + // Try to find a matching Tailwind size class + if (settings?.roundTailwindValues) { + const tailwindSize = pxToLayoutSize(size); + + // If we found a matching Tailwind class, use it + if (!tailwindSize.startsWith("[")) { + return `${prefix}-${tailwindSize}`; + } + } + + // No matching class or rounding disabled, use arbitrary value + const sizeFixed = numberToFixedString(size); + if (sizeFixed === "0") { + return `${prefix}-0`; + } else { + return `${prefix}-[${sizeFixed}px]`; + } +}; export const tailwindSizePartial = ( node: SceneNode, optimizeLayout: boolean, -): { width: string; height: string } => { + settings?: TailwindSettings, +): { width: string; height: string; constraints: string } => { const size = nodeSize(node, optimizeLayout); - const nodeParent = (node.parent && optimizeLayout && "inferredAutoLayout" in node.parent ? node.parent.inferredAutoLayout : null) ?? node.parent; let w = ""; - if ( - typeof size.width === "number" && - "layoutSizingHorizontal" in node && - node.layoutSizingHorizontal === "FIXED" - ) { - w = `w-${pxToLayoutSize(size.width)}`; + if (typeof size.width === "number") { + w = formatTailwindSizeValue(size.width, "w", settings); } else if (size.width === "fill") { if ( nodeParent && "layoutMode" in nodeParent && nodeParent.layoutMode === "HORIZONTAL" ) { - w = `grow shrink basis-0`; + w = "flex-1"; } else { - w = `self-stretch`; + w = "self-stretch"; } } let h = ""; if (typeof size.height === "number") { - h = `h-${pxToLayoutSize(size.height)}`; + h = formatTailwindSizeValue(size.height, "h", settings); } else if (size.height === "fill") { if ( - size.height === "fill" && nodeParent && "layoutMode" in nodeParent && nodeParent.layoutMode === "VERTICAL" ) { - h = `grow shrink basis-0`; + h = "flex-1"; } else { - h = `self-stretch`; + h = "self-stretch"; } } - return { width: w, height: h }; -}; + // Handle min/max constraints in tailwind + const constraints = []; -export const htmlSizePartialForTailwind = ( - node: SceneNode, - isJSX: boolean, -): [string, string] => { - return [ - formatWithJSX("width", isJSX, node.width), - formatWithJSX("height", isJSX, node.height), - ]; + if (node.maxWidth !== undefined && node.maxWidth !== null) { + constraints.push(formatTailwindSizeValue(node.maxWidth, "max-w", settings)); + } + + if (node.minWidth !== undefined && node.minWidth !== null) { + constraints.push(formatTailwindSizeValue(node.minWidth, "min-w", settings)); + } + + if (node.maxHeight !== undefined && node.maxHeight !== null) { + constraints.push( + formatTailwindSizeValue(node.maxHeight, "max-h", settings), + ); + } + + if (node.minHeight !== undefined && node.minHeight !== null) { + constraints.push( + formatTailwindSizeValue(node.minHeight, "min-h", settings), + ); + } + + return { + width: w, + height: h, + constraints: constraints.join(" "), + }; }; diff --git a/packages/backend/src/tailwind/conversionTables.ts b/packages/backend/src/tailwind/conversionTables.ts index cd1a39c7..654db9fe 100644 --- a/packages/backend/src/tailwind/conversionTables.ts +++ b/packages/backend/src/tailwind/conversionTables.ts @@ -111,11 +111,29 @@ export const pxToBlur = (value: number): string | null => { }; export const pxToLayoutSize = (value: number): string => { - const tailwindValue = pxToTailwind(value, config.layoutSize); - if (tailwindValue) { - return tailwindValue; + // First check if it's a direct match to avoid rounding errors + const exactValue = Object.keys(config.layoutSize) + .map(Number) + .find((size) => Math.abs(value - size) < 0.05); + + if (exactValue !== undefined) { + return (config.layoutSize as any)[exactValue]; + } + + // If not an exact match but rounding is enabled, check for a close match + if (localTailwindSettings.roundTailwindValues) { + const thresholdValue = nearestValueWithThreshold( + value, + Object.keys(config.layoutSize).map(Number), + 15, // 15% threshold for layout sizes + ); + + if (thresholdValue !== null) { + return (config.layoutSize as any)[thresholdValue]; + } } + // No match found, return arbitrary value return `[${numberToFixedString(value)}px]`; }; diff --git a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts index d34b4bae..7ccdf3f7 100644 --- a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts +++ b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts @@ -187,8 +187,8 @@ export class TailwindDefaultBuilder { // must be called before Position, because of the hasFixedSize attribute. size(): this { - const { node, optimizeLayout } = this; - const { width, height } = tailwindSizePartial(node, optimizeLayout); + const { node, optimizeLayout, settings } = this; + const { width, height, constraints } = tailwindSizePartial(node, optimizeLayout, settings); if (node.type === "TEXT") { switch (node.textAutoResize) { @@ -206,6 +206,11 @@ export class TailwindDefaultBuilder { this.addAttributes(width, height); } + // Add any min/max constraints + if (constraints) { + this.addAttributes(constraints); + } + return this; } From 8fc5523eafbfaf9a5f7f75bb9f789d97a1094660 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Fri, 7 Mar 2025 21:38:10 -0300 Subject: [PATCH 083/168] Add custom base font --- apps/plugin/plugin-src/code.ts | 3 +- apps/plugin/ui-src/App.tsx | 2 +- .../src/tailwind/builderImpl/tailwindSize.ts | 11 +- .../backend/src/tailwind/conversionTables.ts | 42 +- packages/plugin-ui/src/PluginUI.tsx | 6 +- .../plugin-ui/src/components/CodePanel.tsx | 9 +- .../src/components/CustomPrefixInput.tsx | 413 +++++++++++------- .../src/components/SettingsGroup.tsx | 4 +- .../src/components/TailwindSettings.tsx | 245 +++++++++++ packages/types/src/types.ts | 1 + 10 files changed, 532 insertions(+), 204 deletions(-) create mode 100644 packages/plugin-ui/src/components/TailwindSettings.tsx diff --git a/apps/plugin/plugin-src/code.ts b/apps/plugin/plugin-src/code.ts index 08831db8..2590c4b7 100644 --- a/apps/plugin/plugin-src/code.ts +++ b/apps/plugin/plugin-src/code.ts @@ -33,6 +33,7 @@ export const defaultPluginSettings: PluginSettings = { embedVectors: false, htmlGenerationMode: "html", tailwindGenerationMode: "jsx", + baseFontSize: 16, }; // A helper type guard to ensure the key belongs to the PluginSettings type @@ -175,7 +176,7 @@ const codegenMode = async () => { node, ); - const nodeJson = await nodesToJSON([node]); + const nodeJson = await nodesToJSON([node], userPluginSettings); const convertedSelection = await convertIntoNodes(nodeJson, null); console.log( "[DEBUG] codegen.generate - Converted selection:", diff --git a/apps/plugin/ui-src/App.tsx b/apps/plugin/ui-src/App.tsx index d7cca718..1f5e80a2 100644 --- a/apps/plugin/ui-src/App.tsx +++ b/apps/plugin/ui-src/App.tsx @@ -124,7 +124,7 @@ export default function App() { }; const handlePreferencesChange = ( key: keyof PluginSettings, - value: boolean | string, + value: boolean | string | number, ) => { if (state.settings && state.settings[key] === value) { // do nothing diff --git a/packages/backend/src/tailwind/builderImpl/tailwindSize.ts b/packages/backend/src/tailwind/builderImpl/tailwindSize.ts index c3fef8c1..60208415 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindSize.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindSize.ts @@ -12,14 +12,11 @@ const formatTailwindSizeValue = ( prefix: string, settings?: TailwindSettings, ): string => { - // Try to find a matching Tailwind size class - if (settings?.roundTailwindValues) { - const tailwindSize = pxToLayoutSize(size); + const tailwindSize = pxToLayoutSize(size); - // If we found a matching Tailwind class, use it - if (!tailwindSize.startsWith("[")) { - return `${prefix}-${tailwindSize}`; - } + // If we found a matching Tailwind class, use it + if (!tailwindSize.startsWith("[")) { + return `${prefix}-${tailwindSize}`; } // No matching class or rounding disabled, use arbitrary value diff --git a/packages/backend/src/tailwind/conversionTables.ts b/packages/backend/src/tailwind/conversionTables.ts index 654db9fe..e8f591f2 100644 --- a/packages/backend/src/tailwind/conversionTables.ts +++ b/packages/backend/src/tailwind/conversionTables.ts @@ -44,7 +44,7 @@ export const exactValue = ( /** * convert pixel values to Tailwind attributes. * by default, Tailwind uses rem, while Figma uses px. - * Therefore, a conversion is necessary. Rem = Pixel / 16.abs + * Therefore, a conversion is necessary. Rem = Pixel / baseFontSize * Then, find in the corresponding table the closest value. */ const pxToRemToTailwind = ( @@ -52,7 +52,9 @@ const pxToRemToTailwind = ( conversionMap: Record, ): string => { const keys = Object.keys(conversionMap).map((d) => +d); - const remValue = value / 16; + // Use the configured base font size or fall back to default 16px + const baseFontSize = localTailwindSettings.baseFontSize || 16; + const remValue = value / baseFontSize; const convertedValue = exactValue(remValue, keys); if (convertedValue) { @@ -76,6 +78,8 @@ const pxToTailwind = ( const keys = Object.keys(conversionMap).map((d) => +d); const convertedValue = exactValue(value, keys); + console.log("convertedValue", convertedValue); + if (convertedValue) { return conversionMap[convertedValue]; } else if (localTailwindSettings.roundTailwindValues) { @@ -111,30 +115,16 @@ export const pxToBlur = (value: number): string | null => { }; export const pxToLayoutSize = (value: number): string => { - // First check if it's a direct match to avoid rounding errors - const exactValue = Object.keys(config.layoutSize) - .map(Number) - .find((size) => Math.abs(value - size) < 0.05); - - if (exactValue !== undefined) { - return (config.layoutSize as any)[exactValue]; - } - - // If not an exact match but rounding is enabled, check for a close match - if (localTailwindSettings.roundTailwindValues) { - const thresholdValue = nearestValueWithThreshold( - value, - Object.keys(config.layoutSize).map(Number), - 15, // 15% threshold for layout sizes - ); - - if (thresholdValue !== null) { - return (config.layoutSize as any)[thresholdValue]; - } - } - - // No match found, return arbitrary value - return `[${numberToFixedString(value)}px]`; + // Scale the input value according to the base font size ratio + const baseFontSize = localTailwindSettings.baseFontSize || 16; + // If baseFontSize is different than 16, we need to adjust the pixel value + // For example, with baseFontSize=14, 7px should match with the key for 8px (w-2) + const scaledValue = (value * 16) / baseFontSize; + + // Use pxToTailwind directly with the scaled value, since the keys in config.layoutSize + // are likely in pixels based on a 16px base font size + const result = pxToTailwind(scaledValue, config.layoutSize); + return result !== null ? result : `[${numberToFixedString(value)}px]`; }; export const nearestOpacity = (nodeOpacity: number): number => { diff --git a/packages/plugin-ui/src/PluginUI.tsx b/packages/plugin-ui/src/PluginUI.tsx index fede84a3..9546508e 100644 --- a/packages/plugin-ui/src/PluginUI.tsx +++ b/packages/plugin-ui/src/PluginUI.tsx @@ -31,7 +31,7 @@ type PluginUIProps = { settings: PluginSettings | null; onPreferenceChanged: ( key: keyof PluginSettings, - value: boolean | string, + value: boolean | string | number, ) => void; colors: SolidColorConversion[]; gradients: LinearGradientConversion[]; @@ -97,9 +97,9 @@ export const PluginUI = (props: PluginUIProps) => { {isEmpty === false && props.htmlPreview && ( )} - + {warnings.length > 0 && } - + void; } @@ -202,9 +203,9 @@ const CodePanel = (props: CodePanelProps) => { onPreferenceChanged={onPreferenceChanged} > {selectedFramework === "Tailwind" && ( - )} diff --git a/packages/plugin-ui/src/components/CustomPrefixInput.tsx b/packages/plugin-ui/src/components/CustomPrefixInput.tsx index 2d6bd669..fe9caa9d 100644 --- a/packages/plugin-ui/src/components/CustomPrefixInput.tsx +++ b/packages/plugin-ui/src/components/CustomPrefixInput.tsx @@ -1,177 +1,270 @@ import React, { useState, useRef, useEffect } from "react"; import { HelpCircle, Check } from "lucide-react"; -interface CustomPrefixInputProps { - initialValue: string; - onValueChange: (value: string) => void; +interface FormFieldProps { + // Common props + label: string; + initialValue: string | number; + onValueChange: (value: string | number) => void; + placeholder?: string; + helpText?: string; + + // Validation props + type?: "text" | "number"; + min?: number; + max?: number; + suffix?: string; + + // For text input validation + disallowedPattern?: RegExp; + disallowedMessage?: string; + + // Optional preview (for text inputs) + showPreview?: boolean; + previewExamples?: string[]; + previewTransform?: (value: string, example: string) => React.ReactNode; } -const CustomPrefixInput = React.memo(({ initialValue, onValueChange }: CustomPrefixInputProps) => { - // Use internal state to manage the input value - const [inputValue, setInputValue] = useState(initialValue); - const [isFocused, setIsFocused] = useState(false); - const [hasChanges, setHasChanges] = useState(false); - const [showSuccess, setShowSuccess] = useState(false); - const inputRef = useRef(null); - - // Update internal state when initialValue changes (from parent) - useEffect(() => { - setInputValue(initialValue); - setHasChanges(false); - }, [initialValue]); - - const examples = ["flex"]; - const hasInvalidChars = /\s/.test(inputValue); - - const handleChange = (e: React.ChangeEvent) => { - const newValue = e.target.value; - setInputValue(newValue); - setHasChanges(newValue !== initialValue); - }; - - const applyChanges = () => { - if (hasInvalidChars) return; - - onValueChange(inputValue); - setHasChanges(false); - - // Show success indicator briefly - setShowSuccess(true); - setTimeout(() => setShowSuccess(false), 1500); - }; - - const handleBlur = () => { - setIsFocused(false); - }; - - const handleKeyDown = (e: React.KeyboardEvent) => { - if (e.key === 'Enter') { - e.preventDefault(); - applyChanges(); - inputRef.current?.blur(); - } - }; - - return ( -
-
- - -
- -
- Add a prefix to all generated Tailwind classes. -
- Useful for avoiding conflicts with existing CSS. -
-
+const FormField = React.memo( + ({ + label, + initialValue, + onValueChange, + placeholder, + helpText, + type = "text", + min, + max, + suffix, + disallowedPattern = /\s/, + disallowedMessage = "Input cannot contain spaces", + showPreview = false, + previewExamples = ["flex"], + previewTransform, + }: FormFieldProps) => { + // Use internal state to manage the input value + const [inputValue, setInputValue] = useState(String(initialValue)); + const [isFocused, setIsFocused] = useState(false); + const [hasChanges, setHasChanges] = useState(false); + const [showSuccess, setShowSuccess] = useState(false); + const [hasError, setHasError] = useState(false); + const [errorMessage, setErrorMessage] = useState(""); + const inputRef = useRef(null); + + // Update internal state when initialValue changes (from parent) + useEffect(() => { + setInputValue(String(initialValue)); + setHasChanges(false); + setHasError(false); + setErrorMessage(""); + }, [initialValue]); + + const validateInput = (value: string): boolean => { + // Text validation + if (type === "text") { + if (disallowedPattern && disallowedPattern.test(value)) { + setHasError(true); + setErrorMessage(disallowedMessage); + return false; + } + setHasError(false); + setErrorMessage(""); + return true; + } + + // Number validation + if (type === "number") { + // Check for non-numeric characters + if (/[^0-9]/.test(value)) { + setHasError(true); + setErrorMessage("Only numbers are allowed"); + return false; + } + + const numValue = parseInt(value, 10); + + if (isNaN(numValue)) { + setHasError(true); + setErrorMessage("Please enter a valid number"); + return false; + } + + if (min !== undefined && numValue < min) { + setHasError(true); + setErrorMessage(`Minimum value is ${min}`); + return false; + } + + if (max !== undefined && numValue > max) { + setHasError(true); + setErrorMessage(`Maximum value is ${max}`); + return false; + } + + setHasError(false); + setErrorMessage(""); + return true; + } + + return true; + }; + + const handleChange = (e: React.ChangeEvent) => { + const newValue = e.target.value; + setInputValue(newValue); + validateInput(newValue); + setHasChanges(newValue !== String(initialValue)); + }; + + const applyChanges = () => { + if (hasError) return; + + if (type === "number") { + const numValue = parseInt(inputValue, 10); + if (!isNaN(numValue)) { + onValueChange(numValue); + } + } else { + onValueChange(inputValue); + } + + setHasChanges(false); + + // Show success indicator briefly + setShowSuccess(true); + setTimeout(() => setShowSuccess(false), 1500); + }; + + const handleBlur = () => { + setIsFocused(false); + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === "Enter") { + e.preventDefault(); + applyChanges(); + inputRef.current?.blur(); + } + }; + + // Default preview transform for text prefixes + const defaultPreviewTransform = (value: string, example: string) => ( +
+
+ {value} + {example} +
+ +
+ {example}
- - {showSuccess && ( - - Applied - - )}
+ ); -
-
- setIsFocused(true)} - onBlur={handleBlur} - onKeyDown={handleKeyDown} - placeholder="e.g., tw-" - className={`p-1.5 px-2.5 border rounded-md text-sm w-full transition-all focus:outline-none ${ - hasInvalidChars - ? "border-red-300 dark:border-red-700 bg-red-50 dark:bg-red-900/20" - : isFocused - ? "border-green-400 dark:border-green-600 ring-1 ring-green-300 dark:ring-green-800 bg-white dark:bg-neutral-800" - : "border-gray-300 dark:border-gray-600 bg-white dark:bg-neutral-800 hover:border-gray-400 dark:hover:border-gray-500" - }`} - /> - - {hasInvalidChars && ( -

- Prefix cannot contain spaces -

+ const renderPreview = previewTransform || defaultPreviewTransform; + + return ( +
+
+ + + {helpText && ( +
+ +
+ {helpText} +
+
+
+ )} + + {showSuccess && ( + + Applied + )}
- - {hasChanges && ( - - )} -
- {inputValue && !hasInvalidChars && ( -
-

- Preview{hasChanges ? " (not applied yet)" : ""}: -

-
- {examples.map((example) => ( -
-
- - {inputValue} - - - {example} - -
- - → +
+
+
+ setIsFocused(true)} + onBlur={handleBlur} + onKeyDown={handleKeyDown} + placeholder={placeholder} + className={`p-1.5 px-2.5 text-sm w-full transition-all focus:outline-none ${ + suffix ? "rounded-l-md" : "rounded-md" + } ${ + hasError + ? "border border-red-300 dark:border-red-700 bg-red-50 dark:bg-red-900/20" + : isFocused + ? "border border-green-400 dark:border-green-600 ring-1 ring-green-300 dark:ring-green-800 bg-white dark:bg-neutral-800" + : "border border-gray-300 dark:border-gray-600 bg-white dark:bg-neutral-800 hover:border-gray-400 dark:hover:border-gray-500" + }`} + /> + + {suffix && ( + + {suffix} -
- {example} -
-
- ))} + )} +
+ + {hasError && ( +

{errorMessage}

+ )}
- + {hasChanges && ( -

- Press Enter or click Done to apply changes -

+ )}
- )} -
- ); -}); - -CustomPrefixInput.displayName = "CustomPrefixInput"; - -// Add a keyframe for fade-in-out animation -if (typeof document !== 'undefined') { - const style = document.createElement('style'); - style.innerHTML = ` - @keyframes fadeInOut { - 0% { opacity: 0; } - 20% { opacity: 1; } - 80% { opacity: 1; } - 100% { opacity: 0; } - } - .animate-fade-in-out { - animation: fadeInOut 1.5s ease-in-out; - } - `; - document.head.appendChild(style); -} -export default CustomPrefixInput; + {showPreview && inputValue && !hasError && ( +
+

+ Preview{hasChanges ? " (not applied yet)" : ""}: +

+
+ {previewExamples.map((example) => ( + + {renderPreview(inputValue, example)} + + ))} +
+ + {hasChanges && ( +

+ Press Enter or click Done to apply changes +

+ )} +
+ )} +
+ ); + }, +); + +FormField.displayName = "FormField"; + +export default FormField; diff --git a/packages/plugin-ui/src/components/SettingsGroup.tsx b/packages/plugin-ui/src/components/SettingsGroup.tsx index 8115d31d..2137c191 100644 --- a/packages/plugin-ui/src/components/SettingsGroup.tsx +++ b/packages/plugin-ui/src/components/SettingsGroup.tsx @@ -35,14 +35,14 @@ const SettingsGroup: React.FC = ({
{alwaysExpanded ? (
- + {title}
) : ( + )} +
+
+ ); +}; + +interface TailwindSettingsProps { + settings: PluginSettings | null; + onPreferenceChanged: ( + key: keyof PluginSettings, + value: boolean | string | number, + ) => void; +} + +export const TailwindSettings: React.FC = ({ + settings, + onPreferenceChanged, +}) => { + if (!settings) return null; + + const handleCustomPrefixChange = (newValue: string) => { + onPreferenceChanged("customTailwindPrefix", newValue); + }; + const handleBaseFontSizeChange = (value: number) => { + onPreferenceChanged("baseFontSize", value); + }; + + return ( +
+

+ Advanced Settings +

+ + {/* Advanced Settings Section */} +
+ {/* Class name prefix setting */} +
+ { + handleCustomPrefixChange(d as any); + }} + placeholder="e.g., tw-" + helpText="Add a prefix to all generated Tailwind classes. Useful for avoiding conflicts with existing CSS. Default is empty." + type="text" + showPreview={true} + /> +

+ Add a custom prefix to all Tailwind classes (e.g. "tw-") +

+
+ + {/* Base font size setting */} +
+ { + handleBaseFontSizeChange(d as any); + }} + placeholder="16" + suffix="px" + type="number" + min={1} + max={100} + /> +

+ Use this value to calculate rem values (default: 16px) +

+
+
+
+ ); +}; diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index a4fccee8..6cac3a3f 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -16,6 +16,7 @@ export interface TailwindSettings extends HTMLSettings { useColorVariables: boolean; customTailwindPrefix?: string; embedVectors: boolean; + baseFontSize: number; } export interface FlutterSettings { flutterGenerationMode: string; From 813563b6df0b24390f3728cb807fc2407deff278 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Fri, 7 Mar 2025 21:38:13 -0300 Subject: [PATCH 084/168] Update dependencies --- apps/debug/next-env.d.ts | 2 +- apps/debug/package.json | 2 +- apps/plugin/package.json | 18 +- package.json | 4 +- packages/backend/package.json | 4 +- packages/eslint-config-custom/package.json | 6 +- packages/plugin-ui/package.json | 4 +- packages/types/package.json | 2 +- pnpm-lock.yaml | 1683 +++++++++----------- 9 files changed, 746 insertions(+), 979 deletions(-) diff --git a/apps/debug/next-env.d.ts b/apps/debug/next-env.d.ts index a4a7b3f5..52e831b4 100644 --- a/apps/debug/next-env.d.ts +++ b/apps/debug/next-env.d.ts @@ -2,4 +2,4 @@ /// // NOTE: This file should not be edited -// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information. +// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. diff --git a/apps/debug/package.json b/apps/debug/package.json index 2a589dec..933ddf9a 100644 --- a/apps/debug/package.json +++ b/apps/debug/package.json @@ -11,7 +11,7 @@ }, "dependencies": { "backend": "workspace:*", - "next": "^14.2.24", + "next": "^15.2.1", "plugin-ui": "workspace:*", "react": "^19.0.0", "react-dom": "^19.0.0" diff --git a/apps/plugin/package.json b/apps/plugin/package.json index 9a3d3f6f..ecdd83c7 100644 --- a/apps/plugin/package.json +++ b/apps/plugin/package.json @@ -13,9 +13,9 @@ "@figma/plugin-typings": "^1.108.0", "backend": "workspace:*", "clsx": "^2.1.1", - "lucide-react": "^0.477.0", - "motion": "^12.4.9", - "nanoid": "^5.1.2", + "lucide-react": "^0.479.0", + "motion": "^12.4.10", + "nanoid": "^5.1.3", "plugin-ui": "workspace:*", "react": "^19.0.0", "react-dom": "^19.0.0", @@ -23,18 +23,18 @@ "tailwindcss-animate": "^1.0.7" }, "devDependencies": { - "@types/node": "^20.17.21", + "@types/node": "^22.13.9", "@types/react": "^19.0.10", "@types/react-dom": "^19.0.4", - "@typescript-eslint/eslint-plugin": "^7.18.0", - "@typescript-eslint/parser": "^7.18.0", + "@typescript-eslint/eslint-plugin": "^8.26.0", + "@typescript-eslint/parser": "^8.26.0", "@vitejs/plugin-react": "^4.3.4", "@vitejs/plugin-react-swc": "^3.8.0", "autoprefixer": "^10.4.20", - "concurrently": "^8.2.2", - "esbuild": "^0.23.1", + "concurrently": "^9.1.2", + "esbuild": "^0.25.0", "eslint-config-custom": "workspace:*", - "eslint-plugin-react-hooks": "^4.6.2", + "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.19", "postcss": "^8.5.3", "tailwindcss": "3.4.6", diff --git a/package.json b/package.json index 41e66f7a..8a8cc0c2 100644 --- a/package.json +++ b/package.json @@ -10,9 +10,9 @@ "format": "prettier --write \"**/*.{ts,tsx,css,md}\"" }, "devDependencies": { - "eslint": "^9.21.0", + "eslint": "^9.22.0", "eslint-config-custom": "workspace:*", - "prettier": "^3.5.2", + "prettier": "^3.5.3", "turbo": "^2.4.4", "typescript": "^5.8.2" } diff --git a/packages/backend/package.json b/packages/backend/package.json index 9669b663..fa412603 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -15,7 +15,7 @@ "dependencies": { "@figma/plugin-typings": "^1.108.0", "js-base64": "^3.7.7", - "nanoid": "^5.1.2", + "nanoid": "^5.1.3", "react": "19.0.0", "react-dom": "19.0.0", "types": "workspace:*" @@ -23,7 +23,7 @@ "devDependencies": { "@types/react": "^19.0.10", "@types/react-dom": "^19.0.4", - "eslint": "^9.21.0", + "eslint": "^9.22.0", "eslint-config-custom": "workspace:*", "tsconfig": "workspace:*", "tsup": "^8.4.0", diff --git a/packages/eslint-config-custom/package.json b/packages/eslint-config-custom/package.json index 513f6bd8..f905f154 100644 --- a/packages/eslint-config-custom/package.json +++ b/packages/eslint-config-custom/package.json @@ -4,10 +4,10 @@ "main": "index.js", "license": "MIT", "dependencies": { - "eslint-config-next": "^14.2.24", - "eslint-config-prettier": "^9.1.0", + "eslint-config-next": "^15.2.1", + "eslint-config-prettier": "^10.1.1", "eslint-config-turbo": "^2.4.4", - "eslint-plugin-react": "7.35.0" + "eslint-plugin-react": "7.37.4" }, "publishConfig": { "access": "public" diff --git a/packages/plugin-ui/package.json b/packages/plugin-ui/package.json index 7c61b749..041aee42 100644 --- a/packages/plugin-ui/package.json +++ b/packages/plugin-ui/package.json @@ -15,7 +15,7 @@ "@types/react-syntax-highlighter": "15.5.13", "clsx": "^2.1.1", "copy-to-clipboard": "^3.3.3", - "lucide-react": "^0.477.0", + "lucide-react": "^0.479.0", "react": "^19.0.0", "react-syntax-highlighter": "^15.6.1", "tailwind-merge": "^3.0.2", @@ -23,7 +23,7 @@ "tailwindcss-animate": "^1.0.7" }, "devDependencies": { - "eslint": "^9.21.0", + "eslint": "^9.22.0", "eslint-config-custom": "workspace:*", "tsconfig": "workspace:*", "types": "workspace:*", diff --git a/packages/types/package.json b/packages/types/package.json index 7bffd087..c8e011dd 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -19,7 +19,7 @@ "tsconfig": "workspace:*" }, "devDependencies": { - "eslint": "^9.21.0", + "eslint": "^9.22.0", "eslint-config-custom": "workspace:*", "typescript": "^5.8.2" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 756ec514..e36aa4dd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,14 +9,14 @@ importers: .: devDependencies: eslint: - specifier: ^9.21.0 - version: 9.21.0(jiti@1.21.7) + specifier: ^9.22.0 + version: 9.22.0(jiti@1.21.7) eslint-config-custom: specifier: workspace:* version: link:packages/eslint-config-custom prettier: - specifier: ^3.5.2 - version: 3.5.2 + specifier: ^3.5.3 + version: 3.5.3 turbo: specifier: ^2.4.4 version: 2.4.4 @@ -30,8 +30,8 @@ importers: specifier: workspace:* version: link:../../packages/backend next: - specifier: ^14.2.24 - version: 14.2.24(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + specifier: ^15.2.1 + version: 15.2.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) plugin-ui: specifier: workspace:* version: link:../../packages/plugin-ui @@ -85,14 +85,14 @@ importers: specifier: ^2.1.1 version: 2.1.1 lucide-react: - specifier: ^0.477.0 - version: 0.477.0(react@19.0.0) + specifier: ^0.479.0 + version: 0.479.0(react@19.0.0) motion: - specifier: ^12.4.9 - version: 12.4.9(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + specifier: ^12.4.10 + version: 12.4.10(react-dom@19.0.0(react@19.0.0))(react@19.0.0) nanoid: - specifier: ^5.1.2 - version: 5.1.2 + specifier: ^5.1.3 + version: 5.1.3 plugin-ui: specifier: workspace:* version: link:../../packages/plugin-ui @@ -110,8 +110,8 @@ importers: version: 1.0.7(tailwindcss@3.4.6) devDependencies: '@types/node': - specifier: ^20.17.21 - version: 20.17.21 + specifier: ^22.13.9 + version: 22.13.9 '@types/react': specifier: ^19.0.10 version: 19.0.10 @@ -119,35 +119,35 @@ importers: specifier: ^19.0.4 version: 19.0.4(@types/react@19.0.10) '@typescript-eslint/eslint-plugin': - specifier: ^7.18.0 - version: 7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2))(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2) + specifier: ^8.26.0 + version: 8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@1.21.7))(typescript@5.8.2))(eslint@9.22.0(jiti@1.21.7))(typescript@5.8.2) '@typescript-eslint/parser': - specifier: ^7.18.0 - version: 7.18.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2) + specifier: ^8.26.0 + version: 8.26.0(eslint@9.22.0(jiti@1.21.7))(typescript@5.8.2) '@vitejs/plugin-react': specifier: ^4.3.4 - version: 4.3.4(vite@5.4.14(@types/node@20.17.21)) + version: 4.3.4(vite@5.4.14(@types/node@22.13.9)) '@vitejs/plugin-react-swc': specifier: ^3.8.0 - version: 3.8.0(@swc/helpers@0.5.12)(vite@5.4.14(@types/node@20.17.21)) + version: 3.8.0(@swc/helpers@0.5.15)(vite@5.4.14(@types/node@22.13.9)) autoprefixer: specifier: ^10.4.20 version: 10.4.20(postcss@8.5.3) concurrently: - specifier: ^8.2.2 - version: 8.2.2 + specifier: ^9.1.2 + version: 9.1.2 esbuild: - specifier: ^0.23.1 - version: 0.23.1 + specifier: ^0.25.0 + version: 0.25.0 eslint-config-custom: specifier: workspace:* version: link:../../packages/eslint-config-custom eslint-plugin-react-hooks: - specifier: ^4.6.2 - version: 4.6.2(eslint@9.21.0(jiti@1.21.7)) + specifier: ^5.2.0 + version: 5.2.0(eslint@9.22.0(jiti@1.21.7)) eslint-plugin-react-refresh: specifier: ^0.4.19 - version: 0.4.19(eslint@9.21.0(jiti@1.21.7)) + version: 0.4.19(eslint@9.22.0(jiti@1.21.7)) postcss: specifier: ^8.5.3 version: 8.5.3 @@ -165,10 +165,10 @@ importers: version: 5.8.2 vite: specifier: ^5.4.14 - version: 5.4.14(@types/node@20.17.21) + version: 5.4.14(@types/node@22.13.9) vite-plugin-singlefile: specifier: ^2.1.0 - version: 2.1.0(rollup@4.34.8)(vite@5.4.14(@types/node@20.17.21)) + version: 2.1.0(rollup@4.34.9)(vite@5.4.14(@types/node@22.13.9)) packages/backend: dependencies: @@ -179,8 +179,8 @@ importers: specifier: ^3.7.7 version: 3.7.7 nanoid: - specifier: ^5.1.2 - version: 5.1.2 + specifier: ^5.1.3 + version: 5.1.3 react: specifier: 19.0.0 version: 19.0.0 @@ -198,8 +198,8 @@ importers: specifier: ^19.0.4 version: 19.0.4(@types/react@19.0.10) eslint: - specifier: ^9.21.0 - version: 9.21.0(jiti@1.21.7) + specifier: ^9.22.0 + version: 9.22.0(jiti@1.21.7) eslint-config-custom: specifier: workspace:* version: link:../eslint-config-custom @@ -208,7 +208,7 @@ importers: version: link:../tsconfig tsup: specifier: ^8.4.0 - version: 8.4.0(@swc/core@1.11.5(@swc/helpers@0.5.12))(jiti@1.21.7)(postcss@8.5.3)(typescript@5.8.2)(yaml@2.7.0) + version: 8.4.0(@swc/core@1.11.8(@swc/helpers@0.5.15))(jiti@1.21.7)(postcss@8.5.3)(typescript@5.8.2)(yaml@2.7.0) typescript: specifier: ^5.8.2 version: 5.8.2 @@ -216,17 +216,17 @@ importers: packages/eslint-config-custom: dependencies: eslint-config-next: - specifier: ^14.2.24 - version: 14.2.24(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2) + specifier: ^15.2.1 + version: 15.2.1(eslint@9.22.0(jiti@1.21.7))(typescript@5.8.2) eslint-config-prettier: - specifier: ^9.1.0 - version: 9.1.0(eslint@9.21.0(jiti@1.21.7)) + specifier: ^10.1.1 + version: 10.1.1(eslint@9.22.0(jiti@1.21.7)) eslint-config-turbo: specifier: ^2.4.4 - version: 2.4.4(eslint@9.21.0(jiti@1.21.7))(turbo@2.4.4) + version: 2.4.4(eslint@9.22.0(jiti@1.21.7))(turbo@2.4.4) eslint-plugin-react: - specifier: 7.35.0 - version: 7.35.0(eslint@9.21.0(jiti@1.21.7)) + specifier: 7.37.4 + version: 7.37.4(eslint@9.22.0(jiti@1.21.7)) packages/plugin-ui: dependencies: @@ -246,8 +246,8 @@ importers: specifier: ^3.3.3 version: 3.3.3 lucide-react: - specifier: ^0.477.0 - version: 0.477.0(react@19.0.0) + specifier: ^0.479.0 + version: 0.479.0(react@19.0.0) react: specifier: ^19.0.0 version: 19.0.0 @@ -265,8 +265,8 @@ importers: version: 1.0.7(tailwindcss@3.4.6) devDependencies: eslint: - specifier: ^9.21.0 - version: 9.21.0(jiti@1.21.7) + specifier: ^9.22.0 + version: 9.22.0(jiti@1.21.7) eslint-config-custom: specifier: workspace:* version: link:../eslint-config-custom @@ -298,8 +298,8 @@ importers: version: link:../tsconfig devDependencies: eslint: - specifier: ^9.21.0 - version: 9.21.0(jiti@1.21.7) + specifier: ^9.22.0 + version: 9.22.0(jiti@1.21.7) eslint-config-custom: specifier: workspace:* version: link:../eslint-config-custom @@ -400,18 +400,15 @@ packages: resolution: {integrity: sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==} engines: {node: '>=6.9.0'} + '@emnapi/runtime@1.3.1': + resolution: {integrity: sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==} + '@esbuild/aix-ppc64@0.21.5': resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} engines: {node: '>=12'} cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.23.1': - resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - '@esbuild/aix-ppc64@0.25.0': resolution: {integrity: sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==} engines: {node: '>=18'} @@ -424,12 +421,6 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.23.1': - resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - '@esbuild/android-arm64@0.25.0': resolution: {integrity: sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==} engines: {node: '>=18'} @@ -442,12 +433,6 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.23.1': - resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - '@esbuild/android-arm@0.25.0': resolution: {integrity: sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==} engines: {node: '>=18'} @@ -460,12 +445,6 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.23.1': - resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - '@esbuild/android-x64@0.25.0': resolution: {integrity: sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==} engines: {node: '>=18'} @@ -478,12 +457,6 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.23.1': - resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - '@esbuild/darwin-arm64@0.25.0': resolution: {integrity: sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==} engines: {node: '>=18'} @@ -496,12 +469,6 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.23.1': - resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - '@esbuild/darwin-x64@0.25.0': resolution: {integrity: sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==} engines: {node: '>=18'} @@ -514,12 +481,6 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.23.1': - resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - '@esbuild/freebsd-arm64@0.25.0': resolution: {integrity: sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==} engines: {node: '>=18'} @@ -532,12 +493,6 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.23.1': - resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - '@esbuild/freebsd-x64@0.25.0': resolution: {integrity: sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==} engines: {node: '>=18'} @@ -550,12 +505,6 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.23.1': - resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - '@esbuild/linux-arm64@0.25.0': resolution: {integrity: sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==} engines: {node: '>=18'} @@ -568,12 +517,6 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.23.1': - resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - '@esbuild/linux-arm@0.25.0': resolution: {integrity: sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==} engines: {node: '>=18'} @@ -586,12 +529,6 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.23.1': - resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - '@esbuild/linux-ia32@0.25.0': resolution: {integrity: sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==} engines: {node: '>=18'} @@ -604,12 +541,6 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.23.1': - resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - '@esbuild/linux-loong64@0.25.0': resolution: {integrity: sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==} engines: {node: '>=18'} @@ -622,12 +553,6 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.23.1': - resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - '@esbuild/linux-mips64el@0.25.0': resolution: {integrity: sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==} engines: {node: '>=18'} @@ -640,12 +565,6 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.23.1': - resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - '@esbuild/linux-ppc64@0.25.0': resolution: {integrity: sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==} engines: {node: '>=18'} @@ -658,12 +577,6 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.23.1': - resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - '@esbuild/linux-riscv64@0.25.0': resolution: {integrity: sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==} engines: {node: '>=18'} @@ -676,12 +589,6 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.23.1': - resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - '@esbuild/linux-s390x@0.25.0': resolution: {integrity: sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==} engines: {node: '>=18'} @@ -694,12 +601,6 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.23.1': - resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - '@esbuild/linux-x64@0.25.0': resolution: {integrity: sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==} engines: {node: '>=18'} @@ -718,24 +619,12 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.23.1': - resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - '@esbuild/netbsd-x64@0.25.0': resolution: {integrity: sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.23.1': - resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - '@esbuild/openbsd-arm64@0.25.0': resolution: {integrity: sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==} engines: {node: '>=18'} @@ -748,12 +637,6 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.23.1': - resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - '@esbuild/openbsd-x64@0.25.0': resolution: {integrity: sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==} engines: {node: '>=18'} @@ -766,12 +649,6 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.23.1': - resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - '@esbuild/sunos-x64@0.25.0': resolution: {integrity: sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==} engines: {node: '>=18'} @@ -784,12 +661,6 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.23.1': - resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - '@esbuild/win32-arm64@0.25.0': resolution: {integrity: sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==} engines: {node: '>=18'} @@ -802,12 +673,6 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.23.1': - resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - '@esbuild/win32-ia32@0.25.0': resolution: {integrity: sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==} engines: {node: '>=18'} @@ -820,12 +685,6 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.23.1': - resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - '@esbuild/win32-x64@0.25.0': resolution: {integrity: sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==} engines: {node: '>=18'} @@ -846,6 +705,10 @@ packages: resolution: {integrity: sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/config-helpers@0.1.0': + resolution: {integrity: sha512-kLrdPDJE1ckPo94kmPPf9Hfd0DU0Jw6oKYrhe+pwSC0iTUInmTa+w6fw8sGgcfkFJGNdWOUeOaDM4quW4a7OkA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/core@0.12.0': resolution: {integrity: sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -854,8 +717,8 @@ packages: resolution: {integrity: sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.21.0': - resolution: {integrity: sha512-BqStZ3HX8Yz6LvsF5ByXYrtigrV5AXADWLAGc7PH/1SxOb7/FIYYMszZZWiUou/GB9P2lXWk2SV4d+Z8h0nknw==} + '@eslint/js@9.22.0': + resolution: {integrity: sha512-vLFajx9o8d1/oL2ZkpMYbkLv8nDB6yaIwFNt7nI4+I80U/z03SxmfOMsLbvWr3p7C+Wnoh//aOu2pQW8cS0HCQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.6': @@ -889,6 +752,111 @@ packages: resolution: {integrity: sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==} engines: {node: '>=18.18'} + '@img/sharp-darwin-arm64@0.33.5': + resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.33.5': + resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.0.4': + resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.0.4': + resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.0.4': + resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linux-arm@1.0.5': + resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.0.4': + resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==} + cpu: [s390x] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.0.4': + resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-arm64@1.0.4': + resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-x64@1.0.4': + resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==} + cpu: [x64] + os: [linux] + + '@img/sharp-linux-arm64@0.33.5': + resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linux-arm@0.33.5': + resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-s390x@0.33.5': + resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + + '@img/sharp-linux-x64@0.33.5': + resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-linuxmusl-arm64@0.33.5': + resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linuxmusl-x64@0.33.5': + resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-wasm32@0.33.5': + resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-ia32@0.33.5': + resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.33.5': + resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -911,62 +879,56 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} - '@next/env@14.2.24': - resolution: {integrity: sha512-LAm0Is2KHTNT6IT16lxT+suD0u+VVfYNQqM+EJTKuFRRuY2z+zj01kueWXPCxbMBDt0B5vONYzabHGUNbZYAhA==} + '@next/env@15.2.1': + resolution: {integrity: sha512-JmY0qvnPuS2NCWOz2bbby3Pe0VzdAQ7XpEB6uLIHmtXNfAsAO0KLQLkuAoc42Bxbo3/jMC3dcn9cdf+piCcG2Q==} - '@next/eslint-plugin-next@14.2.24': - resolution: {integrity: sha512-FDL3qs+5DML0AJz56DCVr+KnFYivxeAX73En8QbPw9GjJZ6zbfvqDy+HrarHFzbsIASn7y8y5ySJ/lllSruNVQ==} + '@next/eslint-plugin-next@15.2.1': + resolution: {integrity: sha512-6ppeToFd02z38SllzWxayLxjjNfzvc7Wm07gQOKSLjyASvKcXjNStZrLXMHuaWkhjqxe+cnhb2uzfWXm1VEj/Q==} - '@next/swc-darwin-arm64@14.2.24': - resolution: {integrity: sha512-7Tdi13aojnAZGpapVU6meVSpNzgrFwZ8joDcNS8cJVNuP3zqqrLqeory9Xec5TJZR/stsGJdfwo8KeyloT3+rQ==} + '@next/swc-darwin-arm64@15.2.1': + resolution: {integrity: sha512-aWXT+5KEREoy3K5AKtiKwioeblmOvFFjd+F3dVleLvvLiQ/mD//jOOuUcx5hzcO9ISSw4lrqtUPntTpK32uXXQ==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@14.2.24': - resolution: {integrity: sha512-lXR2WQqUtu69l5JMdTwSvQUkdqAhEWOqJEYUQ21QczQsAlNOW2kWZCucA6b3EXmPbcvmHB1kSZDua/713d52xg==} + '@next/swc-darwin-x64@15.2.1': + resolution: {integrity: sha512-E/w8ervu4fcG5SkLhvn1NE/2POuDCDEy5gFbfhmnYXkyONZR68qbUlJlZwuN82o7BrBVAw+tkR8nTIjGiMW1jQ==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@14.2.24': - resolution: {integrity: sha512-nxvJgWOpSNmzidYvvGDfXwxkijb6hL9+cjZx1PVG6urr2h2jUqBALkKjT7kpfurRWicK6hFOvarmaWsINT1hnA==} + '@next/swc-linux-arm64-gnu@15.2.1': + resolution: {integrity: sha512-gXDX5lIboebbjhiMT6kFgu4svQyjoSed6dHyjx5uZsjlvTwOAnZpn13w9XDaIMFFHw7K8CpBK7HfDKw0VZvUXQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-arm64-musl@14.2.24': - resolution: {integrity: sha512-PaBgOPhqa4Abxa3y/P92F3kklNPsiFjcjldQGT7kFmiY5nuFn8ClBEoX8GIpqU1ODP2y8P6hio6vTomx2Vy0UQ==} + '@next/swc-linux-arm64-musl@15.2.1': + resolution: {integrity: sha512-3v0pF/adKZkBWfUffmB/ROa+QcNTrnmYG4/SS+r52HPwAK479XcWoES2I+7F7lcbqc7mTeVXrIvb4h6rR/iDKg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-x64-gnu@14.2.24': - resolution: {integrity: sha512-vEbyadiRI7GOr94hd2AB15LFVgcJZQWu7Cdi9cWjCMeCiUsHWA0U5BkGPuoYRnTxTn0HacuMb9NeAmStfBCLoQ==} + '@next/swc-linux-x64-gnu@15.2.1': + resolution: {integrity: sha512-RbsVq2iB6KFJRZ2cHrU67jLVLKeuOIhnQB05ygu5fCNgg8oTewxweJE8XlLV+Ii6Y6u4EHwETdUiRNXIAfpBww==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-linux-x64-musl@14.2.24': - resolution: {integrity: sha512-df0FC9ptaYsd8nQCINCzFtDWtko8PNRTAU0/+d7hy47E0oC17tI54U/0NdGk7l/76jz1J377dvRjmt6IUdkpzQ==} + '@next/swc-linux-x64-musl@15.2.1': + resolution: {integrity: sha512-QHsMLAyAIu6/fWjHmkN/F78EFPKmhQlyX5C8pRIS2RwVA7z+t9cTb0IaYWC3EHLOTjsU7MNQW+n2xGXr11QPpg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-win32-arm64-msvc@14.2.24': - resolution: {integrity: sha512-ZEntbLjeYAJ286eAqbxpZHhDFYpYjArotQ+/TW9j7UROh0DUmX7wYDGtsTPpfCV8V+UoqHBPU7q9D4nDNH014Q==} + '@next/swc-win32-arm64-msvc@15.2.1': + resolution: {integrity: sha512-Gk42XZXo1cE89i3hPLa/9KZ8OuupTjkDmhLaMKFohjf9brOeZVEa3BQy1J9s9TWUqPhgAEbwv6B2+ciGfe54Vw==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-ia32-msvc@14.2.24': - resolution: {integrity: sha512-9KuS+XUXM3T6v7leeWU0erpJ6NsFIwiTFD5nzNg8J5uo/DMIPvCp3L1Ao5HjbHX0gkWPB1VrKoo/Il4F0cGK2Q==} - engines: {node: '>= 10'} - cpu: [ia32] - os: [win32] - - '@next/swc-win32-x64-msvc@14.2.24': - resolution: {integrity: sha512-cXcJ2+x0fXQ2CntaE00d7uUH+u1Bfp/E0HsNQH79YiLaZE5Rbm7dZzyAYccn3uICM7mw+DxoMqEfGXZtF4Fgaw==} + '@next/swc-win32-x64-msvc@15.2.1': + resolution: {integrity: sha512-YjqXCl8QGhVlMR8uBftWk0iTmvtntr41PhG1kvzGp0sUP/5ehTM+cwx25hKE54J0CRnHYjSGjSH3gkHEaHIN9g==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -991,98 +953,98 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@rollup/rollup-android-arm-eabi@4.34.8': - resolution: {integrity: sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw==} + '@rollup/rollup-android-arm-eabi@4.34.9': + resolution: {integrity: sha512-qZdlImWXur0CFakn2BJ2znJOdqYZKiedEPEVNTBrpfPjc/YuTGcaYZcdmNFTkUj3DU0ZM/AElcM8Ybww3xVLzA==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.34.8': - resolution: {integrity: sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q==} + '@rollup/rollup-android-arm64@4.34.9': + resolution: {integrity: sha512-4KW7P53h6HtJf5Y608T1ISKvNIYLWRKMvfnG0c44M6In4DQVU58HZFEVhWINDZKp7FZps98G3gxwC1sb0wXUUg==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.34.8': - resolution: {integrity: sha512-02rVdZ5tgdUNRxIUrFdcMBZQoaPMrxtwSb+/hOfBdqkatYHR3lZ2A2EGyHq2sGOd0Owk80oV3snlDASC24He3Q==} + '@rollup/rollup-darwin-arm64@4.34.9': + resolution: {integrity: sha512-0CY3/K54slrzLDjOA7TOjN1NuLKERBgk9nY5V34mhmuu673YNb+7ghaDUs6N0ujXR7fz5XaS5Aa6d2TNxZd0OQ==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.34.8': - resolution: {integrity: sha512-qIP/elwR/tq/dYRx3lgwK31jkZvMiD6qUtOycLhTzCvrjbZ3LjQnEM9rNhSGpbLXVJYQ3rq39A6Re0h9tU2ynw==} + '@rollup/rollup-darwin-x64@4.34.9': + resolution: {integrity: sha512-eOojSEAi/acnsJVYRxnMkPFqcxSMFfrw7r2iD9Q32SGkb/Q9FpUY1UlAu1DH9T7j++gZ0lHjnm4OyH2vCI7l7Q==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.34.8': - resolution: {integrity: sha512-IQNVXL9iY6NniYbTaOKdrlVP3XIqazBgJOVkddzJlqnCpRi/yAeSOa8PLcECFSQochzqApIOE1GHNu3pCz+BDA==} + '@rollup/rollup-freebsd-arm64@4.34.9': + resolution: {integrity: sha512-2lzjQPJbN5UnHm7bHIUKFMulGTQwdvOkouJDpPysJS+QFBGDJqcfh+CxxtG23Ik/9tEvnebQiylYoazFMAgrYw==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.34.8': - resolution: {integrity: sha512-TYXcHghgnCqYFiE3FT5QwXtOZqDj5GmaFNTNt3jNC+vh22dc/ukG2cG+pi75QO4kACohZzidsq7yKTKwq/Jq7Q==} + '@rollup/rollup-freebsd-x64@4.34.9': + resolution: {integrity: sha512-SLl0hi2Ah2H7xQYd6Qaiu01kFPzQ+hqvdYSoOtHYg/zCIFs6t8sV95kaoqjzjFwuYQLtOI0RZre/Ke0nPaQV+g==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.34.8': - resolution: {integrity: sha512-A4iphFGNkWRd+5m3VIGuqHnG3MVnqKe7Al57u9mwgbyZ2/xF9Jio72MaY7xxh+Y87VAHmGQr73qoKL9HPbXj1g==} + '@rollup/rollup-linux-arm-gnueabihf@4.34.9': + resolution: {integrity: sha512-88I+D3TeKItrw+Y/2ud4Tw0+3CxQ2kLgu3QvrogZ0OfkmX/DEppehus7L3TS2Q4lpB+hYyxhkQiYPJ6Mf5/dPg==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.34.8': - resolution: {integrity: sha512-S0lqKLfTm5u+QTxlFiAnb2J/2dgQqRy/XvziPtDd1rKZFXHTyYLoVL58M/XFwDI01AQCDIevGLbQrMAtdyanpA==} + '@rollup/rollup-linux-arm-musleabihf@4.34.9': + resolution: {integrity: sha512-3qyfWljSFHi9zH0KgtEPG4cBXHDFhwD8kwg6xLfHQ0IWuH9crp005GfoUUh/6w9/FWGBwEHg3lxK1iHRN1MFlA==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.34.8': - resolution: {integrity: sha512-jpz9YOuPiSkL4G4pqKrus0pn9aYwpImGkosRKwNi+sJSkz+WU3anZe6hi73StLOQdfXYXC7hUfsQlTnjMd3s1A==} + '@rollup/rollup-linux-arm64-gnu@4.34.9': + resolution: {integrity: sha512-6TZjPHjKZUQKmVKMUowF3ewHxctrRR09eYyvT5eFv8w/fXarEra83A2mHTVJLA5xU91aCNOUnM+DWFMSbQ0Nxw==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.34.8': - resolution: {integrity: sha512-KdSfaROOUJXgTVxJNAZ3KwkRc5nggDk+06P6lgi1HLv1hskgvxHUKZ4xtwHkVYJ1Rep4GNo+uEfycCRRxht7+Q==} + '@rollup/rollup-linux-arm64-musl@4.34.9': + resolution: {integrity: sha512-LD2fytxZJZ6xzOKnMbIpgzFOuIKlxVOpiMAXawsAZ2mHBPEYOnLRK5TTEsID6z4eM23DuO88X0Tq1mErHMVq0A==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.34.8': - resolution: {integrity: sha512-NyF4gcxwkMFRjgXBM6g2lkT58OWztZvw5KkV2K0qqSnUEqCVcqdh2jN4gQrTn/YUpAcNKyFHfoOZEer9nwo6uQ==} + '@rollup/rollup-linux-loongarch64-gnu@4.34.9': + resolution: {integrity: sha512-dRAgTfDsn0TE0HI6cmo13hemKpVHOEyeciGtvlBTkpx/F65kTvShtY/EVyZEIfxFkV5JJTuQ9tP5HGBS0hfxIg==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.34.8': - resolution: {integrity: sha512-LMJc999GkhGvktHU85zNTDImZVUCJ1z/MbAJTnviiWmmjyckP5aQsHtcujMjpNdMZPT2rQEDBlJfubhs3jsMfw==} + '@rollup/rollup-linux-powerpc64le-gnu@4.34.9': + resolution: {integrity: sha512-PHcNOAEhkoMSQtMf+rJofwisZqaU8iQ8EaSps58f5HYll9EAY5BSErCZ8qBDMVbq88h4UxaNPlbrKqfWP8RfJA==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.34.8': - resolution: {integrity: sha512-xAQCAHPj8nJq1PI3z8CIZzXuXCstquz7cIOL73HHdXiRcKk8Ywwqtx2wrIy23EcTn4aZ2fLJNBB8d0tQENPCmw==} + '@rollup/rollup-linux-riscv64-gnu@4.34.9': + resolution: {integrity: sha512-Z2i0Uy5G96KBYKjeQFKbbsB54xFOL5/y1P5wNBsbXB8yE+At3oh0DVMjQVzCJRJSfReiB2tX8T6HUFZ2k8iaKg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.34.8': - resolution: {integrity: sha512-DdePVk1NDEuc3fOe3dPPTb+rjMtuFw89gw6gVWxQFAuEqqSdDKnrwzZHrUYdac7A7dXl9Q2Vflxpme15gUWQFA==} + '@rollup/rollup-linux-s390x-gnu@4.34.9': + resolution: {integrity: sha512-U+5SwTMoeYXoDzJX5dhDTxRltSrIax8KWwfaaYcynuJw8mT33W7oOgz0a+AaXtGuvhzTr2tVKh5UO8GVANTxyQ==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.34.8': - resolution: {integrity: sha512-8y7ED8gjxITUltTUEJLQdgpbPh1sUQ0kMTmufRF/Ns5tI9TNMNlhWtmPKKHCU0SilX+3MJkZ0zERYYGIVBYHIA==} + '@rollup/rollup-linux-x64-gnu@4.34.9': + resolution: {integrity: sha512-FwBHNSOjUTQLP4MG7y6rR6qbGw4MFeQnIBrMe161QGaQoBQLqSUEKlHIiVgF3g/mb3lxlxzJOpIBhaP+C+KP2A==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.34.8': - resolution: {integrity: sha512-SCXcP0ZpGFIe7Ge+McxY5zKxiEI5ra+GT3QRxL0pMMtxPfpyLAKleZODi1zdRHkz5/BhueUrYtYVgubqe9JBNQ==} + '@rollup/rollup-linux-x64-musl@4.34.9': + resolution: {integrity: sha512-cYRpV4650z2I3/s6+5/LONkjIz8MBeqrk+vPXV10ORBnshpn8S32bPqQ2Utv39jCiDcO2eJTuSlPXpnvmaIgRA==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.34.8': - resolution: {integrity: sha512-YHYsgzZgFJzTRbth4h7Or0m5O74Yda+hLin0irAIobkLQFRQd1qWmnoVfwmKm9TXIZVAD0nZ+GEb2ICicLyCnQ==} + '@rollup/rollup-win32-arm64-msvc@4.34.9': + resolution: {integrity: sha512-z4mQK9dAN6byRA/vsSgQiPeuO63wdiDxZ9yg9iyX2QTzKuQM7T4xlBoeUP/J8uiFkqxkcWndWi+W7bXdPbt27Q==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.34.8': - resolution: {integrity: sha512-r3NRQrXkHr4uWy5TOjTpTYojR9XmF0j/RYgKCef+Ag46FWUTltm5ziticv8LdNsDMehjJ543x/+TJAek/xBA2w==} + '@rollup/rollup-win32-ia32-msvc@4.34.9': + resolution: {integrity: sha512-KB48mPtaoHy1AwDNkAJfHXvHp24H0ryZog28spEs0V48l3H1fr4i37tiyHsgKZJnCmvxsbATdZGBpbmxTE3a9w==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.34.8': - resolution: {integrity: sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g==} + '@rollup/rollup-win32-x64-msvc@4.34.9': + resolution: {integrity: sha512-AyleYRPU7+rgkMWbEh71fQlrzRfeP6SyMnRf9XX4fCdDPAJumdSBqYEcWPMzVQ4ScAl7E4oFfK0GUVn77xSwbw==} cpu: [x64] os: [win32] @@ -1092,68 +1054,68 @@ packages: '@rushstack/eslint-patch@1.10.5': resolution: {integrity: sha512-kkKUDVlII2DQiKy7UstOR1ErJP8kUKAQ4oa+SQtM0K+lPdmmjj0YnnxBgtTVYH7mUKtbsxeFC9y0AmK7Yb78/A==} - '@swc/core-darwin-arm64@1.11.5': - resolution: {integrity: sha512-GEd1hzEx0mSGkJYMFMGLnrGgjL2rOsOsuYWyjyiA3WLmhD7o+n/EWBDo6mzD/9aeF8dzSPC0TnW216gJbvrNzA==} + '@swc/core-darwin-arm64@1.11.8': + resolution: {integrity: sha512-rrSsunyJWpHN+5V1zumndwSSifmIeFQBK9i2RMQQp15PgbgUNxHK5qoET1n20pcUrmZeT6jmJaEWlQchkV//Og==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] - '@swc/core-darwin-x64@1.11.5': - resolution: {integrity: sha512-toz04z9wAClVvQSEY3xzrgyyeWBAfMWcKG4K0ugNvO56h/wczi2ZHRlnAXZW1tghKBk3z6MXqa/srfXgNhffKw==} + '@swc/core-darwin-x64@1.11.8': + resolution: {integrity: sha512-44goLqQuuo0HgWnG8qC+ZFw/qnjCVVeqffhzFr9WAXXotogVaxM8ze6egE58VWrfEc8me8yCcxOYL9RbtjhS/Q==} engines: {node: '>=10'} cpu: [x64] os: [darwin] - '@swc/core-linux-arm-gnueabihf@1.11.5': - resolution: {integrity: sha512-5SjmKxXdwbBpsYGTpgeXOXMIjS563/ntRGn8Zc12H/c4VfPrRLGhgbJ/48z2XVFyBLcw7BCHZyFuVX1+ZI3W0Q==} + '@swc/core-linux-arm-gnueabihf@1.11.8': + resolution: {integrity: sha512-Mzo8umKlhTWwF1v8SLuTM1z2A+P43UVhf4R8RZDhzIRBuB2NkeyE+c0gexIOJBuGSIATryuAF4O4luDu727D1w==} engines: {node: '>=10'} cpu: [arm] os: [linux] - '@swc/core-linux-arm64-gnu@1.11.5': - resolution: {integrity: sha512-pydIlInHRzRIwB0NHblz3Dx58H/bsi0I5F2deLf9iOmwPNuOGcEEZF1Qatc7YIjP5DFbXK+Dcz+pMUZb2cc2MQ==} + '@swc/core-linux-arm64-gnu@1.11.8': + resolution: {integrity: sha512-EyhO6U+QdoGYC1MeHOR0pyaaSaKYyNuT4FQNZ1eZIbnuueXpuICC7iNmLIOfr3LE5bVWcZ7NKGVPlM2StJEcgA==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-arm64-musl@1.11.5': - resolution: {integrity: sha512-LhBHKjkZq5tJF1Lh0NJFpx7ROnCWLckrlIAIdSt9XfOV+zuEXJQOj+NFcM1eNk17GFfFyUMOZyGZxzYq5dveEQ==} + '@swc/core-linux-arm64-musl@1.11.8': + resolution: {integrity: sha512-QU6wOkZnS6/QuBN1MHD6G2BgFxB0AclvTVGbqYkRA7MsVkcC29PffESqzTXnypzB252/XkhQjoB2JIt9rPYf6A==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-x64-gnu@1.11.5': - resolution: {integrity: sha512-dCi4xkxXlsk5sQYb3i413Cfh7+wMJeBYTvBZTD5xh+/DgRtIcIJLYJ2tNjWC4/C2i5fj+Ze9bKNSdd8weRWZ3A==} + '@swc/core-linux-x64-gnu@1.11.8': + resolution: {integrity: sha512-r72onUEIU1iJi9EUws3R28pztQ/eM3EshNpsPRBfuLwKy+qn3et55vXOyDhIjGCUph5Eg2Yn8H3h6MTxDdLd+w==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-linux-x64-musl@1.11.5': - resolution: {integrity: sha512-K0AC4TreM5Oo/tXNXnE/Gf5+5y/HwUdd7xvUjOpZddcX/RlsbYOKWLgOtA3fdFIuta7XC+vrGKmIhm5l70DSVQ==} + '@swc/core-linux-x64-musl@1.11.8': + resolution: {integrity: sha512-294k8cLpO103++f4ZUEDr3vnBeUfPitW6G0a3qeVZuoXFhFgaW7ANZIWknUc14WiLOMfMecphJAEiy9C8OeYSw==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-win32-arm64-msvc@1.11.5': - resolution: {integrity: sha512-wzum8sYUsvPY7kgUfuqVYTgIPYmBC8KPksoNM1fz5UfhudU0ciQuYvUBD47GIGOevaoxhLkjPH4CB95vh1mJ9w==} + '@swc/core-win32-arm64-msvc@1.11.8': + resolution: {integrity: sha512-EbjOzQ+B85rumHyeesBYxZ+hq3ZQn+YAAT1ZNE9xW1/8SuLoBmHy/K9YniRGVDq/2NRmp5kI5+5h5TX0asIS9A==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - '@swc/core-win32-ia32-msvc@1.11.5': - resolution: {integrity: sha512-lco7mw0TPRTpVPR6NwggJpjdUkAboGRkLrDHjIsUaR+Y5+0m5FMMkHOMxWXAbrBS5c4ph7QErp4Lma4r9Mn5og==} + '@swc/core-win32-ia32-msvc@1.11.8': + resolution: {integrity: sha512-Z+FF5kgLHfQWIZ1KPdeInToXLzbY0sMAashjd/igKeP1Lz0qKXVAK+rpn6ASJi85Fn8wTftCGCyQUkRVn0bTDg==} engines: {node: '>=10'} cpu: [ia32] os: [win32] - '@swc/core-win32-x64-msvc@1.11.5': - resolution: {integrity: sha512-E+DApLSC6JRK8VkDa4bNsBdD7Qoomx1HvKVZpOXl9v94hUZI5GMExl4vU5isvb+hPWL7rZ0NeI7ITnVLgLJRbA==} + '@swc/core-win32-x64-msvc@1.11.8': + resolution: {integrity: sha512-j6B6N0hChCeAISS6xp/hh6zR5CSCr037BAjCxNLsT8TGe5D+gYZ57heswUWXRH8eMKiRDGiLCYpPB2pkTqxCSw==} engines: {node: '>=10'} cpu: [x64] os: [win32] - '@swc/core@1.11.5': - resolution: {integrity: sha512-EVY7zfpehxhTZXOfy508gb3D78ihoGGmvyiTWtlBPjgIaidP1Xw0naHMD78CWiFlZmeDjKXJufGtsEGOnZdmNA==} + '@swc/core@1.11.8': + resolution: {integrity: sha512-UAL+EULxrc0J73flwYHfu29mO8CONpDJiQv1QPDXsyCvDUcEhqAqUROVTgC+wtJCFFqMQdyr4stAA5/s0KSOmA==} engines: {node: '>=10'} peerDependencies: '@swc/helpers': '*' @@ -1164,11 +1126,8 @@ packages: '@swc/counter@0.1.3': resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} - '@swc/helpers@0.5.12': - resolution: {integrity: sha512-KMZNXiGibsW9kvZAO1Pam2JPTDBm+KSHMMHWdsyI/1DbIZjT2A6Gy3hblVXUMEDvUAKq+e0vL0X0o54owWji7g==} - - '@swc/helpers@0.5.5': - resolution: {integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==} + '@swc/helpers@0.5.15': + resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} '@swc/types@0.1.19': resolution: {integrity: sha512-WkAZaAfj44kh/UFdAQcrMP1I0nwRqpt27u+08LMBYMqmQfwwMofYoMh/48NGkMMRfC4ynpfwRbJuu8ErfNloeA==} @@ -1197,9 +1156,6 @@ packages: '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} - '@types/node@20.17.21': - resolution: {integrity: sha512-yw1WZ94lZpdZbpnaF+WRvlN/Sx2EZWe/YZVdK4mC4u02/ql6Ozen8qbRJhOtltOxCg97/kpijhGs5X6STwkvbg==} - '@types/node@22.13.9': resolution: {integrity: sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw==} @@ -1217,109 +1173,51 @@ packages: '@types/unist@2.0.11': resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} - '@typescript-eslint/eslint-plugin@7.18.0': - resolution: {integrity: sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==} - engines: {node: ^18.18.0 || >=20.0.0} - peerDependencies: - '@typescript-eslint/parser': ^7.0.0 - eslint: ^8.56.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/eslint-plugin@8.25.0': - resolution: {integrity: sha512-VM7bpzAe7JO/BFf40pIT1lJqS/z1F8OaSsUB3rpFJucQA4cOSuH2RVVVkFULN+En0Djgr29/jb4EQnedUo95KA==} + '@typescript-eslint/eslint-plugin@8.26.0': + resolution: {integrity: sha512-cLr1J6pe56zjKYajK6SSSre6nl1Gj6xDp1TY0trpgPzjVbgDwd09v2Ws37LABxzkicmUjhEeg/fAUjPJJB1v5Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.8.0' - - '@typescript-eslint/parser@7.18.0': - resolution: {integrity: sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==} - engines: {node: ^18.18.0 || >=20.0.0} - peerDependencies: - eslint: ^8.56.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/parser@8.25.0': - resolution: {integrity: sha512-4gbs64bnbSzu4FpgMiQ1A+D+urxkoJk/kqlDJ2W//5SygaEiAP2B4GoS7TEdxgwol2el03gckFV9lJ4QOMiiHg==} + '@typescript-eslint/parser@8.26.0': + resolution: {integrity: sha512-mNtXP9LTVBy14ZF3o7JG69gRPBK/2QWtQd0j0oH26HcY/foyJJau6pNUez7QrM5UHnSvwlQcJXKsk0I99B9pOA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.8.0' + typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/scope-manager@7.18.0': - resolution: {integrity: sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==} - engines: {node: ^18.18.0 || >=20.0.0} - - '@typescript-eslint/scope-manager@8.25.0': - resolution: {integrity: sha512-6PPeiKIGbgStEyt4NNXa2ru5pMzQ8OYKO1hX1z53HMomrmiSB+R5FmChgQAP1ro8jMtNawz+TRQo/cSXrauTpg==} + '@typescript-eslint/scope-manager@8.26.0': + resolution: {integrity: sha512-E0ntLvsfPqnPwng8b8y4OGuzh/iIOm2z8U3S9zic2TeMLW61u5IH2Q1wu0oSTkfrSzwbDJIB/Lm8O3//8BWMPA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/type-utils@7.18.0': - resolution: {integrity: sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==} - engines: {node: ^18.18.0 || >=20.0.0} - peerDependencies: - eslint: ^8.56.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/type-utils@8.25.0': - resolution: {integrity: sha512-d77dHgHWnxmXOPJuDWO4FDWADmGQkN5+tt6SFRZz/RtCWl4pHgFl3+WdYCn16+3teG09DY6XtEpf3gGD0a186g==} + '@typescript-eslint/type-utils@8.26.0': + resolution: {integrity: sha512-ruk0RNChLKz3zKGn2LwXuVoeBcUMh+jaqzN461uMMdxy5H9epZqIBtYj7UiPXRuOpaALXGbmRuZQhmwHhaS04Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.8.0' - - '@typescript-eslint/types@7.18.0': - resolution: {integrity: sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==} - engines: {node: ^18.18.0 || >=20.0.0} + typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/types@8.25.0': - resolution: {integrity: sha512-+vUe0Zb4tkNgznQwicsvLUJgZIRs6ITeWSCclX1q85pR1iOiaj+4uZJIUp//Z27QWu5Cseiw3O3AR8hVpax7Aw==} + '@typescript-eslint/types@8.26.0': + resolution: {integrity: sha512-89B1eP3tnpr9A8L6PZlSjBvnJhWXtYfZhECqlBl1D9Lme9mHO6iWlsprBtVenQvY1HMhax1mWOjhtL3fh/u+pA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@7.18.0': - resolution: {integrity: sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==} - engines: {node: ^18.18.0 || >=20.0.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/typescript-estree@8.25.0': - resolution: {integrity: sha512-ZPaiAKEZ6Blt/TPAx5Ot0EIB/yGtLI2EsGoY6F7XKklfMxYQyvtL+gT/UCqkMzO0BVFHLDlzvFqQzurYahxv9Q==} + '@typescript-eslint/typescript-estree@8.26.0': + resolution: {integrity: sha512-tiJ1Hvy/V/oMVRTbEOIeemA2XoylimlDQ03CgPPNaHYZbpsc78Hmngnt+WXZfJX1pjQ711V7g0H7cSJThGYfPQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '>=4.8.4 <5.8.0' + typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/utils@7.18.0': - resolution: {integrity: sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==} - engines: {node: ^18.18.0 || >=20.0.0} - peerDependencies: - eslint: ^8.56.0 - - '@typescript-eslint/utils@8.25.0': - resolution: {integrity: sha512-syqRbrEv0J1wywiLsK60XzHnQe/kRViI3zwFALrNEgnntn1l24Ra2KvOAWwWbWZ1lBZxZljPDGOq967dsl6fkA==} + '@typescript-eslint/utils@8.26.0': + resolution: {integrity: sha512-2L2tU3FVwhvU14LndnQCA2frYC8JnPDVKyQtWFPf8IYFMt/ykEN1bPolNhNbCVgOmdzTlWdusCTKA/9nKrf8Ig==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.8.0' - - '@typescript-eslint/visitor-keys@7.18.0': - resolution: {integrity: sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==} - engines: {node: ^18.18.0 || >=20.0.0} + typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/visitor-keys@8.25.0': - resolution: {integrity: sha512-kCYXKAum9CecGVHGij7muybDfTS2sD3t0L4bJsEZLkyrXUImiCTq1M3LG2SRtOhiHFwMR9wAFplpT6XHYjTkwQ==} + '@typescript-eslint/visitor-keys@8.26.0': + resolution: {integrity: sha512-2z8JQJWAzPdDd51dRQ/oqIJxe99/hoLIqmf8RMCAJQtYDc535W/Jt2+RTP4bP0aKeBG1F65yjIZuczOXCmbWwg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@vitejs/plugin-react-swc@3.8.0': @@ -1338,8 +1236,8 @@ packages: peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - acorn@8.14.0: - resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} + acorn@8.14.1: + resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} engines: {node: '>=0.4.0'} hasBin: true @@ -1387,10 +1285,6 @@ packages: resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} engines: {node: '>= 0.4'} - array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - array.prototype.findlast@1.2.5: resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} engines: {node: '>= 0.4'} @@ -1433,8 +1327,8 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - axe-core@4.10.2: - resolution: {integrity: sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==} + axe-core@4.10.3: + resolution: {integrity: sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==} engines: {node: '>=4'} axobject-query@4.1.0: @@ -1485,8 +1379,8 @@ packages: resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} engines: {node: '>= 0.4'} - call-bound@1.0.3: - resolution: {integrity: sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==} + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} engines: {node: '>= 0.4'} callsites@3.1.0: @@ -1497,8 +1391,8 @@ packages: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} - caniuse-lite@1.0.30001701: - resolution: {integrity: sha512-faRs/AW3jA9nTwmJBSO1PQ6L/EOgsB5HMQQq4iCu5zhPgVVgO/pZRHlmatwijZKetFw8/Pr4q6dEN8sJuq8qTw==} + caniuse-lite@1.0.30001702: + resolution: {integrity: sha512-LoPe/D7zioC0REI5W73PeR1e1MLCipRGq/VkovJnd6Df+QVqT+vT33OXCp8QUd7kA7RZrHWxb1B36OQKI/0gOA==} chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} @@ -1539,6 +1433,13 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + + color@4.2.3: + resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} + engines: {node: '>=12.5.0'} + comma-separated-tokens@1.0.8: resolution: {integrity: sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==} @@ -1549,9 +1450,9 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - concurrently@8.2.2: - resolution: {integrity: sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==} - engines: {node: ^14.13.0 || >=16.0.0} + concurrently@9.1.2: + resolution: {integrity: sha512-H9MWcoPsYddwbOGM6difjVwVZHl63nwMEwDJG/L7VGtuaJhb12h2caPG2tVPWs7emuYix252iGfqOyrz1GczTQ==} + engines: {node: '>=18'} hasBin: true consola@3.4.0: @@ -1591,10 +1492,6 @@ packages: resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} engines: {node: '>= 0.4'} - date-fns@2.30.0: - resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} - engines: {node: '>=0.11'} - debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -1623,13 +1520,13 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} + detect-libc@2.0.3: + resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} + engines: {node: '>=8'} + didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} - dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} - dlv@1.1.3: resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} @@ -1648,8 +1545,8 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - electron-to-chromium@1.5.109: - resolution: {integrity: sha512-AidaH9JETVRr9DIPGfp1kAarm/W6hRJTPuCnkF+2MqhF4KaAgRIcBc8nvjk+YMXZhwfISof/7WG29eS4iGxQLQ==} + electron-to-chromium@1.5.113: + resolution: {integrity: sha512-wjT2O4hX+wdWPJ76gWSkMhcHAV2PTMX+QetUCPYEdCIe+cxmgzzSSiGRCKW8nuh4mwKZlpv0xvoW7OF2X+wmHg==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -1698,11 +1595,6 @@ packages: engines: {node: '>=12'} hasBin: true - esbuild@0.23.1: - resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} - engines: {node: '>=18'} - hasBin: true - esbuild@0.25.0: resolution: {integrity: sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==} engines: {node: '>=18'} @@ -1716,17 +1608,17 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - eslint-config-next@14.2.24: - resolution: {integrity: sha512-9r1ujK++Pgpfixr5+DQ6rXDIQmSzuDbBlAQYMkJRMz9KWqovX7ESUTC0EAyBfOCl3ubkoeplw+aoXDuih3A8fw==} + eslint-config-next@15.2.1: + resolution: {integrity: sha512-mhsprz7l0no8X+PdDnVHF4dZKu9YBJp2Rf6ztWbXBLJ4h6gxmW//owbbGJMBVUU+PibGJDAqZhW4pt8SC8HSow==} peerDependencies: - eslint: ^7.23.0 || ^8.0.0 + eslint: ^7.23.0 || ^8.0.0 || ^9.0.0 typescript: '>=3.3.1' peerDependenciesMeta: typescript: optional: true - eslint-config-prettier@9.1.0: - resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} + eslint-config-prettier@10.1.1: + resolution: {integrity: sha512-4EQQr6wXwS+ZJSzaR5ZCrYgLxqvUjdXctaEtBqHcbkW944B1NQyO4qpdHQbXBONfwxXdkAY81HH4+LUfrg+zPw==} hasBin: true peerDependencies: eslint: '>=7.0.0' @@ -1790,25 +1682,19 @@ packages: peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 - eslint-plugin-react-hooks@4.6.2: - resolution: {integrity: sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==} + eslint-plugin-react-hooks@5.2.0: + resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} engines: {node: '>=10'} peerDependencies: - eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 - - eslint-plugin-react-hooks@5.0.0-canary-7118f5dd7-20230705: - resolution: {integrity: sha512-AZYbMo/NW9chdL7vk6HQzQhT+PvTAEVqWk9ziruUoW2kAOcN5qNyelv70e0F1VNQAbvutOC9oc+xfWycI9FxDw==} - engines: {node: '>=10'} - peerDependencies: - eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 eslint-plugin-react-refresh@0.4.19: resolution: {integrity: sha512-eyy8pcr/YxSYjBoqIFSrlbn9i/xvxUFa8CjzAYo9cFjgGXqq1hyjihcpZvxRLalpaWmueWR81xn7vuKmAFijDQ==} peerDependencies: eslint: '>=8.40' - eslint-plugin-react@7.35.0: - resolution: {integrity: sha512-v501SSMOWv8gerHkk+IIQBkcGRGrO2nfybfj5pLxuJNFTPxxA3PSryhXTK+9pNbtkggheDdsC0E9Q8CuPk6JKA==} + eslint-plugin-react@7.37.4: + resolution: {integrity: sha512-BGP0jRmfYyvOyvMoRX/uoUeW+GqNj9y16bPQzqAHf3AYII/tDs+jMN0dBVkl88/OZwNGwrVFxE7riHsXVfy/LQ==} engines: {node: '>=4'} peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 @@ -1819,8 +1705,8 @@ packages: eslint: '>6.6.0' turbo: '>2.0.0' - eslint-scope@8.2.0: - resolution: {integrity: sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==} + eslint-scope@8.3.0: + resolution: {integrity: sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} eslint-visitor-keys@3.4.3: @@ -1831,8 +1717,8 @@ packages: resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.21.0: - resolution: {integrity: sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg==} + eslint@9.22.0: + resolution: {integrity: sha512-9V/QURhsRN40xuHXWjV64yvrzMjcz7ZyNoF2jJFmy9j/SLk0u1OLSZgXi28MrXjymnjEGSR80WCdab3RGMDveQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -1864,6 +1750,10 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-glob@3.3.1: + resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} + engines: {node: '>=8.6.0'} + fast-glob@3.3.3: resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} @@ -1922,8 +1812,8 @@ packages: fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} - framer-motion@12.4.9: - resolution: {integrity: sha512-c+nDhfiNUwi8G4BrhrP2hjPsDHzIKRbUhDlcK7oC5kXY4QK1IrT/kuhY4BgK6h2ujDrZ8ocvFrG2X8+b1m/MkQ==} + framer-motion@12.4.10: + resolution: {integrity: sha512-3Msuyjcr1Pb5hjkn4EJcRe1HumaveP0Gbv4DBMKTPKcV/1GSMkQXj+Uqgneys+9DPcZM18Hac9qY9iUEF5LZtg==} peerDependencies: '@emotion/is-prop-valid': '*' react: ^18.0.0 || ^19.0.0 @@ -1982,11 +1872,6 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} - glob@10.3.10: - resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==} - engines: {node: '>=16 || 14 >=14.17'} - hasBin: true - glob@10.4.5: resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} hasBin: true @@ -2003,10 +1888,6 @@ packages: resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} engines: {node: '>= 0.4'} - globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} - gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -2082,6 +1963,9 @@ packages: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} + is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + is-async-function@2.1.1: resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} engines: {node: '>= 0.4'} @@ -2201,10 +2085,6 @@ packages: resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} engines: {node: '>= 0.4'} - jackspeak@2.3.6: - resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} - engines: {node: '>=14'} - jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} @@ -2308,8 +2188,8 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - lucide-react@0.477.0: - resolution: {integrity: sha512-yCf7aYxerFZAbd8jHJxjwe1j7jEMPptjnaOqdYeirFnEy85cNR3/L+o0I875CYFYya+eEVzZSbNuRk8BZPDpVw==} + lucide-react@0.479.0: + resolution: {integrity: sha512-aBhNnveRhorBOK7uA4gDjgaf+YlHMdMhQ/3cupk6exM10hWlEU+2QtWYOfhXhjAsmdb6LeKR+NZnow4UxRRiTQ==} peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -2339,14 +2219,14 @@ packages: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} - motion-dom@12.4.5: - resolution: {integrity: sha512-Q2xmhuyYug1CGTo0jdsL05EQ4RhIYXlggFS/yPhQQRNzbrhjKQ1tbjThx5Plv68aX31LsUQRq4uIkuDxdO5vRQ==} + motion-dom@12.4.10: + resolution: {integrity: sha512-ISP5u6FTceoD6qKdLupIPU/LyXBrxGox+P2e3mBbm1+pLdlBbwv01YENJr7+1WZnW5ucVKzFScYsV1eXTCG4Xg==} - motion-utils@12.0.0: - resolution: {integrity: sha512-MNFiBKbbqnmvOjkPyOKgHUp3Q6oiokLkI1bEwm5QA28cxMZrv0CbbBGDNmhF6DIXsi1pCQBSs0dX8xjeER1tmA==} + motion-utils@12.4.10: + resolution: {integrity: sha512-NPwZd94V013SwRf++jMrk2+HEBgPkeIE2RiOzhAuuQlqxMJPkKt/LXVh6Upl+iN8oarSGD2dlY5/bqgsYXDABA==} - motion@12.4.9: - resolution: {integrity: sha512-lOT2+X9b3yvDEC+pAClTzLSW/D5T/zZweO+UN1lMe86WtGFQIbHU/VjEhwGREW0QryG9KECB1uK3QJo8G3NGag==} + motion@12.4.10: + resolution: {integrity: sha512-AM21Lyfn7ZHO+nBuHJEA2REFgS3kUM83CLZnzM0ZY1/sVeKGkCtV4LF4O/YsQXyZ9mrUrrnTaUkKquS4eaIYjg==} peerDependencies: '@emotion/is-prop-valid': '*' react: ^18.0.0 || ^19.0.0 @@ -2365,34 +2245,37 @@ packages: mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - nanoid@3.3.8: - resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} + nanoid@3.3.9: + resolution: {integrity: sha512-SppoicMGpZvbF1l3z4x7No3OlIjP7QJvC9XR7AhZr1kL133KHnKPztkKDc+Ir4aJ/1VhTySrtKhrsycmrMQfvg==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - nanoid@5.1.2: - resolution: {integrity: sha512-b+CiXQCNMUGe0Ri64S9SXFcP9hogjAJ2Rd6GdVxhPLRm7mhGaM7VgOvCAJ1ZshfHbqVDI3uqTI5C8/GaKuLI7g==} + nanoid@5.1.3: + resolution: {integrity: sha512-zAbEOEr7u2CbxwoMRlz/pNSpRP0FdAU4pRaYunCdEezWohXFs+a0Xw7RfkKaezMsmSM1vttcLthJtwRnVtOfHQ==} engines: {node: ^18 || >=20} hasBin: true natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - next@14.2.24: - resolution: {integrity: sha512-En8VEexSJ0Py2FfVnRRh8gtERwDRaJGNvsvad47ShkC2Yi8AXQPXEA2vKoDJlGFSj5WE5SyF21zNi4M5gyi+SQ==} - engines: {node: '>=18.17.0'} + next@15.2.1: + resolution: {integrity: sha512-zxbsdQv3OqWXybK5tMkPCBKyhIz63RstJ+NvlfkaLMc/m5MwXgz2e92k+hSKcyBpyADhMk2C31RIiaDjUZae7g==} + engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} hasBin: true peerDependencies: '@opentelemetry/api': ^1.1.0 '@playwright/test': ^1.41.2 - react: ^18.2.0 - react-dom: ^18.2.0 + babel-plugin-react-compiler: '*' + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 sass: ^1.3.0 peerDependenciesMeta: '@opentelemetry/api': optional: true '@playwright/test': optional: true + babel-plugin-react-compiler: + optional: true sass: optional: true @@ -2484,10 +2367,6 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} - path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -2578,8 +2457,8 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - prettier@3.5.2: - resolution: {integrity: sha512-lc6npv5PH7hVqozBR7lkBNOGXV9vMwROAPlumdBkX0wTbbzPu/U1hk5yL8p2pt4Xoc+2mkT8t/sow2YrV/M5qg==} + prettier@3.5.3: + resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==} engines: {node: '>=14'} hasBin: true @@ -2678,8 +2557,8 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rollup@4.34.8: - resolution: {integrity: sha512-489gTVMzAYdiZHFVA/ig/iYFllCcWFHMvUHI1rpFmkoUtRlQxqh6/yiNqnYibjMZ2b/+FUQwldG+aLsEt6bglQ==} + rollup@4.34.9: + resolution: {integrity: sha512-nF5XYqWWp9hx/LrpC8sZvvvmq0TeTjQgaZHYmAgwysT9nh8sWnZhBnM8ZyVbbJFIQBLwHDNoMqsBZBbUo4U8sQ==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -2725,6 +2604,10 @@ packages: resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} engines: {node: '>= 0.4'} + sharp@0.33.5: + resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -2757,9 +2640,8 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} - slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} + simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} @@ -2772,9 +2654,6 @@ packages: space-separated-tokens@1.1.5: resolution: {integrity: sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==} - spawn-command@0.0.2: - resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==} - stable-hash@0.0.4: resolution: {integrity: sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g==} @@ -2829,13 +2708,13 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - styled-jsx@5.1.1: - resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} + styled-jsx@5.1.6: + resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} engines: {node: '>= 12.0.0'} peerDependencies: '@babel/core': '*' babel-plugin-macros: '*' - react: '>= 16.8.0 || 17.x.x || ^18.0.0-0' + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' peerDependenciesMeta: '@babel/core': optional: true @@ -2904,12 +2783,6 @@ packages: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true - ts-api-utils@1.4.3: - resolution: {integrity: sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==} - engines: {node: '>=16'} - peerDependencies: - typescript: '>=4.2.0' - ts-api-utils@2.0.1: resolution: {integrity: sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==} engines: {node: '>=18.12'} @@ -3007,9 +2880,6 @@ packages: resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} engines: {node: '>= 0.4'} - undici-types@6.19.8: - resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} - undici-types@6.20.0: resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} @@ -3253,10 +3123,12 @@ snapshots: '@babel/helper-string-parser': 7.25.9 '@babel/helper-validator-identifier': 7.25.9 - '@esbuild/aix-ppc64@0.21.5': + '@emnapi/runtime@1.3.1': + dependencies: + tslib: 2.8.1 optional: true - '@esbuild/aix-ppc64@0.23.1': + '@esbuild/aix-ppc64@0.21.5': optional: true '@esbuild/aix-ppc64@0.25.0': @@ -3265,144 +3137,96 @@ snapshots: '@esbuild/android-arm64@0.21.5': optional: true - '@esbuild/android-arm64@0.23.1': - optional: true - '@esbuild/android-arm64@0.25.0': optional: true '@esbuild/android-arm@0.21.5': optional: true - '@esbuild/android-arm@0.23.1': - optional: true - '@esbuild/android-arm@0.25.0': optional: true '@esbuild/android-x64@0.21.5': optional: true - '@esbuild/android-x64@0.23.1': - optional: true - '@esbuild/android-x64@0.25.0': optional: true '@esbuild/darwin-arm64@0.21.5': optional: true - '@esbuild/darwin-arm64@0.23.1': - optional: true - '@esbuild/darwin-arm64@0.25.0': optional: true '@esbuild/darwin-x64@0.21.5': optional: true - '@esbuild/darwin-x64@0.23.1': - optional: true - '@esbuild/darwin-x64@0.25.0': optional: true '@esbuild/freebsd-arm64@0.21.5': optional: true - '@esbuild/freebsd-arm64@0.23.1': - optional: true - '@esbuild/freebsd-arm64@0.25.0': optional: true '@esbuild/freebsd-x64@0.21.5': optional: true - '@esbuild/freebsd-x64@0.23.1': - optional: true - '@esbuild/freebsd-x64@0.25.0': optional: true '@esbuild/linux-arm64@0.21.5': optional: true - '@esbuild/linux-arm64@0.23.1': - optional: true - '@esbuild/linux-arm64@0.25.0': optional: true '@esbuild/linux-arm@0.21.5': optional: true - '@esbuild/linux-arm@0.23.1': - optional: true - '@esbuild/linux-arm@0.25.0': optional: true '@esbuild/linux-ia32@0.21.5': optional: true - '@esbuild/linux-ia32@0.23.1': - optional: true - '@esbuild/linux-ia32@0.25.0': optional: true '@esbuild/linux-loong64@0.21.5': optional: true - '@esbuild/linux-loong64@0.23.1': - optional: true - '@esbuild/linux-loong64@0.25.0': optional: true '@esbuild/linux-mips64el@0.21.5': optional: true - '@esbuild/linux-mips64el@0.23.1': - optional: true - '@esbuild/linux-mips64el@0.25.0': optional: true '@esbuild/linux-ppc64@0.21.5': optional: true - '@esbuild/linux-ppc64@0.23.1': - optional: true - '@esbuild/linux-ppc64@0.25.0': optional: true '@esbuild/linux-riscv64@0.21.5': optional: true - '@esbuild/linux-riscv64@0.23.1': - optional: true - '@esbuild/linux-riscv64@0.25.0': optional: true '@esbuild/linux-s390x@0.21.5': optional: true - '@esbuild/linux-s390x@0.23.1': - optional: true - '@esbuild/linux-s390x@0.25.0': optional: true '@esbuild/linux-x64@0.21.5': optional: true - '@esbuild/linux-x64@0.23.1': - optional: true - '@esbuild/linux-x64@0.25.0': optional: true @@ -3412,66 +3236,45 @@ snapshots: '@esbuild/netbsd-x64@0.21.5': optional: true - '@esbuild/netbsd-x64@0.23.1': - optional: true - '@esbuild/netbsd-x64@0.25.0': optional: true - '@esbuild/openbsd-arm64@0.23.1': - optional: true - '@esbuild/openbsd-arm64@0.25.0': optional: true '@esbuild/openbsd-x64@0.21.5': optional: true - '@esbuild/openbsd-x64@0.23.1': - optional: true - '@esbuild/openbsd-x64@0.25.0': optional: true '@esbuild/sunos-x64@0.21.5': optional: true - '@esbuild/sunos-x64@0.23.1': - optional: true - '@esbuild/sunos-x64@0.25.0': optional: true '@esbuild/win32-arm64@0.21.5': optional: true - '@esbuild/win32-arm64@0.23.1': - optional: true - '@esbuild/win32-arm64@0.25.0': optional: true '@esbuild/win32-ia32@0.21.5': optional: true - '@esbuild/win32-ia32@0.23.1': - optional: true - '@esbuild/win32-ia32@0.25.0': optional: true '@esbuild/win32-x64@0.21.5': optional: true - '@esbuild/win32-x64@0.23.1': - optional: true - '@esbuild/win32-x64@0.25.0': optional: true - '@eslint-community/eslint-utils@4.4.1(eslint@9.21.0(jiti@1.21.7))': + '@eslint-community/eslint-utils@4.4.1(eslint@9.22.0(jiti@1.21.7))': dependencies: - eslint: 9.21.0(jiti@1.21.7) + eslint: 9.22.0(jiti@1.21.7) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} @@ -3484,6 +3287,8 @@ snapshots: transitivePeerDependencies: - supports-color + '@eslint/config-helpers@0.1.0': {} + '@eslint/core@0.12.0': dependencies: '@types/json-schema': 7.0.15 @@ -3502,7 +3307,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.21.0': {} + '@eslint/js@9.22.0': {} '@eslint/object-schema@2.1.6': {} @@ -3526,6 +3331,81 @@ snapshots: '@humanwhocodes/retry@0.4.2': {} + '@img/sharp-darwin-arm64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.0.4 + optional: true + + '@img/sharp-darwin-x64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.0.4 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.0.4': + optional: true + + '@img/sharp-libvips-darwin-x64@1.0.4': + optional: true + + '@img/sharp-libvips-linux-arm64@1.0.4': + optional: true + + '@img/sharp-libvips-linux-arm@1.0.5': + optional: true + + '@img/sharp-libvips-linux-s390x@1.0.4': + optional: true + + '@img/sharp-libvips-linux-x64@1.0.4': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.0.4': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.0.4': + optional: true + + '@img/sharp-linux-arm64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.0.4 + optional: true + + '@img/sharp-linux-arm@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.0.5 + optional: true + + '@img/sharp-linux-s390x@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.0.4 + optional: true + + '@img/sharp-linux-x64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.0.4 + optional: true + + '@img/sharp-linuxmusl-arm64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 + optional: true + + '@img/sharp-linuxmusl-x64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.0.4 + optional: true + + '@img/sharp-wasm32@0.33.5': + dependencies: + '@emnapi/runtime': 1.3.1 + optional: true + + '@img/sharp-win32-ia32@0.33.5': + optional: true + + '@img/sharp-win32-x64@0.33.5': + optional: true + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -3552,37 +3432,34 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 - '@next/env@14.2.24': {} + '@next/env@15.2.1': {} - '@next/eslint-plugin-next@14.2.24': + '@next/eslint-plugin-next@15.2.1': dependencies: - glob: 10.3.10 - - '@next/swc-darwin-arm64@14.2.24': - optional: true + fast-glob: 3.3.1 - '@next/swc-darwin-x64@14.2.24': + '@next/swc-darwin-arm64@15.2.1': optional: true - '@next/swc-linux-arm64-gnu@14.2.24': + '@next/swc-darwin-x64@15.2.1': optional: true - '@next/swc-linux-arm64-musl@14.2.24': + '@next/swc-linux-arm64-gnu@15.2.1': optional: true - '@next/swc-linux-x64-gnu@14.2.24': + '@next/swc-linux-arm64-musl@15.2.1': optional: true - '@next/swc-linux-x64-musl@14.2.24': + '@next/swc-linux-x64-gnu@15.2.1': optional: true - '@next/swc-win32-arm64-msvc@14.2.24': + '@next/swc-linux-x64-musl@15.2.1': optional: true - '@next/swc-win32-ia32-msvc@14.2.24': + '@next/swc-win32-arm64-msvc@15.2.1': optional: true - '@next/swc-win32-x64-msvc@14.2.24': + '@next/swc-win32-x64-msvc@15.2.1': optional: true '@nodelib/fs.scandir@2.1.5': @@ -3602,125 +3479,119 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@rollup/rollup-android-arm-eabi@4.34.8': + '@rollup/rollup-android-arm-eabi@4.34.9': optional: true - '@rollup/rollup-android-arm64@4.34.8': + '@rollup/rollup-android-arm64@4.34.9': optional: true - '@rollup/rollup-darwin-arm64@4.34.8': + '@rollup/rollup-darwin-arm64@4.34.9': optional: true - '@rollup/rollup-darwin-x64@4.34.8': + '@rollup/rollup-darwin-x64@4.34.9': optional: true - '@rollup/rollup-freebsd-arm64@4.34.8': + '@rollup/rollup-freebsd-arm64@4.34.9': optional: true - '@rollup/rollup-freebsd-x64@4.34.8': + '@rollup/rollup-freebsd-x64@4.34.9': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.34.8': + '@rollup/rollup-linux-arm-gnueabihf@4.34.9': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.34.8': + '@rollup/rollup-linux-arm-musleabihf@4.34.9': optional: true - '@rollup/rollup-linux-arm64-gnu@4.34.8': + '@rollup/rollup-linux-arm64-gnu@4.34.9': optional: true - '@rollup/rollup-linux-arm64-musl@4.34.8': + '@rollup/rollup-linux-arm64-musl@4.34.9': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.34.8': + '@rollup/rollup-linux-loongarch64-gnu@4.34.9': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.34.8': + '@rollup/rollup-linux-powerpc64le-gnu@4.34.9': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.34.8': + '@rollup/rollup-linux-riscv64-gnu@4.34.9': optional: true - '@rollup/rollup-linux-s390x-gnu@4.34.8': + '@rollup/rollup-linux-s390x-gnu@4.34.9': optional: true - '@rollup/rollup-linux-x64-gnu@4.34.8': + '@rollup/rollup-linux-x64-gnu@4.34.9': optional: true - '@rollup/rollup-linux-x64-musl@4.34.8': + '@rollup/rollup-linux-x64-musl@4.34.9': optional: true - '@rollup/rollup-win32-arm64-msvc@4.34.8': + '@rollup/rollup-win32-arm64-msvc@4.34.9': optional: true - '@rollup/rollup-win32-ia32-msvc@4.34.8': + '@rollup/rollup-win32-ia32-msvc@4.34.9': optional: true - '@rollup/rollup-win32-x64-msvc@4.34.8': + '@rollup/rollup-win32-x64-msvc@4.34.9': optional: true '@rtsao/scc@1.1.0': {} '@rushstack/eslint-patch@1.10.5': {} - '@swc/core-darwin-arm64@1.11.5': + '@swc/core-darwin-arm64@1.11.8': optional: true - '@swc/core-darwin-x64@1.11.5': + '@swc/core-darwin-x64@1.11.8': optional: true - '@swc/core-linux-arm-gnueabihf@1.11.5': + '@swc/core-linux-arm-gnueabihf@1.11.8': optional: true - '@swc/core-linux-arm64-gnu@1.11.5': + '@swc/core-linux-arm64-gnu@1.11.8': optional: true - '@swc/core-linux-arm64-musl@1.11.5': + '@swc/core-linux-arm64-musl@1.11.8': optional: true - '@swc/core-linux-x64-gnu@1.11.5': + '@swc/core-linux-x64-gnu@1.11.8': optional: true - '@swc/core-linux-x64-musl@1.11.5': + '@swc/core-linux-x64-musl@1.11.8': optional: true - '@swc/core-win32-arm64-msvc@1.11.5': + '@swc/core-win32-arm64-msvc@1.11.8': optional: true - '@swc/core-win32-ia32-msvc@1.11.5': + '@swc/core-win32-ia32-msvc@1.11.8': optional: true - '@swc/core-win32-x64-msvc@1.11.5': + '@swc/core-win32-x64-msvc@1.11.8': optional: true - '@swc/core@1.11.5(@swc/helpers@0.5.12)': + '@swc/core@1.11.8(@swc/helpers@0.5.15)': dependencies: '@swc/counter': 0.1.3 '@swc/types': 0.1.19 optionalDependencies: - '@swc/core-darwin-arm64': 1.11.5 - '@swc/core-darwin-x64': 1.11.5 - '@swc/core-linux-arm-gnueabihf': 1.11.5 - '@swc/core-linux-arm64-gnu': 1.11.5 - '@swc/core-linux-arm64-musl': 1.11.5 - '@swc/core-linux-x64-gnu': 1.11.5 - '@swc/core-linux-x64-musl': 1.11.5 - '@swc/core-win32-arm64-msvc': 1.11.5 - '@swc/core-win32-ia32-msvc': 1.11.5 - '@swc/core-win32-x64-msvc': 1.11.5 - '@swc/helpers': 0.5.12 + '@swc/core-darwin-arm64': 1.11.8 + '@swc/core-darwin-x64': 1.11.8 + '@swc/core-linux-arm-gnueabihf': 1.11.8 + '@swc/core-linux-arm64-gnu': 1.11.8 + '@swc/core-linux-arm64-musl': 1.11.8 + '@swc/core-linux-x64-gnu': 1.11.8 + '@swc/core-linux-x64-musl': 1.11.8 + '@swc/core-win32-arm64-msvc': 1.11.8 + '@swc/core-win32-ia32-msvc': 1.11.8 + '@swc/core-win32-x64-msvc': 1.11.8 + '@swc/helpers': 0.5.15 '@swc/counter@0.1.3': {} - '@swc/helpers@0.5.12': + '@swc/helpers@0.5.15': dependencies: tslib: 2.8.1 - optional: true - - '@swc/helpers@0.5.5': - dependencies: - '@swc/counter': 0.1.3 - tslib: 2.8.1 '@swc/types@0.1.19': dependencies: @@ -3757,10 +3628,6 @@ snapshots: '@types/json5@0.0.29': {} - '@types/node@20.17.21': - dependencies: - undici-types: 6.19.8 - '@types/node@22.13.9': dependencies: undici-types: 6.20.0 @@ -3779,33 +3646,15 @@ snapshots: '@types/unist@2.0.11': {} - '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2))(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2)': - dependencies: - '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 7.18.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2) - '@typescript-eslint/scope-manager': 7.18.0 - '@typescript-eslint/type-utils': 7.18.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2) - '@typescript-eslint/utils': 7.18.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2) - '@typescript-eslint/visitor-keys': 7.18.0 - eslint: 9.21.0(jiti@1.21.7) - graphemer: 1.4.0 - ignore: 5.3.2 - natural-compare: 1.4.0 - ts-api-utils: 1.4.3(typescript@5.8.2) - optionalDependencies: - typescript: 5.8.2 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/eslint-plugin@8.25.0(@typescript-eslint/parser@8.25.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2))(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2)': + '@typescript-eslint/eslint-plugin@8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@1.21.7))(typescript@5.8.2))(eslint@9.22.0(jiti@1.21.7))(typescript@5.8.2)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.25.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2) - '@typescript-eslint/scope-manager': 8.25.0 - '@typescript-eslint/type-utils': 8.25.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2) - '@typescript-eslint/utils': 8.25.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2) - '@typescript-eslint/visitor-keys': 8.25.0 - eslint: 9.21.0(jiti@1.21.7) + '@typescript-eslint/parser': 8.26.0(eslint@9.22.0(jiti@1.21.7))(typescript@5.8.2) + '@typescript-eslint/scope-manager': 8.26.0 + '@typescript-eslint/type-utils': 8.26.0(eslint@9.22.0(jiti@1.21.7))(typescript@5.8.2) + '@typescript-eslint/utils': 8.26.0(eslint@9.22.0(jiti@1.21.7))(typescript@5.8.2) + '@typescript-eslint/visitor-keys': 8.26.0 + eslint: 9.22.0(jiti@1.21.7) graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 @@ -3814,87 +3663,40 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@7.18.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2)': + '@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@1.21.7))(typescript@5.8.2)': dependencies: - '@typescript-eslint/scope-manager': 7.18.0 - '@typescript-eslint/types': 7.18.0 - '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.8.2) - '@typescript-eslint/visitor-keys': 7.18.0 + '@typescript-eslint/scope-manager': 8.26.0 + '@typescript-eslint/types': 8.26.0 + '@typescript-eslint/typescript-estree': 8.26.0(typescript@5.8.2) + '@typescript-eslint/visitor-keys': 8.26.0 debug: 4.4.0 - eslint: 9.21.0(jiti@1.21.7) - optionalDependencies: - typescript: 5.8.2 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/parser@8.25.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2)': - dependencies: - '@typescript-eslint/scope-manager': 8.25.0 - '@typescript-eslint/types': 8.25.0 - '@typescript-eslint/typescript-estree': 8.25.0(typescript@5.8.2) - '@typescript-eslint/visitor-keys': 8.25.0 - debug: 4.4.0 - eslint: 9.21.0(jiti@1.21.7) + eslint: 9.22.0(jiti@1.21.7) typescript: 5.8.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@7.18.0': + '@typescript-eslint/scope-manager@8.26.0': dependencies: - '@typescript-eslint/types': 7.18.0 - '@typescript-eslint/visitor-keys': 7.18.0 - - '@typescript-eslint/scope-manager@8.25.0': - dependencies: - '@typescript-eslint/types': 8.25.0 - '@typescript-eslint/visitor-keys': 8.25.0 - - '@typescript-eslint/type-utils@7.18.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2)': - dependencies: - '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.8.2) - '@typescript-eslint/utils': 7.18.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2) - debug: 4.4.0 - eslint: 9.21.0(jiti@1.21.7) - ts-api-utils: 1.4.3(typescript@5.8.2) - optionalDependencies: - typescript: 5.8.2 - transitivePeerDependencies: - - supports-color + '@typescript-eslint/types': 8.26.0 + '@typescript-eslint/visitor-keys': 8.26.0 - '@typescript-eslint/type-utils@8.25.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2)': + '@typescript-eslint/type-utils@8.26.0(eslint@9.22.0(jiti@1.21.7))(typescript@5.8.2)': dependencies: - '@typescript-eslint/typescript-estree': 8.25.0(typescript@5.8.2) - '@typescript-eslint/utils': 8.25.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2) + '@typescript-eslint/typescript-estree': 8.26.0(typescript@5.8.2) + '@typescript-eslint/utils': 8.26.0(eslint@9.22.0(jiti@1.21.7))(typescript@5.8.2) debug: 4.4.0 - eslint: 9.21.0(jiti@1.21.7) + eslint: 9.22.0(jiti@1.21.7) ts-api-utils: 2.0.1(typescript@5.8.2) typescript: 5.8.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@7.18.0': {} - - '@typescript-eslint/types@8.25.0': {} - - '@typescript-eslint/typescript-estree@7.18.0(typescript@5.8.2)': - dependencies: - '@typescript-eslint/types': 7.18.0 - '@typescript-eslint/visitor-keys': 7.18.0 - debug: 4.4.0 - globby: 11.1.0 - is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.7.1 - ts-api-utils: 1.4.3(typescript@5.8.2) - optionalDependencies: - typescript: 5.8.2 - transitivePeerDependencies: - - supports-color + '@typescript-eslint/types@8.26.0': {} - '@typescript-eslint/typescript-estree@8.25.0(typescript@5.8.2)': + '@typescript-eslint/typescript-estree@8.26.0(typescript@5.8.2)': dependencies: - '@typescript-eslint/types': 8.25.0 - '@typescript-eslint/visitor-keys': 8.25.0 + '@typescript-eslint/types': 8.26.0 + '@typescript-eslint/visitor-keys': 8.26.0 debug: 4.4.0 fast-glob: 3.3.3 is-glob: 4.0.3 @@ -3905,61 +3707,45 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@7.18.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2)': + '@typescript-eslint/utils@8.26.0(eslint@9.22.0(jiti@1.21.7))(typescript@5.8.2)': dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.21.0(jiti@1.21.7)) - '@typescript-eslint/scope-manager': 7.18.0 - '@typescript-eslint/types': 7.18.0 - '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.8.2) - eslint: 9.21.0(jiti@1.21.7) - transitivePeerDependencies: - - supports-color - - typescript - - '@typescript-eslint/utils@8.25.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2)': - dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.21.0(jiti@1.21.7)) - '@typescript-eslint/scope-manager': 8.25.0 - '@typescript-eslint/types': 8.25.0 - '@typescript-eslint/typescript-estree': 8.25.0(typescript@5.8.2) - eslint: 9.21.0(jiti@1.21.7) + '@eslint-community/eslint-utils': 4.4.1(eslint@9.22.0(jiti@1.21.7)) + '@typescript-eslint/scope-manager': 8.26.0 + '@typescript-eslint/types': 8.26.0 + '@typescript-eslint/typescript-estree': 8.26.0(typescript@5.8.2) + eslint: 9.22.0(jiti@1.21.7) typescript: 5.8.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@7.18.0': - dependencies: - '@typescript-eslint/types': 7.18.0 - eslint-visitor-keys: 3.4.3 - - '@typescript-eslint/visitor-keys@8.25.0': + '@typescript-eslint/visitor-keys@8.26.0': dependencies: - '@typescript-eslint/types': 8.25.0 + '@typescript-eslint/types': 8.26.0 eslint-visitor-keys: 4.2.0 - '@vitejs/plugin-react-swc@3.8.0(@swc/helpers@0.5.12)(vite@5.4.14(@types/node@20.17.21))': + '@vitejs/plugin-react-swc@3.8.0(@swc/helpers@0.5.15)(vite@5.4.14(@types/node@22.13.9))': dependencies: - '@swc/core': 1.11.5(@swc/helpers@0.5.12) - vite: 5.4.14(@types/node@20.17.21) + '@swc/core': 1.11.8(@swc/helpers@0.5.15) + vite: 5.4.14(@types/node@22.13.9) transitivePeerDependencies: - '@swc/helpers' - '@vitejs/plugin-react@4.3.4(vite@5.4.14(@types/node@20.17.21))': + '@vitejs/plugin-react@4.3.4(vite@5.4.14(@types/node@22.13.9))': dependencies: '@babel/core': 7.26.9 '@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.9) '@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.9) '@types/babel__core': 7.20.5 react-refresh: 0.14.2 - vite: 5.4.14(@types/node@20.17.21) + vite: 5.4.14(@types/node@22.13.9) transitivePeerDependencies: - supports-color - acorn-jsx@5.3.2(acorn@8.14.0): + acorn-jsx@5.3.2(acorn@8.14.1): dependencies: - acorn: 8.14.0 + acorn: 8.14.1 - acorn@8.14.0: {} + acorn@8.14.1: {} ajv@6.12.6: dependencies: @@ -3993,7 +3779,7 @@ snapshots: array-buffer-byte-length@1.0.2: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 is-array-buffer: 3.0.5 array-includes@3.1.8: @@ -4005,8 +3791,6 @@ snapshots: get-intrinsic: 1.3.0 is-string: 1.1.1 - array-union@2.1.0: {} - array.prototype.findlast@1.2.5: dependencies: call-bind: 1.0.8 @@ -4064,7 +3848,7 @@ snapshots: autoprefixer@10.4.20(postcss@8.5.3): dependencies: browserslist: 4.24.4 - caniuse-lite: 1.0.30001701 + caniuse-lite: 1.0.30001702 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 @@ -4075,7 +3859,7 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 - axe-core@4.10.2: {} + axe-core@4.10.3: {} axobject-query@4.1.0: {} @@ -4098,8 +3882,8 @@ snapshots: browserslist@4.24.4: dependencies: - caniuse-lite: 1.0.30001701 - electron-to-chromium: 1.5.109 + caniuse-lite: 1.0.30001702 + electron-to-chromium: 1.5.113 node-releases: 2.0.19 update-browserslist-db: 1.1.3(browserslist@4.24.4) @@ -4126,7 +3910,7 @@ snapshots: get-intrinsic: 1.3.0 set-function-length: 1.2.2 - call-bound@1.0.3: + call-bound@1.0.4: dependencies: call-bind-apply-helpers: 1.0.2 get-intrinsic: 1.3.0 @@ -4135,7 +3919,7 @@ snapshots: camelcase-css@2.0.1: {} - caniuse-lite@1.0.30001701: {} + caniuse-lite@1.0.30001702: {} chalk@4.1.2: dependencies: @@ -4180,20 +3964,30 @@ snapshots: color-name@1.1.4: {} + color-string@1.9.1: + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + optional: true + + color@4.2.3: + dependencies: + color-convert: 2.0.1 + color-string: 1.9.1 + optional: true + comma-separated-tokens@1.0.8: {} commander@4.1.1: {} concat-map@0.0.1: {} - concurrently@8.2.2: + concurrently@9.1.2: dependencies: chalk: 4.1.2 - date-fns: 2.30.0 lodash: 4.17.21 rxjs: 7.8.2 shell-quote: 1.8.2 - spawn-command: 0.0.2 supports-color: 8.1.1 tree-kill: 1.2.2 yargs: 17.7.2 @@ -4220,26 +4014,22 @@ snapshots: data-view-buffer@1.0.2: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 es-errors: 1.3.0 is-data-view: 1.0.2 data-view-byte-length@1.0.2: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 es-errors: 1.3.0 is-data-view: 1.0.2 data-view-byte-offset@1.0.1: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 es-errors: 1.3.0 is-data-view: 1.0.2 - date-fns@2.30.0: - dependencies: - '@babel/runtime': 7.26.9 - debug@3.2.7: dependencies: ms: 2.1.3 @@ -4262,11 +4052,10 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 - didyoumean@1.2.2: {} + detect-libc@2.0.3: + optional: true - dir-glob@3.0.1: - dependencies: - path-type: 4.0.0 + didyoumean@1.2.2: {} dlv@1.1.3: {} @@ -4284,7 +4073,7 @@ snapshots: eastasianwidth@0.2.0: {} - electron-to-chromium@1.5.109: {} + electron-to-chromium@1.5.113: {} emoji-regex@8.0.0: {} @@ -4301,7 +4090,7 @@ snapshots: arraybuffer.prototype.slice: 1.0.4 available-typed-arrays: 1.0.7 call-bind: 1.0.8 - call-bound: 1.0.3 + call-bound: 1.0.4 data-view-buffer: 1.0.2 data-view-byte-length: 1.0.2 data-view-byte-offset: 1.0.1 @@ -4356,7 +4145,7 @@ snapshots: es-iterator-helpers@1.2.1: dependencies: call-bind: 1.0.8 - call-bound: 1.0.3 + call-bound: 1.0.4 define-properties: 1.2.1 es-abstract: 1.23.9 es-errors: 1.3.0 @@ -4419,33 +4208,6 @@ snapshots: '@esbuild/win32-ia32': 0.21.5 '@esbuild/win32-x64': 0.21.5 - esbuild@0.23.1: - optionalDependencies: - '@esbuild/aix-ppc64': 0.23.1 - '@esbuild/android-arm': 0.23.1 - '@esbuild/android-arm64': 0.23.1 - '@esbuild/android-x64': 0.23.1 - '@esbuild/darwin-arm64': 0.23.1 - '@esbuild/darwin-x64': 0.23.1 - '@esbuild/freebsd-arm64': 0.23.1 - '@esbuild/freebsd-x64': 0.23.1 - '@esbuild/linux-arm': 0.23.1 - '@esbuild/linux-arm64': 0.23.1 - '@esbuild/linux-ia32': 0.23.1 - '@esbuild/linux-loong64': 0.23.1 - '@esbuild/linux-mips64el': 0.23.1 - '@esbuild/linux-ppc64': 0.23.1 - '@esbuild/linux-riscv64': 0.23.1 - '@esbuild/linux-s390x': 0.23.1 - '@esbuild/linux-x64': 0.23.1 - '@esbuild/netbsd-x64': 0.23.1 - '@esbuild/openbsd-arm64': 0.23.1 - '@esbuild/openbsd-x64': 0.23.1 - '@esbuild/sunos-x64': 0.23.1 - '@esbuild/win32-arm64': 0.23.1 - '@esbuild/win32-ia32': 0.23.1 - '@esbuild/win32-x64': 0.23.1 - esbuild@0.25.0: optionalDependencies: '@esbuild/aix-ppc64': 0.25.0 @@ -4478,19 +4240,19 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-config-next@14.2.24(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2): + eslint-config-next@15.2.1(eslint@9.22.0(jiti@1.21.7))(typescript@5.8.2): dependencies: - '@next/eslint-plugin-next': 14.2.24 + '@next/eslint-plugin-next': 15.2.1 '@rushstack/eslint-patch': 1.10.5 - '@typescript-eslint/eslint-plugin': 8.25.0(@typescript-eslint/parser@8.25.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2))(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2) - '@typescript-eslint/parser': 8.25.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2) - eslint: 9.21.0(jiti@1.21.7) + '@typescript-eslint/eslint-plugin': 8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@1.21.7))(typescript@5.8.2))(eslint@9.22.0(jiti@1.21.7))(typescript@5.8.2) + '@typescript-eslint/parser': 8.26.0(eslint@9.22.0(jiti@1.21.7))(typescript@5.8.2) + eslint: 9.22.0(jiti@1.21.7) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.8.3(eslint-plugin-import@2.31.0)(eslint@9.21.0(jiti@1.21.7)) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.25.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2))(eslint-import-resolver-typescript@3.8.3)(eslint@9.21.0(jiti@1.21.7)) - eslint-plugin-jsx-a11y: 6.10.2(eslint@9.21.0(jiti@1.21.7)) - eslint-plugin-react: 7.35.0(eslint@9.21.0(jiti@1.21.7)) - eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@9.21.0(jiti@1.21.7)) + eslint-import-resolver-typescript: 3.8.3(eslint-plugin-import@2.31.0)(eslint@9.22.0(jiti@1.21.7)) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@1.21.7))(typescript@5.8.2))(eslint-import-resolver-typescript@3.8.3)(eslint@9.22.0(jiti@1.21.7)) + eslint-plugin-jsx-a11y: 6.10.2(eslint@9.22.0(jiti@1.21.7)) + eslint-plugin-react: 7.37.4(eslint@9.22.0(jiti@1.21.7)) + eslint-plugin-react-hooks: 5.2.0(eslint@9.22.0(jiti@1.21.7)) optionalDependencies: typescript: 5.8.2 transitivePeerDependencies: @@ -4498,14 +4260,14 @@ snapshots: - eslint-plugin-import-x - supports-color - eslint-config-prettier@9.1.0(eslint@9.21.0(jiti@1.21.7)): + eslint-config-prettier@10.1.1(eslint@9.22.0(jiti@1.21.7)): dependencies: - eslint: 9.21.0(jiti@1.21.7) + eslint: 9.22.0(jiti@1.21.7) - eslint-config-turbo@2.4.4(eslint@9.21.0(jiti@1.21.7))(turbo@2.4.4): + eslint-config-turbo@2.4.4(eslint@9.22.0(jiti@1.21.7))(turbo@2.4.4): dependencies: - eslint: 9.21.0(jiti@1.21.7) - eslint-plugin-turbo: 2.4.4(eslint@9.21.0(jiti@1.21.7))(turbo@2.4.4) + eslint: 9.22.0(jiti@1.21.7) + eslint-plugin-turbo: 2.4.4(eslint@9.22.0(jiti@1.21.7))(turbo@2.4.4) turbo: 2.4.4 eslint-import-resolver-node@0.3.9: @@ -4516,33 +4278,33 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.8.3(eslint-plugin-import@2.31.0)(eslint@9.21.0(jiti@1.21.7)): + eslint-import-resolver-typescript@3.8.3(eslint-plugin-import@2.31.0)(eslint@9.22.0(jiti@1.21.7)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.0 enhanced-resolve: 5.18.1 - eslint: 9.21.0(jiti@1.21.7) + eslint: 9.22.0(jiti@1.21.7) get-tsconfig: 4.10.0 is-bun-module: 1.3.0 stable-hash: 0.0.4 tinyglobby: 0.2.12 optionalDependencies: - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.25.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2))(eslint-import-resolver-typescript@3.8.3)(eslint@9.21.0(jiti@1.21.7)) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@1.21.7))(typescript@5.8.2))(eslint-import-resolver-typescript@3.8.3)(eslint@9.22.0(jiti@1.21.7)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.25.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.8.3)(eslint@9.21.0(jiti@1.21.7)): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@1.21.7))(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.8.3)(eslint@9.22.0(jiti@1.21.7)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.25.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2) - eslint: 9.21.0(jiti@1.21.7) + '@typescript-eslint/parser': 8.26.0(eslint@9.22.0(jiti@1.21.7))(typescript@5.8.2) + eslint: 9.22.0(jiti@1.21.7) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.8.3(eslint-plugin-import@2.31.0)(eslint@9.21.0(jiti@1.21.7)) + eslint-import-resolver-typescript: 3.8.3(eslint-plugin-import@2.31.0)(eslint@9.22.0(jiti@1.21.7)) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.25.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2))(eslint-import-resolver-typescript@3.8.3)(eslint@9.21.0(jiti@1.21.7)): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@1.21.7))(typescript@5.8.2))(eslint-import-resolver-typescript@3.8.3)(eslint@9.22.0(jiti@1.21.7)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.8 @@ -4551,9 +4313,9 @@ snapshots: array.prototype.flatmap: 1.3.3 debug: 3.2.7 doctrine: 2.1.0 - eslint: 9.21.0(jiti@1.21.7) + eslint: 9.22.0(jiti@1.21.7) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.25.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.8.3)(eslint@9.21.0(jiti@1.21.7)) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@1.21.7))(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.8.3)(eslint@9.22.0(jiti@1.21.7)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -4565,23 +4327,23 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.25.0(eslint@9.21.0(jiti@1.21.7))(typescript@5.8.2) + '@typescript-eslint/parser': 8.26.0(eslint@9.22.0(jiti@1.21.7))(typescript@5.8.2) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-jsx-a11y@6.10.2(eslint@9.21.0(jiti@1.21.7)): + eslint-plugin-jsx-a11y@6.10.2(eslint@9.22.0(jiti@1.21.7)): dependencies: aria-query: 5.3.2 array-includes: 3.1.8 array.prototype.flatmap: 1.3.3 ast-types-flow: 0.0.8 - axe-core: 4.10.2 + axe-core: 4.10.3 axobject-query: 4.1.0 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 - eslint: 9.21.0(jiti@1.21.7) + eslint: 9.22.0(jiti@1.21.7) hasown: 2.0.2 jsx-ast-utils: 3.3.5 language-tags: 1.0.9 @@ -4590,19 +4352,15 @@ snapshots: safe-regex-test: 1.1.0 string.prototype.includes: 2.0.1 - eslint-plugin-react-hooks@4.6.2(eslint@9.21.0(jiti@1.21.7)): - dependencies: - eslint: 9.21.0(jiti@1.21.7) - - eslint-plugin-react-hooks@5.0.0-canary-7118f5dd7-20230705(eslint@9.21.0(jiti@1.21.7)): + eslint-plugin-react-hooks@5.2.0(eslint@9.22.0(jiti@1.21.7)): dependencies: - eslint: 9.21.0(jiti@1.21.7) + eslint: 9.22.0(jiti@1.21.7) - eslint-plugin-react-refresh@0.4.19(eslint@9.21.0(jiti@1.21.7)): + eslint-plugin-react-refresh@0.4.19(eslint@9.22.0(jiti@1.21.7)): dependencies: - eslint: 9.21.0(jiti@1.21.7) + eslint: 9.22.0(jiti@1.21.7) - eslint-plugin-react@7.35.0(eslint@9.21.0(jiti@1.21.7)): + eslint-plugin-react@7.37.4(eslint@9.22.0(jiti@1.21.7)): dependencies: array-includes: 3.1.8 array.prototype.findlast: 1.2.5 @@ -4610,7 +4368,7 @@ snapshots: array.prototype.tosorted: 1.1.4 doctrine: 2.1.0 es-iterator-helpers: 1.2.1 - eslint: 9.21.0(jiti@1.21.7) + eslint: 9.22.0(jiti@1.21.7) estraverse: 5.3.0 hasown: 2.0.2 jsx-ast-utils: 3.3.5 @@ -4624,13 +4382,13 @@ snapshots: string.prototype.matchall: 4.0.12 string.prototype.repeat: 1.0.0 - eslint-plugin-turbo@2.4.4(eslint@9.21.0(jiti@1.21.7))(turbo@2.4.4): + eslint-plugin-turbo@2.4.4(eslint@9.22.0(jiti@1.21.7))(turbo@2.4.4): dependencies: dotenv: 16.0.3 - eslint: 9.21.0(jiti@1.21.7) + eslint: 9.22.0(jiti@1.21.7) turbo: 2.4.4 - eslint-scope@8.2.0: + eslint-scope@8.3.0: dependencies: esrecurse: 4.3.0 estraverse: 5.3.0 @@ -4639,14 +4397,15 @@ snapshots: eslint-visitor-keys@4.2.0: {} - eslint@9.21.0(jiti@1.21.7): + eslint@9.22.0(jiti@1.21.7): dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.21.0(jiti@1.21.7)) + '@eslint-community/eslint-utils': 4.4.1(eslint@9.22.0(jiti@1.21.7)) '@eslint-community/regexpp': 4.12.1 '@eslint/config-array': 0.19.2 + '@eslint/config-helpers': 0.1.0 '@eslint/core': 0.12.0 '@eslint/eslintrc': 3.3.0 - '@eslint/js': 9.21.0 + '@eslint/js': 9.22.0 '@eslint/plugin-kit': 0.2.7 '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 @@ -4658,7 +4417,7 @@ snapshots: cross-spawn: 7.0.6 debug: 4.4.0 escape-string-regexp: 4.0.0 - eslint-scope: 8.2.0 + eslint-scope: 8.3.0 eslint-visitor-keys: 4.2.0 espree: 10.3.0 esquery: 1.6.0 @@ -4682,8 +4441,8 @@ snapshots: espree@10.3.0: dependencies: - acorn: 8.14.0 - acorn-jsx: 5.3.2(acorn@8.14.0) + acorn: 8.14.1 + acorn-jsx: 5.3.2(acorn@8.14.1) eslint-visitor-keys: 4.2.0 esquery@1.6.0: @@ -4700,6 +4459,14 @@ snapshots: fast-deep-equal@3.1.3: {} + fast-glob@3.3.1: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + fast-glob@3.3.3: dependencies: '@nodelib/fs.stat': 2.0.5 @@ -4757,10 +4524,10 @@ snapshots: fraction.js@4.3.7: {} - framer-motion@12.4.9(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + framer-motion@12.4.10(react-dom@19.0.0(react@19.0.0))(react@19.0.0): dependencies: - motion-dom: 12.4.5 - motion-utils: 12.0.0 + motion-dom: 12.4.10 + motion-utils: 12.4.10 tslib: 2.8.1 optionalDependencies: react: 19.0.0 @@ -4774,7 +4541,7 @@ snapshots: function.prototype.name@1.1.8: dependencies: call-bind: 1.0.8 - call-bound: 1.0.3 + call-bound: 1.0.4 define-properties: 1.2.1 functions-have-names: 1.2.3 hasown: 2.0.2 @@ -4806,7 +4573,7 @@ snapshots: get-symbol-description@1.1.0: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 es-errors: 1.3.0 get-intrinsic: 1.3.0 @@ -4822,14 +4589,6 @@ snapshots: dependencies: is-glob: 4.0.3 - glob@10.3.10: - dependencies: - foreground-child: 3.3.1 - jackspeak: 2.3.6 - minimatch: 9.0.5 - minipass: 7.1.2 - path-scurry: 1.11.1 - glob@10.4.5: dependencies: foreground-child: 3.3.1 @@ -4848,15 +4607,6 @@ snapshots: define-properties: 1.2.1 gopd: 1.2.0 - globby@11.1.0: - dependencies: - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.3.3 - ignore: 5.3.2 - merge2: 1.4.1 - slash: 3.0.0 - gopd@1.2.0: {} graceful-fs@4.2.11: {} @@ -4924,13 +4674,16 @@ snapshots: is-array-buffer@3.0.5: dependencies: call-bind: 1.0.8 - call-bound: 1.0.3 + call-bound: 1.0.4 get-intrinsic: 1.3.0 + is-arrayish@0.3.2: + optional: true + is-async-function@2.1.1: dependencies: async-function: 1.0.0 - call-bound: 1.0.3 + call-bound: 1.0.4 get-proto: 1.0.1 has-tostringtag: 1.0.2 safe-regex-test: 1.1.0 @@ -4945,7 +4698,7 @@ snapshots: is-boolean-object@1.2.2: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 has-tostringtag: 1.0.2 is-bun-module@1.3.0: @@ -4960,13 +4713,13 @@ snapshots: is-data-view@1.0.2: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 get-intrinsic: 1.3.0 is-typed-array: 1.1.15 is-date-object@1.1.0: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 has-tostringtag: 1.0.2 is-decimal@1.0.4: {} @@ -4975,13 +4728,13 @@ snapshots: is-finalizationregistry@1.1.1: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 is-fullwidth-code-point@3.0.0: {} is-generator-function@1.1.0: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 get-proto: 1.0.1 has-tostringtag: 1.0.2 safe-regex-test: 1.1.0 @@ -4996,14 +4749,14 @@ snapshots: is-number-object@1.1.1: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 has-tostringtag: 1.0.2 is-number@7.0.0: {} is-regex@1.2.1: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 gopd: 1.2.0 has-tostringtag: 1.0.2 hasown: 2.0.2 @@ -5012,16 +4765,16 @@ snapshots: is-shared-array-buffer@1.0.4: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 is-string@1.1.1: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 has-tostringtag: 1.0.2 is-symbol@1.1.1: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 has-symbols: 1.1.0 safe-regex-test: 1.1.0 @@ -5033,11 +4786,11 @@ snapshots: is-weakref@1.1.1: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 is-weakset@2.0.4: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 get-intrinsic: 1.3.0 isarray@2.0.5: {} @@ -5053,12 +4806,6 @@ snapshots: has-symbols: 1.1.0 set-function-name: 2.0.2 - jackspeak@2.3.6: - dependencies: - '@isaacs/cliui': 8.0.2 - optionalDependencies: - '@pkgjs/parseargs': 0.11.0 - jackspeak@3.4.3: dependencies: '@isaacs/cliui': 8.0.2 @@ -5146,7 +4893,7 @@ snapshots: dependencies: yallist: 3.1.1 - lucide-react@0.477.0(react@19.0.0): + lucide-react@0.479.0(react@19.0.0): dependencies: react: 19.0.0 @@ -5171,15 +4918,15 @@ snapshots: minipass@7.1.2: {} - motion-dom@12.4.5: + motion-dom@12.4.10: dependencies: - motion-utils: 12.0.0 + motion-utils: 12.4.10 - motion-utils@12.0.0: {} + motion-utils@12.4.10: {} - motion@12.4.9(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + motion@12.4.10(react-dom@19.0.0(react@19.0.0))(react@19.0.0): dependencies: - framer-motion: 12.4.9(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + framer-motion: 12.4.10(react-dom@19.0.0(react@19.0.0))(react@19.0.0) tslib: 2.8.1 optionalDependencies: react: 19.0.0 @@ -5193,33 +4940,33 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 - nanoid@3.3.8: {} + nanoid@3.3.9: {} - nanoid@5.1.2: {} + nanoid@5.1.3: {} natural-compare@1.4.0: {} - next@14.2.24(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + next@15.2.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0): dependencies: - '@next/env': 14.2.24 - '@swc/helpers': 0.5.5 + '@next/env': 15.2.1 + '@swc/counter': 0.1.3 + '@swc/helpers': 0.5.15 busboy: 1.6.0 - caniuse-lite: 1.0.30001701 - graceful-fs: 4.2.11 + caniuse-lite: 1.0.30001702 postcss: 8.4.31 react: 19.0.0 react-dom: 19.0.0(react@19.0.0) - styled-jsx: 5.1.1(react@19.0.0) + styled-jsx: 5.1.6(react@19.0.0) optionalDependencies: - '@next/swc-darwin-arm64': 14.2.24 - '@next/swc-darwin-x64': 14.2.24 - '@next/swc-linux-arm64-gnu': 14.2.24 - '@next/swc-linux-arm64-musl': 14.2.24 - '@next/swc-linux-x64-gnu': 14.2.24 - '@next/swc-linux-x64-musl': 14.2.24 - '@next/swc-win32-arm64-msvc': 14.2.24 - '@next/swc-win32-ia32-msvc': 14.2.24 - '@next/swc-win32-x64-msvc': 14.2.24 + '@next/swc-darwin-arm64': 15.2.1 + '@next/swc-darwin-x64': 15.2.1 + '@next/swc-linux-arm64-gnu': 15.2.1 + '@next/swc-linux-arm64-musl': 15.2.1 + '@next/swc-linux-x64-gnu': 15.2.1 + '@next/swc-linux-x64-musl': 15.2.1 + '@next/swc-win32-arm64-msvc': 15.2.1 + '@next/swc-win32-x64-msvc': 15.2.1 + sharp: 0.33.5 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros @@ -5241,7 +4988,7 @@ snapshots: object.assign@4.1.7: dependencies: call-bind: 1.0.8 - call-bound: 1.0.3 + call-bound: 1.0.4 define-properties: 1.2.1 es-object-atoms: 1.1.1 has-symbols: 1.1.0 @@ -5269,7 +5016,7 @@ snapshots: object.values@1.2.1: dependencies: call-bind: 1.0.8 - call-bound: 1.0.3 + call-bound: 1.0.4 define-properties: 1.2.1 es-object-atoms: 1.1.1 @@ -5322,8 +5069,6 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.2 - path-type@4.0.0: {} - picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -5377,19 +5122,19 @@ snapshots: postcss@8.4.31: dependencies: - nanoid: 3.3.8 + nanoid: 3.3.9 picocolors: 1.1.1 source-map-js: 1.2.1 postcss@8.5.3: dependencies: - nanoid: 3.3.8 + nanoid: 3.3.9 picocolors: 1.1.1 source-map-js: 1.2.1 prelude-ls@1.2.1: {} - prettier@3.5.2: {} + prettier@3.5.3: {} prismjs@1.27.0: {} @@ -5490,29 +5235,29 @@ snapshots: reusify@1.1.0: {} - rollup@4.34.8: + rollup@4.34.9: dependencies: '@types/estree': 1.0.6 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.34.8 - '@rollup/rollup-android-arm64': 4.34.8 - '@rollup/rollup-darwin-arm64': 4.34.8 - '@rollup/rollup-darwin-x64': 4.34.8 - '@rollup/rollup-freebsd-arm64': 4.34.8 - '@rollup/rollup-freebsd-x64': 4.34.8 - '@rollup/rollup-linux-arm-gnueabihf': 4.34.8 - '@rollup/rollup-linux-arm-musleabihf': 4.34.8 - '@rollup/rollup-linux-arm64-gnu': 4.34.8 - '@rollup/rollup-linux-arm64-musl': 4.34.8 - '@rollup/rollup-linux-loongarch64-gnu': 4.34.8 - '@rollup/rollup-linux-powerpc64le-gnu': 4.34.8 - '@rollup/rollup-linux-riscv64-gnu': 4.34.8 - '@rollup/rollup-linux-s390x-gnu': 4.34.8 - '@rollup/rollup-linux-x64-gnu': 4.34.8 - '@rollup/rollup-linux-x64-musl': 4.34.8 - '@rollup/rollup-win32-arm64-msvc': 4.34.8 - '@rollup/rollup-win32-ia32-msvc': 4.34.8 - '@rollup/rollup-win32-x64-msvc': 4.34.8 + '@rollup/rollup-android-arm-eabi': 4.34.9 + '@rollup/rollup-android-arm64': 4.34.9 + '@rollup/rollup-darwin-arm64': 4.34.9 + '@rollup/rollup-darwin-x64': 4.34.9 + '@rollup/rollup-freebsd-arm64': 4.34.9 + '@rollup/rollup-freebsd-x64': 4.34.9 + '@rollup/rollup-linux-arm-gnueabihf': 4.34.9 + '@rollup/rollup-linux-arm-musleabihf': 4.34.9 + '@rollup/rollup-linux-arm64-gnu': 4.34.9 + '@rollup/rollup-linux-arm64-musl': 4.34.9 + '@rollup/rollup-linux-loongarch64-gnu': 4.34.9 + '@rollup/rollup-linux-powerpc64le-gnu': 4.34.9 + '@rollup/rollup-linux-riscv64-gnu': 4.34.9 + '@rollup/rollup-linux-s390x-gnu': 4.34.9 + '@rollup/rollup-linux-x64-gnu': 4.34.9 + '@rollup/rollup-linux-x64-musl': 4.34.9 + '@rollup/rollup-win32-arm64-msvc': 4.34.9 + '@rollup/rollup-win32-ia32-msvc': 4.34.9 + '@rollup/rollup-win32-x64-msvc': 4.34.9 fsevents: 2.3.3 run-parallel@1.2.0: @@ -5526,7 +5271,7 @@ snapshots: safe-array-concat@1.1.3: dependencies: call-bind: 1.0.8 - call-bound: 1.0.3 + call-bound: 1.0.4 get-intrinsic: 1.3.0 has-symbols: 1.1.0 isarray: 2.0.5 @@ -5538,7 +5283,7 @@ snapshots: safe-regex-test@1.1.0: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 es-errors: 1.3.0 is-regex: 1.2.1 @@ -5570,6 +5315,33 @@ snapshots: es-errors: 1.3.0 es-object-atoms: 1.1.1 + sharp@0.33.5: + dependencies: + color: 4.2.3 + detect-libc: 2.0.3 + semver: 7.7.1 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.33.5 + '@img/sharp-darwin-x64': 0.33.5 + '@img/sharp-libvips-darwin-arm64': 1.0.4 + '@img/sharp-libvips-darwin-x64': 1.0.4 + '@img/sharp-libvips-linux-arm': 1.0.5 + '@img/sharp-libvips-linux-arm64': 1.0.4 + '@img/sharp-libvips-linux-s390x': 1.0.4 + '@img/sharp-libvips-linux-x64': 1.0.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 + '@img/sharp-libvips-linuxmusl-x64': 1.0.4 + '@img/sharp-linux-arm': 0.33.5 + '@img/sharp-linux-arm64': 0.33.5 + '@img/sharp-linux-s390x': 0.33.5 + '@img/sharp-linux-x64': 0.33.5 + '@img/sharp-linuxmusl-arm64': 0.33.5 + '@img/sharp-linuxmusl-x64': 0.33.5 + '@img/sharp-wasm32': 0.33.5 + '@img/sharp-win32-ia32': 0.33.5 + '@img/sharp-win32-x64': 0.33.5 + optional: true + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -5585,14 +5357,14 @@ snapshots: side-channel-map@1.0.1: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 es-errors: 1.3.0 get-intrinsic: 1.3.0 object-inspect: 1.13.4 side-channel-weakmap@1.0.2: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 es-errors: 1.3.0 get-intrinsic: 1.3.0 object-inspect: 1.13.4 @@ -5608,7 +5380,10 @@ snapshots: signal-exit@4.1.0: {} - slash@3.0.0: {} + simple-swizzle@0.2.2: + dependencies: + is-arrayish: 0.3.2 + optional: true source-map-js@1.2.1: {} @@ -5618,8 +5393,6 @@ snapshots: space-separated-tokens@1.1.5: {} - spawn-command@0.0.2: {} - stable-hash@0.0.4: {} streamsearch@1.1.0: {} @@ -5645,7 +5418,7 @@ snapshots: string.prototype.matchall@4.0.12: dependencies: call-bind: 1.0.8 - call-bound: 1.0.3 + call-bound: 1.0.4 define-properties: 1.2.1 es-abstract: 1.23.9 es-errors: 1.3.0 @@ -5666,7 +5439,7 @@ snapshots: string.prototype.trim@1.2.10: dependencies: call-bind: 1.0.8 - call-bound: 1.0.3 + call-bound: 1.0.4 define-data-property: 1.1.4 define-properties: 1.2.1 es-abstract: 1.23.9 @@ -5676,7 +5449,7 @@ snapshots: string.prototype.trimend@1.0.9: dependencies: call-bind: 1.0.8 - call-bound: 1.0.3 + call-bound: 1.0.4 define-properties: 1.2.1 es-object-atoms: 1.1.1 @@ -5698,7 +5471,7 @@ snapshots: strip-json-comments@3.1.1: {} - styled-jsx@5.1.1(react@19.0.0): + styled-jsx@5.1.6(react@19.0.0): dependencies: client-only: 0.0.1 react: 19.0.0 @@ -5785,10 +5558,6 @@ snapshots: tree-kill@1.2.2: {} - ts-api-utils@1.4.3(typescript@5.8.2): - dependencies: - typescript: 5.8.2 - ts-api-utils@2.0.1(typescript@5.8.2): dependencies: typescript: 5.8.2 @@ -5804,7 +5573,7 @@ snapshots: tslib@2.8.1: {} - tsup@8.4.0(@swc/core@1.11.5(@swc/helpers@0.5.12))(jiti@1.21.7)(postcss@8.5.3)(typescript@5.8.2)(yaml@2.7.0): + tsup@8.4.0(@swc/core@1.11.8(@swc/helpers@0.5.15))(jiti@1.21.7)(postcss@8.5.3)(typescript@5.8.2)(yaml@2.7.0): dependencies: bundle-require: 5.1.0(esbuild@0.25.0) cac: 6.7.14 @@ -5816,14 +5585,14 @@ snapshots: picocolors: 1.1.1 postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.3)(yaml@2.7.0) resolve-from: 5.0.0 - rollup: 4.34.8 + rollup: 4.34.9 source-map: 0.8.0-beta.0 sucrase: 3.35.0 tinyexec: 0.3.2 tinyglobby: 0.2.12 tree-kill: 1.2.2 optionalDependencies: - '@swc/core': 1.11.5(@swc/helpers@0.5.12) + '@swc/core': 1.11.8(@swc/helpers@0.5.15) postcss: 8.5.3 typescript: 5.8.2 transitivePeerDependencies: @@ -5865,7 +5634,7 @@ snapshots: typed-array-buffer@1.0.3: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 es-errors: 1.3.0 is-typed-array: 1.1.15 @@ -5900,13 +5669,11 @@ snapshots: unbox-primitive@1.1.0: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 has-bigints: 1.1.0 has-symbols: 1.1.0 which-boxed-primitive: 1.1.1 - undici-types@6.19.8: {} - undici-types@6.20.0: {} update-browserslist-db@1.1.3(browserslist@4.24.4): @@ -5921,19 +5688,19 @@ snapshots: util-deprecate@1.0.2: {} - vite-plugin-singlefile@2.1.0(rollup@4.34.8)(vite@5.4.14(@types/node@20.17.21)): + vite-plugin-singlefile@2.1.0(rollup@4.34.9)(vite@5.4.14(@types/node@22.13.9)): dependencies: micromatch: 4.0.8 - rollup: 4.34.8 - vite: 5.4.14(@types/node@20.17.21) + rollup: 4.34.9 + vite: 5.4.14(@types/node@22.13.9) - vite@5.4.14(@types/node@20.17.21): + vite@5.4.14(@types/node@22.13.9): dependencies: esbuild: 0.21.5 postcss: 8.5.3 - rollup: 4.34.8 + rollup: 4.34.9 optionalDependencies: - '@types/node': 20.17.21 + '@types/node': 22.13.9 fsevents: 2.3.3 webidl-conversions@4.0.2: {} @@ -5954,7 +5721,7 @@ snapshots: which-builtin-type@1.2.1: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 function.prototype.name: 1.1.8 has-tostringtag: 1.0.2 is-async-function: 2.1.1 @@ -5979,7 +5746,7 @@ snapshots: dependencies: available-typed-arrays: 1.0.7 call-bind: 1.0.8 - call-bound: 1.0.3 + call-bound: 1.0.4 for-each: 0.3.5 gopd: 1.2.0 has-tostringtag: 1.0.2 From 8449fb2c4bc012006cd4a9c71d4ff74cd37498b0 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Fri, 7 Mar 2025 22:12:25 -0300 Subject: [PATCH 085/168] Add ring and custom border values to Tailwind --- .../tailwind/builderImpl/tailwindBorder.ts | 96 +++++++++++++++---- .../backend/src/tailwind/conversionTables.ts | 10 +- .../backend/src/tailwind/tailwindConfig.ts | 69 ++++++++----- .../src/tailwind/tailwindDefaultBuilder.ts | 11 ++- packages/plugin-ui/src/components/Preview.tsx | 3 +- packages/types/src/types.ts | 2 +- 6 files changed, 141 insertions(+), 50 deletions(-) diff --git a/packages/backend/src/tailwind/builderImpl/tailwindBorder.ts b/packages/backend/src/tailwind/builderImpl/tailwindBorder.ts index 8639702f..c90095bd 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindBorder.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindBorder.ts @@ -1,38 +1,92 @@ import { getCommonRadius } from "../../common/commonRadius"; import { commonStroke } from "../../common/commonStroke"; -import { nearestValue, pxToBorderRadius } from "../conversionTables"; +import { + nearestValue, + pxToBorderRadius, + pxToBorderWidth, + pxToRing, +} from "../conversionTables"; +import { numberToFixedString } from "../../common/numToAutoFixed"; +import { addWarning } from "../../common/commonConversionWarnings"; + +const getBorder = (weight: number, kind: string, isRing: boolean = false) => { + // Use ring utilities for outside strokes + if (isRing) { + // Special case: ring (without width) is 3px in Tailwind + if (weight === 3) { + return "ring"; + } + + const ringWidth = pxToRing(weight); + if (ringWidth === null) { + return `ring-[${numberToFixedString(weight)}px]`; + } else if (ringWidth === "3") { + // Ring is 3px + return `ring`; + } else { + return `ring-${ringWidth}`; + } + } + + // Special case: border (without width) is 1px in Tailwind + if (weight === 1) { + return `border${kind}`; + } + + // Use border utilities for default and inside strokes + const borderWidth = pxToBorderWidth(weight); + if (borderWidth === null) { + return `border${kind}-[${numberToFixedString(weight)}px]`; + } else if (borderWidth === "DEFAULT") { + // Border is 1px + return `border${kind}`; + } else { + return `border${kind}-${borderWidth}`; + } +}; /** * https://tailwindcss.com/docs/border-width/ * example: border-2 */ -export const tailwindBorderWidth = (node: SceneNode): string => { +export const tailwindBorderWidth = ( + node: SceneNode, +): { + isRing: boolean; + property: string; +} => { const commonBorder = commonStroke(node); if (!commonBorder) { - return ""; + return { + isRing: false, + property: "", + }; } - const getBorder = (weight: number, kind: string) => { - const allowedValues = [1, 2, 4, 8]; - console.log("weight", weight); - const nearest = nearestValue(weight, allowedValues); - console.log("nearest", nearest); - - if (nearest === 1) { - // special case - return `border${kind}`; - } else { - return `border${kind}-${nearest}`; - } - }; + // Check if stroke is outside + const isRing = + "strokeAlign" in node && + (node.strokeAlign === "OUTSIDE" || node.strokeAlign === "CENTER"); if ("all" in commonBorder) { if (commonBorder.all === 0) { - return ""; + return { + isRing: false, + property: "", + }; } - return getBorder(commonBorder.all, ""); + return { + isRing, + property: getBorder(commonBorder.all, "", isRing), + }; + } else { + addWarning( + 'Non-uniform borders are only supported with strokeAlign set to "inside". Will paint inside.', + ); } + // If borders are non-uniform, always use border utilities for better control + // regardless of whether the stroke is outside or not const comp = []; if (commonBorder.left !== 0) { comp.push(getBorder(commonBorder.left, "-l")); @@ -46,7 +100,11 @@ export const tailwindBorderWidth = (node: SceneNode): string => { if (commonBorder.bottom !== 0) { comp.push(getBorder(commonBorder.bottom, "-b")); } - return comp.join(" "); + + return { + isRing, + property: comp.join(" "), + }; }; /** diff --git a/packages/backend/src/tailwind/conversionTables.ts b/packages/backend/src/tailwind/conversionTables.ts index e8f591f2..ad947b17 100644 --- a/packages/backend/src/tailwind/conversionTables.ts +++ b/packages/backend/src/tailwind/conversionTables.ts @@ -78,7 +78,7 @@ const pxToTailwind = ( const keys = Object.keys(conversionMap).map((d) => +d); const convertedValue = exactValue(value, keys); - console.log("convertedValue", convertedValue); + console.log("convertedValue", convertedValue, value, keys); if (convertedValue) { return conversionMap[convertedValue]; @@ -110,6 +110,14 @@ export const pxToBorderRadius = (value: number): string => { return pxToRemToTailwind(value, config.borderRadius); }; +export const pxToBorderWidth = (value: number): string | null => { + return pxToTailwind(value, config.border); +}; + +export const pxToRing = (value: number): string | null => { + return pxToTailwind(value, config.ring); +}; + export const pxToBlur = (value: number): string | null => { return pxToTailwind(value, config.blur); }; diff --git a/packages/backend/src/tailwind/tailwindConfig.ts b/packages/backend/src/tailwind/tailwindConfig.ts index 5660ae41..15e11f11 100644 --- a/packages/backend/src/tailwind/tailwindConfig.ts +++ b/packages/backend/src/tailwind/tailwindConfig.ts @@ -1,5 +1,5 @@ const layoutSize = { - // '0: 0', + "0": "0", 1: "px", 2: "0.5", 4: "1", @@ -37,7 +37,7 @@ const layoutSize = { }; const borderRadius = { - // 0: "none", + 0: "none", 0.125: "sm", 0.25: "", 0.375: "md", @@ -356,37 +356,54 @@ const fontWeight: Record = { 600: "semibold", 700: "bold", 800: "extrabold", - 900: "black" + 900: "black", }; const fontFamily = { sans: [ - 'ui-sans-serif', - 'system-ui', - 'sans-serif', - 'Apple Color Emoji', - 'Segoe UI Emoji', - 'Segoe UI Symbol', - 'Noto Color Emoji' + "ui-sans-serif", + "system-ui", + "sans-serif", + "Apple Color Emoji", + "Segoe UI Emoji", + "Segoe UI Symbol", + "Noto Color Emoji", ], serif: [ - 'ui-serif', - 'Georgia', - 'Cambria', - 'Times New Roman', - 'Times', - 'serif' + "ui-serif", + "Georgia", + "Cambria", + "Times New Roman", + "Times", + "serif", ], mono: [ - 'ui-monospace', - 'SFMono-Regular', - 'Menlo', - 'Monaco', - 'Consolas', - 'Liberation Mono', - 'Courier New', - 'monospace' - ] + "ui-monospace", + "SFMono-Regular", + "Menlo", + "Monaco", + "Consolas", + "Liberation Mono", + "Courier New", + "monospace", + ], +}; + +const border = { + 0: "0", + 1: "1", + 2: "2", + 4: "4", + 8: "8", +}; + +const ring = { + 0: "0", + 1: "1", + 2: "2", + 3: "3", + 4: "4", + 8: "8", }; export const config = { @@ -400,4 +417,6 @@ export const config = { color, fontWeight, fontFamily, + border, + ring, }; diff --git a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts index 7ccdf3f7..8d46a52c 100644 --- a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts +++ b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts @@ -114,8 +114,9 @@ export class TailwindDefaultBuilder { border(): this { if ("strokes" in this.node) { - this.addAttributes(tailwindBorderWidth(this.node)); - this.customColor(this.node.strokes, "border"); + const { isRing, property } = tailwindBorderWidth(this.node); + this.addAttributes(property); + this.customColor(this.node.strokes, isRing ? "ring" : "border"); } return this; @@ -188,7 +189,11 @@ export class TailwindDefaultBuilder { // must be called before Position, because of the hasFixedSize attribute. size(): this { const { node, optimizeLayout, settings } = this; - const { width, height, constraints } = tailwindSizePartial(node, optimizeLayout, settings); + const { width, height, constraints } = tailwindSizePartial( + node, + optimizeLayout, + settings, + ); if (node.type === "TEXT") { switch (node.textAutoResize) { diff --git a/packages/plugin-ui/src/components/Preview.tsx b/packages/plugin-ui/src/components/Preview.tsx index 9cc7461b..447d01c1 100644 --- a/packages/plugin-ui/src/components/Preview.tsx +++ b/packages/plugin-ui/src/components/Preview.tsx @@ -169,7 +169,8 @@ const Preview: React.FC<{ {/* Footer with size info */}
- {props.htmlPreview.size.width}×{props.htmlPreview.size.height}px + {props.htmlPreview.size.width.toFixed(0)}× + {props.htmlPreview.size.height.toFixed(0)}px
{viewMode === "mobile" ? ( diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index 6cac3a3f..f5180d15 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -189,7 +189,7 @@ export interface TailwindTextConversion { contrastBlack: number; } -export type TailwindColorType = "text" | "bg" | "border" | "solid"; +export type TailwindColorType = "text" | "bg" | "border" | "ring"; export type SwiftUIModifier = [ string, From 3146e600264be0ea07f58d2b3741de00dbf47d04 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Fri, 7 Mar 2025 22:28:33 -0300 Subject: [PATCH 086/168] Add variable colors to Flutter --- .../src/flutter/builderImpl/flutterColor.ts | 47 +++++++++++++------ .../plugin-ui/src/codegenPreferenceOptions.ts | 2 +- .../plugin-ui/src/components/CodePanel.tsx | 1 - 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/packages/backend/src/flutter/builderImpl/flutterColor.ts b/packages/backend/src/flutter/builderImpl/flutterColor.ts index 6cb62468..5beb4c31 100644 --- a/packages/backend/src/flutter/builderImpl/flutterColor.ts +++ b/packages/backend/src/flutter/builderImpl/flutterColor.ts @@ -35,7 +35,11 @@ export const flutterColorFromDirectFills = ( const fill = retrieveTopFill(fills); if (fill && fill.type === "SOLID") { - return flutterColor(fill.color, fill.opacity ?? 1.0); + return flutterColor( + fill.color, + fill.opacity ?? 1.0, + (fill as any).variableColorName + ); } else if ( fill && (fill.type === "GRADIENT_LINEAR" || @@ -43,7 +47,12 @@ export const flutterColorFromDirectFills = ( fill.type === "GRADIENT_RADIAL") ) { if (fill.gradientStops.length > 0) { - return flutterColor(fill.gradientStops[0].color, fill.opacity ?? 1.0); + const stop = fill.gradientStops[0]; + return flutterColor( + stop.color, + fill.opacity ?? 1.0, + (stop as any).variableColorName + ); } } @@ -66,7 +75,7 @@ export const flutterBoxDecorationColor = ( if (fill && fill.type === "SOLID") { const opacity = fill.opacity ?? 1.0; - return { color: flutterColor(fill.color, opacity) }; + return { color: flutterColor(fill.color, opacity, (fill as any).variableColorName) }; } else if ( fill?.type === "GRADIENT_LINEAR" || fill?.type === "GRADIENT_RADIAL" || @@ -126,7 +135,7 @@ const gradientDirection = (angle: number): string => { const flutterRadialGradient = (fill: GradientPaint): string => { const colors = fill.gradientStops - .map((d) => flutterColor(d.color, d.color.a)) + .map((d) => flutterColor(d.color, d.color.a, (d as any).variableColorName)) .join(", "); const x = numberToFixedString(fill.gradientTransform[0][2]); @@ -144,7 +153,7 @@ const flutterRadialGradient = (fill: GradientPaint): string => { const flutterAngularGradient = (fill: GradientPaint): string => { const colors = fill.gradientStops - .map((d) => flutterColor(d.color, d.color.a)) + .map((d) => flutterColor(d.color, d.color.a, (d as any).variableColorName)) .join(", "); const x = numberToFixedString(fill.gradientTransform[0][2]); @@ -166,7 +175,7 @@ const flutterLinearGradient = (fill: GradientPaint): string => { const y = Math.sin(radians).toFixed(2); const colors = fill.gradientStops - .map((d) => flutterColor(d.color, d.color.a)) + .map((d) => flutterColor(d.color, d.color.a, (d as any).variableColorName)) .join(", "); return generateWidgetCode("LinearGradient", { @@ -205,21 +214,31 @@ const opacityToAlpha = (opacity: number): number => { return Math.round(opacity * 255); }; -export const flutterColor = (color: RGB, opacity: number): string => { +export const flutterColor = ( + color: RGB, + opacity: number, + variableColorName?: string +): string => { const sum = color.r + color.g + color.b; + let colorCode = ""; if (sum === 0) { - return opacity === 1 + colorCode = opacity === 1 ? "Colors.black" : `Colors.black.withValues(alpha: ${opacityToAlpha(opacity)})`; - } - - if (sum === 3) { - return opacity === 1 + } else if (sum === 3) { + colorCode = opacity === 1 ? "Colors.white" : `Colors.white.withValues(alpha: ${opacityToAlpha(opacity)})`; + } else { + // Always use full 8-digit hex which includes alpha channel + colorCode = `Color(0x${rgbTo8hex(color, opacity).toUpperCase()})`; } - // Always use full 8-digit hex which includes alpha channel - return `Color(0x${rgbTo8hex(color, opacity).toUpperCase()})`; + // Add variable name as a comment if it exists + if (variableColorName) { + return `${colorCode} /* ${variableColorName} */`; + } + + return colorCode; }; diff --git a/packages/plugin-ui/src/codegenPreferenceOptions.ts b/packages/plugin-ui/src/codegenPreferenceOptions.ts index 41acc3c8..7ca4bada 100644 --- a/packages/plugin-ui/src/codegenPreferenceOptions.ts +++ b/packages/plugin-ui/src/codegenPreferenceOptions.ts @@ -42,7 +42,7 @@ export const preferenceOptions: LocalCodegenPreferenceOptions[] = [ description: "Export code using Figma variables as colors. Example: 'bg-background' instead of 'bg-white'.", isDefault: false, - includedLanguages: ["HTML", "Tailwind"], + includedLanguages: ["HTML", "Tailwind", "Flutter"], }, { itemType: "individual_select", diff --git a/packages/plugin-ui/src/components/CodePanel.tsx b/packages/plugin-ui/src/components/CodePanel.tsx index 994f6434..b72e4dab 100644 --- a/packages/plugin-ui/src/components/CodePanel.tsx +++ b/packages/plugin-ui/src/components/CodePanel.tsx @@ -129,7 +129,6 @@ const CodePanel = (props: CodePanelProps) => { stylingPreferences: frameworkPreferences.filter((p) => stylingPropertyNames.includes(p.propertyName), ), - selectableSettingsFiltered: selectPreferenceOptions.filter((p) => p.includedLanguages?.includes(selectedFramework), ), From 469f9fe1425c7794ecef4b01793a40163a1e961f Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Fri, 7 Mar 2025 23:00:02 -0300 Subject: [PATCH 087/168] Add flex-wrap, fix HUG --- packages/backend/src/code.ts | 5 ++- .../backend/src/common/commonChildrenOrder.ts | 5 --- .../backend/src/common/nodeWidthHeight.ts | 31 ++++++++++--------- .../src/html/builderImpl/htmlAutoLayout.ts | 23 ++++++++++++++ .../backend/src/html/builderImpl/htmlSize.ts | 1 + .../builderImpl/tailwindAutoLayout.ts | 23 ++++++++++++++ .../src/tailwind/builderImpl/tailwindColor.ts | 1 - .../backend/src/tailwind/conversionTables.ts | 7 ----- .../src/tailwind/tailwindTextBuilder.ts | 2 -- .../plugin-ui/src/components/CodePanel.tsx | 5 --- 10 files changed, 67 insertions(+), 36 deletions(-) diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index 10a18203..cf9386ec 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -452,7 +452,10 @@ export const run = async (settings: PluginSettings) => { const { framework } = settings; const selection = figma.currentPage.selection; - if (selection.length > 1) { + if (selection.length === 0) { + postEmptyMessage(); + return; + } else if (selection.length > 1) { addWarning( "Ungrouped elements may have incorrect positioning. If this happens, try wrapping the selection in a Frame or Group.", ); diff --git a/packages/backend/src/common/commonChildrenOrder.ts b/packages/backend/src/common/commonChildrenOrder.ts index d7a93965..506870fb 100644 --- a/packages/backend/src/common/commonChildrenOrder.ts +++ b/packages/backend/src/common/commonChildrenOrder.ts @@ -18,11 +18,6 @@ export const commonSortChildrenWhenInferredAutoLayout = ( // NONE is a bug from Figma. case "NONE": case "VERTICAL": - console.log( - "ordering", - children.map((c) => c.name), - children.sort((a, b) => a.y - b.y).map((c) => c.name), - ); return children.sort((a, b) => a.y - b.y); } } diff --git a/packages/backend/src/common/nodeWidthHeight.ts b/packages/backend/src/common/nodeWidthHeight.ts index dff8fb77..abaff57d 100644 --- a/packages/backend/src/common/nodeWidthHeight.ts +++ b/packages/backend/src/common/nodeWidthHeight.ts @@ -1,34 +1,35 @@ import { Size } from "types"; export const nodeSize = (node: SceneNode, optimizeLayout: boolean): Size => { - const nodeAuto = - (optimizeLayout && "inferredAutoLayout" in node - ? node.inferredAutoLayout - : null) ?? node; - - // Check for explicit layout sizing properties - if ( - "layoutSizingHorizontal" in nodeAuto && - "layoutSizingVertical" in nodeAuto - ) { + if ("layoutSizingHorizontal" in node && "layoutSizingVertical" in node) { const width = - nodeAuto.layoutSizingHorizontal === "FILL" + node.layoutSizingHorizontal === "FILL" ? "fill" - : nodeAuto.layoutSizingHorizontal === "HUG" + : node.layoutSizingHorizontal === "HUG" ? null : node.width; const height = - nodeAuto.layoutSizingVertical === "FILL" + node.layoutSizingVertical === "FILL" ? "fill" - : nodeAuto.layoutSizingVertical === "HUG" + : node.layoutSizingVertical === "HUG" ? null : node.height; return { width, height }; } - if ("layoutMode" in nodeAuto && nodeAuto.layoutMode === "NONE") { + const nodeAuto = + (optimizeLayout && "inferredAutoLayout" in node + ? node.inferredAutoLayout + : null) ?? node; + + if ( + nodeAuto && + typeof nodeAuto === "object" && + "layoutMode" in nodeAuto && + nodeAuto.layoutMode === "NONE" + ) { return { width: node.width, height: node.height }; } diff --git a/packages/backend/src/html/builderImpl/htmlAutoLayout.ts b/packages/backend/src/html/builderImpl/htmlAutoLayout.ts index 8bbcad5f..bac3ffe4 100644 --- a/packages/backend/src/html/builderImpl/htmlAutoLayout.ts +++ b/packages/backend/src/html/builderImpl/htmlAutoLayout.ts @@ -37,6 +37,27 @@ const getGap = (node: InferredAutoLayoutResult): string | number => ? node.itemSpacing : ""; +const getFlexWrap = (node: InferredAutoLayoutResult): string => + node.layoutWrap === "WRAP" ? "wrap" : ""; + +const getAlignContent = (node: InferredAutoLayoutResult): string => { + if (node.layoutWrap !== "WRAP") return ""; + + switch (node.counterAxisAlignItems) { + case undefined: + case "MIN": + return "flex-start"; + case "CENTER": + return "center"; + case "MAX": + return "flex-end"; + case "BASELINE": + return "baseline"; + default: + return "normal"; + } +}; + const getFlex = ( node: SceneNode, autoLayout: InferredAutoLayoutResult, @@ -59,6 +80,8 @@ export const htmlAutoLayoutProps = ( "align-items": getAlignItems(autoLayout), gap: getGap(autoLayout), display: getFlex(node, autoLayout), + "flex-wrap": getFlexWrap(autoLayout), + "align-content": getAlignContent(autoLayout), }, settings.htmlGenerationMode === "jsx", ); diff --git a/packages/backend/src/html/builderImpl/htmlSize.ts b/packages/backend/src/html/builderImpl/htmlSize.ts index d6a3a7c1..fdf86cc1 100644 --- a/packages/backend/src/html/builderImpl/htmlSize.ts +++ b/packages/backend/src/html/builderImpl/htmlSize.ts @@ -16,6 +16,7 @@ export const htmlSizePartial = ( } const size = nodeSize(node, optimizeLayout); + console.log("size", size); const nodeParent = (node.parent && optimizeLayout && "inferredAutoLayout" in node.parent ? node.parent.inferredAutoLayout diff --git a/packages/backend/src/tailwind/builderImpl/tailwindAutoLayout.ts b/packages/backend/src/tailwind/builderImpl/tailwindAutoLayout.ts index 84b0ca82..73fda204 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindAutoLayout.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindAutoLayout.ts @@ -36,6 +36,27 @@ const getGap = (node: InferredAutoLayoutResult): string => ? `gap-${pxToLayoutSize(node.itemSpacing)}` : ""; +const getFlexWrap = (node: InferredAutoLayoutResult): string => + node.layoutWrap === "WRAP" ? "flex-wrap" : ""; + +const getAlignContent = (node: InferredAutoLayoutResult): string => { + if (node.layoutWrap !== "WRAP") return ""; + + switch (node.counterAxisAlignItems) { + case undefined: + case "MIN": + return "content-start"; + case "CENTER": + return "content-center"; + case "MAX": + return "content-end"; + case "BASELINE": + return "content-baseline"; + default: + return "content-normal"; + } +}; + const getFlex = ( node: SceneNode, autoLayout: InferredAutoLayoutResult, @@ -56,6 +77,8 @@ export const tailwindAutoLayoutProps = ( getJustifyContent(autoLayout), getAlignItems(autoLayout), getGap(autoLayout), + getFlexWrap(autoLayout), + getAlignContent(autoLayout), ].filter(Boolean); return classes.join(" "); diff --git a/packages/backend/src/tailwind/builderImpl/tailwindColor.ts b/packages/backend/src/tailwind/builderImpl/tailwindColor.ts index 83d13733..31ececde 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindColor.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindColor.ts @@ -66,7 +66,6 @@ export const tailwindSolidColor = ( fill: SolidPaint | ColorStop, kind: TailwindColorType, ): string => { - console.log("fill is", fill); const { colorName } = getColorInfo(fill); const effectiveOpacity = calculateEffectiveOpacity(fill); diff --git a/packages/backend/src/tailwind/conversionTables.ts b/packages/backend/src/tailwind/conversionTables.ts index ad947b17..78b0882e 100644 --- a/packages/backend/src/tailwind/conversionTables.ts +++ b/packages/backend/src/tailwind/conversionTables.ts @@ -78,8 +78,6 @@ const pxToTailwind = ( const keys = Object.keys(conversionMap).map((d) => +d); const convertedValue = exactValue(value, keys); - console.log("convertedValue", convertedValue, value, keys); - if (convertedValue) { return conversionMap[convertedValue]; } else if (localTailwindSettings.roundTailwindValues) { @@ -176,11 +174,6 @@ export function getColorInfo(fill: SolidPaint | ColorStop) { let hex: string = "#" + rgbTo6hex(fill.color); let meta: string = ""; - console.log( - "(fill as any).variableColorName", - fill, - (fill as any).variableColorName, - ); // variable if ((fill as any).variableColorName) { // Use pre-computed variable name if available diff --git a/packages/backend/src/tailwind/tailwindTextBuilder.ts b/packages/backend/src/tailwind/tailwindTextBuilder.ts index d404d9d5..822c9b7e 100644 --- a/packages/backend/src/tailwind/tailwindTextBuilder.ts +++ b/packages/backend/src/tailwind/tailwindTextBuilder.ts @@ -58,8 +58,6 @@ export class TailwindTextBuilder extends TailwindDefaultBuilder { .filter(Boolean) .join(" "); - console.log("styleClasses", styleClasses, segment); - const charsWithLineBreak = segment.characters.split("\n").join("
"); return { style: styleClasses, diff --git a/packages/plugin-ui/src/components/CodePanel.tsx b/packages/plugin-ui/src/components/CodePanel.tsx index b72e4dab..1ee07632 100644 --- a/packages/plugin-ui/src/components/CodePanel.tsx +++ b/packages/plugin-ui/src/components/CodePanel.tsx @@ -135,11 +135,6 @@ const CodePanel = (props: CodePanelProps) => { }; }, [preferenceOptions, selectPreferenceOptions, selectedFramework]); - // Handle custom prefix change - const handleCustomPrefixChange = (newValue: string) => { - onPreferenceChanged("customTailwindPrefix", newValue); - }; - return (
From 7fc482027436467ffe4fb0b9152f5a2439c32dc1 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Fri, 7 Mar 2025 23:05:20 -0300 Subject: [PATCH 088/168] Clean up node width height --- .../backend/src/common/nodeWidthHeight.ts | 49 +------------------ .../src/flutter/builderImpl/flutterSize.ts | 2 +- .../backend/src/html/builderImpl/htmlSize.ts | 2 +- .../src/swiftui/builderImpl/swiftuiSize.ts | 3 +- .../src/swiftui/swiftuiDefaultBuilder.ts | 4 +- packages/backend/src/swiftui/swiftuiMain.ts | 2 +- .../src/tailwind/builderImpl/tailwindSize.ts | 2 +- 7 files changed, 8 insertions(+), 56 deletions(-) diff --git a/packages/backend/src/common/nodeWidthHeight.ts b/packages/backend/src/common/nodeWidthHeight.ts index abaff57d..670a86b5 100644 --- a/packages/backend/src/common/nodeWidthHeight.ts +++ b/packages/backend/src/common/nodeWidthHeight.ts @@ -1,6 +1,6 @@ import { Size } from "types"; -export const nodeSize = (node: SceneNode, optimizeLayout: boolean): Size => { +export const nodeSize = (node: SceneNode): Size => { if ("layoutSizingHorizontal" in node && "layoutSizingVertical" in node) { const width = node.layoutSizingHorizontal === "FILL" @@ -19,52 +19,5 @@ export const nodeSize = (node: SceneNode, optimizeLayout: boolean): Size => { return { width, height }; } - const nodeAuto = - (optimizeLayout && "inferredAutoLayout" in node - ? node.inferredAutoLayout - : null) ?? node; - - if ( - nodeAuto && - typeof nodeAuto === "object" && - "layoutMode" in nodeAuto && - nodeAuto.layoutMode === "NONE" - ) { - return { width: node.width, height: node.height }; - } - - const hasLayout = - "layoutAlign" in node && node.parent && "layoutMode" in node.parent; - - if (!hasLayout) { - return { width: node.width, height: node.height }; - } - return { width: node.width, height: node.height }; - - // const isWidthFill = - // (parentLayoutMode === "HORIZONTAL" && nodeAuto.layoutGrow === 1) || - // (parentLayoutMode === "VERTICAL" && nodeAuto.layoutAlign === "STRETCH"); - // const isHeightFill = - // (parentLayoutMode === "HORIZONTAL" && nodeAuto.layoutAlign === "STRETCH") || - // (parentLayoutMode === "VERTICAL" && nodeAuto.layoutGrow === 1); - // const modesSwapped = parentLayoutMode === "HORIZONTAL"; - // const primaryAxisMode = modesSwapped - // ? "counterAxisSizingMode" - // : "primaryAxisSizingMode"; - // const counterAxisMode = modesSwapped - // ? "primaryAxisSizingMode" - // : "counterAxisSizingMode"; - - // return { - // width: isWidthFill - // ? "fill" - // : "layoutMode" in nodeAuto && nodeAuto[primaryAxisMode] === "AUTO" - // ? null - // : node.width, - // height: isHeightFill - // ? "fill" - // : "layoutMode" in nodeAuto && nodeAuto[counterAxisMode] === "AUTO" - // ? null - // : node.height, }; diff --git a/packages/backend/src/flutter/builderImpl/flutterSize.ts b/packages/backend/src/flutter/builderImpl/flutterSize.ts index 5877d2bb..a219a8a1 100644 --- a/packages/backend/src/flutter/builderImpl/flutterSize.ts +++ b/packages/backend/src/flutter/builderImpl/flutterSize.ts @@ -12,7 +12,7 @@ export const flutterSize = ( node: SceneNode, optimizeLayout: boolean, ): { width: string; height: string; isExpanded: boolean; constraints: Record } => { - const size = nodeSize(node, optimizeLayout); + const size = nodeSize(node); let isExpanded: boolean = false; const nodeParent = diff --git a/packages/backend/src/html/builderImpl/htmlSize.ts b/packages/backend/src/html/builderImpl/htmlSize.ts index fdf86cc1..bc6df727 100644 --- a/packages/backend/src/html/builderImpl/htmlSize.ts +++ b/packages/backend/src/html/builderImpl/htmlSize.ts @@ -15,7 +15,7 @@ export const htmlSizePartial = ( }; } - const size = nodeSize(node, optimizeLayout); + const size = nodeSize(node); console.log("size", size); const nodeParent = (node.parent && optimizeLayout && "inferredAutoLayout" in node.parent diff --git a/packages/backend/src/swiftui/builderImpl/swiftuiSize.ts b/packages/backend/src/swiftui/builderImpl/swiftuiSize.ts index 4b7fc13b..d0fe784b 100644 --- a/packages/backend/src/swiftui/builderImpl/swiftuiSize.ts +++ b/packages/backend/src/swiftui/builderImpl/swiftuiSize.ts @@ -3,9 +3,8 @@ import { numberToFixedString } from "../../common/numToAutoFixed"; export const swiftuiSize = ( node: SceneNode, - optimize: boolean = false, ): { width: string; height: string; constraints: string[] } => { - const size = nodeSize(node, optimize); + const size = nodeSize(node); const constraintProps: string[] = []; let width = ""; diff --git a/packages/backend/src/swiftui/swiftuiDefaultBuilder.ts b/packages/backend/src/swiftui/swiftuiDefaultBuilder.ts index a990adc4..2033e96f 100644 --- a/packages/backend/src/swiftui/swiftuiDefaultBuilder.ts +++ b/packages/backend/src/swiftui/swiftuiDefaultBuilder.ts @@ -138,8 +138,8 @@ export class SwiftuiDefaultBuilder { return this; } - size(node: SceneNode, optimize: boolean): this { - const { width, height, constraints } = swiftuiSize(node, optimize); + size(node: SceneNode): this { + const { width, height, constraints } = swiftuiSize(node); if (width || height) { this.pushModifier([`frame`, [width, height].filter(Boolean).join(", ")]); } diff --git a/packages/backend/src/swiftui/swiftuiMain.ts b/packages/backend/src/swiftui/swiftuiMain.ts index 94a6610f..71d5226f 100644 --- a/packages/backend/src/swiftui/swiftuiMain.ts +++ b/packages/backend/src/swiftui/swiftuiMain.ts @@ -127,7 +127,7 @@ export const swiftuiContainer = ( const result = new SwiftuiDefaultBuilder(kind) .shapeForeground(node) .autoLayoutPadding(node, localSettings.optimizeLayout) - .size(node, localSettings.optimizeLayout) + .size(node) .shapeBackground(node) .cornerRadius(node) .shapeBorder(node) diff --git a/packages/backend/src/tailwind/builderImpl/tailwindSize.ts b/packages/backend/src/tailwind/builderImpl/tailwindSize.ts index 60208415..e514af82 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindSize.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindSize.ts @@ -33,7 +33,7 @@ export const tailwindSizePartial = ( optimizeLayout: boolean, settings?: TailwindSettings, ): { width: string; height: string; constraints: string } => { - const size = nodeSize(node, optimizeLayout); + const size = nodeSize(node); const nodeParent = (node.parent && optimizeLayout && "inferredAutoLayout" in node.parent ? node.parent.inferredAutoLayout From 3dc4cef3c9c49e1976591824eff342569c7495cb Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Fri, 7 Mar 2025 23:42:59 -0300 Subject: [PATCH 089/168] Improve README --- README.md | 80 ++++++++++++++----- .../src/html/builderImpl/htmlAutoLayout.ts | 2 +- 2 files changed, 60 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 0ab9378c..8aa1a839 100644 --- a/README.md +++ b/README.md @@ -13,42 +13,47 @@

-Most _design to code_ plugins are bad, some are even paid. This project aims to raise the bar by generating **responsive** layouts in [Tailwind](https://tailwindcss.com/), [Flutter](https://flutter.github.io/) and [SwiftUI](https://developer.apple.com/xcode/swiftui/). The plan is to eventually add support for [Jetpack Compose](https://developer.android.com/jetpack/compose) and possibly standard HTML or other frameworks like [React Native](https://reactnative.dev/), [Bootstrap](https://getbootstrap.com/) or [Fluent](https://www.microsoft.com/design/fluent/). Feedback, ideas and partnerships are appreciated! +Turning Figma designs into usable code can be a challenge, often requiring time-consuming manual work. Figma to Code simplifies that process. This plugin generates responsive layouts in `HTML`, `React (JSX)`, `Svelte`, `styled-components`, `Tailwind`, `Flutter`, and `SwiftUI` directly from your designs. Your feedback and ideas are always welcome. ![Gif showing the conversion](assets/lossy_gif.gif) ## How it works -This plugin takes an unconventional approach to improve code quality: it optimizes the layout before the conversion to code even begins. The standard Figma [Nodes](https://www.figma.com/plugin-docs/api/nodes/) (what represents each layer) is a joy to work with, but it can't modify a layer without modifying the user project. For this reason, I decided to virtualize it, remaking the official implementation and naming them `AltNodes`. During the process of converting a `Node` into an `AltNode`, the plugin does the following: +The plugin uses a sophisticated multi-step process to transform your Figma designs into clean, optimized code: + +1. **Node Conversion**: First, the plugin converts Figma's native nodes into JSON representations, preserving all necessary properties while adding optimizations and parent references. + +2. **Intermediate Representation**: The JSON nodes are then transformed into `AltNodes` - a custom virtual representation that can be manipulated without affecting your original design. + +3. **Layout Optimization**: The plugin analyzes and optimizes layouts, detecting patterns like auto-layouts, responsive constraints and color variables. + +4. **Code Generation**: Finally, the optimized structure is transformed into the target framework's code, with special handling for each framework's unique patterns and best practices. If a feature is unsupported, the plugin will provide a warning. ![Conversion Workflow](assets/workflow.png) -That process can also be seen as an [Intermediate Representation](https://en.wikipedia.org/wiki/Intermediate_representation) and might allow this plugin to, one day, live outside Figma. +This intermediate representation approach allows for sophisticated transformations and optimizations before any code is generated, resulting in cleaner, more maintainable output. ## Hard cases -When finding the unknown (a `Group` or `Frame` with more than one child and no vertical or horizontal alignment), Tailwind mode uses [insets](https://tailwindcss.com/docs/top-right-bottom-left/#app) for best cases and `left`, `top` from standard CSS for the worst cases. Flutter mode uses `Stack` and `Positioned.fill`. Both are usually not recommended and can easily defeat the responsiveness. In many scenarios, just wrapping some elements in a `Group` or `Frame` can solve: +Converting visual designs to code inevitably encounters complex edge cases. Here are some challenges the plugin handles: -![Conversion Workflow](assets/examples.png) +1. **Complex Layouts**: When working with mixed positioning (absolute + auto-layout), the plugin has to make intelligent decisions about how to structure the resulting code. It detects parent-child relationships and z-index ordering to produce the most accurate representation. -**Tip**: Instead of selecting the whole page, you can also select individual items. This can be useful for both debugging and componentization. For example: you can use the plugin to generate the code of a single element and then replicate it using a for-loop. +2. **Text Styling**: Rich text with multiple styles requires breaking into multiple elements while preserving layout relationships. -### Todo +3. **Color Variables**: The plugin detects and processes color variables, allowing for theme-consistent output. -- Vectors (tricky in HTML, unsupported in Flutter) -- Images (they are local, how to support them?) -- Line/Star/Polygon (todo. Rectangle and Ellipse were prioritized and are more common) -- The source code is fully commented and there are more than 30 "todo"s there +4. **Gradients and Effects**: Different frameworks handle gradients and effects in unique ways, requiring specialized conversion logic. -### Tailwind limitations +![Conversion Workflow](assets/examples.png) -- **Width:** Tailwind has a maximum width of 384px. If an item passes this, the width will be set to `w-full` (unless it is already relative like `w-1/2`, `w-1/3`, etc). This is usually a feature, but be careful: if most layers in your project are larger than 384px, the plugin's result might be less than optimal. +**Tip**: Instead of selecting the whole page, you can also select individual items. This can be useful for both debugging and componentization. For example: you can use the plugin to generate the code of a single element and then replicate it using a for-loop. -### Flutter limits and ideas +### Todo -- **Stack:** in some simpler cases, a `Stack` could be replaced with a `Container` and a `BoxDecoration`. Discover those cases and optimize them. -- **Material Styles**: text could be matched to existing Material styles (like outputting `Headline6` when text size is 20). -- **Identify Buttons**: the plugin could identify specific buttons and output them instead of always using `Container` or `Material`. +- Vectors (possible to enable in HTML and Tailwind) +- Images (possible to enable to inline them in HTML and Tailwind) +- Line/Star/Polygon ## How to build the project @@ -70,16 +75,49 @@ The plugin is organized as a monorepo. There are several packages: - `ui-src` - loads the common `plugin-ui` and compiles to `index.html` - `apps/debug` - This is a debug mode plugin that is a more convenient way to see all the UI elements. -The plugin is built using Turbo which in turn builds the internal packages. +### Development Workflow + +The project uses [Turborepo](https://turbo.build/) for managing the monorepo, and each package is compiled using [esbuild](https://esbuild.github.io/) for fast development cycles. Only modified files are recompiled when changes are made, making the development process more efficient. + +#### Running the Project + +You have two main options for development: + +1. **Root development mode** (includes debug UI): + + ```bash + pnpm dev + ``` + + This runs the plugin in dev mode and also starts a Next.js server for the debug UI. You can access the debug UI at `http://localhost:3000`. + +2. **Plugin-only development mode**: + + ```bash + cd apps/plugin + pnpm dev + ``` + + This focuses only on the plugin without the Next.js debug UI. Use this when you're making changes specifically to the plugin. + +#### Where to Make Changes + +Most of your development work will happen in these directories: + +- `packages/backend` - For plugin backend +- `packages/plugin-ui` - For plugin UI +- `apps/plugin/` - The main plugin result that combines the backend and UI and is called by Figma. + +You'll rarely need to modify files directly in the `apps/` directory, as they mostly contain build configuration. #### Commands `pnpm run ...` - `dev` - runs the app in dev mode. This can be run in the Figma editor. -- `build` -- `build:watch` -- `lint` +- `build` - builds the project for production +- `build:watch` - builds and watches for changes +- `lint` - runs ESLint - `format` - formats with prettier (warning: may edit files!) #### Debug mode diff --git a/packages/backend/src/html/builderImpl/htmlAutoLayout.ts b/packages/backend/src/html/builderImpl/htmlAutoLayout.ts index bac3ffe4..289df9d9 100644 --- a/packages/backend/src/html/builderImpl/htmlAutoLayout.ts +++ b/packages/backend/src/html/builderImpl/htmlAutoLayout.ts @@ -42,7 +42,7 @@ const getFlexWrap = (node: InferredAutoLayoutResult): string => const getAlignContent = (node: InferredAutoLayoutResult): string => { if (node.layoutWrap !== "WRAP") return ""; - + switch (node.counterAxisAlignItems) { case undefined: case "MIN": From 82fe0b543e437a83e40746e1f456f4e887adba9d Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Fri, 7 Mar 2025 23:44:34 -0300 Subject: [PATCH 090/168] Improve README --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8aa1a839..1c063e75 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@

-Turning Figma designs into usable code can be a challenge, often requiring time-consuming manual work. Figma to Code simplifies that process. This plugin generates responsive layouts in `HTML`, `React (JSX)`, `Svelte`, `styled-components`, `Tailwind`, `Flutter`, and `SwiftUI` directly from your designs. Your feedback and ideas are always welcome. +Converting Figma designs into usable code can be a challenge, often requiring time-consuming manual work. Figma to Code simplifies that process. This plugin generates responsive layouts in `HTML`, `React (JSX)`, `Svelte`, `styled-components`, `Tailwind`, `Flutter`, and `SwiftUI` directly from your designs. Your feedback and ideas are always welcome. ![Gif showing the conversion](assets/lossy_gif.gif) @@ -39,11 +39,9 @@ Converting visual designs to code inevitably encounters complex edge cases. Here 1. **Complex Layouts**: When working with mixed positioning (absolute + auto-layout), the plugin has to make intelligent decisions about how to structure the resulting code. It detects parent-child relationships and z-index ordering to produce the most accurate representation. -2. **Text Styling**: Rich text with multiple styles requires breaking into multiple elements while preserving layout relationships. +2. **Color Variables**: The plugin detects and processes color variables, allowing for theme-consistent output. -3. **Color Variables**: The plugin detects and processes color variables, allowing for theme-consistent output. - -4. **Gradients and Effects**: Different frameworks handle gradients and effects in unique ways, requiring specialized conversion logic. +3. **Gradients and Effects**: Different frameworks handle gradients and effects in unique ways, requiring specialized conversion logic. ![Conversion Workflow](assets/examples.png) From c477282ace9a58105597c55959b966fe8a1ff59a Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Sun, 9 Mar 2025 01:42:24 -0300 Subject: [PATCH 091/168] Add background-blend-mode --- packages/backend/src/code.ts | 29 +++++++-- .../backend/src/common/convertFontWeight.ts | 2 +- .../backend/src/html/builderImpl/htmlColor.ts | 49 +++++++++++++++ .../backend/src/html/builderImpl/htmlSize.ts | 1 - .../backend/src/html/htmlDefaultBuilder.ts | 61 ++++++++----------- .../src/tailwind/builderImpl/tailwindBlend.ts | 24 ++++++++ .../src/tailwind/tailwindDefaultBuilder.ts | 19 ++++-- .../src/tailwind/tailwindTextBuilder.ts | 4 +- 8 files changed, 140 insertions(+), 49 deletions(-) diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index cf9386ec..25878cb2 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -44,8 +44,12 @@ const memoizedVariableToColorName = async ( variableId: string, ): Promise => { if (!variableCache.has(variableId)) { - const colorName = await variableToColorName(variableId); + const colorName = (await variableToColorName(variableId)).replaceAll( + ",", + "", + ); variableCache.set(variableId, colorName); + return colorName; } return variableCache.get(variableId)!; }; @@ -255,9 +259,18 @@ const processNodePair = async ( const mutableSegment = Object.assign({}, segment); if (settings.useColorVariables && segment.fills) { - mutableSegment.fills = segment.fills.map((d) => ({ ...d })); - await Promise.all( - mutableSegment.fills.map((fill) => processColorVariables(fill)), + mutableSegment.fills = await Promise.all( + segment.fills.map(async (d) => { + if ( + d.blendMode !== "PASS_THROUGH" && + d.blendMode !== "NORMAL" + ) { + addWarning("BlendMode is not supported in Text colors"); + } + const fill = { ...d }; + await processColorVariables(fill); + return fill; + }), ); } @@ -298,8 +311,12 @@ const processNodePair = async ( if ("width" in figmaNode) { jsonNode.width = figmaNode.width; jsonNode.height = figmaNode.height; - jsonNode.x = figmaNode.x; - jsonNode.y = figmaNode.y; + // jsonNode.x = figmaNode.x; + // jsonNode.y = figmaNode.y; + } + + if ("rotation" in jsonNode) { + jsonNode.rotation = jsonNode.rotation * (180 / Math.PI); } if ("individualStrokeWeights" in jsonNode) { diff --git a/packages/backend/src/common/convertFontWeight.ts b/packages/backend/src/common/convertFontWeight.ts index 7306161a..3231dd51 100644 --- a/packages/backend/src/common/convertFontWeight.ts +++ b/packages/backend/src/common/convertFontWeight.ts @@ -3,7 +3,7 @@ import { FontWeightNumber } from "types"; // Convert generic named weights to numbers, which is the way tailwind understands export const convertFontWeight = (weight: string): FontWeightNumber | null => { // change extra-light to extralight - weight = weight.replace(" ", "").replace("-", "").toLowerCase(); + weight = weight.replaceAll(" ", "").replaceAll("-", "").toLowerCase(); switch (weight) { case "thin": return "100"; diff --git a/packages/backend/src/html/builderImpl/htmlColor.ts b/packages/backend/src/html/builderImpl/htmlColor.ts index 2d2e7fb5..73b0c6db 100644 --- a/packages/backend/src/html/builderImpl/htmlColor.ts +++ b/packages/backend/src/html/builderImpl/htmlColor.ts @@ -243,3 +243,52 @@ export const htmlDiamondGradient = (fill: GradientPaint): string => { ) .join(", "); }; + +export const buildBackgroundValues = ( + paintArray: ReadonlyArray | PluginAPI["mixed"], + settings: HTMLSettings, +): string => { + if (paintArray === figma.mixed) { + return ""; + } + + // If only one fill, just use plain color/gradient + if (paintArray.length === 1) { + const paint = paintArray[0]; + if (paint.type === "SOLID") { + return htmlColorFromFills(paintArray, settings); + } else if ( + paint.type === "GRADIENT_LINEAR" || + paint.type === "GRADIENT_RADIAL" || + paint.type === "GRADIENT_ANGULAR" || + paint.type === "GRADIENT_DIAMOND" + ) { + return htmlGradientFromFills(paint); + } + return ""; + } + + // Reverse the array to match CSS layering (first is top-most in CSS) + const styles = [...paintArray].reverse().map((paint, index) => { + if (paint.type === "SOLID") { + // For multiple fills, always convert solid colors to linear gradients + // to ensure proper layering in CSS backgrounds + const color = htmlColorFromFills([paint], settings); + if (index === 0) { + return `linear-gradient(0deg, ${color} 0%, ${color} 100%)`; + } + + return color; + } else if ( + paint.type === "GRADIENT_LINEAR" || + paint.type === "GRADIENT_RADIAL" || + paint.type === "GRADIENT_ANGULAR" || + paint.type === "GRADIENT_DIAMOND" + ) { + return htmlGradientFromFills(paint); + } + return ""; // Handle other paint types safely + }); + + return styles.filter((value) => value !== "").join(", "); +}; diff --git a/packages/backend/src/html/builderImpl/htmlSize.ts b/packages/backend/src/html/builderImpl/htmlSize.ts index bc6df727..faffc414 100644 --- a/packages/backend/src/html/builderImpl/htmlSize.ts +++ b/packages/backend/src/html/builderImpl/htmlSize.ts @@ -16,7 +16,6 @@ export const htmlSizePartial = ( } const size = nodeSize(node); - console.log("size", size); const nodeParent = (node.parent && optimizeLayout && "inferredAutoLayout" in node.parent ? node.parent.inferredAutoLayout diff --git a/packages/backend/src/html/htmlDefaultBuilder.ts b/packages/backend/src/html/htmlDefaultBuilder.ts index 7febce2e..e9eede9b 100644 --- a/packages/backend/src/html/htmlDefaultBuilder.ts +++ b/packages/backend/src/html/htmlDefaultBuilder.ts @@ -7,8 +7,8 @@ import { htmlBlendMode, } from "./builderImpl/htmlBlend"; import { + buildBackgroundValues, htmlColorFromFills, - htmlGradientFromFills, } from "./builderImpl/htmlColor"; import { htmlPadding } from "./builderImpl/htmlPadding"; import { htmlSizePartial } from "./builderImpl/htmlSize"; @@ -31,7 +31,6 @@ import { HTMLSettings } from "types"; import { cssCollection, generateUniqueClassName, - getSvelteClassName, stylesToCSS, } from "./htmlMain"; @@ -122,12 +121,6 @@ export class HtmlDefaultBuilder { this.autoLayoutPadding(); this.position(); this.blend(); - - // Add z-index if we have a custom value from the itemReverseZIndex handling - if ((this.node as any).customZIndex !== undefined) { - this.addStyles(`z-index: ${(this.node as any).customZIndex}`); - } - return this; } @@ -229,8 +222,11 @@ export class HtmlDefaultBuilder { position(): this { const { node, optimizeLayout, isJSX } = this; - if (commonIsAbsolutePosition(node, optimizeLayout)) { + const isAbsolutePosition = commonIsAbsolutePosition(node, optimizeLayout); + console.log("isAbsolutePosition", isAbsolutePosition, "for node", node); + if (isAbsolutePosition) { const { x, y } = getCommonPositionValue(node); + console.log("x, y, are", x, y); this.addStyles( formatWithJSX("left", isJSX, x), @@ -267,44 +263,39 @@ export class HtmlDefaultBuilder { return this; } - const backgroundValues = this.buildBackgroundValues(paintArray); + const backgroundValues = buildBackgroundValues(paintArray, this.settings); if (backgroundValues) { this.addStyles(formatWithJSX("background", this.isJSX, backgroundValues)); + + // Add blend mode property if multiple fills exist with different blend modes + if (paintArray !== figma.mixed) { + const blendModes = this.buildBackgroundBlendModes(paintArray); + if (blendModes) { + this.addStyles( + formatWithJSX("background-blend-mode", this.isJSX, blendModes), + ); + } + } } return this; } - buildBackgroundValues( - paintArray: ReadonlyArray | PluginAPI["mixed"], - ): string { - if (paintArray === figma.mixed) { + buildBackgroundBlendModes(paintArray: ReadonlyArray): string { + if (paintArray.length === 0) { return ""; } - // If one fill and it's a solid, return the solid RGB color - if (paintArray.length === 1 && paintArray[0].type === "SOLID") { - return htmlColorFromFills(paintArray, this.settings); - } - - // If multiple fills, deal with gradients and convert solid colors to a "dumb" linear-gradient - const styles = paintArray.map((paint) => { - if (paint.type === "SOLID") { - const color = htmlColorFromFills([paint], this.settings); - return `linear-gradient(0deg, ${color} 0%, ${color} 100%)`; - } else if ( - paint.type === "GRADIENT_LINEAR" || - paint.type === "GRADIENT_RADIAL" || - paint.type === "GRADIENT_ANGULAR" || - paint.type === "GRADIENT_DIAMOND" - ) { - return htmlGradientFromFills(paint); + // Reverse the array to match the background order + const blendModes = [...paintArray].reverse().map((paint) => { + if (paint.blendMode === "PASS_THROUGH") { + return "normal"; } - return ""; // Handle other paint types safely + return paint.blendMode?.toLowerCase(); }); - return styles.filter((value) => value !== "").join(", "); + return blendModes.join(", "); } shadow(): this { @@ -374,7 +365,7 @@ export class HtmlDefaultBuilder { formatWithJSX( "filter", this.isJSX, - `blur(${numberToFixedString(blur.radius)}px)`, + `blur(${numberToFixedString(blur.radius / 2)}px)`, ), ); } @@ -387,7 +378,7 @@ export class HtmlDefaultBuilder { formatWithJSX( "backdrop-filter", this.isJSX, - `blur(${numberToFixedString(backgroundBlur.radius)}px)`, + `blur(${numberToFixedString(backgroundBlur.radius / 2)}px)`, ), ); } diff --git a/packages/backend/src/tailwind/builderImpl/tailwindBlend.ts b/packages/backend/src/tailwind/builderImpl/tailwindBlend.ts index 8ad679e8..668f0284 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindBlend.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindBlend.ts @@ -55,6 +55,30 @@ export const tailwindBlendMode = (node: MinimalBlendMixin): string => { return ""; }; +/** + * Convert a Figma background blend mode to a Tailwind bg-blend-* class + * + * @param paintArray The array of paint fills that may have blend modes + * @returns Tailwind background blend mode class if applicable + */ +export const tailwindBackgroundBlendMode = ( + paintArray: ReadonlyArray, +): string => { + if (paintArray.length === 0) { + return ""; + } + + // Get the top fill's blend mode (in Figma, the last item is the top one) + const topFill = paintArray[paintArray.length - 1]; + if (topFill.blendMode === "NORMAL" || topFill.blendMode === "PASS_THROUGH") { + return ""; + } + + const blendMode = + topFill.blendMode?.toLowerCase()?.replaceAll("_", "-") || "normal"; + return `bg-blend-${blendMode}`; +}; + /** * https://tailwindcss.com/docs/visibility/ * example: invisible diff --git a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts index 8d46a52c..ae23b0c5 100644 --- a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts +++ b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts @@ -8,6 +8,7 @@ import { tailwindRotation, tailwindOpacity, tailwindBlendMode, + tailwindBackgroundBlendMode, } from "./builderImpl/tailwindBlend"; import { tailwindBorderWidth, @@ -167,6 +168,14 @@ export class TailwindDefaultBuilder { let gradient = ""; if (kind === "bg") { gradient = tailwindGradientFromFills(paint); + + // Add background blend mode class if applicable + if (paint !== figma.mixed) { + const blendModeClass = tailwindBackgroundBlendMode(paint); + if (blendModeClass) { + this.addAttributes(blendModeClass); + } + } } if (gradient) { this.addAttributes(gradient); @@ -234,9 +243,11 @@ export class TailwindDefaultBuilder { blur() { const { node } = this; if ("effects" in node && node.effects.length > 0) { - const blur = node.effects.find((e) => e.type === "LAYER_BLUR"); + const blur = node.effects.find( + (e) => e.type === "LAYER_BLUR" && e.visible, + ); if (blur) { - const blurValue = pxToBlur(blur.radius); + const blurValue = pxToBlur(blur.radius / 2); if (blurValue) { this.addAttributes( blurValue === "blur" ? "blur" : `blur-${blurValue}`, @@ -245,10 +256,10 @@ export class TailwindDefaultBuilder { } const backgroundBlur = node.effects.find( - (e) => e.type === "BACKGROUND_BLUR", + (e) => e.type === "BACKGROUND_BLUR" && e.visible, ); if (backgroundBlur) { - const backgroundBlurValue = pxToBlur(backgroundBlur.radius); + const backgroundBlurValue = pxToBlur(backgroundBlur.radius / 2); if (backgroundBlurValue) { this.addAttributes( `backdrop-blur${ diff --git a/packages/backend/src/tailwind/tailwindTextBuilder.ts b/packages/backend/src/tailwind/tailwindTextBuilder.ts index 822c9b7e..66ee2882 100644 --- a/packages/backend/src/tailwind/tailwindTextBuilder.ts +++ b/packages/backend/src/tailwind/tailwindTextBuilder.ts @@ -137,8 +137,8 @@ export class TailwindTextBuilder extends TailwindDefaultBuilder { } const value = node.fontName.style - .replace("italic", "") - .replace(" ", "") + .replaceAll("italic", "") + .replaceAll(" ", "") .toLowerCase(); this.addAttributes(`font-${value}`); From a4c640265ebf76322e0e4a87291358c92e11983c Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Sun, 9 Mar 2025 01:55:42 -0300 Subject: [PATCH 092/168] Make it even faster. No more `inferredAutoLayout`. --- packages/backend/src/code.ts | 38 ++----- packages/backend/src/common/commonPosition.ts | 104 +----------------- .../plugin-ui/src/codegenPreferenceOptions.ts | 9 -- 3 files changed, 15 insertions(+), 136 deletions(-) diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index 25878cb2..19506505 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -26,18 +26,6 @@ let nodeCacheHits = 0; // Keep track of node names for sequential numbering const nodeNameCounters: Map = new Map(); -// Helper function to add parent references to all children in the node tree -const addParentReferences = (node: any) => { - if (node.children && node.children.length > 0) { - for (const child of node.children) { - // Add parent reference to the child - child.parent = node; - // Recursively process this child's children - addParentReferences(child); - } - } -}; - const variableCache = new Map(); const memoizedVariableToColorName = async ( @@ -171,14 +159,21 @@ function adjustChildrenOrder(node: any) { * @param jsonNode The JSON node to process * @param figmaNode The corresponding Figma node * @param settings Plugin settings + * @param parentNode Optional parent node reference to set */ const processNodePair = async ( jsonNode: any, figmaNode: SceneNode, settings: PluginSettings, + parentNode?: any ) => { if (!jsonNode.id) return; + // Set parent reference if parent is provided + if (parentNode) { + jsonNode.parent = parentNode; + } + // Ensure node has a unique name with simple numbering const cleanName = jsonNode.name.trim(); @@ -295,13 +290,6 @@ const processNodePair = async ( } } - // Extract inferredAutoLayout if optimizeLayout is enabled - if (settings.optimizeLayout && "inferredAutoLayout" in figmaNode) { - jsonNode.inferredAutoLayout = JSON.parse( - JSON.stringify(figmaNode.inferredAutoLayout), - ); - } - // Extract component metadata from instances if ("variantProperties" in figmaNode && figmaNode.variantProperties) { jsonNode.variantProperties = figmaNode.variantProperties; @@ -311,8 +299,8 @@ const processNodePair = async ( if ("width" in figmaNode) { jsonNode.width = figmaNode.width; jsonNode.height = figmaNode.height; - // jsonNode.x = figmaNode.x; - // jsonNode.y = figmaNode.y; + jsonNode.x = figmaNode.x; + jsonNode.y = figmaNode.y; } if ("rotation" in jsonNode) { @@ -388,6 +376,7 @@ const processNodePair = async ( jsonNode.children[i], figmaNode.children[i], settings, + jsonNode // Pass the current node as parent for its children ); } @@ -444,13 +433,6 @@ export const nodesToJSON = async ( `[benchmark][inside nodesToJSON] Process node pairs: ${Date.now() - processNodesStart}ms`, ); - const addParentStart = Date.now(); - // Add parent references to all children in the node tree - nodeJson.forEach((node) => addParentReferences(node)); - console.log( - `[benchmark][inside nodesToJSON] addParentReferences: ${Date.now() - addParentStart}ms`, - ); - return nodeJson; }; diff --git a/packages/backend/src/common/commonPosition.ts b/packages/backend/src/common/commonPosition.ts index ce97d203..ad4d9fe4 100644 --- a/packages/backend/src/common/commonPosition.ts +++ b/packages/backend/src/common/commonPosition.ts @@ -1,97 +1,3 @@ -import { LayoutMode } from "types"; -import { parentCoordinates } from "./parentCoordinates"; - -export const commonPosition = ( - node: SceneNode & DimensionAndPositionMixin, -): LayoutMode => { - // if node is same size as height, position is not necessary - - // detect if Frame's width is same as Child when Frame has Padding. - // warning: this may return true even when false, if size is same, but position is different. However, it would be an unexpected layout. - let hPadding = 0; - let vPadding = 0; - if (node.parent && "layoutMode" in node.parent) { - hPadding = node.parent.paddingLeft + node.parent.paddingRight; - vPadding = node.parent.paddingTop + node.parent.paddingBottom; - } - - if ( - !node.parent || - !("width" in node.parent) || - (node.width === node.parent.width - hPadding && - node.height === node.parent.height - vPadding) - ) { - return ""; - } - - // position is absolute, parent is relative - // return "absolute inset-0 m-auto "; - - const [parentX, parentY] = parentCoordinates(node.parent); - - // if view is too small, anything will be detected; this is necessary to reduce the tolerance. - let threshold = 8; - if (node.width < 16 || node.height < 16) { - threshold = 1; - } - - // < 4 is a threshold. If === is used, there can be rounding errors (28.002 !== 28) - const centerX = - Math.abs(2 * (node.x - parentX) + node.width - node.parent.width) < - threshold; - const centerY = - Math.abs(2 * (node.y - parentY) + node.height - node.parent.height) < - threshold; - - const minX = node.x - parentX < threshold; - const minY = node.y - parentY < threshold; - - const maxX = node.parent.width - (node.x - parentX + node.width) < threshold; - const maxY = - node.parent.height - (node.y - parentY + node.height) < threshold; - - // this needs to be on top, because Tailwind is incompatible with Center, so this will give preference. - if (minX && minY) { - // x left, y top - return "TopStart"; - } else if (minX && maxY) { - // x left, y bottom - return "BottomStart"; - } else if (maxX && minY) { - // x right, y top - return "TopEnd"; - } else if (maxX && maxY) { - // x right, y bottom - return "BottomEnd"; - } - - if (centerX && centerY) { - return "Center"; - } - - if (centerX) { - if (minY) { - // x center, y top - return "TopCenter"; - } - if (maxY) { - // x center, y bottom - return "BottomCenter"; - } - } else if (centerY) { - if (minX) { - // x left, y center - return "CenterStart"; - } - if (maxX) { - // x right, y center - return "CenterEnd"; - } - } - - return "Absolute"; -}; - export const getCommonPositionValue = ( node: SceneNode, ): { x: number; y: number } => { @@ -112,6 +18,10 @@ export const commonIsAbsolutePosition = ( node: SceneNode, optimizeLayout: boolean, ) => { + if ("layoutPositioning" in node && node.layoutPositioning === "ABSOLUTE") { + return true; + } + // No position when parent is inferred auto layout. if ( optimizeLayout && @@ -130,11 +40,7 @@ export const commonIsAbsolutePosition = ( "layoutMode" in node.parent && node.parent.layoutMode === "NONE"; const hasNoLayoutMode = !("layoutMode" in node.parent); - if ( - ("layoutPositioning" in node && node.layoutPositioning === "ABSOLUTE") || - parentLayoutIsNone || - hasNoLayoutMode - ) { + if (parentLayoutIsNone || hasNoLayoutMode) { return true; } diff --git a/packages/plugin-ui/src/codegenPreferenceOptions.ts b/packages/plugin-ui/src/codegenPreferenceOptions.ts index 7ca4bada..d4e3c39c 100644 --- a/packages/plugin-ui/src/codegenPreferenceOptions.ts +++ b/packages/plugin-ui/src/codegenPreferenceOptions.ts @@ -9,15 +9,6 @@ export const preferenceOptions: LocalCodegenPreferenceOptions[] = [ isDefault: false, includedLanguages: ["HTML", "Tailwind"], }, - { - itemType: "individual_select", - propertyName: "optimizeLayout", - label: "Optimize layout", - description: - "Attempt to auto-layout suitable element groups. This may increase code quality, but may not always work as expected.", - isDefault: true, - includedLanguages: ["HTML", "Tailwind", "Flutter", "SwiftUI"], - }, { itemType: "individual_select", propertyName: "roundTailwindValues", From 42150da05fba62742b2b599ab775d74b448ce5a5 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Sun, 9 Mar 2025 02:37:24 -0300 Subject: [PATCH 093/168] Fix compile --- packages/backend/src/code.ts | 3 --- packages/plugin-ui/src/components/CodePanel.tsx | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index 19506505..9a504274 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -21,7 +21,6 @@ let getStyledTextSegmentsTime = 0; let getStyledTextSegmentsCalls = 0; let processColorVariablesTime = 0; let processColorVariablesCalls = 0; -let nodeCacheHits = 0; // Keep track of node names for sequential numbering const nodeNameCounters: Map = new Map(); @@ -406,7 +405,6 @@ export const nodesToJSON = async ( ): Promise => { // Reset name counters for each conversion nodeNameCounters.clear(); - nodeCacheHits = 0; const exportJsonStart = Date.now(); // First get the JSON representation of nodes @@ -444,7 +442,6 @@ export const run = async (settings: PluginSettings) => { getStyledTextSegmentsCalls = 0; processColorVariablesTime = 0; processColorVariablesCalls = 0; - nodeCacheHits = 0; variableCache.clear(); clearWarnings(); diff --git a/packages/plugin-ui/src/components/CodePanel.tsx b/packages/plugin-ui/src/components/CodePanel.tsx index 1ee07632..583e226e 100644 --- a/packages/plugin-ui/src/components/CodePanel.tsx +++ b/packages/plugin-ui/src/components/CodePanel.tsx @@ -12,7 +12,7 @@ import EmptyState from "./EmptyState"; import SettingsGroup from "./SettingsGroup"; import CustomPrefixInput from "./CustomPrefixInput"; import FrameworkTabs from "./FrameworkTabs"; -import { TailwindSettings } from "./tailwindSettings"; +import { TailwindSettings } from "./TailwindSettings"; interface CodePanelProps { code: string; From 2c9445d438eb593df7f205986d4be664f73e11ff Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Mon, 10 Mar 2025 00:57:49 -0300 Subject: [PATCH 094/168] Fix radius --- packages/backend/src/altNodes/altNodeUtils.ts | 2 - packages/backend/src/common/commonRadius.ts | 19 +++ .../backend/src/common/exportAsyncProxy.ts | 2 +- .../src/html/builderImpl/htmlBorderRadius.ts | 51 ++++---- .../backend/src/html/htmlDefaultBuilder.ts | 2 - .../tailwind/builderImpl/tailwindBorder.ts | 2 +- packages/plugin-ui/src/components/Preview.tsx | 112 ++++++++++++------ 7 files changed, 123 insertions(+), 67 deletions(-) diff --git a/packages/backend/src/altNodes/altNodeUtils.ts b/packages/backend/src/altNodes/altNodeUtils.ts index 50f05cc8..150e5d53 100644 --- a/packages/backend/src/altNodes/altNodeUtils.ts +++ b/packages/backend/src/altNodes/altNodeUtils.ts @@ -56,8 +56,6 @@ export const renderAndAttachSVG = async (node: any) => { // const nodeName = `${node.type}:${node.id}`; // console.log(altNode); if (node.canBeFlattened) { - console.log("altNode is", node); - if (node.svg) { // console.log(`SVG already rendered for ${nodeName}`); return node; diff --git a/packages/backend/src/common/commonRadius.ts b/packages/backend/src/common/commonRadius.ts index 90ece57a..5f8070eb 100644 --- a/packages/backend/src/common/commonRadius.ts +++ b/packages/backend/src/common/commonRadius.ts @@ -1,6 +1,25 @@ import { CornerRadius } from "types"; export const getCommonRadius = (node: SceneNode): CornerRadius => { + if ("rectangleCornerRadii" in node) { + const [topLeft, topRight, bottomRight, bottomLeft] = + node.rectangleCornerRadii as any; + if ( + topLeft === topRight && + topLeft === bottomRight && + topLeft === bottomLeft + ) { + return { all: topLeft }; + } + + return { + topLeft, + topRight, + bottomRight, + bottomLeft, + }; + } + if ( "cornerRadius" in node && node.cornerRadius !== figma.mixed && diff --git a/packages/backend/src/common/exportAsyncProxy.ts b/packages/backend/src/common/exportAsyncProxy.ts index 1d68ca59..04af43ab 100644 --- a/packages/backend/src/common/exportAsyncProxy.ts +++ b/packages/backend/src/common/exportAsyncProxy.ts @@ -22,7 +22,7 @@ export const exportAsyncProxy = async < } const figmaNode = (await figma.getNodeByIdAsync(node.id)) as ExportMixin; - console.log("getting figma id for", figmaNode); + // console.log("getting figma id for", figmaNode); if (figmaNode.exportAsync === undefined) { // console.log(node); diff --git a/packages/backend/src/html/builderImpl/htmlBorderRadius.ts b/packages/backend/src/html/builderImpl/htmlBorderRadius.ts index f2f7fc39..16d39260 100644 --- a/packages/backend/src/html/builderImpl/htmlBorderRadius.ts +++ b/packages/backend/src/html/builderImpl/htmlBorderRadius.ts @@ -2,13 +2,13 @@ import { getCommonRadius } from "../../common/commonRadius"; import { formatWithJSX } from "../../common/parseJSX"; export const htmlBorderRadius = (node: SceneNode, isJsx: boolean): string[] => { - const radius = getCommonRadius(node); if (node.type === "ELLIPSE") { return [formatWithJSX("border-radius", isJsx, 9999)]; } + const radius = getCommonRadius(node); + let comp: string[] = []; - let cornerValues: number[] = [0, 0, 0, 0]; let singleCorner: number = 0; if ("all" in radius) { @@ -17,21 +17,28 @@ export const htmlBorderRadius = (node: SceneNode, isJsx: boolean): string[] => { } singleCorner = radius.all; comp.push(formatWithJSX("border-radius", isJsx, radius.all)); - } else if ("topLeftRadius" in node) { - cornerValues = handleIndividualRadius(node); - comp.push( - ...cornerValues - .filter((d) => d > 0) - .map((value, index) => { - const property = [ - "border-top-left-radius", - "border-top-right-radius", - "border-bottom-right-radius", - "border-bottom-left-radius", - ][index]; - return formatWithJSX(property, isJsx, value); - }), - ); + } else { + const cornerValues = [ + radius.topLeft, + radius.topRight, + radius.bottomRight, + radius.bottomLeft, + ]; + + // Map each corner value to its corresponding CSS property + const cornerProperties = [ + "border-top-left-radius", + "border-top-right-radius", + "border-bottom-right-radius", + "border-bottom-left-radius", + ]; + + // Add CSS properties for non-zero corner values + for (let i = 0; i < 4; i++) { + if (cornerValues[i] > 0) { + comp.push(formatWithJSX(cornerProperties[i], isJsx, cornerValues[i])); + } + } } if ( @@ -45,13 +52,3 @@ export const htmlBorderRadius = (node: SceneNode, isJsx: boolean): string[] => { return comp; }; - -const handleIndividualRadius = (node: RectangleCornerMixin): number[] => { - const cornerValues = [ - node.topLeftRadius, - node.topRightRadius, - node.bottomRightRadius, - node.bottomLeftRadius, - ]; - return cornerValues; -}; diff --git a/packages/backend/src/html/htmlDefaultBuilder.ts b/packages/backend/src/html/htmlDefaultBuilder.ts index e9eede9b..6c721e9f 100644 --- a/packages/backend/src/html/htmlDefaultBuilder.ts +++ b/packages/backend/src/html/htmlDefaultBuilder.ts @@ -223,10 +223,8 @@ export class HtmlDefaultBuilder { position(): this { const { node, optimizeLayout, isJSX } = this; const isAbsolutePosition = commonIsAbsolutePosition(node, optimizeLayout); - console.log("isAbsolutePosition", isAbsolutePosition, "for node", node); if (isAbsolutePosition) { const { x, y } = getCommonPositionValue(node); - console.log("x, y, are", x, y); this.addStyles( formatWithJSX("left", isJSX, x), diff --git a/packages/backend/src/tailwind/builderImpl/tailwindBorder.ts b/packages/backend/src/tailwind/builderImpl/tailwindBorder.ts index c90095bd..57d4e159 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindBorder.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindBorder.ts @@ -142,7 +142,7 @@ export const tailwindBorderRadius = (node: SceneNode): string => { return `rounded${getRadius(radius.all)}`; } - // todo optimize for tr/tl/br/bl instead of t/r/l/b + // todo optimize for t/r/l/b instead of tr/tl/br/bl let comp: string[] = []; if (radius.topLeft !== 0) { comp.push(`rounded-tl${getRadius(radius.topLeft)}`); diff --git a/packages/plugin-ui/src/components/Preview.tsx b/packages/plugin-ui/src/components/Preview.tsx index 447d01c1..65c8d0f4 100644 --- a/packages/plugin-ui/src/components/Preview.tsx +++ b/packages/plugin-ui/src/components/Preview.tsx @@ -6,13 +6,17 @@ import { MonitorSmartphone, Smartphone, Circle, + Ruler, + MonitorIcon, } from "lucide-react"; const Preview: React.FC<{ htmlPreview: HTMLPreview; }> = (props) => { const [expanded, setExpanded] = useState(false); - const [viewMode, setViewMode] = useState<"desktop" | "mobile">("desktop"); + const [viewMode, setViewMode] = useState<"desktop" | "mobile" | "precision">( + "desktop", + ); const [animationClass, setAnimationClass] = useState(""); const [bgColor, setBgColor] = useState<"white" | "black">("white"); @@ -22,27 +26,26 @@ const Preview: React.FC<{ // Calculate content dimensions based on view mode const contentWidth = - viewMode === "desktop" ? containerWidth : Math.floor(containerWidth * 0.4); // Narrower for mobile - - // Adjust scale factor based on view mode - const scaleFactor = viewMode === "desktop" - ? Math.min( - containerWidth / props.htmlPreview.size.width, - containerHeight / props.htmlPreview.size.height, - ) - : Math.min( - contentWidth / props.htmlPreview.size.width, - containerHeight / props.htmlPreview.size.height, - ); + ? containerWidth + : viewMode === "mobile" + ? Math.floor(containerWidth * 0.4) // Narrower for mobile + : containerWidth; // For precision, use container width for the outer frame + + const scaleFactor = Math.min( + containerWidth / props.htmlPreview.size.width, + containerHeight / props.htmlPreview.size.height, + ); // Add animation when changing view mode useEffect(() => { - setAnimationClass( - viewMode === "desktop" - ? "animate-slide-in-left" - : "animate-slide-in-right", - ); + if (viewMode === "desktop") { + setAnimationClass("animate-slide-in-left"); + } else if (viewMode === "mobile") { + setAnimationClass("animate-slide-in-right"); + } else { + setAnimationClass("animate-fade-in"); + } const timer = setTimeout(() => setAnimationClass(""), 300); // Remove animation class after it completes return () => clearTimeout(timer); }, [viewMode]); @@ -65,15 +68,19 @@ const Preview: React.FC<{ Preview
- {/* Background Color Toggle */} - + {/* Background Color Toggle - Only show in desktop and mobile modes */} + {viewMode !== "precision" && ( + + )} {/* View Mode Toggle */}
@@ -101,6 +108,18 @@ const Preview: React.FC<{ > +
{/* Expand/Collapse Button */} @@ -134,26 +153,46 @@ const Preview: React.FC<{ height: viewMode === "mobile" ? Math.min(containerHeight * 0.9, containerHeight) - : containerHeight, + : containerHeight, // Use full container height for both desktop and precision transition: "width 0.3s ease, height 0.3s ease", }} > - {/* Device frame - just a border for mobile, no status bar or home indicator */} + {/* Device frame - no background for precision mode */}
- {/* Content - no padding needed anymore */} + {/* Content */}
Mobile view + ) : viewMode === "precision" ? ( + + + Precision view + ) : ( - + Desktop view )} From 4dd5448a158e1d31a4a581fbf6d501da7f0fa993 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Mon, 10 Mar 2025 01:16:42 -0300 Subject: [PATCH 095/168] Comment responsive preview (for now) --- packages/plugin-ui/src/PluginUI.tsx | 19 ++- packages/plugin-ui/src/components/Preview.tsx | 130 ++++++++---------- 2 files changed, 76 insertions(+), 73 deletions(-) diff --git a/packages/plugin-ui/src/PluginUI.tsx b/packages/plugin-ui/src/PluginUI.tsx index 9546508e..fc2feee8 100644 --- a/packages/plugin-ui/src/PluginUI.tsx +++ b/packages/plugin-ui/src/PluginUI.tsx @@ -43,10 +43,17 @@ const frameworks: Framework[] = ["HTML", "Tailwind", "Flutter", "SwiftUI"]; export const PluginUI = (props: PluginUIProps) => { const [showAbout, setShowAbout] = useState(false); + const [previewExpanded, setPreviewExpanded] = useState(false); + const [previewViewMode, setPreviewViewMode] = useState< + "desktop" | "mobile" | "precision" + >("precision"); + const [previewBgColor, setPreviewBgColor] = useState<"white" | "black">( + "white", + ); + if (props.isLoading) return ; const isEmpty = props.code === ""; - const warnings = props.warnings ?? []; return ( @@ -95,7 +102,15 @@ export const PluginUI = (props: PluginUIProps) => { ) : (
{isEmpty === false && props.htmlPreview && ( - + )} {warnings.length > 0 && } diff --git a/packages/plugin-ui/src/components/Preview.tsx b/packages/plugin-ui/src/components/Preview.tsx index 65c8d0f4..691af1f5 100644 --- a/packages/plugin-ui/src/components/Preview.tsx +++ b/packages/plugin-ui/src/components/Preview.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import React from "react"; import { HTMLPreview } from "types"; import { Maximize2, @@ -7,57 +7,49 @@ import { Smartphone, Circle, Ruler, - MonitorIcon, + Monitor, } from "lucide-react"; +import { cn } from "../lib/utils"; +// Update the component props to receive state from parent const Preview: React.FC<{ htmlPreview: HTMLPreview; + expanded: boolean; + setExpanded: React.Dispatch>; + viewMode: "desktop" | "mobile" | "precision"; + setViewMode: React.Dispatch< + React.SetStateAction<"desktop" | "mobile" | "precision"> + >; + bgColor: "white" | "black"; + setBgColor: React.Dispatch>; }> = (props) => { - const [expanded, setExpanded] = useState(false); - const [viewMode, setViewMode] = useState<"desktop" | "mobile" | "precision">( - "desktop", - ); - const [animationClass, setAnimationClass] = useState(""); - const [bgColor, setBgColor] = useState<"white" | "black">("white"); + const { + htmlPreview, + expanded, + setExpanded, + viewMode, + setViewMode, + bgColor, + setBgColor, + } = props; // Define consistent dimensions regardless of mode const containerWidth = expanded ? 320 : 240; const containerHeight = expanded ? 180 : 120; + // Calculate scale factor first to use in content width calculation + const scaleFactor = Math.min( + containerWidth / htmlPreview.size.width, + containerHeight / htmlPreview.size.height, + ); + // Calculate content dimensions based on view mode const contentWidth = viewMode === "desktop" ? containerWidth : viewMode === "mobile" ? Math.floor(containerWidth * 0.4) // Narrower for mobile - : containerWidth; // For precision, use container width for the outer frame - - const scaleFactor = Math.min( - containerWidth / props.htmlPreview.size.width, - containerHeight / props.htmlPreview.size.height, - ); - - // Add animation when changing view mode - useEffect(() => { - if (viewMode === "desktop") { - setAnimationClass("animate-slide-in-left"); - } else if (viewMode === "mobile") { - setAnimationClass("animate-slide-in-right"); - } else { - setAnimationClass("animate-fade-in"); - } - const timer = setTimeout(() => setAnimationClass(""), 300); // Remove animation class after it completes - return () => clearTimeout(timer); - }, [viewMode]); - - // Add animation when changing size - useEffect(() => { - const timer = setTimeout(() => setAnimationClass("animate-scale-in"), 50); - return () => { - clearTimeout(timer); - setAnimationClass(""); - }; - }, [expanded]); + : htmlPreview.size.width * scaleFactor; // For precision, use scaled original width return (
@@ -69,21 +61,18 @@ const Preview: React.FC<{
{/* Background Color Toggle - Only show in desktop and mobile modes */} - {viewMode !== "precision" && ( - - )} + + {/* View Mode Toggle */} -
+ {/*
-
+
*/} {/* Expand/Collapse Button */} + + {/* Hidden setting for using old plugin version */} +
+ +

+ The new version is up to 100x faster, but might still cause some issues. + If you encounter problems, you can switch to the old version + (and please report issues so they can be fixed). +

+
+
{/* Footer */} diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index 6f0779c7..55d04ccb 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -30,7 +30,7 @@ export interface PluginSettings FlutterSettings, SwiftUISettings { framework: Framework; - inlineStyle: boolean; + useOldPluginVersion2025: boolean; responsiveRoot: boolean; } // Messaging diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e36aa4dd..8b3c1f76 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -84,6 +84,9 @@ importers: clsx: specifier: ^2.1.1 version: 2.1.1 + copy-to-clipboard: + specifier: ^3.3.3 + version: 3.3.3 lucide-react: specifier: ^0.479.0 version: 0.479.0(react@19.0.0) @@ -110,8 +113,8 @@ importers: version: 1.0.7(tailwindcss@3.4.6) devDependencies: '@types/node': - specifier: ^22.13.9 - version: 22.13.9 + specifier: ^22.13.10 + version: 22.13.10 '@types/react': specifier: ^19.0.10 version: 19.0.10 @@ -126,19 +129,19 @@ importers: version: 8.26.0(eslint@9.22.0(jiti@1.21.7))(typescript@5.8.2) '@vitejs/plugin-react': specifier: ^4.3.4 - version: 4.3.4(vite@5.4.14(@types/node@22.13.9)) + version: 4.3.4(vite@5.4.14(@types/node@22.13.10)) '@vitejs/plugin-react-swc': specifier: ^3.8.0 - version: 3.8.0(@swc/helpers@0.5.15)(vite@5.4.14(@types/node@22.13.9)) + version: 3.8.0(@swc/helpers@0.5.15)(vite@5.4.14(@types/node@22.13.10)) autoprefixer: - specifier: ^10.4.20 - version: 10.4.20(postcss@8.5.3) + specifier: ^10.4.21 + version: 10.4.21(postcss@8.5.3) concurrently: specifier: ^9.1.2 version: 9.1.2 esbuild: - specifier: ^0.25.0 - version: 0.25.0 + specifier: ^0.25.1 + version: 0.25.1 eslint-config-custom: specifier: workspace:* version: link:../../packages/eslint-config-custom @@ -165,10 +168,10 @@ importers: version: 5.8.2 vite: specifier: ^5.4.14 - version: 5.4.14(@types/node@22.13.9) + version: 5.4.14(@types/node@22.13.10) vite-plugin-singlefile: - specifier: ^2.1.0 - version: 2.1.0(rollup@4.34.9)(vite@5.4.14(@types/node@22.13.9)) + specifier: ^2.2.0 + version: 2.2.0(rollup@4.35.0)(vite@5.4.14(@types/node@22.13.10)) packages/backend: dependencies: @@ -409,8 +412,8 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.25.0': - resolution: {integrity: sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==} + '@esbuild/aix-ppc64@0.25.1': + resolution: {integrity: sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] @@ -421,8 +424,8 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.25.0': - resolution: {integrity: sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==} + '@esbuild/android-arm64@0.25.1': + resolution: {integrity: sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==} engines: {node: '>=18'} cpu: [arm64] os: [android] @@ -433,8 +436,8 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.25.0': - resolution: {integrity: sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==} + '@esbuild/android-arm@0.25.1': + resolution: {integrity: sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==} engines: {node: '>=18'} cpu: [arm] os: [android] @@ -445,8 +448,8 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.25.0': - resolution: {integrity: sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==} + '@esbuild/android-x64@0.25.1': + resolution: {integrity: sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==} engines: {node: '>=18'} cpu: [x64] os: [android] @@ -457,8 +460,8 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.25.0': - resolution: {integrity: sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==} + '@esbuild/darwin-arm64@0.25.1': + resolution: {integrity: sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] @@ -469,8 +472,8 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.25.0': - resolution: {integrity: sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==} + '@esbuild/darwin-x64@0.25.1': + resolution: {integrity: sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==} engines: {node: '>=18'} cpu: [x64] os: [darwin] @@ -481,8 +484,8 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.25.0': - resolution: {integrity: sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==} + '@esbuild/freebsd-arm64@0.25.1': + resolution: {integrity: sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] @@ -493,8 +496,8 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.0': - resolution: {integrity: sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==} + '@esbuild/freebsd-x64@0.25.1': + resolution: {integrity: sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] @@ -505,8 +508,8 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.25.0': - resolution: {integrity: sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==} + '@esbuild/linux-arm64@0.25.1': + resolution: {integrity: sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==} engines: {node: '>=18'} cpu: [arm64] os: [linux] @@ -517,8 +520,8 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.25.0': - resolution: {integrity: sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==} + '@esbuild/linux-arm@0.25.1': + resolution: {integrity: sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==} engines: {node: '>=18'} cpu: [arm] os: [linux] @@ -529,8 +532,8 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.25.0': - resolution: {integrity: sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==} + '@esbuild/linux-ia32@0.25.1': + resolution: {integrity: sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==} engines: {node: '>=18'} cpu: [ia32] os: [linux] @@ -541,8 +544,8 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.25.0': - resolution: {integrity: sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==} + '@esbuild/linux-loong64@0.25.1': + resolution: {integrity: sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==} engines: {node: '>=18'} cpu: [loong64] os: [linux] @@ -553,8 +556,8 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.25.0': - resolution: {integrity: sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==} + '@esbuild/linux-mips64el@0.25.1': + resolution: {integrity: sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] @@ -565,8 +568,8 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.25.0': - resolution: {integrity: sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==} + '@esbuild/linux-ppc64@0.25.1': + resolution: {integrity: sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] @@ -577,8 +580,8 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.25.0': - resolution: {integrity: sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==} + '@esbuild/linux-riscv64@0.25.1': + resolution: {integrity: sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] @@ -589,8 +592,8 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.25.0': - resolution: {integrity: sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==} + '@esbuild/linux-s390x@0.25.1': + resolution: {integrity: sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==} engines: {node: '>=18'} cpu: [s390x] os: [linux] @@ -601,14 +604,14 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.25.0': - resolution: {integrity: sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==} + '@esbuild/linux-x64@0.25.1': + resolution: {integrity: sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.0': - resolution: {integrity: sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==} + '@esbuild/netbsd-arm64@0.25.1': + resolution: {integrity: sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] @@ -619,14 +622,14 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.0': - resolution: {integrity: sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==} + '@esbuild/netbsd-x64@0.25.1': + resolution: {integrity: sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.0': - resolution: {integrity: sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==} + '@esbuild/openbsd-arm64@0.25.1': + resolution: {integrity: sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] @@ -637,8 +640,8 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.0': - resolution: {integrity: sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==} + '@esbuild/openbsd-x64@0.25.1': + resolution: {integrity: sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] @@ -649,8 +652,8 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.25.0': - resolution: {integrity: sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==} + '@esbuild/sunos-x64@0.25.1': + resolution: {integrity: sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==} engines: {node: '>=18'} cpu: [x64] os: [sunos] @@ -661,8 +664,8 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.25.0': - resolution: {integrity: sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==} + '@esbuild/win32-arm64@0.25.1': + resolution: {integrity: sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==} engines: {node: '>=18'} cpu: [arm64] os: [win32] @@ -673,8 +676,8 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.25.0': - resolution: {integrity: sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==} + '@esbuild/win32-ia32@0.25.1': + resolution: {integrity: sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==} engines: {node: '>=18'} cpu: [ia32] os: [win32] @@ -685,8 +688,8 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.25.0': - resolution: {integrity: sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==} + '@esbuild/win32-x64@0.25.1': + resolution: {integrity: sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -958,96 +961,191 @@ packages: cpu: [arm] os: [android] + '@rollup/rollup-android-arm-eabi@4.35.0': + resolution: {integrity: sha512-uYQ2WfPaqz5QtVgMxfN6NpLD+no0MYHDBywl7itPYd3K5TjjSghNKmX8ic9S8NU8w81NVhJv/XojcHptRly7qQ==} + cpu: [arm] + os: [android] + '@rollup/rollup-android-arm64@4.34.9': resolution: {integrity: sha512-4KW7P53h6HtJf5Y608T1ISKvNIYLWRKMvfnG0c44M6In4DQVU58HZFEVhWINDZKp7FZps98G3gxwC1sb0wXUUg==} cpu: [arm64] os: [android] + '@rollup/rollup-android-arm64@4.35.0': + resolution: {integrity: sha512-FtKddj9XZudurLhdJnBl9fl6BwCJ3ky8riCXjEw3/UIbjmIY58ppWwPEvU3fNu+W7FUsAsB1CdH+7EQE6CXAPA==} + cpu: [arm64] + os: [android] + '@rollup/rollup-darwin-arm64@4.34.9': resolution: {integrity: sha512-0CY3/K54slrzLDjOA7TOjN1NuLKERBgk9nY5V34mhmuu673YNb+7ghaDUs6N0ujXR7fz5XaS5Aa6d2TNxZd0OQ==} cpu: [arm64] os: [darwin] + '@rollup/rollup-darwin-arm64@4.35.0': + resolution: {integrity: sha512-Uk+GjOJR6CY844/q6r5DR/6lkPFOw0hjfOIzVx22THJXMxktXG6CbejseJFznU8vHcEBLpiXKY3/6xc+cBm65Q==} + cpu: [arm64] + os: [darwin] + '@rollup/rollup-darwin-x64@4.34.9': resolution: {integrity: sha512-eOojSEAi/acnsJVYRxnMkPFqcxSMFfrw7r2iD9Q32SGkb/Q9FpUY1UlAu1DH9T7j++gZ0lHjnm4OyH2vCI7l7Q==} cpu: [x64] os: [darwin] + '@rollup/rollup-darwin-x64@4.35.0': + resolution: {integrity: sha512-3IrHjfAS6Vkp+5bISNQnPogRAW5GAV1n+bNCrDwXmfMHbPl5EhTmWtfmwlJxFRUCBZ+tZ/OxDyU08aF6NI/N5Q==} + cpu: [x64] + os: [darwin] + '@rollup/rollup-freebsd-arm64@4.34.9': resolution: {integrity: sha512-2lzjQPJbN5UnHm7bHIUKFMulGTQwdvOkouJDpPysJS+QFBGDJqcfh+CxxtG23Ik/9tEvnebQiylYoazFMAgrYw==} cpu: [arm64] os: [freebsd] + '@rollup/rollup-freebsd-arm64@4.35.0': + resolution: {integrity: sha512-sxjoD/6F9cDLSELuLNnY0fOrM9WA0KrM0vWm57XhrIMf5FGiN8D0l7fn+bpUeBSU7dCgPV2oX4zHAsAXyHFGcQ==} + cpu: [arm64] + os: [freebsd] + '@rollup/rollup-freebsd-x64@4.34.9': resolution: {integrity: sha512-SLl0hi2Ah2H7xQYd6Qaiu01kFPzQ+hqvdYSoOtHYg/zCIFs6t8sV95kaoqjzjFwuYQLtOI0RZre/Ke0nPaQV+g==} cpu: [x64] os: [freebsd] + '@rollup/rollup-freebsd-x64@4.35.0': + resolution: {integrity: sha512-2mpHCeRuD1u/2kruUiHSsnjWtHjqVbzhBkNVQ1aVD63CcexKVcQGwJ2g5VphOd84GvxfSvnnlEyBtQCE5hxVVw==} + cpu: [x64] + os: [freebsd] + '@rollup/rollup-linux-arm-gnueabihf@4.34.9': resolution: {integrity: sha512-88I+D3TeKItrw+Y/2ud4Tw0+3CxQ2kLgu3QvrogZ0OfkmX/DEppehus7L3TS2Q4lpB+hYyxhkQiYPJ6Mf5/dPg==} cpu: [arm] os: [linux] + '@rollup/rollup-linux-arm-gnueabihf@4.35.0': + resolution: {integrity: sha512-mrA0v3QMy6ZSvEuLs0dMxcO2LnaCONs1Z73GUDBHWbY8tFFocM6yl7YyMu7rz4zS81NDSqhrUuolyZXGi8TEqg==} + cpu: [arm] + os: [linux] + '@rollup/rollup-linux-arm-musleabihf@4.34.9': resolution: {integrity: sha512-3qyfWljSFHi9zH0KgtEPG4cBXHDFhwD8kwg6xLfHQ0IWuH9crp005GfoUUh/6w9/FWGBwEHg3lxK1iHRN1MFlA==} cpu: [arm] os: [linux] + '@rollup/rollup-linux-arm-musleabihf@4.35.0': + resolution: {integrity: sha512-DnYhhzcvTAKNexIql8pFajr0PiDGrIsBYPRvCKlA5ixSS3uwo/CWNZxB09jhIapEIg945KOzcYEAGGSmTSpk7A==} + cpu: [arm] + os: [linux] + '@rollup/rollup-linux-arm64-gnu@4.34.9': resolution: {integrity: sha512-6TZjPHjKZUQKmVKMUowF3ewHxctrRR09eYyvT5eFv8w/fXarEra83A2mHTVJLA5xU91aCNOUnM+DWFMSbQ0Nxw==} cpu: [arm64] os: [linux] + '@rollup/rollup-linux-arm64-gnu@4.35.0': + resolution: {integrity: sha512-uagpnH2M2g2b5iLsCTZ35CL1FgyuzzJQ8L9VtlJ+FckBXroTwNOaD0z0/UF+k5K3aNQjbm8LIVpxykUOQt1m/A==} + cpu: [arm64] + os: [linux] + '@rollup/rollup-linux-arm64-musl@4.34.9': resolution: {integrity: sha512-LD2fytxZJZ6xzOKnMbIpgzFOuIKlxVOpiMAXawsAZ2mHBPEYOnLRK5TTEsID6z4eM23DuO88X0Tq1mErHMVq0A==} cpu: [arm64] os: [linux] + '@rollup/rollup-linux-arm64-musl@4.35.0': + resolution: {integrity: sha512-XQxVOCd6VJeHQA/7YcqyV0/88N6ysSVzRjJ9I9UA/xXpEsjvAgDTgH3wQYz5bmr7SPtVK2TsP2fQ2N9L4ukoUg==} + cpu: [arm64] + os: [linux] + '@rollup/rollup-linux-loongarch64-gnu@4.34.9': resolution: {integrity: sha512-dRAgTfDsn0TE0HI6cmo13hemKpVHOEyeciGtvlBTkpx/F65kTvShtY/EVyZEIfxFkV5JJTuQ9tP5HGBS0hfxIg==} cpu: [loong64] os: [linux] + '@rollup/rollup-linux-loongarch64-gnu@4.35.0': + resolution: {integrity: sha512-5pMT5PzfgwcXEwOaSrqVsz/LvjDZt+vQ8RT/70yhPU06PTuq8WaHhfT1LW+cdD7mW6i/J5/XIkX/1tCAkh1W6g==} + cpu: [loong64] + os: [linux] + '@rollup/rollup-linux-powerpc64le-gnu@4.34.9': resolution: {integrity: sha512-PHcNOAEhkoMSQtMf+rJofwisZqaU8iQ8EaSps58f5HYll9EAY5BSErCZ8qBDMVbq88h4UxaNPlbrKqfWP8RfJA==} cpu: [ppc64] os: [linux] + '@rollup/rollup-linux-powerpc64le-gnu@4.35.0': + resolution: {integrity: sha512-c+zkcvbhbXF98f4CtEIP1EBA/lCic5xB0lToneZYvMeKu5Kamq3O8gqrxiYYLzlZH6E3Aq+TSW86E4ay8iD8EA==} + cpu: [ppc64] + os: [linux] + '@rollup/rollup-linux-riscv64-gnu@4.34.9': resolution: {integrity: sha512-Z2i0Uy5G96KBYKjeQFKbbsB54xFOL5/y1P5wNBsbXB8yE+At3oh0DVMjQVzCJRJSfReiB2tX8T6HUFZ2k8iaKg==} cpu: [riscv64] os: [linux] + '@rollup/rollup-linux-riscv64-gnu@4.35.0': + resolution: {integrity: sha512-s91fuAHdOwH/Tad2tzTtPX7UZyytHIRR6V4+2IGlV0Cej5rkG0R61SX4l4y9sh0JBibMiploZx3oHKPnQBKe4g==} + cpu: [riscv64] + os: [linux] + '@rollup/rollup-linux-s390x-gnu@4.34.9': resolution: {integrity: sha512-U+5SwTMoeYXoDzJX5dhDTxRltSrIax8KWwfaaYcynuJw8mT33W7oOgz0a+AaXtGuvhzTr2tVKh5UO8GVANTxyQ==} cpu: [s390x] os: [linux] + '@rollup/rollup-linux-s390x-gnu@4.35.0': + resolution: {integrity: sha512-hQRkPQPLYJZYGP+Hj4fR9dDBMIM7zrzJDWFEMPdTnTy95Ljnv0/4w/ixFw3pTBMEuuEuoqtBINYND4M7ujcuQw==} + cpu: [s390x] + os: [linux] + '@rollup/rollup-linux-x64-gnu@4.34.9': resolution: {integrity: sha512-FwBHNSOjUTQLP4MG7y6rR6qbGw4MFeQnIBrMe161QGaQoBQLqSUEKlHIiVgF3g/mb3lxlxzJOpIBhaP+C+KP2A==} cpu: [x64] os: [linux] + '@rollup/rollup-linux-x64-gnu@4.35.0': + resolution: {integrity: sha512-Pim1T8rXOri+0HmV4CdKSGrqcBWX0d1HoPnQ0uw0bdp1aP5SdQVNBy8LjYncvnLgu3fnnCt17xjWGd4cqh8/hA==} + cpu: [x64] + os: [linux] + '@rollup/rollup-linux-x64-musl@4.34.9': resolution: {integrity: sha512-cYRpV4650z2I3/s6+5/LONkjIz8MBeqrk+vPXV10ORBnshpn8S32bPqQ2Utv39jCiDcO2eJTuSlPXpnvmaIgRA==} cpu: [x64] os: [linux] + '@rollup/rollup-linux-x64-musl@4.35.0': + resolution: {integrity: sha512-QysqXzYiDvQWfUiTm8XmJNO2zm9yC9P/2Gkrwg2dH9cxotQzunBHYr6jk4SujCTqnfGxduOmQcI7c2ryuW8XVg==} + cpu: [x64] + os: [linux] + '@rollup/rollup-win32-arm64-msvc@4.34.9': resolution: {integrity: sha512-z4mQK9dAN6byRA/vsSgQiPeuO63wdiDxZ9yg9iyX2QTzKuQM7T4xlBoeUP/J8uiFkqxkcWndWi+W7bXdPbt27Q==} cpu: [arm64] os: [win32] + '@rollup/rollup-win32-arm64-msvc@4.35.0': + resolution: {integrity: sha512-OUOlGqPkVJCdJETKOCEf1mw848ZyJ5w50/rZ/3IBQVdLfR5jk/6Sr5m3iO2tdPgwo0x7VcncYuOvMhBWZq8ayg==} + cpu: [arm64] + os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.34.9': resolution: {integrity: sha512-KB48mPtaoHy1AwDNkAJfHXvHp24H0ryZog28spEs0V48l3H1fr4i37tiyHsgKZJnCmvxsbATdZGBpbmxTE3a9w==} cpu: [ia32] os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.35.0': + resolution: {integrity: sha512-2/lsgejMrtwQe44glq7AFFHLfJBPafpsTa6JvP2NGef/ifOa4KBoglVf7AKN7EV9o32evBPRqfg96fEHzWo5kw==} + cpu: [ia32] + os: [win32] + '@rollup/rollup-win32-x64-msvc@4.34.9': resolution: {integrity: sha512-AyleYRPU7+rgkMWbEh71fQlrzRfeP6SyMnRf9XX4fCdDPAJumdSBqYEcWPMzVQ4ScAl7E4oFfK0GUVn77xSwbw==} cpu: [x64] os: [win32] + '@rollup/rollup-win32-x64-msvc@4.35.0': + resolution: {integrity: sha512-PIQeY5XDkrOysbQblSW7v3l1MDZzkTEzAfTPkj5VAu3FW8fS4ynyLg2sINp0fp3SjZ8xkRYpLqoKcYqAkhU1dw==} + cpu: [x64] + os: [win32] + '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} @@ -1156,6 +1254,9 @@ packages: '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + '@types/node@22.13.10': + resolution: {integrity: sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==} + '@types/node@22.13.9': resolution: {integrity: sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw==} @@ -1323,6 +1424,13 @@ packages: peerDependencies: postcss: ^8.1.0 + autoprefixer@10.4.21: + resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} @@ -1394,6 +1502,9 @@ packages: caniuse-lite@1.0.30001702: resolution: {integrity: sha512-LoPe/D7zioC0REI5W73PeR1e1MLCipRGq/VkovJnd6Df+QVqT+vT33OXCp8QUd7kA7RZrHWxb1B36OQKI/0gOA==} + caniuse-lite@1.0.30001703: + resolution: {integrity: sha512-kRlAGTRWgPsOj7oARC9m1okJEXdL/8fekFVcxA8Hl7GH4r/sN4OJn/i6Flde373T50KS7Y37oFbMwlE8+F42kQ==} + chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -1545,8 +1656,8 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - electron-to-chromium@1.5.113: - resolution: {integrity: sha512-wjT2O4hX+wdWPJ76gWSkMhcHAV2PTMX+QetUCPYEdCIe+cxmgzzSSiGRCKW8nuh4mwKZlpv0xvoW7OF2X+wmHg==} + electron-to-chromium@1.5.114: + resolution: {integrity: sha512-DFptFef3iktoKlFQK/afbo274/XNWD00Am0xa7M8FZUepHlHT8PEuiNBoRfFHbH1okqN58AlhbJ4QTkcnXorjA==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -1595,8 +1706,8 @@ packages: engines: {node: '>=12'} hasBin: true - esbuild@0.25.0: - resolution: {integrity: sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==} + esbuild@0.25.1: + resolution: {integrity: sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==} engines: {node: '>=18'} hasBin: true @@ -2562,6 +2673,11 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + rollup@4.35.0: + resolution: {integrity: sha512-kg6oI4g+vc41vePJyO6dHt/yl0Rz3Thv0kJeVQ3D1kS3E5XSuKbPc29G4IpT/Kv1KQwgHVcN+HtyS+HYLNSvQg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -2895,11 +3011,11 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - vite-plugin-singlefile@2.1.0: - resolution: {integrity: sha512-7tJo+UgZABlKpY/nubth/wxJ4+pUGREPnEwNOknxwl2MM0zTvF14KTU4Ln1lc140gjLLV5mjDrvuoquU7OZqCg==} + vite-plugin-singlefile@2.2.0: + resolution: {integrity: sha512-Ik1wXmJaGzeQtUeIV7JprDUqqy6DlLzXAY27Blei5peE4c9VJF+Kp9xWDJeuX0RJUZmFbIAuw1/RAh06A+Ql7w==} engines: {node: '>18.0.0'} peerDependencies: - rollup: ^4.28.1 + rollup: ^4.35.0 vite: ^5.4.11 || ^6.0.0 vite@5.4.14: @@ -3131,145 +3247,145 @@ snapshots: '@esbuild/aix-ppc64@0.21.5': optional: true - '@esbuild/aix-ppc64@0.25.0': + '@esbuild/aix-ppc64@0.25.1': optional: true '@esbuild/android-arm64@0.21.5': optional: true - '@esbuild/android-arm64@0.25.0': + '@esbuild/android-arm64@0.25.1': optional: true '@esbuild/android-arm@0.21.5': optional: true - '@esbuild/android-arm@0.25.0': + '@esbuild/android-arm@0.25.1': optional: true '@esbuild/android-x64@0.21.5': optional: true - '@esbuild/android-x64@0.25.0': + '@esbuild/android-x64@0.25.1': optional: true '@esbuild/darwin-arm64@0.21.5': optional: true - '@esbuild/darwin-arm64@0.25.0': + '@esbuild/darwin-arm64@0.25.1': optional: true '@esbuild/darwin-x64@0.21.5': optional: true - '@esbuild/darwin-x64@0.25.0': + '@esbuild/darwin-x64@0.25.1': optional: true '@esbuild/freebsd-arm64@0.21.5': optional: true - '@esbuild/freebsd-arm64@0.25.0': + '@esbuild/freebsd-arm64@0.25.1': optional: true '@esbuild/freebsd-x64@0.21.5': optional: true - '@esbuild/freebsd-x64@0.25.0': + '@esbuild/freebsd-x64@0.25.1': optional: true '@esbuild/linux-arm64@0.21.5': optional: true - '@esbuild/linux-arm64@0.25.0': + '@esbuild/linux-arm64@0.25.1': optional: true '@esbuild/linux-arm@0.21.5': optional: true - '@esbuild/linux-arm@0.25.0': + '@esbuild/linux-arm@0.25.1': optional: true '@esbuild/linux-ia32@0.21.5': optional: true - '@esbuild/linux-ia32@0.25.0': + '@esbuild/linux-ia32@0.25.1': optional: true '@esbuild/linux-loong64@0.21.5': optional: true - '@esbuild/linux-loong64@0.25.0': + '@esbuild/linux-loong64@0.25.1': optional: true '@esbuild/linux-mips64el@0.21.5': optional: true - '@esbuild/linux-mips64el@0.25.0': + '@esbuild/linux-mips64el@0.25.1': optional: true '@esbuild/linux-ppc64@0.21.5': optional: true - '@esbuild/linux-ppc64@0.25.0': + '@esbuild/linux-ppc64@0.25.1': optional: true '@esbuild/linux-riscv64@0.21.5': optional: true - '@esbuild/linux-riscv64@0.25.0': + '@esbuild/linux-riscv64@0.25.1': optional: true '@esbuild/linux-s390x@0.21.5': optional: true - '@esbuild/linux-s390x@0.25.0': + '@esbuild/linux-s390x@0.25.1': optional: true '@esbuild/linux-x64@0.21.5': optional: true - '@esbuild/linux-x64@0.25.0': + '@esbuild/linux-x64@0.25.1': optional: true - '@esbuild/netbsd-arm64@0.25.0': + '@esbuild/netbsd-arm64@0.25.1': optional: true '@esbuild/netbsd-x64@0.21.5': optional: true - '@esbuild/netbsd-x64@0.25.0': + '@esbuild/netbsd-x64@0.25.1': optional: true - '@esbuild/openbsd-arm64@0.25.0': + '@esbuild/openbsd-arm64@0.25.1': optional: true '@esbuild/openbsd-x64@0.21.5': optional: true - '@esbuild/openbsd-x64@0.25.0': + '@esbuild/openbsd-x64@0.25.1': optional: true '@esbuild/sunos-x64@0.21.5': optional: true - '@esbuild/sunos-x64@0.25.0': + '@esbuild/sunos-x64@0.25.1': optional: true '@esbuild/win32-arm64@0.21.5': optional: true - '@esbuild/win32-arm64@0.25.0': + '@esbuild/win32-arm64@0.25.1': optional: true '@esbuild/win32-ia32@0.21.5': optional: true - '@esbuild/win32-ia32@0.25.0': + '@esbuild/win32-ia32@0.25.1': optional: true '@esbuild/win32-x64@0.21.5': optional: true - '@esbuild/win32-x64@0.25.0': + '@esbuild/win32-x64@0.25.1': optional: true '@eslint-community/eslint-utils@4.4.1(eslint@9.22.0(jiti@1.21.7))': @@ -3482,60 +3598,117 @@ snapshots: '@rollup/rollup-android-arm-eabi@4.34.9': optional: true + '@rollup/rollup-android-arm-eabi@4.35.0': + optional: true + '@rollup/rollup-android-arm64@4.34.9': optional: true + '@rollup/rollup-android-arm64@4.35.0': + optional: true + '@rollup/rollup-darwin-arm64@4.34.9': optional: true + '@rollup/rollup-darwin-arm64@4.35.0': + optional: true + '@rollup/rollup-darwin-x64@4.34.9': optional: true + '@rollup/rollup-darwin-x64@4.35.0': + optional: true + '@rollup/rollup-freebsd-arm64@4.34.9': optional: true + '@rollup/rollup-freebsd-arm64@4.35.0': + optional: true + '@rollup/rollup-freebsd-x64@4.34.9': optional: true + '@rollup/rollup-freebsd-x64@4.35.0': + optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.34.9': optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.35.0': + optional: true + '@rollup/rollup-linux-arm-musleabihf@4.34.9': optional: true + '@rollup/rollup-linux-arm-musleabihf@4.35.0': + optional: true + '@rollup/rollup-linux-arm64-gnu@4.34.9': optional: true + '@rollup/rollup-linux-arm64-gnu@4.35.0': + optional: true + '@rollup/rollup-linux-arm64-musl@4.34.9': optional: true + '@rollup/rollup-linux-arm64-musl@4.35.0': + optional: true + '@rollup/rollup-linux-loongarch64-gnu@4.34.9': optional: true + '@rollup/rollup-linux-loongarch64-gnu@4.35.0': + optional: true + '@rollup/rollup-linux-powerpc64le-gnu@4.34.9': optional: true + '@rollup/rollup-linux-powerpc64le-gnu@4.35.0': + optional: true + '@rollup/rollup-linux-riscv64-gnu@4.34.9': optional: true + '@rollup/rollup-linux-riscv64-gnu@4.35.0': + optional: true + '@rollup/rollup-linux-s390x-gnu@4.34.9': optional: true + '@rollup/rollup-linux-s390x-gnu@4.35.0': + optional: true + '@rollup/rollup-linux-x64-gnu@4.34.9': optional: true + '@rollup/rollup-linux-x64-gnu@4.35.0': + optional: true + '@rollup/rollup-linux-x64-musl@4.34.9': optional: true + '@rollup/rollup-linux-x64-musl@4.35.0': + optional: true + '@rollup/rollup-win32-arm64-msvc@4.34.9': optional: true + '@rollup/rollup-win32-arm64-msvc@4.35.0': + optional: true + '@rollup/rollup-win32-ia32-msvc@4.34.9': optional: true + '@rollup/rollup-win32-ia32-msvc@4.35.0': + optional: true + '@rollup/rollup-win32-x64-msvc@4.34.9': optional: true + '@rollup/rollup-win32-x64-msvc@4.35.0': + optional: true + '@rtsao/scc@1.1.0': {} '@rushstack/eslint-patch@1.10.5': {} @@ -3628,6 +3801,10 @@ snapshots: '@types/json5@0.0.29': {} + '@types/node@22.13.10': + dependencies: + undici-types: 6.20.0 + '@types/node@22.13.9': dependencies: undici-types: 6.20.0 @@ -3723,21 +3900,21 @@ snapshots: '@typescript-eslint/types': 8.26.0 eslint-visitor-keys: 4.2.0 - '@vitejs/plugin-react-swc@3.8.0(@swc/helpers@0.5.15)(vite@5.4.14(@types/node@22.13.9))': + '@vitejs/plugin-react-swc@3.8.0(@swc/helpers@0.5.15)(vite@5.4.14(@types/node@22.13.10))': dependencies: '@swc/core': 1.11.8(@swc/helpers@0.5.15) - vite: 5.4.14(@types/node@22.13.9) + vite: 5.4.14(@types/node@22.13.10) transitivePeerDependencies: - '@swc/helpers' - '@vitejs/plugin-react@4.3.4(vite@5.4.14(@types/node@22.13.9))': + '@vitejs/plugin-react@4.3.4(vite@5.4.14(@types/node@22.13.10))': dependencies: '@babel/core': 7.26.9 '@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.9) '@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.9) '@types/babel__core': 7.20.5 react-refresh: 0.14.2 - vite: 5.4.14(@types/node@22.13.9) + vite: 5.4.14(@types/node@22.13.10) transitivePeerDependencies: - supports-color @@ -3855,6 +4032,16 @@ snapshots: postcss: 8.5.3 postcss-value-parser: 4.2.0 + autoprefixer@10.4.21(postcss@8.5.3): + dependencies: + browserslist: 4.24.4 + caniuse-lite: 1.0.30001703 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.1.1 + postcss: 8.5.3 + postcss-value-parser: 4.2.0 + available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.1.0 @@ -3882,14 +4069,14 @@ snapshots: browserslist@4.24.4: dependencies: - caniuse-lite: 1.0.30001702 - electron-to-chromium: 1.5.113 + caniuse-lite: 1.0.30001703 + electron-to-chromium: 1.5.114 node-releases: 2.0.19 update-browserslist-db: 1.1.3(browserslist@4.24.4) - bundle-require@5.1.0(esbuild@0.25.0): + bundle-require@5.1.0(esbuild@0.25.1): dependencies: - esbuild: 0.25.0 + esbuild: 0.25.1 load-tsconfig: 0.2.5 busboy@1.6.0: @@ -3921,6 +4108,8 @@ snapshots: caniuse-lite@1.0.30001702: {} + caniuse-lite@1.0.30001703: {} + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 @@ -4073,7 +4262,7 @@ snapshots: eastasianwidth@0.2.0: {} - electron-to-chromium@1.5.113: {} + electron-to-chromium@1.5.114: {} emoji-regex@8.0.0: {} @@ -4208,33 +4397,33 @@ snapshots: '@esbuild/win32-ia32': 0.21.5 '@esbuild/win32-x64': 0.21.5 - esbuild@0.25.0: + esbuild@0.25.1: optionalDependencies: - '@esbuild/aix-ppc64': 0.25.0 - '@esbuild/android-arm': 0.25.0 - '@esbuild/android-arm64': 0.25.0 - '@esbuild/android-x64': 0.25.0 - '@esbuild/darwin-arm64': 0.25.0 - '@esbuild/darwin-x64': 0.25.0 - '@esbuild/freebsd-arm64': 0.25.0 - '@esbuild/freebsd-x64': 0.25.0 - '@esbuild/linux-arm': 0.25.0 - '@esbuild/linux-arm64': 0.25.0 - '@esbuild/linux-ia32': 0.25.0 - '@esbuild/linux-loong64': 0.25.0 - '@esbuild/linux-mips64el': 0.25.0 - '@esbuild/linux-ppc64': 0.25.0 - '@esbuild/linux-riscv64': 0.25.0 - '@esbuild/linux-s390x': 0.25.0 - '@esbuild/linux-x64': 0.25.0 - '@esbuild/netbsd-arm64': 0.25.0 - '@esbuild/netbsd-x64': 0.25.0 - '@esbuild/openbsd-arm64': 0.25.0 - '@esbuild/openbsd-x64': 0.25.0 - '@esbuild/sunos-x64': 0.25.0 - '@esbuild/win32-arm64': 0.25.0 - '@esbuild/win32-ia32': 0.25.0 - '@esbuild/win32-x64': 0.25.0 + '@esbuild/aix-ppc64': 0.25.1 + '@esbuild/android-arm': 0.25.1 + '@esbuild/android-arm64': 0.25.1 + '@esbuild/android-x64': 0.25.1 + '@esbuild/darwin-arm64': 0.25.1 + '@esbuild/darwin-x64': 0.25.1 + '@esbuild/freebsd-arm64': 0.25.1 + '@esbuild/freebsd-x64': 0.25.1 + '@esbuild/linux-arm': 0.25.1 + '@esbuild/linux-arm64': 0.25.1 + '@esbuild/linux-ia32': 0.25.1 + '@esbuild/linux-loong64': 0.25.1 + '@esbuild/linux-mips64el': 0.25.1 + '@esbuild/linux-ppc64': 0.25.1 + '@esbuild/linux-riscv64': 0.25.1 + '@esbuild/linux-s390x': 0.25.1 + '@esbuild/linux-x64': 0.25.1 + '@esbuild/netbsd-arm64': 0.25.1 + '@esbuild/netbsd-x64': 0.25.1 + '@esbuild/openbsd-arm64': 0.25.1 + '@esbuild/openbsd-x64': 0.25.1 + '@esbuild/sunos-x64': 0.25.1 + '@esbuild/win32-arm64': 0.25.1 + '@esbuild/win32-ia32': 0.25.1 + '@esbuild/win32-x64': 0.25.1 escalade@3.2.0: {} @@ -5260,6 +5449,31 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.34.9 fsevents: 2.3.3 + rollup@4.35.0: + dependencies: + '@types/estree': 1.0.6 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.35.0 + '@rollup/rollup-android-arm64': 4.35.0 + '@rollup/rollup-darwin-arm64': 4.35.0 + '@rollup/rollup-darwin-x64': 4.35.0 + '@rollup/rollup-freebsd-arm64': 4.35.0 + '@rollup/rollup-freebsd-x64': 4.35.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.35.0 + '@rollup/rollup-linux-arm-musleabihf': 4.35.0 + '@rollup/rollup-linux-arm64-gnu': 4.35.0 + '@rollup/rollup-linux-arm64-musl': 4.35.0 + '@rollup/rollup-linux-loongarch64-gnu': 4.35.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.35.0 + '@rollup/rollup-linux-riscv64-gnu': 4.35.0 + '@rollup/rollup-linux-s390x-gnu': 4.35.0 + '@rollup/rollup-linux-x64-gnu': 4.35.0 + '@rollup/rollup-linux-x64-musl': 4.35.0 + '@rollup/rollup-win32-arm64-msvc': 4.35.0 + '@rollup/rollup-win32-ia32-msvc': 4.35.0 + '@rollup/rollup-win32-x64-msvc': 4.35.0 + fsevents: 2.3.3 + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -5575,12 +5789,12 @@ snapshots: tsup@8.4.0(@swc/core@1.11.8(@swc/helpers@0.5.15))(jiti@1.21.7)(postcss@8.5.3)(typescript@5.8.2)(yaml@2.7.0): dependencies: - bundle-require: 5.1.0(esbuild@0.25.0) + bundle-require: 5.1.0(esbuild@0.25.1) cac: 6.7.14 chokidar: 4.0.3 consola: 3.4.0 debug: 4.4.0 - esbuild: 0.25.0 + esbuild: 0.25.1 joycon: 3.1.1 picocolors: 1.1.1 postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.3)(yaml@2.7.0) @@ -5688,19 +5902,19 @@ snapshots: util-deprecate@1.0.2: {} - vite-plugin-singlefile@2.1.0(rollup@4.34.9)(vite@5.4.14(@types/node@22.13.9)): + vite-plugin-singlefile@2.2.0(rollup@4.35.0)(vite@5.4.14(@types/node@22.13.10)): dependencies: micromatch: 4.0.8 - rollup: 4.34.9 - vite: 5.4.14(@types/node@22.13.9) + rollup: 4.35.0 + vite: 5.4.14(@types/node@22.13.10) - vite@5.4.14(@types/node@22.13.9): + vite@5.4.14(@types/node@22.13.10): dependencies: esbuild: 0.21.5 postcss: 8.5.3 - rollup: 4.34.9 + rollup: 4.35.0 optionalDependencies: - '@types/node': 22.13.9 + '@types/node': 22.13.10 fsevents: 2.3.3 webidl-conversions@4.0.2: {} From 672ad5c55f5a724632b2453ae9164e0e0b18bbc0 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Mon, 10 Mar 2025 12:19:54 -0300 Subject: [PATCH 100/168] Fix --- packages/backend/src/code.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index 28e6ec30..02a0fb83 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -468,7 +468,6 @@ export const run = async (settings: PluginSettings) => { if (useOldPluginVersion2025) { convertedSelection = oldConvertNodesToAltNodes(selection, null); console.log("convertedSelection", convertedSelection); - console.log("convertedSelection", convertedSelection[0].children[0]); } else { const nodeJson = await nodesToJSON(selection, settings); console.log(`[benchmark] nodesToJSON: ${Date.now() - nodeToJSONStart}ms`); From fd771395586fa489602a5e8d6fb99d9411228209 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Mon, 10 Mar 2025 13:08:17 -0300 Subject: [PATCH 101/168] Debug --- apps/plugin/plugin-src/code.ts | 5 +-- packages/backend/src/code.ts | 40 +++++-------------- .../src/html/builderImpl/htmlBorderRadius.ts | 27 +++++++------ .../backend/src/html/htmlDefaultBuilder.ts | 1 - .../tailwind/builderImpl/tailwindBorder.ts | 1 - packages/backend/src/tailwind/tailwindMain.ts | 5 ++- 6 files changed, 30 insertions(+), 49 deletions(-) diff --git a/apps/plugin/plugin-src/code.ts b/apps/plugin/plugin-src/code.ts index c8fa2d78..3fe67a9b 100644 --- a/apps/plugin/plugin-src/code.ts +++ b/apps/plugin/plugin-src/code.ts @@ -198,10 +198,7 @@ const standardMode = async () => { } try { - result.newConversion = await convertNodesToAltNodes( - result.json || [], - null, - ); + result.newConversion = await nodesToJSON(nodes, userPluginSettings); } catch (error) { console.error("Error in new conversion:", error); } diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index 02a0fb83..a592cfe4 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -187,39 +187,19 @@ const processNodePair = async ( ? cleanName : `${cleanName}_${count.toString().padStart(2, "0")}`; - // Check if we need to handle gradients - const hasGradient = GRADIENT_PROPERTIES.some((propName) => { + GRADIENT_PROPERTIES.forEach((propName) => { const property = jsonNode[propName]; - return ( + if ( + propName in figmaNode && property && - Array.isArray(property) && - property.length > 0 && - property.some( - (item: any) => item.type && item.type.startsWith("GRADIENT_"), - ) - ); + property.some((item: any) => item.type.startsWith("GRADIENT_")) + ) { + jsonNode[propName] = JSON.parse( + JSON.stringify((figmaNode as any)[propName]), + ); + } }); - // Handle gradients - if (hasGradient) { - GRADIENT_PROPERTIES.forEach((propName) => { - const property = jsonNode[propName]; - if ( - property && - Array.isArray(property) && - property.length > 0 && - property.some( - (item) => item.type && item.type.startsWith("GRADIENT_"), - ) && - propName in figmaNode - ) { - jsonNode[propName] = JSON.parse( - JSON.stringify((figmaNode as any)[propName]), - ); - } - }); - } - // Handle text-specific properties if (figmaNode.type === "TEXT") { const getSegmentsStart = Date.now(); @@ -376,7 +356,7 @@ const processNodePair = async ( jsonNode.children[i], figmaNode.children[i], settings, - jsonNode, // Pass the current node as parent for its children + jsonNode, ); } diff --git a/packages/backend/src/html/builderImpl/htmlBorderRadius.ts b/packages/backend/src/html/builderImpl/htmlBorderRadius.ts index 16d39260..33451874 100644 --- a/packages/backend/src/html/builderImpl/htmlBorderRadius.ts +++ b/packages/backend/src/html/builderImpl/htmlBorderRadius.ts @@ -2,18 +2,29 @@ import { getCommonRadius } from "../../common/commonRadius"; import { formatWithJSX } from "../../common/parseJSX"; export const htmlBorderRadius = (node: SceneNode, isJsx: boolean): string[] => { + let comp: string[] = []; + + if ( + "children" in node && + node.children.length > 0 && + "clipsContent" in node && + node.clipsContent === true + ) { + comp.push(formatWithJSX("overflow", isJsx, "hidden")); + } + if (node.type === "ELLIPSE") { - return [formatWithJSX("border-radius", isJsx, 9999)]; + comp.push(formatWithJSX("border-radius", isJsx, 9999)); + return comp; } const radius = getCommonRadius(node); - let comp: string[] = []; let singleCorner: number = 0; if ("all" in radius) { if (radius.all === 0) { - return []; + return comp; } singleCorner = radius.all; comp.push(formatWithJSX("border-radius", isJsx, radius.all)); @@ -41,14 +52,6 @@ export const htmlBorderRadius = (node: SceneNode, isJsx: boolean): string[] => { } } - if ( - "children" in node && - "clipsContent" in node && - node.children.length > 0 && - node.clipsContent === true - ) { - comp.push(formatWithJSX("overflow", isJsx, "hidden")); - } - + console.log("comp was", comp); return comp; }; diff --git a/packages/backend/src/html/htmlDefaultBuilder.ts b/packages/backend/src/html/htmlDefaultBuilder.ts index 510ecccf..8f6469c2 100644 --- a/packages/backend/src/html/htmlDefaultBuilder.ts +++ b/packages/backend/src/html/htmlDefaultBuilder.ts @@ -262,7 +262,6 @@ export class HtmlDefaultBuilder { position(): this { const { node, optimizeLayout, isJSX } = this; const isAbsolutePosition = commonIsAbsolutePosition(node, optimizeLayout); - console.log("node is absolute", isAbsolutePosition, node, node.parent); if (isAbsolutePosition) { const { x, y } = getCommonPositionValue(node); diff --git a/packages/backend/src/tailwind/builderImpl/tailwindBorder.ts b/packages/backend/src/tailwind/builderImpl/tailwindBorder.ts index ed48f0e7..23459cc4 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindBorder.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindBorder.ts @@ -67,7 +67,6 @@ export const tailwindBorderWidth = ( // Check stroke alignment and layout mode const strokeAlign = "strokeAlign" in node ? node.strokeAlign : "INSIDE"; - const layoutMode = "layoutMode" in node ? node.layoutMode : "NONE"; if ("all" in commonBorder) { if (commonBorder.all === 0) { diff --git a/packages/backend/src/tailwind/tailwindMain.ts b/packages/backend/src/tailwind/tailwindMain.ts index 8bee9378..8e960747 100644 --- a/packages/backend/src/tailwind/tailwindMain.ts +++ b/packages/backend/src/tailwind/tailwindMain.ts @@ -179,7 +179,10 @@ const tailwindFrame = async ( ); const childrenStr = await tailwindWidgetGenerator(sortedChildren, settings); - const clipsContentClass = node.clipsContent ? "overflow-hidden" : ""; + const clipsContentClass = + node.clipsContent && "children" in node && node.children.length > 0 + ? "overflow-hidden" + : ""; let layoutProps = ""; if (node.layoutMode !== "NONE") { From 80a2f65d2c90345c7d7cfee9b0846f27cce17b45 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Mon, 10 Mar 2025 13:31:04 -0300 Subject: [PATCH 102/168] Try to fix --- apps/plugin/plugin-src/code.ts | 18 +++++++++++------- apps/plugin/ui-src/App.tsx | 1 - packages/backend/src/code.ts | 16 ++++++++-------- .../src/html/builderImpl/htmlAutoLayout.ts | 17 ++++++++--------- packages/backend/src/html/htmlMain.ts | 19 +++++-------------- 5 files changed, 32 insertions(+), 39 deletions(-) diff --git a/apps/plugin/plugin-src/code.ts b/apps/plugin/plugin-src/code.ts index 3fe67a9b..974166b1 100644 --- a/apps/plugin/plugin-src/code.ts +++ b/apps/plugin/plugin-src/code.ts @@ -8,12 +8,6 @@ import { htmlMain, postSettingsChanged, } from "backend"; -import { convertNodesToAltNodes } from "backend/src/altNodes/altConversion"; -import { - disableParent, - oldConvertNodesToAltNodes, - setDisableParent, -} from "backend/src/altNodes/oldAltConversion"; import { nodesToJSON } from "backend/src/code"; import { retrieveGenericSolidUIColors } from "backend/src/common/retrieveUI/retrieveColors"; import { flutterCodeGenTextStyles } from "backend/src/flutter/flutterMain"; @@ -198,7 +192,17 @@ const standardMode = async () => { } try { - result.newConversion = await nodesToJSON(nodes, userPluginSettings); + const newNodes = await nodesToJSON(nodes, userPluginSettings); + const removeParent = (node: any) => { + if (node.parent) { + delete node.parent; + } + if (node.children) { + node.children.forEach(removeParent); + } + }; + newNodes.forEach(removeParent); + result.newConversion = newNodes; } catch (error) { console.error("Error in new conversion:", error); } diff --git a/apps/plugin/ui-src/App.tsx b/apps/plugin/ui-src/App.tsx index 90549a1b..0a4045a7 100644 --- a/apps/plugin/ui-src/App.tsx +++ b/apps/plugin/ui-src/App.tsx @@ -104,7 +104,6 @@ export default function App() { break; case "selection-json": - console.log("selection json"); const json = event.data.pluginMessage.data; copy(JSON.stringify(json, null, 2)); diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index a592cfe4..1a40790b 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -163,12 +163,17 @@ function adjustChildrenOrder(node: any) { */ const processNodePair = async ( jsonNode: any, - figmaNode: SceneNode, settings: PluginSettings, parentNode?: any, ) => { if (!jsonNode.id) return; + const figmaNode = await figma.getNodeByIdAsync(jsonNode.id); + + if (!figmaNode) { + return; + } + // Set parent reference if parent is provided if (parentNode) { jsonNode.parent = parentNode; @@ -352,12 +357,7 @@ const processNodePair = async ( // ); for (let i = 0; i < jsonNode.children.length; i++) { - await processNodePair( - jsonNode.children[i], - figmaNode.children[i], - settings, - jsonNode, - ); + await processNodePair(jsonNode.children[i], settings, jsonNode); } if ( @@ -409,7 +409,7 @@ export const nodesToJSON = async ( // Now process each top-level node pair (JSON node + Figma node) const processNodesStart = Date.now(); for (let i = 0; i < nodes.length; i++) { - await processNodePair(nodeJson[i], nodes[i], settings); + await processNodePair(nodeJson[i], settings); } console.log( `[benchmark][inside nodesToJSON] Process node pairs: ${Date.now() - processNodesStart}ms`, diff --git a/packages/backend/src/html/builderImpl/htmlAutoLayout.ts b/packages/backend/src/html/builderImpl/htmlAutoLayout.ts index 289df9d9..2fb246c3 100644 --- a/packages/backend/src/html/builderImpl/htmlAutoLayout.ts +++ b/packages/backend/src/html/builderImpl/htmlAutoLayout.ts @@ -69,19 +69,18 @@ const getFlex = ( : "inline-flex"; export const htmlAutoLayoutProps = ( - node: SceneNode, - autoLayout: InferredAutoLayoutResult, + node: SceneNode & InferredAutoLayoutResult, settings: HTMLSettings, ): string[] => formatMultipleJSXArray( { - "flex-direction": getFlexDirection(autoLayout), - "justify-content": getJustifyContent(autoLayout), - "align-items": getAlignItems(autoLayout), - gap: getGap(autoLayout), - display: getFlex(node, autoLayout), - "flex-wrap": getFlexWrap(autoLayout), - "align-content": getAlignContent(autoLayout), + "flex-direction": getFlexDirection(node), + "justify-content": getJustifyContent(node), + "align-items": getAlignItems(node), + gap: getGap(node), + display: getFlex(node, node), + "flex-wrap": getFlexWrap(node), + "align-content": getAlignContent(node), }, settings.htmlGenerationMode === "jsx", ); diff --git a/packages/backend/src/html/htmlMain.ts b/packages/backend/src/html/htmlMain.ts index 749b7217..adc17e0c 100644 --- a/packages/backend/src/html/htmlMain.ts +++ b/packages/backend/src/html/htmlMain.ts @@ -564,22 +564,13 @@ const htmlFrame = async ( ); if (node.layoutMode !== "NONE") { - const rowColumn = htmlAutoLayoutProps(node, node, settings); + const rowColumn = htmlAutoLayoutProps(node, settings); return await htmlContainer(node, childrenStr, rowColumn, settings); - } else { - if (settings.optimizeLayout && node.inferredAutoLayout !== null) { - const rowColumn = htmlAutoLayoutProps( - node, - node.inferredAutoLayout, - settings, - ); - return await htmlContainer(node, childrenStr, rowColumn, settings); - } - - // node.layoutMode === "NONE" && node.children.length > 1 - // children needs to be absolute - return await htmlContainer(node, childrenStr, [], settings); } + + // node.layoutMode === "NONE" && node.children.length > 1 + // children needs to be absolute + return await htmlContainer(node, childrenStr, [], settings); }; // properties named propSomething always take care of "," From bfa2da488b4c394671243bffffeb1c04f31b363f Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Mon, 10 Mar 2025 13:43:55 -0300 Subject: [PATCH 103/168] No more optimize layout --- apps/plugin/plugin-src/code.ts | 1 - packages/backend/src/code.ts | 16 +++++----- .../backend/src/common/commonChildrenOrder.ts | 25 ---------------- packages/backend/src/common/commonPosition.ts | 5 +--- .../src/flutter/builderImpl/flutterSize.ts | 23 ++++++++------- .../backend/src/flutter/flutterContainer.ts | 15 ++-------- .../src/flutter/flutterDefaultBuilder.ts | 8 ++--- packages/backend/src/flutter/flutterMain.ts | 22 ++++---------- .../backend/src/html/builderImpl/htmlSize.ts | 6 +--- .../backend/src/html/htmlDefaultBuilder.ts | 23 ++++----------- packages/backend/src/html/htmlMain.ts | 6 +--- .../src/swiftui/swiftuiDefaultBuilder.ts | 16 ++++------ packages/backend/src/swiftui/swiftuiMain.ts | 29 ++++--------------- .../src/tailwind/builderImpl/tailwindSize.ts | 6 +--- .../src/tailwind/tailwindDefaultBuilder.ts | 27 ++++------------- packages/backend/src/tailwind/tailwindMain.ts | 12 +------- .../plugin-ui/src/components/CodePanel.tsx | 1 - packages/types/src/types.ts | 1 - 18 files changed, 61 insertions(+), 181 deletions(-) delete mode 100644 packages/backend/src/common/commonChildrenOrder.ts diff --git a/apps/plugin/plugin-src/code.ts b/apps/plugin/plugin-src/code.ts index 974166b1..2b10d3a0 100644 --- a/apps/plugin/plugin-src/code.ts +++ b/apps/plugin/plugin-src/code.ts @@ -19,7 +19,6 @@ let userPluginSettings: PluginSettings; export const defaultPluginSettings: PluginSettings = { framework: "HTML", - optimizeLayout: true, showLayerNames: false, useOldPluginVersion2025: false, responsiveRoot: false, diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index 1a40790b..a592cfe4 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -163,17 +163,12 @@ function adjustChildrenOrder(node: any) { */ const processNodePair = async ( jsonNode: any, + figmaNode: SceneNode, settings: PluginSettings, parentNode?: any, ) => { if (!jsonNode.id) return; - const figmaNode = await figma.getNodeByIdAsync(jsonNode.id); - - if (!figmaNode) { - return; - } - // Set parent reference if parent is provided if (parentNode) { jsonNode.parent = parentNode; @@ -357,7 +352,12 @@ const processNodePair = async ( // ); for (let i = 0; i < jsonNode.children.length; i++) { - await processNodePair(jsonNode.children[i], settings, jsonNode); + await processNodePair( + jsonNode.children[i], + figmaNode.children[i], + settings, + jsonNode, + ); } if ( @@ -409,7 +409,7 @@ export const nodesToJSON = async ( // Now process each top-level node pair (JSON node + Figma node) const processNodesStart = Date.now(); for (let i = 0; i < nodes.length; i++) { - await processNodePair(nodeJson[i], settings); + await processNodePair(nodeJson[i], nodes[i], settings); } console.log( `[benchmark][inside nodesToJSON] Process node pairs: ${Date.now() - processNodesStart}ms`, diff --git a/packages/backend/src/common/commonChildrenOrder.ts b/packages/backend/src/common/commonChildrenOrder.ts deleted file mode 100644 index 506870fb..00000000 --- a/packages/backend/src/common/commonChildrenOrder.ts +++ /dev/null @@ -1,25 +0,0 @@ -export const commonSortChildrenWhenInferredAutoLayout = ( - node: SceneNode & ChildrenMixin, - optimize: boolean, -) => { - if (node.children.length <= 1) { - return node.children; - } - - if ( - optimize && - "inferredAutoLayout" in node && - node.inferredAutoLayout !== null - ) { - const children = [...node.children]; - switch (node.inferredAutoLayout.layoutMode) { - case "HORIZONTAL": - return children.sort((a, b) => a.x - b.x); - // NONE is a bug from Figma. - case "NONE": - case "VERTICAL": - return children.sort((a, b) => a.y - b.y); - } - } - return node.children; -}; diff --git a/packages/backend/src/common/commonPosition.ts b/packages/backend/src/common/commonPosition.ts index 8515b931..7899b4e7 100644 --- a/packages/backend/src/common/commonPosition.ts +++ b/packages/backend/src/common/commonPosition.ts @@ -14,10 +14,7 @@ export const getCommonPositionValue = ( }; }; -export const commonIsAbsolutePosition = ( - node: SceneNode, - optimizeLayout: boolean, -) => { +export const commonIsAbsolutePosition = (node: SceneNode) => { if ("layoutPositioning" in node && node.layoutPositioning === "ABSOLUTE") { return true; } diff --git a/packages/backend/src/flutter/builderImpl/flutterSize.ts b/packages/backend/src/flutter/builderImpl/flutterSize.ts index a219a8a1..ccd4f2bd 100644 --- a/packages/backend/src/flutter/builderImpl/flutterSize.ts +++ b/packages/backend/src/flutter/builderImpl/flutterSize.ts @@ -3,22 +3,23 @@ import { numberToFixedString } from "../../common/numToAutoFixed"; // Used in tests. export const flutterSizeWH = (node: SceneNode): string => { - const fSize = flutterSize(node, false); + const fSize = flutterSize(node); const size = fSize.width + fSize.height; return size; }; export const flutterSize = ( node: SceneNode, - optimizeLayout: boolean, -): { width: string; height: string; isExpanded: boolean; constraints: Record } => { +): { + width: string; + height: string; + isExpanded: boolean; + constraints: Record; +} => { const size = nodeSize(node); let isExpanded: boolean = false; - const nodeParent = - (node.parent && optimizeLayout && "inferredAutoLayout" in node.parent - ? node.parent.inferredAutoLayout - : null) ?? node.parent; + const nodeParent = node.parent; // this cast will always be true, since nodeWidthHeight was called with false to relative. let propWidth = ""; @@ -55,19 +56,19 @@ export const flutterSize = ( // Handle min/max constraints const constraints: Record = {}; - + if (node.minWidth !== undefined && node.minWidth !== null) { constraints.minWidth = numberToFixedString(node.minWidth); } - + if (node.maxWidth !== undefined && node.maxWidth !== null) { constraints.maxWidth = numberToFixedString(node.maxWidth); } - + if (node.minHeight !== undefined && node.minHeight !== null) { constraints.minHeight = numberToFixedString(node.minHeight); } - + if (node.maxHeight !== undefined && node.maxHeight !== null) { constraints.maxHeight = numberToFixedString(node.maxHeight); } diff --git a/packages/backend/src/flutter/flutterContainer.ts b/packages/backend/src/flutter/flutterContainer.ts index aac9f96e..df4ae110 100644 --- a/packages/backend/src/flutter/flutterContainer.ts +++ b/packages/backend/src/flutter/flutterContainer.ts @@ -14,11 +14,7 @@ import { numberToFixedString } from "../common/numToAutoFixed"; import { getCommonRadius } from "../common/commonRadius"; import { commonStroke } from "../common/commonStroke"; -export const flutterContainer = ( - node: SceneNode, - child: string, - optimizeLayout: boolean, -): string => { +export const flutterContainer = (node: SceneNode, child: string): string => { // ignore the view when size is zero or less // while technically it shouldn't get less than 0, due to rounding errors, // it can get to values like: -0.000004196293048153166 @@ -28,10 +24,7 @@ export const flutterContainer = ( // ignore for Groups const propBoxDecoration = getDecoration(node); - const { width, height, isExpanded, constraints } = flutterSize( - node, - optimizeLayout, - ); + const { width, height, isExpanded, constraints } = flutterSize(node); const clipBehavior = "clipsContent" in node && node.clipsContent === true @@ -43,9 +36,7 @@ export const flutterContainer = ( // [propPadding] will be "padding: const EdgeInsets.symmetric(...)" or "" let propPadding = ""; if ("paddingLeft" in node) { - propPadding = flutterPadding( - (optimizeLayout ? node.inferredAutoLayout : null) ?? node, - ); + propPadding = flutterPadding(node); } let result: string; diff --git a/packages/backend/src/flutter/flutterDefaultBuilder.ts b/packages/backend/src/flutter/flutterDefaultBuilder.ts index c25a5063..47d1c581 100644 --- a/packages/backend/src/flutter/flutterDefaultBuilder.ts +++ b/packages/backend/src/flutter/flutterDefaultBuilder.ts @@ -18,8 +18,8 @@ export class FlutterDefaultBuilder { this.child = optChild; } - createContainer(node: SceneNode, optimizeLayout: boolean): this { - this.child = flutterContainer(node, this.child, optimizeLayout); + createContainer(node: SceneNode): this { + this.child = flutterContainer(node, this.child); return this; } @@ -34,8 +34,8 @@ export class FlutterDefaultBuilder { return this; } - position(node: SceneNode, optimizeLayout: boolean): this { - if (commonIsAbsolutePosition(node, optimizeLayout)) { + position(node: SceneNode): this { + if (commonIsAbsolutePosition(node)) { const { x, y } = getCommonPositionValue(node); this.child = generateWidgetCode("Positioned", { left: x, diff --git a/packages/backend/src/flutter/flutterMain.ts b/packages/backend/src/flutter/flutterMain.ts index 8dffb577..162e45e5 100644 --- a/packages/backend/src/flutter/flutterMain.ts +++ b/packages/backend/src/flutter/flutterMain.ts @@ -11,7 +11,6 @@ import { getCrossAxisAlignment, getMainAxisAlignment, } from "./builderImpl/flutterAutoLayout"; -import { commonSortChildrenWhenInferredAutoLayout } from "../common/commonChildrenOrder"; import { PluginSettings } from "types"; import { addWarning } from "../common/commonConversionWarnings"; import { getPlaceholderImage } from "../common/images"; @@ -152,9 +151,9 @@ const flutterContainer = (node: SceneNode, child: string): string => { } const builder = new FlutterDefaultBuilder(propChild) - .createContainer(node, localSettings.optimizeLayout) + .createContainer(node) .blendAttr(node) - .position(node, localSettings.optimizeLayout); + .position(node); return builder.child; }; @@ -163,24 +162,15 @@ const flutterText = (node: TextNode): string => { const builder = new FlutterTextBuilder().createText(node); previousExecutionCache.push(builder.child); - return builder - .blendAttr(node) - .textAutoSize(node) - .position(node, localSettings.optimizeLayout).child; + return builder.blendAttr(node).textAutoSize(node).position(node).child; }; const flutterFrame = ( node: SceneNode & BaseFrameMixin & MinimalBlendMixin, ): string => { - // Sort children according to layout direction - const sortedChildren = commonSortChildrenWhenInferredAutoLayout( - node, - localSettings.optimizeLayout, - ); - // Check if any direct children need absolute positioning - const hasAbsoluteChildren = sortedChildren.some( - (child) => (child as any).layoutPositioning === "ABSOLUTE", + const hasAbsoluteChildren = node.children.some( + (child: any) => (child as any).layoutPositioning === "ABSOLUTE", ); // Add warning if we need to use Stack due to absolute positioning @@ -209,7 +199,7 @@ const flutterFrame = ( const rowColumn = makeRowColumn(node, children); return flutterContainer(node, rowColumn); } else { - if (localSettings.optimizeLayout && node.inferredAutoLayout) { + if (node.inferredAutoLayout) { const rowColumn = makeRowColumn(node.inferredAutoLayout, children); return flutterContainer(node, rowColumn); } diff --git a/packages/backend/src/html/builderImpl/htmlSize.ts b/packages/backend/src/html/builderImpl/htmlSize.ts index faffc414..d6bbc280 100644 --- a/packages/backend/src/html/builderImpl/htmlSize.ts +++ b/packages/backend/src/html/builderImpl/htmlSize.ts @@ -5,7 +5,6 @@ import { isPreviewGlobal } from "../htmlMain"; export const htmlSizePartial = ( node: SceneNode, isJsx: boolean, - optimizeLayout: boolean, ): { width: string; height: string; constraints: string[] } => { if (isPreviewGlobal && node.parent === undefined) { return { @@ -16,10 +15,7 @@ export const htmlSizePartial = ( } const size = nodeSize(node); - const nodeParent = - (node.parent && optimizeLayout && "inferredAutoLayout" in node.parent - ? node.parent.inferredAutoLayout - : null) ?? node.parent; + const nodeParent = node.parent; let w = ""; if (typeof size.width === "number") { diff --git a/packages/backend/src/html/htmlDefaultBuilder.ts b/packages/backend/src/html/htmlDefaultBuilder.ts index 8f6469c2..12ceb5b4 100644 --- a/packages/backend/src/html/htmlDefaultBuilder.ts +++ b/packages/backend/src/html/htmlDefaultBuilder.ts @@ -58,10 +58,6 @@ export class HtmlDefaultBuilder { return this.settings.htmlGenerationMode === "jsx"; } - get optimizeLayout() { - return this.settings.optimizeLayout; - } - get exportCSS() { return this.settings.htmlGenerationMode === "svelte"; } @@ -260,8 +256,8 @@ export class HtmlDefaultBuilder { } position(): this { - const { node, optimizeLayout, isJSX } = this; - const isAbsolutePosition = commonIsAbsolutePosition(node, optimizeLayout); + const { node, isJSX } = this; + const isAbsolutePosition = commonIsAbsolutePosition(node); if (isAbsolutePosition) { const { x, y } = getCommonPositionValue(node); @@ -273,10 +269,7 @@ export class HtmlDefaultBuilder { } else { if ( node.type === "GROUP" || - ("layoutMode" in node && - ((optimizeLayout ? node.inferredAutoLayout : null) ?? node) - ?.layoutMode === "NONE") || - (node as any).isRelative + ("layoutMode" in node && (node as any).isRelative) ) { this.addStyles(formatWithJSX("position", isJSX, "relative")); } @@ -356,7 +349,6 @@ export class HtmlDefaultBuilder { const { width, height, constraints } = htmlSizePartial( node, settings.htmlGenerationMode === "jsx", - settings.optimizeLayout, ); if (node.type === "TEXT") { @@ -384,14 +376,9 @@ export class HtmlDefaultBuilder { } autoLayoutPadding(): this { - const { node, isJSX, optimizeLayout } = this; + const { node, isJSX } = this; if ("paddingLeft" in node) { - this.addStyles( - ...htmlPadding( - (optimizeLayout ? (node as any).inferredAutoLayout : null) ?? node, - isJSX, - ), - ); + this.addStyles(...htmlPadding(node, isJSX)); } return this; } diff --git a/packages/backend/src/html/htmlMain.ts b/packages/backend/src/html/htmlMain.ts index adc17e0c..2972fc38 100644 --- a/packages/backend/src/html/htmlMain.ts +++ b/packages/backend/src/html/htmlMain.ts @@ -3,7 +3,6 @@ import { HtmlTextBuilder } from "./htmlTextBuilder"; import { HtmlDefaultBuilder } from "./htmlDefaultBuilder"; import { htmlAutoLayoutProps } from "./builderImpl/htmlAutoLayout"; import { formatWithJSX } from "../common/parseJSX"; -import { commonSortChildrenWhenInferredAutoLayout } from "../common/commonChildrenOrder"; import { PluginSettings, HTMLPreview, @@ -558,10 +557,7 @@ const htmlFrame = async ( node: SceneNode & BaseFrameMixin, settings: HTMLSettings, ): Promise => { - const childrenStr = await htmlWidgetGenerator( - commonSortChildrenWhenInferredAutoLayout(node, settings.optimizeLayout), - settings, - ); + const childrenStr = await htmlWidgetGenerator(node.children, settings); if (node.layoutMode !== "NONE") { const rowColumn = htmlAutoLayoutProps(node, settings); diff --git a/packages/backend/src/swiftui/swiftuiDefaultBuilder.ts b/packages/backend/src/swiftui/swiftuiDefaultBuilder.ts index 2033e96f..c082d38b 100644 --- a/packages/backend/src/swiftui/swiftuiDefaultBuilder.ts +++ b/packages/backend/src/swiftui/swiftuiDefaultBuilder.ts @@ -36,8 +36,8 @@ export class SwiftuiDefaultBuilder { }); } - commonPositionStyles(node: SceneNode, optimizeLayout: boolean): this { - this.position(node, optimizeLayout); + commonPositionStyles(node: SceneNode): this { + this.position(node); if ("layoutAlign" in node && "opacity" in node) { this.blend(node); } @@ -75,8 +75,8 @@ export class SwiftuiDefaultBuilder { return { centerX: centerBasedX, centerY: centerBasedY }; } - position(node: SceneNode, optimizeLayout: boolean): this { - if (commonIsAbsolutePosition(node, optimizeLayout)) { + position(node: SceneNode): this { + if (commonIsAbsolutePosition(node)) { const { x, y } = getCommonPositionValue(node); const { centerX, centerY } = this.topLeftToCenterOffset( x, @@ -152,13 +152,9 @@ export class SwiftuiDefaultBuilder { return this; } - autoLayoutPadding(node: SceneNode, optimizeLayout: boolean): this { + autoLayoutPadding(node: SceneNode): this { if ("paddingLeft" in node) { - this.pushModifier( - swiftuiPadding( - (optimizeLayout ? node.inferredAutoLayout : null) ?? node, - ), - ); + this.pushModifier(swiftuiPadding(node)); } return this; } diff --git a/packages/backend/src/swiftui/swiftuiMain.ts b/packages/backend/src/swiftui/swiftuiMain.ts index 84acbf7c..28b2b7db 100644 --- a/packages/backend/src/swiftui/swiftuiMain.ts +++ b/packages/backend/src/swiftui/swiftuiMain.ts @@ -5,7 +5,6 @@ import { } from "../common/numToAutoFixed"; import { SwiftuiTextBuilder } from "./swiftuiTextBuilder"; import { SwiftuiDefaultBuilder } from "./swiftuiDefaultBuilder"; -import { commonSortChildrenWhenInferredAutoLayout } from "../common/commonChildrenOrder"; import { PluginSettings } from "types"; import { addWarning } from "../common/commonConversionWarnings"; import { getVisibleNodes } from "../common/nodeVisibility"; @@ -126,12 +125,12 @@ export const swiftuiContainer = ( const result = new SwiftuiDefaultBuilder(kind) .shapeForeground(node) - .autoLayoutPadding(node, localSettings.optimizeLayout) + .autoLayoutPadding(node) .size(node) .shapeBackground(node) .cornerRadius(node) .shapeBorder(node) - .commonPositionStyles(node, localSettings.optimizeLayout) + .commonPositionStyles(node) .effects(node) .build(kind === stack ? -2 : 0); @@ -153,9 +152,7 @@ const swiftuiText = (node: TextNode): string => { const result = new SwiftuiTextBuilder().createText(node); previousExecutionCache.push(result.build()); - return result - .commonPositionStyles(node, localSettings.optimizeLayout) - .build(); + return result.commonPositionStyles(node).build(); }; const swiftuiFrame = ( @@ -167,12 +164,7 @@ const swiftuiFrame = ( node.children.length > 1 ? indentLevel + 1 : indentLevel, ); - const anyStack = createDirectionalStack( - children, - localSettings.optimizeLayout && node.inferredAutoLayout !== null - ? node.inferredAutoLayout - : node, - ); + const anyStack = createDirectionalStack(children, node); return swiftuiContainer(node, anyStack); }; @@ -250,21 +242,12 @@ const widgetGeneratorWithLimits = ( ) => { if (node.children.length < 10) { // standard way - return swiftuiWidgetGenerator( - commonSortChildrenWhenInferredAutoLayout( - node, - localSettings.optimizeLayout, - ), - indentLevel, - ); + return swiftuiWidgetGenerator(node.children, indentLevel); } const chunk = 10; let strBuilder = ""; - const slicedChildren = commonSortChildrenWhenInferredAutoLayout( - node, - localSettings.optimizeLayout, - ).slice(0, 100); + const slicedChildren = node.children.slice(0, 100); // I believe no one should have more than 100 items in a single nesting level. If you do, please email me. if (node.children.length > 100) { diff --git a/packages/backend/src/tailwind/builderImpl/tailwindSize.ts b/packages/backend/src/tailwind/builderImpl/tailwindSize.ts index e514af82..43575681 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindSize.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindSize.ts @@ -30,14 +30,10 @@ const formatTailwindSizeValue = ( export const tailwindSizePartial = ( node: SceneNode, - optimizeLayout: boolean, settings?: TailwindSettings, ): { width: string; height: string; constraints: string } => { const size = nodeSize(node); - const nodeParent = - (node.parent && optimizeLayout && "inferredAutoLayout" in node.parent - ? node.parent.inferredAutoLayout - : null) ?? node.parent; + const nodeParent = node.parent; let w = ""; if (typeof size.width === "number") { diff --git a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts index ebfc2f86..3340e674 100644 --- a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts +++ b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts @@ -51,9 +51,6 @@ export class TailwindDefaultBuilder { get isJSX() { return this.settings.tailwindGenerationMode === "jsx"; } - get optimizeLayout() { - return this.settings.optimizeLayout; - } constructor(node: SceneNode, settings: TailwindSettings) { this.node = node; @@ -123,8 +120,8 @@ export class TailwindDefaultBuilder { } position(): this { - const { node, optimizeLayout } = this; - if (commonIsAbsolutePosition(node, optimizeLayout)) { + const { node } = this; + if (commonIsAbsolutePosition(node)) { const { x, y } = getCommonPositionValue(node); const parsedX = numberToFixedString(x); @@ -143,10 +140,7 @@ export class TailwindDefaultBuilder { this.addAttributes(`absolute`); } else if ( node.type === "GROUP" || - ("layoutMode" in node && - ((optimizeLayout ? node.inferredAutoLayout : null) ?? node) - ?.layoutMode === "NONE") || - (node as any).isRelative + ("layoutMode" in node && (node as any)).isRelative ) { this.addAttributes("relative"); } @@ -196,12 +190,8 @@ export class TailwindDefaultBuilder { // must be called before Position, because of the hasFixedSize attribute. size(): this { - const { node, optimizeLayout, settings } = this; - const { width, height, constraints } = tailwindSizePartial( - node, - optimizeLayout, - settings, - ); + const { node, settings } = this; + const { width, height, constraints } = tailwindSizePartial(node, settings); if (node.type === "TEXT") { switch (node.textAutoResize) { @@ -229,12 +219,7 @@ export class TailwindDefaultBuilder { autoLayoutPadding(): this { if ("paddingLeft" in this.node) { - this.addAttributes( - ...tailwindPadding( - (this.optimizeLayout ? this.node.inferredAutoLayout : null) ?? - this.node, - ), - ); + this.addAttributes(...tailwindPadding(this.node)); } return this; } diff --git a/packages/backend/src/tailwind/tailwindMain.ts b/packages/backend/src/tailwind/tailwindMain.ts index 8e960747..d2ed446c 100644 --- a/packages/backend/src/tailwind/tailwindMain.ts +++ b/packages/backend/src/tailwind/tailwindMain.ts @@ -1,7 +1,6 @@ import { retrieveTopFill } from "../common/retrieveFill"; import { indentString } from "../common/indentString"; import { addWarning } from "../common/commonConversionWarnings"; -import { commonSortChildrenWhenInferredAutoLayout } from "../common/commonChildrenOrder"; import { getVisibleNodes } from "../common/nodeVisibility"; import { getPlaceholderImage } from "../common/images"; import { TailwindTextBuilder } from "./tailwindTextBuilder"; @@ -173,11 +172,7 @@ const tailwindFrame = async ( node: FrameNode | InstanceNode | ComponentNode | ComponentSetNode, settings: TailwindSettings, ): Promise => { - const sortedChildren = commonSortChildrenWhenInferredAutoLayout( - node, - localTailwindSettings.optimizeLayout, - ); - const childrenStr = await tailwindWidgetGenerator(sortedChildren, settings); + const childrenStr = await tailwindWidgetGenerator(node.children, settings); const clipsContentClass = node.clipsContent && "children" in node && node.children.length > 0 @@ -187,11 +182,6 @@ const tailwindFrame = async ( if (node.layoutMode !== "NONE") { layoutProps = tailwindAutoLayoutProps(node, node); - } else if ( - localTailwindSettings.optimizeLayout && - node.inferredAutoLayout !== null - ) { - layoutProps = tailwindAutoLayoutProps(node, node.inferredAutoLayout); } // Combine classes properly, ensuring no extra spaces diff --git a/packages/plugin-ui/src/components/CodePanel.tsx b/packages/plugin-ui/src/components/CodePanel.tsx index 583e226e..285f3dd2 100644 --- a/packages/plugin-ui/src/components/CodePanel.tsx +++ b/packages/plugin-ui/src/components/CodePanel.tsx @@ -116,7 +116,6 @@ const CodePanel = (props: CodePanelProps) => { "roundTailwindColors", "useColorVariables", "showLayerNames", - "optimizeLayout", "embedImages", "embedVectors", ]; diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index 55d04ccb..6f02854f 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -2,7 +2,6 @@ import "@figma/plugin-typings"; // Settings export type Framework = "Flutter" | "SwiftUI" | "HTML" | "Tailwind"; export interface HTMLSettings { - optimizeLayout: boolean; showLayerNames: boolean; embedImages: boolean; embedVectors: boolean; From 353a9375218fd8ebcf2aa6097ac9612991243c50 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Tue, 11 Mar 2025 02:04:33 -0300 Subject: [PATCH 104/168] Improve gradients --- packages/backend/src/code.ts | 3118 ++++++++++++++++- packages/backend/src/common/color.ts | 50 +- packages/backend/src/common/commonPosition.ts | 19 +- packages/backend/src/common/retrieveFill.ts | 8 +- .../src/flutter/builderImpl/flutterColor.ts | 192 +- packages/backend/src/flutter/flutterMain.ts | 3 +- .../src/html/builderImpl/htmlBorderRadius.ts | 1 - .../backend/src/html/builderImpl/htmlColor.ts | 159 +- .../backend/src/html/htmlDefaultBuilder.ts | 8 +- packages/backend/src/html/htmlMain.ts | 1 + packages/backend/src/html/htmlTextBuilder.ts | 7 +- .../src/swiftui/builderImpl/swiftuiColor.ts | 18 +- .../src/tailwind/builderImpl/tailwindColor.ts | 5 +- 13 files changed, 3323 insertions(+), 266 deletions(-) diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index a592cfe4..d14c9e09 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -1,4 +1,3 @@ -import { convertNodesToAltNodes } from "./altNodes/altConversion"; import { retrieveGenericSolidUIColors, retrieveGenericLinearGradients as retrieveGenericGradients, @@ -14,6 +13,7 @@ import { convertToCode } from "./common/retrieveUI/convertToCode"; import { generateHTMLPreview } from "./html/htmlMain"; import { variableToColorName } from "./tailwind/conversionTables"; import { oldConvertNodesToAltNodes } from "./altNodes/oldAltConversion"; +import { convertNodesToAltNodes, convertNodeToAltNode } from "./altNodes/altConversion"; // Performance tracking counters let getNodeByIdAsyncTime = 0; @@ -187,19 +187,6 @@ const processNodePair = async ( ? cleanName : `${cleanName}_${count.toString().padStart(2, "0")}`; - GRADIENT_PROPERTIES.forEach((propName) => { - const property = jsonNode[propName]; - if ( - propName in figmaNode && - property && - property.some((item: any) => item.type.startsWith("GRADIENT_")) - ) { - jsonNode[propName] = JSON.parse( - JSON.stringify((figmaNode as any)[propName]), - ); - } - }); - // Handle text-specific properties if (figmaNode.type === "TEXT") { const getSegmentsStart = Date.now(); @@ -371,6 +358,13 @@ const processNodePair = async ( } adjustChildrenOrder(jsonNode); + } else if ( + "children" in figmaNode && + figmaNode.children.length !== jsonNode.children.length + ) { + addWarning( + "Error: JSON and Figma nodes have different child counts. Please report this issue.", + ); } }; @@ -456,11 +450,3103 @@ export const run = async (settings: PluginSettings) => { // Now we work directly with the JSON nodes const convertNodesStart = Date.now(); convertedSelection = await convertNodesToAltNodes(nodeJson, null); + const convertedSelection2 = [ + { + id: "I2099:38616;1739:34914", + name: "Modal", + type: "FRAME", + scrollBehavior: "SCROLLS", + boundVariables: { + minHeight: { + type: "VARIABLE_ALIAS", + id: "VariableID:ca5fdd543c7de4d7a5d043eb31c365871c484b3e/4411:779", + }, + maxHeight: { + type: "VARIABLE_ALIAS", + id: "VariableID:143b8d97896be058533bf7578cac052ff1473fe5/4411:780", + }, + size: { + x: { + type: "VARIABLE_ALIAS", + id: "VariableID:51164a6f21a5daac8ea5fa8551389d4d7864a05e/1129:893", + }, + y: { + type: "VARIABLE_ALIAS", + id: "VariableID:c7de0427328e0238030ad2c66fb3a86abcdf6421/2867:8", + }, + }, + rectangleCornerRadii: { + RECTANGLE_TOP_LEFT_CORNER_RADIUS: { + type: "VARIABLE_ALIAS", + id: "VariableID:f4761dc8bffff3a6dc119f76d86928372facdf02/3157:870", + }, + RECTANGLE_TOP_RIGHT_CORNER_RADIUS: { + type: "VARIABLE_ALIAS", + id: "VariableID:f4761dc8bffff3a6dc119f76d86928372facdf02/3157:870", + }, + RECTANGLE_BOTTOM_LEFT_CORNER_RADIUS: { + type: "VARIABLE_ALIAS", + id: "VariableID:f4761dc8bffff3a6dc119f76d86928372facdf02/3157:870", + }, + RECTANGLE_BOTTOM_RIGHT_CORNER_RADIUS: { + type: "VARIABLE_ALIAS", + id: "VariableID:f4761dc8bffff3a6dc119f76d86928372facdf02/3157:870", + }, + }, + fills: [ + { + type: "VARIABLE_ALIAS", + id: "VariableID:8cbcd0032a7cac3b9799f16f6f48c35cab554a40/2243:10", + }, + ], + effects: [ + { + type: "VARIABLE_ALIAS", + id: "VariableID:55268df3aca26e8ed4182c6831670c631ab2e88b/4411:298", + }, + { + type: "VARIABLE_ALIAS", + id: "VariableID:7b576d4f7cef936e728857b8d6f7952e2a6dd6fe/3157:78", + }, + { + type: "VARIABLE_ALIAS", + id: "VariableID:e7dccd708e8eb5f689ef26147d2d985b7af1bfd8/2013:336", + }, + ], + }, + children: [ + { + id: "I2099:38616;1739:61360", + name: "Content", + type: "FRAME", + scrollBehavior: "SCROLLS", + children: [ + { + id: "I2099:38616;1739:34917", + name: "Modal Container", + type: "INSTANCE", + scrollBehavior: "SCROLLS", + componentPropertyReferences: { + mainComponent: "Swap Modal Container#893:1", + }, + boundVariables: { + itemSpacing: { + type: "VARIABLE_ALIAS", + id: "VariableID:b1aa965163834bcfd01131ac315d7d493f241ba6/10434:244", + }, + paddingLeft: { + type: "VARIABLE_ALIAS", + id: "VariableID:c44b7d196360345cd2e77fddb9fdbb56b074630c/10434:249", + }, + paddingTop: { + type: "VARIABLE_ALIAS", + id: "VariableID:becb73e51eeba1f8c786ade8394c2a4d29eb3ecf/10434:245", + }, + paddingRight: { + type: "VARIABLE_ALIAS", + id: "VariableID:dca108e1e0b09a774a4527df680b8306bc9208b2/10434:246", + }, + paddingBottom: { + type: "VARIABLE_ALIAS", + id: "VariableID:f6e871ff408efebce7861cdde12d1530510445e9/10434:248", + }, + minHeight: { + type: "VARIABLE_ALIAS", + id: "VariableID:d4bef8dbc813180acafb9ee86fec3ab4e9cb2983/3203:9", + }, + maxHeight: { + type: "VARIABLE_ALIAS", + id: "VariableID:5c0fdb551d4699d20f1fa1328995f4fb9d0ad8d5/3203:12", + }, + }, + componentId: "2099:38674", + isExposedInstance: true, + componentProperties: { + Type: { + value: "Fixed", + type: "VARIANT", + boundVariables: {}, + }, + Allignment: { + value: "Default (L-R)", + type: "VARIANT", + boundVariables: {}, + }, + }, + overrides: [ + { + id: "I2099:38616;1739:34917;2326:19664", + overriddenFields: [ + "componentProperties", + "primaryAxisSizingMode", + "layoutGrow", + "counterAxisSizingMode", + ], + }, + { + id: "I2099:38616;1739:34917;2326:19688", + overriddenFields: ["visible"], + }, + { + id: "I2099:38616;1739:34917;2326:19673;979:10619", + overriddenFields: ["sharedPluginData", "pluginData"], + }, + { + id: "I2099:38616;1739:34917;2326:19661", + overriddenFields: [ + "componentProperties", + "counterAxisSizingMode", + "primaryAxisSizingMode", + ], + }, + { + id: "I2099:38616;1739:34917;2326:19706", + overriddenFields: ["visible"], + }, + { + id: "I2099:38616;1739:34917;2326:19664;1202:3054", + overriddenFields: [ + "layoutAlign", + "sharedPluginData", + "pluginData", + "componentProperties", + "counterAxisSizingMode", + "layoutGrow", + "paddingRight", + "paddingLeft", + "primaryAxisSizingMode", + ], + }, + { + id: "I2099:38616;1739:34917;2326:19697", + overriddenFields: ["visible"], + }, + { + id: "I2099:38616;1739:34917;2326:19673", + overriddenFields: ["visible"], + }, + { + id: "I2099:38616;1739:34917;2326:19664;1202:3050;1856:4", + overriddenFields: ["sharedPluginData", "pluginData"], + }, + { + id: "I2099:38616;1739:34917;2326:19691", + overriddenFields: ["visible"], + }, + { + id: "I2099:38616;1739:34917;2326:19703;979:10619", + overriddenFields: ["sharedPluginData", "pluginData"], + }, + { + id: "I2099:38616;1739:34917;2326:19667", + overriddenFields: ["visible"], + }, + { + id: "I2099:38616;1739:34917;2326:19691;979:10619", + overriddenFields: ["sharedPluginData", "pluginData"], + }, + { + id: "I2099:38616;1739:34917", + overriddenFields: [ + "topLeftRadius", + "cornerRadius", + "topRightRadius", + "name", + "bottomLeftRadius", + "bottomRightRadius", + "primaryAxisSizingMode", + "counterAxisSizingMode", + "layoutGrow", + "boundVariables", + ], + }, + { + id: "I2099:38616;1739:34917;2326:19670", + overriddenFields: ["visible"], + }, + { + id: "I2099:38616;1739:34917;2326:19700", + overriddenFields: ["visible"], + }, + { + id: "I2099:38616;1739:34917;2326:19703", + overriddenFields: ["visible"], + }, + { + id: "I2099:38616;1739:34917;2326:19664;1202:3055", + overriddenFields: ["sharedPluginData", "pluginData"], + }, + { + id: "I2099:38616;1739:34917;2326:19661;117:435;1658:6969", + overriddenFields: ["sharedPluginData", "pluginData"], + }, + { + id: "I2099:38616;1739:34917;2326:19670;979:10619", + overriddenFields: ["sharedPluginData", "pluginData"], + }, + { + id: "I2099:38616;1739:34917;2326:19697;979:10619", + overriddenFields: ["sharedPluginData", "pluginData"], + }, + { + id: "I2099:38616;1739:34917;2326:19688;979:10619", + overriddenFields: ["sharedPluginData", "pluginData"], + }, + { + id: "I2099:38616;1739:34917;2326:19706;979:10619", + overriddenFields: ["sharedPluginData", "pluginData"], + }, + { + id: "I2099:38616;1739:34917;2326:19664;1202:3056", + overriddenFields: ["sharedPluginData", "pluginData"], + }, + { + id: "I2099:38616;1739:34917;2326:19667;979:10619", + overriddenFields: ["sharedPluginData", "pluginData"], + }, + { + id: "I2099:38616;1739:34917;1732:20311", + overriddenFields: ["sharedPluginData", "pluginData"], + }, + { + id: "I2099:38616;1739:34917;2326:19694", + overriddenFields: ["visible"], + }, + { + id: "I2099:38616;1739:34917;2326:19664;1202:3053", + overriddenFields: ["sharedPluginData", "pluginData"], + }, + { + id: "I2099:38616;1739:34917;2326:19694;979:10619", + overriddenFields: ["sharedPluginData", "pluginData"], + }, + { + id: "I2099:38616;1739:34917;2326:19664;1202:3052", + overriddenFields: ["sharedPluginData", "pluginData"], + }, + { + id: "I2099:38616;1739:34917;2326:19700;979:10619", + overriddenFields: ["sharedPluginData", "pluginData"], + }, + ], + children: [ + { + id: "I2099:38616;1739:34917;1732:20311", + name: "Grid", + type: "FRAME", + scrollBehavior: "SCROLLS", + boundVariables: { + itemSpacing: { + type: "VARIABLE_ALIAS", + id: "VariableID:b1aa965163834bcfd01131ac315d7d493f241ba6/10434:244", + }, + }, + children: [ + { + id: "I2099:38616;1739:34917;1732:20312", + name: "Row", + type: "FRAME", + scrollBehavior: "SCROLLS", + boundVariables: { + itemSpacing: { + type: "VARIABLE_ALIAS", + id: "VariableID:be243c44965c9affe677211ea0cd661d873653e1/10434:247", + }, + counterAxisSpacing: { + type: "VARIABLE_ALIAS", + id: "VariableID:b1aa965163834bcfd01131ac315d7d493f241ba6/10434:244", + }, + }, + children: [ + { + id: "I2099:38616;1739:34917;2326:19661", + name: "TextImage Variation 2", + type: "INSTANCE", + scrollBehavior: "SCROLLS", + boundVariables: { + size: { + x: { + type: "VARIABLE_ALIAS", + id: "VariableID:7dde29c0269cd3c53a0f9f7c1e2cc8e86d43cf3b/3157:236", + }, + y: { + type: "VARIABLE_ALIAS", + id: "VariableID:7dde29c0269cd3c53a0f9f7c1e2cc8e86d43cf3b/3157:236", + }, + }, + }, + componentId: "2043:22065", + componentProperties: { + "↳Swap Icon/Image/Graphic#9837:448": { + value: "2092:64304", + type: "INSTANCE_SWAP", + preferredValues: [], + }, + "Custom Size": { + value: "False", + type: "VARIANT", + boundVariables: {}, + }, + "Predefined Size": { + value: "L Container", + type: "VARIANT", + boundVariables: {}, + }, + }, + overrides: [ + { + id: "I2099:38616;1739:34917;2326:19661", + overriddenFields: [ + "componentProperties", + "counterAxisSizingMode", + "primaryAxisSizingMode", + ], + }, + { + id: "I2099:38616;1739:34917;2326:19661;117:435;1658:6969", + overriddenFields: [ + "sharedPluginData", + "pluginData", + ], + }, + ], + children: [ + { + id: "I2099:38616;1739:34917;2326:19661;117:435", + name: "FaxOfHook", + type: "INSTANCE", + scrollBehavior: "SCROLLS", + componentPropertyReferences: { + mainComponent: + "↳Swap Icon/Image/Graphic#9837:448", + }, + componentId: "2092:64304", + overrides: [ + { + id: "I2099:38616;1739:34917;2326:19661;117:435;1658:6969", + overriddenFields: [ + "sharedPluginData", + "pluginData", + ], + }, + ], + children: [ + { + id: "I2099:38616;1739:34917;2326:19661;117:435;1658:6969", + name: "Vector", + type: "VECTOR", + scrollBehavior: "SCROLLS", + boundVariables: { + fills: [ + { + type: "VARIABLE_ALIAS", + id: "VariableID:e208fc12668fdb73b1d1a743ba7f06ef9dd690ba/2013:512", + }, + ], + }, + blendMode: "PASS_THROUGH", + fills: [ + { + blendMode: "NORMAL", + type: "SOLID", + color: { + r: 1, + g: 1, + b: 1, + a: 1, + }, + boundVariables: { + color: { + type: "VARIABLE_ALIAS", + id: "VariableID:e208fc12668fdb73b1d1a743ba7f06ef9dd690ba/2013:512", + }, + }, + }, + ], + fillOverrideTable: { + "1": null, + }, + strokes: [], + strokeWeight: 0.75, + strokeAlign: "CENTER", + strokeJoin: "ROUND", + strokeCap: "ROUND", + strokeMiterAngle: 11.478341102600098, + absoluteBoundingBox: { + x: -1897.455078125, + y: -2168.515380859375, + width: 127.45187377929688, + height: 125.89124298095703, + }, + absoluteRenderBounds: { + x: -1897.455078125, + y: -2168.515380859375, + width: 127.451904296875, + height: 125.8912353515625, + }, + constraints: { + vertical: "SCALE", + horizontal: "SCALE", + }, + effects: [], + interactions: [], + }, + ], + blendMode: "PASS_THROUGH", + clipsContent: true, + background: [], + fills: [], + strokes: [], + strokeWeight: 1, + strokeAlign: "INSIDE", + backgroundColor: { + r: 0, + g: 0, + b: 0, + a: 0, + }, + absoluteBoundingBox: { + x: -1907.5, + y: -2177.75, + width: 150, + height: 150, + }, + absoluteRenderBounds: { + x: -1907.5, + y: -2177.75, + width: 150, + height: 150, + }, + constraints: { + vertical: "TOP", + horizontal: "LEFT", + }, + layoutAlign: "STRETCH", + layoutGrow: 1, + layoutSizingHorizontal: "FILL", + layoutSizingVertical: "FILL", + effects: [], + interactions: [], + }, + ], + blendMode: "PASS_THROUGH", + clipsContent: false, + background: [], + fills: [], + strokes: [], + strokeWeight: 1, + strokeAlign: "INSIDE", + backgroundColor: { + r: 0, + g: 0, + b: 0, + a: 0, + }, + layoutMode: "HORIZONTAL", + itemSpacing: 10, + counterAxisAlignItems: "CENTER", + primaryAxisAlignItems: "CENTER", + layoutWrap: "NO_WRAP", + absoluteBoundingBox: { + x: -1907.5, + y: -2177.75, + width: 150, + height: 150, + }, + absoluteRenderBounds: { + x: -1907.5, + y: -2177.75, + width: 150, + height: 150, + }, + constraints: { + vertical: "TOP", + horizontal: "LEFT", + }, + layoutAlign: "INHERIT", + layoutGrow: 0, + layoutSizingHorizontal: "HUG", + layoutSizingVertical: "HUG", + effects: [], + interactions: [], + }, + { + id: "I2099:38616;1739:34917;2326:19664", + name: "TextImage Variation 1", + type: "INSTANCE", + scrollBehavior: "SCROLLS", + boundVariables: { + itemSpacing: { + type: "VARIABLE_ALIAS", + id: "VariableID:3f744b5e3c8b3619411d855f7fe34dd7351a9435/3157:1061", + }, + minWidth: { + type: "VARIABLE_ALIAS", + id: "VariableID:1b137df291c4095c5e80c61d2c69a449cba2ccd9/3157:87", + }, + }, + componentId: "2058:19231", + exposedInstances: [ + "I2099:38616;1739:34917;2326:19664;1202:3050", + "I2099:38616;1739:34917;2326:19664;1202:3052", + "I2099:38616;1739:34917;2326:19664;1202:3053", + "I2099:38616;1739:34917;2326:19664;1202:3054", + "I2099:38616;1739:34917;2326:19664;1202:3055", + "I2099:38616;1739:34917;2326:19664;1202:3056", + ], + componentProperties: { + "↳Show Title Big#1053:9": { + value: false, + type: "BOOLEAN", + }, + "↳Show Content Small#1053:13": { + value: false, + type: "BOOLEAN", + }, + "Show Icon#1053:11": { + value: false, + type: "BOOLEAN", + }, + "Show Text#3122:8": { + value: true, + type: "BOOLEAN", + }, + "↳Show Title Small#1053:12": { + value: false, + type: "BOOLEAN", + }, + "↳Show Content#1053:10": { + value: true, + type: "BOOLEAN", + }, + "↳Show Clarification#1053:7": { + value: false, + type: "BOOLEAN", + }, + "↳Swap Icon#1053:8": { + value: "2033:1462", + type: "INSTANCE_SWAP", + preferredValues: [], + }, + Alignment: { + value: "ImageLeft-Middle", + type: "VARIANT", + boundVariables: {}, + }, + }, + overrides: [ + { + id: "I2099:38616;1739:34917;2326:19664", + overriddenFields: [ + "componentProperties", + "primaryAxisSizingMode", + "layoutGrow", + "counterAxisSizingMode", + ], + }, + { + id: "I2099:38616;1739:34917;2326:19664;1202:3056", + overriddenFields: [ + "sharedPluginData", + "pluginData", + ], + }, + { + id: "I2099:38616;1739:34917;2326:19664;1202:3052", + overriddenFields: [ + "sharedPluginData", + "pluginData", + ], + }, + { + id: "I2099:38616;1739:34917;2326:19664;1202:3054", + overriddenFields: [ + "layoutAlign", + "sharedPluginData", + "pluginData", + "componentProperties", + "counterAxisSizingMode", + "layoutGrow", + "paddingRight", + "paddingLeft", + "primaryAxisSizingMode", + ], + }, + { + id: "I2099:38616;1739:34917;2326:19664;1202:3055", + overriddenFields: [ + "sharedPluginData", + "pluginData", + ], + }, + { + id: "I2099:38616;1739:34917;2326:19664;1202:3050;1856:4", + overriddenFields: [ + "sharedPluginData", + "pluginData", + ], + }, + { + id: "I2099:38616;1739:34917;2326:19664;1202:3053", + overriddenFields: [ + "sharedPluginData", + "pluginData", + ], + }, + ], + children: [ + { + id: "I2099:38616;1739:34917;2326:19664;1202:3050", + name: "Placeholder", + visible: false, + type: "INSTANCE", + scrollBehavior: "SCROLLS", + componentPropertyReferences: { + mainComponent: "↳Swap Icon#1053:8", + visible: "Show Icon#1053:11", + }, + boundVariables: { + size: { + x: { + type: "VARIABLE_ALIAS", + id: "VariableID:1b137df291c4095c5e80c61d2c69a449cba2ccd9/3157:87", + }, + y: { + type: "VARIABLE_ALIAS", + id: "VariableID:1b137df291c4095c5e80c61d2c69a449cba2ccd9/3157:87", + }, + }, + }, + explicitVariableModes: { + "VariableCollectionId:e2fa2f7f8d460f250b4a1269b312f661b543be85/443:278": + "146:9", + }, + componentId: "2033:1462", + isExposedInstance: true, + overrides: [ + { + id: "I2099:38616;1739:34917;2326:19664;1202:3050;1856:4", + overriddenFields: [ + "sharedPluginData", + "pluginData", + ], + }, + ], + children: [], + blendMode: "PASS_THROUGH", + clipsContent: false, + background: [], + fills: [], + strokes: [], + strokeWeight: 1, + strokeAlign: "INSIDE", + backgroundColor: { + r: 0, + g: 0, + b: 0, + a: 0, + }, + absoluteBoundingBox: { + x: -1720, + y: -2177.75, + width: 37.5, + height: 37.5, + }, + absoluteRenderBounds: null, + constraints: { + vertical: "TOP", + horizontal: "LEFT", + }, + layoutAlign: "INHERIT", + layoutGrow: 0, + layoutSizingHorizontal: "FIXED", + layoutSizingVertical: "FIXED", + effects: [], + interactions: [], + }, + { + id: "I2099:38616;1739:34917;2326:19664;1202:3051", + name: "Text", + type: "FRAME", + scrollBehavior: "SCROLLS", + componentPropertyReferences: { + visible: "Show Text#3122:8", + }, + boundVariables: { + itemSpacing: { + type: "VARIABLE_ALIAS", + id: "VariableID:61bc0ffe4bb813f939fb25252ec1ae428556cc4e/10419:0", + }, + }, + children: [ + { + id: "I2099:38616;1739:34917;2326:19664;1202:3052", + name: "TitleBig", + visible: false, + type: "INSTANCE", + scrollBehavior: "SCROLLS", + componentPropertyReferences: { + visible: "↳Show Title Big#1053:9", + }, + componentId: "2033:2259", + isExposedInstance: true, + componentProperties: { + "Title Label#1366:0": { + value: "Title big text", + type: "TEXT", + }, + }, + overrides: [ + { + id: "I2099:38616;1739:34917;2326:19664;1202:3052", + overriddenFields: [ + "sharedPluginData", + "pluginData", + ], + }, + ], + children: [], + blendMode: "PASS_THROUGH", + clipsContent: false, + background: [], + fills: [], + strokes: [], + strokeWeight: 1, + strokeAlign: "INSIDE", + backgroundColor: { + r: 0, + g: 0, + b: 0, + a: 0, + }, + layoutMode: "HORIZONTAL", + primaryAxisSizingMode: "FIXED", + counterAxisAlignItems: "CENTER", + primaryAxisAlignItems: "CENTER", + layoutWrap: "NO_WRAP", + absoluteBoundingBox: { + x: -1720, + y: -2177.75, + width: 321.5, + height: 32, + }, + absoluteRenderBounds: null, + constraints: { + vertical: "TOP", + horizontal: "LEFT", + }, + layoutAlign: "STRETCH", + layoutGrow: 0, + layoutSizingHorizontal: "FIXED", + layoutSizingVertical: "HUG", + effects: [], + interactions: [], + }, + { + id: "I2099:38616;1739:34917;2326:19664;1202:3053", + name: "TitleSmall", + visible: false, + type: "INSTANCE", + scrollBehavior: "SCROLLS", + componentPropertyReferences: { + visible: "↳Show Title Small#1053:12", + }, + boundVariables: { + itemSpacing: { + type: "VARIABLE_ALIAS", + id: "VariableID:61bc0ffe4bb813f939fb25252ec1ae428556cc4e/10419:0", + }, + }, + componentId: "2058:19206", + isExposedInstance: true, + componentProperties: { + "Title small Label#1366:1": { + value: "Title small text", + type: "TEXT", + }, + }, + overrides: [ + { + id: "I2099:38616;1739:34917;2326:19664;1202:3053", + overriddenFields: [ + "sharedPluginData", + "pluginData", + ], + }, + ], + children: [], + blendMode: "PASS_THROUGH", + clipsContent: false, + background: [], + fills: [], + strokes: [], + strokeWeight: 1, + strokeAlign: "INSIDE", + backgroundColor: { + r: 0, + g: 0, + b: 0, + a: 0, + }, + layoutMode: "VERTICAL", + counterAxisSizingMode: "FIXED", + itemSpacing: 18, + counterAxisAlignItems: "CENTER", + primaryAxisAlignItems: "CENTER", + layoutWrap: "NO_WRAP", + absoluteBoundingBox: { + x: -1720, + y: -2177.75, + width: 321.5, + height: 26, + }, + absoluteRenderBounds: null, + constraints: { + vertical: "TOP", + horizontal: "LEFT", + }, + layoutAlign: "STRETCH", + layoutGrow: 0, + layoutSizingHorizontal: "FIXED", + layoutSizingVertical: "HUG", + effects: [], + interactions: [], + }, + { + id: "I2099:38616;1739:34917;2326:19664;1202:3054", + name: "Content", + type: "INSTANCE", + scrollBehavior: "SCROLLS", + componentPropertyReferences: { + visible: "↳Show Content#1053:10", + }, + boundVariables: { + itemSpacing: { + type: "VARIABLE_ALIAS", + id: "VariableID:61bc0ffe4bb813f939fb25252ec1ae428556cc4e/10419:0", + }, + }, + componentId: "2058:19212", + isExposedInstance: true, + componentProperties: { + "Content Label#1366:2": { + value: + "You can send a fax to the dialed number or manually receive a fax by selecting one of these options.", + type: "TEXT", + }, + }, + overrides: [ + { + id: "I2099:38616;1739:34917;2326:19664;1202:3054", + overriddenFields: [ + "layoutAlign", + "sharedPluginData", + "pluginData", + "componentProperties", + "counterAxisSizingMode", + "layoutGrow", + "paddingRight", + "paddingLeft", + "primaryAxisSizingMode", + ], + }, + ], + children: [ + { + id: "I2099:38616;1739:34917;2326:19664;1202:3054;1053:1206", + name: "Content", + type: "TEXT", + scrollBehavior: "SCROLLS", + componentPropertyReferences: { + characters: "Content Label#1366:2", + }, + boundVariables: { + fills: [ + { + type: "VARIABLE_ALIAS", + id: "VariableID:e208fc12668fdb73b1d1a743ba7f06ef9dd690ba/2013:512", + }, + ], + lineHeight: [ + { + type: "VARIABLE_ALIAS", + id: "VariableID:7458686840ba6bbe67d34048518a37290df777ed/4411:711", + }, + ], + fontFamily: [ + { + type: "VARIABLE_ALIAS", + id: "VariableID:f698a4f032592c139aea25637f9791852d11c35b/1203:148", + }, + ], + fontSize: [ + { + type: "VARIABLE_ALIAS", + id: "VariableID:5926de5ea9f1df6a583324845256eec3e10058d8/3157:790", + }, + ], + }, + blendMode: "PASS_THROUGH", + fills: [ + { + blendMode: "NORMAL", + type: "SOLID", + color: { + r: 1, + g: 1, + b: 1, + a: 1, + }, + boundVariables: { + color: { + type: "VARIABLE_ALIAS", + id: "VariableID:e208fc12668fdb73b1d1a743ba7f06ef9dd690ba/2013:512", + }, + }, + }, + ], + strokes: [], + strokeWeight: 1, + strokeAlign: "OUTSIDE", + absoluteBoundingBox: { + x: -1710, + y: -2177.75, + width: 707.5, + height: 62, + }, + absoluteRenderBounds: { + x: -1709.5155029296875, + y: -2171.339599609375, + width: 681.0682373046875, + height: 53.128662109375, + }, + constraints: { + vertical: "TOP", + horizontal: "LEFT", + }, + layoutAlign: "STRETCH", + layoutGrow: 0, + layoutSizingHorizontal: "FILL", + layoutSizingVertical: "HUG", + characters: + "You can send a fax to the dialed number or manually receive a fax by selecting one of these options.", + characterStyleOverrides: [], + styleOverrideTable: {}, + lineTypes: ["NONE"], + lineIndentations: [0], + style: { + fontFamily: "HP Simplified", + fontPostScriptName: + "HPSimplified-Regular", + fontStyle: "Regular", + fontWeight: 400, + textAutoResize: "HEIGHT", + fontSize: 25.5, + textAlignHorizontal: "LEFT", + textAlignVertical: "TOP", + letterSpacing: 0, + lineHeightPx: 30.600000381469727, + lineHeightPercent: 103.53753662109375, + lineHeightPercentFontSize: 120, + lineHeightUnit: "PIXELS", + }, + layoutVersion: 4, + styles: { + text: "2022:10407", + }, + effects: [], + interactions: [], + }, + { + id: "I2099:38616;1739:34917;2326:19664;1202:3054;1202:1256", + name: "Content", + visible: false, + type: "TEXT", + scrollBehavior: "SCROLLS", + boundVariables: { + fills: [ + { + type: "VARIABLE_ALIAS", + id: "VariableID:e208fc12668fdb73b1d1a743ba7f06ef9dd690ba/2013:512", + }, + ], + lineHeight: [ + { + type: "VARIABLE_ALIAS", + id: "VariableID:7458686840ba6bbe67d34048518a37290df777ed/4411:711", + }, + ], + fontFamily: [ + { + type: "VARIABLE_ALIAS", + id: "VariableID:f698a4f032592c139aea25637f9791852d11c35b/1203:148", + }, + ], + fontSize: [ + { + type: "VARIABLE_ALIAS", + id: "VariableID:5926de5ea9f1df6a583324845256eec3e10058d8/3157:790", + }, + ], + }, + blendMode: "PASS_THROUGH", + fills: [ + { + blendMode: "NORMAL", + type: "SOLID", + color: { + r: 1, + g: 1, + b: 1, + a: 1, + }, + boundVariables: { + color: { + type: "VARIABLE_ALIAS", + id: "VariableID:e208fc12668fdb73b1d1a743ba7f06ef9dd690ba/2013:512", + }, + }, + }, + ], + strokes: [], + strokeWeight: 1, + strokeAlign: "OUTSIDE", + absoluteBoundingBox: { + x: -1720, + y: -2132.75, + width: 216, + height: 31, + }, + absoluteRenderBounds: null, + constraints: { + vertical: "TOP", + horizontal: "LEFT", + }, + layoutAlign: "STRETCH", + layoutGrow: 0, + layoutSizingHorizontal: "FIXED", + layoutSizingVertical: "FIXED", + characters: "Content text", + characterStyleOverrides: [], + styleOverrideTable: {}, + lineTypes: ["NONE"], + lineIndentations: [0], + style: { + fontFamily: "HP Simplified", + fontPostScriptName: + "HPSimplified-Regular", + fontStyle: "Regular", + fontWeight: 400, + textAutoResize: "HEIGHT", + fontSize: 25.5, + textAlignHorizontal: "LEFT", + textAlignVertical: "TOP", + letterSpacing: 0, + lineHeightPx: 30.600000381469727, + lineHeightPercent: 103.53753662109375, + lineHeightPercentFontSize: 120, + lineHeightUnit: "PIXELS", + }, + layoutVersion: 4, + styles: { + text: "2022:10407", + }, + effects: [], + interactions: [], + }, + { + id: "I2099:38616;1739:34917;2326:19664;1202:3054;1202:1267", + name: "Content", + visible: false, + type: "TEXT", + scrollBehavior: "SCROLLS", + boundVariables: { + fills: [ + { + type: "VARIABLE_ALIAS", + id: "VariableID:e208fc12668fdb73b1d1a743ba7f06ef9dd690ba/2013:512", + }, + ], + lineHeight: [ + { + type: "VARIABLE_ALIAS", + id: "VariableID:7458686840ba6bbe67d34048518a37290df777ed/4411:711", + }, + ], + fontFamily: [ + { + type: "VARIABLE_ALIAS", + id: "VariableID:f698a4f032592c139aea25637f9791852d11c35b/1203:148", + }, + ], + fontSize: [ + { + type: "VARIABLE_ALIAS", + id: "VariableID:5926de5ea9f1df6a583324845256eec3e10058d8/3157:790", + }, + ], + }, + blendMode: "PASS_THROUGH", + fills: [ + { + blendMode: "NORMAL", + type: "SOLID", + color: { + r: 1, + g: 1, + b: 1, + a: 1, + }, + boundVariables: { + color: { + type: "VARIABLE_ALIAS", + id: "VariableID:e208fc12668fdb73b1d1a743ba7f06ef9dd690ba/2013:512", + }, + }, + }, + ], + strokes: [], + strokeWeight: 1, + strokeAlign: "OUTSIDE", + absoluteBoundingBox: { + x: -1720, + y: -2132.75, + width: 216, + height: 31, + }, + absoluteRenderBounds: null, + constraints: { + vertical: "TOP", + horizontal: "LEFT", + }, + layoutAlign: "STRETCH", + layoutGrow: 0, + layoutSizingHorizontal: "FIXED", + layoutSizingVertical: "FIXED", + characters: "Content text", + characterStyleOverrides: [], + styleOverrideTable: {}, + lineTypes: ["NONE"], + lineIndentations: [0], + style: { + fontFamily: "HP Simplified", + fontPostScriptName: + "HPSimplified-Regular", + fontStyle: "Regular", + fontWeight: 400, + textAutoResize: "HEIGHT", + fontSize: 25.5, + textAlignHorizontal: "LEFT", + textAlignVertical: "TOP", + letterSpacing: 0, + lineHeightPx: 30.600000381469727, + lineHeightPercent: 103.53753662109375, + lineHeightPercentFontSize: 120, + lineHeightUnit: "PIXELS", + }, + layoutVersion: 4, + styles: { + text: "2022:10407", + }, + effects: [], + interactions: [], + }, + { + id: "I2099:38616;1739:34917;2326:19664;1202:3054;1202:1283", + name: "Content", + visible: false, + type: "TEXT", + scrollBehavior: "SCROLLS", + boundVariables: { + fills: [ + { + type: "VARIABLE_ALIAS", + id: "VariableID:e208fc12668fdb73b1d1a743ba7f06ef9dd690ba/2013:512", + }, + ], + lineHeight: [ + { + type: "VARIABLE_ALIAS", + id: "VariableID:7458686840ba6bbe67d34048518a37290df777ed/4411:711", + }, + ], + fontFamily: [ + { + type: "VARIABLE_ALIAS", + id: "VariableID:f698a4f032592c139aea25637f9791852d11c35b/1203:148", + }, + ], + fontSize: [ + { + type: "VARIABLE_ALIAS", + id: "VariableID:5926de5ea9f1df6a583324845256eec3e10058d8/3157:790", + }, + ], + }, + blendMode: "PASS_THROUGH", + fills: [ + { + blendMode: "NORMAL", + type: "SOLID", + color: { + r: 1, + g: 1, + b: 1, + a: 1, + }, + boundVariables: { + color: { + type: "VARIABLE_ALIAS", + id: "VariableID:e208fc12668fdb73b1d1a743ba7f06ef9dd690ba/2013:512", + }, + }, + }, + ], + strokes: [], + strokeWeight: 1, + strokeAlign: "OUTSIDE", + absoluteBoundingBox: { + x: -1720, + y: -2132.75, + width: 216, + height: 31, + }, + absoluteRenderBounds: null, + constraints: { + vertical: "TOP", + horizontal: "LEFT", + }, + layoutAlign: "STRETCH", + layoutGrow: 0, + layoutSizingHorizontal: "FIXED", + layoutSizingVertical: "FIXED", + characters: "Content text", + characterStyleOverrides: [], + styleOverrideTable: {}, + lineTypes: ["NONE"], + lineIndentations: [0], + style: { + fontFamily: "HP Simplified", + fontPostScriptName: + "HPSimplified-Regular", + fontStyle: "Regular", + fontWeight: 400, + textAutoResize: "HEIGHT", + fontSize: 25.5, + textAlignHorizontal: "LEFT", + textAlignVertical: "TOP", + letterSpacing: 0, + lineHeightPx: 30.600000381469727, + lineHeightPercent: 103.53753662109375, + lineHeightPercentFontSize: 120, + lineHeightUnit: "PIXELS", + }, + layoutVersion: 4, + styles: { + text: "2022:10407", + }, + effects: [], + interactions: [], + }, + { + id: "I2099:38616;1739:34917;2326:19664;1202:3054;1202:1304", + name: "Content", + visible: false, + type: "TEXT", + scrollBehavior: "SCROLLS", + boundVariables: { + fills: [ + { + type: "VARIABLE_ALIAS", + id: "VariableID:e208fc12668fdb73b1d1a743ba7f06ef9dd690ba/2013:512", + }, + ], + lineHeight: [ + { + type: "VARIABLE_ALIAS", + id: "VariableID:7458686840ba6bbe67d34048518a37290df777ed/4411:711", + }, + ], + fontFamily: [ + { + type: "VARIABLE_ALIAS", + id: "VariableID:f698a4f032592c139aea25637f9791852d11c35b/1203:148", + }, + ], + fontSize: [ + { + type: "VARIABLE_ALIAS", + id: "VariableID:5926de5ea9f1df6a583324845256eec3e10058d8/3157:790", + }, + ], + }, + blendMode: "PASS_THROUGH", + fills: [ + { + blendMode: "NORMAL", + type: "SOLID", + color: { + r: 1, + g: 1, + b: 1, + a: 1, + }, + boundVariables: { + color: { + type: "VARIABLE_ALIAS", + id: "VariableID:e208fc12668fdb73b1d1a743ba7f06ef9dd690ba/2013:512", + }, + }, + }, + ], + strokes: [], + strokeWeight: 1, + strokeAlign: "OUTSIDE", + absoluteBoundingBox: { + x: -1720, + y: -2132.75, + width: 216, + height: 31, + }, + absoluteRenderBounds: null, + constraints: { + vertical: "TOP", + horizontal: "LEFT", + }, + layoutAlign: "STRETCH", + layoutGrow: 0, + layoutSizingHorizontal: "FIXED", + layoutSizingVertical: "FIXED", + characters: "Content text", + characterStyleOverrides: [], + styleOverrideTable: {}, + lineTypes: ["NONE"], + lineIndentations: [0], + style: { + fontFamily: "HP Simplified", + fontPostScriptName: + "HPSimplified-Regular", + fontStyle: "Regular", + fontWeight: 400, + textAutoResize: "HEIGHT", + fontSize: 25.5, + textAlignHorizontal: "LEFT", + textAlignVertical: "TOP", + letterSpacing: 0, + lineHeightPx: 30.600000381469727, + lineHeightPercent: 103.53753662109375, + lineHeightPercentFontSize: 120, + lineHeightUnit: "PIXELS", + }, + layoutVersion: 4, + styles: { + text: "2022:10407", + }, + effects: [], + interactions: [], + }, + ], + blendMode: "PASS_THROUGH", + clipsContent: false, + background: [], + fills: [], + strokes: [], + strokeWeight: 1, + strokeAlign: "INSIDE", + backgroundColor: { + r: 0, + g: 0, + b: 0, + a: 0, + }, + layoutMode: "VERTICAL", + counterAxisSizingMode: "FIXED", + itemSpacing: 18, + counterAxisAlignItems: "CENTER", + primaryAxisAlignItems: "CENTER", + paddingLeft: 10, + paddingRight: 10, + layoutWrap: "NO_WRAP", + absoluteBoundingBox: { + x: -1720, + y: -2177.75, + width: 727.5, + height: 62, + }, + absoluteRenderBounds: { + x: -1720, + y: -2177.75, + width: 727.5, + height: 62, + }, + constraints: { + vertical: "TOP", + horizontal: "LEFT", + }, + layoutAlign: "STRETCH", + layoutGrow: 0, + layoutSizingHorizontal: "FILL", + layoutSizingVertical: "HUG", + effects: [], + interactions: [], + }, + { + id: "I2099:38616;1739:34917;2326:19664;1202:3055", + name: "ContentSmall", + visible: false, + type: "INSTANCE", + scrollBehavior: "SCROLLS", + componentPropertyReferences: { + visible: "↳Show Content Small#1053:13", + }, + boundVariables: { + itemSpacing: { + type: "VARIABLE_ALIAS", + id: "VariableID:61bc0ffe4bb813f939fb25252ec1ae428556cc4e/10419:0", + }, + }, + componentId: "2058:19218", + isExposedInstance: true, + componentProperties: { + "Content Small Label#1366:3": { + value: "Content small text", + type: "TEXT", + }, + }, + overrides: [ + { + id: "I2099:38616;1739:34917;2326:19664;1202:3055", + overriddenFields: [ + "sharedPluginData", + "pluginData", + ], + }, + ], + children: [], + blendMode: "PASS_THROUGH", + clipsContent: false, + background: [], + fills: [], + strokes: [], + strokeWeight: 1, + strokeAlign: "INSIDE", + backgroundColor: { + r: 0, + g: 0, + b: 0, + a: 0, + }, + layoutMode: "VERTICAL", + counterAxisSizingMode: "FIXED", + itemSpacing: 18, + counterAxisAlignItems: "CENTER", + primaryAxisAlignItems: "CENTER", + layoutWrap: "NO_WRAP", + absoluteBoundingBox: { + x: -1720, + y: -2132.75, + width: 321.5, + height: 20, + }, + absoluteRenderBounds: null, + constraints: { + vertical: "TOP", + horizontal: "LEFT", + }, + layoutAlign: "STRETCH", + layoutGrow: 0, + layoutSizingHorizontal: "FIXED", + layoutSizingVertical: "HUG", + effects: [], + interactions: [], + }, + { + id: "I2099:38616;1739:34917;2326:19664;1202:3056", + name: "Clarification", + visible: false, + type: "INSTANCE", + scrollBehavior: "SCROLLS", + componentPropertyReferences: { + visible: "↳Show Clarification#1053:7", + }, + boundVariables: { + itemSpacing: { + type: "VARIABLE_ALIAS", + id: "VariableID:61bc0ffe4bb813f939fb25252ec1ae428556cc4e/10419:0", + }, + }, + componentId: "2058:19224", + isExposedInstance: true, + componentProperties: { + "Clarification Label#1366:4": { + value: "Clarification text", + type: "TEXT", + }, + }, + overrides: [ + { + id: "I2099:38616;1739:34917;2326:19664;1202:3056", + overriddenFields: [ + "sharedPluginData", + "pluginData", + ], + }, + ], + children: [], + blendMode: "PASS_THROUGH", + clipsContent: false, + background: [], + fills: [], + strokes: [], + strokeWeight: 1, + strokeAlign: "INSIDE", + backgroundColor: { + r: 0, + g: 0, + b: 0, + a: 0, + }, + layoutMode: "VERTICAL", + counterAxisSizingMode: "FIXED", + itemSpacing: 18, + counterAxisAlignItems: "CENTER", + primaryAxisAlignItems: "CENTER", + layoutWrap: "NO_WRAP", + absoluteBoundingBox: { + x: -1720, + y: -2132.75, + width: 321.5, + height: 20, + }, + absoluteRenderBounds: null, + constraints: { + vertical: "TOP", + horizontal: "LEFT", + }, + layoutAlign: "STRETCH", + layoutGrow: 0, + layoutSizingHorizontal: "FIXED", + layoutSizingVertical: "HUG", + effects: [], + interactions: [], + }, + ], + blendMode: "PASS_THROUGH", + clipsContent: false, + background: [], + fills: [], + strokes: [], + strokeWeight: 1, + strokeAlign: "INSIDE", + backgroundColor: { + r: 0, + g: 0, + b: 0, + a: 0, + }, + layoutMode: "VERTICAL", + counterAxisSizingMode: "FIXED", + itemSpacing: 18, + primaryAxisAlignItems: "CENTER", + layoutWrap: "NO_WRAP", + absoluteBoundingBox: { + x: -1720, + y: -2177.75, + width: 727.5, + height: 62, + }, + absoluteRenderBounds: { + x: -1720, + y: -2177.75, + width: 727.5, + height: 62, + }, + constraints: { + vertical: "TOP", + horizontal: "LEFT", + }, + layoutAlign: "INHERIT", + layoutGrow: 1, + layoutSizingHorizontal: "FILL", + layoutSizingVertical: "HUG", + effects: [], + interactions: [], + }, + ], + blendMode: "PASS_THROUGH", + clipsContent: false, + background: [], + fills: [], + strokes: [], + strokeWeight: 1, + strokeAlign: "INSIDE", + backgroundColor: { + r: 0, + g: 0, + b: 0, + a: 0, + }, + layoutMode: "HORIZONTAL", + itemSpacing: 15, + primaryAxisSizingMode: "FIXED", + counterAxisAlignItems: "CENTER", + layoutWrap: "NO_WRAP", + absoluteBoundingBox: { + x: -1720, + y: -2177.75, + width: 727.5, + height: 62, + }, + absoluteRenderBounds: { + x: -1720, + y: -2177.75, + width: 727.5, + height: 62, + }, + constraints: { + vertical: "TOP", + horizontal: "LEFT", + }, + layoutAlign: "INHERIT", + layoutGrow: 1, + minWidth: 37.5, + layoutSizingHorizontal: "FILL", + layoutSizingVertical: "HUG", + effects: [], + interactions: [], + }, + { + id: "I2099:38616;1739:34917;2326:19667", + name: "Slot", + visible: false, + type: "INSTANCE", + scrollBehavior: "SCROLLS", + componentId: "2060:58189", + overrides: [ + { + id: "I2099:38616;1739:34917;2326:19667", + overriddenFields: ["visible"], + }, + { + id: "I2099:38616;1739:34917;2326:19667;979:10619", + overriddenFields: [ + "sharedPluginData", + "pluginData", + ], + }, + ], + children: [], + blendMode: "PASS_THROUGH", + clipsContent: true, + background: [], + fills: [], + strokes: [], + cornerRadius: 4, + cornerSmoothing: 0, + strokeWeight: 2, + strokeAlign: "INSIDE", + backgroundColor: { + r: 0, + g: 0, + b: 0, + a: 0, + }, + overflowDirection: "VERTICAL_SCROLLING", + layoutMode: "HORIZONTAL", + counterAxisAlignItems: "CENTER", + primaryAxisAlignItems: "CENTER", + layoutWrap: "NO_WRAP", + strokeDashes: [8, 4], + absoluteBoundingBox: { + x: -1678.5, + y: -2177.75, + width: 98, + height: 39, + }, + absoluteRenderBounds: null, + constraints: { + vertical: "TOP", + horizontal: "LEFT", + }, + layoutAlign: "INHERIT", + layoutGrow: 0, + layoutSizingHorizontal: "HUG", + layoutSizingVertical: "HUG", + effects: [], + interactions: [], + }, + { + id: "I2099:38616;1739:34917;2326:19670", + name: "Slot", + visible: false, + type: "INSTANCE", + scrollBehavior: "SCROLLS", + componentId: "2060:58189", + overrides: [ + { + id: "I2099:38616;1739:34917;2326:19670", + overriddenFields: ["visible"], + }, + { + id: "I2099:38616;1739:34917;2326:19670;979:10619", + overriddenFields: [ + "sharedPluginData", + "pluginData", + ], + }, + ], + children: [], + blendMode: "PASS_THROUGH", + clipsContent: true, + background: [], + fills: [], + strokes: [], + cornerRadius: 4, + cornerSmoothing: 0, + strokeWeight: 2, + strokeAlign: "INSIDE", + backgroundColor: { + r: 0, + g: 0, + b: 0, + a: 0, + }, + overflowDirection: "VERTICAL_SCROLLING", + layoutMode: "HORIZONTAL", + counterAxisAlignItems: "CENTER", + primaryAxisAlignItems: "CENTER", + layoutWrap: "NO_WRAP", + strokeDashes: [8, 4], + absoluteBoundingBox: { + x: -1564, + y: -2177.75, + width: 98, + height: 39, + }, + absoluteRenderBounds: null, + constraints: { + vertical: "TOP", + horizontal: "LEFT", + }, + layoutAlign: "INHERIT", + layoutGrow: 0, + layoutSizingHorizontal: "HUG", + layoutSizingVertical: "HUG", + effects: [], + interactions: [], + }, + { + id: "I2099:38616;1739:34917;2326:19688", + name: "Slot", + visible: false, + type: "INSTANCE", + scrollBehavior: "SCROLLS", + componentId: "2060:58189", + overrides: [ + { + id: "I2099:38616;1739:34917;2326:19688", + overriddenFields: ["visible"], + }, + { + id: "I2099:38616;1739:34917;2326:19688;979:10619", + overriddenFields: [ + "sharedPluginData", + "pluginData", + ], + }, + ], + children: [], + blendMode: "PASS_THROUGH", + clipsContent: true, + background: [], + fills: [], + strokes: [], + cornerRadius: 4, + cornerSmoothing: 0, + strokeWeight: 2, + strokeAlign: "INSIDE", + backgroundColor: { + r: 0, + g: 0, + b: 0, + a: 0, + }, + overflowDirection: "VERTICAL_SCROLLING", + layoutMode: "HORIZONTAL", + counterAxisAlignItems: "CENTER", + primaryAxisAlignItems: "CENTER", + layoutWrap: "NO_WRAP", + strokeDashes: [8, 4], + absoluteBoundingBox: { + x: -1907.5, + y: -2122.25, + width: 98, + height: 39, + }, + absoluteRenderBounds: null, + constraints: { + vertical: "TOP", + horizontal: "LEFT", + }, + layoutAlign: "INHERIT", + layoutGrow: 0, + layoutSizingHorizontal: "HUG", + layoutSizingVertical: "HUG", + effects: [], + interactions: [], + }, + { + id: "I2099:38616;1739:34917;2326:19691", + name: "Slot", + visible: false, + type: "INSTANCE", + scrollBehavior: "SCROLLS", + componentId: "2060:58189", + overrides: [ + { + id: "I2099:38616;1739:34917;2326:19691", + overriddenFields: ["visible"], + }, + { + id: "I2099:38616;1739:34917;2326:19691;979:10619", + overriddenFields: [ + "sharedPluginData", + "pluginData", + ], + }, + ], + children: [], + blendMode: "PASS_THROUGH", + clipsContent: true, + background: [], + fills: [], + strokes: [], + cornerRadius: 4, + cornerSmoothing: 0, + strokeWeight: 2, + strokeAlign: "INSIDE", + backgroundColor: { + r: 0, + g: 0, + b: 0, + a: 0, + }, + overflowDirection: "VERTICAL_SCROLLING", + layoutMode: "HORIZONTAL", + counterAxisAlignItems: "CENTER", + primaryAxisAlignItems: "CENTER", + layoutWrap: "NO_WRAP", + strokeDashes: [8, 4], + absoluteBoundingBox: { + x: -1793, + y: -2122.25, + width: 98, + height: 39, + }, + absoluteRenderBounds: null, + constraints: { + vertical: "TOP", + horizontal: "LEFT", + }, + layoutAlign: "INHERIT", + layoutGrow: 0, + layoutSizingHorizontal: "HUG", + layoutSizingVertical: "HUG", + effects: [], + interactions: [], + }, + { + id: "I2099:38616;1739:34917;2326:19694", + name: "Slot", + visible: false, + type: "INSTANCE", + scrollBehavior: "SCROLLS", + componentId: "2060:58189", + overrides: [ + { + id: "I2099:38616;1739:34917;2326:19694;979:10619", + overriddenFields: [ + "sharedPluginData", + "pluginData", + ], + }, + { + id: "I2099:38616;1739:34917;2326:19694", + overriddenFields: ["visible"], + }, + ], + children: [], + blendMode: "PASS_THROUGH", + clipsContent: true, + background: [], + fills: [], + strokes: [], + cornerRadius: 4, + cornerSmoothing: 0, + strokeWeight: 2, + strokeAlign: "INSIDE", + backgroundColor: { + r: 0, + g: 0, + b: 0, + a: 0, + }, + overflowDirection: "VERTICAL_SCROLLING", + layoutMode: "HORIZONTAL", + counterAxisAlignItems: "CENTER", + primaryAxisAlignItems: "CENTER", + layoutWrap: "NO_WRAP", + strokeDashes: [8, 4], + absoluteBoundingBox: { + x: -1678.5, + y: -2122.25, + width: 98, + height: 39, + }, + absoluteRenderBounds: null, + constraints: { + vertical: "TOP", + horizontal: "LEFT", + }, + layoutAlign: "INHERIT", + layoutGrow: 0, + layoutSizingHorizontal: "HUG", + layoutSizingVertical: "HUG", + effects: [], + interactions: [], + }, + { + id: "I2099:38616;1739:34917;2326:19673", + name: "Slot", + visible: false, + type: "INSTANCE", + scrollBehavior: "SCROLLS", + componentId: "2060:58189", + overrides: [ + { + id: "I2099:38616;1739:34917;2326:19673;979:10619", + overriddenFields: [ + "sharedPluginData", + "pluginData", + ], + }, + { + id: "I2099:38616;1739:34917;2326:19673", + overriddenFields: ["visible"], + }, + ], + children: [], + blendMode: "PASS_THROUGH", + clipsContent: true, + background: [], + fills: [], + strokes: [], + cornerRadius: 4, + cornerSmoothing: 0, + strokeWeight: 2, + strokeAlign: "INSIDE", + backgroundColor: { + r: 0, + g: 0, + b: 0, + a: 0, + }, + overflowDirection: "VERTICAL_SCROLLING", + layoutMode: "HORIZONTAL", + counterAxisAlignItems: "CENTER", + primaryAxisAlignItems: "CENTER", + layoutWrap: "NO_WRAP", + strokeDashes: [8, 4], + absoluteBoundingBox: { + x: -1564, + y: -2122.25, + width: 98, + height: 39, + }, + absoluteRenderBounds: null, + constraints: { + vertical: "TOP", + horizontal: "LEFT", + }, + layoutAlign: "INHERIT", + layoutGrow: 0, + layoutSizingHorizontal: "HUG", + layoutSizingVertical: "HUG", + effects: [], + interactions: [], + }, + { + id: "I2099:38616;1739:34917;2326:19697", + name: "Slot", + visible: false, + type: "INSTANCE", + scrollBehavior: "SCROLLS", + componentId: "2060:58189", + overrides: [ + { + id: "I2099:38616;1739:34917;2326:19697", + overriddenFields: ["visible"], + }, + { + id: "I2099:38616;1739:34917;2326:19697;979:10619", + overriddenFields: [ + "sharedPluginData", + "pluginData", + ], + }, + ], + children: [], + blendMode: "PASS_THROUGH", + clipsContent: true, + background: [], + fills: [], + strokes: [], + cornerRadius: 4, + cornerSmoothing: 0, + strokeWeight: 2, + strokeAlign: "INSIDE", + backgroundColor: { + r: 0, + g: 0, + b: 0, + a: 0, + }, + overflowDirection: "VERTICAL_SCROLLING", + layoutMode: "HORIZONTAL", + counterAxisAlignItems: "CENTER", + primaryAxisAlignItems: "CENTER", + layoutWrap: "NO_WRAP", + strokeDashes: [8, 4], + absoluteBoundingBox: { + x: -1907.5, + y: -2066.75, + width: 98, + height: 39, + }, + absoluteRenderBounds: null, + constraints: { + vertical: "TOP", + horizontal: "LEFT", + }, + layoutAlign: "INHERIT", + layoutGrow: 0, + layoutSizingHorizontal: "HUG", + layoutSizingVertical: "HUG", + effects: [], + interactions: [], + }, + { + id: "I2099:38616;1739:34917;2326:19700", + name: "Slot", + visible: false, + type: "INSTANCE", + scrollBehavior: "SCROLLS", + componentId: "2060:58189", + overrides: [ + { + id: "I2099:38616;1739:34917;2326:19700", + overriddenFields: ["visible"], + }, + { + id: "I2099:38616;1739:34917;2326:19700;979:10619", + overriddenFields: [ + "sharedPluginData", + "pluginData", + ], + }, + ], + children: [], + blendMode: "PASS_THROUGH", + clipsContent: true, + background: [], + fills: [], + strokes: [], + cornerRadius: 4, + cornerSmoothing: 0, + strokeWeight: 2, + strokeAlign: "INSIDE", + backgroundColor: { + r: 0, + g: 0, + b: 0, + a: 0, + }, + overflowDirection: "VERTICAL_SCROLLING", + layoutMode: "HORIZONTAL", + counterAxisAlignItems: "CENTER", + primaryAxisAlignItems: "CENTER", + layoutWrap: "NO_WRAP", + strokeDashes: [8, 4], + absoluteBoundingBox: { + x: -1793, + y: -2066.75, + width: 98, + height: 39, + }, + absoluteRenderBounds: null, + constraints: { + vertical: "TOP", + horizontal: "LEFT", + }, + layoutAlign: "INHERIT", + layoutGrow: 0, + layoutSizingHorizontal: "HUG", + layoutSizingVertical: "HUG", + effects: [], + interactions: [], + }, + { + id: "I2099:38616;1739:34917;2326:19703", + name: "Slot", + visible: false, + type: "INSTANCE", + scrollBehavior: "SCROLLS", + componentId: "2060:58189", + overrides: [ + { + id: "I2099:38616;1739:34917;2326:19703", + overriddenFields: ["visible"], + }, + { + id: "I2099:38616;1739:34917;2326:19703;979:10619", + overriddenFields: [ + "sharedPluginData", + "pluginData", + ], + }, + ], + children: [], + blendMode: "PASS_THROUGH", + clipsContent: true, + background: [], + fills: [], + strokes: [], + cornerRadius: 4, + cornerSmoothing: 0, + strokeWeight: 2, + strokeAlign: "INSIDE", + backgroundColor: { + r: 0, + g: 0, + b: 0, + a: 0, + }, + overflowDirection: "VERTICAL_SCROLLING", + layoutMode: "HORIZONTAL", + counterAxisAlignItems: "CENTER", + primaryAxisAlignItems: "CENTER", + layoutWrap: "NO_WRAP", + strokeDashes: [8, 4], + absoluteBoundingBox: { + x: -1678.5, + y: -2066.75, + width: 98, + height: 39, + }, + absoluteRenderBounds: null, + constraints: { + vertical: "TOP", + horizontal: "LEFT", + }, + layoutAlign: "INHERIT", + layoutGrow: 0, + layoutSizingHorizontal: "HUG", + layoutSizingVertical: "HUG", + effects: [], + interactions: [], + }, + { + id: "I2099:38616;1739:34917;2326:19706", + name: "Slot", + visible: false, + type: "INSTANCE", + scrollBehavior: "SCROLLS", + componentId: "2060:58189", + overrides: [ + { + id: "I2099:38616;1739:34917;2326:19706", + overriddenFields: ["visible"], + }, + { + id: "I2099:38616;1739:34917;2326:19706;979:10619", + overriddenFields: [ + "sharedPluginData", + "pluginData", + ], + }, + ], + children: [], + blendMode: "PASS_THROUGH", + clipsContent: true, + background: [], + fills: [], + strokes: [], + cornerRadius: 4, + cornerSmoothing: 0, + strokeWeight: 2, + strokeAlign: "INSIDE", + backgroundColor: { + r: 0, + g: 0, + b: 0, + a: 0, + }, + overflowDirection: "VERTICAL_SCROLLING", + layoutMode: "HORIZONTAL", + counterAxisAlignItems: "CENTER", + primaryAxisAlignItems: "CENTER", + layoutWrap: "NO_WRAP", + strokeDashes: [8, 4], + absoluteBoundingBox: { + x: -1564, + y: -2066.75, + width: 98, + height: 39, + }, + absoluteRenderBounds: null, + constraints: { + vertical: "TOP", + horizontal: "LEFT", + }, + layoutAlign: "INHERIT", + layoutGrow: 0, + layoutSizingHorizontal: "HUG", + layoutSizingVertical: "HUG", + effects: [], + interactions: [], + }, + ], + blendMode: "PASS_THROUGH", + clipsContent: false, + background: [], + fills: [], + strokes: [], + strokeWeight: 1, + strokeAlign: "INSIDE", + backgroundColor: { + r: 0, + g: 0, + b: 0, + a: 0, + }, + layoutMode: "HORIZONTAL", + itemSpacing: 37.5, + primaryAxisSizingMode: "FIXED", + layoutWrap: "WRAP", + counterAxisSpacing: 37.5, + counterAxisAlignContent: "AUTO", + absoluteBoundingBox: { + x: -1907.5, + y: -2177.75, + width: 915, + height: 150, + }, + absoluteRenderBounds: { + x: -1907.5, + y: -2177.75, + width: 915, + height: 150, + }, + constraints: { + vertical: "TOP", + horizontal: "LEFT", + }, + layoutAlign: "STRETCH", + layoutGrow: 0, + layoutSizingHorizontal: "FILL", + layoutSizingVertical: "HUG", + effects: [], + interactions: [], + uniqueName: "Row", + width: 915, + height: 150, + x: 0, + y: 0, + paddingLeft: 0, + paddingRight: 0, + paddingTop: 0, + paddingBottom: 0, + primaryAxisAlignItems: "MIN", + counterAxisAlignItems: "MIN", + }, + ], + blendMode: "PASS_THROUGH", + clipsContent: true, + background: [], + fills: [], + strokes: [], + strokeWeight: 1, + strokeAlign: "INSIDE", + backgroundColor: { + r: 0, + g: 0, + b: 0, + a: 0, + }, + layoutMode: "VERTICAL", + counterAxisSizingMode: "FIXED", + itemSpacing: 37.5, + layoutWrap: "NO_WRAP", + absoluteBoundingBox: { + x: -1907.5, + y: -2177.75, + width: 915, + height: 150, + }, + absoluteRenderBounds: { + x: -1907.5, + y: -2177.75, + width: 915, + height: 150, + }, + constraints: { + vertical: "TOP", + horizontal: "LEFT", + }, + layoutAlign: "STRETCH", + layoutGrow: 0, + layoutSizingHorizontal: "FILL", + layoutSizingVertical: "HUG", + effects: [], + interactions: [], + uniqueName: "Grid", + width: 915, + height: 150, + x: 22.5, + y: 22.5, + paddingLeft: 0, + paddingRight: 0, + paddingTop: 0, + paddingBottom: 0, + primaryAxisAlignItems: "MIN", + counterAxisAlignItems: "MIN", + }, + ], + blendMode: "PASS_THROUGH", + clipsContent: false, + background: [], + fills: [], + strokes: [], + strokeWeight: 1, + strokeAlign: "INSIDE", + backgroundColor: { + r: 0, + g: 0, + b: 0, + a: 0, + }, + overflowDirection: "VERTICAL_SCROLLING", + layoutMode: "VERTICAL", + counterAxisSizingMode: "FIXED", + itemSpacing: 37.5, + primaryAxisSizingMode: "FIXED", + paddingLeft: 22.5, + paddingRight: 22.5, + paddingTop: 22.5, + paddingBottom: 22.5, + layoutWrap: "NO_WRAP", + absoluteBoundingBox: { + x: -1930, + y: -2200.25, + width: 960, + height: 536.25, + }, + absoluteRenderBounds: { + x: -1930, + y: -2200.25, + width: 960, + height: 536.25, + }, + constraints: { + vertical: "TOP", + horizontal: "LEFT", + }, + layoutAlign: "STRETCH", + layoutGrow: 1, + minHeight: 136.25, + maxHeight: 536.25, + layoutSizingHorizontal: "FILL", + layoutSizingVertical: "FILL", + effects: [], + interactions: [], + uniqueName: "Modal Container", + variantProperties: { + Type: "Fixed", + Allignment: "Default (L-R)", + }, + width: 960, + height: 536.25, + x: 0, + y: 0, + primaryAxisAlignItems: "MIN", + counterAxisAlignItems: "MIN", + }, + { + id: "I2099:38616;1739:61707", + name: "Scroll Container", + type: "INSTANCE", + locked: true, + scrollBehavior: "SCROLLS", + componentPropertyReferences: { + visible: "↳Show Scroll Container#1739:42", + }, + componentId: "438:9274", + componentProperties: { + "Show Top Gradient#1272:20": { + value: false, + type: "BOOLEAN", + }, + "Show Bottom Gradient#1272:19": { + value: true, + type: "BOOLEAN", + }, + "Show Container Bullets#2778:4": { + value: true, + type: "BOOLEAN", + }, + "Show Left Gradient#1359:0": { + value: false, + type: "BOOLEAN", + }, + "Show Right Gradient#1359:4": { + value: true, + type: "BOOLEAN", + }, + "Show Scrollbar#1272:18": { + value: true, + type: "BOOLEAN", + }, + "Show V Scrollbar#1359:8": { + value: true, + type: "BOOLEAN", + }, + "Show H Scrollbar#1359:12": { + value: true, + type: "BOOLEAN", + }, + Variation: { + value: "Vertical", + type: "VARIANT", + boundVariables: {}, + }, + }, + overrides: [], + children: [ + { + id: "I2099:38616;1739:61707;548:24039", + name: "Gradient Top", + visible: false, + type: "RECTANGLE", + scrollBehavior: "SCROLLS", + componentPropertyReferences: { + visible: "Show Top Gradient#1272:20", + }, + boundVariables: { + size: { + y: { + type: "VARIABLE_ALIAS", + id: "VariableID:7de17999810183e2464dbfd32573c3ecf15a9f2e/3157:462", + }, + }, + fills: [ + { + type: "VARIABLE_ALIAS", + id: "VariableID:26724dcbb1ea60587379f89ef3dc160ae7f7f7da/2022:241", + }, + { + type: "VARIABLE_ALIAS", + id: "VariableID:86e61e4b2a2fe9ab507a9302544920d38675a086/2036:2", + }, + ], + }, + blendMode: "PASS_THROUGH", + fills: [ + { + blendMode: "NORMAL", + type: "GRADIENT_LINEAR", + gradientHandlePositions: [ + { + x: 0.5, + y: -3.0616171314629196e-17, + }, + { + x: 0.5, + y: 0.9999999999999999, + }, + { + x: 0, + y: 0, + }, + ], + gradientStops: [ + { + color: { + r: 0.0784313753247261, + g: 0.0784313753247261, + b: 0.0784313753247261, + a: 1, + }, + position: 0, + boundVariables: { + color: { + type: "VARIABLE_ALIAS", + id: "VariableID:86e61e4b2a2fe9ab507a9302544920d38675a086/2036:2", + }, + }, + }, + { + color: { + r: 0.0784313753247261, + g: 0.0784313753247261, + b: 0.0784313753247261, + a: 0, + }, + position: 1, + boundVariables: { + color: { + type: "VARIABLE_ALIAS", + id: "VariableID:26724dcbb1ea60587379f89ef3dc160ae7f7f7da/2022:241", + }, + }, + }, + ], + }, + ], + strokes: [], + strokeWeight: 1, + strokeAlign: "INSIDE", + styles: { + fill: "438:9260", + }, + absoluteBoundingBox: { + x: -1930, + y: -2200, + width: 960, + height: 30, + }, + absoluteRenderBounds: null, + constraints: { + vertical: "TOP", + horizontal: "LEFT_RIGHT", + }, + layoutAlign: "INHERIT", + layoutGrow: 0, + layoutPositioning: "ABSOLUTE", + layoutSizingHorizontal: "FIXED", + layoutSizingVertical: "FIXED", + effects: [], + interactions: [], + }, + { + id: "I2099:38616;1739:61707;548:24040", + name: "Gradient Bottom", + type: "RECTANGLE", + scrollBehavior: "SCROLLS", + componentPropertyReferences: { + visible: "Show Bottom Gradient#1272:19", + }, + boundVariables: { + size: { + y: { + type: "VARIABLE_ALIAS", + id: "VariableID:7de17999810183e2464dbfd32573c3ecf15a9f2e/3157:462", + }, + }, + fills: [ + { + type: "VARIABLE_ALIAS", + id: "VariableID:86e61e4b2a2fe9ab507a9302544920d38675a086/2036:2", + }, + { + type: "VARIABLE_ALIAS", + id: "VariableID:26724dcbb1ea60587379f89ef3dc160ae7f7f7da/2022:241", + }, + ], + }, + blendMode: "PASS_THROUGH", + fills: [ + { + blendMode: "NORMAL", + type: "GRADIENT_LINEAR", + gradientHandlePositions: [ + { + x: 0.5, + y: -3.0616171314629196e-17, + }, + { + x: 0.5, + y: 0.9999999999999999, + }, + { + x: 0, + y: 0, + }, + ], + gradientStops: [ + { + color: { + r: 0.0784313753247261, + g: 0.0784313753247261, + b: 0.0784313753247261, + a: 0, + }, + position: 0, + boundVariables: { + color: { + type: "VARIABLE_ALIAS", + id: "VariableID:26724dcbb1ea60587379f89ef3dc160ae7f7f7da/2022:241", + }, + }, + }, + { + color: { + r: 0.0784313753247261, + g: 0.0784313753247261, + b: 0.0784313753247261, + a: 1, + }, + position: 1, + boundVariables: { + color: { + type: "VARIABLE_ALIAS", + id: "VariableID:86e61e4b2a2fe9ab507a9302544920d38675a086/2036:2", + }, + }, + }, + ], + }, + ], + strokes: [], + strokeWeight: 1, + strokeAlign: "INSIDE", + styles: { + fill: "438:9261", + }, + absoluteBoundingBox: { + x: -1930, + y: -1686, + width: 960, + height: 30, + }, + absoluteRenderBounds: { + x: -1930, + y: -1686, + width: 960, + height: 22, + }, + constraints: { + vertical: "BOTTOM", + horizontal: "LEFT_RIGHT", + }, + layoutAlign: "INHERIT", + layoutGrow: 0, + layoutPositioning: "ABSOLUTE", + layoutSizingHorizontal: "FIXED", + layoutSizingVertical: "FIXED", + effects: [], + interactions: [], + }, + { + id: "I2099:38616;1739:61707;548:24046", + name: "Scrollbar V", + type: "FRAME", + scrollBehavior: "SCROLLS", + componentPropertyReferences: { + visible: "Show Scrollbar#1272:18", + }, + boundVariables: { + paddingTop: { + type: "VARIABLE_ALIAS", + id: "VariableID:eb2b818a81b47a755734677805c327446a7eaacc/3157:1078", + }, + paddingRight: { + type: "VARIABLE_ALIAS", + id: "VariableID:eb2b818a81b47a755734677805c327446a7eaacc/3157:1078", + }, + paddingBottom: { + type: "VARIABLE_ALIAS", + id: "VariableID:eb2b818a81b47a755734677805c327446a7eaacc/3157:1078", + }, + }, + children: [ + { + id: "I2099:38616;1739:61707;548:24047", + name: "Bar", + type: "RECTANGLE", + scrollBehavior: "SCROLLS", + boundVariables: { + minHeight: { + type: "VARIABLE_ALIAS", + id: "VariableID:5fdc417718534e1862bd74d2b0cae46cbc3fdd93/3157:975", + }, + size: { + x: { + type: "VARIABLE_ALIAS", + id: "VariableID:4e7717a4a0ee45c0a9977c43d56cf4cb32848299/3157:257", + }, + }, + rectangleCornerRadii: { + RECTANGLE_TOP_LEFT_CORNER_RADIUS: { + type: "VARIABLE_ALIAS", + id: "VariableID:2044450eb10ae8e476766aeb5bf3550e3c2ba36e/3157:901", + }, + RECTANGLE_TOP_RIGHT_CORNER_RADIUS: { + type: "VARIABLE_ALIAS", + id: "VariableID:2044450eb10ae8e476766aeb5bf3550e3c2ba36e/3157:901", + }, + RECTANGLE_BOTTOM_LEFT_CORNER_RADIUS: { + type: "VARIABLE_ALIAS", + id: "VariableID:2044450eb10ae8e476766aeb5bf3550e3c2ba36e/3157:901", + }, + RECTANGLE_BOTTOM_RIGHT_CORNER_RADIUS: { + type: "VARIABLE_ALIAS", + id: "VariableID:2044450eb10ae8e476766aeb5bf3550e3c2ba36e/3157:901", + }, + }, + fills: [ + { + type: "VARIABLE_ALIAS", + id: "VariableID:9b31d8ddc760d048c5d9e42dbce12aa5946a3041/2036:15", + }, + ], + }, + blendMode: "PASS_THROUGH", + fills: [ + { + blendMode: "NORMAL", + type: "SOLID", + color: { + r: 0.658823549747467, + g: 0.658823549747467, + b: 0.658823549747467, + a: 1, + }, + boundVariables: { + color: { + type: "VARIABLE_ALIAS", + id: "VariableID:9b31d8ddc760d048c5d9e42dbce12aa5946a3041/2036:15", + }, + }, + }, + ], + strokes: [], + strokeWeight: 1, + strokeAlign: "INSIDE", + cornerRadius: 1000, + cornerSmoothing: 0, + absoluteBoundingBox: { + x: -979, + y: -2195.5, + width: 4.5, + height: 31, + }, + absoluteRenderBounds: { + x: -979, + y: -2195.5, + width: 4.5, + height: 31, + }, + constraints: { + vertical: "TOP", + horizontal: "LEFT", + }, + layoutAlign: "INHERIT", + layoutGrow: 1, + minHeight: 15, + layoutSizingHorizontal: "FIXED", + layoutSizingVertical: "FILL", + effects: [], + interactions: [], + }, + ], + blendMode: "PASS_THROUGH", + clipsContent: false, + background: [], + fills: [], + strokes: [], + strokeWeight: 1, + strokeAlign: "INSIDE", + backgroundColor: { + r: 0, + g: 0, + b: 0, + a: 0, + }, + layoutMode: "VERTICAL", + primaryAxisSizingMode: "FIXED", + counterAxisAlignItems: "MAX", + paddingRight: 4.5, + paddingTop: 4.5, + paddingBottom: 4.5, + layoutWrap: "NO_WRAP", + absoluteBoundingBox: { + x: -979, + y: -2200, + width: 9, + height: 40, + }, + absoluteRenderBounds: { + x: -979, + y: -2200, + width: 9, + height: 40, + }, + constraints: { + vertical: "TOP", + horizontal: "LEFT", + }, + layoutAlign: "INHERIT", + layoutGrow: 0, + layoutSizingHorizontal: "HUG", + layoutSizingVertical: "FIXED", + effects: [], + interactions: [], + }, + ], + blendMode: "PASS_THROUGH", + clipsContent: true, + background: [], + fills: [], + strokes: [], + strokeWeight: 1, + strokeAlign: "INSIDE", + backgroundColor: { + r: 0, + g: 0, + b: 0, + a: 0, + }, + layoutMode: "VERTICAL", + counterAxisSizingMode: "FIXED", + primaryAxisSizingMode: "FIXED", + counterAxisAlignItems: "MAX", + layoutWrap: "NO_WRAP", + absoluteBoundingBox: { + x: -1930, + y: -2200, + width: 960, + height: 536, + }, + absoluteRenderBounds: { + x: -1930, + y: -2200, + width: 960, + height: 536, + }, + constraints: { + vertical: "TOP_BOTTOM", + horizontal: "LEFT_RIGHT", + }, + layoutAlign: "INHERIT", + layoutGrow: 0, + layoutPositioning: "ABSOLUTE", + layoutSizingHorizontal: "FIXED", + layoutSizingVertical: "FIXED", + effects: [], + interactions: [], + uniqueName: "Scroll Container", + variantProperties: { + Variation: "Vertical", + }, + width: 960, + height: 536, + x: 0, + y: 0.25, + paddingLeft: 0, + paddingRight: 0, + paddingTop: 0, + paddingBottom: 0, + primaryAxisAlignItems: "MIN", + }, + ], + blendMode: "PASS_THROUGH", + clipsContent: true, + background: [], + fills: [], + strokes: [], + strokeWeight: 1, + strokeAlign: "INSIDE", + backgroundColor: { + r: 0, + g: 0, + b: 0, + a: 0, + }, + layoutMode: "VERTICAL", + counterAxisSizingMode: "FIXED", + itemSpacing: 10, + primaryAxisSizingMode: "FIXED", + layoutWrap: "NO_WRAP", + absoluteBoundingBox: { + x: -1930, + y: -2200.25, + width: 960, + height: 536.25, + }, + absoluteRenderBounds: { + x: -1930, + y: -2200.25, + width: 960, + height: 536.25, + }, + constraints: { + vertical: "TOP", + horizontal: "LEFT", + }, + layoutAlign: "STRETCH", + layoutGrow: 1, + layoutSizingHorizontal: "FILL", + layoutSizingVertical: "FILL", + effects: [], + interactions: [], + uniqueName: "Content", + width: 960, + height: 536.25, + x: 0, + y: 93.75, + paddingLeft: 0, + paddingRight: 0, + paddingTop: 0, + paddingBottom: 0, + primaryAxisAlignItems: "MIN", + counterAxisAlignItems: "MIN", + isRelative: true, + }, + ], + blendMode: "PASS_THROUGH", + clipsContent: true, + background: [ + { + blendMode: "NORMAL", + type: "SOLID", + color: { + r: 0.1411764770746231, + g: 0.1411764770746231, + b: 0.1411764770746231, + a: 1, + }, + boundVariables: { + color: { + type: "VARIABLE_ALIAS", + id: "VariableID:8cbcd0032a7cac3b9799f16f6f48c35cab554a40/2243:10", + }, + }, + }, + ], + fills: [ + { + blendMode: "NORMAL", + type: "SOLID", + color: { + r: 0.1411764770746231, + g: 0.1411764770746231, + b: 0.1411764770746231, + a: 1, + }, + boundVariables: { + color: { + type: "VARIABLE_ALIAS", + id: "VariableID:8cbcd0032a7cac3b9799f16f6f48c35cab554a40/2243:10", + }, + }, + variableColorName: "Box-Style-box4", + }, + ], + strokes: [], + cornerRadius: 4.5, + cornerSmoothing: 0, + strokeWeight: 1, + strokeAlign: "INSIDE", + backgroundColor: { + r: 0.1411764770746231, + g: 0.1411764770746231, + b: 0.1411764770746231, + a: 1, + }, + layoutMode: "VERTICAL", + counterAxisSizingMode: "FIXED", + primaryAxisSizingMode: "FIXED", + counterAxisAlignItems: "CENTER", + layoutWrap: "NO_WRAP", + absoluteBoundingBox: { + x: -1930, + y: -2294, + width: 960, + height: 720, + }, + absoluteRenderBounds: { + x: -1969, + y: -2323.10009765625, + width: 1038, + height: 789.10009765625, + }, + constraints: { + vertical: "TOP", + horizontal: "LEFT", + }, + layoutAlign: "INHERIT", + layoutGrow: 0, + minHeight: 320, + maxHeight: 720, + layoutSizingHorizontal: "FIXED", + layoutSizingVertical: "FIXED", + effects: [ + { + type: "DROP_SHADOW", + visible: true, + color: { + r: 0, + g: 0, + b: 0, + a: 0.47999998927116394, + }, + blendMode: "NORMAL", + offset: { + x: 0, + y: 9.899999618530273, + }, + radius: 39, + showShadowBehindNode: true, + boundVariables: { + radius: { + type: "VARIABLE_ALIAS", + id: "VariableID:7b576d4f7cef936e728857b8d6f7952e2a6dd6fe/3157:78", + }, + color: { + type: "VARIABLE_ALIAS", + id: "VariableID:e7dccd708e8eb5f689ef26147d2d985b7af1bfd8/2013:336", + }, + offsetY: { + type: "VARIABLE_ALIAS", + id: "VariableID:55268df3aca26e8ed4182c6831670c631ab2e88b/4411:298", + }, + }, + variableColorName: "Elevation-elevation5", + }, + ], + styles: { + effect: "438:10166", + }, + interactions: [], + uniqueName: "Modal", + width: 960, + height: 720, + x: 160, + y: 40, + paddingLeft: 0, + paddingRight: 0, + paddingTop: 0, + paddingBottom: 0, + primaryAxisAlignItems: "MIN", + }, + ]; console.log( `[benchmark] convertNodesToAltNodes: ${Date.now() - convertNodesStart}ms`, ); } + console.log("[debug] convertedSelection", { ...convertedSelection[0] }); + // ignore when nothing was selected // If the selection was empty, the converted selection will also be empty. if (convertedSelection.length === 0) { @@ -482,7 +3568,7 @@ export const run = async (settings: PluginSettings) => { const colorPanelStart = Date.now(); const colors = retrieveGenericSolidUIColors(framework); - const gradients = retrieveGenericGradients(framework); + // const gradients = retrieveGenericGradients(framework); console.log( `[benchmark] color and gradient panel: ${Date.now() - colorPanelStart}ms`, ); @@ -513,7 +3599,7 @@ export const run = async (settings: PluginSettings) => { code, htmlPreview, colors, - gradients, + gradients: [], settings, warnings: [...warnings], }); diff --git a/packages/backend/src/common/color.ts b/packages/backend/src/common/color.ts index a7c9a496..e143fec2 100644 --- a/packages/backend/src/common/color.ts +++ b/packages/backend/src/common/color.ts @@ -59,42 +59,24 @@ export const rgbToCssColor = (color: RGB | RGBA, alpha: number = 1): string => { // ---- Gradient Transformation ---- export const gradientAngle = (fill: GradientPaint): number => { - // Thanks Gleb and Liam for helping! - const decomposed = decomposeRelativeTransform( - fill.gradientTransform[0], - fill.gradientTransform[1], - ); - - return (decomposed.rotation * 180) / Math.PI; -}; - -// Calculate gradient angle for CSS (different coordinate system) -export const cssGradientAngle = (angle: number): number => { - // Normalize angle: if negative, add 360 to make it positive. - return angle < 0 ? angle + 360 : angle; + const [start, end] = fill.gradientHandlePositions; + return calculateAngle(start, end); }; -// Calculate gradient coordinates for a matrix transform -export const getGradientTransformCoordinates = ( - gradientTransform: number[][], -): { centerX: string; centerY: string; radiusX: string; radiusY: string } => { - const a = gradientTransform[0][0]; - const b = gradientTransform[0][1]; - const c = gradientTransform[1][0]; - const d = gradientTransform[1][1]; - const e = gradientTransform[0][2]; - const f = gradientTransform[1][2]; - - const scaleX = Math.sqrt(a ** 2 + b ** 2); - const scaleY = Math.sqrt(c ** 2 + d ** 2); - - const centerX = ((e * scaleX * 100) / (1 - scaleX)).toFixed(2); - const centerY = (((1 - f) * scaleY * 100) / (1 - scaleY)).toFixed(2); - - const radiusX = (scaleX * 100).toFixed(2); - const radiusY = (scaleY * 100).toFixed(2); - - return { centerX, centerY, radiusX, radiusY }; +/** + * Calculate the angle between two points in degrees + * @param start Starting point {x, y} in normalized coordinates (0-1) + * @param end Ending point {x, y} in normalized coordinates (0-1) + * @returns Angle in degrees (0-360) + */ +export const calculateAngle = ( + start: { x: number; y: number }, + end: { x: number; y: number }, +): number => { + const dx = end.x - start.x; + const dy = end.y - start.y; + let angle = Math.atan2(dy, dx) * (180 / Math.PI); + return (angle + 360) % 360; // Normalize to 0-360 degrees }; // from https://math.stackexchange.com/a/2888105 diff --git a/packages/backend/src/common/commonPosition.ts b/packages/backend/src/common/commonPosition.ts index 7899b4e7..5001088c 100644 --- a/packages/backend/src/common/commonPosition.ts +++ b/packages/backend/src/common/commonPosition.ts @@ -19,25 +19,14 @@ export const commonIsAbsolutePosition = (node: SceneNode) => { return true; } - // No position when parent is inferred auto layout. - // if ( - // optimizeLayout && - // node.parent && - // "layoutMode" in node.parent && - // node.parent.inferredAutoLayout !== null - // ) { - // return false; - // } - if (!node.parent || node.parent === undefined) { return false; } - const parentLayoutIsNone = - "layoutMode" in node.parent && node.parent.layoutMode === "NONE"; - const hasNoLayoutMode = !("layoutMode" in node.parent); - - if (parentLayoutIsNone || hasNoLayoutMode) { + if ( + ("layoutMode" in node.parent && node.parent.layoutMode === "NONE") || + !("layoutMode" in node.parent) + ) { return true; } diff --git a/packages/backend/src/common/retrieveFill.ts b/packages/backend/src/common/retrieveFill.ts index 233c2edb..2fe1cd4a 100644 --- a/packages/backend/src/common/retrieveFill.ts +++ b/packages/backend/src/common/retrieveFill.ts @@ -1,12 +1,16 @@ +import { Paint } from "../api_types"; + /** * Retrieve the first visible color that is being used by the layer, in case there are more than one. */ export const retrieveTopFill = ( - fills: ReadonlyArray | PluginAPI["mixed"] | undefined, + fills: ReadonlyArray | undefined, ): Paint | undefined => { - if (fills && fills !== figma.mixed && fills.length > 0) { + if (fills && Array.isArray(fills) && fills.length > 0) { // on Figma, the top layer is always at the last position // reverse, then try to find the first layer that is visible, if any. return [...fills].reverse().find((d) => d.visible !== false); } + + return undefined; }; diff --git a/packages/backend/src/flutter/builderImpl/flutterColor.ts b/packages/backend/src/flutter/builderImpl/flutterColor.ts index 5beb4c31..9964f520 100644 --- a/packages/backend/src/flutter/builderImpl/flutterColor.ts +++ b/packages/backend/src/flutter/builderImpl/flutterColor.ts @@ -1,12 +1,13 @@ -import { rgbTo8hex, gradientAngle } from "../../common/color"; +import { StarNode } from "./../../api_types"; +import { rgbTo8hex } from "../../common/color"; import { addWarning } from "../../common/commonConversionWarnings"; import { generateWidgetCode, numberToFixedString, } from "../../common/numToAutoFixed"; import { retrieveTopFill } from "../../common/retrieveFill"; -import { nearestValue } from "../../tailwind/conversionTables"; import { getPlaceholderImage } from "../../common/images"; +import { GradientPaint, ImagePaint, Paint } from "../../api_types"; /** * Retrieve the SOLID color for Flutter when existent, otherwise "" @@ -17,11 +18,9 @@ export const flutterColorFromFills = ( node: SceneNode, propertyPath: string, ): string => { - let fills: ReadonlyArray | PluginAPI["mixed"]; - fills = node[propertyPath as keyof SceneNode] as - | ReadonlyArray - | PluginAPI["mixed"]; - + let fills: ReadonlyArray = node[ + propertyPath as keyof SceneNode + ] as ReadonlyArray; return flutterColorFromDirectFills(fills); }; @@ -30,15 +29,15 @@ export const flutterColorFromFills = ( * @param fills The fills array to process */ export const flutterColorFromDirectFills = ( - fills: ReadonlyArray | PluginAPI["mixed"], + fills: ReadonlyArray, ): string => { const fill = retrieveTopFill(fills); if (fill && fill.type === "SOLID") { return flutterColor( - fill.color, - fill.opacity ?? 1.0, - (fill as any).variableColorName + fill.color, + fill.opacity ?? 1.0, + (fill as any).variableColorName, ); } else if ( fill && @@ -49,9 +48,9 @@ export const flutterColorFromDirectFills = ( if (fill.gradientStops.length > 0) { const stop = fill.gradientStops[0]; return flutterColor( - stop.color, - fill.opacity ?? 1.0, - (stop as any).variableColorName + stop.color, + fill.opacity ?? 1.0, + (stop as any).variableColorName, ); } } @@ -66,16 +65,16 @@ export const flutterBoxDecorationColor = ( node: SceneNode, propertyPath: string, ): Record => { - let fills: ReadonlyArray | PluginAPI["mixed"]; - fills = node[propertyPath as keyof SceneNode] as - | ReadonlyArray - | PluginAPI["mixed"]; + let fills: ReadonlyArray; + fills = node[propertyPath as keyof SceneNode] as ReadonlyArray; const fill = retrieveTopFill(fills); if (fill && fill.type === "SOLID") { const opacity = fill.opacity ?? 1.0; - return { color: flutterColor(fill.color, opacity, (fill as any).variableColorName) }; + return { + color: flutterColor(fill.color, opacity, (fill as any).variableColorName), + }; } else if ( fill?.type === "GRADIENT_LINEAR" || fill?.type === "GRADIENT_RADIAL" || @@ -100,13 +99,13 @@ export const flutterDecorationImage = (node: SceneNode, fill: ImagePaint) => { const fitToBoxFit = (fill: ImagePaint): string => { switch (fill.scaleMode) { case "FILL": - return "BoxFit.fill"; + return "BoxFit.cover"; // FILL in Figma covers the entire area, similar to BoxFit.cover case "FIT": - return "BoxFit.contain"; - case "CROP": - return "BoxFit.cover"; + return "BoxFit.contain"; // FIT in Figma fits the image while maintaining aspect ratio, like BoxFit.contain + case "STRETCH": + return "BoxFit.fill"; // STRETCH in Figma stretches the image, like BoxFit.fill case "TILE": - return "BoxFit.none"; + return "BoxFit.none"; // TILE doesn't have a direct equivalent, but BoxFit.none is closest default: return "BoxFit.cover"; } @@ -126,87 +125,92 @@ export const flutterGradient = (fill: GradientPaint): string => { } }; -const gradientDirection = (angle: number): string => { - const radians = (angle * Math.PI) / 180; - const x = Math.cos(radians).toFixed(2); - const y = Math.sin(radians).toFixed(2); - return `begin: Alignment(${x}, ${y}), end: Alignment(${-x}, ${-y})`; -}; - -const flutterRadialGradient = (fill: GradientPaint): string => { +/** + * Generate a Flutter LinearGradient widget + * @param fill The linear gradient fill + * @returns LinearGradient widget code + */ +const flutterLinearGradient = (fill: GradientPaint): string => { + const [start, end] = fill.gradientHandlePositions; const colors = fill.gradientStops .map((d) => flutterColor(d.color, d.color.a, (d as any).variableColorName)) .join(", "); - - const x = numberToFixedString(fill.gradientTransform[0][2]); - const y = numberToFixedString(fill.gradientTransform[1][2]); - const scaleX = fill.gradientTransform[0][0]; - const scaleY = fill.gradientTransform[1][1]; - const r = numberToFixedString(Math.sqrt(scaleX * scaleX + scaleY * scaleY)); - - return generateWidgetCode("RadialGradient", { - center: `Alignment(${x}, ${y})`, - radius: r, + return generateWidgetCode("LinearGradient", { + begin: `Alignment(${start.x.toFixed(2)}, ${start.y.toFixed(2)})`, + end: `Alignment(${end.x.toFixed(2)}, ${end.y.toFixed(2)})`, colors: `[${colors}]`, }); }; -const flutterAngularGradient = (fill: GradientPaint): string => { +/** + * Generate a Flutter RadialGradient widget + * @param fill The radial gradient fill + * @returns RadialGradient widget code + */ +const flutterRadialGradient = (fill: GradientPaint): string => { + const [center, h1, h2] = (fill as any).gradientHandlePositions; + const radius1 = Math.sqrt((h1.x - center.x) ** 2 + (h1.y - center.y) ** 2); + const radius2 = Math.sqrt((h2.x - center.x) ** 2 + (h2.y - center.y) ** 2); + const radius = Math.max(radius1, radius2); const colors = fill.gradientStops .map((d) => flutterColor(d.color, d.color.a, (d as any).variableColorName)) .join(", "); - - const x = numberToFixedString(fill.gradientTransform[0][2]); - const y = numberToFixedString(fill.gradientTransform[1][2]); - const startAngle = numberToFixedString(-fill.gradientTransform[0][0]); - const endAngle = numberToFixedString(-fill.gradientTransform[0][1]); - - return generateWidgetCode("SweepGradient", { - center: `Alignment(${x}, ${y})`, - startAngle: startAngle, - endAngle: endAngle, + return generateWidgetCode("RadialGradient", { + center: `Alignment(${center.x.toFixed(2)}, ${center.y.toFixed(2)})`, + radius: radius.toFixed(2), colors: `[${colors}]`, }); }; -const flutterLinearGradient = (fill: GradientPaint): string => { - const radians = (-gradientAngle(fill) * Math.PI) / 180; - const x = Math.cos(radians).toFixed(2); - const y = Math.sin(radians).toFixed(2); +/** + * Convert Figma's normalized coordinates (0 to 1) to Flutter's Alignment (-1 to 1) + * @param x Figma's x coordinate (0 to 1) + * @param y Figma's y coordinate (0 to 1) + * @returns Flutter's Alignment string + */ +const figmaToFlutterAlignment = (x: number, y: number): string => { + const alignmentX = x * 2 - 1; + const alignmentY = y * 2 - 1; + return `Alignment(${numberToFixedString(alignmentX)}, ${numberToFixedString(alignmentY)})`; +}; + +/** + * Generate a Flutter SweepGradient widget (for angular gradients) + * @param fill The angular gradient fill + * @returns SweepGradient widget code + */ +export const flutterAngularGradient = (fill: GradientPaint): string => { + // TODO This function is not 100% perfect but gets close. It is hard to get AngularGradient in Flutter. + const [center, _, startDirection] = fill.gradientHandlePositions; + + // Center alignment + const centerAlignment = figmaToFlutterAlignment(center.x, center.y); + // Starting angle + const dx = startDirection.x - center.x; + const dy = startDirection.y - center.y; + const startAngle = -(90 * Math.PI) / 180 + Math.atan2(dy, dx); + + // Generate colors and stops const colors = fill.gradientStops - .map((d) => flutterColor(d.color, d.color.a, (d as any).variableColorName)) + .map((stop) => flutterColor(stop.color, stop.color.a)) .join(", "); - return generateWidgetCode("LinearGradient", { - begin: `Alignment(${x}, ${y})`, - end: `Alignment(${-x}, ${-y})`, + const stops = fill.gradientStops + .map((stop) => numberToFixedString(stop.position)) + .join(", "); + + // Generate SweepGradient code + return generateWidgetCode("SweepGradient", { + center: centerAlignment, + startAngle: numberToFixedString(startAngle), + endAngle: numberToFixedString(startAngle + 2 * Math.PI), colors: `[${colors}]`, + stops: `[${stops}]`, + transform: `GradientRotation(${numberToFixedString(startAngle)})`, }); }; -const gradientDirectionReadable = (angle: number): string => { - switch (nearestValue(angle, [-180, -135, -90, -45, 0, 45, 90, 135, 180])) { - case 0: - return "begin: Alignment.centerLeft, end: Alignment.centerRight"; - case 45: - return "begin: Alignment.topLeft, end: Alignment.bottomRight"; - case 90: - return "begin: Alignment.topCenter, end: Alignment.bottomCenter"; - case 135: - return "begin: Alignment.topRight, end: Alignment.bottomLeft"; - case -45: - return "begin: Alignment.bottomLeft, end: Alignment.topRight"; - case -90: - return "begin: Alignment.bottomCenter, end: Alignment.topCenter"; - case -135: - return "begin: Alignment.bottomRight, end: Alignment.topLeft"; - default: - // 180 and -180 - return "begin: Alignment.centerRight, end: Alignment.centerLeft"; - } -}; - /** * Convert opacity (0-1) to alpha (0-255) */ @@ -215,21 +219,23 @@ const opacityToAlpha = (opacity: number): number => { }; export const flutterColor = ( - color: RGB, - opacity: number, - variableColorName?: string + color: RGB, + opacity: number, + variableColorName?: string, ): string => { const sum = color.r + color.g + color.b; let colorCode = ""; if (sum === 0) { - colorCode = opacity === 1 - ? "Colors.black" - : `Colors.black.withValues(alpha: ${opacityToAlpha(opacity)})`; + colorCode = + opacity === 1 + ? "Colors.black" + : `Colors.black.withValues(alpha: ${opacityToAlpha(opacity)})`; } else if (sum === 3) { - colorCode = opacity === 1 - ? "Colors.white" - : `Colors.white.withValues(alpha: ${opacityToAlpha(opacity)})`; + colorCode = + opacity === 1 + ? "Colors.white" + : `Colors.white.withValues(alpha: ${opacityToAlpha(opacity)})`; } else { // Always use full 8-digit hex which includes alpha channel colorCode = `Color(0x${rgbTo8hex(color, opacity).toUpperCase()})`; @@ -239,6 +245,6 @@ export const flutterColor = ( if (variableColorName) { return `${colorCode} /* ${variableColorName} */`; } - + return colorCode; }; diff --git a/packages/backend/src/flutter/flutterMain.ts b/packages/backend/src/flutter/flutterMain.ts index 162e45e5..6d550e1f 100644 --- a/packages/backend/src/flutter/flutterMain.ts +++ b/packages/backend/src/flutter/flutterMain.ts @@ -89,7 +89,6 @@ const flutterWidgetGenerator = ( // filter non visible nodes. This is necessary at this step because conversion already happened. const visibleSceneNode = getVisibleNodes(sceneNode); - const sceneLen = visibleSceneNode.length; visibleSceneNode.forEach((node) => { switch (node.type) { @@ -183,7 +182,7 @@ const flutterFrame = ( } // Generate widget code for children - const children = flutterWidgetGenerator(sortedChildren); + const children = flutterWidgetGenerator(node.children); // Force Stack for any frame that has absolute positioned children if (hasAbsoluteChildren) { diff --git a/packages/backend/src/html/builderImpl/htmlBorderRadius.ts b/packages/backend/src/html/builderImpl/htmlBorderRadius.ts index 33451874..3ba1094d 100644 --- a/packages/backend/src/html/builderImpl/htmlBorderRadius.ts +++ b/packages/backend/src/html/builderImpl/htmlBorderRadius.ts @@ -52,6 +52,5 @@ export const htmlBorderRadius = (node: SceneNode, isJsx: boolean): string[] => { } } - console.log("comp was", comp); return comp; }; diff --git a/packages/backend/src/html/builderImpl/htmlColor.ts b/packages/backend/src/html/builderImpl/htmlColor.ts index 73b0c6db..0b9cda0a 100644 --- a/packages/backend/src/html/builderImpl/htmlColor.ts +++ b/packages/backend/src/html/builderImpl/htmlColor.ts @@ -1,6 +1,6 @@ -import { HTMLSettings } from "types"; import { numberToFixedString } from "../../common/numToAutoFixed"; import { retrieveTopFill } from "../../common/retrieveFill"; +import { GradientPaint, Paint } from "../../api_types"; /** * Helper to process a color with variable binding if present @@ -53,10 +53,11 @@ const getColorAndVariable = ( return { color: { r: 0, g: 0, b: 0 }, opacity: 0 }; }; -// Retrieve the SOLID color or approximate gradient as HTML color +/** + * Convert fills to an HTML color string + */ export const htmlColorFromFills = ( - fills: ReadonlyArray | PluginAPI["mixed"] | undefined, - settings: HTMLSettings, + fills: ReadonlyArray | undefined, ): string => { const fill = retrieveTopFill(fills); if (fill) { @@ -66,6 +67,9 @@ export const htmlColorFromFills = ( return ""; }; +/** + * Convert RGB color to CSS color string + */ export const htmlColor = (color: RGB, alpha: number = 1): string => { if (color.r === 1 && color.g === 1 && color.b === 1 && alpha === 1) { return "white"; @@ -87,7 +91,9 @@ export const htmlColor = (color: RGB, alpha: number = 1): string => { return `rgba(${r}, ${g}, ${b}, ${a})`; }; -// Process a single gradient stop with proper color and position +/** + * Process a single gradient stop + */ const processGradientStop = ( stop: ColorStop, fillOpacity: number = 1, @@ -106,7 +112,9 @@ const processGradientStop = ( return `${color} ${position}`; }; -// Process all gradient stops for any gradient type +/** + * Process all gradient stops for a gradient + */ const processGradientStops = ( stops: ReadonlyArray, fillOpacity: number = 1, @@ -120,6 +128,9 @@ const processGradientStops = ( .join(", "); }; +/** + * Determine the appropriate gradient function based on fill type + */ export const htmlGradientFromFills = (fill: Paint): string => { if (!fill) return ""; switch (fill.type) { @@ -128,7 +139,7 @@ export const htmlGradientFromFills = (fill: Paint): string => { case "GRADIENT_ANGULAR": return htmlAngularGradient(fill); case "GRADIENT_RADIAL": - return htmlRadialGradient(fill); // Updated to use radial gradient function + return htmlRadialGradient(fill); case "GRADIENT_DIAMOND": return htmlDiamondGradient(fill); default: @@ -136,98 +147,70 @@ export const htmlGradientFromFills = (fill: Paint): string => { } }; -export const gradientAngle2 = (fill: GradientPaint): number => { - const x1 = fill.gradientTransform[0][2]; - const y1 = fill.gradientTransform[1][2]; - const x2 = fill.gradientTransform[0][0] + x1; - const y2 = fill.gradientTransform[1][0] + y1; - const dx = x2 - x1; - const dy = y1 - y2; - const radians = Math.atan2(dy, dx); - const unadjustedAngle = (radians * 180) / Math.PI; - const adjustedAngle = unadjustedAngle + 90; - return adjustedAngle; -}; - -export const cssGradientAngle = (angle: number): number => { - const cssAngle = angle; - return cssAngle < 0 ? cssAngle + 360 : cssAngle; -}; - -export const htmlLinearGradient = (fill: GradientPaint): string => { - const figmaAngle = gradientAngle2(fill); - const angle = cssGradientAngle(figmaAngle).toFixed(0); +/** + * Generate CSS linear gradient + */ +export const htmlLinearGradient = (fill: GradientPaint) => { + const [start, end] = fill.gradientHandlePositions; + const dx = end.x - start.x; + const dy = end.y - start.y; + let angle = Math.atan2(dy, dx) * (180 / Math.PI); // Angle in degrees + angle = (angle + 360) % 360; // Normalize to 0-360 + const cssAngle = (angle + 90) % 360; // Adjust for CSS convention const mappedFill = processGradientStops( fill.gradientStops, fill.opacity ?? 1, - 100, - "%", ); - return `linear-gradient(${angle}deg, ${mappedFill})`; + return `linear-gradient(${cssAngle.toFixed(0)}deg, ${mappedFill})`; }; -export const invertYCoordinate = (y: number): number => 1 - y; - -export const htmlAngularGradient = (fill: GradientPaint): string => { - const angle = gradientAngle2(fill).toFixed(0); - // Extract matrix components - const a = fill.gradientTransform[0][0]; - const b = fill.gradientTransform[0][1]; - const tx = fill.gradientTransform[0][2]; - const c = fill.gradientTransform[1][0]; - const d = fill.gradientTransform[1][1]; - const ty = fill.gradientTransform[1][2]; - // Compute center by transforming (0.5, 0.5) - const centerX = (a * 0.5 + b * 0.5 + tx) * 100; - const centerY = (c * 0.5 + d * 0.5 + ty) * 100; - const centerXPercent = centerX.toFixed(2); - const centerYPercent = centerY.toFixed(2); - const mappedFill = processGradientStops( +/** + * Generate CSS radial gradient + */ +export const htmlRadialGradient = (fill: GradientPaint) => { + const [center, h1, h2] = fill.gradientHandlePositions; + const cx = center.x * 100; // Center X as percentage + const cy = center.y * 100; // Center Y as percentage + // Calculate horizontal radius (distance from center to h1) + const rx = Math.sqrt((h1.x - center.x) ** 2 + (h1.y - center.y) ** 2) * 100; + // Calculate vertical radius (distance from center to h2) + const ry = Math.sqrt((h2.x - center.x) ** 2 + (h2.y - center.y) ** 2) * 100; + const mappedStops = processGradientStops( fill.gradientStops, fill.opacity ?? 1, - 360, - "deg", ); - return `conic-gradient(from ${angle}deg at ${centerXPercent}% ${centerYPercent}%, ${mappedFill})`; + return `radial-gradient(ellipse ${rx.toFixed(2)}% ${ry.toFixed(2)}% at ${cx.toFixed(2)}% ${cy.toFixed(2)}%, ${mappedStops})`; }; -export const htmlRadialGradient = (fill: GradientPaint): string => { - const [[a, b, tx], [c, d, ty]] = fill.gradientTransform; - - // Calculate inverse of the linear part of the gradientTransform matrix - const det = a * d - b * c; - if (Math.abs(det) < 1e-6) return ""; // Avoid division by zero - - const invDet = 1 / det; - const invA = d * invDet; - const invB = -b * invDet; - const invC = -c * invDet; - const invD = a * invDet; - - // Calculate center by solving inverse transform for (0.5, 0.5) - const cx = (invA * (0.5 - tx) + invB * (0.5 - ty)) * 100; - const cy = (invC * (0.5 - tx) + invD * (0.5 - ty)) * 100; - - // Calculate column vectors of inverse matrix - const col1Length = Math.sqrt(invA ** 2 + invC ** 2) * 100; - const col2Length = Math.sqrt(invB ** 2 + invD ** 2) * 100; - - // Get radii as half lengths of column vectors (sorted) - const radii = [col1Length / 2, col2Length / 2].sort((a, b) => b - a); - - const mappedStops = processGradientStops( +/** + * Generate CSS conic (angular) gradient + */ +export const htmlAngularGradient = (fill: GradientPaint) => { + const [center, _, startDirection] = fill.gradientHandlePositions; + const cx = center.x * 100; // Center X as percentage + const cy = center.y * 100; // Center Y as percentage + // Calculate the starting angle + const dx = startDirection.x - center.x; + const dy = startDirection.y - center.y; + let angle = Math.atan2(dy, dx) * (180 / Math.PI); // Convert to degrees + angle = (angle + 360) % 360; // Normalize to 0-360 degrees + const mappedFill = processGradientStops( fill.gradientStops, fill.opacity ?? 1, + 360, + "deg", ); - return `radial-gradient(ellipse ${radii[0].toFixed(2)}% ${radii[1].toFixed(2)}% at ${cx.toFixed(2)}% ${cy.toFixed(2)}%, ${mappedStops})`; + return `conic-gradient(from ${angle.toFixed(0)}deg at ${cx.toFixed(2)}% ${cy.toFixed(2)}%, ${mappedFill})`; }; -// Added function for diamond gradient -export const htmlDiamondGradient = (fill: GradientPaint): string => { +/** + * Generate CSS diamond gradient (approximation using four linear gradients) + */ +export const htmlDiamondGradient = (fill: GradientPaint) => { const stops = processGradientStops( fill.gradientStops, fill.opacity ?? 1, - 50, // Adjusted multiplier for diamond gradient + 50, "%", ); const gradientConfigs = [ @@ -244,19 +227,21 @@ export const htmlDiamondGradient = (fill: GradientPaint): string => { .join(", "); }; +/** + * Build CSS background value from an array of paints + */ export const buildBackgroundValues = ( paintArray: ReadonlyArray | PluginAPI["mixed"], - settings: HTMLSettings, ): string => { if (paintArray === figma.mixed) { return ""; } - // If only one fill, just use plain color/gradient + // If only one fill, use plain color or gradient if (paintArray.length === 1) { const paint = paintArray[0]; if (paint.type === "SOLID") { - return htmlColorFromFills(paintArray, settings); + return htmlColorFromFills(paintArray); } else if ( paint.type === "GRADIENT_LINEAR" || paint.type === "GRADIENT_RADIAL" || @@ -268,16 +253,14 @@ export const buildBackgroundValues = ( return ""; } - // Reverse the array to match CSS layering (first is top-most in CSS) + // For multiple fills, reverse to match CSS layering (first is top-most) const styles = [...paintArray].reverse().map((paint, index) => { if (paint.type === "SOLID") { - // For multiple fills, always convert solid colors to linear gradients - // to ensure proper layering in CSS backgrounds - const color = htmlColorFromFills([paint], settings); + // Convert solid colors to gradients for proper layering + const color = htmlColorFromFills([paint]); if (index === 0) { return `linear-gradient(0deg, ${color} 0%, ${color} 100%)`; } - return color; } else if ( paint.type === "GRADIENT_LINEAR" || diff --git a/packages/backend/src/html/htmlDefaultBuilder.ts b/packages/backend/src/html/htmlDefaultBuilder.ts index 12ceb5b4..a9d68f6d 100644 --- a/packages/backend/src/html/htmlDefaultBuilder.ts +++ b/packages/backend/src/html/htmlDefaultBuilder.ts @@ -122,6 +122,7 @@ export class HtmlDefaultBuilder { commonShapeStyles(): this { if ("fills" in this.node) { + console.log("node is", this.node); this.applyFillsToStyle( this.node.fills, this.node.type === "TEXT" ? "text" : "background", @@ -158,7 +159,7 @@ export class HtmlDefaultBuilder { } const strokes = ("strokes" in node && node.strokes) || undefined; - const color = htmlColorFromFills(strokes, settings); + const color = htmlColorFromFills(strokes as any); if (!color) { return this; } @@ -166,7 +167,6 @@ export class HtmlDefaultBuilder { "dashPattern" in node && node.dashPattern.length > 0 ? "dotted" : "solid"; const strokeAlign = "strokeAlign" in node ? node.strokeAlign : "INSIDE"; - const layoutMode = "layoutMode" in node ? node.layoutMode : "NONE"; // Function to create border value string const consolidateBorders = (border: number): string => @@ -287,13 +287,13 @@ export class HtmlDefaultBuilder { formatWithJSX( "text", this.isJSX, - htmlColorFromFills(paintArray, this.settings), + htmlColorFromFills(paintArray as any), ), ); return this; } - const backgroundValues = buildBackgroundValues(paintArray, this.settings); + const backgroundValues = buildBackgroundValues(paintArray as any); if (backgroundValues) { this.addStyles(formatWithJSX("background", this.isJSX, backgroundValues)); diff --git a/packages/backend/src/html/htmlMain.ts b/packages/backend/src/html/htmlMain.ts index 2972fc38..3c04e5df 100644 --- a/packages/backend/src/html/htmlMain.ts +++ b/packages/backend/src/html/htmlMain.ts @@ -396,6 +396,7 @@ const convertNode = (settings: HTMLSettings) => async (node: SceneNode) => { return htmlWrapSVG(altNode, settings); } } + console.log("[convert] node is", node); switch (node.type) { case "RECTANGLE": diff --git a/packages/backend/src/html/htmlTextBuilder.ts b/packages/backend/src/html/htmlTextBuilder.ts index d870d562..436577ec 100644 --- a/packages/backend/src/html/htmlTextBuilder.ts +++ b/packages/backend/src/html/htmlTextBuilder.ts @@ -52,7 +52,7 @@ export class HtmlTextBuilder extends HtmlDefaultBuilder { const styleAttributes = formatMultipleJSX( { - color: htmlColorFromFills(segment.fills, this.settings), + color: htmlColorFromFills(segment.fills as any), "font-size": segment.fontSize, "font-family": segment.fontName.family, "font-style": this.getFontStyle(segment.fontName.style), @@ -86,9 +86,10 @@ export class HtmlTextBuilder extends HtmlDefaultBuilder { ) { // Use the pre-assigned uniqueId from the segment if available, // or generate one if not (as a fallback) - const segmentName = (segment as any).uniqueId || + const segmentName = + (segment as any).uniqueId || `${((node as any).uniqueName || node.name || "text").replace(/[^a-zA-Z0-9_-]/g, "").toLowerCase()}_text_${(index + 1).toString().padStart(2, "0")}`; - + const className = generateUniqueClassName(segmentName); result.className = className; diff --git a/packages/backend/src/swiftui/builderImpl/swiftuiColor.ts b/packages/backend/src/swiftui/builderImpl/swiftuiColor.ts index 39b6c851..c871a5af 100644 --- a/packages/backend/src/swiftui/builderImpl/swiftuiColor.ts +++ b/packages/backend/src/swiftui/builderImpl/swiftuiColor.ts @@ -2,8 +2,6 @@ import { retrieveTopFill } from "../../common/retrieveFill"; import { gradientAngle } from "../../common/color"; import { nearestValue } from "../../tailwind/conversionTables"; import { numberToFixedString } from "../../common/numToAutoFixed"; -import { addWarning } from "../../common/commonConversionWarnings"; -import { getPlaceholderImage } from "../../common/images"; /** * Retrieve the SwiftUI color for a Paint object @@ -74,13 +72,21 @@ export const swiftuiSolidColorFromDirectFills = ( return ""; }; +/** + * Generate a SwiftUI gradient from a GradientPaint object + * @param fill The gradient fill object from Figma + * @returns SwiftUI gradient code as a string + */ export const swiftuiGradient = (fill: GradientPaint): string => { - const direction = gradientDirection(gradientAngle(fill)); + if (fill.type !== "GRADIENT_LINEAR") { + return ""; // Only handling linear gradients here for simplicity + } + + const angle = gradientAngle(fill); + const direction = gradientDirection(angle); const colors = fill.gradientStops - .map((d) => { - return swiftuiColor(d.color, d.color.a); - }) + .map((d) => swiftuiColor(d.color, d.color.a)) .join(", "); return `LinearGradient(gradient: Gradient(colors: [${colors}]), ${direction})`; diff --git a/packages/backend/src/tailwind/builderImpl/tailwindColor.ts b/packages/backend/src/tailwind/builderImpl/tailwindColor.ts index 31ececde..3c352256 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindColor.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindColor.ts @@ -12,6 +12,7 @@ import { htmlAngularGradient, htmlRadialGradient, } from "../../html/builderImpl/htmlColor"; +import { Paint } from "../../api_types"; /** * Get a tailwind color value object @@ -100,7 +101,7 @@ export const tailwindGradientStop = ( // retrieve the SOLID color for tailwind export const tailwindColorFromFills = ( - fills: ReadonlyArray | PluginAPI["mixed"], + fills: ReadonlyArray, kind: TailwindColorType, ): string => { // [when testing] fills can be undefined @@ -123,7 +124,7 @@ export const tailwindColorFromFills = ( }; export const tailwindGradientFromFills = ( - fills: ReadonlyArray | PluginAPI["mixed"], + fills: ReadonlyArray, ): string => { const fill = retrieveTopFill(fills); From 9192100ff4c2d8e1eca1d6aafb8cb4bf4d6d0186 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Tue, 11 Mar 2025 02:11:47 -0300 Subject: [PATCH 105/168] Fix https://github.com/bernaferrari/FigmaToCode/issues/196 --- packages/backend/src/code.ts | 7 +++++-- packages/backend/src/html/htmlDefaultBuilder.ts | 5 +---- packages/backend/src/tailwind/tailwindDefaultBuilder.ts | 5 +---- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index d14c9e09..67464094 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -13,7 +13,10 @@ import { convertToCode } from "./common/retrieveUI/convertToCode"; import { generateHTMLPreview } from "./html/htmlMain"; import { variableToColorName } from "./tailwind/conversionTables"; import { oldConvertNodesToAltNodes } from "./altNodes/oldAltConversion"; -import { convertNodesToAltNodes, convertNodeToAltNode } from "./altNodes/altConversion"; +import { + convertNodesToAltNodes, + convertNodeToAltNode, +} from "./altNodes/altConversion"; // Performance tracking counters let getNodeByIdAsyncTime = 0; @@ -348,7 +351,7 @@ const processNodePair = async ( } if ( - jsonNode.layoutMode !== "NONE" && + jsonNode.layoutMode === "NONE" || jsonNode.children.some( (d: any) => "layoutPositioning" in d && d.layoutPositioning === "ABSOLUTE", diff --git a/packages/backend/src/html/htmlDefaultBuilder.ts b/packages/backend/src/html/htmlDefaultBuilder.ts index a9d68f6d..77e4e8d9 100644 --- a/packages/backend/src/html/htmlDefaultBuilder.ts +++ b/packages/backend/src/html/htmlDefaultBuilder.ts @@ -267,10 +267,7 @@ export class HtmlDefaultBuilder { formatWithJSX("position", isJSX, "absolute"), ); } else { - if ( - node.type === "GROUP" || - ("layoutMode" in node && (node as any).isRelative) - ) { + if (node.type === "GROUP" || (node as any).isRelative) { this.addStyles(formatWithJSX("position", isJSX, "relative")); } } diff --git a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts index 3340e674..e2ab2064 100644 --- a/packages/backend/src/tailwind/tailwindDefaultBuilder.ts +++ b/packages/backend/src/tailwind/tailwindDefaultBuilder.ts @@ -138,10 +138,7 @@ export class TailwindDefaultBuilder { } this.addAttributes(`absolute`); - } else if ( - node.type === "GROUP" || - ("layoutMode" in node && (node as any)).isRelative - ) { + } else if (node.type === "GROUP" || (node as any).isRelative) { this.addAttributes("relative"); } return this; From b696c128ed11f71f6f110bd686e1193c00f053c0 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Tue, 11 Mar 2025 04:17:26 -0300 Subject: [PATCH 106/168] Improve rotation? --- packages/backend/src/code.ts | 3119 +---------------- packages/backend/src/common/commonPosition.ts | 77 + .../backend/src/html/builderImpl/htmlBlend.ts | 5 +- 3 files changed, 100 insertions(+), 3101 deletions(-) diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index 67464094..479923a3 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -17,6 +17,13 @@ import { convertNodesToAltNodes, convertNodeToAltNode, } from "./altNodes/altConversion"; +import { + HasGeometryTrait, + MinimalFillsTrait, + MinimalStrokesTrait, + Node, + Paint, +} from "./api_types"; // Performance tracking counters let getNodeByIdAsyncTime = 0; @@ -104,7 +111,7 @@ const processEffectVariables = async ( }; const getColorVariables = async ( - node: GeometryMixin, + node: HasGeometryTrait, settings: PluginSettings, ) => { // This tries to be as fast as it can, using Promise.all so it can parallelize calls. @@ -165,16 +172,16 @@ function adjustChildrenOrder(node: any) { * @param parentNode Optional parent node reference to set */ const processNodePair = async ( - jsonNode: any, + jsonNode: Node, figmaNode: SceneNode, settings: PluginSettings, - parentNode?: any, + parentNode?: Node, ) => { if (!jsonNode.id) return; // Set parent reference if parent is provided if (parentNode) { - jsonNode.parent = parentNode; + (jsonNode as any).parent = parentNode; } // Ensure node has a unique name with simple numbering @@ -221,7 +228,7 @@ const processNodePair = async ( // Add a uniqueId to each segment styledTextSegments = await Promise.all( styledTextSegments.map(async (segment, index) => { - const mutableSegment = Object.assign({}, segment); + const mutableSegment: any = Object.assign({}, segment); if (settings.useColorVariables && segment.fills) { mutableSegment.fills = await Promise.all( @@ -232,7 +239,7 @@ const processNodePair = async ( ) { addWarning("BlendMode is not supported in Text colors"); } - const fill = { ...d }; + const fill = { ...d } as Paint; await processColorVariables(fill); return fill; }), @@ -287,7 +294,7 @@ const processNodePair = async ( await getColorVariables(jsonNode, settings); // Some places check if paddingLeft exists. This makes sure they all exist, even if 0. - if (jsonNode.layoutMode) { + if ("layoutMode" in jsonNode && jsonNode.layoutMode) { if (jsonNode.paddingLeft === undefined) { jsonNode.paddingLeft = 0; } @@ -317,9 +324,11 @@ const processNodePair = async ( // If layout sizing is HUG but there are no children, set it to FIXED const hasChildren = + "children" in jsonNode && jsonNode.children && Array.isArray(jsonNode.children) && jsonNode.children.length > 0; + if (jsonNode.layoutSizingHorizontal === "HUG" && !hasChildren) { jsonNode.layoutSizingHorizontal = "FIXED"; } @@ -329,6 +338,7 @@ const processNodePair = async ( // Process children recursively if both have children if ( + "children" in jsonNode && jsonNode.children && Array.isArray(jsonNode.children) && "children" in figmaNode && @@ -380,7 +390,7 @@ const processNodePair = async ( export const nodesToJSON = async ( nodes: ReadonlyArray, settings: PluginSettings, -): Promise => { +): Promise => { // Reset name counters for each conversion nodeNameCounters.clear(); @@ -395,7 +405,7 @@ export const nodesToJSON = async ( })) as any ).document, ), - )) as SceneNode[]; + )) as Node[]; console.log("[debug] initial nodeJson", { ...nodeJson[0] }); @@ -453,3096 +463,7 @@ export const run = async (settings: PluginSettings) => { // Now we work directly with the JSON nodes const convertNodesStart = Date.now(); convertedSelection = await convertNodesToAltNodes(nodeJson, null); - const convertedSelection2 = [ - { - id: "I2099:38616;1739:34914", - name: "Modal", - type: "FRAME", - scrollBehavior: "SCROLLS", - boundVariables: { - minHeight: { - type: "VARIABLE_ALIAS", - id: "VariableID:ca5fdd543c7de4d7a5d043eb31c365871c484b3e/4411:779", - }, - maxHeight: { - type: "VARIABLE_ALIAS", - id: "VariableID:143b8d97896be058533bf7578cac052ff1473fe5/4411:780", - }, - size: { - x: { - type: "VARIABLE_ALIAS", - id: "VariableID:51164a6f21a5daac8ea5fa8551389d4d7864a05e/1129:893", - }, - y: { - type: "VARIABLE_ALIAS", - id: "VariableID:c7de0427328e0238030ad2c66fb3a86abcdf6421/2867:8", - }, - }, - rectangleCornerRadii: { - RECTANGLE_TOP_LEFT_CORNER_RADIUS: { - type: "VARIABLE_ALIAS", - id: "VariableID:f4761dc8bffff3a6dc119f76d86928372facdf02/3157:870", - }, - RECTANGLE_TOP_RIGHT_CORNER_RADIUS: { - type: "VARIABLE_ALIAS", - id: "VariableID:f4761dc8bffff3a6dc119f76d86928372facdf02/3157:870", - }, - RECTANGLE_BOTTOM_LEFT_CORNER_RADIUS: { - type: "VARIABLE_ALIAS", - id: "VariableID:f4761dc8bffff3a6dc119f76d86928372facdf02/3157:870", - }, - RECTANGLE_BOTTOM_RIGHT_CORNER_RADIUS: { - type: "VARIABLE_ALIAS", - id: "VariableID:f4761dc8bffff3a6dc119f76d86928372facdf02/3157:870", - }, - }, - fills: [ - { - type: "VARIABLE_ALIAS", - id: "VariableID:8cbcd0032a7cac3b9799f16f6f48c35cab554a40/2243:10", - }, - ], - effects: [ - { - type: "VARIABLE_ALIAS", - id: "VariableID:55268df3aca26e8ed4182c6831670c631ab2e88b/4411:298", - }, - { - type: "VARIABLE_ALIAS", - id: "VariableID:7b576d4f7cef936e728857b8d6f7952e2a6dd6fe/3157:78", - }, - { - type: "VARIABLE_ALIAS", - id: "VariableID:e7dccd708e8eb5f689ef26147d2d985b7af1bfd8/2013:336", - }, - ], - }, - children: [ - { - id: "I2099:38616;1739:61360", - name: "Content", - type: "FRAME", - scrollBehavior: "SCROLLS", - children: [ - { - id: "I2099:38616;1739:34917", - name: "Modal Container", - type: "INSTANCE", - scrollBehavior: "SCROLLS", - componentPropertyReferences: { - mainComponent: "Swap Modal Container#893:1", - }, - boundVariables: { - itemSpacing: { - type: "VARIABLE_ALIAS", - id: "VariableID:b1aa965163834bcfd01131ac315d7d493f241ba6/10434:244", - }, - paddingLeft: { - type: "VARIABLE_ALIAS", - id: "VariableID:c44b7d196360345cd2e77fddb9fdbb56b074630c/10434:249", - }, - paddingTop: { - type: "VARIABLE_ALIAS", - id: "VariableID:becb73e51eeba1f8c786ade8394c2a4d29eb3ecf/10434:245", - }, - paddingRight: { - type: "VARIABLE_ALIAS", - id: "VariableID:dca108e1e0b09a774a4527df680b8306bc9208b2/10434:246", - }, - paddingBottom: { - type: "VARIABLE_ALIAS", - id: "VariableID:f6e871ff408efebce7861cdde12d1530510445e9/10434:248", - }, - minHeight: { - type: "VARIABLE_ALIAS", - id: "VariableID:d4bef8dbc813180acafb9ee86fec3ab4e9cb2983/3203:9", - }, - maxHeight: { - type: "VARIABLE_ALIAS", - id: "VariableID:5c0fdb551d4699d20f1fa1328995f4fb9d0ad8d5/3203:12", - }, - }, - componentId: "2099:38674", - isExposedInstance: true, - componentProperties: { - Type: { - value: "Fixed", - type: "VARIANT", - boundVariables: {}, - }, - Allignment: { - value: "Default (L-R)", - type: "VARIANT", - boundVariables: {}, - }, - }, - overrides: [ - { - id: "I2099:38616;1739:34917;2326:19664", - overriddenFields: [ - "componentProperties", - "primaryAxisSizingMode", - "layoutGrow", - "counterAxisSizingMode", - ], - }, - { - id: "I2099:38616;1739:34917;2326:19688", - overriddenFields: ["visible"], - }, - { - id: "I2099:38616;1739:34917;2326:19673;979:10619", - overriddenFields: ["sharedPluginData", "pluginData"], - }, - { - id: "I2099:38616;1739:34917;2326:19661", - overriddenFields: [ - "componentProperties", - "counterAxisSizingMode", - "primaryAxisSizingMode", - ], - }, - { - id: "I2099:38616;1739:34917;2326:19706", - overriddenFields: ["visible"], - }, - { - id: "I2099:38616;1739:34917;2326:19664;1202:3054", - overriddenFields: [ - "layoutAlign", - "sharedPluginData", - "pluginData", - "componentProperties", - "counterAxisSizingMode", - "layoutGrow", - "paddingRight", - "paddingLeft", - "primaryAxisSizingMode", - ], - }, - { - id: "I2099:38616;1739:34917;2326:19697", - overriddenFields: ["visible"], - }, - { - id: "I2099:38616;1739:34917;2326:19673", - overriddenFields: ["visible"], - }, - { - id: "I2099:38616;1739:34917;2326:19664;1202:3050;1856:4", - overriddenFields: ["sharedPluginData", "pluginData"], - }, - { - id: "I2099:38616;1739:34917;2326:19691", - overriddenFields: ["visible"], - }, - { - id: "I2099:38616;1739:34917;2326:19703;979:10619", - overriddenFields: ["sharedPluginData", "pluginData"], - }, - { - id: "I2099:38616;1739:34917;2326:19667", - overriddenFields: ["visible"], - }, - { - id: "I2099:38616;1739:34917;2326:19691;979:10619", - overriddenFields: ["sharedPluginData", "pluginData"], - }, - { - id: "I2099:38616;1739:34917", - overriddenFields: [ - "topLeftRadius", - "cornerRadius", - "topRightRadius", - "name", - "bottomLeftRadius", - "bottomRightRadius", - "primaryAxisSizingMode", - "counterAxisSizingMode", - "layoutGrow", - "boundVariables", - ], - }, - { - id: "I2099:38616;1739:34917;2326:19670", - overriddenFields: ["visible"], - }, - { - id: "I2099:38616;1739:34917;2326:19700", - overriddenFields: ["visible"], - }, - { - id: "I2099:38616;1739:34917;2326:19703", - overriddenFields: ["visible"], - }, - { - id: "I2099:38616;1739:34917;2326:19664;1202:3055", - overriddenFields: ["sharedPluginData", "pluginData"], - }, - { - id: "I2099:38616;1739:34917;2326:19661;117:435;1658:6969", - overriddenFields: ["sharedPluginData", "pluginData"], - }, - { - id: "I2099:38616;1739:34917;2326:19670;979:10619", - overriddenFields: ["sharedPluginData", "pluginData"], - }, - { - id: "I2099:38616;1739:34917;2326:19697;979:10619", - overriddenFields: ["sharedPluginData", "pluginData"], - }, - { - id: "I2099:38616;1739:34917;2326:19688;979:10619", - overriddenFields: ["sharedPluginData", "pluginData"], - }, - { - id: "I2099:38616;1739:34917;2326:19706;979:10619", - overriddenFields: ["sharedPluginData", "pluginData"], - }, - { - id: "I2099:38616;1739:34917;2326:19664;1202:3056", - overriddenFields: ["sharedPluginData", "pluginData"], - }, - { - id: "I2099:38616;1739:34917;2326:19667;979:10619", - overriddenFields: ["sharedPluginData", "pluginData"], - }, - { - id: "I2099:38616;1739:34917;1732:20311", - overriddenFields: ["sharedPluginData", "pluginData"], - }, - { - id: "I2099:38616;1739:34917;2326:19694", - overriddenFields: ["visible"], - }, - { - id: "I2099:38616;1739:34917;2326:19664;1202:3053", - overriddenFields: ["sharedPluginData", "pluginData"], - }, - { - id: "I2099:38616;1739:34917;2326:19694;979:10619", - overriddenFields: ["sharedPluginData", "pluginData"], - }, - { - id: "I2099:38616;1739:34917;2326:19664;1202:3052", - overriddenFields: ["sharedPluginData", "pluginData"], - }, - { - id: "I2099:38616;1739:34917;2326:19700;979:10619", - overriddenFields: ["sharedPluginData", "pluginData"], - }, - ], - children: [ - { - id: "I2099:38616;1739:34917;1732:20311", - name: "Grid", - type: "FRAME", - scrollBehavior: "SCROLLS", - boundVariables: { - itemSpacing: { - type: "VARIABLE_ALIAS", - id: "VariableID:b1aa965163834bcfd01131ac315d7d493f241ba6/10434:244", - }, - }, - children: [ - { - id: "I2099:38616;1739:34917;1732:20312", - name: "Row", - type: "FRAME", - scrollBehavior: "SCROLLS", - boundVariables: { - itemSpacing: { - type: "VARIABLE_ALIAS", - id: "VariableID:be243c44965c9affe677211ea0cd661d873653e1/10434:247", - }, - counterAxisSpacing: { - type: "VARIABLE_ALIAS", - id: "VariableID:b1aa965163834bcfd01131ac315d7d493f241ba6/10434:244", - }, - }, - children: [ - { - id: "I2099:38616;1739:34917;2326:19661", - name: "TextImage Variation 2", - type: "INSTANCE", - scrollBehavior: "SCROLLS", - boundVariables: { - size: { - x: { - type: "VARIABLE_ALIAS", - id: "VariableID:7dde29c0269cd3c53a0f9f7c1e2cc8e86d43cf3b/3157:236", - }, - y: { - type: "VARIABLE_ALIAS", - id: "VariableID:7dde29c0269cd3c53a0f9f7c1e2cc8e86d43cf3b/3157:236", - }, - }, - }, - componentId: "2043:22065", - componentProperties: { - "↳Swap Icon/Image/Graphic#9837:448": { - value: "2092:64304", - type: "INSTANCE_SWAP", - preferredValues: [], - }, - "Custom Size": { - value: "False", - type: "VARIANT", - boundVariables: {}, - }, - "Predefined Size": { - value: "L Container", - type: "VARIANT", - boundVariables: {}, - }, - }, - overrides: [ - { - id: "I2099:38616;1739:34917;2326:19661", - overriddenFields: [ - "componentProperties", - "counterAxisSizingMode", - "primaryAxisSizingMode", - ], - }, - { - id: "I2099:38616;1739:34917;2326:19661;117:435;1658:6969", - overriddenFields: [ - "sharedPluginData", - "pluginData", - ], - }, - ], - children: [ - { - id: "I2099:38616;1739:34917;2326:19661;117:435", - name: "FaxOfHook", - type: "INSTANCE", - scrollBehavior: "SCROLLS", - componentPropertyReferences: { - mainComponent: - "↳Swap Icon/Image/Graphic#9837:448", - }, - componentId: "2092:64304", - overrides: [ - { - id: "I2099:38616;1739:34917;2326:19661;117:435;1658:6969", - overriddenFields: [ - "sharedPluginData", - "pluginData", - ], - }, - ], - children: [ - { - id: "I2099:38616;1739:34917;2326:19661;117:435;1658:6969", - name: "Vector", - type: "VECTOR", - scrollBehavior: "SCROLLS", - boundVariables: { - fills: [ - { - type: "VARIABLE_ALIAS", - id: "VariableID:e208fc12668fdb73b1d1a743ba7f06ef9dd690ba/2013:512", - }, - ], - }, - blendMode: "PASS_THROUGH", - fills: [ - { - blendMode: "NORMAL", - type: "SOLID", - color: { - r: 1, - g: 1, - b: 1, - a: 1, - }, - boundVariables: { - color: { - type: "VARIABLE_ALIAS", - id: "VariableID:e208fc12668fdb73b1d1a743ba7f06ef9dd690ba/2013:512", - }, - }, - }, - ], - fillOverrideTable: { - "1": null, - }, - strokes: [], - strokeWeight: 0.75, - strokeAlign: "CENTER", - strokeJoin: "ROUND", - strokeCap: "ROUND", - strokeMiterAngle: 11.478341102600098, - absoluteBoundingBox: { - x: -1897.455078125, - y: -2168.515380859375, - width: 127.45187377929688, - height: 125.89124298095703, - }, - absoluteRenderBounds: { - x: -1897.455078125, - y: -2168.515380859375, - width: 127.451904296875, - height: 125.8912353515625, - }, - constraints: { - vertical: "SCALE", - horizontal: "SCALE", - }, - effects: [], - interactions: [], - }, - ], - blendMode: "PASS_THROUGH", - clipsContent: true, - background: [], - fills: [], - strokes: [], - strokeWeight: 1, - strokeAlign: "INSIDE", - backgroundColor: { - r: 0, - g: 0, - b: 0, - a: 0, - }, - absoluteBoundingBox: { - x: -1907.5, - y: -2177.75, - width: 150, - height: 150, - }, - absoluteRenderBounds: { - x: -1907.5, - y: -2177.75, - width: 150, - height: 150, - }, - constraints: { - vertical: "TOP", - horizontal: "LEFT", - }, - layoutAlign: "STRETCH", - layoutGrow: 1, - layoutSizingHorizontal: "FILL", - layoutSizingVertical: "FILL", - effects: [], - interactions: [], - }, - ], - blendMode: "PASS_THROUGH", - clipsContent: false, - background: [], - fills: [], - strokes: [], - strokeWeight: 1, - strokeAlign: "INSIDE", - backgroundColor: { - r: 0, - g: 0, - b: 0, - a: 0, - }, - layoutMode: "HORIZONTAL", - itemSpacing: 10, - counterAxisAlignItems: "CENTER", - primaryAxisAlignItems: "CENTER", - layoutWrap: "NO_WRAP", - absoluteBoundingBox: { - x: -1907.5, - y: -2177.75, - width: 150, - height: 150, - }, - absoluteRenderBounds: { - x: -1907.5, - y: -2177.75, - width: 150, - height: 150, - }, - constraints: { - vertical: "TOP", - horizontal: "LEFT", - }, - layoutAlign: "INHERIT", - layoutGrow: 0, - layoutSizingHorizontal: "HUG", - layoutSizingVertical: "HUG", - effects: [], - interactions: [], - }, - { - id: "I2099:38616;1739:34917;2326:19664", - name: "TextImage Variation 1", - type: "INSTANCE", - scrollBehavior: "SCROLLS", - boundVariables: { - itemSpacing: { - type: "VARIABLE_ALIAS", - id: "VariableID:3f744b5e3c8b3619411d855f7fe34dd7351a9435/3157:1061", - }, - minWidth: { - type: "VARIABLE_ALIAS", - id: "VariableID:1b137df291c4095c5e80c61d2c69a449cba2ccd9/3157:87", - }, - }, - componentId: "2058:19231", - exposedInstances: [ - "I2099:38616;1739:34917;2326:19664;1202:3050", - "I2099:38616;1739:34917;2326:19664;1202:3052", - "I2099:38616;1739:34917;2326:19664;1202:3053", - "I2099:38616;1739:34917;2326:19664;1202:3054", - "I2099:38616;1739:34917;2326:19664;1202:3055", - "I2099:38616;1739:34917;2326:19664;1202:3056", - ], - componentProperties: { - "↳Show Title Big#1053:9": { - value: false, - type: "BOOLEAN", - }, - "↳Show Content Small#1053:13": { - value: false, - type: "BOOLEAN", - }, - "Show Icon#1053:11": { - value: false, - type: "BOOLEAN", - }, - "Show Text#3122:8": { - value: true, - type: "BOOLEAN", - }, - "↳Show Title Small#1053:12": { - value: false, - type: "BOOLEAN", - }, - "↳Show Content#1053:10": { - value: true, - type: "BOOLEAN", - }, - "↳Show Clarification#1053:7": { - value: false, - type: "BOOLEAN", - }, - "↳Swap Icon#1053:8": { - value: "2033:1462", - type: "INSTANCE_SWAP", - preferredValues: [], - }, - Alignment: { - value: "ImageLeft-Middle", - type: "VARIANT", - boundVariables: {}, - }, - }, - overrides: [ - { - id: "I2099:38616;1739:34917;2326:19664", - overriddenFields: [ - "componentProperties", - "primaryAxisSizingMode", - "layoutGrow", - "counterAxisSizingMode", - ], - }, - { - id: "I2099:38616;1739:34917;2326:19664;1202:3056", - overriddenFields: [ - "sharedPluginData", - "pluginData", - ], - }, - { - id: "I2099:38616;1739:34917;2326:19664;1202:3052", - overriddenFields: [ - "sharedPluginData", - "pluginData", - ], - }, - { - id: "I2099:38616;1739:34917;2326:19664;1202:3054", - overriddenFields: [ - "layoutAlign", - "sharedPluginData", - "pluginData", - "componentProperties", - "counterAxisSizingMode", - "layoutGrow", - "paddingRight", - "paddingLeft", - "primaryAxisSizingMode", - ], - }, - { - id: "I2099:38616;1739:34917;2326:19664;1202:3055", - overriddenFields: [ - "sharedPluginData", - "pluginData", - ], - }, - { - id: "I2099:38616;1739:34917;2326:19664;1202:3050;1856:4", - overriddenFields: [ - "sharedPluginData", - "pluginData", - ], - }, - { - id: "I2099:38616;1739:34917;2326:19664;1202:3053", - overriddenFields: [ - "sharedPluginData", - "pluginData", - ], - }, - ], - children: [ - { - id: "I2099:38616;1739:34917;2326:19664;1202:3050", - name: "Placeholder", - visible: false, - type: "INSTANCE", - scrollBehavior: "SCROLLS", - componentPropertyReferences: { - mainComponent: "↳Swap Icon#1053:8", - visible: "Show Icon#1053:11", - }, - boundVariables: { - size: { - x: { - type: "VARIABLE_ALIAS", - id: "VariableID:1b137df291c4095c5e80c61d2c69a449cba2ccd9/3157:87", - }, - y: { - type: "VARIABLE_ALIAS", - id: "VariableID:1b137df291c4095c5e80c61d2c69a449cba2ccd9/3157:87", - }, - }, - }, - explicitVariableModes: { - "VariableCollectionId:e2fa2f7f8d460f250b4a1269b312f661b543be85/443:278": - "146:9", - }, - componentId: "2033:1462", - isExposedInstance: true, - overrides: [ - { - id: "I2099:38616;1739:34917;2326:19664;1202:3050;1856:4", - overriddenFields: [ - "sharedPluginData", - "pluginData", - ], - }, - ], - children: [], - blendMode: "PASS_THROUGH", - clipsContent: false, - background: [], - fills: [], - strokes: [], - strokeWeight: 1, - strokeAlign: "INSIDE", - backgroundColor: { - r: 0, - g: 0, - b: 0, - a: 0, - }, - absoluteBoundingBox: { - x: -1720, - y: -2177.75, - width: 37.5, - height: 37.5, - }, - absoluteRenderBounds: null, - constraints: { - vertical: "TOP", - horizontal: "LEFT", - }, - layoutAlign: "INHERIT", - layoutGrow: 0, - layoutSizingHorizontal: "FIXED", - layoutSizingVertical: "FIXED", - effects: [], - interactions: [], - }, - { - id: "I2099:38616;1739:34917;2326:19664;1202:3051", - name: "Text", - type: "FRAME", - scrollBehavior: "SCROLLS", - componentPropertyReferences: { - visible: "Show Text#3122:8", - }, - boundVariables: { - itemSpacing: { - type: "VARIABLE_ALIAS", - id: "VariableID:61bc0ffe4bb813f939fb25252ec1ae428556cc4e/10419:0", - }, - }, - children: [ - { - id: "I2099:38616;1739:34917;2326:19664;1202:3052", - name: "TitleBig", - visible: false, - type: "INSTANCE", - scrollBehavior: "SCROLLS", - componentPropertyReferences: { - visible: "↳Show Title Big#1053:9", - }, - componentId: "2033:2259", - isExposedInstance: true, - componentProperties: { - "Title Label#1366:0": { - value: "Title big text", - type: "TEXT", - }, - }, - overrides: [ - { - id: "I2099:38616;1739:34917;2326:19664;1202:3052", - overriddenFields: [ - "sharedPluginData", - "pluginData", - ], - }, - ], - children: [], - blendMode: "PASS_THROUGH", - clipsContent: false, - background: [], - fills: [], - strokes: [], - strokeWeight: 1, - strokeAlign: "INSIDE", - backgroundColor: { - r: 0, - g: 0, - b: 0, - a: 0, - }, - layoutMode: "HORIZONTAL", - primaryAxisSizingMode: "FIXED", - counterAxisAlignItems: "CENTER", - primaryAxisAlignItems: "CENTER", - layoutWrap: "NO_WRAP", - absoluteBoundingBox: { - x: -1720, - y: -2177.75, - width: 321.5, - height: 32, - }, - absoluteRenderBounds: null, - constraints: { - vertical: "TOP", - horizontal: "LEFT", - }, - layoutAlign: "STRETCH", - layoutGrow: 0, - layoutSizingHorizontal: "FIXED", - layoutSizingVertical: "HUG", - effects: [], - interactions: [], - }, - { - id: "I2099:38616;1739:34917;2326:19664;1202:3053", - name: "TitleSmall", - visible: false, - type: "INSTANCE", - scrollBehavior: "SCROLLS", - componentPropertyReferences: { - visible: "↳Show Title Small#1053:12", - }, - boundVariables: { - itemSpacing: { - type: "VARIABLE_ALIAS", - id: "VariableID:61bc0ffe4bb813f939fb25252ec1ae428556cc4e/10419:0", - }, - }, - componentId: "2058:19206", - isExposedInstance: true, - componentProperties: { - "Title small Label#1366:1": { - value: "Title small text", - type: "TEXT", - }, - }, - overrides: [ - { - id: "I2099:38616;1739:34917;2326:19664;1202:3053", - overriddenFields: [ - "sharedPluginData", - "pluginData", - ], - }, - ], - children: [], - blendMode: "PASS_THROUGH", - clipsContent: false, - background: [], - fills: [], - strokes: [], - strokeWeight: 1, - strokeAlign: "INSIDE", - backgroundColor: { - r: 0, - g: 0, - b: 0, - a: 0, - }, - layoutMode: "VERTICAL", - counterAxisSizingMode: "FIXED", - itemSpacing: 18, - counterAxisAlignItems: "CENTER", - primaryAxisAlignItems: "CENTER", - layoutWrap: "NO_WRAP", - absoluteBoundingBox: { - x: -1720, - y: -2177.75, - width: 321.5, - height: 26, - }, - absoluteRenderBounds: null, - constraints: { - vertical: "TOP", - horizontal: "LEFT", - }, - layoutAlign: "STRETCH", - layoutGrow: 0, - layoutSizingHorizontal: "FIXED", - layoutSizingVertical: "HUG", - effects: [], - interactions: [], - }, - { - id: "I2099:38616;1739:34917;2326:19664;1202:3054", - name: "Content", - type: "INSTANCE", - scrollBehavior: "SCROLLS", - componentPropertyReferences: { - visible: "↳Show Content#1053:10", - }, - boundVariables: { - itemSpacing: { - type: "VARIABLE_ALIAS", - id: "VariableID:61bc0ffe4bb813f939fb25252ec1ae428556cc4e/10419:0", - }, - }, - componentId: "2058:19212", - isExposedInstance: true, - componentProperties: { - "Content Label#1366:2": { - value: - "You can send a fax to the dialed number or manually receive a fax by selecting one of these options.", - type: "TEXT", - }, - }, - overrides: [ - { - id: "I2099:38616;1739:34917;2326:19664;1202:3054", - overriddenFields: [ - "layoutAlign", - "sharedPluginData", - "pluginData", - "componentProperties", - "counterAxisSizingMode", - "layoutGrow", - "paddingRight", - "paddingLeft", - "primaryAxisSizingMode", - ], - }, - ], - children: [ - { - id: "I2099:38616;1739:34917;2326:19664;1202:3054;1053:1206", - name: "Content", - type: "TEXT", - scrollBehavior: "SCROLLS", - componentPropertyReferences: { - characters: "Content Label#1366:2", - }, - boundVariables: { - fills: [ - { - type: "VARIABLE_ALIAS", - id: "VariableID:e208fc12668fdb73b1d1a743ba7f06ef9dd690ba/2013:512", - }, - ], - lineHeight: [ - { - type: "VARIABLE_ALIAS", - id: "VariableID:7458686840ba6bbe67d34048518a37290df777ed/4411:711", - }, - ], - fontFamily: [ - { - type: "VARIABLE_ALIAS", - id: "VariableID:f698a4f032592c139aea25637f9791852d11c35b/1203:148", - }, - ], - fontSize: [ - { - type: "VARIABLE_ALIAS", - id: "VariableID:5926de5ea9f1df6a583324845256eec3e10058d8/3157:790", - }, - ], - }, - blendMode: "PASS_THROUGH", - fills: [ - { - blendMode: "NORMAL", - type: "SOLID", - color: { - r: 1, - g: 1, - b: 1, - a: 1, - }, - boundVariables: { - color: { - type: "VARIABLE_ALIAS", - id: "VariableID:e208fc12668fdb73b1d1a743ba7f06ef9dd690ba/2013:512", - }, - }, - }, - ], - strokes: [], - strokeWeight: 1, - strokeAlign: "OUTSIDE", - absoluteBoundingBox: { - x: -1710, - y: -2177.75, - width: 707.5, - height: 62, - }, - absoluteRenderBounds: { - x: -1709.5155029296875, - y: -2171.339599609375, - width: 681.0682373046875, - height: 53.128662109375, - }, - constraints: { - vertical: "TOP", - horizontal: "LEFT", - }, - layoutAlign: "STRETCH", - layoutGrow: 0, - layoutSizingHorizontal: "FILL", - layoutSizingVertical: "HUG", - characters: - "You can send a fax to the dialed number or manually receive a fax by selecting one of these options.", - characterStyleOverrides: [], - styleOverrideTable: {}, - lineTypes: ["NONE"], - lineIndentations: [0], - style: { - fontFamily: "HP Simplified", - fontPostScriptName: - "HPSimplified-Regular", - fontStyle: "Regular", - fontWeight: 400, - textAutoResize: "HEIGHT", - fontSize: 25.5, - textAlignHorizontal: "LEFT", - textAlignVertical: "TOP", - letterSpacing: 0, - lineHeightPx: 30.600000381469727, - lineHeightPercent: 103.53753662109375, - lineHeightPercentFontSize: 120, - lineHeightUnit: "PIXELS", - }, - layoutVersion: 4, - styles: { - text: "2022:10407", - }, - effects: [], - interactions: [], - }, - { - id: "I2099:38616;1739:34917;2326:19664;1202:3054;1202:1256", - name: "Content", - visible: false, - type: "TEXT", - scrollBehavior: "SCROLLS", - boundVariables: { - fills: [ - { - type: "VARIABLE_ALIAS", - id: "VariableID:e208fc12668fdb73b1d1a743ba7f06ef9dd690ba/2013:512", - }, - ], - lineHeight: [ - { - type: "VARIABLE_ALIAS", - id: "VariableID:7458686840ba6bbe67d34048518a37290df777ed/4411:711", - }, - ], - fontFamily: [ - { - type: "VARIABLE_ALIAS", - id: "VariableID:f698a4f032592c139aea25637f9791852d11c35b/1203:148", - }, - ], - fontSize: [ - { - type: "VARIABLE_ALIAS", - id: "VariableID:5926de5ea9f1df6a583324845256eec3e10058d8/3157:790", - }, - ], - }, - blendMode: "PASS_THROUGH", - fills: [ - { - blendMode: "NORMAL", - type: "SOLID", - color: { - r: 1, - g: 1, - b: 1, - a: 1, - }, - boundVariables: { - color: { - type: "VARIABLE_ALIAS", - id: "VariableID:e208fc12668fdb73b1d1a743ba7f06ef9dd690ba/2013:512", - }, - }, - }, - ], - strokes: [], - strokeWeight: 1, - strokeAlign: "OUTSIDE", - absoluteBoundingBox: { - x: -1720, - y: -2132.75, - width: 216, - height: 31, - }, - absoluteRenderBounds: null, - constraints: { - vertical: "TOP", - horizontal: "LEFT", - }, - layoutAlign: "STRETCH", - layoutGrow: 0, - layoutSizingHorizontal: "FIXED", - layoutSizingVertical: "FIXED", - characters: "Content text", - characterStyleOverrides: [], - styleOverrideTable: {}, - lineTypes: ["NONE"], - lineIndentations: [0], - style: { - fontFamily: "HP Simplified", - fontPostScriptName: - "HPSimplified-Regular", - fontStyle: "Regular", - fontWeight: 400, - textAutoResize: "HEIGHT", - fontSize: 25.5, - textAlignHorizontal: "LEFT", - textAlignVertical: "TOP", - letterSpacing: 0, - lineHeightPx: 30.600000381469727, - lineHeightPercent: 103.53753662109375, - lineHeightPercentFontSize: 120, - lineHeightUnit: "PIXELS", - }, - layoutVersion: 4, - styles: { - text: "2022:10407", - }, - effects: [], - interactions: [], - }, - { - id: "I2099:38616;1739:34917;2326:19664;1202:3054;1202:1267", - name: "Content", - visible: false, - type: "TEXT", - scrollBehavior: "SCROLLS", - boundVariables: { - fills: [ - { - type: "VARIABLE_ALIAS", - id: "VariableID:e208fc12668fdb73b1d1a743ba7f06ef9dd690ba/2013:512", - }, - ], - lineHeight: [ - { - type: "VARIABLE_ALIAS", - id: "VariableID:7458686840ba6bbe67d34048518a37290df777ed/4411:711", - }, - ], - fontFamily: [ - { - type: "VARIABLE_ALIAS", - id: "VariableID:f698a4f032592c139aea25637f9791852d11c35b/1203:148", - }, - ], - fontSize: [ - { - type: "VARIABLE_ALIAS", - id: "VariableID:5926de5ea9f1df6a583324845256eec3e10058d8/3157:790", - }, - ], - }, - blendMode: "PASS_THROUGH", - fills: [ - { - blendMode: "NORMAL", - type: "SOLID", - color: { - r: 1, - g: 1, - b: 1, - a: 1, - }, - boundVariables: { - color: { - type: "VARIABLE_ALIAS", - id: "VariableID:e208fc12668fdb73b1d1a743ba7f06ef9dd690ba/2013:512", - }, - }, - }, - ], - strokes: [], - strokeWeight: 1, - strokeAlign: "OUTSIDE", - absoluteBoundingBox: { - x: -1720, - y: -2132.75, - width: 216, - height: 31, - }, - absoluteRenderBounds: null, - constraints: { - vertical: "TOP", - horizontal: "LEFT", - }, - layoutAlign: "STRETCH", - layoutGrow: 0, - layoutSizingHorizontal: "FIXED", - layoutSizingVertical: "FIXED", - characters: "Content text", - characterStyleOverrides: [], - styleOverrideTable: {}, - lineTypes: ["NONE"], - lineIndentations: [0], - style: { - fontFamily: "HP Simplified", - fontPostScriptName: - "HPSimplified-Regular", - fontStyle: "Regular", - fontWeight: 400, - textAutoResize: "HEIGHT", - fontSize: 25.5, - textAlignHorizontal: "LEFT", - textAlignVertical: "TOP", - letterSpacing: 0, - lineHeightPx: 30.600000381469727, - lineHeightPercent: 103.53753662109375, - lineHeightPercentFontSize: 120, - lineHeightUnit: "PIXELS", - }, - layoutVersion: 4, - styles: { - text: "2022:10407", - }, - effects: [], - interactions: [], - }, - { - id: "I2099:38616;1739:34917;2326:19664;1202:3054;1202:1283", - name: "Content", - visible: false, - type: "TEXT", - scrollBehavior: "SCROLLS", - boundVariables: { - fills: [ - { - type: "VARIABLE_ALIAS", - id: "VariableID:e208fc12668fdb73b1d1a743ba7f06ef9dd690ba/2013:512", - }, - ], - lineHeight: [ - { - type: "VARIABLE_ALIAS", - id: "VariableID:7458686840ba6bbe67d34048518a37290df777ed/4411:711", - }, - ], - fontFamily: [ - { - type: "VARIABLE_ALIAS", - id: "VariableID:f698a4f032592c139aea25637f9791852d11c35b/1203:148", - }, - ], - fontSize: [ - { - type: "VARIABLE_ALIAS", - id: "VariableID:5926de5ea9f1df6a583324845256eec3e10058d8/3157:790", - }, - ], - }, - blendMode: "PASS_THROUGH", - fills: [ - { - blendMode: "NORMAL", - type: "SOLID", - color: { - r: 1, - g: 1, - b: 1, - a: 1, - }, - boundVariables: { - color: { - type: "VARIABLE_ALIAS", - id: "VariableID:e208fc12668fdb73b1d1a743ba7f06ef9dd690ba/2013:512", - }, - }, - }, - ], - strokes: [], - strokeWeight: 1, - strokeAlign: "OUTSIDE", - absoluteBoundingBox: { - x: -1720, - y: -2132.75, - width: 216, - height: 31, - }, - absoluteRenderBounds: null, - constraints: { - vertical: "TOP", - horizontal: "LEFT", - }, - layoutAlign: "STRETCH", - layoutGrow: 0, - layoutSizingHorizontal: "FIXED", - layoutSizingVertical: "FIXED", - characters: "Content text", - characterStyleOverrides: [], - styleOverrideTable: {}, - lineTypes: ["NONE"], - lineIndentations: [0], - style: { - fontFamily: "HP Simplified", - fontPostScriptName: - "HPSimplified-Regular", - fontStyle: "Regular", - fontWeight: 400, - textAutoResize: "HEIGHT", - fontSize: 25.5, - textAlignHorizontal: "LEFT", - textAlignVertical: "TOP", - letterSpacing: 0, - lineHeightPx: 30.600000381469727, - lineHeightPercent: 103.53753662109375, - lineHeightPercentFontSize: 120, - lineHeightUnit: "PIXELS", - }, - layoutVersion: 4, - styles: { - text: "2022:10407", - }, - effects: [], - interactions: [], - }, - { - id: "I2099:38616;1739:34917;2326:19664;1202:3054;1202:1304", - name: "Content", - visible: false, - type: "TEXT", - scrollBehavior: "SCROLLS", - boundVariables: { - fills: [ - { - type: "VARIABLE_ALIAS", - id: "VariableID:e208fc12668fdb73b1d1a743ba7f06ef9dd690ba/2013:512", - }, - ], - lineHeight: [ - { - type: "VARIABLE_ALIAS", - id: "VariableID:7458686840ba6bbe67d34048518a37290df777ed/4411:711", - }, - ], - fontFamily: [ - { - type: "VARIABLE_ALIAS", - id: "VariableID:f698a4f032592c139aea25637f9791852d11c35b/1203:148", - }, - ], - fontSize: [ - { - type: "VARIABLE_ALIAS", - id: "VariableID:5926de5ea9f1df6a583324845256eec3e10058d8/3157:790", - }, - ], - }, - blendMode: "PASS_THROUGH", - fills: [ - { - blendMode: "NORMAL", - type: "SOLID", - color: { - r: 1, - g: 1, - b: 1, - a: 1, - }, - boundVariables: { - color: { - type: "VARIABLE_ALIAS", - id: "VariableID:e208fc12668fdb73b1d1a743ba7f06ef9dd690ba/2013:512", - }, - }, - }, - ], - strokes: [], - strokeWeight: 1, - strokeAlign: "OUTSIDE", - absoluteBoundingBox: { - x: -1720, - y: -2132.75, - width: 216, - height: 31, - }, - absoluteRenderBounds: null, - constraints: { - vertical: "TOP", - horizontal: "LEFT", - }, - layoutAlign: "STRETCH", - layoutGrow: 0, - layoutSizingHorizontal: "FIXED", - layoutSizingVertical: "FIXED", - characters: "Content text", - characterStyleOverrides: [], - styleOverrideTable: {}, - lineTypes: ["NONE"], - lineIndentations: [0], - style: { - fontFamily: "HP Simplified", - fontPostScriptName: - "HPSimplified-Regular", - fontStyle: "Regular", - fontWeight: 400, - textAutoResize: "HEIGHT", - fontSize: 25.5, - textAlignHorizontal: "LEFT", - textAlignVertical: "TOP", - letterSpacing: 0, - lineHeightPx: 30.600000381469727, - lineHeightPercent: 103.53753662109375, - lineHeightPercentFontSize: 120, - lineHeightUnit: "PIXELS", - }, - layoutVersion: 4, - styles: { - text: "2022:10407", - }, - effects: [], - interactions: [], - }, - ], - blendMode: "PASS_THROUGH", - clipsContent: false, - background: [], - fills: [], - strokes: [], - strokeWeight: 1, - strokeAlign: "INSIDE", - backgroundColor: { - r: 0, - g: 0, - b: 0, - a: 0, - }, - layoutMode: "VERTICAL", - counterAxisSizingMode: "FIXED", - itemSpacing: 18, - counterAxisAlignItems: "CENTER", - primaryAxisAlignItems: "CENTER", - paddingLeft: 10, - paddingRight: 10, - layoutWrap: "NO_WRAP", - absoluteBoundingBox: { - x: -1720, - y: -2177.75, - width: 727.5, - height: 62, - }, - absoluteRenderBounds: { - x: -1720, - y: -2177.75, - width: 727.5, - height: 62, - }, - constraints: { - vertical: "TOP", - horizontal: "LEFT", - }, - layoutAlign: "STRETCH", - layoutGrow: 0, - layoutSizingHorizontal: "FILL", - layoutSizingVertical: "HUG", - effects: [], - interactions: [], - }, - { - id: "I2099:38616;1739:34917;2326:19664;1202:3055", - name: "ContentSmall", - visible: false, - type: "INSTANCE", - scrollBehavior: "SCROLLS", - componentPropertyReferences: { - visible: "↳Show Content Small#1053:13", - }, - boundVariables: { - itemSpacing: { - type: "VARIABLE_ALIAS", - id: "VariableID:61bc0ffe4bb813f939fb25252ec1ae428556cc4e/10419:0", - }, - }, - componentId: "2058:19218", - isExposedInstance: true, - componentProperties: { - "Content Small Label#1366:3": { - value: "Content small text", - type: "TEXT", - }, - }, - overrides: [ - { - id: "I2099:38616;1739:34917;2326:19664;1202:3055", - overriddenFields: [ - "sharedPluginData", - "pluginData", - ], - }, - ], - children: [], - blendMode: "PASS_THROUGH", - clipsContent: false, - background: [], - fills: [], - strokes: [], - strokeWeight: 1, - strokeAlign: "INSIDE", - backgroundColor: { - r: 0, - g: 0, - b: 0, - a: 0, - }, - layoutMode: "VERTICAL", - counterAxisSizingMode: "FIXED", - itemSpacing: 18, - counterAxisAlignItems: "CENTER", - primaryAxisAlignItems: "CENTER", - layoutWrap: "NO_WRAP", - absoluteBoundingBox: { - x: -1720, - y: -2132.75, - width: 321.5, - height: 20, - }, - absoluteRenderBounds: null, - constraints: { - vertical: "TOP", - horizontal: "LEFT", - }, - layoutAlign: "STRETCH", - layoutGrow: 0, - layoutSizingHorizontal: "FIXED", - layoutSizingVertical: "HUG", - effects: [], - interactions: [], - }, - { - id: "I2099:38616;1739:34917;2326:19664;1202:3056", - name: "Clarification", - visible: false, - type: "INSTANCE", - scrollBehavior: "SCROLLS", - componentPropertyReferences: { - visible: "↳Show Clarification#1053:7", - }, - boundVariables: { - itemSpacing: { - type: "VARIABLE_ALIAS", - id: "VariableID:61bc0ffe4bb813f939fb25252ec1ae428556cc4e/10419:0", - }, - }, - componentId: "2058:19224", - isExposedInstance: true, - componentProperties: { - "Clarification Label#1366:4": { - value: "Clarification text", - type: "TEXT", - }, - }, - overrides: [ - { - id: "I2099:38616;1739:34917;2326:19664;1202:3056", - overriddenFields: [ - "sharedPluginData", - "pluginData", - ], - }, - ], - children: [], - blendMode: "PASS_THROUGH", - clipsContent: false, - background: [], - fills: [], - strokes: [], - strokeWeight: 1, - strokeAlign: "INSIDE", - backgroundColor: { - r: 0, - g: 0, - b: 0, - a: 0, - }, - layoutMode: "VERTICAL", - counterAxisSizingMode: "FIXED", - itemSpacing: 18, - counterAxisAlignItems: "CENTER", - primaryAxisAlignItems: "CENTER", - layoutWrap: "NO_WRAP", - absoluteBoundingBox: { - x: -1720, - y: -2132.75, - width: 321.5, - height: 20, - }, - absoluteRenderBounds: null, - constraints: { - vertical: "TOP", - horizontal: "LEFT", - }, - layoutAlign: "STRETCH", - layoutGrow: 0, - layoutSizingHorizontal: "FIXED", - layoutSizingVertical: "HUG", - effects: [], - interactions: [], - }, - ], - blendMode: "PASS_THROUGH", - clipsContent: false, - background: [], - fills: [], - strokes: [], - strokeWeight: 1, - strokeAlign: "INSIDE", - backgroundColor: { - r: 0, - g: 0, - b: 0, - a: 0, - }, - layoutMode: "VERTICAL", - counterAxisSizingMode: "FIXED", - itemSpacing: 18, - primaryAxisAlignItems: "CENTER", - layoutWrap: "NO_WRAP", - absoluteBoundingBox: { - x: -1720, - y: -2177.75, - width: 727.5, - height: 62, - }, - absoluteRenderBounds: { - x: -1720, - y: -2177.75, - width: 727.5, - height: 62, - }, - constraints: { - vertical: "TOP", - horizontal: "LEFT", - }, - layoutAlign: "INHERIT", - layoutGrow: 1, - layoutSizingHorizontal: "FILL", - layoutSizingVertical: "HUG", - effects: [], - interactions: [], - }, - ], - blendMode: "PASS_THROUGH", - clipsContent: false, - background: [], - fills: [], - strokes: [], - strokeWeight: 1, - strokeAlign: "INSIDE", - backgroundColor: { - r: 0, - g: 0, - b: 0, - a: 0, - }, - layoutMode: "HORIZONTAL", - itemSpacing: 15, - primaryAxisSizingMode: "FIXED", - counterAxisAlignItems: "CENTER", - layoutWrap: "NO_WRAP", - absoluteBoundingBox: { - x: -1720, - y: -2177.75, - width: 727.5, - height: 62, - }, - absoluteRenderBounds: { - x: -1720, - y: -2177.75, - width: 727.5, - height: 62, - }, - constraints: { - vertical: "TOP", - horizontal: "LEFT", - }, - layoutAlign: "INHERIT", - layoutGrow: 1, - minWidth: 37.5, - layoutSizingHorizontal: "FILL", - layoutSizingVertical: "HUG", - effects: [], - interactions: [], - }, - { - id: "I2099:38616;1739:34917;2326:19667", - name: "Slot", - visible: false, - type: "INSTANCE", - scrollBehavior: "SCROLLS", - componentId: "2060:58189", - overrides: [ - { - id: "I2099:38616;1739:34917;2326:19667", - overriddenFields: ["visible"], - }, - { - id: "I2099:38616;1739:34917;2326:19667;979:10619", - overriddenFields: [ - "sharedPluginData", - "pluginData", - ], - }, - ], - children: [], - blendMode: "PASS_THROUGH", - clipsContent: true, - background: [], - fills: [], - strokes: [], - cornerRadius: 4, - cornerSmoothing: 0, - strokeWeight: 2, - strokeAlign: "INSIDE", - backgroundColor: { - r: 0, - g: 0, - b: 0, - a: 0, - }, - overflowDirection: "VERTICAL_SCROLLING", - layoutMode: "HORIZONTAL", - counterAxisAlignItems: "CENTER", - primaryAxisAlignItems: "CENTER", - layoutWrap: "NO_WRAP", - strokeDashes: [8, 4], - absoluteBoundingBox: { - x: -1678.5, - y: -2177.75, - width: 98, - height: 39, - }, - absoluteRenderBounds: null, - constraints: { - vertical: "TOP", - horizontal: "LEFT", - }, - layoutAlign: "INHERIT", - layoutGrow: 0, - layoutSizingHorizontal: "HUG", - layoutSizingVertical: "HUG", - effects: [], - interactions: [], - }, - { - id: "I2099:38616;1739:34917;2326:19670", - name: "Slot", - visible: false, - type: "INSTANCE", - scrollBehavior: "SCROLLS", - componentId: "2060:58189", - overrides: [ - { - id: "I2099:38616;1739:34917;2326:19670", - overriddenFields: ["visible"], - }, - { - id: "I2099:38616;1739:34917;2326:19670;979:10619", - overriddenFields: [ - "sharedPluginData", - "pluginData", - ], - }, - ], - children: [], - blendMode: "PASS_THROUGH", - clipsContent: true, - background: [], - fills: [], - strokes: [], - cornerRadius: 4, - cornerSmoothing: 0, - strokeWeight: 2, - strokeAlign: "INSIDE", - backgroundColor: { - r: 0, - g: 0, - b: 0, - a: 0, - }, - overflowDirection: "VERTICAL_SCROLLING", - layoutMode: "HORIZONTAL", - counterAxisAlignItems: "CENTER", - primaryAxisAlignItems: "CENTER", - layoutWrap: "NO_WRAP", - strokeDashes: [8, 4], - absoluteBoundingBox: { - x: -1564, - y: -2177.75, - width: 98, - height: 39, - }, - absoluteRenderBounds: null, - constraints: { - vertical: "TOP", - horizontal: "LEFT", - }, - layoutAlign: "INHERIT", - layoutGrow: 0, - layoutSizingHorizontal: "HUG", - layoutSizingVertical: "HUG", - effects: [], - interactions: [], - }, - { - id: "I2099:38616;1739:34917;2326:19688", - name: "Slot", - visible: false, - type: "INSTANCE", - scrollBehavior: "SCROLLS", - componentId: "2060:58189", - overrides: [ - { - id: "I2099:38616;1739:34917;2326:19688", - overriddenFields: ["visible"], - }, - { - id: "I2099:38616;1739:34917;2326:19688;979:10619", - overriddenFields: [ - "sharedPluginData", - "pluginData", - ], - }, - ], - children: [], - blendMode: "PASS_THROUGH", - clipsContent: true, - background: [], - fills: [], - strokes: [], - cornerRadius: 4, - cornerSmoothing: 0, - strokeWeight: 2, - strokeAlign: "INSIDE", - backgroundColor: { - r: 0, - g: 0, - b: 0, - a: 0, - }, - overflowDirection: "VERTICAL_SCROLLING", - layoutMode: "HORIZONTAL", - counterAxisAlignItems: "CENTER", - primaryAxisAlignItems: "CENTER", - layoutWrap: "NO_WRAP", - strokeDashes: [8, 4], - absoluteBoundingBox: { - x: -1907.5, - y: -2122.25, - width: 98, - height: 39, - }, - absoluteRenderBounds: null, - constraints: { - vertical: "TOP", - horizontal: "LEFT", - }, - layoutAlign: "INHERIT", - layoutGrow: 0, - layoutSizingHorizontal: "HUG", - layoutSizingVertical: "HUG", - effects: [], - interactions: [], - }, - { - id: "I2099:38616;1739:34917;2326:19691", - name: "Slot", - visible: false, - type: "INSTANCE", - scrollBehavior: "SCROLLS", - componentId: "2060:58189", - overrides: [ - { - id: "I2099:38616;1739:34917;2326:19691", - overriddenFields: ["visible"], - }, - { - id: "I2099:38616;1739:34917;2326:19691;979:10619", - overriddenFields: [ - "sharedPluginData", - "pluginData", - ], - }, - ], - children: [], - blendMode: "PASS_THROUGH", - clipsContent: true, - background: [], - fills: [], - strokes: [], - cornerRadius: 4, - cornerSmoothing: 0, - strokeWeight: 2, - strokeAlign: "INSIDE", - backgroundColor: { - r: 0, - g: 0, - b: 0, - a: 0, - }, - overflowDirection: "VERTICAL_SCROLLING", - layoutMode: "HORIZONTAL", - counterAxisAlignItems: "CENTER", - primaryAxisAlignItems: "CENTER", - layoutWrap: "NO_WRAP", - strokeDashes: [8, 4], - absoluteBoundingBox: { - x: -1793, - y: -2122.25, - width: 98, - height: 39, - }, - absoluteRenderBounds: null, - constraints: { - vertical: "TOP", - horizontal: "LEFT", - }, - layoutAlign: "INHERIT", - layoutGrow: 0, - layoutSizingHorizontal: "HUG", - layoutSizingVertical: "HUG", - effects: [], - interactions: [], - }, - { - id: "I2099:38616;1739:34917;2326:19694", - name: "Slot", - visible: false, - type: "INSTANCE", - scrollBehavior: "SCROLLS", - componentId: "2060:58189", - overrides: [ - { - id: "I2099:38616;1739:34917;2326:19694;979:10619", - overriddenFields: [ - "sharedPluginData", - "pluginData", - ], - }, - { - id: "I2099:38616;1739:34917;2326:19694", - overriddenFields: ["visible"], - }, - ], - children: [], - blendMode: "PASS_THROUGH", - clipsContent: true, - background: [], - fills: [], - strokes: [], - cornerRadius: 4, - cornerSmoothing: 0, - strokeWeight: 2, - strokeAlign: "INSIDE", - backgroundColor: { - r: 0, - g: 0, - b: 0, - a: 0, - }, - overflowDirection: "VERTICAL_SCROLLING", - layoutMode: "HORIZONTAL", - counterAxisAlignItems: "CENTER", - primaryAxisAlignItems: "CENTER", - layoutWrap: "NO_WRAP", - strokeDashes: [8, 4], - absoluteBoundingBox: { - x: -1678.5, - y: -2122.25, - width: 98, - height: 39, - }, - absoluteRenderBounds: null, - constraints: { - vertical: "TOP", - horizontal: "LEFT", - }, - layoutAlign: "INHERIT", - layoutGrow: 0, - layoutSizingHorizontal: "HUG", - layoutSizingVertical: "HUG", - effects: [], - interactions: [], - }, - { - id: "I2099:38616;1739:34917;2326:19673", - name: "Slot", - visible: false, - type: "INSTANCE", - scrollBehavior: "SCROLLS", - componentId: "2060:58189", - overrides: [ - { - id: "I2099:38616;1739:34917;2326:19673;979:10619", - overriddenFields: [ - "sharedPluginData", - "pluginData", - ], - }, - { - id: "I2099:38616;1739:34917;2326:19673", - overriddenFields: ["visible"], - }, - ], - children: [], - blendMode: "PASS_THROUGH", - clipsContent: true, - background: [], - fills: [], - strokes: [], - cornerRadius: 4, - cornerSmoothing: 0, - strokeWeight: 2, - strokeAlign: "INSIDE", - backgroundColor: { - r: 0, - g: 0, - b: 0, - a: 0, - }, - overflowDirection: "VERTICAL_SCROLLING", - layoutMode: "HORIZONTAL", - counterAxisAlignItems: "CENTER", - primaryAxisAlignItems: "CENTER", - layoutWrap: "NO_WRAP", - strokeDashes: [8, 4], - absoluteBoundingBox: { - x: -1564, - y: -2122.25, - width: 98, - height: 39, - }, - absoluteRenderBounds: null, - constraints: { - vertical: "TOP", - horizontal: "LEFT", - }, - layoutAlign: "INHERIT", - layoutGrow: 0, - layoutSizingHorizontal: "HUG", - layoutSizingVertical: "HUG", - effects: [], - interactions: [], - }, - { - id: "I2099:38616;1739:34917;2326:19697", - name: "Slot", - visible: false, - type: "INSTANCE", - scrollBehavior: "SCROLLS", - componentId: "2060:58189", - overrides: [ - { - id: "I2099:38616;1739:34917;2326:19697", - overriddenFields: ["visible"], - }, - { - id: "I2099:38616;1739:34917;2326:19697;979:10619", - overriddenFields: [ - "sharedPluginData", - "pluginData", - ], - }, - ], - children: [], - blendMode: "PASS_THROUGH", - clipsContent: true, - background: [], - fills: [], - strokes: [], - cornerRadius: 4, - cornerSmoothing: 0, - strokeWeight: 2, - strokeAlign: "INSIDE", - backgroundColor: { - r: 0, - g: 0, - b: 0, - a: 0, - }, - overflowDirection: "VERTICAL_SCROLLING", - layoutMode: "HORIZONTAL", - counterAxisAlignItems: "CENTER", - primaryAxisAlignItems: "CENTER", - layoutWrap: "NO_WRAP", - strokeDashes: [8, 4], - absoluteBoundingBox: { - x: -1907.5, - y: -2066.75, - width: 98, - height: 39, - }, - absoluteRenderBounds: null, - constraints: { - vertical: "TOP", - horizontal: "LEFT", - }, - layoutAlign: "INHERIT", - layoutGrow: 0, - layoutSizingHorizontal: "HUG", - layoutSizingVertical: "HUG", - effects: [], - interactions: [], - }, - { - id: "I2099:38616;1739:34917;2326:19700", - name: "Slot", - visible: false, - type: "INSTANCE", - scrollBehavior: "SCROLLS", - componentId: "2060:58189", - overrides: [ - { - id: "I2099:38616;1739:34917;2326:19700", - overriddenFields: ["visible"], - }, - { - id: "I2099:38616;1739:34917;2326:19700;979:10619", - overriddenFields: [ - "sharedPluginData", - "pluginData", - ], - }, - ], - children: [], - blendMode: "PASS_THROUGH", - clipsContent: true, - background: [], - fills: [], - strokes: [], - cornerRadius: 4, - cornerSmoothing: 0, - strokeWeight: 2, - strokeAlign: "INSIDE", - backgroundColor: { - r: 0, - g: 0, - b: 0, - a: 0, - }, - overflowDirection: "VERTICAL_SCROLLING", - layoutMode: "HORIZONTAL", - counterAxisAlignItems: "CENTER", - primaryAxisAlignItems: "CENTER", - layoutWrap: "NO_WRAP", - strokeDashes: [8, 4], - absoluteBoundingBox: { - x: -1793, - y: -2066.75, - width: 98, - height: 39, - }, - absoluteRenderBounds: null, - constraints: { - vertical: "TOP", - horizontal: "LEFT", - }, - layoutAlign: "INHERIT", - layoutGrow: 0, - layoutSizingHorizontal: "HUG", - layoutSizingVertical: "HUG", - effects: [], - interactions: [], - }, - { - id: "I2099:38616;1739:34917;2326:19703", - name: "Slot", - visible: false, - type: "INSTANCE", - scrollBehavior: "SCROLLS", - componentId: "2060:58189", - overrides: [ - { - id: "I2099:38616;1739:34917;2326:19703", - overriddenFields: ["visible"], - }, - { - id: "I2099:38616;1739:34917;2326:19703;979:10619", - overriddenFields: [ - "sharedPluginData", - "pluginData", - ], - }, - ], - children: [], - blendMode: "PASS_THROUGH", - clipsContent: true, - background: [], - fills: [], - strokes: [], - cornerRadius: 4, - cornerSmoothing: 0, - strokeWeight: 2, - strokeAlign: "INSIDE", - backgroundColor: { - r: 0, - g: 0, - b: 0, - a: 0, - }, - overflowDirection: "VERTICAL_SCROLLING", - layoutMode: "HORIZONTAL", - counterAxisAlignItems: "CENTER", - primaryAxisAlignItems: "CENTER", - layoutWrap: "NO_WRAP", - strokeDashes: [8, 4], - absoluteBoundingBox: { - x: -1678.5, - y: -2066.75, - width: 98, - height: 39, - }, - absoluteRenderBounds: null, - constraints: { - vertical: "TOP", - horizontal: "LEFT", - }, - layoutAlign: "INHERIT", - layoutGrow: 0, - layoutSizingHorizontal: "HUG", - layoutSizingVertical: "HUG", - effects: [], - interactions: [], - }, - { - id: "I2099:38616;1739:34917;2326:19706", - name: "Slot", - visible: false, - type: "INSTANCE", - scrollBehavior: "SCROLLS", - componentId: "2060:58189", - overrides: [ - { - id: "I2099:38616;1739:34917;2326:19706", - overriddenFields: ["visible"], - }, - { - id: "I2099:38616;1739:34917;2326:19706;979:10619", - overriddenFields: [ - "sharedPluginData", - "pluginData", - ], - }, - ], - children: [], - blendMode: "PASS_THROUGH", - clipsContent: true, - background: [], - fills: [], - strokes: [], - cornerRadius: 4, - cornerSmoothing: 0, - strokeWeight: 2, - strokeAlign: "INSIDE", - backgroundColor: { - r: 0, - g: 0, - b: 0, - a: 0, - }, - overflowDirection: "VERTICAL_SCROLLING", - layoutMode: "HORIZONTAL", - counterAxisAlignItems: "CENTER", - primaryAxisAlignItems: "CENTER", - layoutWrap: "NO_WRAP", - strokeDashes: [8, 4], - absoluteBoundingBox: { - x: -1564, - y: -2066.75, - width: 98, - height: 39, - }, - absoluteRenderBounds: null, - constraints: { - vertical: "TOP", - horizontal: "LEFT", - }, - layoutAlign: "INHERIT", - layoutGrow: 0, - layoutSizingHorizontal: "HUG", - layoutSizingVertical: "HUG", - effects: [], - interactions: [], - }, - ], - blendMode: "PASS_THROUGH", - clipsContent: false, - background: [], - fills: [], - strokes: [], - strokeWeight: 1, - strokeAlign: "INSIDE", - backgroundColor: { - r: 0, - g: 0, - b: 0, - a: 0, - }, - layoutMode: "HORIZONTAL", - itemSpacing: 37.5, - primaryAxisSizingMode: "FIXED", - layoutWrap: "WRAP", - counterAxisSpacing: 37.5, - counterAxisAlignContent: "AUTO", - absoluteBoundingBox: { - x: -1907.5, - y: -2177.75, - width: 915, - height: 150, - }, - absoluteRenderBounds: { - x: -1907.5, - y: -2177.75, - width: 915, - height: 150, - }, - constraints: { - vertical: "TOP", - horizontal: "LEFT", - }, - layoutAlign: "STRETCH", - layoutGrow: 0, - layoutSizingHorizontal: "FILL", - layoutSizingVertical: "HUG", - effects: [], - interactions: [], - uniqueName: "Row", - width: 915, - height: 150, - x: 0, - y: 0, - paddingLeft: 0, - paddingRight: 0, - paddingTop: 0, - paddingBottom: 0, - primaryAxisAlignItems: "MIN", - counterAxisAlignItems: "MIN", - }, - ], - blendMode: "PASS_THROUGH", - clipsContent: true, - background: [], - fills: [], - strokes: [], - strokeWeight: 1, - strokeAlign: "INSIDE", - backgroundColor: { - r: 0, - g: 0, - b: 0, - a: 0, - }, - layoutMode: "VERTICAL", - counterAxisSizingMode: "FIXED", - itemSpacing: 37.5, - layoutWrap: "NO_WRAP", - absoluteBoundingBox: { - x: -1907.5, - y: -2177.75, - width: 915, - height: 150, - }, - absoluteRenderBounds: { - x: -1907.5, - y: -2177.75, - width: 915, - height: 150, - }, - constraints: { - vertical: "TOP", - horizontal: "LEFT", - }, - layoutAlign: "STRETCH", - layoutGrow: 0, - layoutSizingHorizontal: "FILL", - layoutSizingVertical: "HUG", - effects: [], - interactions: [], - uniqueName: "Grid", - width: 915, - height: 150, - x: 22.5, - y: 22.5, - paddingLeft: 0, - paddingRight: 0, - paddingTop: 0, - paddingBottom: 0, - primaryAxisAlignItems: "MIN", - counterAxisAlignItems: "MIN", - }, - ], - blendMode: "PASS_THROUGH", - clipsContent: false, - background: [], - fills: [], - strokes: [], - strokeWeight: 1, - strokeAlign: "INSIDE", - backgroundColor: { - r: 0, - g: 0, - b: 0, - a: 0, - }, - overflowDirection: "VERTICAL_SCROLLING", - layoutMode: "VERTICAL", - counterAxisSizingMode: "FIXED", - itemSpacing: 37.5, - primaryAxisSizingMode: "FIXED", - paddingLeft: 22.5, - paddingRight: 22.5, - paddingTop: 22.5, - paddingBottom: 22.5, - layoutWrap: "NO_WRAP", - absoluteBoundingBox: { - x: -1930, - y: -2200.25, - width: 960, - height: 536.25, - }, - absoluteRenderBounds: { - x: -1930, - y: -2200.25, - width: 960, - height: 536.25, - }, - constraints: { - vertical: "TOP", - horizontal: "LEFT", - }, - layoutAlign: "STRETCH", - layoutGrow: 1, - minHeight: 136.25, - maxHeight: 536.25, - layoutSizingHorizontal: "FILL", - layoutSizingVertical: "FILL", - effects: [], - interactions: [], - uniqueName: "Modal Container", - variantProperties: { - Type: "Fixed", - Allignment: "Default (L-R)", - }, - width: 960, - height: 536.25, - x: 0, - y: 0, - primaryAxisAlignItems: "MIN", - counterAxisAlignItems: "MIN", - }, - { - id: "I2099:38616;1739:61707", - name: "Scroll Container", - type: "INSTANCE", - locked: true, - scrollBehavior: "SCROLLS", - componentPropertyReferences: { - visible: "↳Show Scroll Container#1739:42", - }, - componentId: "438:9274", - componentProperties: { - "Show Top Gradient#1272:20": { - value: false, - type: "BOOLEAN", - }, - "Show Bottom Gradient#1272:19": { - value: true, - type: "BOOLEAN", - }, - "Show Container Bullets#2778:4": { - value: true, - type: "BOOLEAN", - }, - "Show Left Gradient#1359:0": { - value: false, - type: "BOOLEAN", - }, - "Show Right Gradient#1359:4": { - value: true, - type: "BOOLEAN", - }, - "Show Scrollbar#1272:18": { - value: true, - type: "BOOLEAN", - }, - "Show V Scrollbar#1359:8": { - value: true, - type: "BOOLEAN", - }, - "Show H Scrollbar#1359:12": { - value: true, - type: "BOOLEAN", - }, - Variation: { - value: "Vertical", - type: "VARIANT", - boundVariables: {}, - }, - }, - overrides: [], - children: [ - { - id: "I2099:38616;1739:61707;548:24039", - name: "Gradient Top", - visible: false, - type: "RECTANGLE", - scrollBehavior: "SCROLLS", - componentPropertyReferences: { - visible: "Show Top Gradient#1272:20", - }, - boundVariables: { - size: { - y: { - type: "VARIABLE_ALIAS", - id: "VariableID:7de17999810183e2464dbfd32573c3ecf15a9f2e/3157:462", - }, - }, - fills: [ - { - type: "VARIABLE_ALIAS", - id: "VariableID:26724dcbb1ea60587379f89ef3dc160ae7f7f7da/2022:241", - }, - { - type: "VARIABLE_ALIAS", - id: "VariableID:86e61e4b2a2fe9ab507a9302544920d38675a086/2036:2", - }, - ], - }, - blendMode: "PASS_THROUGH", - fills: [ - { - blendMode: "NORMAL", - type: "GRADIENT_LINEAR", - gradientHandlePositions: [ - { - x: 0.5, - y: -3.0616171314629196e-17, - }, - { - x: 0.5, - y: 0.9999999999999999, - }, - { - x: 0, - y: 0, - }, - ], - gradientStops: [ - { - color: { - r: 0.0784313753247261, - g: 0.0784313753247261, - b: 0.0784313753247261, - a: 1, - }, - position: 0, - boundVariables: { - color: { - type: "VARIABLE_ALIAS", - id: "VariableID:86e61e4b2a2fe9ab507a9302544920d38675a086/2036:2", - }, - }, - }, - { - color: { - r: 0.0784313753247261, - g: 0.0784313753247261, - b: 0.0784313753247261, - a: 0, - }, - position: 1, - boundVariables: { - color: { - type: "VARIABLE_ALIAS", - id: "VariableID:26724dcbb1ea60587379f89ef3dc160ae7f7f7da/2022:241", - }, - }, - }, - ], - }, - ], - strokes: [], - strokeWeight: 1, - strokeAlign: "INSIDE", - styles: { - fill: "438:9260", - }, - absoluteBoundingBox: { - x: -1930, - y: -2200, - width: 960, - height: 30, - }, - absoluteRenderBounds: null, - constraints: { - vertical: "TOP", - horizontal: "LEFT_RIGHT", - }, - layoutAlign: "INHERIT", - layoutGrow: 0, - layoutPositioning: "ABSOLUTE", - layoutSizingHorizontal: "FIXED", - layoutSizingVertical: "FIXED", - effects: [], - interactions: [], - }, - { - id: "I2099:38616;1739:61707;548:24040", - name: "Gradient Bottom", - type: "RECTANGLE", - scrollBehavior: "SCROLLS", - componentPropertyReferences: { - visible: "Show Bottom Gradient#1272:19", - }, - boundVariables: { - size: { - y: { - type: "VARIABLE_ALIAS", - id: "VariableID:7de17999810183e2464dbfd32573c3ecf15a9f2e/3157:462", - }, - }, - fills: [ - { - type: "VARIABLE_ALIAS", - id: "VariableID:86e61e4b2a2fe9ab507a9302544920d38675a086/2036:2", - }, - { - type: "VARIABLE_ALIAS", - id: "VariableID:26724dcbb1ea60587379f89ef3dc160ae7f7f7da/2022:241", - }, - ], - }, - blendMode: "PASS_THROUGH", - fills: [ - { - blendMode: "NORMAL", - type: "GRADIENT_LINEAR", - gradientHandlePositions: [ - { - x: 0.5, - y: -3.0616171314629196e-17, - }, - { - x: 0.5, - y: 0.9999999999999999, - }, - { - x: 0, - y: 0, - }, - ], - gradientStops: [ - { - color: { - r: 0.0784313753247261, - g: 0.0784313753247261, - b: 0.0784313753247261, - a: 0, - }, - position: 0, - boundVariables: { - color: { - type: "VARIABLE_ALIAS", - id: "VariableID:26724dcbb1ea60587379f89ef3dc160ae7f7f7da/2022:241", - }, - }, - }, - { - color: { - r: 0.0784313753247261, - g: 0.0784313753247261, - b: 0.0784313753247261, - a: 1, - }, - position: 1, - boundVariables: { - color: { - type: "VARIABLE_ALIAS", - id: "VariableID:86e61e4b2a2fe9ab507a9302544920d38675a086/2036:2", - }, - }, - }, - ], - }, - ], - strokes: [], - strokeWeight: 1, - strokeAlign: "INSIDE", - styles: { - fill: "438:9261", - }, - absoluteBoundingBox: { - x: -1930, - y: -1686, - width: 960, - height: 30, - }, - absoluteRenderBounds: { - x: -1930, - y: -1686, - width: 960, - height: 22, - }, - constraints: { - vertical: "BOTTOM", - horizontal: "LEFT_RIGHT", - }, - layoutAlign: "INHERIT", - layoutGrow: 0, - layoutPositioning: "ABSOLUTE", - layoutSizingHorizontal: "FIXED", - layoutSizingVertical: "FIXED", - effects: [], - interactions: [], - }, - { - id: "I2099:38616;1739:61707;548:24046", - name: "Scrollbar V", - type: "FRAME", - scrollBehavior: "SCROLLS", - componentPropertyReferences: { - visible: "Show Scrollbar#1272:18", - }, - boundVariables: { - paddingTop: { - type: "VARIABLE_ALIAS", - id: "VariableID:eb2b818a81b47a755734677805c327446a7eaacc/3157:1078", - }, - paddingRight: { - type: "VARIABLE_ALIAS", - id: "VariableID:eb2b818a81b47a755734677805c327446a7eaacc/3157:1078", - }, - paddingBottom: { - type: "VARIABLE_ALIAS", - id: "VariableID:eb2b818a81b47a755734677805c327446a7eaacc/3157:1078", - }, - }, - children: [ - { - id: "I2099:38616;1739:61707;548:24047", - name: "Bar", - type: "RECTANGLE", - scrollBehavior: "SCROLLS", - boundVariables: { - minHeight: { - type: "VARIABLE_ALIAS", - id: "VariableID:5fdc417718534e1862bd74d2b0cae46cbc3fdd93/3157:975", - }, - size: { - x: { - type: "VARIABLE_ALIAS", - id: "VariableID:4e7717a4a0ee45c0a9977c43d56cf4cb32848299/3157:257", - }, - }, - rectangleCornerRadii: { - RECTANGLE_TOP_LEFT_CORNER_RADIUS: { - type: "VARIABLE_ALIAS", - id: "VariableID:2044450eb10ae8e476766aeb5bf3550e3c2ba36e/3157:901", - }, - RECTANGLE_TOP_RIGHT_CORNER_RADIUS: { - type: "VARIABLE_ALIAS", - id: "VariableID:2044450eb10ae8e476766aeb5bf3550e3c2ba36e/3157:901", - }, - RECTANGLE_BOTTOM_LEFT_CORNER_RADIUS: { - type: "VARIABLE_ALIAS", - id: "VariableID:2044450eb10ae8e476766aeb5bf3550e3c2ba36e/3157:901", - }, - RECTANGLE_BOTTOM_RIGHT_CORNER_RADIUS: { - type: "VARIABLE_ALIAS", - id: "VariableID:2044450eb10ae8e476766aeb5bf3550e3c2ba36e/3157:901", - }, - }, - fills: [ - { - type: "VARIABLE_ALIAS", - id: "VariableID:9b31d8ddc760d048c5d9e42dbce12aa5946a3041/2036:15", - }, - ], - }, - blendMode: "PASS_THROUGH", - fills: [ - { - blendMode: "NORMAL", - type: "SOLID", - color: { - r: 0.658823549747467, - g: 0.658823549747467, - b: 0.658823549747467, - a: 1, - }, - boundVariables: { - color: { - type: "VARIABLE_ALIAS", - id: "VariableID:9b31d8ddc760d048c5d9e42dbce12aa5946a3041/2036:15", - }, - }, - }, - ], - strokes: [], - strokeWeight: 1, - strokeAlign: "INSIDE", - cornerRadius: 1000, - cornerSmoothing: 0, - absoluteBoundingBox: { - x: -979, - y: -2195.5, - width: 4.5, - height: 31, - }, - absoluteRenderBounds: { - x: -979, - y: -2195.5, - width: 4.5, - height: 31, - }, - constraints: { - vertical: "TOP", - horizontal: "LEFT", - }, - layoutAlign: "INHERIT", - layoutGrow: 1, - minHeight: 15, - layoutSizingHorizontal: "FIXED", - layoutSizingVertical: "FILL", - effects: [], - interactions: [], - }, - ], - blendMode: "PASS_THROUGH", - clipsContent: false, - background: [], - fills: [], - strokes: [], - strokeWeight: 1, - strokeAlign: "INSIDE", - backgroundColor: { - r: 0, - g: 0, - b: 0, - a: 0, - }, - layoutMode: "VERTICAL", - primaryAxisSizingMode: "FIXED", - counterAxisAlignItems: "MAX", - paddingRight: 4.5, - paddingTop: 4.5, - paddingBottom: 4.5, - layoutWrap: "NO_WRAP", - absoluteBoundingBox: { - x: -979, - y: -2200, - width: 9, - height: 40, - }, - absoluteRenderBounds: { - x: -979, - y: -2200, - width: 9, - height: 40, - }, - constraints: { - vertical: "TOP", - horizontal: "LEFT", - }, - layoutAlign: "INHERIT", - layoutGrow: 0, - layoutSizingHorizontal: "HUG", - layoutSizingVertical: "FIXED", - effects: [], - interactions: [], - }, - ], - blendMode: "PASS_THROUGH", - clipsContent: true, - background: [], - fills: [], - strokes: [], - strokeWeight: 1, - strokeAlign: "INSIDE", - backgroundColor: { - r: 0, - g: 0, - b: 0, - a: 0, - }, - layoutMode: "VERTICAL", - counterAxisSizingMode: "FIXED", - primaryAxisSizingMode: "FIXED", - counterAxisAlignItems: "MAX", - layoutWrap: "NO_WRAP", - absoluteBoundingBox: { - x: -1930, - y: -2200, - width: 960, - height: 536, - }, - absoluteRenderBounds: { - x: -1930, - y: -2200, - width: 960, - height: 536, - }, - constraints: { - vertical: "TOP_BOTTOM", - horizontal: "LEFT_RIGHT", - }, - layoutAlign: "INHERIT", - layoutGrow: 0, - layoutPositioning: "ABSOLUTE", - layoutSizingHorizontal: "FIXED", - layoutSizingVertical: "FIXED", - effects: [], - interactions: [], - uniqueName: "Scroll Container", - variantProperties: { - Variation: "Vertical", - }, - width: 960, - height: 536, - x: 0, - y: 0.25, - paddingLeft: 0, - paddingRight: 0, - paddingTop: 0, - paddingBottom: 0, - primaryAxisAlignItems: "MIN", - }, - ], - blendMode: "PASS_THROUGH", - clipsContent: true, - background: [], - fills: [], - strokes: [], - strokeWeight: 1, - strokeAlign: "INSIDE", - backgroundColor: { - r: 0, - g: 0, - b: 0, - a: 0, - }, - layoutMode: "VERTICAL", - counterAxisSizingMode: "FIXED", - itemSpacing: 10, - primaryAxisSizingMode: "FIXED", - layoutWrap: "NO_WRAP", - absoluteBoundingBox: { - x: -1930, - y: -2200.25, - width: 960, - height: 536.25, - }, - absoluteRenderBounds: { - x: -1930, - y: -2200.25, - width: 960, - height: 536.25, - }, - constraints: { - vertical: "TOP", - horizontal: "LEFT", - }, - layoutAlign: "STRETCH", - layoutGrow: 1, - layoutSizingHorizontal: "FILL", - layoutSizingVertical: "FILL", - effects: [], - interactions: [], - uniqueName: "Content", - width: 960, - height: 536.25, - x: 0, - y: 93.75, - paddingLeft: 0, - paddingRight: 0, - paddingTop: 0, - paddingBottom: 0, - primaryAxisAlignItems: "MIN", - counterAxisAlignItems: "MIN", - isRelative: true, - }, - ], - blendMode: "PASS_THROUGH", - clipsContent: true, - background: [ - { - blendMode: "NORMAL", - type: "SOLID", - color: { - r: 0.1411764770746231, - g: 0.1411764770746231, - b: 0.1411764770746231, - a: 1, - }, - boundVariables: { - color: { - type: "VARIABLE_ALIAS", - id: "VariableID:8cbcd0032a7cac3b9799f16f6f48c35cab554a40/2243:10", - }, - }, - }, - ], - fills: [ - { - blendMode: "NORMAL", - type: "SOLID", - color: { - r: 0.1411764770746231, - g: 0.1411764770746231, - b: 0.1411764770746231, - a: 1, - }, - boundVariables: { - color: { - type: "VARIABLE_ALIAS", - id: "VariableID:8cbcd0032a7cac3b9799f16f6f48c35cab554a40/2243:10", - }, - }, - variableColorName: "Box-Style-box4", - }, - ], - strokes: [], - cornerRadius: 4.5, - cornerSmoothing: 0, - strokeWeight: 1, - strokeAlign: "INSIDE", - backgroundColor: { - r: 0.1411764770746231, - g: 0.1411764770746231, - b: 0.1411764770746231, - a: 1, - }, - layoutMode: "VERTICAL", - counterAxisSizingMode: "FIXED", - primaryAxisSizingMode: "FIXED", - counterAxisAlignItems: "CENTER", - layoutWrap: "NO_WRAP", - absoluteBoundingBox: { - x: -1930, - y: -2294, - width: 960, - height: 720, - }, - absoluteRenderBounds: { - x: -1969, - y: -2323.10009765625, - width: 1038, - height: 789.10009765625, - }, - constraints: { - vertical: "TOP", - horizontal: "LEFT", - }, - layoutAlign: "INHERIT", - layoutGrow: 0, - minHeight: 320, - maxHeight: 720, - layoutSizingHorizontal: "FIXED", - layoutSizingVertical: "FIXED", - effects: [ - { - type: "DROP_SHADOW", - visible: true, - color: { - r: 0, - g: 0, - b: 0, - a: 0.47999998927116394, - }, - blendMode: "NORMAL", - offset: { - x: 0, - y: 9.899999618530273, - }, - radius: 39, - showShadowBehindNode: true, - boundVariables: { - radius: { - type: "VARIABLE_ALIAS", - id: "VariableID:7b576d4f7cef936e728857b8d6f7952e2a6dd6fe/3157:78", - }, - color: { - type: "VARIABLE_ALIAS", - id: "VariableID:e7dccd708e8eb5f689ef26147d2d985b7af1bfd8/2013:336", - }, - offsetY: { - type: "VARIABLE_ALIAS", - id: "VariableID:55268df3aca26e8ed4182c6831670c631ab2e88b/4411:298", - }, - }, - variableColorName: "Elevation-elevation5", - }, - ], - styles: { - effect: "438:10166", - }, - interactions: [], - uniqueName: "Modal", - width: 960, - height: 720, - x: 160, - y: 40, - paddingLeft: 0, - paddingRight: 0, - paddingTop: 0, - paddingBottom: 0, - primaryAxisAlignItems: "MIN", - }, - ]; + console.log( `[benchmark] convertNodesToAltNodes: ${Date.now() - convertNodesStart}ms`, ); diff --git a/packages/backend/src/common/commonPosition.ts b/packages/backend/src/common/commonPosition.ts index 5001088c..53bf4ddb 100644 --- a/packages/backend/src/common/commonPosition.ts +++ b/packages/backend/src/common/commonPosition.ts @@ -1,6 +1,23 @@ export const getCommonPositionValue = ( node: SceneNode, ): { x: number; y: number } => { + if (node.parent && node.parent.absoluteBoundingBox) { + const x = node.absoluteBoundingBox.x - node.parent.absoluteBoundingBox.x; + const y = node.absoluteBoundingBox.y - node.parent.absoluteBoundingBox.y; + + const rect = calculateRectangleFromBoundingBox( + { + width: node.absoluteBoundingBox.width, + height: node.absoluteBoundingBox.height, + x: x, + y: y, + }, + node.cumulativeRotation || node.rotation || 0, + ); + + return { x: rect.left, y: rect.top }; + } + if (node.parent && node.parent.type === "GROUP") { return { x: node.x - node.parent.x, @@ -14,6 +31,66 @@ export const getCommonPositionValue = ( }; }; +interface BoundingBox { + width: number; // w_b + height: number; // h_b + x: number; // x_b + y: number; // y_b +} + +interface RectangleStyle { + width: number; // Original width (w) + height: number; // Original height (h) + left: number; // Final CSS left + top: number; // Final CSS top + rotation: number; // Rotation in degrees +} + +function calculateRectangleFromBoundingBox( + boundingBox: BoundingBox, + figmaRotationDegrees: number, +): RectangleStyle { + const cssRotationDegrees = -figmaRotationDegrees; // Direct CSS mapping + const theta = (cssRotationDegrees * Math.PI) / 180; + const cosTheta = Math.cos(theta); + const sinTheta = Math.sin(theta); + const absCosTheta = Math.abs(cosTheta); + const absSinTheta = Math.abs(sinTheta); + + const { width: w_b, height: h_b, x: x_b, y: y_b } = boundingBox; + + // For top-left origin, bounding box depends on rotation direction + const denominator = absCosTheta * absCosTheta - absSinTheta * absSinTheta; + const h = (w_b * absSinTheta - h_b * absCosTheta) / -denominator; + const w = (w_b - h * absSinTheta) / absCosTheta; + + // Rotate corners to find bounding box offsets + const corners = [ + { x: 0, y: 0 }, + { x: w, y: 0 }, + { x: w, y: h }, + { x: 0, y: h }, + ]; + const rotatedCorners = corners.map(({ x, y }) => ({ + x: x * cosTheta + y * sinTheta, + y: -x * sinTheta + y * cosTheta, + })); + + const minX = Math.min(...rotatedCorners.map((c) => c.x)); + const minY = Math.min(...rotatedCorners.map((c) => c.y)); + + const left = x_b - minX; + const top = y_b - minY; + + return { + width: parseFloat(w.toFixed(2)), + height: parseFloat(h.toFixed(2)), + left: parseFloat(left.toFixed(2)), + top: parseFloat(top.toFixed(2)), + rotation: cssRotationDegrees, + }; +} + export const commonIsAbsolutePosition = (node: SceneNode) => { if ("layoutPositioning" in node && node.layoutPositioning === "ABSOLUTE") { return true; diff --git a/packages/backend/src/html/builderImpl/htmlBlend.ts b/packages/backend/src/html/builderImpl/htmlBlend.ts index 1a68039a..3a63197a 100644 --- a/packages/backend/src/html/builderImpl/htmlBlend.ts +++ b/packages/backend/src/html/builderImpl/htmlBlend.ts @@ -125,8 +125,9 @@ export const htmlRotation = (node: LayoutMixin, isJsx: boolean): string[] => { const parentRotation: number = parent && "rotation" in parent ? parent.rotation : 0; - const nodeRotation = node.rotation || 0; - const rotation = Math.round(parentRotation - nodeRotation) ?? 0; + const cssRotationDegrees = node.rotation || 0; + const nodeRotation = cssRotationDegrees; + const rotation = Math.round(nodeRotation) ?? 0; if ( roundToNearestHundreth(parentRotation) !== 0 && From 07d094e6f63cd05975667645af7c7b095181fb05 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Tue, 11 Mar 2025 04:22:54 -0300 Subject: [PATCH 107/168] Debug --- packages/backend/src/html/builderImpl/htmlBlend.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/html/builderImpl/htmlBlend.ts b/packages/backend/src/html/builderImpl/htmlBlend.ts index 3a63197a..94c6bfd2 100644 --- a/packages/backend/src/html/builderImpl/htmlBlend.ts +++ b/packages/backend/src/html/builderImpl/htmlBlend.ts @@ -125,9 +125,8 @@ export const htmlRotation = (node: LayoutMixin, isJsx: boolean): string[] => { const parentRotation: number = parent && "rotation" in parent ? parent.rotation : 0; - const cssRotationDegrees = node.rotation || 0; - const nodeRotation = cssRotationDegrees; - const rotation = Math.round(nodeRotation) ?? 0; + const nodeRotation = -node.rotation || 0; + const rotation = Math.round(parentRotation - nodeRotation) ?? 0; if ( roundToNearestHundreth(parentRotation) !== 0 && From 958c34379468eaabde18175f3f945b00c7fef94a Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Wed, 12 Mar 2025 02:59:22 -0300 Subject: [PATCH 108/168] New UI --- apps/plugin/plugin-src/code.ts | 6 +- apps/plugin/tailwind.config.js | 56 +- apps/plugin/ui-src/index.css | 67 +++ .../backend/src/altNodes/altConversion.ts | 87 --- .../src/altNodes/jsonNodeConversion.ts | 498 ++++++++++++++++++ packages/backend/src/code.ts | 447 +--------------- .../src/common/commonFormatAttributes.ts | 2 +- .../src/common/retrieveUI/retrieveColors.ts | 21 +- .../backend/src/html/builderImpl/htmlSize.ts | 12 +- packages/backend/src/index.ts | 1 - .../tailwind/builderImpl/tailwindBorder.ts | 4 +- .../src/tailwind/builderImpl/tailwindSize.ts | 12 +- packages/plugin-ui/src/PluginUI.tsx | 90 ++-- packages/plugin-ui/src/components/About.tsx | 14 +- .../plugin-ui/src/components/CodePanel.tsx | 3 +- .../plugin-ui/src/components/ColorsPanel.tsx | 11 +- .../plugin-ui/src/components/CopyButton.tsx | 34 +- .../plugin-ui/src/components/EmptyState.tsx | 2 +- .../src/components/FrameworkTabs.tsx | 6 +- .../src/components/GradientsPanel.tsx | 4 +- packages/plugin-ui/src/components/Preview.tsx | 10 +- .../src/components/SelectableToggle.tsx | 15 +- .../src/components/SettingsGroup.tsx | 4 +- packages/plugin-ui/tailwind.config.js | 56 ++ 24 files changed, 841 insertions(+), 621 deletions(-) delete mode 100644 packages/backend/src/altNodes/altConversion.ts create mode 100644 packages/backend/src/altNodes/jsonNodeConversion.ts diff --git a/apps/plugin/plugin-src/code.ts b/apps/plugin/plugin-src/code.ts index 2b10d3a0..6cf5e016 100644 --- a/apps/plugin/plugin-src/code.ts +++ b/apps/plugin/plugin-src/code.ts @@ -4,11 +4,10 @@ import { flutterMain, tailwindMain, swiftuiMain, - convertIntoNodes, htmlMain, postSettingsChanged, } from "backend"; -import { nodesToJSON } from "backend/src/code"; +import { nodesToJSON } from "backend/src/altNodes/jsonNodeConversion"; import { retrieveGenericSolidUIColors } from "backend/src/common/retrieveUI/retrieveColors"; import { flutterCodeGenTextStyles } from "backend/src/flutter/flutterMain"; import { htmlCodeGenTextStyles } from "backend/src/html/htmlMain"; @@ -140,7 +139,8 @@ const standardMode = async () => { }); // Listen for page changes - figma.on("currentpagechange", () => { + figma.loadAllPagesAsync(); + figma.on("documentchange", () => { console.log("[DEBUG] documentchange event triggered"); // Node: This was causing an infinite load when you try to export a background image from a group that contains children. // The reason for this is that the code will temporarily hide the children of the group in order to export a clean image diff --git a/apps/plugin/tailwind.config.js b/apps/plugin/tailwind.config.js index 0c74c637..a134a47d 100644 --- a/apps/plugin/tailwind.config.js +++ b/apps/plugin/tailwind.config.js @@ -6,5 +6,59 @@ module.exports = { "../../packages/plugin-ui/**/*.{js,ts,jsx,tsx}", ], darkMode: "class", - + theme: { + extend: { + colors: { + border: "hsl(var(--border))", + input: "hsl(var(--input))", + ring: "hsl(var(--ring))", + background: "hsl(var(--background))", + foreground: "hsl(var(--foreground))", + primary: { + DEFAULT: "hsl(var(--primary))", + foreground: "hsl(var(--primary-foreground))", + }, + secondary: { + DEFAULT: "hsl(var(--secondary))", + foreground: "hsl(var(--secondary-foreground))", + }, + destructive: { + DEFAULT: "hsl(var(--destructive) / )", + foreground: "hsl(var(--destructive-foreground) / )", + }, + muted: { + DEFAULT: "hsl(var(--muted))", + foreground: "hsl(var(--muted-foreground))", + }, + accent: { + DEFAULT: "hsl(var(--accent))", + foreground: "hsl(var(--accent-foreground))", + }, + popover: { + DEFAULT: "hsl(var(--popover))", + foreground: "hsl(var(--popover-foreground))", + }, + card: { + DEFAULT: "hsl(var(--card))", + foreground: "hsl(var(--card-foreground))", + }, + sidebar: { + DEFAULT: "hsl(var(--sidebar-background))", + foreground: "hsl(var(--sidebar-foreground))", + primary: "hsl(var(--sidebar-primary))", + "primary-foreground": "hsl(var(--sidebar-primary-foreground))", + accent: "hsl(var(--sidebar-accent))", + "accent-foreground": "hsl(var(--sidebar-accent-foreground))", + border: "hsl(var(--sidebar-border))", + ring: "hsl(var(--sidebar-ring))", + }, + }, + borderRadius: { + xl: "calc(var(--radius) + 4px)", + lg: "var(--radius)", + md: "calc(var(--radius) - 2px)", + sm: "calc(var(--radius) - 4px)", + }, + } + } }; diff --git a/apps/plugin/ui-src/index.css b/apps/plugin/ui-src/index.css index b5c61c95..5fbe3dcf 100644 --- a/apps/plugin/ui-src/index.css +++ b/apps/plugin/ui-src/index.css @@ -1,3 +1,70 @@ @tailwind base; @tailwind components; @tailwind utilities; + + +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 240 10% 3.9%; + --card: 220 14% 96%; + --card-foreground: 240 10% 3.9%; + --popover: 0 0% 100%; + --popover-foreground: 240 10% 3.9%; + --primary: 142 70% 45%; + --primary-foreground: 0 0% 100%; + --secondary: 240 4.8% 95.9%; + --secondary-foreground: 240 5.9% 10%; + --muted: 240 4.8% 95.9%; + --muted-foreground: 240 3.8% 46.1%; + --accent: 240 4.8% 95.9%; + --accent-foreground: 240 5.9% 10%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 0 0% 98%; + --border: 240 5.9% 90%; + --input: 240 5.9% 90%; + --ring: 240 5.9% 10%; + --radius: 0.5rem; + --chart-1: 12 76% 61%; + --chart-2: 173 58% 39%; + --chart-3: 197 37% 24%; + --chart-4: 43 74% 66%; + --chart-5: 27 87% 67%; + } + + .dark { + --background: 240 10% 3.9%; + --foreground: 0 0% 98%; + --card: 0 0% 13%; + --card-foreground: 0 0% 98%; + --popover: 240 10% 3.9%; + --popover-foreground: 0 0% 98%; + --primary: 142 70% 45%; + --primary-foreground: 0 0% 100%; + --secondary: 240 3.7% 15.9%; + --secondary-foreground: 0 0% 98%; + --muted: 240 3.7% 15.9%; + --muted-foreground: 240 5% 64.9%; + --accent: 240 3.7% 15.9%; + --accent-foreground: 0 0% 98%; + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 0 0% 98%; + --border: 240 3.7% 15.9%; + --input: 240 3.7% 15.9%; + --ring: 240 4.9% 83.9%; + --chart-1: 220 70% 50%; + --chart-2: 160 60% 45%; + --chart-3: 30 80% 55%; + --chart-4: 280 65% 60%; + --chart-5: 340 75% 55%; + } + } + + @layer base { + * { + @apply border-border outline-ring/50; + } + /* body { + @apply bg-background text-foreground; + } */ + } \ No newline at end of file diff --git a/packages/backend/src/altNodes/altConversion.ts b/packages/backend/src/altNodes/altConversion.ts deleted file mode 100644 index 9865d713..00000000 --- a/packages/backend/src/altNodes/altConversion.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { ParentNode } from "types"; -import { - isNotEmpty, - assignRectangleType, - assignChildren, - isTypeOrGroupOfTypes, -} from "./altNodeUtils"; -import { addWarning } from "../common/commonConversionWarnings"; - -// List of types that can be flattened into SVG -const canBeFlattened = (node: SceneNode): boolean => { - // These node types should be directly flattened - const flattenableTypes: NodeType[] = [ - "VECTOR", - "STAR", - "POLYGON", - "BOOLEAN_OPERATION", - ]; - - // Handle special case for Rectangle nodes with zero or near-zero height - if (node.type === "RECTANGLE") { - // Check if the node is essentially a divider/line (near-zero height) - return false; // Rectangles should not be flattened by default - } - - return isTypeOrGroupOfTypes(flattenableTypes, node); -}; - -export const convertNodeToAltNode = - (parent: ParentNode | null) => - async (node: SceneNode): Promise => { - (node as any).canBeFlattened = canBeFlattened(node); - const type = node.type; - switch (type) { - // Standard nodes - case "RECTANGLE": - case "ELLIPSE": - case "LINE": - case "STAR": - case "POLYGON": - case "VECTOR": - case "BOOLEAN_OPERATION": - return node; - - // Group nodes - case "FRAME": - case "INSTANCE": - case "COMPONENT": - case "COMPONENT_SET": - // if the frame, instance etc. has no children, convert the frame to rectangle - if (node.children.length === 0) return cloneAsRectangleNode(node); - case "GROUP": - // if a Group is visible and has only one child, the Group should be ungrouped. - if (type === "GROUP" && node.children.length === 1 && node.visible) - return convertNodeToAltNode(parent)(node.children[0]); - case "SECTION": - const groupChildren = await convertNodesToAltNodes(node.children, node); - return assignChildren(groupChildren, node); - // Text Nodes - case "TEXT": - return node; - // Unsupported Nodes - case "SLICE": - return null; - default: - addWarning(`${node.type} node is not supported`); - return node; - // throw new Error( - // `Sorry, an unsupported node type was selected. Type:${node.type} id:${node.id}`, - // ); - } - }; - -export const convertNodesToAltNodes = async ( - sceneNode: ReadonlyArray, - parent: ParentNode | null, -): Promise> => - (await Promise.all(sceneNode.map(convertNodeToAltNode(parent)))).filter( - isNotEmpty, - ); - -// auto convert Frame to Rectangle when Frame has no Children -const cloneAsRectangleNode = (node: T): RectangleNode => { - assignRectangleType(node); - - return node as unknown as RectangleNode; -}; diff --git a/packages/backend/src/altNodes/jsonNodeConversion.ts b/packages/backend/src/altNodes/jsonNodeConversion.ts new file mode 100644 index 00000000..36b1272c --- /dev/null +++ b/packages/backend/src/altNodes/jsonNodeConversion.ts @@ -0,0 +1,498 @@ +import { addWarning } from "../common/commonConversionWarnings"; +import { PluginSettings } from "types"; +import { variableToColorName } from "../tailwind/conversionTables"; +import { HasGeometryTrait, Node, Paint } from "../api_types"; + +// Performance tracking counters +export let getNodeByIdAsyncTime = 0; +export let getNodeByIdAsyncCalls = 0; +export let getStyledTextSegmentsTime = 0; +export let getStyledTextSegmentsCalls = 0; +export let processColorVariablesTime = 0; +export let processColorVariablesCalls = 0; + +export const resetPerformanceCounters = () => { + getNodeByIdAsyncTime = 0; + getNodeByIdAsyncCalls = 0; + getStyledTextSegmentsTime = 0; + getStyledTextSegmentsCalls = 0; + processColorVariablesTime = 0; + processColorVariablesCalls = 0; +}; + +// Keep track of node names for sequential numbering +const nodeNameCounters: Map = new Map(); + +const variableCache = new Map(); + +const memoizedVariableToColorName = async ( + variableId: string, +): Promise => { + if (!variableCache.has(variableId)) { + const colorName = (await variableToColorName(variableId)).replaceAll( + ",", + "", + ); + variableCache.set(variableId, colorName); + return colorName; + } + return variableCache.get(variableId)!; +}; + +/** + * Process color variables in a paint style and add pre-computed variable names + * @param paint The paint style to process (fill or stroke) + */ +const processColorVariables = async (paint: Paint) => { + const start = Date.now(); + processColorVariablesCalls++; + + if ( + paint.type === "GRADIENT_ANGULAR" || + paint.type === "GRADIENT_DIAMOND" || + paint.type === "GRADIENT_LINEAR" || + paint.type === "GRADIENT_RADIAL" + ) { + // Filter stops with bound variables first to avoid unnecessary work + const stopsWithVariables = paint.gradientStops.filter( + (stop) => stop.boundVariables?.color, + ); + + // Process all gradient stops with variables in parallel + if (stopsWithVariables.length > 0) { + await Promise.all( + stopsWithVariables.map(async (stop) => { + (stop as any).variableColorName = await memoizedVariableToColorName( + stop.boundVariables!.color!.id, + ); + }), + ); + } + } else if (paint.type === "SOLID" && paint.boundVariables?.color) { + // Pre-compute and store the variable name + (paint as any).variableColorName = await memoizedVariableToColorName( + paint.boundVariables.color.id, + ); + } + + processColorVariablesTime += Date.now() - start; +}; + +const processEffectVariables = async ( + paint: DropShadowEffect | InnerShadowEffect, +) => { + const start = Date.now(); + processColorVariablesCalls++; + + if (paint.boundVariables?.color) { + // Pre-compute and store the variable name + (paint as any).variableColorName = await memoizedVariableToColorName( + paint.boundVariables.color.id, + ); + } + + processColorVariablesTime += Date.now() - start; +}; + +const getColorVariables = async ( + node: HasGeometryTrait, + settings: PluginSettings, +) => { + // This tries to be as fast as it can, using Promise.all so it can parallelize calls. + if (settings.useColorVariables) { + if (node.fills && Array.isArray(node.fills)) { + await Promise.all( + node.fills.map((fill: Paint) => processColorVariables(fill)), + ); + } + if (node.strokes && Array.isArray(node.strokes)) { + await Promise.all( + node.strokes.map((stroke: Paint) => processColorVariables(stroke)), + ); + } + if ("effects" in node && node.effects && Array.isArray(node.effects)) { + await Promise.all( + node.effects + .filter( + (effect: Effect) => + effect.type === "DROP_SHADOW" || effect.type === "INNER_SHADOW", + ) + .map((effect: DropShadowEffect | InnerShadowEffect) => + processEffectVariables(effect), + ), + ); + } + } +}; + +function adjustChildrenOrder(node: any) { + if (!node.itemReverseZIndex || !node.children || node.layoutMode === "NONE") { + return; + } + + const children = node.children; + const absoluteChildren = []; + const fixedChildren = []; + + // Single pass to separate absolute and fixed children + for (let i = children.length - 1; i >= 0; i--) { + const child = children[i]; + if (child.layoutPositioning === "ABSOLUTE") { + absoluteChildren.push(child); + } else { + fixedChildren.unshift(child); // Add to beginning to maintain original order + } + } + + // Combine the arrays (reversed absolute children + original order fixed children) + node.children = [...absoluteChildren, ...fixedChildren]; +} + +/** + * Checks if a node can be flattened into SVG + */ +const canBeFlattened = (node: Node): boolean => { + // These node types should be directly flattened + const flattenableTypes: string[] = [ + "VECTOR", + "STAR", + "POLYGON", + "BOOLEAN_OPERATION", + ]; + + // Handle special case for Rectangle nodes with zero or near-zero height + if (node.type === "RECTANGLE") { + return false; // Rectangles should not be flattened by default + } + + return flattenableTypes.includes(node.type); +}; + +/** + * Recursively process both JSON node and Figma node to update with data not available in JSON + * This now includes the functionality from convertNodeToAltNode + * @param jsonNode The JSON node to process + * @param figmaNode The corresponding Figma node + * @param settings Plugin settings + * @param parentNode Optional parent node reference to set + * @param parentCumulativeRotation Optional parent cumulative rotation to inherit + * @returns Potentially modified jsonNode + */ +const processNodePair = async ( + jsonNode: Node, + figmaNode: SceneNode, + settings: PluginSettings, + parentNode?: Node, + parentCumulativeRotation: number = 0, +): Promise => { + if (!jsonNode.id) return null; + if (jsonNode.visible === false) return null; + + // Add canBeFlattened property + (jsonNode as any).canBeFlattened = canBeFlattened(jsonNode); + + // Handle node type-specific conversions (from convertNodeToAltNode) + const nodeType = jsonNode.type; + + // Handle empty frames and convert to rectangles + if ( + (nodeType === "FRAME" || + nodeType === "INSTANCE" || + nodeType === "COMPONENT" || + nodeType === "COMPONENT_SET") && + (!jsonNode.children || jsonNode.children.length === 0) + ) { + // Convert to rectangle + jsonNode.type = "RECTANGLE"; + return processNodePair( + jsonNode, + figmaNode, + settings, + parentNode, + parentCumulativeRotation, + ); + } + + // Handle single-child groups that should be ungrouped + if ( + nodeType === "GROUP" && + jsonNode.children && + jsonNode.children.length === 1 && + jsonNode.visible + ) { + // Process the child directly, but preserve parent reference + return processNodePair( + jsonNode.children[0], + (figmaNode as GroupNode).children[0], + settings, + parentNode, + parentCumulativeRotation, + ); + } + + // Return null for unsupported nodes + if (nodeType === "SLICE") { + return null; + } + + // Set parent reference if parent is provided + if (parentNode) { + (jsonNode as any).parent = parentNode; + } + + // Store the cumulative rotation (parent's cumulative + node's own) + if (parentNode?.type === "GROUP") { + jsonNode.cumulativeRotation = parentCumulativeRotation; + } + + // Ensure node has a unique name with simple numbering + const cleanName = jsonNode.name.trim(); + + // Track names with simple counter + const count = nodeNameCounters.get(cleanName) || 0; + nodeNameCounters.set(cleanName, count + 1); + + // For first occurrence, use original name; for duplicates, add sequential suffix + jsonNode.uniqueName = + count === 0 + ? cleanName + : `${cleanName}_${count.toString().padStart(2, "0")}`; + + // Handle text-specific properties + if (figmaNode.type === "TEXT") { + const getSegmentsStart = Date.now(); + getStyledTextSegmentsCalls++; + let styledTextSegments = figmaNode.getStyledTextSegments([ + "fontName", + "fills", + "fontSize", + "fontWeight", + "hyperlink", + "indentation", + "letterSpacing", + "lineHeight", + "listOptions", + "textCase", + "textDecoration", + "textStyleId", + "fillStyleId", + "openTypeFeatures", + ]); + getStyledTextSegmentsTime += Date.now() - getSegmentsStart; + + // Assign unique IDs to each segment + if (styledTextSegments.length > 0) { + const baseSegmentName = (jsonNode.uniqueName || jsonNode.name) + .replace(/[^a-zA-Z0-9_-]/g, "") + .toLowerCase(); + + // Add a uniqueId to each segment + styledTextSegments = await Promise.all( + styledTextSegments.map(async (segment, index) => { + const mutableSegment: any = Object.assign({}, segment); + + if (settings.useColorVariables && segment.fills) { + mutableSegment.fills = await Promise.all( + segment.fills.map(async (d) => { + if ( + d.blendMode !== "PASS_THROUGH" && + d.blendMode !== "NORMAL" + ) { + addWarning("BlendMode is not supported in Text colors"); + } + const fill = { ...d } as Paint; + await processColorVariables(fill); + return fill; + }), + ); + } + + // For single segments, don't add index suffix + if (styledTextSegments.length === 1) { + (mutableSegment as any).uniqueId = `${baseSegmentName}_span`; + } else { + // For multiple segments, add index suffix + (mutableSegment as any).uniqueId = + `${baseSegmentName}_span_${(index + 1).toString().padStart(2, "0")}`; + } + return mutableSegment; + }), + ); + + jsonNode.styledTextSegments = styledTextSegments; + } + + Object.assign(jsonNode, jsonNode.style); + if (!jsonNode.textAutoResize) { + jsonNode.textAutoResize = "NONE"; + } + } + + // Extract component metadata from instances + if ("variantProperties" in figmaNode && figmaNode.variantProperties) { + jsonNode.variantProperties = figmaNode.variantProperties; + } + + // Always copy size and position + if ("width" in figmaNode) { + jsonNode.width = figmaNode.width; + jsonNode.height = figmaNode.height; + jsonNode.x = figmaNode.x; + jsonNode.y = figmaNode.y; + } + + if ("rotation" in jsonNode) { + jsonNode.rotation = jsonNode.rotation * (180 / Math.PI); + } + + if ("individualStrokeWeights" in jsonNode) { + jsonNode.strokeTopWeight = jsonNode.individualStrokeWeights.top; + jsonNode.strokeBottomWeight = jsonNode.individualStrokeWeights.bottom; + jsonNode.strokeLeftWeight = jsonNode.individualStrokeWeights.left; + jsonNode.strokeRightWeight = jsonNode.individualStrokeWeights.right; + } + + await getColorVariables(jsonNode, settings); + + // Some places check if paddingLeft exists. This makes sure they all exist, even if 0. + if ("layoutMode" in jsonNode && jsonNode.layoutMode) { + if (jsonNode.paddingLeft === undefined) { + jsonNode.paddingLeft = 0; + } + if (jsonNode.paddingRight === undefined) { + jsonNode.paddingRight = 0; + } + if (jsonNode.paddingTop === undefined) { + jsonNode.paddingTop = 0; + } + if (jsonNode.paddingBottom === undefined) { + jsonNode.paddingBottom = 0; + } + } + + // Set default layout properties if missing + if (!jsonNode.layoutMode) jsonNode.layoutMode = "NONE"; + if (!jsonNode.layoutGrow) jsonNode.layoutGrow = 0; + if (!jsonNode.layoutSizingHorizontal) + jsonNode.layoutSizingHorizontal = "FIXED"; + if (!jsonNode.layoutSizingVertical) jsonNode.layoutSizingVertical = "FIXED"; + if (!jsonNode.primaryAxisAlignItems) { + jsonNode.primaryAxisAlignItems = "MIN"; + } + if (!jsonNode.counterAxisAlignItems) { + jsonNode.counterAxisAlignItems = "MIN"; + } + + // If layout sizing is HUG but there are no children, set it to FIXED + const hasChildren = + "children" in jsonNode && + jsonNode.children && + Array.isArray(jsonNode.children) && + jsonNode.children.length > 0; + + if (jsonNode.layoutSizingHorizontal === "HUG" && !hasChildren) { + jsonNode.layoutSizingHorizontal = "FIXED"; + } + if (jsonNode.layoutSizingVertical === "HUG" && !hasChildren) { + jsonNode.layoutSizingVertical = "FIXED"; + } + + // Process children recursively if both have children + if ( + "children" in jsonNode && + jsonNode.children && + Array.isArray(jsonNode.children) && + "children" in figmaNode && + figmaNode.children.length === jsonNode.children.length + ) { + console.log("cumulative", parentCumulativeRotation); + + const cumulative = + parentCumulativeRotation + + (jsonNode.type === "GROUP" ? jsonNode.rotation || 0 : 0); + + // Process children and handle potential null returns + const processedChildren = []; + for (let i = 0; i < jsonNode.children.length; i++) { + const processedChild = await processNodePair( + jsonNode.children[i], + figmaNode.children[i], + settings, + jsonNode, + cumulative, + ); + + if (processedChild !== null) { + processedChildren.push(processedChild); + } + } + + // Replace children array with processed children + jsonNode.children = processedChildren; + + if ( + jsonNode.layoutMode === "NONE" || + jsonNode.children.some( + (d: any) => + "layoutPositioning" in d && d.layoutPositioning === "ABSOLUTE", + ) + ) { + jsonNode.isRelative = true; + } + + adjustChildrenOrder(jsonNode); + } else if ( + "children" in figmaNode && + figmaNode.children.length !== jsonNode.children.length + ) { + addWarning( + "Error: JSON and Figma nodes have different child counts. Please report this issue.", + ); + } + + return jsonNode; +}; + +/** + * Convert Figma nodes to JSON format with parent references added + * @param nodes The Figma nodes to convert to JSON + * @param settings Plugin settings + * @returns JSON representation of the nodes with parent references + */ +export const nodesToJSON = async ( + nodes: ReadonlyArray, + settings: PluginSettings, +): Promise => { + // Reset name counters for each conversion + nodeNameCounters.clear(); + + const exportJsonStart = Date.now(); + // First get the JSON representation of nodes + const nodeJson = (await Promise.all( + nodes.map( + async (node) => + ( + (await node.exportAsync({ + format: "JSON_REST_V1", + })) as any + ).document, + ), + )) as Node[]; + + console.log("[debug] initial nodeJson", { ...nodeJson[0] }); + + console.log( + `[benchmark][inside nodesToJSON] JSON_REST_V1 export: ${Date.now() - exportJsonStart}ms`, + ); + + // Now process each top-level node pair (JSON node + Figma node) + const processNodesStart = Date.now(); + for (let i = 0; i < nodes.length; i++) { + await processNodePair(nodeJson[i], nodes[i], settings); + } + console.log( + `[benchmark][inside nodesToJSON] Process node pairs: ${Date.now() - processNodesStart}ms`, + ); + + return nodeJson; +}; diff --git a/packages/backend/src/code.ts b/packages/backend/src/code.ts index 479923a3..f4e0c784 100644 --- a/packages/backend/src/code.ts +++ b/packages/backend/src/code.ts @@ -1,6 +1,6 @@ import { + retrieveGenericLinearGradients, retrieveGenericSolidUIColors, - retrieveGenericLinearGradients as retrieveGenericGradients, } from "./common/retrieveUI/retrieveColors"; import { addWarning, @@ -11,429 +11,20 @@ import { postConversionComplete, postEmptyMessage } from "./messaging"; import { PluginSettings } from "types"; import { convertToCode } from "./common/retrieveUI/convertToCode"; import { generateHTMLPreview } from "./html/htmlMain"; -import { variableToColorName } from "./tailwind/conversionTables"; import { oldConvertNodesToAltNodes } from "./altNodes/oldAltConversion"; import { - convertNodesToAltNodes, - convertNodeToAltNode, -} from "./altNodes/altConversion"; -import { - HasGeometryTrait, - MinimalFillsTrait, - MinimalStrokesTrait, - Node, - Paint, -} from "./api_types"; - -// Performance tracking counters -let getNodeByIdAsyncTime = 0; -let getNodeByIdAsyncCalls = 0; -let getStyledTextSegmentsTime = 0; -let getStyledTextSegmentsCalls = 0; -let processColorVariablesTime = 0; -let processColorVariablesCalls = 0; - -// Keep track of node names for sequential numbering -const nodeNameCounters: Map = new Map(); - -const variableCache = new Map(); - -const memoizedVariableToColorName = async ( - variableId: string, -): Promise => { - if (!variableCache.has(variableId)) { - const colorName = (await variableToColorName(variableId)).replaceAll( - ",", - "", - ); - variableCache.set(variableId, colorName); - return colorName; - } - return variableCache.get(variableId)!; -}; - -// Define all property paths that might contain gradients -const GRADIENT_PROPERTIES = ["fills", "strokes"]; - -/** - * Process color variables in a paint style and add pre-computed variable names - * @param paint The paint style to process (fill or stroke) - */ -const processColorVariables = async (paint: Paint) => { - const start = Date.now(); - processColorVariablesCalls++; - - if ( - paint.type === "GRADIENT_ANGULAR" || - paint.type === "GRADIENT_DIAMOND" || - paint.type === "GRADIENT_LINEAR" || - paint.type === "GRADIENT_RADIAL" - ) { - // Filter stops with bound variables first to avoid unnecessary work - const stopsWithVariables = paint.gradientStops.filter( - (stop) => stop.boundVariables?.color, - ); - - // Process all gradient stops with variables in parallel - if (stopsWithVariables.length > 0) { - await Promise.all( - stopsWithVariables.map(async (stop) => { - (stop as any).variableColorName = await memoizedVariableToColorName( - stop.boundVariables!.color!.id, - ); - }), - ); - } - } else if (paint.type === "SOLID" && paint.boundVariables?.color) { - // Pre-compute and store the variable name - (paint as any).variableColorName = await memoizedVariableToColorName( - paint.boundVariables.color.id, - ); - } - - processColorVariablesTime += Date.now() - start; -}; - -const processEffectVariables = async ( - paint: DropShadowEffect | InnerShadowEffect, -) => { - const start = Date.now(); - processColorVariablesCalls++; - - if (paint.boundVariables?.color) { - // Pre-compute and store the variable name - (paint as any).variableColorName = await memoizedVariableToColorName( - paint.boundVariables.color.id, - ); - } - - processColorVariablesTime += Date.now() - start; -}; - -const getColorVariables = async ( - node: HasGeometryTrait, - settings: PluginSettings, -) => { - // This tries to be as fast as it can, using Promise.all so it can parallelize calls. - if (settings.useColorVariables) { - if (node.fills && Array.isArray(node.fills)) { - await Promise.all( - node.fills.map((fill: Paint) => processColorVariables(fill)), - ); - } - if (node.strokes && Array.isArray(node.strokes)) { - await Promise.all( - node.strokes.map((stroke: Paint) => processColorVariables(stroke)), - ); - } - if ("effects" in node && node.effects && Array.isArray(node.effects)) { - await Promise.all( - node.effects - .filter( - (effect: Effect) => - effect.type === "DROP_SHADOW" || effect.type === "INNER_SHADOW", - ) - .map((effect: DropShadowEffect | InnerShadowEffect) => - processEffectVariables(effect), - ), - ); - } - } -}; - -function adjustChildrenOrder(node: any) { - if (!node.itemReverseZIndex || !node.children || node.layoutMode === "NONE") { - return; - } - - const children = node.children; - const absoluteChildren = []; - const fixedChildren = []; - - // Single pass to separate absolute and fixed children - for (let i = children.length - 1; i >= 0; i--) { - const child = children[i]; - if (child.layoutPositioning === "ABSOLUTE") { - absoluteChildren.push(child); - } else { - fixedChildren.unshift(child); // Add to beginning to maintain original order - } - } - - // Combine the arrays (reversed absolute children + original order fixed children) - node.children = [...absoluteChildren, ...fixedChildren]; -} - -/** - * Recursively process both JSON node and Figma node to update with data not available in JSON - * @param jsonNode The JSON node to process - * @param figmaNode The corresponding Figma node - * @param settings Plugin settings - * @param parentNode Optional parent node reference to set - */ -const processNodePair = async ( - jsonNode: Node, - figmaNode: SceneNode, - settings: PluginSettings, - parentNode?: Node, -) => { - if (!jsonNode.id) return; - - // Set parent reference if parent is provided - if (parentNode) { - (jsonNode as any).parent = parentNode; - } - - // Ensure node has a unique name with simple numbering - const cleanName = jsonNode.name.trim(); - - // Track names with simple counter - const count = nodeNameCounters.get(cleanName) || 0; - nodeNameCounters.set(cleanName, count + 1); - - // For first occurrence, use original name; for duplicates, add sequential suffix - jsonNode.uniqueName = - count === 0 - ? cleanName - : `${cleanName}_${count.toString().padStart(2, "0")}`; - - // Handle text-specific properties - if (figmaNode.type === "TEXT") { - const getSegmentsStart = Date.now(); - getStyledTextSegmentsCalls++; - let styledTextSegments = figmaNode.getStyledTextSegments([ - "fontName", - "fills", - "fontSize", - "fontWeight", - "hyperlink", - "indentation", - "letterSpacing", - "lineHeight", - "listOptions", - "textCase", - "textDecoration", - "textStyleId", - "fillStyleId", - "openTypeFeatures", - ]); - getStyledTextSegmentsTime += Date.now() - getSegmentsStart; - - // Assign unique IDs to each segment - if (styledTextSegments.length > 0) { - const baseSegmentName = (jsonNode.uniqueName || jsonNode.name) - .replace(/[^a-zA-Z0-9_-]/g, "") - .toLowerCase(); - - // Add a uniqueId to each segment - styledTextSegments = await Promise.all( - styledTextSegments.map(async (segment, index) => { - const mutableSegment: any = Object.assign({}, segment); - - if (settings.useColorVariables && segment.fills) { - mutableSegment.fills = await Promise.all( - segment.fills.map(async (d) => { - if ( - d.blendMode !== "PASS_THROUGH" && - d.blendMode !== "NORMAL" - ) { - addWarning("BlendMode is not supported in Text colors"); - } - const fill = { ...d } as Paint; - await processColorVariables(fill); - return fill; - }), - ); - } - - // For single segments, don't add index suffix - if (styledTextSegments.length === 1) { - (mutableSegment as any).uniqueId = `${baseSegmentName}_span`; - } else { - // For multiple segments, add index suffix - (mutableSegment as any).uniqueId = - `${baseSegmentName}_span_${(index + 1).toString().padStart(2, "0")}`; - } - return mutableSegment; - }), - ); - - jsonNode.styledTextSegments = styledTextSegments; - } - - Object.assign(jsonNode, jsonNode.style); - if (!jsonNode.textAutoResize) { - jsonNode.textAutoResize = "NONE"; - } - } - - // Extract component metadata from instances - if ("variantProperties" in figmaNode && figmaNode.variantProperties) { - jsonNode.variantProperties = figmaNode.variantProperties; - } - - // Always copy size and position - if ("width" in figmaNode) { - jsonNode.width = figmaNode.width; - jsonNode.height = figmaNode.height; - jsonNode.x = figmaNode.x; - jsonNode.y = figmaNode.y; - } - - if ("rotation" in jsonNode) { - jsonNode.rotation = jsonNode.rotation * (180 / Math.PI); - } - - if ("individualStrokeWeights" in jsonNode) { - jsonNode.strokeTopWeight = jsonNode.individualStrokeWeights.top; - jsonNode.strokeBottomWeight = jsonNode.individualStrokeWeights.bottom; - jsonNode.strokeLeftWeight = jsonNode.individualStrokeWeights.left; - jsonNode.strokeRightWeight = jsonNode.individualStrokeWeights.right; - } - - await getColorVariables(jsonNode, settings); - - // Some places check if paddingLeft exists. This makes sure they all exist, even if 0. - if ("layoutMode" in jsonNode && jsonNode.layoutMode) { - if (jsonNode.paddingLeft === undefined) { - jsonNode.paddingLeft = 0; - } - if (jsonNode.paddingRight === undefined) { - jsonNode.paddingRight = 0; - } - if (jsonNode.paddingTop === undefined) { - jsonNode.paddingTop = 0; - } - if (jsonNode.paddingBottom === undefined) { - jsonNode.paddingBottom = 0; - } - } - - // Set default layout properties if missing - if (!jsonNode.layoutMode) jsonNode.layoutMode = "NONE"; - if (!jsonNode.layoutGrow) jsonNode.layoutGrow = 0; - if (!jsonNode.layoutSizingHorizontal) - jsonNode.layoutSizingHorizontal = "FIXED"; - if (!jsonNode.layoutSizingVertical) jsonNode.layoutSizingVertical = "FIXED"; - if (!jsonNode.primaryAxisAlignItems) { - jsonNode.primaryAxisAlignItems = "MIN"; - } - if (!jsonNode.counterAxisAlignItems) { - jsonNode.counterAxisAlignItems = "MIN"; - } - - // If layout sizing is HUG but there are no children, set it to FIXED - const hasChildren = - "children" in jsonNode && - jsonNode.children && - Array.isArray(jsonNode.children) && - jsonNode.children.length > 0; - - if (jsonNode.layoutSizingHorizontal === "HUG" && !hasChildren) { - jsonNode.layoutSizingHorizontal = "FIXED"; - } - if (jsonNode.layoutSizingVertical === "HUG" && !hasChildren) { - jsonNode.layoutSizingVertical = "FIXED"; - } - - // Process children recursively if both have children - if ( - "children" in jsonNode && - jsonNode.children && - Array.isArray(jsonNode.children) && - "children" in figmaNode && - figmaNode.children.length === jsonNode.children.length - ) { - // Somehow this is slower than the for loop. - // await Promise.all( - // jsonNode.children.map((child: any, i: number) => - // processNodePair(child, figmaNode.children[i], settings), - // ), - // ); - - for (let i = 0; i < jsonNode.children.length; i++) { - await processNodePair( - jsonNode.children[i], - figmaNode.children[i], - settings, - jsonNode, - ); - } - - if ( - jsonNode.layoutMode === "NONE" || - jsonNode.children.some( - (d: any) => - "layoutPositioning" in d && d.layoutPositioning === "ABSOLUTE", - ) - ) { - jsonNode.isRelative = true; - } - - adjustChildrenOrder(jsonNode); - } else if ( - "children" in figmaNode && - figmaNode.children.length !== jsonNode.children.length - ) { - addWarning( - "Error: JSON and Figma nodes have different child counts. Please report this issue.", - ); - } -}; - -/** - * Convert Figma nodes to JSON format with parent references added - * @param nodes The Figma nodes to convert to JSON - * @param settings Plugin settings - * @returns JSON representation of the nodes with parent references - */ -export const nodesToJSON = async ( - nodes: ReadonlyArray, - settings: PluginSettings, -): Promise => { - // Reset name counters for each conversion - nodeNameCounters.clear(); - - const exportJsonStart = Date.now(); - // First get the JSON representation of nodes - const nodeJson = (await Promise.all( - nodes.map( - async (node) => - ( - (await node.exportAsync({ - format: "JSON_REST_V1", - })) as any - ).document, - ), - )) as Node[]; - - console.log("[debug] initial nodeJson", { ...nodeJson[0] }); - - console.log( - `[benchmark][inside nodesToJSON] JSON_REST_V1 export: ${Date.now() - exportJsonStart}ms`, - ); - - // Now process each top-level node pair (JSON node + Figma node) - const processNodesStart = Date.now(); - for (let i = 0; i < nodes.length; i++) { - await processNodePair(nodeJson[i], nodes[i], settings); - } - console.log( - `[benchmark][inside nodesToJSON] Process node pairs: ${Date.now() - processNodesStart}ms`, - ); - - return nodeJson; -}; + getNodeByIdAsyncCalls, + getNodeByIdAsyncTime, + getStyledTextSegmentsCalls, + getStyledTextSegmentsTime, + nodesToJSON, + processColorVariablesCalls, + processColorVariablesTime, + resetPerformanceCounters, +} from "./altNodes/jsonNodeConversion"; export const run = async (settings: PluginSettings) => { - // Reset performance counters at the beginning - getNodeByIdAsyncTime = 0; - getNodeByIdAsyncCalls = 0; - getStyledTextSegmentsTime = 0; - getStyledTextSegmentsCalls = 0; - processColorVariablesTime = 0; - processColorVariablesCalls = 0; - variableCache.clear(); + resetPerformanceCounters(); clearWarnings(); const { framework, useOldPluginVersion2025 } = settings; @@ -456,17 +47,9 @@ export const run = async (settings: PluginSettings) => { convertedSelection = oldConvertNodesToAltNodes(selection, null); console.log("convertedSelection", convertedSelection); } else { - const nodeJson = await nodesToJSON(selection, settings); + convertedSelection = await nodesToJSON(selection, settings); console.log(`[benchmark] nodesToJSON: ${Date.now() - nodeToJSONStart}ms`); - console.log("nodeJson", nodeJson); - - // Now we work directly with the JSON nodes - const convertNodesStart = Date.now(); - convertedSelection = await convertNodesToAltNodes(nodeJson, null); - - console.log( - `[benchmark] convertNodesToAltNodes: ${Date.now() - convertNodesStart}ms`, - ); + console.log("nodeJson", convertedSelection); } console.log("[debug] convertedSelection", { ...convertedSelection[0] }); @@ -492,7 +75,7 @@ export const run = async (settings: PluginSettings) => { const colorPanelStart = Date.now(); const colors = retrieveGenericSolidUIColors(framework); - // const gradients = retrieveGenericGradients(framework); + const gradients = retrieveGenericLinearGradients(framework); console.log( `[benchmark] color and gradient panel: ${Date.now() - colorPanelStart}ms`, ); @@ -523,7 +106,7 @@ export const run = async (settings: PluginSettings) => { code, htmlPreview, colors, - gradients: [], + gradients, settings, warnings: [...warnings], }); diff --git a/packages/backend/src/common/commonFormatAttributes.ts b/packages/backend/src/common/commonFormatAttributes.ts index 8adb6392..e71fd588 100644 --- a/packages/backend/src/common/commonFormatAttributes.ts +++ b/packages/backend/src/common/commonFormatAttributes.ts @@ -18,7 +18,7 @@ export const formatStyleAttribute = ( }; export const formatDataAttribute = (label: string, value?: string) => - ` data-${lowercaseFirstLetter(label)}${value === undefined ? `` : `="${value}"`}`; + ` data-${lowercaseFirstLetter(label).replace(" ", "-")}${value === undefined ? `` : `="${value}"`}`; export const formatClassAttribute = ( classes: string[], diff --git a/packages/backend/src/common/retrieveUI/retrieveColors.ts b/packages/backend/src/common/retrieveUI/retrieveColors.ts index b50a14ac..331e35cc 100644 --- a/packages/backend/src/common/retrieveUI/retrieveColors.ts +++ b/packages/backend/src/common/retrieveUI/retrieveColors.ts @@ -20,7 +20,6 @@ import { LinearGradientConversion, SolidColorConversion, Framework, - HTMLSettings, } from "types"; export const retrieveGenericSolidUIColors = ( @@ -80,25 +79,35 @@ export const retrieveGenericLinearGradients = ( const selectionColors = figma.getSelectionColors(); const colorStr: Array = []; + console.log("selectionColors", selectionColors); + selectionColors?.paints.forEach((paint) => { if (paint.type === "GRADIENT_LINEAR") { + let fill = { ...paint }; + const t = fill.gradientTransform; + fill.gradientHandlePositions = [ + { x: t[0][2], y: t[1][2] }, // Start: (e, f) + { x: t[0][0] + t[0][2], y: t[1][0] + t[1][2] }, // End: (a + e, b + f) + ]; + console.log("fill is", { ...fill }); + let exportValue = ""; switch (framework) { case "Flutter": - exportValue = flutterGradient(paint); + exportValue = flutterGradient(fill); break; case "HTML": - exportValue = htmlGradientFromFills(paint); + exportValue = htmlGradientFromFills(fill); break; case "Tailwind": - exportValue = tailwindGradient(paint); + exportValue = tailwindGradient(fill); break; case "SwiftUI": - exportValue = swiftuiGradient(paint); + exportValue = swiftuiGradient(fill); break; } colorStr.push({ - cssPreview: htmlGradientFromFills(paint), + cssPreview: htmlGradientFromFills(fill), exportValue, }); } diff --git a/packages/backend/src/html/builderImpl/htmlSize.ts b/packages/backend/src/html/builderImpl/htmlSize.ts index d6bbc280..ad30981c 100644 --- a/packages/backend/src/html/builderImpl/htmlSize.ts +++ b/packages/backend/src/html/builderImpl/htmlSize.ts @@ -28,7 +28,11 @@ export const htmlSizePartial = ( ) { w = formatWithJSX("flex", isJsx, "1 1 0"); } else { - w = formatWithJSX("align-self", isJsx, "stretch"); + if (node.maxWidth) { + w = formatWithJSX("width", isJsx, "100%"); + } else { + w = formatWithJSX("align-self", isJsx, "stretch"); + } } } @@ -43,7 +47,11 @@ export const htmlSizePartial = ( ) { h = formatWithJSX("flex", isJsx, "1 1 0"); } else { - h = formatWithJSX("align-self", isJsx, "stretch"); + if (node.maxHeight) { + h = formatWithJSX("height", isJsx, "100%"); + } else { + h = formatWithJSX("align-self", isJsx, "stretch"); + } } } diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index 90f21d61..17d9ac79 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -3,5 +3,4 @@ export { htmlMain } from "./html/htmlMain"; export { tailwindMain } from "./tailwind/tailwindMain"; export { swiftuiMain } from "./swiftui/swiftuiMain"; export { run } from "./code"; -export { convertNodesToAltNodes as convertIntoNodes } from "./altNodes/altConversion"; export * from "./messaging"; diff --git a/packages/backend/src/tailwind/builderImpl/tailwindBorder.ts b/packages/backend/src/tailwind/builderImpl/tailwindBorder.ts index 23459cc4..667e5ef7 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindBorder.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindBorder.ts @@ -23,9 +23,9 @@ const getBorder = ( if (useOutline) { const outlineWidth = pxToOutline(weight); if (outlineWidth === null) { - return `outline-[${numberToFixedString(weight)}px]`; + return `outline outline-[${numberToFixedString(weight)}px]`; } else { - return `outline-${outlineWidth}`; + return `outline outline-${outlineWidth}`; } } diff --git a/packages/backend/src/tailwind/builderImpl/tailwindSize.ts b/packages/backend/src/tailwind/builderImpl/tailwindSize.ts index 43575681..22444dfc 100644 --- a/packages/backend/src/tailwind/builderImpl/tailwindSize.ts +++ b/packages/backend/src/tailwind/builderImpl/tailwindSize.ts @@ -46,7 +46,11 @@ export const tailwindSizePartial = ( ) { w = "flex-1"; } else { - w = "self-stretch"; + if (node.maxWidth) { + w = "w-full"; + } else { + w = "self-stretch"; + } } } @@ -61,7 +65,11 @@ export const tailwindSizePartial = ( ) { h = "flex-1"; } else { - h = "self-stretch"; + if (node.maxHeight) { + h = "h-full"; + } else { + h = "self-stretch"; + } } } diff --git a/packages/plugin-ui/src/PluginUI.tsx b/packages/plugin-ui/src/PluginUI.tsx index 912a74e7..928756d7 100644 --- a/packages/plugin-ui/src/PluginUI.tsx +++ b/packages/plugin-ui/src/PluginUI.tsx @@ -40,6 +40,43 @@ type PluginUIProps = { const frameworks: Framework[] = ["HTML", "Tailwind", "Flutter", "SwiftUI"]; +type FrameworkTabsProps = { + frameworks: Framework[]; + selectedFramework: Framework; + setSelectedFramework: (framework: Framework) => void; + showAbout: boolean; + setShowAbout: (show: boolean) => void; +}; + +const FrameworkTabs = ({ + frameworks, + selectedFramework, + setSelectedFramework, + showAbout, + setShowAbout, +}: FrameworkTabsProps) => { + return ( +
+ {frameworks.map((tab) => ( + + ))} +
+ ); +}; + export const PluginUI = (props: PluginUIProps) => { const [showAbout, setShowAbout] = useState(false); @@ -58,36 +95,27 @@ export const PluginUI = (props: PluginUIProps) => { return (
-
-
- {frameworks.map((tab) => ( - - ))} +
+
+ +
-
{ >
{showAbout ? ( - ) : ( diff --git a/packages/plugin-ui/src/components/About.tsx b/packages/plugin-ui/src/components/About.tsx index e39d3e33..c7bbc431 100644 --- a/packages/plugin-ui/src/components/About.tsx +++ b/packages/plugin-ui/src/components/About.tsx @@ -169,7 +169,7 @@ const About = ({
{/* Contact Card */} -
+
{/* Debug Helper Card */} -
+
@@ -219,11 +219,11 @@ const About = ({

- The new version is up to 100x faster, but might still cause some issues. - If you encounter problems, you can switch to the old version - (and please report issues so they can be fixed). + The new version is up to 100x faster, but might still cause some + issues. If you encounter problems, you can switch to the old + version (and please report issues so they can be fixed).

diff --git a/packages/plugin-ui/src/components/CodePanel.tsx b/packages/plugin-ui/src/components/CodePanel.tsx index 285f3dd2..9496e3e6 100644 --- a/packages/plugin-ui/src/components/CodePanel.tsx +++ b/packages/plugin-ui/src/components/CodePanel.tsx @@ -10,7 +10,6 @@ import { coldarkDark as theme } from "react-syntax-highlighter/dist/esm/styles/p import { CopyButton } from "./CopyButton"; import EmptyState from "./EmptyState"; import SettingsGroup from "./SettingsGroup"; -import CustomPrefixInput from "./CustomPrefixInput"; import FrameworkTabs from "./FrameworkTabs"; import { TailwindSettings } from "./TailwindSettings"; @@ -150,7 +149,7 @@ const CodePanel = (props: CodePanelProps) => {
{!isCodeEmpty && ( -
+
{/* Essential settings always shown */} -
+
+
-

- {/*
*/} +

Color Palette

- + {props.colors.length} color{props.colors.length > 1 ? "s" : ""}
@@ -33,7 +32,7 @@ const ColorsPanel = (props: { key={"button" + idx} className={`w-full h-16 rounded-lg text-sm font-semibold shadow-sm transition-all duration-300 ${ isPressed === idx - ? "ring-4 ring-green-300 ring-opacity-50 animate-pulse" + ? "ring-4 ring-primary ring-opacity-50 animate-pulse" : "ring-0" }`} style={{ backgroundColor: color.hex }} diff --git a/packages/plugin-ui/src/components/CopyButton.tsx b/packages/plugin-ui/src/components/CopyButton.tsx index 7c302b8c..8d209e02 100644 --- a/packages/plugin-ui/src/components/CopyButton.tsx +++ b/packages/plugin-ui/src/components/CopyButton.tsx @@ -1,8 +1,9 @@ "use client"; import { useState, useEffect } from "react"; -import { Copy, CheckCircle, Check } from "lucide-react"; +import { Copy, Check } from "lucide-react"; import copy from "copy-to-clipboard"; +import { cn } from "../lib/utils"; interface CopyButtonProps { value: string; @@ -47,11 +48,14 @@ export function CopyButton({ onClick={handleCopy} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} - className={`inline-flex items-center justify-center px-3 py-1.5 text-sm font-medium border border-green-500 rounded-md shadow-sm hover:bg-green-500 dark:hover:bg-green-600 hover:text-white hover:border-transparent transition-all duration-300 ${ + className={cn( + `inline-flex items-center justify-center px-3 py-1.5 text-sm font-medium border rounded-md transition-all duration-300`, isCopied - ? "bg-green-600 text-white dark:text-white hover:bg-green-500" - : "bg-neutral-100 dark:bg-neutral-700 text-neutral-700 dark:text-neutral-200 border-neutral-300 dark:border-neutral-600" - } ${className || ""} relative`} + ? "bg-primary border-primary text-primary-foreground" + : "bg-neutral-100 dark:bg-neutral-700 dark:hover:bg-muted-foreground/30 border-border text-foreground", + className, + `relative`, + )} aria-label={isCopied ? "Copied!" : "Copy to clipboard"} >
@@ -62,13 +66,7 @@ export function CopyButton({ : "opacity-100 scale-100 rotate-0" }`} > - + - +
{showLabel && ( - - {isCopied ? "Copied" : "Copy"} - + {isCopied ? "Copied" : "Copy"} )} {isCopied && (