Skip to content

Commit effebaa

Browse files
authored
Refactor Plugin
2 parents 30ee7ed + 91b6bc9 commit effebaa

File tree

103 files changed

+15344
-3813
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

103 files changed

+15344
-3813
lines changed
Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,58 @@
11
name: Bug Report
22
description: Report a bug in the project
33
title: "[bug]: "
4-
labels: ["bug", "needs triage"]
5-
assignees: []
4+
labels: ["bug"]
65

76
body:
87
- type: markdown
98
attributes:
10-
value: "## 🐛 Bug Report\nPlease fill out the details below."
9+
value: "## 🐛 Bug Report\nPlease fill out the details below to help us resolve your issue quickly."
1110

1211
- type: textarea
13-
id: reproduction
12+
id: steps
1413
attributes:
15-
label: "Settings / Steps to Reproduce"
16-
description: "Provide a step-by-step guide on how to reproduce the bug. Please include the plugin settings you were using."
17-
placeholder: "Framework: Tailwind with JSX\n1. Selected design\n2. Selected Optimize Layers\n3. See error"
14+
label: "Steps to Reproduce"
15+
description: "Provide a step-by-step guide of the actions you took that led to the bug."
16+
placeholder: |
17+
1. Open the plugin
18+
2. Select a text layer
19+
3. Reorder the layer.
20+
4. Observe the error
1821
validations:
1922
required: true
2023

2124
- type: textarea
2225
id: expected-behavior
2326
attributes:
2427
label: "Expected Behavior"
25-
description: "What did you expect to happen?"
26-
placeholder: "The button should navigate to the dashboard."
28+
description: "Describe what you expected to happen."
29+
placeholder: "The code should be generated successfully and displayed in the output panel."
2730
validations:
28-
required: true
31+
required: false
2932

3033
- type: textarea
3134
id: actual-behavior
3235
attributes:
3336
label: "Actual Behavior"
34-
description: "What actually happened?"
35-
placeholder: "The button does nothing."
37+
description: "Describe what actually happened. Include any error messages or logs if applicable."
38+
placeholder: "An error message appears: 'Failed to generate code due to invalid layer configuration.'"
3639
validations:
37-
required: true
40+
required: false
3841

3942
- type: input
4043
id: design-link
4144
attributes:
4245
label: "Design Reference"
43-
description: "Provide a link to the design that you were working on (if possible)."
44-
placeholder: "Figma design link"
46+
description: "If applicable, provide a link to the design related to this bug (e.g., Figma link)."
47+
placeholder: "e.g., https://www.figma.com/file/example"
4548
validations:
4649
required: false
4750

4851
- type: textarea
4952
id: screenshots
5053
attributes:
5154
label: "Screenshots or Videos"
52-
description: "If applicable, add screenshots or screen recordings to help explain the issue."
53-
placeholder: "Drag and drop images here or paste them from your clipboard."
55+
description: "If applicable, add screenshots or provide links to videos to help explain the issue. You can drag and drop images here or paste them from your clipboard."
56+
placeholder: "Drag and drop images or videos here"
5457
validations:
5558
required: false
56-
57-
- type: textarea
58-
id: environment
59-
attributes:
60-
label: "Environment"
61-
description: "Optionally provide details about your development environment."
62-
placeholder: "OS: macOS 14.1\nBrowser: Chrome 120\nNode.js: 18.17.0"
63-
validations:
64-
required: true

README.md

Lines changed: 58 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,42 +13,45 @@
1313
<a href="https://www.figma.com/community/plugin/842128343887142055"><img src="assets/badge.png" height="60"/></a>
1414
</p>
1515

16-
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!
16+
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.
1717

1818
![Gif showing the conversion](assets/lossy_gif.gif)
1919

2020
## How it works
2121

22-
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:
22+
The plugin uses a sophisticated multi-step process to transform your Figma designs into clean, optimized code:
23+
24+
1. **Node Conversion**: First, the plugin converts Figma's native nodes into JSON representations, preserving all necessary properties while adding optimizations and parent references.
25+
26+
2. **Intermediate Representation**: The JSON nodes are then transformed into `AltNodes` - a custom virtual representation that can be manipulated without affecting your original design.
27+
28+
3. **Layout Optimization**: The plugin analyzes and optimizes layouts, detecting patterns like auto-layouts, responsive constraints and color variables.
29+
30+
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.
2331

2432
![Conversion Workflow](assets/workflow.png)
2533

26-
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.
34+
This intermediate representation approach allows for sophisticated transformations and optimizations before any code is generated, resulting in cleaner, more maintainable output.
2735

2836
## Hard cases
2937

30-
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:
31-
32-
![Conversion Workflow](assets/examples.png)
38+
Converting visual designs to code inevitably encounters complex edge cases. Here are some challenges the plugin handles:
3339

34-
**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.
40+
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.
3541

36-
### Todo
42+
2. **Color Variables**: The plugin detects and processes color variables, allowing for theme-consistent output.
3743

38-
- Vectors (tricky in HTML, unsupported in Flutter)
39-
- Images (they are local, how to support them?)
40-
- Line/Star/Polygon (todo. Rectangle and Ellipse were prioritized and are more common)
41-
- The source code is fully commented and there are more than 30 "todo"s there
44+
3. **Gradients and Effects**: Different frameworks handle gradients and effects in unique ways, requiring specialized conversion logic.
4245

43-
### Tailwind limitations
46+
![Conversion Workflow](assets/examples.png)
4447

45-
- **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.
48+
**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.
4649

47-
### Flutter limits and ideas
50+
### Todo
4851

49-
- **Stack:** in some simpler cases, a `Stack` could be replaced with a `Container` and a `BoxDecoration`. Discover those cases and optimize them.
50-
- **Material Styles**: text could be matched to existing Material styles (like outputting `Headline6` when text size is 20).
51-
- **Identify Buttons**: the plugin could identify specific buttons and output them instead of always using `Container` or `Material`.
52+
- Vectors (possible to enable in HTML and Tailwind)
53+
- Images (possible to enable to inline them in HTML and Tailwind)
54+
- Line/Star/Polygon
5255

5356
## How to build the project
5457

@@ -70,16 +73,49 @@ The plugin is organized as a monorepo. There are several packages:
7073
- `ui-src` - loads the common `plugin-ui` and compiles to `index.html`
7174
- `apps/debug` - This is a debug mode plugin that is a more convenient way to see all the UI elements.
7275

73-
The plugin is built using Turbo which in turn builds the internal packages.
76+
### Development Workflow
77+
78+
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.
79+
80+
#### Running the Project
81+
82+
You have two main options for development:
83+
84+
1. **Root development mode** (includes debug UI):
85+
86+
```bash
87+
pnpm dev
88+
```
89+
90+
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`.
91+
92+
2. **Plugin-only development mode**:
93+
94+
```bash
95+
cd apps/plugin
96+
pnpm dev
97+
```
98+
99+
This focuses only on the plugin without the Next.js debug UI. Use this when you're making changes specifically to the plugin.
100+
101+
#### Where to Make Changes
102+
103+
Most of your development work will happen in these directories:
104+
105+
- `packages/backend` - For plugin backend
106+
- `packages/plugin-ui` - For plugin UI
107+
- `apps/plugin/` - The main plugin result that combines the backend and UI and is called by Figma.
108+
109+
You'll rarely need to modify files directly in the `apps/` directory, as they mostly contain build configuration.
74110

75111
#### Commands
76112

77113
`pnpm run ...`
78114

79115
- `dev` - runs the app in dev mode. This can be run in the Figma editor.
80-
- `build`
81-
- `build:watch`
82-
- `lint`
116+
- `build` - builds the project for production
117+
- `build:watch` - builds and watches for changes
118+
- `lint` - runs ESLint
83119
- `format` - formats with prettier (warning: may edit files!)
84120

85121
#### Debug mode

apps/debug/app/layout.tsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
2+
import type { Metadata } from "next";
3+
import "../styles/globals.css";
4+
5+
export const metadata: Metadata = {
6+
title: "v0 App",
7+
description: "Created with v0",
8+
generator: "v0.dev",
9+
};
10+
11+
export default function RootLayout({
12+
children,
13+
}: Readonly<{
14+
children: React.ReactNode;
15+
}>) {
16+
return (
17+
<html lang="en">
18+
<body>{children}</body>
19+
</html>
20+
);
21+
}

apps/debug/app/page.tsx

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
"use client";
2+
3+
import { Framework } from "types";
4+
import * as React from "react";
5+
import { PluginUI } from "plugin-ui";
6+
7+
export default function Web() {
8+
const [selectedFramework, setSelectedFramework] =
9+
React.useState<Framework>("HTML");
10+
const testWarnings = ["This is an example of a conversion warning message."];
11+
12+
return (
13+
<div className="min-h-screen bg-gradient-to-br from-gray-50 to-gray-100 p-8">
14+
<header className="mb-10">
15+
<h1 className="text-5xl font-bold text-gray-900 tracking-tight">
16+
Debug Mode
17+
</h1>
18+
<p className="text-gray-600 mt-2">
19+
Preview your Figma to Code plugin in both light and dark modes
20+
</p>
21+
<div className="h-0.5 bg-gradient-to-r from-blue-500 to-purple-500 mt-6"></div>
22+
</header>
23+
24+
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
25+
<div className="flex flex-col">
26+
<div className="bg-gradient-to-br from-white to-gray-50 p-6 rounded-xl shadow-xl border border-gray-100">
27+
<h2 className="text-2xl font-semibold mb-4 text-gray-800">
28+
Light Mode
29+
</h2>
30+
<div className="border rounded-xl">
31+
<PluginFigmaToolbar variant="(Light)" />
32+
<PluginUI
33+
code={"code goes hereeeee"}
34+
isLoading={false}
35+
selectedFramework={selectedFramework}
36+
setSelectedFramework={setSelectedFramework}
37+
htmlPreview={null}
38+
settings={undefined}
39+
onPreferenceChanged={() => {}}
40+
colors={[]}
41+
gradients={[]}
42+
warnings={testWarnings}
43+
/>
44+
</div>
45+
</div>
46+
</div>
47+
48+
<div className="flex flex-col">
49+
<div className="p-6 rounded-xl shadow-xl border border-gray-700 bg-[#2C2C2C]">
50+
<h2 className="text-2xl font-semibold mb-4 text-gray-100">
51+
Dark Mode
52+
</h2>
53+
<div className="border rounded-xl dark">
54+
<PluginFigmaToolbar variant="(Dark)" />
55+
<PluginUI
56+
code={"code goes hereeeee"}
57+
isLoading={false}
58+
selectedFramework={selectedFramework}
59+
setSelectedFramework={setSelectedFramework}
60+
htmlPreview={null}
61+
settings={undefined}
62+
onPreferenceChanged={() => {}}
63+
colors={[]}
64+
gradients={[]}
65+
warnings={testWarnings}
66+
/>
67+
</div>
68+
</div>
69+
</div>
70+
</div>
71+
</div>
72+
);
73+
}
74+
75+
const PluginFigmaToolbar = (props: { variant: string }) => (
76+
<div className="bg-neutral-800 w-full h-12 flex items-center text-white gap-4 px-5 rounded-t-lg shadow-sm">
77+
<div className="flex items-center gap-2">
78+
<div className="w-3 h-3 rounded-full bg-red-500"></div>
79+
<div className="w-3 h-3 rounded-full bg-yellow-500"></div>
80+
<div className="w-3 h-3 rounded-full bg-green-500"></div>
81+
</div>
82+
<span className="font-medium ml-2">Figma to Code {props.variant}</span>
83+
</div>
84+
);

apps/debug/next-env.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
/// <reference types="next/image-types/global" />
33

44
// NOTE: This file should not be edited
5-
// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information.
5+
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.

apps/debug/next.config.js

Lines changed: 0 additions & 29 deletions
This file was deleted.

apps/debug/next.config.mjs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
let userConfig = undefined
2+
try {
3+
userConfig = await import('./v0-user-next.config')
4+
} catch (e) {
5+
// ignore error
6+
}
7+
8+
/** @type {import('next').NextConfig} */
9+
const nextConfig = {
10+
eslint: {
11+
ignoreDuringBuilds: true,
12+
},
13+
typescript: {
14+
ignoreBuildErrors: true,
15+
},
16+
experimental: {
17+
webpackBuildWorker: true,
18+
parallelServerBuildTraces: true,
19+
parallelServerCompiles: true,
20+
},
21+
}
22+
23+
mergeConfig(nextConfig, userConfig)
24+
25+
function mergeConfig(nextConfig, userConfig) {
26+
if (!userConfig) {
27+
return
28+
}
29+
30+
for (const key in userConfig) {
31+
if (
32+
typeof nextConfig[key] === 'object' &&
33+
!Array.isArray(nextConfig[key])
34+
) {
35+
nextConfig[key] = {
36+
...nextConfig[key],
37+
...userConfig[key],
38+
}
39+
} else {
40+
nextConfig[key] = userConfig[key]
41+
}
42+
}
43+
}
44+
45+
export default nextConfig

0 commit comments

Comments
 (0)